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