Merge "ChangesListSpecialPage and subclasses: Reorder functions"
[lhc/web/wiklou.git] / tests / phpunit / includes / utils / IPTest.php
1 <?php
2 /**
3 * Tests for IP validity functions.
4 *
5 * Ported from /t/inc/IP.t by avar.
6 *
7 * @group IP
8 * @todo Test methods in this call should be split into a method and a
9 * dataprovider.
10 */
11
12 class IPTest extends MediaWikiTestCase {
13 /**
14 * not sure it should be tested with boolean false. hashar 20100924
15 * @covers IP::isIPAddress
16 */
17 public function testisIPAddress() {
18 $this->assertFalse( IP::isIPAddress( false ), 'Boolean false is not an IP' );
19 $this->assertFalse( IP::isIPAddress( true ), 'Boolean true is not an IP' );
20 $this->assertFalse( IP::isIPAddress( "" ), 'Empty string is not an IP' );
21 $this->assertFalse( IP::isIPAddress( 'abc' ), 'Garbage IP string' );
22 $this->assertFalse( IP::isIPAddress( ':' ), 'Single ":" is not an IP' );
23 $this->assertFalse( IP::isIPAddress( '2001:0DB8::A:1::1' ), 'IPv6 with a double :: occurrence' );
24 $this->assertFalse(
25 IP::isIPAddress( '2001:0DB8::A:1::' ),
26 'IPv6 with a double :: occurrence, last at end'
27 );
28 $this->assertFalse(
29 IP::isIPAddress( '::2001:0DB8::5:1' ),
30 'IPv6 with a double :: occurrence, firt at beginning'
31 );
32 $this->assertFalse( IP::isIPAddress( '124.24.52' ), 'IPv4 not enough quads' );
33 $this->assertFalse( IP::isIPAddress( '24.324.52.13' ), 'IPv4 out of range' );
34 $this->assertFalse( IP::isIPAddress( '.24.52.13' ), 'IPv4 starts with period' );
35 $this->assertFalse( IP::isIPAddress( 'fc:100:300' ), 'IPv6 with only 3 words' );
36
37 $this->assertTrue( IP::isIPAddress( '::' ), 'RFC 4291 IPv6 Unspecified Address' );
38 $this->assertTrue( IP::isIPAddress( '::1' ), 'RFC 4291 IPv6 Loopback Address' );
39 $this->assertTrue( IP::isIPAddress( '74.24.52.13/20', 'IPv4 range' ) );
40 $this->assertTrue( IP::isIPAddress( 'fc:100:a:d:1:e:ac:0/24' ), 'IPv6 range' );
41 $this->assertTrue( IP::isIPAddress( 'fc::100:a:d:1:e:ac/96' ), 'IPv6 range with "::"' );
42
43 $validIPs = array( 'fc:100::', 'fc:100:a:d:1:e:ac::', 'fc::100', '::fc:100:a:d:1:e:ac',
44 '::fc', 'fc::100:a:d:1:e:ac', 'fc:100:a:d:1:e:ac:0', '124.24.52.13', '1.24.52.13' );
45 foreach ( $validIPs as $ip ) {
46 $this->assertTrue( IP::isIPAddress( $ip ), "$ip is a valid IP address" );
47 }
48 }
49
50 /**
51 * @covers IP::isIPv6
52 */
53 public function testisIPv6() {
54 $this->assertFalse( IP::isIPv6( ':fc:100::' ), 'IPv6 starting with lone ":"' );
55 $this->assertFalse( IP::isIPv6( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
56 $this->assertFalse( IP::isIPv6( 'fc:300' ), 'IPv6 with only 2 words' );
57 $this->assertFalse( IP::isIPv6( 'fc:100:300' ), 'IPv6 with only 3 words' );
58
59 $this->assertTrue( IP::isIPv6( 'fc:100::' ) );
60 $this->assertTrue( IP::isIPv6( 'fc:100:a::' ) );
61 $this->assertTrue( IP::isIPv6( 'fc:100:a:d::' ) );
62 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1::' ) );
63 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e::' ) );
64 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac::' ) );
65
66 $this->assertFalse( IP::isIPv6( 'fc:100:a:d:1:e:ac:0::' ), 'IPv6 with 8 words ending with "::"' );
67 $this->assertFalse(
68 IP::isIPv6( 'fc:100:a:d:1:e:ac:0:1::' ),
69 'IPv6 with 9 words ending with "::"'
70 );
71
72 $this->assertFalse( IP::isIPv6( ':::' ) );
73 $this->assertFalse( IP::isIPv6( '::0:' ), 'IPv6 ending in a lone ":"' );
74
75 $this->assertTrue( IP::isIPv6( '::' ), 'IPv6 zero address' );
76 $this->assertTrue( IP::isIPv6( '::0' ) );
77 $this->assertTrue( IP::isIPv6( '::fc' ) );
78 $this->assertTrue( IP::isIPv6( '::fc:100' ) );
79 $this->assertTrue( IP::isIPv6( '::fc:100:a' ) );
80 $this->assertTrue( IP::isIPv6( '::fc:100:a:d' ) );
81 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1' ) );
82 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e' ) );
83 $this->assertTrue( IP::isIPv6( '::fc:100:a:d:1:e:ac' ) );
84
85 $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
86 $this->assertFalse( IP::isIPv6( '::fc:100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
87
88 $this->assertFalse( IP::isIPv6( ':fc::100' ), 'IPv6 starting with lone ":"' );
89 $this->assertFalse( IP::isIPv6( 'fc::100:' ), 'IPv6 ending with lone ":"' );
90 $this->assertFalse( IP::isIPv6( 'fc:::100' ), 'IPv6 with ":::" in the middle' );
91
92 $this->assertTrue( IP::isIPv6( 'fc::100' ), 'IPv6 with "::" and 2 words' );
93 $this->assertTrue( IP::isIPv6( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
94 $this->assertTrue( IP::isIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' ) );
95 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
96 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e' ), 'IPv6 with "::" and 6 words' );
97 $this->assertTrue( IP::isIPv6( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
98 $this->assertTrue( IP::isIPv6( '2001::df' ), 'IPv6 with "::" and 2 words' );
99 $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
100 $this->assertTrue( IP::isIPv6( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
101
102 $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0' ), 'IPv6 with "::" and 8 words' );
103 $this->assertFalse( IP::isIPv6( 'fc::100:a:d:1:e:ac:0:1' ), 'IPv6 with 9 words' );
104
105 $this->assertTrue( IP::isIPv6( 'fc:100:a:d:1:e:ac:0' ) );
106 }
107
108 /**
109 * @covers IP::isIPv4
110 */
111 public function testisIPv4() {
112 $this->assertFalse( IP::isIPv4( false ), 'Boolean false is not an IP' );
113 $this->assertFalse( IP::isIPv4( true ), 'Boolean true is not an IP' );
114 $this->assertFalse( IP::isIPv4( "" ), 'Empty string is not an IP' );
115 $this->assertFalse( IP::isIPv4( 'abc' ) );
116 $this->assertFalse( IP::isIPv4( ':' ) );
117 $this->assertFalse( IP::isIPv4( '124.24.52' ), 'IPv4 not enough quads' );
118 $this->assertFalse( IP::isIPv4( '24.324.52.13' ), 'IPv4 out of range' );
119 $this->assertFalse( IP::isIPv4( '.24.52.13' ), 'IPv4 starts with period' );
120
121 $this->assertTrue( IP::isIPv4( '124.24.52.13' ) );
122 $this->assertTrue( IP::isIPv4( '1.24.52.13' ) );
123 $this->assertTrue( IP::isIPv4( '74.24.52.13/20', 'IPv4 range' ) );
124 }
125
126 /**
127 * @covers IP::isValid
128 */
129 public function testValidIPs() {
130 foreach ( range( 0, 255 ) as $i ) {
131 $a = sprintf( "%03d", $i );
132 $b = sprintf( "%02d", $i );
133 $c = sprintf( "%01d", $i );
134 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
135 $ip = "$f.$f.$f.$f";
136 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv4 address" );
137 }
138 }
139 foreach ( range( 0x0, 0xFFFF, 0xF ) as $i ) {
140 $a = sprintf( "%04x", $i );
141 $b = sprintf( "%03x", $i );
142 $c = sprintf( "%02x", $i );
143 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
144 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
145 $this->assertTrue( IP::isValid( $ip ), "$ip is a valid IPv6 address" );
146 }
147 }
148 // test with some abbreviations
149 $this->assertFalse( IP::isValid( ':fc:100::' ), 'IPv6 starting with lone ":"' );
150 $this->assertFalse( IP::isValid( 'fc:100:::' ), 'IPv6 ending with a ":::"' );
151 $this->assertFalse( IP::isValid( 'fc:300' ), 'IPv6 with only 2 words' );
152 $this->assertFalse( IP::isValid( 'fc:100:300' ), 'IPv6 with only 3 words' );
153
154 $this->assertTrue( IP::isValid( 'fc:100::' ) );
155 $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e::' ) );
156 $this->assertTrue( IP::isValid( 'fc:100:a:d:1:e:ac::' ) );
157
158 $this->assertTrue( IP::isValid( 'fc::100' ), 'IPv6 with "::" and 2 words' );
159 $this->assertTrue( IP::isValid( 'fc::100:a' ), 'IPv6 with "::" and 3 words' );
160 $this->assertTrue( IP::isValid( '2001::df' ), 'IPv6 with "::" and 2 words' );
161 $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df' ), 'IPv6 with "::" and 5 words' );
162 $this->assertTrue( IP::isValid( '2001:5c0:1400:a::df:2' ), 'IPv6 with "::" and 6 words' );
163 $this->assertTrue( IP::isValid( 'fc::100:a:d:1' ), 'IPv6 with "::" and 5 words' );
164 $this->assertTrue( IP::isValid( 'fc::100:a:d:1:e:ac' ), 'IPv6 with "::" and 7 words' );
165
166 $this->assertFalse(
167 IP::isValid( 'fc:100:a:d:1:e:ac:0::' ),
168 'IPv6 with 8 words ending with "::"'
169 );
170 $this->assertFalse(
171 IP::isValid( 'fc:100:a:d:1:e:ac:0:1::' ),
172 'IPv6 with 9 words ending with "::"'
173 );
174 }
175
176 /**
177 * @covers IP::isValid
178 */
179 public function testInvalidIPs() {
180 // Out of range...
181 foreach ( range( 256, 999 ) as $i ) {
182 $a = sprintf( "%03d", $i );
183 $b = sprintf( "%02d", $i );
184 $c = sprintf( "%01d", $i );
185 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
186 $ip = "$f.$f.$f.$f";
187 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv4 address" );
188 }
189 }
190 foreach ( range( 'g', 'z' ) as $i ) {
191 $a = sprintf( "%04s", $i );
192 $b = sprintf( "%03s", $i );
193 $c = sprintf( "%02s", $i );
194 foreach ( array_unique( array( $a, $b, $c ) ) as $f ) {
195 $ip = "$f:$f:$f:$f:$f:$f:$f:$f";
196 $this->assertFalse( IP::isValid( $ip ), "$ip is not a valid IPv6 address" );
197 }
198 }
199 // Have CIDR
200 $ipCIDRs = array(
201 '212.35.31.121/32',
202 '212.35.31.121/18',
203 '212.35.31.121/24',
204 '::ff:d:321:5/96',
205 'ff::d3:321:5/116',
206 'c:ff:12:1:ea:d:321:5/120',
207 );
208 foreach ( $ipCIDRs as $i ) {
209 $this->assertFalse( IP::isValid( $i ),
210 "$i is an invalid IP address because it is a block" );
211 }
212 // Incomplete/garbage
213 $invalid = array(
214 'www.xn--var-xla.net',
215 '216.17.184.G',
216 '216.17.184.1.',
217 '216.17.184',
218 '216.17.184.',
219 '256.17.184.1'
220 );
221 foreach ( $invalid as $i ) {
222 $this->assertFalse( IP::isValid( $i ), "$i is an invalid IP address" );
223 }
224 }
225
226 /**
227 * @covers IP::isValidBlock
228 */
229 public function testValidBlocks() {
230 $valid = array(
231 '116.17.184.5/32',
232 '0.17.184.5/30',
233 '16.17.184.1/24',
234 '30.242.52.14/1',
235 '10.232.52.13/8',
236 '30.242.52.14/0',
237 '::e:f:2001/96',
238 '::c:f:2001/128',
239 '::10:f:2001/70',
240 '::fe:f:2001/1',
241 '::6d:f:2001/8',
242 '::fe:f:2001/0',
243 );
244 foreach ( $valid as $i ) {
245 $this->assertTrue( IP::isValidBlock( $i ), "$i is a valid IP block" );
246 }
247 }
248
249 /**
250 * @covers IP::isValidBlock
251 */
252 public function testInvalidBlocks() {
253 $invalid = array(
254 '116.17.184.5/33',
255 '0.17.184.5/130',
256 '16.17.184.1/-1',
257 '10.232.52.13/*',
258 '7.232.52.13/ab',
259 '11.232.52.13/',
260 '::e:f:2001/129',
261 '::c:f:2001/228',
262 '::10:f:2001/-1',
263 '::6d:f:2001/*',
264 '::86:f:2001/ab',
265 '::23:f:2001/',
266 );
267 foreach ( $invalid as $i ) {
268 $this->assertFalse( IP::isValidBlock( $i ), "$i is not a valid IP block" );
269 }
270 }
271
272 /**
273 * Improve IP::sanitizeIP() code coverage
274 * @todo Most probably incomplete
275 */
276 public function testSanitizeIP() {
277 $this->assertNull( IP::sanitizeIP( '' ) );
278 $this->assertNull( IP::sanitizeIP( ' ' ) );
279 }
280
281 /**
282 * @covers IP::toUnsigned
283 * @dataProvider provideToUnsigned
284 */
285 public function testToUnsigned( $expected, $input ) {
286 $result = IP::toUnsigned( $input );
287 $this->assertTrue( $result === false || is_string( $result ) || is_int( $result ) );
288 $this->assertEquals( $expected, $result );
289 }
290
291 /**
292 * Provider for IP::testToUnsigned()
293 */
294 public static function provideToUnsigned() {
295 return array(
296 array( 1, '0.0.0.1' ),
297 array( 16909060, '1.2.3.4' ),
298 array( 2130706433, '127.0.0.1' ),
299 array( '2147483648', '128.0.0.0' ),
300 array( 2130706440, '127.0.0.08' ),
301 array( 2130706441, '127.0.0.09' ),
302 array( '3735931646', '222.173.202.254' ),
303 array( pow( 2, 32 ) - 1, '255.255.255.255' ),
304 array( false, 'IN.VA.LI.D' ),
305 array( 1, '::1' ),
306 array( '42540766452641154071740215577757643572', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
307 array( '42540766452641154071740215577757643572', '2001:db8:85a3::8a2e:0370:7334' ),
308 array( false, 'IN:VA::LI:D' ),
309 array( false, ':::1' )
310 );
311 }
312
313 /**
314 * @covers IP::toHex
315 * @dataProvider provideToHex
316 */
317 public function testToHex( $expected, $input ) {
318 $result = IP::toHex( $input );
319 $this->assertTrue( $result === false || is_string( $result ) );
320 $this->assertEquals( $expected, $result );
321 }
322
323 /**
324 * Provider for IP::testToHex()
325 */
326 public static function provideToHex() {
327 return array(
328 array( '00000001', '0.0.0.1' ),
329 array( '01020304', '1.2.3.4' ),
330 array( '7F000001', '127.0.0.1' ),
331 array( '80000000', '128.0.0.0' ),
332 array( 'DEADCAFE', '222.173.202.254' ),
333 array( 'FFFFFFFF', '255.255.255.255' ),
334 array( false, 'IN.VA.LI.D' ),
335 array( 'v6-00000000000000000000000000000001', '::1' ),
336 array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
337 array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
338 array( false, 'IN:VA::LI:D' ),
339 array( false, ':::1' )
340 );
341 }
342
343 /**
344 * @covers IP::isPublic
345 */
346 public function testPrivateIPs() {
347 $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
348 foreach ( $private as $p ) {
349 $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
350 }
351 $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
352 foreach ( $public as $p ) {
353 $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
354 }
355 }
356
357 // Private wrapper used to test CIDR Parsing.
358 private function assertFalseCIDR( $CIDR, $msg = '' ) {
359 $ff = array( false, false );
360 $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
361 }
362
363 // Private wrapper to test network shifting using only dot notation
364 private function assertNet( $expected, $CIDR ) {
365 $parse = IP::parseCIDR( $CIDR );
366 $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
367 }
368
369 /**
370 * @covers IP::hexToQuad
371 */
372 public function testHexToQuad() {
373 $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
374 $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
375 $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
376 $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
377 // hex not left-padded...
378 $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
379 $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
380 $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
381 $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
382 }
383
384 /**
385 * @covers IP::hexToOctet
386 */
387 public function testHexToOctet() {
388 $this->assertEquals( '0:0:0:0:0:0:0:1',
389 IP::hexToOctet( '00000000000000000000000000000001' ) );
390 $this->assertEquals( '0:0:0:0:0:0:FF:3',
391 IP::hexToOctet( '00000000000000000000000000FF0003' ) );
392 $this->assertEquals( '0:0:0:0:0:0:FF00:6',
393 IP::hexToOctet( '000000000000000000000000FF000006' ) );
394 $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
395 IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
396 $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
397 IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
398 // hex not left-padded...
399 $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
400 $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
401 $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
402 $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
403 $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
404 $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
405 }
406
407 /**
408 * IP::parseCIDR() returns an array containing a signed IP address
409 * representing the network mask and the bit mask.
410 * @covers IP::parseCIDR
411 */
412 public function testCIDRParsing() {
413 $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
414 $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
415
416 // Verify if statement
417 $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
418 $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
419 $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
420 $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
421
422 // Check internal logic
423 # 0 mask always result in array(0,0)
424 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
425 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
426 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
427
428 // @todo FIXME: Add more tests.
429
430 # This part test network shifting
431 $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
432 $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
433 $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
434 $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
435 $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
436 $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
437 $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
438 $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
439 $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
440 }
441
442 /**
443 * @covers IP::canonicalize
444 */
445 public function testIPCanonicalizeOnValidIp() {
446 $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
447 'Canonicalization of a valid IP returns it unchanged' );
448 }
449
450 /**
451 * @covers IP::canonicalize
452 */
453 public function testIPCanonicalizeMappedAddress() {
454 $this->assertEquals(
455 '192.0.2.152',
456 IP::canonicalize( '::ffff:192.0.2.152' )
457 );
458 $this->assertEquals(
459 '192.0.2.152',
460 IP::canonicalize( '::192.0.2.152' )
461 );
462 }
463
464 /**
465 * Issues there are most probably from IP::toHex() or IP::parseRange()
466 * @covers IP::isInRange
467 * @dataProvider provideIPsAndRanges
468 */
469 public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
470 $this->assertEquals(
471 $expected,
472 IP::isInRange( $addr, $range ),
473 $message
474 );
475 }
476
477 /** Provider for testIPIsInRange() */
478 public static function provideIPsAndRanges() {
479 # Format: (expected boolean, address, range, optional message)
480 return array(
481 # IPv4
482 array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
483 array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
484 array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
485
486 array( false, '0.0.0.0', '192.0.2.0/24' ),
487 array( false, '255.255.255', '192.0.2.0/24' ),
488
489 # IPv6
490 array( false, '::1', '2001:DB8::/32' ),
491 array( false, '::', '2001:DB8::/32' ),
492 array( false, 'FE80::1', '2001:DB8::/32' ),
493
494 array( true, '2001:DB8::', '2001:DB8::/32' ),
495 array( true, '2001:0DB8::', '2001:DB8::/32' ),
496 array( true, '2001:DB8::1', '2001:DB8::/32' ),
497 array( true, '2001:0DB8::1', '2001:DB8::/32' ),
498 array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
499 '2001:DB8::/32' ),
500
501 array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
502 );
503 }
504
505 /**
506 * Test for IP::splitHostAndPort().
507 * @dataProvider provideSplitHostAndPort
508 */
509 public function testSplitHostAndPort( $expected, $input, $description ) {
510 $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
511 }
512
513 /**
514 * Provider for IP::splitHostAndPort()
515 */
516 public static function provideSplitHostAndPort() {
517 return array(
518 array( false, '[', 'Unclosed square bracket' ),
519 array( false, '[::', 'Unclosed square bracket 2' ),
520 array( array( '::', false ), '::', 'Bare IPv6 0' ),
521 array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
522 array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
523 array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
524 array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
525 array( false, '::x', 'Double colon but no IPv6' ),
526 array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
527 array( false, 'x:x', 'Hostname and invalid port' ),
528 array( array( 'x', false ), 'x', 'Plain hostname' )
529 );
530 }
531
532 /**
533 * Test for IP::combineHostAndPort()
534 * @dataProvider provideCombineHostAndPort
535 */
536 public function testCombineHostAndPort( $expected, $input, $description ) {
537 list( $host, $port, $defaultPort ) = $input;
538 $this->assertEquals(
539 $expected,
540 IP::combineHostAndPort( $host, $port, $defaultPort ),
541 $description );
542 }
543
544 /**
545 * Provider for IP::combineHostAndPort()
546 */
547 public static function provideCombineHostAndPort() {
548 return array(
549 array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
550 array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
551 array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
552 array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
553 );
554 }
555
556 /**
557 * Test for IP::sanitizeRange()
558 * @dataProvider provideIPCIDRs
559 */
560 public function testSanitizeRange( $input, $expected, $description ) {
561 $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
562 }
563
564 /**
565 * Provider for IP::testSanitizeRange()
566 */
567 public static function provideIPCIDRs() {
568 return array(
569 array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
570 array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
571 array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
572 array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
573 array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
574 array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
575 array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
576 array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
577 );
578 }
579
580 /**
581 * Test for IP::prettifyIP()
582 * @dataProvider provideIPsToPrettify
583 */
584 public function testPrettifyIP( $ip, $prettified ) {
585 $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
586 }
587
588 /**
589 * Provider for IP::testPrettifyIP()
590 */
591 public static function provideIPsToPrettify() {
592 return array(
593 array( '0:0:0:0:0:0:0:0', '::' ),
594 array( '0:0:0::0:0:0', '::' ),
595 array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
596 array( '0:0::f', '::f' ),
597 array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
598 array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
599 array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
600 array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
601 array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
602 array( '0:0:0:0:0:0:0:0/16', '::/16' ),
603 array( '0:0:0::0:0:0/64', '::/64' ),
604 array( '0:0::f/52', '::f/52' ),
605 array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
606 array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
607 array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
608 array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
609 array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
610 );
611 }
612 }