Skip to content

Commit 773f544

Browse files
committed
Initial Version
Initial version with just the basic functionality needed to adorn unit test output in a meaningful way.
1 parent 9336894 commit 773f544

File tree

5 files changed

+331
-0
lines changed

5 files changed

+331
-0
lines changed

.gitignore

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
vendor/
19+
20+
# env file
21+
.env
22+
23+
# Others
24+
TODO*
25+
test/assets/
26+
*.php~
27+
*.bak
28+
29+
##### VisualStudioCode
30+
.vscode/*
31+
!.vscode/settings.json
32+
!.vscode/tasks.json
33+
!.vscode/launch.json
34+
!.vscode/extensions.json
35+
*.code-workspace
36+
.history/
37+
38+
##### Composer
39+
composer.phar
40+
/vendor/
41+
composer.lock
42+

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# PHP NEAT TEST
2+
3+
A super simple utility to neat-up your test cases and tally the
4+
results of your PHP test suite. It's here because I just need
5+
something simple.
6+
7+
# Usage
8+
9+
```php
10+
set_include_path(get_include_path() . PATH_SEPARATOR . NEAT_TEST);
11+
require_once('TestContext.php');
12+
13+
use function Coralys\Tests\AnnounceTestSuite;
14+
use function Coralys\Tests\AnnounceUnit;
15+
use function Coralys\Tests\AnnounceCase;
16+
use function Coralys\Tests\PrintResult;
17+
use function Coralys\Tests\PropName;
18+
use function Coralys\Tests\MethName;
19+
20+
function test_Division() {
21+
AnnounceUnit("Division");
22+
23+
$op1 = 10; // divident
24+
$op2 = 2; // divisor
25+
26+
AnnounceCase("Normal division");
27+
$result = $op1 / $op2; // quotient
28+
$expected = 5;
29+
PrintResult($expected == $result, 'Valid quotient', $result, $expected);
30+
31+
// Variations of the same test case
32+
AnnounceCase("Division exception");
33+
try {
34+
$result = $op1 / 0; // quotient
35+
} catch (Exception $e) {
36+
PrintResult(true, 'Division by zero');
37+
}
38+
}
39+
40+
function test_Multiply() {
41+
AnnounceUnit("Multiplication");
42+
43+
PrintResult($expected == $got, 'Any multiplication', $got, $expected)
44+
}
45+
46+
AnnounceTestSuite("Mathematical Operations");
47+
test_Division();
48+
test_Multiply();
49+
```
50+

composer.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "lordofscripts/php-neat-test",
3+
"description": "A private package for making PHP test runs neater.",
4+
"autoload": {
5+
"psr-4": {
6+
"lordofscripts\\php-neat-test\\": "src/"
7+
}
8+
},
9+
"minimum-stability": "dev",
10+
"prefer-stable": true
11+
}

src/TestContext.php

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
namespace Coralys\Tests;
3+
4+
/**
5+
* A simple utility to pretty-up your test suites by simply
6+
* and neatly printing out the PASS/Fail result with comment and checkmark.
7+
* The user only has to call AnnounceCase() at the beginning of the test case
8+
* and then PrintResult() to test the condition which internally takes care
9+
* of updating the tally. Internally a shutdown function is registered so that
10+
* when the test suite file terminates, a summary of passed/failed is presented.
11+
*
12+
* Example:
13+
* use Coralys\Tests;
14+
* function testCaseOne {
15+
* AnnounceCase("Testing constructor");
16+
* : do the test
17+
* PrintResult($got == $obtained, 'Constructor with parameters');
18+
* }
19+
*/
20+
21+
ini_set('display_errors', '1');
22+
ini_set('display_startup_errors', '1');
23+
require_once('functions.php');
24+
25+
/*
26+
* G L O B A L S
27+
*/
28+
$Ctx = new TestContext(); // But no need to access it outside. PrintResult takes care of it.
29+
30+
/*
31+
* C L A S S E S
32+
*/
33+
34+
/**
35+
* A Test Context is a unique instance for every file that runs one or
36+
* more tests. It must be instantiated in the file that has the test cases.
37+
* Example:
38+
* $Ctx = new Coralys\Tests\TestContext();
39+
*/
40+
class TestContext {
41+
public $Passed;
42+
public $Failed;
43+
public $SuiteCount;
44+
public $TotalPassed;
45+
public $TotalFailed;
46+
public $CurrentSuite;
47+
48+
public function Pass() {
49+
$this->Passed += 1;
50+
}
51+
52+
public function Fail() {
53+
$this->Failed += 1;
54+
}
55+
56+
/**
57+
* Usually invoked by AnnounceTestSuite() and saves the
58+
* current tally to the total (accumulative) and resets
59+
* the current count for the new test suite.
60+
* @see AnnounceTestSuite()
61+
*/
62+
public function Accumulate(string $currentSuite) {
63+
$this->TotalPassed += $this->Passed;
64+
$this->TotalFailed += $this->Failed;
65+
$this->Passed = 0;
66+
$this->Failed = 0;
67+
$this->SuiteCount += 1;
68+
$this->CurrentSuite = $currentSuite;
69+
}
70+
71+
/**
72+
* Resets both the current test suite tally as well as the
73+
* totals.
74+
*/
75+
public function Reset() {
76+
$this->Passed = 0;
77+
$this->Failed = 0;
78+
$this->TotalPassed = 0;
79+
$this->TotalFailed = 0;
80+
}
81+
82+
private function SubTotal(): int {
83+
return $this->Passed + $this->Failed;
84+
}
85+
86+
private function Total(): int {
87+
return $this->TotalPassed + $this->TotalFailed;
88+
}
89+
90+
private function SubPercentPassed(): int {
91+
if ($this->SubTotal() == 0) {
92+
return 0;
93+
}
94+
return intval(100 * $this->Passed / $this->SubTotal());
95+
}
96+
97+
private function TotalPercentPassed(): int {
98+
return intval(100 * $this->Passed / $this->Total());
99+
}
100+
101+
public function String() {
102+
printf("\n\n\tUnit Test Summary\n");
103+
printf("\t(%s)\n", $this->CurrentSuite);
104+
if ($this->SuiteCount > 1) {
105+
106+
}
107+
$pctPassed = $this->SubPercentPassed();
108+
$pctFailed = 100 - $pctPassed;
109+
printf("\t============================\n\t\u{2705} Passed: %3d / %-4d (%d%%)\n",
110+
$this->Passed, $this->SubTotal(), $pctPassed
111+
);
112+
if ($this->Failed > 0) {
113+
printf("\t\u{274c} Failed: %3d / %-4d (%d%%)\n",
114+
$this->Failed, $this->SubTotal(), $pctFailed
115+
);
116+
}
117+
}
118+
}
119+
120+
121+
function shutdown()
122+
{
123+
// This is our shutdown function, in
124+
// here we can do any last operations
125+
// before the script is complete.
126+
127+
//echo 'Script executed with success', PHP_EOL;
128+
global $Ctx;
129+
$Ctx->String();
130+
}
131+
132+
register_shutdown_function('Coralys\Tests\shutdown');
133+
134+
/**
135+
* Convert a boolean to OK or FAILED.
136+
* @see PrintResult()
137+
*/
138+
function Result($condition) {
139+
global $Ctx;
140+
if ($condition) {
141+
$Ctx->Pass();
142+
} else {
143+
$Ctx->Fail();
144+
}
145+
return $condition ? 'OK' : 'FAILED';
146+
}
147+
148+
149+
?>
150+

