1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: namespace Jyxo\Beholder;
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
39: class Executor
40: {
41: 42: 43: 44: 45:
46: const OUTPUT_TEXT = 't';
47:
48: 49: 50: 51: 52:
53: const OUTPUT_HTML = 'h';
54:
55: 56: 57: 58: 59:
60: const PARAM_OUTPUT = 'o';
61:
62: 63: 64: 65: 66:
67: const PARAM_INCLUDE = 't';
68:
69: 70: 71: 72: 73:
74: const PARAM_EXCLUDE = 'nt';
75:
76: 77: 78: 79: 80:
81: private $project = '';
82:
83: 84: 85: 86: 87:
88: private $tests = array();
89:
90: 91: 92: 93: 94:
95: private $includeFilter = '*';
96:
97: 98: 99: 100: 101:
102: private $excludeFilter = '';
103:
104: 105: 106: 107: 108:
109: private $output = self::OUTPUT_HTML;
110:
111: 112: 113: 114: 115: 116:
117: public function __construct($project, array $params = array())
118: {
119:
120: $this->project = (string) $project;
121:
122:
123: if (!empty($params[self::PARAM_INCLUDE])) {
124: $this->includeFilter = (string) $params[self::PARAM_INCLUDE];
125: }
126: if (!empty($params[self::PARAM_EXCLUDE])) {
127: $this->excludeFilter = (string) $params[self::PARAM_EXCLUDE];
128: }
129:
130:
131: if (!empty($params[self::PARAM_OUTPUT])) {
132: switch ($params[self::PARAM_OUTPUT]) {
133:
134: case self::OUTPUT_TEXT:
135: $this->output = self::OUTPUT_TEXT;
136: break;
137:
138: case self::OUTPUT_HTML:
139: default:
140: $this->output = self::OUTPUT_HTML;
141: break;
142: }
143: }
144: }
145:
146: 147: 148: 149: 150:
151: public function run()
152: {
153:
154: foreach (array_keys($this->tests) as $ident) {
155: if (!$this->includeTest($ident)) {
156: unset($this->tests[$ident]);
157: }
158: }
159:
160:
161: $idents = array_keys($this->tests);
162: shuffle($idents);
163:
164:
165: $outputData = array();
166: $order = 1;
167: $allSucceeded = true;
168: foreach ($idents as $ident) {
169:
170: $data = $this->runTest($ident);
171:
172:
173: $allSucceeded = $allSucceeded && $data['result']->isSuccess();
174:
175:
176: $data['order'] = $order++;
177: $outputData[] = $data;
178: }
179:
180:
181: $idents = array();
182: foreach ($outputData as $key => $data) {
183: $idents[$key] = $data['ident'];
184: }
185: array_multisort($idents, SORT_ASC, $outputData);
186:
187:
188: if ($allSucceeded) {
189: header('HTTP/1.1 200 OK');
190: } else {
191: header('HTTP/1.1 500 Internal Server Error');
192: }
193:
194:
195: switch ($this->output) {
196:
197: case self::OUTPUT_TEXT:
198: $this->writeText($allSucceeded, $outputData);
199: break;
200:
201: case self::OUTPUT_HTML:
202: default:
203: $this->writeHtml($allSucceeded, $outputData);
204: break;
205: }
206:
207: return $allSucceeded;
208: }
209:
210: 211: 212: 213: 214: 215: 216:
217: public function addTest($ident, \Jyxo\Beholder\TestCase $test)
218: {
219: $this->tests[(string) $ident] = $test;
220:
221: return $this;
222: }
223:
224: 225: 226: 227: 228: 229: 230:
231: private function runTest($ident)
232: {
233:
234: $timer = \Jyxo\Timer::start();
235: $result = $this->tests[$ident]->run();
236: if (!($result instanceof \Jyxo\Beholder\Result)) {
237: throw new \UnexpectedValueException(sprintf('Result %s of the test %s is not a \Jyxo\Beholder\Result instance.', $result, $ident));
238: }
239:
240:
241: return array(
242: 'ident' => $ident,
243: 'test' => $this->tests[$ident],
244: 'result' => $result,
245: 'duration' => \Jyxo\Timer::stop($timer)
246: );
247: }
248:
249: 250: 251: 252: 253: 254:
255: private function includeTest($ident)
256: {
257:
258: $include = false;
259: foreach (explode(',', $this->includeFilter) as $pattern) {
260: if ($this->patternMatch($pattern, $ident)) {
261:
262: $include = true;
263: }
264: }
265: if (!$include) {
266: return false;
267: }
268:
269:
270: foreach (explode(',', $this->excludeFilter) as $pattern) {
271: if ($this->patternMatch($pattern, $ident)) {
272: return false;
273: }
274: }
275:
276:
277: return true;
278: }
279:
280: 281: 282: 283: 284: 285: 286:
287: private function patternMatch($pattern, $string)
288: {
289: return fnmatch($pattern, $string);
290: }
291:
292: 293: 294: 295: 296: 297:
298: private function writeHtml($allSucceeded, array $outputData)
299: {
300: header('Content-Type: text/html; charset=utf-8');
301: echo '<head>' . "\n";
302: echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />' . "\n";
303: echo '<title>Beholder for project ' . $this->project . '</title>' . "\n";
304: echo '<style>' . "\n";
305: echo ' body {font: 12px Verdana, Geneva, Arial, Helvetica, sans-serif;}' . "\n";
306: echo ' table {font-size: small; border-collapse: collapse;}' . "\n";
307: echo ' table th {border: 1px solid #000; background: #000; color: #fff;}' . "\n";
308: echo ' table td {border: 1px solid #000; padding: .25em .5em;}' . "\n";
309: echo '</style>' . "\n";
310: echo '</head>' . "\n";
311: echo '<body style="background-color: ' . ($allSucceeded ? '#ccffcc' : '#ffcccc') . '; width: 90%; height: 100%; padding: 1em; margin: 0;">' . "\n";
312: echo '<h1>Beholder for project ' . $this->project . "</h1>\n";
313: echo '<p>Tests included: ' . $this->includeFilter . "\n";
314: echo '<br>Tests excluded: ' . $this->excludeFilter . "\n";
315: echo '</p>' . "\n";
316: echo '<table><tr><th>Run order</th><th>Duration</th><th>Ident</th><th>Status</th><th>Test name</th><th>Comment</th></tr>' . "\n";
317: foreach ($outputData as $data) {
318: echo sprintf('
319: <tr>
320: <td>%d</td>
321: <td>%.2fs</td>
322: <td>%s</td>
323: <td style="color: %s;">%s</td>
324: <td><b>%s</b></td>
325: <td><i>%s</i></td>
326: </tr>' . "\n",
327: $data['order'],
328: $data['duration'],
329: $data['ident'],
330: $data['result']->isSuccess() ? 'green' : 'red; font-weight: bold;', $data['result']->getStatusMessage(),
331: $data['test']->getDescription(),
332: $data['result']->getDescription()
333: );
334: }
335: echo '</table>
336: <h2>Parameters</h2>
337: <dl>
338: <dt>' . self::PARAM_INCLUDE . '</dt>
339: <dd>Tests to include, list of shell patterns separated by comma, default *</dd>
340: <dt>' . self::PARAM_EXCLUDE . '</dt>
341: <dd>Tests to exclude, empty by default</dd>
342: <dt>' . self::PARAM_OUTPUT . '</dt>
343: <dd>' . self::OUTPUT_HTML . ' = HTML output, ' . self::OUTPUT_TEXT . ' = text output</dd>
344: </dl>
345: <p>Tests are included, then excluded.</p>
346: <p><a href="?' . self::PARAM_INCLUDE . '=' . $this->includeFilter
347: . '&' . self::PARAM_EXCLUDE . '=' . $this->excludeFilter
348: . '&' . self::PARAM_OUTPUT . '=' . self::OUTPUT_TEXT . '">Text version</a></p>
349: </body>' . "\n";
350: }
351:
352: 353: 354: 355: 356: 357:
358: private function writeText($allSucceeded, array $outputData)
359: {
360:
361: header('Content-Type: text/html; charset=utf-8');
362: echo '<pre>This is Beholder for project ' . $this->project . "\n";
363: echo 'Tests included: ' . $this->includeFilter . "\n";
364: echo 'Tests excluded: ' . $this->excludeFilter . "\n\n";
365: echo '<a href="?' . self::PARAM_INCLUDE . '=' . $this->includeFilter
366: . '&' . self::PARAM_EXCLUDE . '=' . $this->excludeFilter
367: . '&' . self::PARAM_OUTPUT . '=' . self::OUTPUT_HTML . "\">Html version</a>\n\n";
368:
369: echo sprintf("%-9s %10s %-10s %-7s %-35s %s\n",
370: 'Run Order', 'Duration', 'Ident', 'Status', 'Test Name', 'Description');
371: foreach ($outputData as $data) {
372: echo sprintf("%9d %9.2fs %-10s %-7s %-35s %s\n",
373: $data['order'],
374: $data['duration'],
375: $data['ident'],
376: $data['result']->getStatusMessage(),
377: $data['test']->getDescription(),
378: $data['result']->getDescription());
379: }
380:
381: if ($allSucceeded) {
382: echo "\nJust a little prayer so we know we are allright.\n\n";
383:
384: for ($i = 0; $i < 5; $i++) {
385: echo 'Our Father in heaven,' . "\n";
386: echo 'hallowed be your name,' . "\n";
387: echo 'your kingdom come,' . "\n";
388: echo 'your will be done' . "\n";
389: echo 'on earth as it is in heaven.' . "\n";
390: echo 'Give us today our daily bread,' . "\n";
391: echo 'and forgive us the wrong we have done' . "\n";
392: echo 'as we forgive those who wrong us.' . "\n";
393: echo 'Subject us not to the trial' . "\n";
394: echo 'but deliver us from the evil one.' . "\n";
395: echo 'And make the ' . $this->project . " project work.\n";
396: echo 'Amen.' . "\n\n";
397: }
398: }
399: }
400: }
401: