Merge "Add rc.unpatrolled to the recentchanges API"
[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( '3735931646', '222.173.202.254' ),
301 array( pow( 2, 32 ) - 1, '255.255.255.255' ),
302 array( false, 'IN.VA.LI.D' ),
303 array( 1, '::1' ),
304 array( '42540766452641154071740215577757643572', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
305 array( '42540766452641154071740215577757643572', '2001:db8:85a3::8a2e:0370:7334' ),
306 array( false, 'IN:VA::LI:D' ),
307 array( false, ':::1' )
308 );
309 }
310
311 /**
312 * @covers IP::toHex
313 * @dataProvider provideToHex
314 */
315 public function testToHex( $expected, $input ) {
316 $result = IP::toHex( $input );
317 $this->assertTrue( $result === false || is_string( $result ) );
318 $this->assertEquals( $expected, $result );
319 }
320
321 /**
322 * Provider for IP::testToHex()
323 */
324 public static function provideToHex() {
325 return array(
326 array( '00000001', '0.0.0.1' ),
327 array( '01020304', '1.2.3.4' ),
328 array( '7F000001', '127.0.0.1' ),
329 array( '80000000', '128.0.0.0' ),
330 array( 'DEADCAFE', '222.173.202.254' ),
331 array( 'FFFFFFFF', '255.255.255.255' ),
332 array( false, 'IN.VA.LI.D' ),
333 array( 'v6-00000000000000000000000000000001', '::1' ),
334 array( 'v6-20010DB885A3000000008A2E03707334', '2001:0db8:85a3:0000:0000:8a2e:0370:7334' ),
335 array( 'v6-20010DB885A3000000008A2E03707334', '2001:db8:85a3::8a2e:0370:7334' ),
336 array( false, 'IN:VA::LI:D' ),
337 array( false, ':::1' )
338 );
339 }
340
341 /**
342 * @covers IP::isPublic
343 */
344 public function testPrivateIPs() {
345 $private = array( 'fc00::3', 'fc00::ff', '::1', '10.0.0.1', '172.16.0.1', '192.168.0.1' );
346 foreach ( $private as $p ) {
347 $this->assertFalse( IP::isPublic( $p ), "$p is not a public IP address" );
348 }
349 $public = array( '2001:5c0:1000:a::133', 'fc::3', '00FC::' );
350 foreach ( $public as $p ) {
351 $this->assertTrue( IP::isPublic( $p ), "$p is a public IP address" );
352 }
353 }
354
355 // Private wrapper used to test CIDR Parsing.
356 private function assertFalseCIDR( $CIDR, $msg = '' ) {
357 $ff = array( false, false );
358 $this->assertEquals( $ff, IP::parseCIDR( $CIDR ), $msg );
359 }
360
361 // Private wrapper to test network shifting using only dot notation
362 private function assertNet( $expected, $CIDR ) {
363 $parse = IP::parseCIDR( $CIDR );
364 $this->assertEquals( $expected, long2ip( $parse[0] ), "network shifting $CIDR" );
365 }
366
367 /**
368 * @covers IP::hexToQuad
369 */
370 public function testHexToQuad() {
371 $this->assertEquals( '0.0.0.1', IP::hexToQuad( '00000001' ) );
372 $this->assertEquals( '255.0.0.0', IP::hexToQuad( 'FF000000' ) );
373 $this->assertEquals( '255.255.255.255', IP::hexToQuad( 'FFFFFFFF' ) );
374 $this->assertEquals( '10.188.222.255', IP::hexToQuad( '0ABCDEFF' ) );
375 // hex not left-padded...
376 $this->assertEquals( '0.0.0.0', IP::hexToQuad( '0' ) );
377 $this->assertEquals( '0.0.0.1', IP::hexToQuad( '1' ) );
378 $this->assertEquals( '0.0.0.255', IP::hexToQuad( 'FF' ) );
379 $this->assertEquals( '0.0.255.0', IP::hexToQuad( 'FF00' ) );
380 }
381
382 /**
383 * @covers IP::hexToOctet
384 */
385 public function testHexToOctet() {
386 $this->assertEquals( '0:0:0:0:0:0:0:1',
387 IP::hexToOctet( '00000000000000000000000000000001' ) );
388 $this->assertEquals( '0:0:0:0:0:0:FF:3',
389 IP::hexToOctet( '00000000000000000000000000FF0003' ) );
390 $this->assertEquals( '0:0:0:0:0:0:FF00:6',
391 IP::hexToOctet( '000000000000000000000000FF000006' ) );
392 $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF',
393 IP::hexToOctet( '000000000000000000000000FCCFFAFF' ) );
394 $this->assertEquals( 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
395 IP::hexToOctet( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' ) );
396 // hex not left-padded...
397 $this->assertEquals( '0:0:0:0:0:0:0:0', IP::hexToOctet( '0' ) );
398 $this->assertEquals( '0:0:0:0:0:0:0:1', IP::hexToOctet( '1' ) );
399 $this->assertEquals( '0:0:0:0:0:0:0:FF', IP::hexToOctet( 'FF' ) );
400 $this->assertEquals( '0:0:0:0:0:0:0:FFD0', IP::hexToOctet( 'FFD0' ) );
401 $this->assertEquals( '0:0:0:0:0:0:FA00:0', IP::hexToOctet( 'FA000000' ) );
402 $this->assertEquals( '0:0:0:0:0:0:FCCF:FAFF', IP::hexToOctet( 'FCCFFAFF' ) );
403 }
404
405 /**
406 * IP::parseCIDR() returns an array containing a signed IP address
407 * representing the network mask and the bit mask.
408 * @covers IP::parseCIDR
409 */
410 public function testCIDRParsing() {
411 $this->assertFalseCIDR( '192.0.2.0', "missing mask" );
412 $this->assertFalseCIDR( '192.0.2.0/', "missing bitmask" );
413
414 // Verify if statement
415 $this->assertFalseCIDR( '256.0.0.0/32', "invalid net" );
416 $this->assertFalseCIDR( '192.0.2.0/AA', "mask not numeric" );
417 $this->assertFalseCIDR( '192.0.2.0/-1', "mask < 0" );
418 $this->assertFalseCIDR( '192.0.2.0/33', "mask > 32" );
419
420 // Check internal logic
421 # 0 mask always result in array(0,0)
422 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '192.0.0.2/0' ) );
423 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '0.0.0.0/0' ) );
424 $this->assertEquals( array( 0, 0 ), IP::parseCIDR( '255.255.255.255/0' ) );
425
426 // @todo FIXME: Add more tests.
427
428 # This part test network shifting
429 $this->assertNet( '192.0.0.0', '192.0.0.2/24' );
430 $this->assertNet( '192.168.5.0', '192.168.5.13/24' );
431 $this->assertNet( '10.0.0.160', '10.0.0.161/28' );
432 $this->assertNet( '10.0.0.0', '10.0.0.3/28' );
433 $this->assertNet( '10.0.0.0', '10.0.0.3/30' );
434 $this->assertNet( '10.0.0.4', '10.0.0.4/30' );
435 $this->assertNet( '172.17.32.0', '172.17.35.48/21' );
436 $this->assertNet( '10.128.0.0', '10.135.0.0/9' );
437 $this->assertNet( '134.0.0.0', '134.0.5.1/8' );
438 }
439
440 /**
441 * @covers IP::canonicalize
442 */
443 public function testIPCanonicalizeOnValidIp() {
444 $this->assertEquals( '192.0.2.152', IP::canonicalize( '192.0.2.152' ),
445 'Canonicalization of a valid IP returns it unchanged' );
446 }
447
448 /**
449 * @covers IP::canonicalize
450 */
451 public function testIPCanonicalizeMappedAddress() {
452 $this->assertEquals(
453 '192.0.2.152',
454 IP::canonicalize( '::ffff:192.0.2.152' )
455 );
456 $this->assertEquals(
457 '192.0.2.152',
458 IP::canonicalize( '::192.0.2.152' )
459 );
460 }
461
462 /**
463 * Issues there are most probably from IP::toHex() or IP::parseRange()
464 * @covers IP::isInRange
465 * @dataProvider provideIPsAndRanges
466 */
467 public function testIPIsInRange( $expected, $addr, $range, $message = '' ) {
468 $this->assertEquals(
469 $expected,
470 IP::isInRange( $addr, $range ),
471 $message
472 );
473 }
474
475 /** Provider for testIPIsInRange() */
476 public static function provideIPsAndRanges() {
477 # Format: (expected boolean, address, range, optional message)
478 return array(
479 # IPv4
480 array( true, '192.0.2.0', '192.0.2.0/24', 'Network address' ),
481 array( true, '192.0.2.77', '192.0.2.0/24', 'Simple address' ),
482 array( true, '192.0.2.255', '192.0.2.0/24', 'Broadcast address' ),
483
484 array( false, '0.0.0.0', '192.0.2.0/24' ),
485 array( false, '255.255.255', '192.0.2.0/24' ),
486
487 # IPv6
488 array( false, '::1', '2001:DB8::/32' ),
489 array( false, '::', '2001:DB8::/32' ),
490 array( false, 'FE80::1', '2001:DB8::/32' ),
491
492 array( true, '2001:DB8::', '2001:DB8::/32' ),
493 array( true, '2001:0DB8::', '2001:DB8::/32' ),
494 array( true, '2001:DB8::1', '2001:DB8::/32' ),
495 array( true, '2001:0DB8::1', '2001:DB8::/32' ),
496 array( true, '2001:0DB8:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF',
497 '2001:DB8::/32' ),
498
499 array( false, '2001:0DB8:F::', '2001:DB8::/96' ),
500 );
501 }
502
503 /**
504 * Test for IP::splitHostAndPort().
505 * @dataProvider provideSplitHostAndPort
506 */
507 public function testSplitHostAndPort( $expected, $input, $description ) {
508 $this->assertEquals( $expected, IP::splitHostAndPort( $input ), $description );
509 }
510
511 /**
512 * Provider for IP::splitHostAndPort()
513 */
514 public static function provideSplitHostAndPort() {
515 return array(
516 array( false, '[', 'Unclosed square bracket' ),
517 array( false, '[::', 'Unclosed square bracket 2' ),
518 array( array( '::', false ), '::', 'Bare IPv6 0' ),
519 array( array( '::1', false ), '::1', 'Bare IPv6 1' ),
520 array( array( '::', false ), '[::]', 'Bracketed IPv6 0' ),
521 array( array( '::1', false ), '[::1]', 'Bracketed IPv6 1' ),
522 array( array( '::1', 80 ), '[::1]:80', 'Bracketed IPv6 with port' ),
523 array( false, '::x', 'Double colon but no IPv6' ),
524 array( array( 'x', 80 ), 'x:80', 'Hostname and port' ),
525 array( false, 'x:x', 'Hostname and invalid port' ),
526 array( array( 'x', false ), 'x', 'Plain hostname' )
527 );
528 }
529
530 /**
531 * Test for IP::combineHostAndPort()
532 * @dataProvider provideCombineHostAndPort
533 */
534 public function testCombineHostAndPort( $expected, $input, $description ) {
535 list( $host, $port, $defaultPort ) = $input;
536 $this->assertEquals(
537 $expected,
538 IP::combineHostAndPort( $host, $port, $defaultPort ),
539 $description );
540 }
541
542 /**
543 * Provider for IP::combineHostAndPort()
544 */
545 public static function provideCombineHostAndPort() {
546 return array(
547 array( '[::1]', array( '::1', 2, 2 ), 'IPv6 default port' ),
548 array( '[::1]:2', array( '::1', 2, 3 ), 'IPv6 non-default port' ),
549 array( 'x', array( 'x', 2, 2 ), 'Normal default port' ),
550 array( 'x:2', array( 'x', 2, 3 ), 'Normal non-default port' ),
551 );
552 }
553
554 /**
555 * Test for IP::sanitizeRange()
556 * @dataProvider provideIPCIDRs
557 */
558 public function testSanitizeRange( $input, $expected, $description ) {
559 $this->assertEquals( $expected, IP::sanitizeRange( $input ), $description );
560 }
561
562 /**
563 * Provider for IP::testSanitizeRange()
564 */
565 public static function provideIPCIDRs() {
566 return array(
567 array( '35.56.31.252/16', '35.56.0.0/16', 'IPv4 range' ),
568 array( '135.16.21.252/24', '135.16.21.0/24', 'IPv4 range' ),
569 array( '5.36.71.252/32', '5.36.71.252/32', 'IPv4 silly range' ),
570 array( '5.36.71.252', '5.36.71.252', 'IPv4 non-range' ),
571 array( '0:1:2:3:4:c5:f6:7/96', '0:1:2:3:4:C5:0:0/96', 'IPv6 range' ),
572 array( '0:1:2:3:4:5:6:7/120', '0:1:2:3:4:5:6:0/120', 'IPv6 range' ),
573 array( '0:e1:2:3:4:5:e6:7/128', '0:E1:2:3:4:5:E6:7/128', 'IPv6 silly range' ),
574 array( '0:c1:A2:3:4:5:c6:7', '0:C1:A2:3:4:5:C6:7', 'IPv6 non range' ),
575 );
576 }
577
578 /**
579 * Test for IP::prettifyIP()
580 * @dataProvider provideIPsToPrettify
581 */
582 public function testPrettifyIP( $ip, $prettified ) {
583 $this->assertEquals( $prettified, IP::prettifyIP( $ip ), "Prettify of $ip" );
584 }
585
586 /**
587 * Provider for IP::testPrettifyIP()
588 */
589 public static function provideIPsToPrettify() {
590 return array(
591 array( '0:0:0:0:0:0:0:0', '::' ),
592 array( '0:0:0::0:0:0', '::' ),
593 array( '0:0:0:1:0:0:0:0', '0:0:0:1::' ),
594 array( '0:0::f', '::f' ),
595 array( '0::0:0:0:33:fef:b', '::33:fef:b' ),
596 array( '3f:535:0:0:0:0:e:fbb', '3f:535::e:fbb' ),
597 array( '0:0:fef:0:0:0:e:fbb', '0:0:fef::e:fbb' ),
598 array( 'abbc:2004::0:0:0:0', 'abbc:2004::' ),
599 array( 'cebc:2004:f:0:0:0:0:0', 'cebc:2004:f::' ),
600 array( '0:0:0:0:0:0:0:0/16', '::/16' ),
601 array( '0:0:0::0:0:0/64', '::/64' ),
602 array( '0:0::f/52', '::f/52' ),
603 array( '::0:0:33:fef:b/52', '::33:fef:b/52' ),
604 array( '3f:535:0:0:0:0:e:fbb/48', '3f:535::e:fbb/48' ),
605 array( '0:0:fef:0:0:0:e:fbb/96', '0:0:fef::e:fbb/96' ),
606 array( 'abbc:2004:0:0::0:0/40', 'abbc:2004::/40' ),
607 array( 'aebc:2004:f:0:0:0:0:0/80', 'aebc:2004:f::/80' ),
608 );
609 }
610 }