src/functions.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
namespace Coralys\Tests;
3+
4+
/*
5+
* L O C A L F U N C T I O N S
6+
*/
7+
8+
/**
9+
* Annouce a new Test Suite (a collection of Unit tests).
10+
* It resets the current TestContext instance.
11+
*/
12+
function AnnounceTestSuite($title) {
13+
global $Ctx;
14+
$filler = (70 - mb_strlen($title) - 6) / 2;
15+
echo str_repeat("\u{22c6}", 70) . PHP_EOL;
16+
printf("\u{22c6}\u{22c6}%s %s %s\u{22c6}\u{22c6}\n", str_repeat(' ', $filler), $title, str_repeat(' ', $filler));
17+
echo str_repeat("\u{22c6}", 70) . PHP_EOL . PHP_EOL;
18+
$Ctx->Accumulate($title);
19+
}
20+
21+
/**
22+
* Announce a new test Unit, invoked at the top of the test unit function.
23+
* A test unit may contain several test case variants.
24+
* Example:
25+
* use function Coralys\Tests\AnnounceCase;
26+
* AnnounceCase("Test symmetric encryption");
27+
*/
28+
function AnnounceUnit($title) {
29+
$filler = (70 - mb_strlen($title)) / 2;
30+
printf("%s %s %s\n", str_repeat("\u{269b}", $filler), $title, str_repeat("\u{269b}", $filler));
31+
}
32+
33+
function AnnounceCase($title) {
34+
$filler = (70 - mb_strlen($title) - 4) / 2;
35+
printf("%s\u{2668} %s \u{2668}%s\n", str_repeat(' ', $filler), $title, str_repeat(' ', $filler));
36+
}
37+
38+
/**
39+
* Prettyfy an object's member variable or property
40+
*/
41+
function PropName($class_name, $method_name, $comment='') {
42+
return sprintf("%s\u{27a4}%s %s", $class_name, $method_name, $comment);
43+
}
44+
45+
/**
46+
* Prettyfy an object's Method name.
47+
*/
48+
function MethName($class_name, $method_name, $comment='') {
49+
return sprintf("%s\u{27a4}%s() %s", $class_name, $method_name, $comment);
50+
}
51+
52+
/**
53+
* Convert a boolean to OK or FAILED and print it
54+
*/
55+
function PrintResult($condition, $descr = null, $got = null, $expected = null) {
56+
// COLORED
57+
//$mark = $condition ? "\u{2705}" : "\u{274c}";
58+
// NOT COLORED
59+
$mark = $condition ? "\u{2714}" : "\u{2718}"; // ✓ OR
60+
if (!is_null($descr)) {
61+
printf("%s Test Case: %s\n", $mark, $descr);
62+
}
63+
if (!$condition && !is_null($got)) {
64+
printf("\tGot : %s\n", $got);
65+
}
66+
if (!is_null($expected)) {
67+
printf("\tExpected: %s\n", $expected);
68+
}
69+
//printf("\tResult: %s\n", $condition ? 'OK' : 'FAILED');
70+
printf("\tResult: %s\n", Result($condition));
71+
}
72+
73+
function Indent(string $str): string {
74+
$pattern = '/(\n)/m';
75+
$replacement = "$1\t\t";
76+
return "\t" . preg_replace($pattern, $replacement, $str);
77+
}
78+
?>

0 commit comments

Comments
 (0)