guard-phpunit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Maher Sallam
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ Guard::PHPUnit
2
+ ==============
3
+
4
+ PHPUnit guard allows to automatically & intelligently launch tests when files
5
+ are modified.
6
+
7
+ Tested on MRI Ruby 1.8.7, 1.9.2 and 1.9.3.
8
+
9
+ Install
10
+ -------
11
+
12
+ Please be sure to have [Ruby][1] running on your machine.
13
+ The latest versions of Ruby come with a packages-manger called Gem. Gem can be used to
14
+ install various packages, including PHPUnit guard.
15
+
16
+ Before you continue, also make sure you have the [Guard][2] gem installed
17
+
18
+ To install the PHPUnit gem, run the following command in the terminal:
19
+
20
+ gem install guard-phpunit
21
+
22
+ Usage
23
+ -----
24
+
25
+ Please read the [Guard usage documentation][3].
26
+
27
+ Guardfile
28
+ ---------
29
+
30
+ Guard::PHPUnit can be used with any kind of PHP projects that uses PHPUnit as
31
+ its testing framwork. Please read the [Guard documentation][3] for more information
32
+ about the Guardfile DSL.
33
+
34
+ By default, Guard::PHPUnit will use the current working directory (pwd) to
35
+ search for tests and run them on start (if you enabled the `:all_on_start` option).
36
+
37
+ ### Example PHP project
38
+
39
+ The [PHPUnit documentaion][4] uses the [Object Freezer][5] library as an example on how
40
+ to organize tests. This project uses the `Tests` directory for its tests.
41
+
42
+ An example of the Guardfile for the same project would look
43
+ something like:
44
+
45
+ ```ruby
46
+ guard 'phpunit', :tests_path => 'Tests', :cli => '--colors' do
47
+ # Watch tests files
48
+ watch(%r{^.+Test\.php$})
49
+
50
+ # Watch library files and run their tests
51
+ watch(%r{^Object/(.+)\.php}) { |m| "Tests/#{m[1]}Test.php" }
52
+ end
53
+ ```
54
+
55
+ Options
56
+ -------
57
+
58
+ The following options can be passed to Guard::PHPUnit:
59
+
60
+ ```ruby
61
+ :all_on_start => false # Run all tests on startup.
62
+ # default: true
63
+
64
+ :tests_path # Relative path to the tests directory. This path
65
+ # is used when running all the tests.
66
+ # default: the current working directory (pwd)
67
+
68
+ :cli # The options passed to the phpunit command
69
+ # when running the tests.
70
+ # default: nil
71
+ ```
72
+
73
+ Development
74
+ -----------
75
+
76
+ * Source hosted at [GitHub](https://github.com/Maher4Ever/guard-phpunit)
77
+ * Report issues/Questions/Feature requests on [GitHub Issues](https://github.com/Maher4Ever/guard-phpunit/issues)
78
+
79
+ Pull requests are very welcome! Make sure your patches are well tested. Please create a topic branch for every separate change
80
+ you make.
81
+
82
+ Author
83
+ ------
84
+
85
+ [Maher Sallam](https://github.com/Maher4Ever)
86
+
87
+ [1]:http://ruby-lang.org
88
+ [2]:https://github.com/guard/guard
89
+ [3]:https://github.com/guard/guard#readme
90
+ [4]:http://www.phpunit.de/manual/current/en/
91
+ [5]:https://github.com/sebastianbergmann/php-object-freezer/
@@ -0,0 +1,110 @@
1
+ module Guard
2
+ class PHPUnit
3
+
4
+ # The Guard::PHPUnit formatter parses the output
5
+ # of phpunit which gets printed by the progress
6
+ # printer and formats the parsed results
7
+ # for the notifier.
8
+ #
9
+ module Formatter
10
+ class << self
11
+
12
+ # Parses the tests output.
13
+ #
14
+ # @param [String] text the output of phpunit.
15
+ # @return [Hash] the parsed results
16
+ #
17
+ def parse_output(text)
18
+ results = {
19
+ :tests => look_for_words_in('test', text),
20
+ :failures => look_for_words_in('failure', text),
21
+ :errors => look_for_words_in('error', text),
22
+ :pending => look_for_words_in(['skipped', 'incomplete'], text),
23
+ :duration => look_for_duration_in(text)
24
+ }
25
+ results.freeze
26
+ end
27
+
28
+ # Outputs a system notification.
29
+ #
30
+ # @param [Hash] test_results the parsed tests results
31
+ # @option test_results [Integer] :tests tests count
32
+ # @option test_results [Integer] :failures failures count
33
+ # @option test_results [Integer] :errors count count
34
+ # @option test_results [Integer] :pending pending tests count
35
+ # @option test_results [Integer] :duration tests duration
36
+ #
37
+ def notify(test_results)
38
+ ::Guard::Notifier.notify(notifier_message(test_results), {
39
+ :title => 'PHPUnit results',
40
+ :image => notifier_image(test_results)
41
+ })
42
+ end
43
+
44
+ private
45
+
46
+ # Searches for a list of strings in the tests output
47
+ # and returns the total number assigned to these strings.
48
+ #
49
+ # @param [String, Array<String>] string_list the words
50
+ # @param [String] text the tests output
51
+ # @return [Integer] the total number assigned to the words
52
+ #
53
+ def look_for_words_in(strings_list, text)
54
+ count = 0
55
+ strings_list = Array(strings_list)
56
+ strings_list.each do |s|
57
+ text =~ %r{
58
+ (\d+) # count of what we are looking for
59
+ [ ] # then a space
60
+ #{s}s? # then the string
61
+ .* # then whatever
62
+ \Z # start looking at the end of the text
63
+ }x
64
+ count += $1.to_i unless $1.nil?
65
+ end
66
+ count
67
+ end
68
+
69
+ # Searches for the duration in the tests output
70
+ #
71
+ # @param [String] text the tests output
72
+ # @return [Integer] the duration
73
+ #
74
+ def look_for_duration_in(text)
75
+ text =~ %r{Finished in (\d)+ seconds?.*\Z}m
76
+ $1.nil? ? 0 : $1.to_i
77
+ end
78
+
79
+ # Formats the message for the Notifier.
80
+ #
81
+ # @param (see .notify)
82
+ # @return [String] the message
83
+ #
84
+ def notifier_message(results)
85
+ message = "#{results[:tests]} tests, #{results[:failures]} failures"
86
+ message << "\n#{results[:errors]} errors" if results[:errors] > 0
87
+ message << " (#{results[:pending]} pending)" if results[:pending] > 0
88
+ message << "\nin #{results[:duration]} seconds"
89
+ message
90
+ end
91
+
92
+ # Returns the appropriate image for the tests results
93
+ #
94
+ # @param (see .notify)
95
+ # @return [Symbol] the image symbol
96
+ #
97
+ def notifier_image(results)
98
+ case
99
+ when results[:failures] + results[:errors] > 0
100
+ :failed
101
+ when results[:pending] > 0
102
+ :pending
103
+ else
104
+ :success
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,498 @@
1
+ <?php
2
+
3
+ /**
4
+ * Prints tests' results in a similar way
5
+ * to rspec's progress formatter.
6
+ *
7
+ * @package PHPUnit
8
+ * @subpackage Progress
9
+ * @author Maher Sallam <maher@sallam.me>
10
+ * @copyright 2011 Maher Sallam <maher@sallam.me>
11
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
12
+ * @version 0.1
13
+ */
14
+ class PHPUnit_Extensions_Progress_ResultPrinter extends PHPUnit_TextUI_ResultPrinter {
15
+
16
+ /**
17
+ * Constructor.
18
+ *
19
+ * @param mixed $out
20
+ * @param boolean $verbose
21
+ * @param boolean $colors
22
+ * @param boolean $debug
23
+ */
24
+ public function __construct($out = NULL, $verbose = FALSE, $colors = FALSE, $debug = FALSE) {
25
+
26
+ // Start capturing output
27
+ ob_start();
28
+
29
+ $argv = $_SERVER['argv'];
30
+ $colors = in_array('--colors', $argv) || $colors;
31
+ $verbose = in_array('--verbose', $argv) || in_array('-v', $argv) || $verbose;
32
+ $debug = in_array('--debug', $argv) || $debug;
33
+
34
+ parent::__construct($out, $verbose, $colors, $debug);
35
+ }
36
+
37
+ /**
38
+ * @param PHPUnit_Framework_TestResult $result
39
+ */
40
+ public function printResult(PHPUnit_Framework_TestResult $result)
41
+ {
42
+ print "\n";
43
+
44
+ if ($result->errorCount() > 0) {
45
+ $this->printErrors($result);
46
+ }
47
+
48
+ if ($result->failureCount() > 0) {
49
+ $this->printFailures($result);
50
+ }
51
+
52
+ if ($this->verbose) {
53
+ if ($result->deprecatedFeaturesCount() > 0) {
54
+ if ($result->failureCount() > 0) {
55
+ print "\n--\n\nDeprecated PHPUnit features are being used";
56
+ }
57
+
58
+ foreach ($result->deprecatedFeatures() as $deprecatedFeature) {
59
+ $this->write($deprecatedFeature . "\n\n");
60
+ }
61
+ }
62
+
63
+ if ($result->notImplementedCount() > 0) {
64
+ $this->printIncompletes($result);
65
+ }
66
+
67
+ if ($result->skippedCount() > 0) {
68
+ $this->printSkipped($result);
69
+ }
70
+ }
71
+
72
+ $this->printFooter($result);
73
+ }
74
+
75
+ /**
76
+ * @param array $defects
77
+ * @param integer $count
78
+ * @param string $type
79
+ */
80
+ protected function printDefects(array $defects, $count, $type)
81
+ {
82
+ if ($count == 0) {
83
+ return;
84
+ }
85
+
86
+ $this->write("\n" . $type . ":\n");
87
+
88
+ $i = 1;
89
+ $failOrError = $type == 'Failures' || $type == 'Errors';
90
+
91
+ foreach ($defects as $defect) {
92
+ $this->printDefect($defect, $i++, $failOrError);
93
+ $this->write("\n");
94
+ }
95
+ }
96
+
97
+ /**
98
+ * @param PHPUnit_Framework_TestFailure $defect
99
+ * @param integer $count
100
+ * @param boolean $failOrError
101
+ */
102
+ protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count, $failOrError = true)
103
+ {
104
+ $this->printDefectHeader($defect, $count, $failOrError);
105
+
106
+ $padding = str_repeat(' ',
107
+ 4 + ( $failOrError ? strlen((string)$count) : 0 )
108
+ );
109
+
110
+ $this->printDefectBody($defect, $count, $failOrError, $padding);
111
+ $this->printDefectTrace($defect, $padding);
112
+ }
113
+
114
+ /**
115
+ * @param PHPUnit_Framework_TestFailure $defect
116
+ * @param integer $count
117
+ * @param boolean $failOrError
118
+ */
119
+ protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count, $failOrError = true)
120
+ {
121
+ $failedTest = $defect->failedTest();
122
+
123
+ if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) {
124
+ $testName = $failedTest->toString();
125
+ } else {
126
+ $testName = get_class($failedTest);
127
+ }
128
+
129
+ if ( $failOrError ) {
130
+ $this->write(
131
+ sprintf(
132
+ "\n %d) %s",
133
+
134
+ $count,
135
+ $testName
136
+ )
137
+ );
138
+ } else {
139
+ $this->write(
140
+ sprintf( " %s", $this->yellow($testName) )
141
+ );
142
+ }
143
+ }
144
+
145
+ /**
146
+ * @param PHPUnit_Framework_TestFailure $defect
147
+ * @param integer $count
148
+ * @param boolean $failOrError
149
+ * @param string $padding
150
+ */
151
+ protected function printDefectBody(PHPUnit_Framework_TestFailure $defect, $count, $failOrError, $padding)
152
+ {
153
+ $error = trim($defect->getExceptionAsString());
154
+
155
+ if ( !empty($error) ) {
156
+ $error = explode("\n", $error);
157
+ $error = "\n" . $padding . implode("\n " . $padding , $error);
158
+
159
+ $this->write( $failOrError ? $this->red($error) : $this->cyan($error) );
160
+ }
161
+ }
162
+
163
+ /**
164
+ * @param PHPUnit_Framework_TestFailure $defect
165
+ * @param string $padding
166
+ */
167
+ protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect, $padding = 0)
168
+ {
169
+ $trace = trim(
170
+ PHPUnit_Util_Filter::getFilteredStacktrace(
171
+ $defect->thrownException()
172
+ )
173
+ );
174
+
175
+ if ( ! empty($trace) ) {
176
+ $trace = explode("\n", $trace);
177
+ $trace = "\n" . $padding . '# ' . implode("\n${padding}# ", $trace);
178
+
179
+ $this->write($this->cyan($trace));
180
+ }
181
+ }
182
+
183
+ /**
184
+ * @param PHPUnit_Framework_TestResult $result
185
+ */
186
+ protected function printErrors(PHPUnit_Framework_TestResult $result)
187
+ {
188
+ $this->printDefects(
189
+ $result->errors(),
190
+ $result->errorCount(),
191
+ 'Errors'
192
+ );
193
+ }
194
+
195
+ /**
196
+ * @param PHPUnit_Framework_TestResult $result
197
+ */
198
+ protected function printFailures(PHPUnit_Framework_TestResult $result)
199
+ {
200
+ $this->printDefects(
201
+ $result->failures(),
202
+ $result->failureCount(),
203
+ 'Failures'
204
+ );
205
+ }
206
+
207
+ /**
208
+ * @param PHPUnit_Framework_TestResult $result
209
+ */
210
+ protected function printIncompletes(PHPUnit_Framework_TestResult $result)
211
+ {
212
+ $this->printDefects(
213
+ $result->notImplemented(),
214
+ $result->notImplementedCount(),
215
+ 'Incomplete tests'
216
+ );
217
+ }
218
+
219
+ /**
220
+ * @param PHPUnit_Framework_TestResult $result
221
+ * @since Method available since Release 3.0.0
222
+ */
223
+ protected function printSkipped(PHPUnit_Framework_TestResult $result)
224
+ {
225
+ $this->printDefects(
226
+ $result->skipped(),
227
+ $result->skippedCount(),
228
+ 'Skipped tests'
229
+ );
230
+ }
231
+
232
+ /**
233
+ * @param PHPUnit_Framework_TestResult $result
234
+ */
235
+ protected function printFooter(PHPUnit_Framework_TestResult $result)
236
+ {
237
+
238
+ $this->write( sprintf("\nFinished in %s\n", PHP_Timer::timeSinceStartOfRequest()) );
239
+
240
+ $resultsCount = count($result);
241
+
242
+ $footer = sprintf("%d test%s, %d assertion%s",
243
+ $resultsCount,
244
+ $resultsCount == 1 ? '' : 's',
245
+ $this->numAssertions,
246
+ $this->numAssertions == 1 ? '' : 's'
247
+ );
248
+
249
+ if ( $result->wasSuccessful() &&
250
+ $result->allCompletlyImplemented() &&
251
+ $result->noneSkipped() )
252
+ {
253
+ $this->write($this->green($footer));
254
+ }
255
+
256
+ else if ( ( !$result->allCompletlyImplemented() || !$result->noneSkipped() )
257
+ &&
258
+ $result->wasSuccessful() )
259
+ {
260
+
261
+ $footer .= sprintf(
262
+ "%s%s",
263
+
264
+ $this->getCountString(
265
+ $result->notImplementedCount(), 'incomplete'
266
+ ),
267
+ $this->getCountString(
268
+ $result->skippedCount(), 'skipped'
269
+ )
270
+ );
271
+
272
+ $this->write($this->yellow($footer));
273
+
274
+ }
275
+
276
+ else {
277
+
278
+ $footer .= sprintf(
279
+ "%s%s%s%s",
280
+
281
+ $this->getCountString($result->failureCount(), 'failures'),
282
+ $this->getCountString($result->errorCount(), 'errors'),
283
+ $this->getCountString(
284
+ $result->notImplementedCount(), 'incomplete'
285
+ ),
286
+ $this->getCountString($result->skippedCount(), 'skipped')
287
+ );
288
+
289
+ $footer = preg_replace('/,$/', '', $footer);
290
+
291
+ $this->write($this->red($footer));
292
+ }
293
+
294
+ if ( ! $this->verbose &&
295
+ $result->deprecatedFeaturesCount() > 0 )
296
+ {
297
+ $message = sprintf(
298
+ "Warning: Deprecated PHPUnit features are being used %s times!\n".
299
+ "Use --verbose for more information.\n",
300
+ $result->deprecatedFeaturesCount()
301
+ );
302
+
303
+ if ($this->colors) {
304
+ $message = "\x1b[37;41m\x1b[2K" . $message .
305
+ "\x1b[0m";
306
+ }
307
+
308
+ $this->write("\n" . $message);
309
+ }
310
+
311
+ $this->writeNewLine();
312
+ }
313
+
314
+ /**
315
+ * @param integer $count
316
+ * @param string $name
317
+ * @return string
318
+ * @since Method available since Release 3.0.0
319
+ */
320
+ protected function getCountString($count, $name)
321
+ {
322
+ $string = '';
323
+
324
+ if ($count > 0) {
325
+ $string = sprintf(
326
+ ', %d %s',
327
+
328
+ $count,
329
+ $name
330
+ );
331
+ }
332
+
333
+ return $string;
334
+ }
335
+
336
+ /**
337
+ * An error occurred.
338
+ *
339
+ * @param PHPUnit_Framework_Test $test
340
+ * @param Exception $e
341
+ * @param float $time
342
+ */
343
+ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
344
+ {
345
+ $this->writeProgress($this->red('E'));
346
+ $this->lastTestFailed = TRUE;
347
+ }
348
+
349
+ /**
350
+ * A failure occurred.
351
+ *
352
+ * @param PHPUnit_Framework_Test $test
353
+ * @param PHPUnit_Framework_AssertionFailedError $e
354
+ * @param float $time
355
+ */
356
+ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
357
+ {
358
+ $this->writeProgress($this->red('F'));
359
+ $this->lastTestFailed = TRUE;
360
+ }
361
+
362
+ /**
363
+ * Incomplete test.
364
+ *
365
+ * @param PHPUnit_Framework_Test $test
366
+ * @param Exception $e
367
+ * @param float $time
368
+ */
369
+ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
370
+ {
371
+ $this->writeProgress($this->yellow('I'));
372
+ $this->lastTestFailed = TRUE;
373
+ }
374
+
375
+ /**
376
+ * Skipped test.
377
+ *
378
+ * @param PHPUnit_Framework_Test $test
379
+ * @param Exception $e
380
+ * @param float $time
381
+ * @since Method available since Release 3.0.0
382
+ */
383
+ public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
384
+ {
385
+ $this->writeProgress($this->yellow('S'));
386
+ $this->lastTestFailed = TRUE;
387
+ }
388
+
389
+ /**
390
+ * A test ended.
391
+ *
392
+ * @param PHPUnit_Framework_Test $test
393
+ * @param float $time
394
+ */
395
+ public function endTest(PHPUnit_Framework_Test $test, $time)
396
+ {
397
+ if (!$this->lastTestFailed) {
398
+ $this->writeProgress($this->green('.'));
399
+ }
400
+
401
+ if ($test instanceof PHPUnit_Framework_TestCase) {
402
+ $this->numAssertions += $test->getNumAssertions();
403
+ }
404
+
405
+ else if ($test instanceof PHPUnit_Extensions_PhptTestCase) {
406
+ $this->numAssertions++;
407
+ }
408
+
409
+ $this->lastTestFailed = FALSE;
410
+
411
+ if ($this->verbose && $test instanceof PHPUnit_Framework_TestCase) {
412
+ $this->write($test->getActualOutput());
413
+ }
414
+
415
+ }
416
+
417
+ /**
418
+ * @param string $progress
419
+ */
420
+ protected function writeProgress($progress)
421
+ {
422
+ static $deletedHeader = false;
423
+
424
+ if ( ! $deletedHeader ) {
425
+ ob_clean();
426
+ $deletedHeader = true;
427
+ }
428
+
429
+ parent::writeProgress($progress);
430
+ }
431
+
432
+ /**
433
+ * Returns a colored string which can be used
434
+ * in the terminal.
435
+ *
436
+ * @param string $text
437
+ * @param integer $color_code
438
+ */
439
+ protected function color($text, $color_code) {
440
+ return $this->colors ? "\033[${color_code}m" . $text . "\033[0m" : $text;
441
+ }
442
+
443
+ /**
444
+ * @param string $text
445
+ */
446
+ protected function bold($text) {
447
+ return $this->color($text, "1");
448
+ }
449
+
450
+ /**
451
+ * @param string $text
452
+ */
453
+ protected function red($text) {
454
+ return $this->color($text, "31");
455
+ }
456
+
457
+ /**
458
+ * @param string $text
459
+ */
460
+ protected function green($text) {
461
+ return $this->color($text, "32");
462
+ }
463
+
464
+ /**
465
+ * @param string $text
466
+ */
467
+ protected function yellow($text) {
468
+ return $this->color($text, "33");
469
+ }
470
+
471
+ /**
472
+ * @param string $text
473
+ */
474
+ protected function blue($text) {
475
+ return $this->color($text, "34");
476
+ }
477
+
478
+ /**
479
+ * @param string $text
480
+ */
481
+ protected function magenta($text) {
482
+ return $this->color($text, "35");
483
+ }
484
+
485
+ /**
486
+ * @param string $text
487
+ */
488
+ protected function cyan($text) {
489
+ return $this->color($text, "36");
490
+ }
491
+
492
+ /**
493
+ * @param string $text
494
+ */
495
+ protected function white($text) {
496
+ return $this->color($text, "37");
497
+ }
498
+ }
@@ -0,0 +1,54 @@
1
+ module Guard
2
+ class PHPUnit
3
+
4
+ # The Guard::PHPUnit inspector verfies that the changed paths
5
+ # are valid for Guard::PHPUnit.
6
+ #
7
+ module Inspector
8
+ class << self
9
+
10
+ attr_accessor :tests_path
11
+
12
+ # Clean the changed paths and return only valid
13
+ # PHPUnit tests files.
14
+ #
15
+ # @param [Array<String>] paths the changed paths
16
+ # @return [Array<String>] the valid tests files
17
+ #
18
+ def clean(paths)
19
+ paths.uniq!
20
+ paths.compact!
21
+ paths = paths.select { |p| test_file?(p) }
22
+ clear_tests_files_list
23
+ paths
24
+ end
25
+
26
+ private
27
+
28
+ # Checks if the paths is a valid test file.
29
+ #
30
+ # @param [String] path the test path
31
+ # @return [Boolean] whether the path a valid test or not
32
+ #
33
+ def test_file?(path)
34
+ tests_files.include?(path)
35
+ end
36
+
37
+ # Scans the tests path and keeps a list of all
38
+ # tests paths.
39
+ #
40
+ def tests_files
41
+ @tests_files ||= Dir.glob( File.join(tests_path, '**', '*Test.php') )
42
+ end
43
+
44
+ # Clears the list of PHPUnit tests.
45
+ #
46
+ # @see #clean
47
+ #
48
+ def clear_tests_files_list
49
+ @tests_files = nil
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,170 @@
1
+ require 'tmpdir'
2
+ require 'fileutils'
3
+
4
+ module Guard
5
+ class PHPUnit
6
+
7
+ # The Guard::PHPUnit runner handles running the tests, displaying
8
+ # their output and notifying the user about the results.
9
+ #
10
+ module Runner
11
+ class << self
12
+
13
+ PHPUNIT_ERRORS_EXITCODE = 2
14
+
15
+ # Runs the PHPUnit tests and displays notifications
16
+ # about the results.
17
+ #
18
+ # @param [Array<Strings>] path to the tests files.
19
+ # @param (see PHPUnit#initialize)
20
+ # @return [Boolean] whether the tests were run successfully
21
+ #
22
+ def run(paths, options = {})
23
+ paths = Array(paths)
24
+ return false if paths.empty?
25
+
26
+ notify_start(paths, options)
27
+ output = run_tests(paths, options)
28
+
29
+ print_output output
30
+
31
+ # return false in case the system call fails with no status!
32
+ return false if $?.nil?
33
+
34
+ if $?.success? or tests_contain_errors?
35
+ notify_results(output, options)
36
+ else
37
+ notify_failure(options)
38
+ end
39
+
40
+ $?.success?
41
+ end
42
+
43
+ private
44
+
45
+ # Displays the start testing notification.
46
+ #
47
+ # @param (see #run)
48
+ # @param (see #run)
49
+ #
50
+ def notify_start(paths, options)
51
+ message = options[:message] || "Running: #{paths.join(' ')}"
52
+ UI.info(message, :reset => true)
53
+ end
54
+
55
+ # Executes the testing commandon the tests
56
+ # and returns the output.
57
+ #
58
+ # @param (see #run)
59
+ # @param (see #run)
60
+ #
61
+ def run_tests(paths, options)
62
+ if paths.length == 1
63
+ tests_path = paths.first
64
+ output = execute_command phpunit_command(tests_path, options)
65
+ else
66
+ create_tests_folder_for(paths) do |tests_folder|
67
+ output = execute_command phpunit_command(tests_folder, options)
68
+ end
69
+ end
70
+ output
71
+ end
72
+
73
+ # Prints the tests output to the terminal.
74
+ #
75
+ # @param [String] output the tests output
76
+ #
77
+ def print_output(output)
78
+ UI.info output
79
+ end
80
+
81
+ # Displays a notification about the tests results.
82
+ #
83
+ # @param [String] output the tests output
84
+ # @param (see #run)
85
+ #
86
+ def notify_results(output, options)
87
+ return if options[:notification] == false
88
+ results = Formatter.parse_output(output)
89
+ Formatter.notify(results)
90
+ end
91
+
92
+ # Displays a notification about failing to run the tests
93
+ #
94
+ # @param (see #run)
95
+ #
96
+ def notify_failure(options)
97
+ return if options[:notification] == false
98
+ ::Guard::Notifier.notify('Failed! Check the console', :title => 'PHPUnit results', :image => :failed)
99
+ end
100
+
101
+ # Checks the exitstatus of the phpunit command
102
+ # for a sign of errors in the tests.
103
+ #
104
+ # @return [Boolean] whether the tests contain errors or not
105
+ #
106
+ def tests_contain_errors?
107
+ $?.exitstatus == PHPUNIT_ERRORS_EXITCODE
108
+ end
109
+
110
+ # Creates a temporary folder which has links to
111
+ # the tests paths. This method is used because PHPUnit
112
+ # can't run multiple tests files at the same time and generate
113
+ # one result for them.
114
+ #
115
+ # @param (see #run)
116
+ # @yield [String] d the temporary dir for the tests
117
+ #
118
+ def create_tests_folder_for(paths)
119
+ Dir.mktmpdir('guard_phpunit') do |d|
120
+ symlink_paths_to_tests_folder(paths, d)
121
+ yield d
122
+ end
123
+ end
124
+
125
+ # Creates symbolic links inside the folder pointing
126
+ # back to the paths.
127
+ #
128
+ # @see #create_tests_folder_for
129
+ #
130
+ # @param (see #run)
131
+ # @param [String] the folder in which the links must be made
132
+ #
133
+ def symlink_paths_to_tests_folder(paths, folder)
134
+ paths.each do |p|
135
+ FileUtils.mkdir_p( File.join(folder, File.dirname(p) ) ) unless File.dirname(p) == '.'
136
+ FileUtils.ln_s(Pathname.new(p).realpath, File.join(folder, p))
137
+ end
138
+ end
139
+
140
+ # Generates the phpunit command for the tests paths.
141
+ #
142
+ # @param (see #run)
143
+ # @param (see #run)
144
+ # @see #run_tests
145
+ #
146
+ def phpunit_command(path, options)
147
+ formatter_path = File.join( File.dirname(__FILE__), 'formatters', 'PHPUnit-Progress')
148
+
149
+ cmd_parts = []
150
+ cmd_parts << "phpunit"
151
+ cmd_parts << "--include-path #{formatter_path}"
152
+ cmd_parts << "--printer PHPUnit_Extensions_Progress_ResultPrinter"
153
+ cmd_parts << options[:cli] if options[:cli]
154
+ cmd_parts << path
155
+
156
+ cmd_parts.join(' ')
157
+ end
158
+
159
+ # Executes a system command and returns the output.
160
+ #
161
+ # @param [String] command the command to be run
162
+ # @return [String] the output of the executed command
163
+ #
164
+ def execute_command(command)
165
+ %x{#{command}}
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,3 @@
1
+ guard 'phpunit', :cli => '--colors' do
2
+ watch(%r{^.+Test\.php$})
3
+ end
@@ -0,0 +1,6 @@
1
+ module Guard
2
+ # Guard::PHPUnitVersion is used in the gem specification.
3
+ module PHPUnitVersion
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
@@ -0,0 +1,64 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Guard
5
+
6
+ # The PHPUnit guard gets notified about system
7
+ # events.
8
+ #
9
+ class PHPUnit < Guard
10
+
11
+ autoload :Inspector, 'guard/phpunit/inspector'
12
+ autoload :Formatter, 'guard/phpunit/formatter'
13
+ autoload :Runner, 'guard/phpunit/runner'
14
+
15
+ DEFAULT_OPTIONS = {
16
+ :all_on_start => true,
17
+ :tests_path => Dir.pwd
18
+ }
19
+
20
+ # Initialize Guard::PHPUnit.
21
+ #
22
+ # @param [Array<Guard::Watcher>] watchers the watchers in the Guard block
23
+ # @param [Hash] options the options for the Guard
24
+ # @option options [Boolean] :all_on_start run all tests on start
25
+ # @option options [String] :cli The CLI arguments passed to phpunit
26
+ # @option options [String] :tests_path the path where all tests exist
27
+ #
28
+ def initialize(watchers = [], options = {})
29
+ defaults = DEFAULT_OPTIONS.clone
30
+ @options = defaults.merge(options)
31
+ super(watchers, @options)
32
+ Inspector.tests_path = @options[:tests_path]
33
+ end
34
+
35
+ # Gets called once when Guard starts.
36
+ #
37
+ # @raise [:task_has_failed] when stop has failed
38
+ #
39
+ def start
40
+ run_all if options[:all_on_start]
41
+ end
42
+
43
+ # Gets called when all tests should be run.
44
+ #
45
+ # @raise (see #start)
46
+ #
47
+ def run_all
48
+ success = Runner.run(options[:tests_path], options.merge(
49
+ :message => 'Running all tests'
50
+ ))
51
+ throw :task_has_failed unless success
52
+ end
53
+
54
+ # Gets called when the watched tests have changes.
55
+ #
56
+ # @param [Array<String>] paths to the changed tests
57
+ # @raise (see #start)
58
+ #
59
+ def run_on_change(paths)
60
+ success = Runner.run(paths, options)
61
+ throw :task_has_failed unless success
62
+ end
63
+ end
64
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guard-phpunit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Maher Sallam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: guard
16
+ requirement: &13084420 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.8.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *13084420
25
+ - !ruby/object:Gem::Dependency
26
+ name: bundler
27
+ requirement: &13083340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *13083340
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &13082360 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '2.7'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *13082360
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-rspec
49
+ requirement: &13081360 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.5'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *13081360
58
+ description: Guard::PHPUnit automatically run your unit-tests written with the PHPUnit
59
+ testing framework.
60
+ email:
61
+ - maher@sallam.me
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - lib/guard/phpunit.rb
67
+ - lib/guard/phpunit/formatter.rb
68
+ - lib/guard/phpunit/inspector.rb
69
+ - lib/guard/phpunit/runner.rb
70
+ - lib/guard/phpunit/templates/Guardfile
71
+ - lib/guard/phpunit/version.rb
72
+ - lib/guard/phpunit/formatters/PHPUnit-Progress/PHPUnit/Extensions/Progress/ResultPrinter.php
73
+ - LICENSE
74
+ - README.md
75
+ homepage: ''
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: 1.3.6
93
+ requirements: []
94
+ rubyforge_project: guard-phpunit
95
+ rubygems_version: 1.8.10
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Guard gem for PHPUnit
99
+ test_files: []
100
+ has_rdoc: