2 // $Id: SpamTest.php,v 1.17 2007/06/23 15:21:32 henoheno Exp $
3 // Copyright (C) 2007 heno
5 // Design test case for spam.php (called from runner.php)
7 if (! defined('SPAM_INI_FILE')) define('SPAM_INI_FILE', 'spam.ini.php');
9 require_once('spam.php');
10 require_once('PHPUnit/PHPUnit.php');
12 class SpamTest extends PHPUnit_TestCase
14 function setup_string_null()
20 '[array(foobar)]' => array('foobar'),
27 function testFunc_strings()
30 $this->assertEquals('', strings(NULL, 0));
31 $this->assertEquals('', strings(TRUE, 0));
32 $this->assertEquals('', strings(FALSE, 0));
33 $this->assertEquals('', strings('', 0));
34 $this->assertEquals('0', strings(0, 0));
35 $this->assertEquals('1', strings(1, 0));
43 $test = $t1 . $t2 . $t3 . $t4 . $t5;
46 $this->assertEquals($t1 . $t2 . $t3 . $t4 . $t5, strings($test, -1));
47 $this->assertEquals($t1 . $t2 . $t3 . $t4 . $t5, strings($test, 0));
48 $this->assertEquals($t1 . $t2 . $t3 . $t4 . $t5, strings($test, 1));
49 $this->assertEquals( $t2 . $t3 . $t4 . $t5, strings($test, 2));
50 $this->assertEquals( $t3 . $t4 . $t5, strings($test, 3));
51 $this->assertEquals( $t4 . $t5, strings($test, 4));
52 $this->assertEquals( $t4 . $t5, strings($test)); // Default
53 $this->assertEquals( $t5, strings($test, 5));
55 // Preserve the last newline
56 $this->assertEquals($t4 . $t5, strings($test , 4));
57 $this->assertEquals($t4 . $t5 . "\n", strings($test . "\n", 4));
59 // Ignore sequential spaces, and spaces at the beginning/end of lines
60 $test = ' A' . ' ' . ' ' . 'B ';
61 $this->assertEquals($test, strings($test, 0, FALSE));
62 $this->assertEquals('A B', strings($test, 0, TRUE ));
65 function testFunc_array_count_leaves()
67 // Empty array = 0, if option is not set
69 $this->assertEquals(0, array_count_leaves($array, FALSE));
70 $this->assertEquals(1, array_count_leaves($array, TRUE));
76 $this->assertEquals(0, array_count_leaves($array, FALSE));
77 $this->assertEquals(1, array_count_leaves($array, TRUE));
80 foreach(array(NULL, TRUE, FALSE, -1, 0, 1, '', 'foobar') as $value) {
81 $this->assertEquals(1, array_count_leaves($value, FALSE));
82 $this->assertEquals(1, array_count_leaves($value, TRUE));
89 array(), // Empty array
95 'k3' => array(), // Empty array
100 'k6' => array(), // Empty array
105 'k9' => array(), // Empty array
110 'k12' => array(), // Empty array
113 $this->assertEquals(14, array_count_leaves($array, FALSE));
114 $this->assertEquals(19, array_count_leaves($array, TRUE));
117 function testPhpFunc_array_unique()
119 $this->assertEquals(array(1), array_unique(array(1, 1)));
121 // Keys are preserved, array()s inside are preserved
123 array(0, 2 => array(1, 1), 3 => 2),
125 array(0, 0, array(1, 1), 2, 2)
129 // Keys are preserved
131 array(0, 2 => array(1, 1), 3 => 2),
132 array_unique(array(0, 0, array(1, 1), 2, 2))
135 // ONLY the first array() is preserved
137 array(0 => array(1, 1)),
138 array_unique(array_unique(array(0 => array(1, 1), 'a' => array(2,2), 'b' => array(3, 3))))
142 // And array_unique_recursive()
143 function testPhPFunc_array_merge_recursive()
147 $result = array_merge_recursive($array1, $array2);
148 $this->assertEquals(array(1, 1), $result);
149 $result = array_unique_recursive($result);
150 $this->assertEquals(array(1), $result);
154 $result = array(2, 1);
155 $this->assertEquals($result, array_merge_recursive($array1, $array2));
157 // All NUMERIC keys are always renumbered from 0
158 $array1 = array('10' => 'f3');
159 $array2 = array('10' => 'f4');
160 $result = array('f3', 'f4');
161 $this->assertEquals($result, array_merge_recursive($array1, $array2));
163 // One more thing ...
164 $array1 = array('20' => 'f5');
166 $result = array('f5');
167 $this->assertEquals($result, array_merge_recursive($array1, $array2));
169 // Non-numeric keys and values will be marged as you think
170 $array1 = array('a' => 'f1');
171 $array2 = array('a' => 'f2');
172 $result = array('a' => array('f1', 'f2'));
173 $this->assertEquals($result, array_merge_recursive($array1, $array2));
175 // Non-numeric keys: An array and a value will be marged
176 $array1 = array('b' => array('k1'));
177 $array2 = array('b' => 'k2');
178 $result = array('b' => array('k1', 'k2'));
179 $this->assertEquals($result, array_merge_recursive($array1, $array2));
210 $this->assertEquals($result, array_merge_recursive($array1, $array2));
212 // Values will not be unique
213 $array1 = array(5, 4);
214 $array2 = array(4, 5);
215 $result = array_merge_recursive($array1, $array2);
216 $this->assertEquals(array(5, 4, 4, 5), $result);
217 $result = array_unique_recursive($result);
218 $this->assertEquals(array(5, 4), $result);
220 // One more thing ...
221 $array1 = array('b' => array('k3'));
222 $array2 = array('b' => 'k3');
223 $result = array_merge_recursive($array1, $array2);
224 $this->assertEquals(array('b' => array('k3', 'k3')), $result);
225 $result = array_unique_recursive($result);
226 $this->assertEquals(array('b' => array('k3')), $result);
229 function testFunc_uri_pickup()
231 // 1st argument: Null
232 foreach($this->setup_string_null() as $key => $value){
233 $this->assertEquals(0, count(uri_pickup($value)), $key);
236 // 1st argument: Some
237 $test_string = <<<EOF
238 TTP://wwW.Example.Org#TTP_and_www
239 https://nasty.example.org:443/foo/xxx#port443/slash
240 sftp://foobar.example.org:80/dfsdfs#ftp_bat_port80
241 ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm
242 http://192.168.1.4:443#IPv4
244 $results = uri_pickup_normalize(uri_pickup($test_string));
245 $this->assertEquals(5, count($results));
247 // ttp://wwW.Example.Org:80#TTP_and_www
248 $this->assertEquals('http', $results[0]['scheme']);
249 $this->assertEquals('', $results[0]['userinfo']);
250 $this->assertEquals('example.org', $results[0]['host']);
251 $this->assertEquals('', $results[0]['port']);
252 $this->assertEquals('/', $results[0]['path']);
253 $this->assertEquals('', $results[0]['file']);
254 $this->assertEquals('', $results[0]['query']);
255 $this->assertEquals('ttp_and_www', $results[0]['fragment']);
257 // https://nasty.example.org:443/foo/xxx#port443/slash
258 $this->assertEquals('https', $results[1]['scheme']);
259 $this->assertEquals('', $results[1]['userinfo']);
260 $this->assertEquals('nasty.example.org', $results[1]['host']);
261 $this->assertEquals('', $results[1]['port']);
262 $this->assertEquals('/foo/', $results[1]['path']);
263 $this->assertEquals('xxx', $results[1]['file']);
264 $this->assertEquals('', $results[1]['query']);
265 $this->assertEquals('port443', $results[1]['fragment']);
267 // sftp://foobar.example.org:80/dfsdfs#sftp_bat_port80
268 $this->assertEquals('sftp', $results[2]['scheme']);
269 $this->assertEquals('', $results[2]['userinfo']);
270 $this->assertEquals('foobar.example.org', $results[2]['host']);
271 $this->assertEquals('80', $results[2]['port']);
272 $this->assertEquals('/', $results[2]['path']);
273 $this->assertEquals('dfsdfs', $results[2]['file']);
274 $this->assertEquals('', $results[2]['query']);
275 $this->assertEquals('ftp_bat_port80', $results[2]['fragment']);
277 // ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm
278 $this->assertEquals('ftp', $results[3]['scheme']);
279 $this->assertEquals('cnn.example.com&story=breaking_news', $results[3]['userinfo']);
280 $this->assertEquals('10.0.0.1', $results[3]['host']);
281 $this->assertEquals('', $results[3]['port']);
282 $this->assertEquals('/', $results[3]['path']);
283 $this->assertEquals('top_story.htm', $results[3]['file']);
284 $this->assertEquals('', $results[3]['query']);
285 $this->assertEquals('', $results[3]['fragment']);
288 // Specific tests ----
290 // Divider: Back-slash
291 $test_string = ' http:\\backslash.org\fobar.html ';
292 $results = uri_pickup_normalize(uri_pickup($test_string));
293 $this->assertEquals('backslash.org', $results[0]['host']);
296 $test_string = ' http://under_score.org/fobar.html ';
297 $results = uri_pickup_normalize(uri_pickup($test_string));
298 $this->assertEquals('under_score.org',$results[0]['host']); // Not 'under'
301 $test_string = ' http://192.168.0.1/fobar.html ';
302 $results = uri_pickup_normalize(uri_pickup($test_string));
303 $this->assertEquals('192.168.0.1', $results[0]['host']);
306 $test_string = ' http://_sss/foo.html ';
307 $results = uri_pickup_normalize(uri_pickup($test_string));
308 $this->assertEquals('_sss', $results[0]['host']);
309 $this->assertEquals('foo.html', $results[0]['file']);
312 $test_string = ' http://sss_/foo.html ';
313 $results = uri_pickup_normalize(uri_pickup($test_string));
314 $this->assertEquals('sss_', $results[0]['host']);
315 $this->assertEquals('foo.html', $results[0]['file']);
318 // Specific tests ---- Fails
320 // Divider: Colon only (Too sensitive to capture)
321 $test_string = ' http:colon.org ';
322 $results = uri_pickup_normalize(uri_pickup($test_string));
323 $this->assertEquals(0, count($results));
326 $test_string = ' http://s/foo.html http://ss/foo.html ';
327 $results = uri_pickup_normalize(uri_pickup($test_string));
328 $this->assertEquals(0, count($results));
330 $test_string = ' http://sss/foo.html ';
331 $results = uri_pickup_normalize(uri_pickup($test_string));
332 $this->assertEquals('sss', $results[0]['host']);
333 $this->assertEquals('foo.html', $results[0]['file']);
336 function testFunc_scheme_normalize()
339 foreach($this->setup_string_null() as $key => $value){
340 $this->assertEquals('', scheme_normalize($value), $key);
344 $this->assertEquals('http', scheme_normalize('HTTP'));
347 $this->assertEquals('pop3', scheme_normalize('pop'));
348 $this->assertEquals('nntp', scheme_normalize('news'));
349 $this->assertEquals('imap', scheme_normalize('imap4'));
350 $this->assertEquals('nntps', scheme_normalize('snntp'));
351 $this->assertEquals('nntps', scheme_normalize('snews'));
352 $this->assertEquals('pop3s', scheme_normalize('spop3'));
353 $this->assertEquals('pop3s', scheme_normalize('pops'));
356 $this->assertEquals('http', scheme_normalize('ttp'));
357 $this->assertEquals('https', scheme_normalize('ttps'));
359 // Abbrevs considererd harmless
360 $this->assertEquals('', scheme_normalize('ttp', FALSE));
361 $this->assertEquals('', scheme_normalize('ttps', FALSE));
364 function testFunc_host_normalize()
367 foreach($this->setup_string_null() as $key => $value){
368 $this->assertEquals('', host_normalize($value), $key);
371 // Hostname is case-insensitive
372 $this->assertEquals('example.org', host_normalize('ExAMPle.ORG'));
374 // Cut 'www' (destructive)
375 $this->assertEquals('example.org', host_normalize('WWW.example.org'));
378 function testFunc_port_normalize()
380 $scheme = 'dont_care';
382 // 1st argument: Null
383 $this->assertEquals('', port_normalize(NULL, $scheme));
384 $this->assertEquals('', port_normalize(TRUE, $scheme));
385 $this->assertEquals('', port_normalize(FALSE, $scheme));
386 $this->assertEquals('', port_normalize(array('foobar'), $scheme));
387 $this->assertEquals('', port_normalize('', $scheme));
389 // 1st argument: Known port
390 $this->assertEquals('', port_normalize( -1, $scheme));
391 $this->assertEquals(0, port_normalize( 0, $scheme));
392 $this->assertEquals(1, port_normalize( 1, $scheme));
393 $this->assertEquals('', port_normalize( 21, 'ftp'));
394 $this->assertEquals('', port_normalize( 22, 'ssh'));
395 $this->assertEquals('', port_normalize( 23, 'telnet'));
396 $this->assertEquals('', port_normalize( 25, 'smtp'));
397 $this->assertEquals('', port_normalize( 69, 'tftp'));
398 $this->assertEquals('', port_normalize( 70, 'gopher'));
399 $this->assertEquals('', port_normalize( 79, 'finger'));
400 $this->assertEquals('', port_normalize( 80, 'http'));
401 $this->assertEquals('', port_normalize( 110, 'pop3'));
402 $this->assertEquals('', port_normalize( 115, 'sftp'));
403 $this->assertEquals('', port_normalize( 119, 'nntp'));
404 $this->assertEquals('', port_normalize( 143, 'imap'));
405 $this->assertEquals('', port_normalize( 194, 'irc'));
406 $this->assertEquals('', port_normalize( 210, 'wais'));
407 $this->assertEquals('', port_normalize( 443, 'https'));
408 $this->assertEquals('', port_normalize( 563, 'nntps'));
409 $this->assertEquals('', port_normalize( 873, 'rsync'));
410 $this->assertEquals('', port_normalize( 990, 'ftps'));
411 $this->assertEquals('', port_normalize( 992, 'telnets'));
412 $this->assertEquals('', port_normalize( 993, 'imaps'));
413 $this->assertEquals('', port_normalize( 994, 'ircs'));
414 $this->assertEquals('', port_normalize( 995, 'pop3s'));
415 $this->assertEquals('', port_normalize( 3306, 'mysql'));
416 $this->assertEquals(8080, port_normalize( 8080, $scheme));
417 $this->assertEquals(65535, port_normalize(65535, $scheme));
418 $this->assertEquals(65536, port_normalize(65536, $scheme)); // Seems not invalid in RFC
420 // 1st argument: Invalid type
421 $this->assertEquals('1x', port_normalize('001', $scheme) . 'x');
422 $this->assertEquals('', port_normalize('+0', $scheme));
423 $this->assertEquals('', port_normalize('0-1', $scheme)); // intval() says '0'
424 $this->assertEquals('', port_normalize('str', $scheme));
426 // 2nd and 3rd argument: Null
427 $this->assertEquals(80, port_normalize(80, NULL, TRUE));
428 $this->assertEquals(80, port_normalize(80, TRUE, TRUE));
429 $this->assertEquals(80, port_normalize(80, FALSE, TRUE));
430 $this->assertEquals(80, port_normalize(80, array('foobar'), TRUE));
431 $this->assertEquals(80, port_normalize(80, '', TRUE));
433 // 2nd and 3rd argument: Do $scheme_normalize
434 $this->assertEquals('', port_normalize(80, 'TTP', TRUE));
435 $this->assertEquals('', port_normalize(110, 'POP', TRUE));
436 $this->assertEquals(80, port_normalize(80, 'HTTP', FALSE));
439 function testFunc_path_normalize()
441 // 1st argument: Null
442 foreach($this->setup_string_null() as $key => $value){
443 $this->assertEquals('/', path_normalize($value), $key);
446 // 1st argument: CASE sensitive
447 $this->assertEquals('/ExAMPle', path_normalize('ExAMPle'));
448 $this->assertEquals('/#hoge', path_normalize('#hoge'));
449 $this->assertEquals('/a/b/c/d', path_normalize('/a/b/./c////./d'));
450 $this->assertEquals('/b/', path_normalize('/a/../../../b/'));
453 $this->assertEquals('\\b\\c\\d\\', path_normalize('\\a\\..\\b\\.\\c\\\\.\\d\\', '\\'));
454 $this->assertEquals('str1str3str', path_normalize('str1strstr2str..str3str', 'str'));
455 $this->assertEquals('/do/../nothing/', path_normalize('/do/../nothing/', TRUE));
456 $this->assertEquals('/do/../nothing/', path_normalize('/do/../nothing/', array('a')));
457 $this->assertEquals('', path_normalize(array('a'), array('b')));
460 function testFunc_file_normalize()
462 // 1st argument: Null
463 foreach($this->setup_string_null() as $key => $value){
464 $this->assertEquals('', file_normalize($value), $key);
467 // 1st argument: Cut DirectoryIndexes (Destructive)
487 // Apache 2.0.59 default 'index.html' variants
489 'index.html.cz.iso8859-2',
498 'index.html.he.iso8859-8',
499 'index.html.hr.iso8859-2',
501 'index.html.ja.iso2022-jp',
502 'index.html.ko.euc-kr',
503 'index.html.lb.utf8',
507 'index.html.po.iso8859-2',
510 'index.html.ru.cp866',
511 'index.html.ru.cp-1251',
512 'index.html.ru.iso-ru',
513 'index.html.ru.koi8-r',
514 'index.html.ru.utf8',
516 'index.html.var', // default
517 'index.html.zh-cn.gb2312',
518 'index.html.zh-tw.big5',
520 'index.html.po.iso8859-2',
521 'index.html.zh-tw.big5',
523 'index.ja.en.de.html',
527 'index.html.en.ja.ca.z',
529 $this->assertEquals('', file_normalize($arg));
532 //$this->assertEquals('foo/', file_normalize('foo/index.html'));
534 //$this->assertEquals('ExAMPle', file_normalize('ExAMPle'));
535 //$this->assertEquals('exe.exe', file_normalize('exe.exe'));
536 //$this->assertEquals('sample.html', file_normalize('sample.html.en'));
537 //$this->assertEquals('sample.html', file_normalize('sample.html.pt-br'));
538 //$this->assertEquals('sample.html', file_normalize('sample.html.po.iso8859-2'));
539 //$this->assertEquals('sample.html', file_normalize('sample.html.zh-tw.big5'));
542 function testFunc_query_normalize()
544 // 1st argument: Null
545 foreach($this->setup_string_null() as $key => $value){
546 $this->assertEquals('', query_normalize($value), $key);
549 $this->assertEquals('a=0dd&b&c&d&f=d', query_normalize('&&&&f=d&b&d&c&a=0dd'));
550 $this->assertEquals('eg=foobar', query_normalize('nothing==&eg=dummy&eg=padding&eg=foobar'));
553 function testFunc_generate_glob_regex()
555 // 1st argument: Null
556 foreach($this->setup_string_null() as $key => $value){
557 $this->assertEquals('', generate_glob_regex($value), $key);
560 $this->assertEquals('.*\.txt', generate_glob_regex('*.txt'));
561 $this->assertEquals('A.A', generate_glob_regex('A?A'));
564 function testFunc_generate_host_regex()
566 // 1st argument: Null
567 foreach($this->setup_string_null() as $key => $value){
568 $this->assertEquals('', generate_host_regex($value), $key);
571 $this->assertEquals('localhost', generate_host_regex('localhost'));
572 $this->assertEquals('example\.org', generate_host_regex('example.org'));
573 $this->assertEquals('(?:.*\.)?example\.org', generate_host_regex('.example.org'));
574 $this->assertEquals('.*\.example\.org', generate_host_regex('*.example.org'));
575 $this->assertEquals('.*\..*\.example\.org', generate_host_regex('*.*.example.org'));
576 $this->assertEquals('10\.20\.30\.40', generate_host_regex('10.20.30.40'));
578 // Should match with 192.168.0.0/16
579 //$this->assertEquals('192\.168\.', generate_host_regex('192.168.'));
582 function testFunc_get_blocklist()
584 if (! defined('SPAM_INI_FILE') || ! file_exists(SPAM_INI_FILE)) {
585 $this->fail('SPAM_INI_FILE not defined or not found');
589 // get_blocklist_add()
592 get_blocklist_add($array, 'foo', 'bar');
593 $this->assertEquals(1, count($array));
594 $this->assertEquals('bar', $array['foo']);
596 get_blocklist_add($array, 'hoge', 'fuga');
597 $this->assertEquals(2, count($array));
598 $this->assertEquals('bar', $array['foo']);
599 $this->assertEquals('fuga', $array['hoge']);
601 get_blocklist_add($array, -1, '*.txt');
602 $this->assertEquals(3, count($array));
603 $this->assertEquals('bar', $array['foo']);
604 $this->assertEquals('fuga', $array['hoge']);
605 $this->assertEquals('/^.*\.txt$/i', $array['*.txt']);
609 $array = get_blocklist();
610 $this->assertTrue(isset($array['C']));
611 $this->assertTrue(isset($array['goodhost']));
613 $array = get_blocklist('B-1');
614 $this->assertTrue(isset($array['*.blogspot.com']));
616 $array = get_blocklist('goodhost');
617 $this->assertTrue(isset($array['IANA-examples']));
621 function testFunc_whois_responsibility()
623 // 1st argument: Null
624 foreach($this->setup_string_null() as $key => $value){
625 $this->assertEquals('', whois_responsibility($value), $key);
628 // 'act.edu.au' is known as 3rd level domain
629 $this->AssertEquals('bar.act.edu.au', whois_responsibility('foo.bar.act.edu.au'));
630 $this->AssertEquals('bar.act.edu.au', whois_responsibility('bar.act.edu.au'));
631 $this->AssertEquals('act.edu.au', whois_responsibility('act.edu.au'));
632 $this->AssertEquals('edu.au', whois_responsibility('edu.au'));
633 $this->AssertEquals('au', whois_responsibility('au'));
635 // 'co.uk' is known as 2nd level domain
636 $this->AssertEquals('bar.co.uk', whois_responsibility('foo.bar.co.uk'));
637 $this->AssertEquals('bar.co.uk', whois_responsibility('bar.co.uk'));
638 $this->AssertEquals('co.uk', whois_responsibility('co.uk'));
639 $this->AssertEquals('uk', whois_responsibility('uk'));
641 // 'bar.uk' is not 2nd level (implicit responsibility)
642 $this->AssertEquals('bar.uk', whois_responsibility('foo.bar.uk'));
643 $this->AssertEquals('bar.uk', whois_responsibility('bar.uk'));
646 $this->AssertEquals('192.168.0.1', whois_responsibility('192.168.0.1'));
648 // Invalid Top-Level Domain (With implicit)
649 $this->AssertEquals('bar.local', whois_responsibility('foo.bar.local')); // Implicit responsibility
650 $this->AssertEquals('bar.local', whois_responsibility('bar.local'));
651 $this->AssertEquals('local', whois_responsibility('local'));
652 $this->AssertEquals('localhost', whois_responsibility('localhost'));
653 $this->AssertEquals('s', whois_responsibility('s'));