testcase-canonical.php 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <?php
  2. class WP_Canonical_UnitTestCase extends WP_UnitTestCase {
  3. public static $old_current_user;
  4. public static $author_id;
  5. public static $post_ids = array();
  6. public static $comment_ids = array();
  7. public static $term_ids = array();
  8. public static $terms = array();
  9. public static $old_options = array();
  10. /**
  11. * This can be defined in a subclass of this class which contains its own data() method.
  12. * Those tests will be run against the specified permastruct.
  13. */
  14. public $structure = '/%year%/%monthnum%/%day%/%postname%/';
  15. public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
  16. self::generate_shared_fixtures( $factory );
  17. }
  18. public static function wpTearDownAfterClass() {
  19. self::delete_shared_fixtures();
  20. }
  21. public function setUp() {
  22. parent::setUp();
  23. update_option( 'page_comments', true );
  24. update_option( 'comments_per_page', 5 );
  25. update_option( 'posts_per_page', 5 );
  26. $this->set_permalink_structure( $this->structure );
  27. create_initial_taxonomies();
  28. }
  29. /**
  30. * Generate fixtures to be shared between canonical tests.
  31. *
  32. * Abstracted here because it's invoked by setUpBeforeClass() in more than one class.
  33. *
  34. * @since 4.1.0
  35. */
  36. public static function generate_shared_fixtures( WP_UnitTest_Factory $factory ) {
  37. self::$old_current_user = get_current_user_id();
  38. self::$author_id = $factory->user->create( array( 'user_login' => 'canonical-author' ) );
  39. /*
  40. * Also set in self::setUp(), but we must configure here to make sure that
  41. * post authorship is properly attributed for fixtures.
  42. */
  43. wp_set_current_user( self::$author_id );
  44. // Already created by install defaults:
  45. // $factory->term->create( array( 'taxonomy' => 'category', 'name' => 'uncategorized' ) );
  46. self::$post_ids[] = $factory->post->create(
  47. array(
  48. 'import_id' => 587,
  49. 'post_title' => 'post-format-test-audio',
  50. 'post_date' => '2008-06-02 00:00:00',
  51. )
  52. );
  53. $gallery_post_id = $factory->post->create(
  54. array(
  55. 'post_title' => 'post-format-test-gallery',
  56. 'post_date' => '2008-06-10 00:00:00',
  57. )
  58. );
  59. self::$post_ids[] = $gallery_post_id;
  60. self::$post_ids[] = $factory->post->create(
  61. array(
  62. 'import_id' => 611,
  63. 'post_type' => 'attachment',
  64. 'post_title' => 'canola2',
  65. 'post_parent' => $gallery_post_id,
  66. )
  67. );
  68. self::$post_ids[] = $factory->post->create(
  69. array(
  70. 'post_title' => 'images-test',
  71. 'post_date' => '2008-09-03 00:00:00',
  72. )
  73. );
  74. self::$post_ids[] = $factory->post->create(
  75. array(
  76. 'post_title' => 'multipage-post-test',
  77. 'post_date' => '2008-09-03 00:00:00',
  78. 'post_content' => 'Page 1 <!--nextpage--> Page 2 <!--nextpage--> Page 3',
  79. )
  80. );
  81. self::$post_ids[] = $factory->post->create(
  82. array(
  83. 'post_title' => 'non-paged-post-test',
  84. 'post_date' => '2008-09-03 00:00:00',
  85. )
  86. );
  87. $comment_post_id = $factory->post->create(
  88. array(
  89. 'import_id' => 149,
  90. 'post_title' => 'comment-test',
  91. 'post_date' => '2008-03-03 00:00:00',
  92. )
  93. );
  94. self::$post_ids[] = $comment_post_id;
  95. self::$comment_ids = $factory->comment->create_post_comments( $comment_post_id, 15 );
  96. self::$post_ids[] = $factory->post->create( array( 'post_date' => '2008-09-05 00:00:00' ) );
  97. self::$post_ids[] = $factory->post->create( array( 'import_id' => 123 ) );
  98. self::$post_ids[] = $factory->post->create( array( 'import_id' => 1 ) );
  99. self::$post_ids[] = $factory->post->create( array( 'import_id' => 358 ) );
  100. self::$post_ids[] = $factory->post->create(
  101. array(
  102. 'post_type' => 'page',
  103. 'post_title' => 'sample-page',
  104. )
  105. );
  106. self::$post_ids[] = $factory->post->create(
  107. array(
  108. 'post_type' => 'page',
  109. 'post_title' => 'about',
  110. )
  111. );
  112. $parent_page_id = $factory->post->create(
  113. array(
  114. 'post_type' => 'page',
  115. 'post_title' => 'parent-page',
  116. )
  117. );
  118. self::$post_ids[] = $parent_page_id;
  119. self::$post_ids[] = $factory->post->create(
  120. array(
  121. 'import_id' => 144,
  122. 'post_type' => 'page',
  123. 'post_title' => 'child-page-1',
  124. 'post_parent' => $parent_page_id,
  125. )
  126. );
  127. $parent_page_id = $factory->post->create(
  128. array(
  129. 'post_name' => 'parent',
  130. 'post_type' => 'page',
  131. )
  132. );
  133. self::$post_ids[] = $parent_page_id;
  134. $child_id_1 = $factory->post->create(
  135. array(
  136. 'post_name' => 'child1',
  137. 'post_type' => 'page',
  138. 'post_parent' => $parent_page_id,
  139. )
  140. );
  141. self::$post_ids[] = $child_id_1;
  142. $child_id_2 = $factory->post->create(
  143. array(
  144. 'post_name' => 'child2',
  145. 'post_type' => 'page',
  146. 'post_parent' => $parent_page_id,
  147. )
  148. );
  149. self::$post_ids[] = $child_id_2;
  150. $grandchild_id_1 = $factory->post->create(
  151. array(
  152. 'post_name' => 'grandchild',
  153. 'post_type' => 'page',
  154. 'post_parent' => $child_id_1,
  155. )
  156. );
  157. self::$post_ids[] = $grandchild_id_1;
  158. $grandchild_id_2 = $factory->post->create(
  159. array(
  160. 'post_name' => 'grandchild',
  161. 'post_type' => 'page',
  162. 'post_parent' => $child_id_2,
  163. )
  164. );
  165. self::$post_ids[] = $grandchild_id_2;
  166. $cat1 = $factory->term->create(
  167. array(
  168. 'taxonomy' => 'category',
  169. 'name' => 'parent',
  170. )
  171. );
  172. self::$terms['/category/parent/'] = $cat1;
  173. self::$term_ids[ $cat1 ] = 'category';
  174. $cat2 = $factory->term->create(
  175. array(
  176. 'taxonomy' => 'category',
  177. 'name' => 'child-1',
  178. 'parent' => self::$terms['/category/parent/'],
  179. )
  180. );
  181. self::$terms['/category/parent/child-1/'] = $cat2;
  182. self::$term_ids[ $cat2 ] = 'category';
  183. $cat3 = $factory->term->create(
  184. array(
  185. 'taxonomy' => 'category',
  186. 'name' => 'child-2',
  187. 'parent' => self::$terms['/category/parent/child-1/'],
  188. )
  189. );
  190. self::$terms['/category/parent/child-1/child-2/'] = $cat3;
  191. self::$term_ids[ $cat3 ] = 'category';
  192. $cat4 = $factory->term->create(
  193. array(
  194. 'taxonomy' => 'category',
  195. 'name' => 'cat-a',
  196. )
  197. );
  198. self::$term_ids[ $cat4 ] = 'category';
  199. $cat5 = $factory->term->create(
  200. array(
  201. 'taxonomy' => 'category',
  202. 'name' => 'cat-b',
  203. )
  204. );
  205. self::$term_ids[ $cat5 ] = 'category';
  206. $tag1 = $factory->term->create(
  207. array(
  208. 'name' => 'post-formats',
  209. )
  210. );
  211. self::$term_ids[ $tag1 ] = 'post_tag';
  212. }
  213. /**
  214. * Clean up shared fixtures.
  215. *
  216. * @since 4.1.0
  217. */
  218. public static function delete_shared_fixtures() {
  219. self::$author_id = null;
  220. self::$post_ids = array();
  221. self::$comment_ids = array();
  222. self::$term_ids = array();
  223. self::$terms = array();
  224. }
  225. /**
  226. * Assert that a given URL is the same a the canonical URL generated by WP.
  227. *
  228. * @since 4.1.0
  229. *
  230. * @param string $test_url Raw URL that will be run through redirect_canonical().
  231. * @param string $expected Expected string.
  232. * @param int $ticket Optional. Trac ticket number.
  233. * @param array $expected_doing_it_wrong Array of class/function names expected to throw _doing_it_wrong() notices.
  234. */
  235. public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_doing_it_wrong = array() ) {
  236. $this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, (array) $expected_doing_it_wrong );
  237. $ticket_ref = ( $ticket > 0 ) ? 'Ticket #' . $ticket : '';
  238. if ( is_string( $expected ) ) {
  239. $expected = array( 'url' => $expected );
  240. } elseif ( is_array( $expected ) && ! isset( $expected['url'] ) && ! isset( $expected['qv'] ) ) {
  241. $expected = array( 'qv' => $expected );
  242. }
  243. if ( ! isset( $expected['url'] ) && ! isset( $expected['qv'] ) ) {
  244. $this->fail( 'No valid expected output was provided' );
  245. }
  246. $this->go_to( home_url( $test_url ) );
  247. // Does the redirect match what's expected?
  248. $can_url = $this->get_canonical( $test_url );
  249. $parsed_can_url = parse_url( $can_url );
  250. // Just test the path and query if present.
  251. if ( isset( $expected['url'] ) ) {
  252. $this->assertSame( $expected['url'], $parsed_can_url['path'] . ( ! empty( $parsed_can_url['query'] ) ? '?' . $parsed_can_url['query'] : '' ), $ticket_ref );
  253. }
  254. // If the test data doesn't include expected query vars, then we're done here.
  255. if ( ! isset( $expected['qv'] ) ) {
  256. return;
  257. }
  258. // "make" that the request and check the query is correct.
  259. $this->go_to( $can_url );
  260. // Are all query vars accounted for, and correct?
  261. global $wp;
  262. $query_vars = array_diff( $wp->query_vars, $wp->extra_query_vars );
  263. if ( ! empty( $parsed_can_url['query'] ) ) {
  264. parse_str( $parsed_can_url['query'], $_qv );
  265. // $_qv should not contain any elements which are set in $query_vars already
  266. // (i.e. $_GET vars should not be present in the Rewrite).
  267. $this->assertSame( array(), array_intersect( $query_vars, $_qv ), 'Query vars are duplicated from the Rewrite into $_GET; ' . $ticket_ref );
  268. $query_vars = array_merge( $query_vars, $_qv );
  269. }
  270. $this->assertEquals( $expected['qv'], $query_vars );
  271. }
  272. /**
  273. * Get the canonical URL given a raw URL.
  274. *
  275. * @param string $test_url Should be relative to the site "front", ie /category/uncategorized/
  276. * as opposed to http://example.com/category/uncategorized/
  277. * @return $can_url Returns the original $test_url if no canonical can be generated, otherwise returns
  278. * the fully-qualified URL as generated by redirect_canonical().
  279. */
  280. public function get_canonical( $test_url ) {
  281. $test_url = home_url( $test_url );
  282. $can_url = redirect_canonical( $test_url, false );
  283. if ( ! $can_url ) {
  284. return $test_url; // No redirect will take place for this request.
  285. }
  286. return $can_url;
  287. }
  288. }