summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorUrban Müller2018-06-21 16:42:32 +0200
committerUrban Müller2018-06-21 16:43:32 +0200
commit3dabbbd5325c9fad9582cd44b1da68dece78eaa0 (patch)
tree92d951b948f0e01dc6b7ae3f11b9c03034edb69a /test
parent455b15f7a850a58ef667ad170732769043eb1522 (diff)
downloaditools-3dabbbd5325c9fad9582cd44b1da68dece78eaa0.tar.gz
itools-3dabbbd5325c9fad9582cd44b1da68dece78eaa0.tar.bz2
itools-3dabbbd5325c9fad9582cd44b1da68dece78eaa0.zip
no reason for different naming
Diffstat (limited to 'test')
-rw-r--r--test/U_tests.json211
-rwxr-xr-xtest/autoprepend.t38
-rwxr-xr-xtest/exec.t93
-rwxr-xr-xtest/getopt.t28
-rwxr-xr-xtest/it.t474
-rwxr-xr-xtest/it_cache.t49
-rwxr-xr-xtest/it_dbi.t364
-rwxr-xr-xtest/it_html.t291
-rwxr-xr-xtest/it_mail.t93
-rwxr-xr-xtest/it_pipe.t18
-rwxr-xr-xtest/it_text.t21
-rwxr-xr-xtest/it_url.t315
-rw-r--r--test/it_url.testserver.php71
-rw-r--r--test/it_url_server.php32
-rwxr-xr-xtest/it_url_slow.t59
-rwxr-xr-xtest/it_xml.t127
-rwxr-xr-xtest/itjs.t66
17 files changed, 2350 insertions, 0 deletions
diff --git a/test/U_tests.json b/test/U_tests.json
new file mode 100644
index 0000000..e35ff9d
--- /dev/null
+++ b/test/U_tests.json
@@ -0,0 +1,211 @@
+[
+ {
+ "args": ["/Zürich"],
+ "exp": "/Z%C3%BCrich",
+ "name": "U() with utf8 umlaut"
+ },
+
+ {
+ "args": ["/path#Zürich"],
+ "exp": "/path#Z%C3%BCrich",
+ "name": "U() with utf8 umlaut in fragment"
+ },
+
+ {
+ "args": ["/foo.html?"],
+ "exp": "/foo.html",
+ "name": "U() removes ? at end"
+ },
+
+ {
+ "args": ["/foo.html#"],
+ "exp": "/foo.html",
+ "name": "U() removes # at end"
+ },
+
+ {
+ "args": ["/foo.html?#"],
+ "exp": "/foo.html",
+ "name": "U() removes ?# at end"
+ },
+
+ {
+ "args": ["/foo.html#?"],
+ "exp": "/foo.html#?",
+ "name": "U() keeps #? at end"
+ },
+
+ {
+ "args": ["/foo.html", {"bar": null}],
+ "exp": "/foo.html",
+ "name": "U() empty parameter removed"
+ },
+
+ {
+ "args": ["/foo.html?bar="],
+ "exp": "/foo.html?bar=",
+ "name": "U() keep empty parameter with = if only base is given"
+ },
+
+ {
+ "args": ["/foo.html?foo=bar&qux"],
+ "exp": "/foo.html?foo=bar&qux",
+ "name": "U() keep empty parameter without = if only base is given"
+ },
+
+ {
+ "args": ["/foo.html?bar=", {"foo": "gna"}],
+ "exp": "/foo.html?foo=gna",
+ "name": "U() empty parameter with = removed if parameters get changed"
+ },
+
+ {
+ "args": ["/foo.html?foo=bar&qux", {"gna": "gnaber"}],
+ "exp": "/foo.html?foo=bar&gna=gnaber",
+ "name": "U() empty parameter without = removed if parameters get changed"
+ },
+
+ {
+ "args": ["/foo.html?bar=gna&foo=g", {"bar": ""}],
+ "exp": "/foo.html?foo=g",
+ "name": "U() remove parameter in base by overwriting with empty string"
+ },
+
+ {
+ "args": ["/foo.html?bar=gna&foo=g", {"bar": false}],
+ "exp": "/foo.html?foo=g",
+ "name": "U() remove parameter in base by overwriting with false"
+ },
+
+ {
+ "args": ["/foo.html?bar=gna&foo=g", {"bar": null}],
+ "exp": "/foo.html?bar=gna&foo=g",
+ "name": "U() keep parameter in base instead of overwriting with null"
+ },
+
+ {
+ "args": ["/foo.html", {"bar": {"gna": 42, "qux": {"quux": "<Zürich>", "gnöp": "fasel"}}}],
+ "exp": "/foo.html?bar%5Bgna%5D=42&bar%5Bqux%5D%5Bquux%5D=%3CZ%C3%BCrich%3E&bar%5Bqux%5D%5Bgn%C3%B6p%5D=fasel",
+ "name": "U() with nested arrays"
+ },
+
+ {
+ "args": ["/foo.html?", {"foo" : "bar[qux][gnöp]=fasel"}],
+ "exp": "/foo.html?foo=bar%5Bqux%5D%5Bgn%C3%B6p%5D%3Dfasel",
+ "name": "U() with array syntax in value and ? at end of base"
+ },
+
+ {
+ "args": ["/foo.html?a=b&bar=qux&c=d", {"bar": "baz"}],
+ "exp": "/foo.html?a=b&bar=baz&c=d",
+ "name": "U() args override get params in base url"
+ },
+
+ {
+ "args": ["/foo.html?a=b&bar=qux&c=d", {"bar": "baz", "gna": "gnaber"}],
+ "exp": "/foo.html?a=b&bar=baz&c=d&gna=gnaber",
+ "name": "U() args override get params in base url and appends remaining params"
+ },
+
+ {
+ "args": ["/foo.html?gna=g&a=b&bar=qux&c=d", {"bar": "baz", "gna": "gnaber"}],
+ "exp": "/foo.html?gna=gnaber&a=b&bar=baz&c=d",
+ "name": "U() args override get params in base url keeping same order"
+ },
+
+ {
+ "args": ["/foo.html?bar.qux=a.b", {"c.d": "e.f", "g h": "i j"}],
+ "exp": "/foo.html?bar.qux=a.b&c.d=e.f&g+h=i+j",
+ "name": "U() dots and spaces in arg names are preserved"
+ },
+
+ {
+ "args": ["/foo.html?bar=a+b+c", {"foo": "d e f"}],
+ "exp": "/foo.html?bar=a+b+c&foo=d+e+f",
+ "name": "U() handles multiple spaces in parameter values"
+ },
+
+ {
+ "args": ["/foo.html?bar=qux#frag=frog", {"baz": "gna"}],
+ "exp": "/foo.html?bar=qux&baz=gna#frag=frog",
+ "name": "U() fragment after params"
+ },
+
+ {
+ "args": ["Jet d'eau"],
+ "exp": "Jet%20d%27eau",
+ "name": "U() with single quotes in URL"
+ },
+
+ {
+ "args": ["/test.html?foo=bar?qux=gna", {"?q": "?r"}],
+ "exp": "/test.html?foo=bar%3Fqux%3Dgna&%3Fq=%3Fr",
+ "name": "U() quoting of ? in args but not base"
+ },
+
+ {
+ "args": ["%% %1%x %1x%x1%xx%11%ff%FF%0f%0F %"],
+ "exp": "%25%25%20%251%25x%20%251x%25x1%25xx%11%ff%FF%0f%0F%20%25",
+ "name": "U() quoting of % if not followed by 2 hex digits"
+ },
+
+ {
+ "args": ["path#%% %1%x %1x%x1%xx%11%ff%FF%0f%0F %"],
+ "exp": "path#%25%25%20%251%25x%20%251x%25x1%25xx%11%ff%FF%0f%0F%20%25",
+ "name": "U() quoting of % if not followed by 2 hex digits in fragment"
+ },
+
+ {
+ "args": ["a\\b"],
+ "exp": "a/b",
+ "name": "U() converting of \\ to /"
+ },
+
+ {
+ "args": ["path/#a\\b"],
+ "exp": "path/#a%5Cb",
+ "name": "U() encode \\ in fragment"
+ },
+
+ {
+ "args": ["foo.html?bar=\\gna"],
+ "exp": "foo.html?bar=%5Cgna",
+ "name": "U() encode \\ in url parameter"
+ },
+
+ {
+ "args": ["path/#?a=b"],
+ "exp": "path/#?a=b",
+ "name": "U() keep query syntax in fragment"
+ },
+
+ {
+ "args": ["path/#Jet d'eau"],
+ "exp": "path/#Jet%20d%27eau",
+ "name": "U() with single quotes in fragment"
+ },
+
+ {
+ "args": ["//gna.ch"],
+ "exp": "//gna.ch/",
+ "name": "U() add / for absolute url without path"
+ },
+
+ {
+ "args": ["http://gna.ch"],
+ "exp": "http://gna.ch/",
+ "name": "U() add / for absolute http url without path"
+ },
+
+ {
+ "args": ["app://settings"],
+ "exp": "app://settings",
+ "name": "U() do not add / for non-http url without path"
+ },
+
+ {
+ "args": ["/foo.html", {"<spam>": {"<spam>": "ham"}}],
+ "exp": "/foo.html?%3Cspam%3E%5B%3Cspam%3E%5D=ham",
+ "name": "U() urlencode keys of nested structures"
+ }
+]
diff --git a/test/autoprepend.t b/test/autoprepend.t
new file mode 100755
index 0000000..bf40605
--- /dev/null
+++ b/test/autoprepend.t
@@ -0,0 +1,38 @@
+#!/www/server/bin/php -qC
+<?php
+
+it_text::init();
+$GLOBALS['it_text']->statictext = array(
+ '_' => array("de" => "Deutsch", "en" => "English"),
+ 'foo' => array("de" => "bar {v1}", "en" => "qux {v1}"),
+);
+
+is(
+ T('foo'),
+ "bar {v1}",
+ "simple T()"
+);
+
+is(
+ T('foo', 'en'),
+ "qux {v1}",
+ "simple T() with language"
+);
+
+is(
+ T('foo', array('v1' => "gna<bber")),
+ "bar gna&lt;bber",
+ "T() with quoted values"
+);
+
+is(
+ T('foo', array('v1' => "gna<bber"), 'en'),
+ "qux gna&lt;bber",
+ "T() with with quoted values and language"
+);
+
+is(
+ T('foo', 'en', array('v1' => "gna<bber")),
+ "qux gna&lt;bber",
+ "T() with with language and quoted values"
+);
diff --git a/test/exec.t b/test/exec.t
new file mode 100755
index 0000000..c155c88
--- /dev/null
+++ b/test/exec.t
@@ -0,0 +1,93 @@
+#!/www/server/bin/php -qC
+<?php
+
+# Tests for getopt in it.class
+
+is(it::exec("echo gna"), "gna\n", "basic exec");
+is(it::exec("echo {arg}", array('arg' => 'gna')), "gna\n", "exec with argument");
+is(it::exec("echo {0}", 'gna'), "gna\n", "exec with positional argument");
+is(it::shell_command("echo {arg}", array('arg' => 'gna07,-:blah')), "echo gna07,-:blah", "don't quote arguments with only whitelistes characters");
+is(it::shell_command("echo {arg}", array('arg' => '2>&1')), "echo '2>&1'", "quote arguments with dangerous characters");
+is(it::shell_command("echo {arg}", array('arg' => '')), "echo ''", "quote empty arguments");
+
+
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('--longopt' => true))),
+ "echo --longopt",
+ "options argument with long option"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('-onedash' => true))),
+ "echo -onedash",
+ "... with long option but only one dash"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('-s' => true))),
+ "echo -s",
+ "... with short option"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('--longopt' => 'val'))),
+ "echo --longopt val",
+ "... with long option with value"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('-onedash' => 'val'))),
+ "echo -onedash val",
+ "... with long option but only one dash and with value"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('-s' => 'val'))),
+ "echo -s val",
+ "... with short option with value"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('--longopt' => false))),
+ "echo ",
+ "... with disabled long option"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('-s' => false))),
+ "echo ",
+ "... with disabled short option"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('longopt' => true))),
+ "echo --longopt",
+ "... long option without dashes"
+);
+is(
+ it::shell_command("echo {-opts}", array('-opts' => array('s' => true))),
+ "echo -s",
+ "... short option without dashes"
+);
+
+foreach (array("", "C", "de_CH", "de_CH.utf8") as $locale) {
+ setlocale(LC_ALL, $locale);
+ $arg = "preüpost";
+ if (it::match('utf8', $locale))
+ $arg = utf8_encode($arg);
+ is(it::exec("echo " . $arg), $arg . "\n", "exec with umlaut (locale '$locale')");
+ is(it::exec("echo {arg}", array('arg' => $arg)), $arg . "\n", "exec with argument and umlaut (locale '$locale')");
+}
+
+is(it::_exec_quotevalue(""), "''", "empty arg needs quotes");
+is(it::_exec_quotevalue("*"), "'*'", "special chars need quotes");
+is(it::_exec_quotevalue("Aabcdef0123456789"), "Aabcdef0123456789", "simple case. tel:debug_getdata needs unquoted vals");
+
+is(it::system('exit 0'), 0, 'return exit code 0');
+is(it::system('exit 1'), 1, 'return exit code 1');
+is(it::system('exit -1'), 255, 'return exit code unsigned');
+
+@unlink('/tmp/it_system_test');
+it::system('touch /tmp/it_system_test');
+ok(file_exists('/tmp/it_system_test'), 'shell command gets executed');
+
+@unlink('/tmp/it_system_test');
+it::system('touch {path}', array('path' => '/tmp/it_system_test'));
+ok(file_exists('/tmp/it_system_test'), 'shell command with argument');
+
+@unlink('/tmp/it_system_test');
+it::system('touch {0}', '/tmp/it_system_test');
+ok(file_exists('/tmp/it_system_test'), 'shell command with positional argument');
+@unlink('/tmp/it_system_test');
diff --git a/test/getopt.t b/test/getopt.t
new file mode 100755
index 0000000..8b0ff70
--- /dev/null
+++ b/test/getopt.t
@@ -0,0 +1,28 @@
+#!/www/server/bin/php -qC
+<?php
+
+# Tests for getopt in it.class
+
+$GLOBALS['usage'] = "Usage: doesnotexist.php [OPTIONS]
+ Some help to a not existing program
+ -h,--help the help argument
+ -a,--argument=ARG the arg argument
+ -0,--zero testworthy shortarg
+";
+
+function getopt_ok($argv, $exp, $name)
+{
+ $_SERVER['argv'] = array_merge(array('doesnotexist.php'), $argv);
+ $got = it::getopt($GLOBALS['usage']);
+ return is($got['argument'], $exp, $name);
+}
+
+foreach (array("" => "blah gnaber", " (umlaute)" => "pre üäpost") as $variant => $testarg) {
+ getopt_ok(array('-a', $testarg), $testarg, "Short version" . $variant);
+ getopt_ok(array('--argument', $testarg), $testarg, "Long version with space" . $variant);
+ getopt_ok(array("--argument=$testarg"), $testarg, "Long version with equal" . $variant);
+}
+
+$_SERVER['argv'] = array('doesnotexist.php', '-0');
+$zero_opts = it::getopt($GLOBALS['usage']);
+ok($zero_opts['zero'], '-0');
diff --git a/test/it.t b/test/it.t
new file mode 100755
index 0000000..996f44a
--- /dev/null
+++ b/test/it.t
@@ -0,0 +1,474 @@
+#!/www/server/bin/php -qC
+<?php
+
+# Tests for it.class
+
+#
+# tests for it::match()
+#
+$oldcharset = ini_get('default_charset');
+$oldlocale = setlocale(LC_CTYPE, 0);
+
+ini_set('default_charset', 'utf-8');
+setlocale(LC_CTYPE, 'de_CH'); # required becuase we're checking German umlauts in latin1 mode
+
+function match($regex, $string, $expect, $name, $p = array())
+{
+ $GLOBALS['TEST_MORE_LEVEL'] = 1;
+ $pass = is (it::match($regex, $string, $p), $expect, $name);
+ if (!$pass) {
+ diag(" regex given: $regex" . ($p ? " " .D($p) : ""));
+ diag(" regex converted: " . it::convertregex($regex));
+ }
+ $GLOBALS['TEST_MORE_LEVEL'] = 0;
+}
+
+
+match(
+ 'b', 'aaaabaaaa',
+ 'b',
+ 'simple regex'
+);
+
+match(
+ 'a/b', ' a/b ',
+ 'a/b',
+ 'regex with /'
+);
+
+match(
+ 'aa(bb)aa(cc)aa(dd)qq', 'aabbaaccaaddqq',
+ array('bb', 'cc', 'dd'),
+ 'return array of captures'
+);
+
+match(
+ '\bblah\b', ' blah ',
+ 'blah',
+ 'match \b at spaces'
+);
+
+match(
+ '\bblah\b', 'blah',
+ 'blah',
+ 'match \b at end of string'
+);
+
+match(
+ '\bblah\b', 'ablahc',
+ null,
+ 'don\'t match \b at word chars'
+);
+
+match(
+ '\bblah\b', 'Üblahä',
+ null,
+ 'don\'t match \b at umlaute'
+);
+
+match(
+ '\Bblah\B', ' blah ',
+ null,
+ 'don\'t match \B at spaces'
+);
+
+match(
+ '\Bblah\B', 'blah',
+ null,
+ 'don\'t match \B at end of string'
+);
+
+match(
+ '\Bblah\B', 'ablahc',
+ 'blah',
+ 'match \B at word chars'
+);
+
+match(
+ '\Bblah\B', 'Üblahä',
+ 'blah',
+ 'match \B at umlaute'
+);
+
+match(
+ '\w+', ' |#Üblahä ',
+ 'Üblahä',
+ 'include umlaute in \w'
+);
+
+match(
+ '[[:alpha:]]+', ' |#blahä ',
+ 'blahä',
+ 'include umlaute in [[:alpha:]]'
+);
+
+match(
+ '\W+', ' |#Üblahä ',
+ ' |#',
+ 'don\'t include umlaute in \W'
+);
+
+match(
+ '\ba', 'äa',
+ null,
+ '\b must know umlauts'
+);
+
+match(
+ 'aaa\\\\w+', ' aaa\www ',
+ 'aaa\www',
+ 'don\'t parse \w in \\\\w at beginning (match)'
+);
+
+match(
+ 'aaa\\\\w+', ' aaa\www ',
+ 'aaa\www',
+ 'don\'t parse \w in \\\\w after chars (match)'
+);
+
+eval('$escapedwordregex = "' . it::convertregex('\w') . '";');
+$escapedwordregex = preg_replace('|[\\\\/]|', '', $escapedwordregex);
+
+match(
+ '\\\\w+', $escapedwordregex,
+ null,
+ 'don\'t parse \w in \\\\w at beginning (no match)'
+);
+
+match(
+ 'aaa\\\\w+', 'aaa' . $escapedwordregex,
+ null,
+ 'don\'t parse \w in \\\\w after chars (no match)'
+);
+
+match(
+ '\\\\\\\\w+', '\\' . $escapedwordregex,
+ null,
+ 'don\'t parse \w in \\\\\\\w (no match)'
+);
+
+match(
+ '\\\\\\\\w+', ' \\\\www ',
+ '\\\\www',
+ 'don\'t parse \\\\\\\\w as \w (match)'
+);
+
+match(
+ '[\w]+', '[[[]]]---',
+ null,
+ 'replace \w in [\w] correctly (no match)'
+);
+
+match(
+ '[\w]+', ' \\\\aword[[[]]] ',
+ 'aword',
+ 'replace \w in [\w] correctly (match)'
+);
+
+match(
+ '[\\\\w]+', ' blabergna ',
+ null,
+ 'don\'t parse \w in [\\\\w] (no match)'
+);
+
+match(
+ '[\\\\w]+', ' \\\\worda[[[]',
+ '\\\\w',
+ 'don\'t parse \w in [\\\\w] (match)'
+);
+
+match(
+ '[a\W]+', 'bbbbbbb a a%$+ accccc',
+ ' a a%$+ a',
+ '\W in []'
+);
+
+match(
+ '\\\\\\w+', ' \Üblahä ',
+ '\Üblahä',
+ 'parse \w in \\\\\\w at beginning'
+);
+
+match(
+ 'aaa\\\\\\w+', ' aaa\Üblahä ',
+ 'aaa\Üblahä',
+ 'parse \w in \\\\\\w after chars'
+);
+
+match(
+ '\w+', 'word1 wörd2 word_3',
+ array('word1', 'wörd2', 'word_3'),
+ "test match_all function",
+ array('all' => true)
+);
+
+match(
+ 'aBcD', ' aBcD ',
+ 'aBcD',
+ "caseinsensitive is default"
+);
+
+match(
+ '\w+', 'Müller',
+ 'Müller',
+ '\w matches umlaut in utf-8 mode'
+);
+
+match(
+ 'M.ller', 'Müller',
+ 'Müller',
+ '. matches umlaut in utf-8 mode'
+);
+
+match(
+ utf8_decode('ö'), utf8_decode('Ö'),
+ utf8_decode('Ö'),
+ 'match umlaute in de_CH.latin1 case insensitive',
+ array('utf8' => false)
+);
+
+match(
+ utf8_decode('aöBÜ'), utf8_decode('AÖbü'),
+ utf8_decode('AÖbü'),
+ "match umlaute with non-utf-8 override in p",
+ array('utf8' => false)
+);
+
+
+match(
+ 'abc', "aBc",
+ null,
+ "set case sensitivity by parameter",
+ array('casesensitive' => 1)
+);
+
+match(
+ '\w+', 'word1 wörd2 word_3',
+ array('word1', 'wörd2', 'word_3'),
+ "test all => 1 without captures",
+ array('all' => 1)
+);
+
+match(
+ '\w+\s+(\d+)', 'word1 12 wörd2 3 word_3 4',
+ array('12', '3', '4'),
+ "test all => 1 with one capture",
+ array('all' => 1)
+);
+
+match(
+ '(\w+)\s+(\d+)', 'word1 12 wörd2 3 word_3 4',
+ array(array('word1', '12'), array('wörd2', '3'), array('word_3', '4')),
+ "test all => 1 with captures",
+ array('all' => 1)
+);
+
+match(
+ '(\w+)\s+(\d+)', 'word1 12 wörd2 3 word_3 4',
+ array(array('word1', 'wörd2', 'word_3'), array('12', '3', '4')),
+ "test all => 1,pattern_order => 1",
+ array('all' => 1, 'pattern_order' => 1)
+);
+
+ini_set('default_charset', 'iso-8859-1');
+match(
+ 'aöBÜ', "AÖbü",
+ 'AÖbü',
+ "match utf-8 umlaute in case insensitive mode with utf8 override",
+ array('utf8' => true)
+);
+ini_set('default_charset', 'utf-8');
+
+
+#
+# tests for it::replace()
+#
+is(
+ it::replace(
+ array(
+ 'regex1' => 'repl1',
+ 'regex2' => 'repl2',
+ 'regex3' => 'repl3'),
+ 'regex2 regex1 regex3'),
+ 'repl2 repl1 repl3',
+ 'test tr regex function'
+);
+
+is(it::replace(array('a' => "1", 'b' => "2"), "ab"), "12");
+is(it::replace(array('!' => "x"), "!"), "x");
+is(it::replace(array('\w' => "x"), "oö"), "xx");
+is(it::replace(array('[[:alpha:]]' => "x"), "ö"), "x");
+is(it::replace(array('\w' => "x", '#' => "!"), "#ö"), "!x");
+is(it::replace(array('#' => "!", '\w' => "x"), "#ö"), "!x");
+is(it::replace(array('ö' => "x"), "Ö"), "x");
+is(it::replace(array('a' => "1"), "aaa", array('limit' => 1)), "1aa");
+is(it::replace(array('\s' => "x"), it_html::entity_decode("&nbsp;")), "x", "match non-breaking space as white-space character");
+is(it::replace(array('a' => "b", 'b' => "c"), "a"), "c");
+
+is(it::grep('ismatch', array('ismatch', 'isnomatch')), array('ismatch'), 'grep with simple regex');
+is(it::grep('!', array('ismatch!', 'isnomatch')), array('ismatch!'), '! in regex');
+is(it::grep('lower|UPPER', array('lower', 'LOWER', 'upper', 'UPPER'), array('casesensitive' => 1)), array(0 => 'lower', 3 => 'UPPER'), 'set casesensitive');
+is(it::grep('match', array('foo' => 'match', 'bar' => 'gna')), array('foo' => 'match'), 'with keys');
+is(it::grep('2', [0 => 1, 1 => 2, 2 => 3], ['invert' => true]), [0 => 1, 2 => 3]);
+
+setlocale(LC_CTYPE, $oldlocale);
+ini_set('default_charset', $oldcharset); # end of tests that must run with specific charset
+
+# it::cidr_match tests
+is(it::cidr_match('192.168.2.3', '192.168.2.5'), false, "cidr_match full IP no match no mask");
+is(it::cidr_match('192.168.2.3', '192.168.2.3'), true, "cidr_match full IP match no mask");
+is(it::cidr_match('192.168.2.3', '192.168.2.5/32'), false, "cidr_match full IP no match");
+is(it::cidr_match('192.168.2.5', '192.168.2.5/32'), true, "cidr_match full IP match");
+is(it::cidr_match('192.168.1.1', '192.168.42.0/24'), false, "cidr_match no match");
+is(it::cidr_match('192.168.42.1', '192.168.42.0/24'), true, "cidr_match basic match");
+is(it::cidr_match('192.168.42.42', '192.168.0.0/16'), true, "cidr_match class b");
+is(it::cidr_match('192.168.42.42', '192.168.42.64/26'), false, "cidr_match offset no match");
+is(it::cidr_match('192.168.42.42', '192.168.42.32/27'), true, "cidr_match offset");
+is(it::cidr_match('2001:918:ff83:101:798e:77c0:b722:fe56', '2001:918:ff83:101::/64'), true, "cidr_match ipv6");
+is(it::cidr_match('2001:918:ff83:102:798e:77c0:b722:fe56', '2001:918:ff83:101::/64'), false, "cidr_match ipv6 no match" );
+is(it::cidr_match('10.11.12.13', array('10.0.0.0/8', '192.168.0.0./16')), true, "cidr_match array");
+
+# it::filter_keys tests
+
+$data = array('a' => 1, 'b' => 2, 'c' => 3);
+is(it::filter_keys($data, 'a'), array('a' => 1), "select one key");
+is(it::filter_keys($data, array('a', 'b')), array('a' => 1, 'b' => 2), "select two keys with array");
+is(it::filter_keys($data, 'a,b'), array('a' => 1, 'b' => 2), "select two keys with string");
+is(
+ array_keys(it::filter_keys($data, 'b,a')),
+ array('a', 'b'),
+ "keep order of data array per default");
+is(
+ array_keys(it::filter_keys($data, 'b,a', array('reorder' => true))),
+ array('b', 'a'),
+ "reorder with given key order");
+
+# it::date tests
+
+function _time($debug_time, $expected)
+{
+ $GLOBALS['debug_time'] = $debug_time;
+ is(it::date(), $expected, ".time-$debug_time");
+ $GLOBALS['debug_time'] = '';
+}
+_time("2014-01-01", "2014-01-01 00:00:00");
+_time("2014-01-01-15-27-15", "2014-01-01 15:27:15");
+_time("15-27", it::date('', '15:27'));
+_time("1527", it::date('', '15:27'));
+_time("15-27-15", it::date('', '15:27:15'));
+_time("152715", it::date('', '15:27:15'));
+_time("yesterday", it::date('', 'yesterday'));
+
+is(it::date('date', '2011-10-25'), '25.10.2011', 'parse date string with strtotime');
+is(it::date('date', '2011-10-25 + 3 days'), '28.10.2011', 'some date arithmetic');
+is(it::date('datetime', it::time()), it::date('datetime'), 'recognize int as timestamp');
+is(it::date('datetime', it::time()*1.0), it::date('datetime'), 'recognize float as timestamp');
+is(it::date('datetime', it::time() . ''), it::date('datetime'), 'recognize digit string as timestamp');
+is(it::date('datetime', '@' . it::time()), it::date('datetime'), 'recognize strtotime timestamp format');
+is(it::date('datetime', 10), it::date('datetime', "10"), 'numeric and string give same result');
+is(it::date('datetime', 10.0), it::date('datetime', "10"), '... as long as num is properly truncated');
+is(it::date('datetime', 10.5), it::date('datetime', "10"), '... with one digit after point');
+is(it::date('datetime', 10.56), it::date('datetime', "10"), '... with two digits after point');
+is(it::date('datetime', 1000000), it::date('datetime', "1000000"), '... large nummer');
+is(it::date('datetime', 1000000.543), it::date('datetime', "1000000"), '... large nummer and point');
+is(it::date('time', "10.5"), "10:05", 'interpret string with points with strtotime');
+is(it::date('time', "10.05"), "10:05", 'interpret string with points with strtotime');
+
+# it::uc*
+is(it::ucfirst('foo bär über'), 'Foo bär über');
+is(it::ucwords('foo bär über'), 'Foo Bär Über');
+
+# it::substr_replace
+is(it::substr_replace('abcdefgh', 'xyz', 2, 4), substr_replace('abcdefgh', 'xyz', 2, 4), 'it::substr_replace the same as substr_replace for ascii');
+is(it::substr_replace('✔☯♥', '☃☃', 1, 1), '✔☃☃♥', 'it::substr_replace for utf-8');
+
+is(grapheme_strlen("\xc1"), null, "need grapheme_strlen side effect for any2utf8");
+
+is(it::any2utf8('Meier'), 'Meier', "it::any2utf8 ascii input");
+is(it::any2utf8('Müller'), 'Müller', "it::any2utf8 utf8 input");
+is(it::any2utf8('Aslı'), 'Aslı', "it::any2utf8 utf8 non-latin1 input");
+is(it::any2utf8(utf8_decode('Müller')), 'Müller', "it::any2utf8 latin1 input");
+
+is(it::any2utf8(
+ ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ'), # omit soft hyphen cause we filter it
+ ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ',
+ "it::any2utf8 utf8 input (exhaustive alphabet)");
+is(it::any2utf8(
+ utf8_decode(' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ')),
+ ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ',
+ "it::any2utf8 latin1 input (exhaustive alphabet)");
+
+is(it::any2utf8(utf8_encode("ü")), "ü", "it::any2utf8 double encoding");
+
+is(it::any2utf8("Meier"), "Meier", "it::any2utf8 ascii");
+is(it::any2utf8("Müller"), "Müller", "it::any2utf8 utf-8 latin1");
+is(it::any2utf8("Aslı"), "Aslı", "it::any2utf8 utf-8 non-latin1");
+is(it::any2utf8("é»"), "é»", "it::any2utf8 utf-8 latin1 special combination");
+is(it::any2utf8(utf8_encode("Müller")), "Müller", "it::any2utf8 doubly encoded utf8");
+is(it::any2utf8(utf8_encode(utf8_encode("Müller"))), "Müller", "it::any2utf8 triply encoded utf8");
+is(it::any2utf8(utf8_decode("Müller")), "Müller", "it::any2utf8 incorrectly encoded latin1");
+is(it::any2utf8("a💚b"), "a💚b", "it::any2utf8 correctly handles 4-byte utf-8 character GREEN HEART");
+
+is(it::any2utf8(array("foo", utf8_decode("bär"))), array("foo", "bär"), "any2utf8 on arrays");
+is(it::any2utf8(array("foo", array(utf8_decode("bär")))), array("foo", array("bär")), "any2utf8 on recursive arrays");
+is(it::any2utf8(array(1, true, false, null)), array(1, true, false, null), "any2utf8 should leave types alone");
+is(it::any2utf8(array(utf8_decode('Müller') => utf8_decode('Müller'))), array('Müller' => 'Müller'), "it::any2utf8 latin1 keys");
+
+is(it::any2utf8("\xc2\xad"), "", "it::any2utf8 remove soft hyphens");
+
+foreach ([ 'a' => 'ä', 'e' => 'ë', 'i' => 'ï', 'o' => 'ö', 'u' => 'ü' ] as $src => $dst)
+{
+ is(it::any2utf8("$src\xcc\x88"), $dst, "it::any2utf8 normalize combining diaeresis $dst to umlaut code $dst");
+ $src = mb_strtoupper($src);
+ $dst = mb_strtoupper($dst);
+ is(it::any2utf8("$src\xcc\x88"), $dst, "it::any2utf8 normalize combining diaeresis $dst to umlaut code $dst");
+}
+
+foreach (array($dummy, false, true, null, 1, "a", "Ä", "/", array()) as $var)
+ is(it::json_decode(it::json_encode($var)), $var);
+
+is(it::sort(array("2!","19!","1!")), array("1!", "19!", "2!"));
+is(it::sort(array("2!","19!","1!"), "r"), array("2!", "19!", "1!"));
+is(it::sort(array("2!","19!","1!"), "rn"), array("19!", "2!", "1!"));
+
+is(it::mod(-9, 4), 3);
+is(it::mod(-8, 4), 0);
+is(it::mod(0, 4), 0);
+is(it::mod(7, 4), 3);
+
+is(it::map('5*$k+$v', array(0 => 1, 1 => 2)), array(1, 7));
+is(it::map(function($k, $v) {return 5*$k+$v;}, array(0 => 1, 1 => 2)), array(1, 7));
+is(it::map('strlen', array("aaa", "aa")), array(3, 2));
+is(it::map('it::ucfirst', array("aaa")), array("Aaa"));
+is(it::map('$v->C14N()', DOMDocument::loadXML('<foo>42</foo>')->childNodes), [ '<foo>42</foo>' ], "Traversable: Needs copy, not modifiable in-place");
+# Special values which are not callable
+is(it::map('null', array("aaa", "aa")), array(null, null));
+is(it::map('1', array("aaa", "aa")), array(1, 1));
+is(it::map(42, array("aaa", "aa")), array(42, 42));
+# Only map selected keys
+is(it::map('2*$v', [3, 4, 5], ['keys' => '0,1']), [6, 8, 5]);
+is(it::map('2*$v', [3, 4, 5], ['keys' => [0,1]]), [6, 8, 5]);
+is(it::map('2*$v', ['foo' => 1, 'bar' => 2], ['keys' => 'foo']), ['foo' => 2, 'bar' => 2]);
+
+# it::filter
+is(it::filter('$v > 2', [1, 5 => 2, 2 => 3]), [2 => 3]);
+is(it::filter('$k > 2', [1, 5 => 2, 2 => 3]), [5 => 2]);
+
+is(it::split("b", "aba"), ["a", "a"]);
+is(it::split("b", "aBa"), ["a", "a"]);
+is(it::split("b", "abba"), ["a", "", "a"]);
+is(it::split("b", "ababa", ['limit' => 2]), ["a", "aba"]);
+is(it::split("b", "abbba", ['no_empty' => true]), ["a", "a"]);
+is(it::split("(b)", "aba", ['delim_capture' => true]), ["a", "b", "a"]);
+is(it::split("b", "aabaa", ['offset_capture' => true]), [["aa", 0], ["aa", 3]]);
+
+it::file_put_contents("/tmp/it_test", "aa");
+is(($fh = it::fopen("/tmp/it_test", "r")) ? fgets($fh) : null, "aa");
+is(it::file_get_contents("/tmp/it_test"), "aa");
+is(it::file("/tmp/it_test"), ["aa"]);
+ob_start();
+it::readfile("/tmp/it_test");
+is(ob_get_clean(), "aa");
+
+it::file_put("/tmp/it_test", "bb");
+is(it::file_get("/tmp/it_test"), "bb");
diff --git a/test/it_cache.t b/test/it_cache.t
new file mode 100755
index 0000000..8f804d4
--- /dev/null
+++ b/test/it_cache.t
@@ -0,0 +1,49 @@
+#!/www/server/bin/php
+<?php
+
+# test array_based process cache
+it_cache::put('it_cache_t', 42);
+is(it_cache::get('it_cache_t'), 42, "cache put number");
+
+it_cache::put('it_cache_t', false);
+is(it_cache::get('it_cache_t'), false, "cache get number");
+
+it_cache::put('it_cache_t', array(2));
+is(it_cache::get('it_cache_t'), array(2), "cache put/get array");
+
+
+# test non-distributed apc cache
+it_cache::put('it_cache_t', 42);
+unset($GLOBALS['it_cache_local']);
+is(it_cache::get('it_cache_t'), 42, "local put/get number");
+
+it_cache::put('it_cache_t', false);
+unset($GLOBALS['it_cache_local']);
+is(it_cache::get('it_cache_t'), false, "local put/get false");
+
+it_cache::put('it_cache_t', array(2));
+unset($GLOBALS['it_cache_local']);
+is(it_cache::get('it_cache_t'), array(2), "local put/get array");
+
+is(it_cache::get('it_cache_t'.rand(1, 1000)), null, "local get unknown key");
+
+
+# test distributed memcache
+$GLOBALS['debug_aslive'] = 1;
+
+it_cache::put('it_cache_d', 42, array('distributed' => 1));
+it_cache::put('it_cache_d', 0);
+unset($GLOBALS['it_cache_local']);
+is(intval(it_cache::get('it_cache_d', array('distributed' => 1))), 42, "distributed put/get number");
+
+it_cache::put('it_cache_d', false, array('distributed' => 1));
+it_cache::put('it_cache_d', 1);
+unset($GLOBALS['it_cache_local']);
+is(boolval(it_cache::get('it_cache_d', array('distributed' => 1))), false, "distributed put/get false");
+
+it_cache::put('it_cache_d', array(2), array('distributed' => 1));
+it_cache::put('it_cache_d', 0);
+unset($GLOBALS['it_cache_local']);
+is(it_cache::get('it_cache_d', array('distributed' => 1)), array(2), "distributed put/get array");
+
+is(it_cache::get('it_cache_d'.rand(1, 1000), array('distributed' => 1)), null, "distributed get unknown key");
diff --git a/test/it_dbi.t b/test/it_dbi.t
new file mode 100755
index 0000000..6a37637
--- /dev/null
+++ b/test/it_dbi.t
@@ -0,0 +1,364 @@
+#!/www/server/bin/php -qC
+<?php
+
+# Tests for it_dbi.class
+
+# Initialize DB
+$db = array('db' => "lib_search_ch", 'safety' => 0);
+$dbi = new it_dbi($db);
+$dbi->query('create temporary table it_dbi_test (
+ ID int not null auto_increment,
+ x int,
+ foo varchar(42),
+ dyncols LONGBLOB,
+ primary key(ID)
+);');
+
+$record = new it_dbi($db + array('table' => "it_dbi_test"));
+
+$record->insert(array('x' => 42, 'foo' => null));
+$record->insert(array('foo' => "bar"));
+$record->insert(array('x' => 64738, 'foo' => "q'uux"));
+
+is(
+ $record->ID,
+ 3,
+ "auto_increment"
+);
+
+$record->read(1);
+is(
+ array($record->_key, $record->x, $record->foo),
+ array(1, 42, null),
+ "read"
+);
+
+is(
+ $record->select(),
+ 3,
+ "select without parameters select all"
+);
+
+is(
+ $record->select(array('foo <>' => ""), "LIMIT 1"),
+ 1,
+ "select with multiple parameters (LIMIT part)"
+);
+is(
+ $record->foo,
+ "bar",
+ "select with multiple parameters (foo part)"
+);
+is(
+ $record->select(array('ID IN' => array(2,3))),
+ 2,
+ "select with IN"
+);
+is(
+ $record->select(array('ID NI' => array(2,3))),
+ 2,
+ "select with NI"
+);