Merge branch 'Wikidata' of ssh://gerrit.wikimedia.org:29418/mediawiki/core into Wikidata
[lhc/web/wiklou.git] / tests / phpunit / includes / PathRouterTest.php
1 <?php
2 /**
3 * Tests for the PathRouter parsing
4 */
5
6 class PathRouterTest extends MediaWikiTestCase {
7
8 public function setUp() {
9 $router = new PathRouter;
10 $router->add("/wiki/$1");
11 $this->basicRouter = $router;
12 }
13
14 /**
15 * Test basic path parsing
16 */
17 public function testBasic() {
18 $matches = $this->basicRouter->parse( "/wiki/Foo" );
19 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
20 }
21
22 /**
23 * Test loose path auto-$1
24 */
25 public function testLoose() {
26 $router = new PathRouter;
27 $router->add("/"); # Should be the same as "/$1"
28 $matches = $router->parse( "/Foo" );
29 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
30
31 $router = new PathRouter;
32 $router->add("/wiki"); # Should be the same as /wiki/$1
33 $matches = $router->parse( "/wiki/Foo" );
34 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
35
36 $router = new PathRouter;
37 $router->add("/wiki/"); # Should be the same as /wiki/$1
38 $matches = $router->parse( "/wiki/Foo" );
39 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
40 }
41
42 /**
43 * Test to ensure that path is based on specifity, not order
44 */
45 public function testOrder() {
46 $router = new PathRouter;
47 $router->add("/$1");
48 $router->add("/a/$1");
49 $router->add("/b/$1");
50 $matches = $router->parse( "/a/Foo" );
51 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
52
53 $router = new PathRouter;
54 $router->add("/b/$1");
55 $router->add("/a/$1");
56 $router->add("/$1");
57 $matches = $router->parse( "/a/Foo" );
58 $this->assertEquals( $matches, array( 'title' => "Foo" ) );
59 }
60
61 /**
62 * Test the handling of key based arrays with a url parameter
63 */
64 public function testKeyParameter() {
65 $router = new PathRouter;
66 $router->add( array( 'edit' => "/edit/$1" ), array( 'action' => '$key' ) );
67 $matches = $router->parse( "/edit/Foo" );
68 $this->assertEquals( $matches, array( 'title' => "Foo", 'action' => 'edit' ) );
69 }
70
71 /**
72 * Test the handling of $2 inside paths
73 */
74 public function testAdditionalParameter() {
75 // Basic $2
76 $router = new PathRouter;
77 $router->add( '/$2/$1', array( 'test' => '$2' ) );
78 $matches = $router->parse( "/asdf/Foo" );
79 $this->assertEquals( $matches, array( 'title' => "Foo", 'test' => 'asdf' ) );
80 }
81
82 /**
83 * Test additional restricted value parameter
84 */
85 public function testRestrictedValue() {
86 $router = new PathRouter;
87 $router->add( '/$2/$1',
88 array( 'test' => '$2' ),
89 array( '$2' => array( 'a', 'b' ) )
90 );
91 $router->add( '/$2/$1',
92 array( 'test2' => '$2' ),
93 array( '$2' => 'c' )
94 );
95 $router->add( '/$1' );
96
97 $matches = $router->parse( "/asdf/Foo" );
98 $this->assertEquals( $matches, array( 'title' => "asdf/Foo" ) );
99
100 $matches = $router->parse( "/a/Foo" );
101 $this->assertEquals( $matches, array( 'title' => "Foo", 'test' => 'a' ) );
102
103 $matches = $router->parse( "/c/Foo" );
104 $this->assertEquals( $matches, array( 'title' => "Foo", 'test2' => 'c' ) );
105 }
106
107 public function callbackForTest( &$matches, $data ) {
108 $matches['x'] = $data['$1'];
109 $matches['foo'] = $data['foo'];
110 }
111
112 public function testCallback() {
113 $router = new PathRouter;
114 $router->add( "/$1",
115 array( 'a' => 'b', 'data:foo' => 'bar' ),
116 array( 'callback' => array( $this, 'callbackForTest' ) )
117 );
118 $matches = $router->parse( '/Foo' );
119 $this->assertEquals( $matches, array(
120 'title' => "Foo",
121 'x' => 'Foo',
122 'a' => 'b',
123 'foo' => 'bar'
124 ) );
125 }
126
127 /**
128 * Test to ensure that matches are not made if a parameter expects nonexistent input
129 */
130 public function testFail() {
131 $router = new PathRouter;
132 $router->add( "/wiki/$1", array( 'title' => "$1$2" ) );
133 $matches = $router->parse( "/wiki/A" );
134 $this->assertEquals( array(), $matches );
135 }
136
137 /**
138 * Test to ensure weight of paths is handled correctly
139 */
140 public function testWeight() {
141 $router = new PathRouter;
142 $router->addStrict( "/Bar", array( 'ping' => 'pong' ) );
143 $router->add( "/asdf-$1", array( 'title' => 'qwerty-$1' ) );
144 $router->add( "/$1" );
145 $router->add( "/qwerty-$1", array( 'title' => 'asdf-$1' ) );
146 $router->addStrict( "/Baz", array( 'marco' => 'polo' ) );
147 $router->add( "/a/$1" );
148 $router->add( "/asdf/$1" );
149 $router->add( "/$2/$1", array( 'unrestricted' => '$2' ) );
150 $router->add( array( 'qwerty' => "/qwerty/$1" ), array( 'qwerty' => '$key' ) );
151 $router->add( "/$2/$1", array( 'restricted-to-y' => '$2' ), array( '$2' => 'y' ) );
152
153 foreach( array(
154 "/Foo" => array( 'title' => "Foo" ),
155 "/Bar" => array( 'ping' => 'pong' ),
156 "/Baz" => array( 'marco' => 'polo' ),
157 "/asdf-foo" => array( 'title' => "qwerty-foo" ),
158 "/qwerty-bar" => array( 'title' => "asdf-bar" ),
159 "/a/Foo" => array( 'title' => "Foo" ),
160 "/asdf/Foo" => array( 'title' => "Foo" ),
161 "/qwerty/Foo" => array( 'title' => "Foo", 'qwerty' => 'qwerty' ),
162 "/baz/Foo" => array( 'title' => "Foo", 'unrestricted' => 'baz' ),
163 "/y/Foo" => array( 'title' => "Foo", 'restricted-to-y' => 'y' ),
164 ) as $path => $result ) {
165 $this->assertEquals( $router->parse( $path ), $result );
166 }
167 }
168
169 /**
170 * Make sure the router handles titles like Special:Recentchanges correctly
171 */
172 public function testSpecial() {
173 $matches = $this->basicRouter->parse( "/wiki/Special:Recentchanges" );
174 $this->assertEquals( $matches, array( 'title' => "Special:Recentchanges" ) );
175 }
176
177 /**
178 * Make sure the router decodes urlencoding properly
179 */
180 public function testUrlencoding() {
181 $matches = $this->basicRouter->parse( "/wiki/Title_With%20Space" );
182 $this->assertEquals( $matches, array( 'title' => "Title_With Space" ) );
183 }
184
185 public function dataRegexpChars() {
186 return array(
187 array( "$" ),
188 array( "$1" ),
189 array( "\\" ),
190 array( "\\$1" ),
191 );
192 }
193
194 /**
195 * Make sure the router doesn't break on special characters like $ used in regexp replacements
196 * @dataProvider dataRegexpChars
197 */
198 public function testRegexpChars( $char ) {
199 $matches = $this->basicRouter->parse( "/wiki/$char" );
200 $this->assertEquals( $matches, array( 'title' => "$char" ) );
201 }
202
203 /**
204 * Make sure the router handles characters like +&() properly
205 */
206 public function testCharacters() {
207 $matches = $this->basicRouter->parse( "/wiki/Plus+And&Dollar\\Stuff();[]{}*" );
208 $this->assertEquals( $matches, array( 'title' => "Plus+And&Dollar\\Stuff();[]{}*" ) );
209 }
210
211 /**
212 * Make sure the router handles unicode characters correctly
213 * @depends testSpecial
214 * @depends testUrlencoding
215 * @depends testCharacters
216 */
217 public function testUnicode() {
218 $matches = $this->basicRouter->parse( "/wiki/Spécial:Modifications_récentes" );
219 $this->assertEquals( $matches, array( 'title' => "Spécial:Modifications_récentes" ) );
220
221 $matches = $this->basicRouter->parse( "/wiki/Sp%C3%A9cial:Modifications_r%C3%A9centes" );
222 $this->assertEquals( $matches, array( 'title' => "Spécial:Modifications_récentes" ) );
223 }
224
225 /**
226 * Ensure the router doesn't choke on long paths.
227 */
228 public function testLength() {
229 $matches = $this->basicRouter->parse( "/wiki/Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum." );
230 $this->assertEquals( $matches, array( 'title' => "Lorem_ipsum_dolor_sit_amet,_consectetur_adipisicing_elit,_sed_do_eiusmod_tempor_incididunt_ut_labore_et_dolore_magna_aliqua._Ut_enim_ad_minim_veniam,_quis_nostrud_exercitation_ullamco_laboris_nisi_ut_aliquip_ex_ea_commodo_consequat._Duis_aute_irure_dolor_in_reprehenderit_in_voluptate_velit_esse_cillum_dolore_eu_fugiat_nulla_pariatur._Excepteur_sint_occaecat_cupidatat_non_proident,_sunt_in_culpa_qui_officia_deserunt_mollit_anim_id_est_laborum." ) );
231 }
232
233
234 /**
235 * Ensure that the php passed site of parameter values are not urldecoded
236 */
237 public function testPatternUrlencoding() {
238 $router = new PathRouter;
239 $router->add( "/wiki/$1", array( 'title' => '%20:$1' ) );
240 $matches = $router->parse( "/wiki/Foo" );
241 $this->assertEquals( $matches, array( 'title' => '%20:Foo' ) );
242 }
243
244 /**
245 * Ensure that raw parameter values do not have any variable replacements or urldecoding
246 */
247 public function testRawParamValue() {
248 $router = new PathRouter;
249 $router->add( "/wiki/$1", array( 'title' => array( 'value' => 'bar%20$1' ) ) );
250 $matches = $router->parse( "/wiki/Foo" );
251 $this->assertEquals( $matches, array( 'title' => 'bar%20$1' ) );
252 }
253
254 }