00001 <?php
00002 # See the end of this file for documentation
00003
00004 # The latest release of this test framework can always be found on CPAN:
00005 # http://search.cpan.org/search?query=Test.php
00006
00007 register_shutdown_function('_test_ends');
00008
00009 $__Test = array(
00010 # How many tests are planned
00011 'planned' => null,
00012
00013 # How many tests we've run, if 'planned' is still null by the time we're
00014 # done we report the total count at the end
00015 'run' => 0,
00016
00017 # Are are we currently within todo_start()/todo_end() ?
00018 'todo' => array(),
00019 );
00020
00021 function plan($plan, $why = '')
00022 {
00023 global $__Test;
00024
00025 $__Test['planned'] = true;
00026
00027 switch ($plan)
00028 {
00029 case 'no_plan':
00030 $__Test['planned'] = false;
00031 break;
00032 case 'skip_all';
00033 printf("1..0%s\n", $why ? " # Skip $why" : '');
00034 exit;
00035 default:
00036 printf("1..%d\n", $plan);
00037 break;
00038 }
00039 }
00040
00041 function pass($desc = '')
00042 {
00043 return _proclaim(true, $desc);
00044 }
00045
00046 function fail($desc = '')
00047 {
00048 return _proclaim(false, $desc);
00049 }
00050
00051 function ok($cond, $desc = '') {
00052 return _proclaim($cond, $desc);
00053 }
00054
00055 function is($got, $expected, $desc = '') {
00056 $pass = $got == $expected;
00057 return _proclaim($pass, $desc, false, $got, $expected);
00058 }
00059
00060 function isnt($got, $expected, $desc = '') {
00061 $pass = $got != $expected;
00062 return _proclaim($pass, $desc, false, $got, $expected, true);
00063 }
00064
00065 function like($got, $expected, $desc = '') {
00066 $pass = preg_match($expected, $got);
00067 return _proclaim($pass, $desc, false, $got, $expected);
00068 }
00069
00070 function unlike($got, $expected, $desc = '') {
00071 $pass = !preg_match($expected, $got);
00072 return _proclaim($pass, $desc, false, $got, $expected, true);
00073 }
00074
00075 function cmp_ok($got, $op, $expected, $desc = '')
00076 {
00077 $pass = null;
00078
00079 # See http://www.php.net/manual/en/language.operators.comparison.php
00080 switch ($op)
00081 {
00082 case '==':
00083 $pass = $got == $expected;
00084 break;
00085 case '===':
00086 $pass = $got === $expected;
00087 break;
00088 case '!=':
00089 case '<>':
00090 $pass = $got != $expected;
00091 break;
00092 case '!==':
00093 $pass = $got !== $expected;
00094 break;
00095 case '<':
00096 $pass = $got < $expected;
00097 break;
00098 case '>':
00099 $pass = $got > $expected;
00100 break;
00101 case '<=':
00102 $pass = $got <= $expected;
00103 break;
00104 case '>=':
00105 $pass = $got >= $expected;
00106 break;
00107 default:
00108 if (function_exists($op)) {
00109 $pass = $op($got, $expected);
00110 } else {
00111 die("No such operator or function $op\n");
00112 }
00113 }
00114
00115 return _proclaim($pass, $desc, false, $got, "$got $op $expected");
00116 }
00117
00118 function diag($message)
00119 {
00120 if (is_array($message))
00121 {
00122 $message = implode("\n", $message);
00123 }
00124
00125 foreach (explode("\n", $message) as $line)
00126 {
00127 echo "# $line\n";
00128 }
00129 }
00130
00131 function include_ok($file, $desc = '')
00132 {
00133 $pass = include $file;
00134 return _proclaim($pass, $desc == '' ? "include $file" : $desc);
00135 }
00136
00137 function require_ok($file, $desc = '')
00138 {
00139 $pass = require $file;
00140 return _proclaim($pass, $desc == '' ? "require $file" : $desc);
00141 }
00142
00143 function is_deeply($got, $expected, $desc = '')
00144 {
00145 $diff = _cmp_deeply($got, $expected);
00146 $pass = is_null($diff);
00147
00148 if (!$pass) {
00149 $got = strlen($diff['gpath']) ? ($diff['gpath'] . ' = ' . $diff['got'])
00150 : _repl($got);
00151 $expected = strlen($diff['epath']) ? ($diff['epath'] . ' = ' . $diff['expected'])
00152 : _repl($expected);
00153 }
00154
00155 _proclaim($pass, $desc, false, $got, $expected);
00156 }
00157
00158 function isa_ok($obj, $expected, $desc = '')
00159 {
00160 $pass = is_a($obj, $expected);
00161 _proclaim($pass, $desc, false, $name, $expected);
00162 }
00163
00164 function todo_start($why = '')
00165 {
00166 global $__Test;
00167
00168 $__Test['todo'][] = $why;
00169 }
00170
00171 function todo_end()
00172 {
00173 global $__Test;
00174
00175 if (count($__Test['todo']) == 0) {
00176 die("todo_end() called without a matching todo_start() call");
00177 } else {
00178 array_pop($__Test['todo']);
00179 }
00180 }
00181
00182 #
00183 # The code below consists of private utility functions for the above functions
00184 #
00185
00186 function _proclaim(
00187 $cond, # bool
00188 $desc = '',
00189 $todo = false,
00190 $got = null,
00191 $expected = null,
00192 $negate = false) {
00193
00194 global $__Test;
00195
00196 $__Test['run'] += 1;
00197
00198 # We're in a TODO block via todo_start()/todo_end(). TODO via specific
00199 # functions is currently unimplemented and will probably stay that way
00200 if (count($__Test['todo'])) {
00201 $todo = true;
00202 }
00203
00204 # Everything after the first # is special, so escape user-supplied messages
00205 $desc = str_replace('#', '\\#', $desc);
00206 $desc = str_replace("\n", '\\n', $desc);
00207
00208 $ok = $cond ? "ok" : "not ok";
00209 $directive = '';
00210
00211 if ($todo) {
00212 $todo_idx = count($__Test['todo']) - 1;
00213 $directive .= ' # TODO ' . $__Test['todo'][$todo_idx];
00214 }
00215
00216 printf("%s %d %s%s\n", $ok, $__Test['run'], $desc, $directive);
00217
00218 # report a failure
00219 if (!$cond) {
00220 # Every public function in this file calls _proclaim so our culprit is
00221 # the second item in the stack
00222 $caller = debug_backtrace();
00223 $call = $caller['1'];
00224
00225 diag(
00226 sprintf(" Failed%stest '%s'\n in %s at line %d\n got: %s\n expected: %s",
00227 $todo ? ' TODO ' : ' ',
00228 $desc,
00229 $call['file'],
00230 $call['line'],
00231 $got,
00232 $expected
00233 )
00234 );
00235 }
00236
00237 return $cond;
00238 }
00239
00240 function _test_ends()
00241 {
00242 global $__Test;
00243
00244 if (count($__Test['todo']) != 0) {
00245 $todos = join("', '", $__Test['todo']);
00246 die("Missing todo_end() for '$todos'");
00247 }
00248
00249 if (!$__Test['planned']) {
00250 printf("1..%d\n", $__Test['run']);
00251 }
00252 }
00253
00254 #
00255 # All of the below is for is_deeply()
00256 #
00257
00258 function _repl($obj, $deep = true) {
00259 if (is_string($obj)) {
00260 return "'" . $obj . "'";
00261 } else if (is_numeric($obj)) {
00262 return $obj;
00263 } else if (is_null($obj)) {
00264 return 'null';
00265 } else if (is_bool($obj)) {
00266 return $obj ? 'true' : 'false';
00267 } else if (is_array($obj)) {
00268 return _repl_array($obj, $deep);
00269 }else {
00270 return gettype($obj);
00271 }
00272 }
00273
00274 function _diff($gpath, $got, $epath, $expected) {
00275 return array(
00276 'gpath' => $gpath,
00277 'got' => $got,
00278 'epath' => $epath,
00279 'expected' => $expected
00280 );
00281 }
00282
00283 function _idx($obj, $path = '') {
00284 return $path . '[' . _repl($obj) . ']';
00285 }
00286
00287 function _cmp_deeply($got, $exp, $path = '') {
00288 if (is_array($exp)) {
00289
00290 if (!is_array($got)) {
00291 return _diff($path, _repl($got), $path, _repl($exp));
00292 }
00293
00294 $gk = array_keys($got);
00295 $ek = array_keys($exp);
00296 $mc = max(count($gk), count($ek));
00297
00298 for ($el = 0; $el < $mc; $el++) {
00299 # One array shorter than the other?
00300 if ($el >= count($ek)) {
00301 return _diff(_idx($gk[$el], $path), _repl($got[$gk[$el]]),
00302 'missing', 'nothing');
00303 } else if ($el >= count($gk)) {
00304 return _diff('missing', 'nothing',
00305 _idx($ek[$el], $path), _repl($exp[$ek[$el]]));
00306 }
00307
00308 # Keys differ?
00309 if ($gk[$el] != $ek[$el]) {
00310 return _diff(_idx($gk[$el], $path), _repl($got[$gk[$el]]),
00311 _idx($ek[$el], $path), _repl($exp[$ek[$el]]));
00312 }
00313
00314 # Recurse
00315 $rc = _cmp_deeply($got[$gk[$el]], $exp[$ek[$el]], _idx($gk[$el], $path));
00316 if (!is_null($rc)) {
00317 return $rc;
00318 }
00319 }
00320 }
00321 else {
00322 # Default to serialize hack
00323 if (serialize($got) != serialize($exp)) {
00324 return _diff($path, _repl($got), $path, _repl($exp));
00325 }
00326 }
00327
00328 return null;
00329 }
00330
00331 function _plural($n, $singular, $plural = null) {
00332 if (is_null($plural)) {
00333 $plural = $singular . 's';
00334 }
00335 return $n == 1 ? "$n $singular" : "$n $plural";
00336 }
00337
00338 function _repl_array($obj, $deep) {
00339 if ($deep) {
00340 $slice = array_slice($obj, 0, 3); # Increase from 3 to show more
00341 $repl = array();
00342 $next = 0;
00343 foreach ($slice as $idx => $el) {
00344 $elrep = _repl($el, false);
00345 if (is_numeric($idx) && $next == $idx) {
00346
00347 $next++;
00348 } else {
00349
00350 $elrep = _repl($idx, false) . ' => ' . $elrep;
00351 }
00352 $repl[] = $elrep;
00353 }
00354 $more = count($obj) - count($slice);
00355 if ($more > 0) {
00356 $repl[] = '... ' . _plural($more, 'more element') . ' ...';
00357 }
00358 return 'array(' . join(', ', $repl) . ')';
00359 }
00360 else {
00361 return 'array(' . count($obj) . ')';
00362 }
00363 }
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496