1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Tester\Runner;
9:
10: use Tester;
11:
12:
13: 14: 15: 16: 17:
18: class Runner
19: {
20: const
21: PASSED = 1,
22: SKIPPED = 2,
23: FAILED = 3;
24:
25:
26: public $paths = array();
27:
28:
29: public $threadCount = 1;
30:
31:
32: public $testHandler;
33:
34:
35: public $outputHandlers = array();
36:
37:
38: private $php;
39:
40:
41: private $jobs;
42:
43:
44: private $jobCount;
45:
46:
47: private $results;
48:
49:
50: private $interrupted;
51:
52:
53: public function __construct(PhpExecutable $php)
54: {
55: $this->php = $php;
56: $this->testHandler = new TestHandler($this);
57: }
58:
59:
60: 61: 62: 63:
64: public function run()
65: {
66: foreach ($this->outputHandlers as $handler) {
67: $handler->begin();
68: }
69:
70: $this->results = array(self::PASSED => 0, self::SKIPPED => 0, self::FAILED => 0);
71: $this->jobs = $running = array();
72: foreach ($this->paths as $path) {
73: $this->findTests($path);
74: }
75: $this->jobCount = count($this->jobs) + array_sum($this->results);
76:
77: $this->installInterruptHandler();
78: while (($this->jobs || $running) && !$this->isInterrupted()) {
79: for ($i = count($running); $this->jobs && $i < $this->threadCount; $i++) {
80: $running[] = $job = array_shift($this->jobs);
81: $job->run($this->threadCount <= 1 || (count($running) + count($this->jobs) <= 1));
82: }
83:
84: if (count($running) > 1) {
85: usleep(Job::RUN_USLEEP);
86: }
87:
88: foreach ($running as $key => $job) {
89: if ($this->isInterrupted()) {
90: break 2;
91: }
92:
93: if (!$job->isRunning()) {
94: $this->testHandler->assess($job);
95: unset($running[$key]);
96: }
97: }
98: }
99: $this->removeInterruptHandler();
100:
101: foreach ($this->outputHandlers as $handler) {
102: $handler->end();
103: }
104: return !$this->results[self::FAILED];
105: }
106:
107:
108: 109: 110:
111: private function findTests($path)
112: {
113: if (is_dir($path)) {
114: foreach (glob("$path/*", GLOB_ONLYDIR) as $dir) {
115: $this->findTests($dir);
116: }
117: $path .= '/*.phpt';
118: }
119: foreach (glob($path) as $file) {
120: if (is_file($file)) {
121: $this->testHandler->initiate(realpath($file));
122: }
123: }
124: }
125:
126:
127: 128: 129: 130:
131: public function addJob(Job $job)
132: {
133: $this->jobs[] = $job;
134: }
135:
136:
137: 138: 139: 140:
141: public function getJobCount()
142: {
143: return $this->jobCount;
144: }
145:
146:
147: 148: 149: 150:
151: public function writeResult($testName, $result, $message = NULL)
152: {
153: $this->results[$result]++;
154: foreach ($this->outputHandlers as $handler) {
155: $handler->result($testName, $result, $message);
156: }
157: }
158:
159:
160: 161: 162:
163: public function getPhp()
164: {
165: return $this->php;
166: }
167:
168:
169: 170: 171:
172: public function getResults()
173: {
174: return $this->results;
175: }
176:
177:
178: 179: 180:
181: private function installInterruptHandler()
182: {
183: $this->interrupted = FALSE;
184:
185: if (extension_loaded('pcntl')) {
186: $interrupted = & $this->interrupted;
187: pcntl_signal(SIGINT, function() use (& $interrupted) {
188: pcntl_signal(SIGINT, SIG_DFL);
189: $interrupted = TRUE;
190: });
191: }
192: }
193:
194:
195: 196: 197:
198: private function removeInterruptHandler()
199: {
200: if (extension_loaded('pcntl')) {
201: pcntl_signal(SIGINT, SIG_DFL);
202: }
203: }
204:
205:
206: 207: 208:
209: private function isInterrupted()
210: {
211: if (extension_loaded('pcntl')) {
212: pcntl_signal_dispatch();
213: }
214:
215: return $this->interrupted;
216: }
217:
218: }
219: