1 <?php
2
3 namespace Alo\Test;
4
5 use Alo\Exception\TesterException as TE;
6 use Alo\Statics\Format as F;
7
8 if(!defined('GEN_START')) {
9 http_response_code(404);
10 die();
11 }
12
13 /**
14 * The abstract tester class
15 *
16 * @author Art <a.molcanovas@gmail.com>
17 * @package TestingSuite
18 * @deprecated Since v1.1
19 */
20 abstract class AbstractTester {
21
22 /**
23 * Defines a parameter as "name"
24 *
25 * @var string
26 */
27 const P_NAME = 'name';
28 /**
29 * Defines a parameter as "arguments"
30 *
31 * @var string
32 */
33 const P_ARGS = 'args';
34 /**
35 * Defines a parameter as "expected outcome"
36 *
37 * @var string
38 */
39 const P_OUTCOME = 'outcome';
40 /**
41 * Defines a parameter as "type"
42 *
43 * @var string
44 */
45 const P_TYPE = 'type';
46 /**
47 * Defines a parameter as "definition"
48 *
49 * @var string
50 */
51 const P_DEFINITION = 'definition';
52 /**
53 * Defines a parameter as "passed"
54 *
55 * @var string
56 */
57 const P_PASSED = 'passed';
58 /**
59 * Defines the test type as "output test"
60 *
61 * @var string
62 */
63 const T_OUTPUT = 'output';
64 /**
65 * Defines the test type as "return test"
66 *
67 * @var string
68 */
69 const T_RETURN = 'return';
70 /**
71 * Defines an outcome as "method/function not callable"
72 *
73 * @var string
74 */
75 const O_NOT_CALLABLE = 'Not callable';
76 /**
77 * Defines a parameter as "start of test"
78 *
79 * @var string
80 */
81 const P_TEST_START = 'start';
82 /**
83 * Defines a parameter as "end of test"
84 *
85 * @var string
86 */
87 const P_TEST_END = 'end';
88 /**
89 * Defines a parameter as "test runtime"
90 *
91 * @var string
92 */
93 const P_TEST_RUNTIME = 'runtime';
94 /**
95 * The test queue
96 *
97 * @var array
98 */
99 protected $queue;
100 /**
101 * The test results
102 *
103 * @var array
104 */
105 protected $results;
106
107 /**
108 * Instantiates the tester
109 *
110 * @author Art <a.molcanovas@gmail.com>
111 */
112 function __construct() {
113 $this->queue = $this->results = [];
114 }
115
116 /**
117 * Runs the tests
118 *
119 * @author Art <a.molcanovas@gmail.com>
120 * @return array Test results
121 */
122 function runTests() {
123 foreach($this->queue as $test) {
124 $this->runTest($test);
125 }
126
127 return $this->results;
128 }
129
130 /**
131 * Runs an individual test
132 *
133 * @author Art <a.molcanovas@gmail.com>
134 *
135 * @param array $test The test specs
136 *
137 * @return AbstractTester
138 */
139 protected function runTest(array $test) {
140 $callable = $this->getCallable($test[self::P_DEFINITION][self::P_NAME]);
141
142 $add = [
143 self::P_DEFINITION => $test[self::P_DEFINITION],
144 self::P_TEST_START => microtime(true)
145 ];
146
147 if(!is_callable($callable)) {
148 $add[self::P_PASSED] = false;
149 $add[self::P_OUTCOME] = self::O_NOT_CALLABLE;
150 $add[self::P_TEST_END] = microtime(true);
151 } else {
152 ob_start();
153 $call = call_user_func_array($callable, $test[self::P_DEFINITION][self::P_ARGS]);
154 $ob = ob_get_clean();
155 $add[self::P_TEST_END] = microtime(true);
156
157 $add[self::P_OUTCOME] = $test[self::P_TYPE] == self::T_OUTPUT ? $ob : $call;
158 $add[self::P_PASSED] = $add[self::P_OUTCOME] == $test[self::P_DEFINITION][self::P_OUTCOME];
159 }
160
161 $add[self::P_TEST_RUNTIME] = $add[self::P_TEST_END] - $add[self::P_TEST_START];
162 $this->results[] = $add;
163
164 return $this;
165 }
166
167 /**
168 * Returns the callable parameter for call_user_func_array()
169 *
170 * @author Art <a.molcanovas@gmail.com>
171 *
172 * @param string $name Function/method name
173 *
174 * @return array|string
175 */
176 abstract protected function getCallable($name);
177
178 /**
179 * Adds a test for the output
180 *
181 * @author Art <a.molcanovas@gmail.com>
182 *
183 * @param string $name The method/function name
184 * @param mixed $outcome The expected outcome
185 * @param array $args The arguments to pass on to the function/method
186 *
187 * @throws TE When the method/function name is invalid
188 * @return AbstractTester
189 */
190 function addOutputTest($name, $outcome, array $args = []) {
191 return $this->addGenericTest(self::T_OUTPUT, $name, $outcome, $args);
192 }
193
194 /**
195 * Adds a test for the output
196 *
197 * @author Art <a.molcanovas@gmail.com>
198 *
199 * @param string $type The type of the test - see this class' T_* constants
200 * @param string $name The method/function name
201 * @param mixed $outcome The expected outcome
202 * @param array $args The arguments to pass on to the function/method
203 *
204 * @throws TE When the method/function name is invalid
205 * @return AbstractTester
206 */
207 protected function addGenericTest($type, $name, $outcome, array $args = []) {
208 if(!is_string($name)) {
209 throw new TE('The name must be a string!', TE::E_NAME_INVALID);
210 } else {
211 $this->queue[] = [
212 self::P_TYPE => $type,
213 self::P_DEFINITION => [
214 self::P_NAME => $name,
215 self::P_ARGS => $args,
216 self::P_OUTCOME => $outcome
217 ]
218 ];
219 }
220
221 return $this;
222 }
223
224 /**
225 * Adds a test for the return value
226 *
227 * @author Art <a.molcanovas@gmail.com>
228 *
229 * @param string $name The method/function name
230 * @param mixed $outcome The expected outcome
231 * @param array $args The arguments to pass on to the function/method
232 *
233 * @throws TE When the method/function name is invalid
234 * @return AbstractTester
235 */
236 function addReturnTest($name, $outcome, array $args = []) {
237 return $this->addGenericTest(self::T_RETURN, $name, $outcome, $args);
238 }
239
240 /**
241 * Returns the common queue
242 *
243 * @return array
244 */
245 function getQueue() {
246 return $this->queue;
247 }
248
249 /**
250 * Returns the testing results
251 *
252 * @author Art <a.molcanovas@gmail.com>
253 * @return array
254 */
255 function getResults() {
256 return $this->results;
257 }
258
259 /**
260 * Returns the test results as plaintext
261 *
262 * @author Art <a.molcanovas@gmail.com>
263 * @return string
264 */
265 function toPlaintextString() {
266 $ret = "";
267
268 foreach($this->results as $r) {
269 $ret .= $r[self::P_DEFINITION][self::P_NAME] . "\n"
270 . "\tArgs:\t\t\t" . F::scalarOutput(array_shift($r[self::P_DEFINITION][self::P_ARGS])) . "\n";
271
272 foreach($r[self::P_DEFINITION][self::P_ARGS] as $arg) {
273 $ret .= "\t\t\t\t" . F::scalarOutput($arg) . "\n";
274 }
275
276 $ret .= "\tExpected outcome:\t" . F::scalarOutput($r[self::P_DEFINITION][self::P_OUTCOME]) . "\n"
277 . "\tOutcome:\t\t" . F::scalarOutput($r[self::P_OUTCOME]) . "\n"
278 . "\tResult:\t\t\t" . ($r[self::P_PASSED] ? 'PASSED' : 'FAILED') . "\n"
279 . "\tStart time:\t\t" . \timestamp_precise($r[self::P_TEST_START]) . "\n"
280 . "\tEnd time:\t\t" . \timestamp_precise($r[self::P_TEST_END]) . "\n"
281 . "\tRuntime:\t\t" . (($r[self::P_TEST_END] - $r[self::P_TEST_START]) * 1000) . "ms\n\n";
282
283 }
284
285 return $ret;
286 }
287
288 /**
289 * Returns the test results in an HTML table
290 *
291 * @author Art <a.molcanovas@gmail.com>
292 * @return string HTML code
293 */
294 function __toString() {
295 $ret = '<table border="1" style="background:#000;color:#fff" cellpadding="2">'
296 . '<thead>'
297 . '<tr>'
298 . '<th>Tested item</th>'
299 . '<th>Item args</th>'
300 . '<th>Expected outcome</th>'
301 . '<th>Outcome</th>'
302 . '<th>Result</th>'
303 . '<th>Start time</th>'
304 . '<th>End time</th>'
305 . '<th>Runtime</th>'
306 . '</tr>'
307 . '</thead>'
308 . '<tbody>';
309
310 foreach($this->results as $r) {
311 foreach($r[self::P_DEFINITION][self::P_ARGS] as &$a) {
312 $a = '<span style="color:gold">' . F::scalarOutput($a) . '</span>';
313 }
314
315 $ret .=
316 '<tr>'
317 .
318 '<td>' .
319 $r[self::P_DEFINITION][self::P_NAME] .
320 '</td>'
321 .
322 '<td><ol style="margin:0;"><li>' .
323 implode('</li><li>', $r[self::P_DEFINITION][self::P_ARGS]) .
324 '</li></ol></td>'
325 .
326 '<td><pre>' .
327 F::scalarOutput($r[self::P_DEFINITION][self::P_OUTCOME]) .
328 '</pre></td>'
329 .
330 '<td><pre>' .
331 F::scalarOutput($r[self::P_OUTCOME]) .
332 '</pre></td>'
333 .
334 '<td style="color:';
335
336 if($r[self::P_PASSED]) {
337 $ret .= 'lime">PASSED';
338 } else {
339 $ret .= 'red">FAILED';
340 }
341
342 $ret .= '</td>'
343 . '<td>' . \timestamp_precise($r[self::P_TEST_START]) . '</td>'
344 . '<td>' . \timestamp_precise($r[self::P_TEST_END]) . '</td>'
345 . '<td>' . (($r[self::P_TEST_END] - $r[self::P_TEST_START]) * 1000) . 'ms</td>'
346 . '</tr>';
347 }
348
349 return $ret . '</tbody></table>';
350 }
351 }