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