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