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