guard-rackunit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/ChangeLog +3 -0
- data/LICENSE +674 -0
- data/README.md +82 -0
- data/lib/guard/rackunit.rb +88 -0
- data/lib/guard/rackunit/command.rb +48 -0
- data/lib/guard/rackunit/notifier.rb +35 -0
- data/lib/guard/rackunit/run_result.rb +108 -0
- data/lib/guard/rackunit/runner.rb +61 -0
- data/lib/guard/rackunit/templates/Guardfile +4 -0
- data/lib/guard/rackunit/version.rb +22 -0
- data/spec/lib/guard/rackunit/command_spec.rb +29 -0
- data/spec/lib/guard/rackunit/notifier_spec.rb +16 -0
- data/spec/lib/guard/rackunit/run_result_spec.rb +216 -0
- data/spec/lib/guard/rackunit/runner_spec.rb +84 -0
- data/spec/lib/guard/rackunit_spec.rb +133 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/helpers.rb +86 -0
- data/spec/support/samples/failed_tests +12 -0
- data/spec/support/samples/runtime_error +16 -0
- data/spec/support/samples/success +2 -0
- metadata +135 -0
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
|
3
|
+
`Guard::RackUnit` is a Guard plugin to run
|
4
|
+
[Racket's](http:/racket-lang.org)
|
5
|
+
[RackUnit](http://docs.racket-lang.org/rackunit/index.html) unit
|
6
|
+
tests.
|
7
|
+
|
8
|
+
By default, `Guard::RackUnit` uses the `raco test` command to run
|
9
|
+
tests. Consequently, your RackUnit tests should be placed in
|
10
|
+
Racket test modules: `(module+ test ...)`.
|
11
|
+
|
12
|
+
```
|
13
|
+
guard init rackunit
|
14
|
+
```
|
15
|
+
|
16
|
+
## Setup
|
17
|
+
There are many ways to set up Guard. For the non-Rubyist, the ceremony
|
18
|
+
described below is a typical setup.
|
19
|
+
|
20
|
+
1. Install a relatively recent version of Ruby.
|
21
|
+
2. Install [bundler](http://bundler.io)
|
22
|
+
3. Install [rvm](https://rvm.io)
|
23
|
+
4. Create a `Gemfile` in your Racket project's root directory. To the
|
24
|
+
`Gemfile`, add both the guard gem and the guard-rackunit gem. See
|
25
|
+
the example below.
|
26
|
+
5. In the directory where the `Gemfile` is stored, run `bundle install`
|
27
|
+
|
28
|
+
If everything successfully installs, you are now ready to use
|
29
|
+
Guard. Consult the usage section below on how to use the RackUnit
|
30
|
+
plugin.
|
31
|
+
|
32
|
+
### Example Gemfile
|
33
|
+
``` ruby
|
34
|
+
source 'https://www.rubygems.org'
|
35
|
+
gem "guard", "~> 2.5.1"
|
36
|
+
gem 'guard-rackunit', path: '/home/calbers/src/mine/guard-rackunit'
|
37
|
+
```
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
To use the RackUnit Guard plugin, Guard must be initialized. To do so,
|
41
|
+
execute the following command in your project's root directory: `bundle
|
42
|
+
exec guard init`. This should create a `Guardfile` with a default
|
43
|
+
setup for RackUnit.
|
44
|
+
|
45
|
+
Now, to start Guard, execute the following command: `bundle exec guard
|
46
|
+
start`.
|
47
|
+
|
48
|
+
Please consult Guard's own
|
49
|
+
[usage notes](https://github.com/guard/guard#readme) for more
|
50
|
+
information.
|
51
|
+
|
52
|
+
##List of available options:
|
53
|
+
``` ruby
|
54
|
+
test_paths: ['tests/'] # Specify an array of paths that contain unit test files
|
55
|
+
all_on_start: true # Run all the tests at startup, default: false
|
56
|
+
```
|
57
|
+
|
58
|
+
## Support and Issues
|
59
|
+
Please submit support questions, feature requests, and issues to
|
60
|
+
the Github repository's [issue tracker](https://github.com/neomantic/guard-rackunit/issues).
|
61
|
+
|
62
|
+
## Updates
|
63
|
+
Consult the ChangeLog when upgrading to newer versions.
|
64
|
+
|
65
|
+
## Development
|
66
|
+
The source code is hosted at
|
67
|
+
[GitHub](https://github.com/neomantic/guard-rackunit).
|
68
|
+
|
69
|
+
Pull requests are more than welcome. To contribute, please add new
|
70
|
+
unit tests to the existing suite of Ruby
|
71
|
+
[rspec](https://relishapp.com/rspec) unit tests.
|
72
|
+
|
73
|
+
## Requirements
|
74
|
+
1. Racket, with `raco test` support
|
75
|
+
2. Ruby, and the gems which `Guard::RackUnit` depends on.
|
76
|
+
|
77
|
+
## Author
|
78
|
+
[Chad Albers](https://github.com/neomantic)
|
79
|
+
|
80
|
+
## License
|
81
|
+
This Guard plugin is released under the GPLv3. Consult the LICENSE
|
82
|
+
file for more details
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'guard/plugin'
|
2
|
+
# Guard::RackUnit
|
3
|
+
# Copyright (C) 2014 Chad Albers
|
4
|
+
#
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
module Guard
|
19
|
+
class RackUnit < Plugin
|
20
|
+
|
21
|
+
require_relative 'rackunit/runner'
|
22
|
+
|
23
|
+
# Initializes a Guard plugin.
|
24
|
+
# Don't do any work here, especially as Guard plugins get initialized even if they are not in an active group!
|
25
|
+
#
|
26
|
+
# @param [Hash] options the custom Guard plugin options
|
27
|
+
# @option options [Array<Guard::Watcher>] watchers the Guard plugin file watchers
|
28
|
+
# @option options [Symbol] group the group this Guard plugin belongs to
|
29
|
+
# @option options [Boolean] any_return allow any object to be returned from a watcher
|
30
|
+
#
|
31
|
+
def initialize(options = {})
|
32
|
+
super
|
33
|
+
@start_on_run = options.delete(:all_on_start) || false
|
34
|
+
@test_paths = options.delete(:test_paths) || []
|
35
|
+
@runner = RackUnit::Runner.new
|
36
|
+
end
|
37
|
+
|
38
|
+
# Called once when Guard starts. Please override initialize method to init stuff.
|
39
|
+
#
|
40
|
+
# @raise [:task_has_failed] when start has failed
|
41
|
+
# @return [Object] the task result
|
42
|
+
#
|
43
|
+
def start
|
44
|
+
::Guard::UI.info 'Guard::RackUnit is running'
|
45
|
+
return run_all if @start_on_run
|
46
|
+
pending_result
|
47
|
+
end
|
48
|
+
|
49
|
+
# Called when just `enter` is pressed
|
50
|
+
# This method should be principally used for long action like running all specs/tests/...
|
51
|
+
#
|
52
|
+
# @raise [:task_has_failed] when run_all has failed
|
53
|
+
# @return [Object] the task result
|
54
|
+
#
|
55
|
+
def run_all
|
56
|
+
return pending_result unless test_paths?
|
57
|
+
Guard::UI.info("Resetting", reset: true)
|
58
|
+
do_run{ @runner.run(@test_paths) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Called on file(s) modifications that the Guard plugin watches.
|
62
|
+
#
|
63
|
+
# @param [Array<String>] paths the changes files or paths
|
64
|
+
# @raise [:task_has_failed] when run_on_modifications has failed
|
65
|
+
# @return [Object] the task result
|
66
|
+
#
|
67
|
+
def run_on_modifications(paths)
|
68
|
+
return pending_result if paths.empty?
|
69
|
+
Guard::UI.info("Running: #{paths.join(', ')}", reset: true)
|
70
|
+
do_run{ @runner.run(paths) }
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def do_run
|
75
|
+
run_result = yield
|
76
|
+
run_result.issue_notification
|
77
|
+
run_result.successful? ? run_result : throw(:task_has_failed)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_paths?
|
81
|
+
!@test_paths.nil? && !@test_paths.empty?
|
82
|
+
end
|
83
|
+
|
84
|
+
def pending_result
|
85
|
+
@pending ||= RunResult::Pending.new
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
# Copyright (C) 2014 Chad Albers
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'open3'
|
18
|
+
require_relative './run_result'
|
19
|
+
module Guard
|
20
|
+
class RackUnit
|
21
|
+
class Command
|
22
|
+
|
23
|
+
def execute(paths)
|
24
|
+
return RunResult::Pending.new if paths.empty?
|
25
|
+
cmd = sprintf('%s %s', DEFAULT_CMD_STR, paths.join(' '))
|
26
|
+
with_environment do
|
27
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
|
28
|
+
pid = wait_thr.pid
|
29
|
+
wait_thr.value
|
30
|
+
RunResult.create(stdout, stderr)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_environment
|
36
|
+
if defined?(::Bundler)
|
37
|
+
::Bundler.with_clean_env { yield }
|
38
|
+
else
|
39
|
+
yield
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
DEFAULT_CMD_STR = 'raco test'.freeze
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
# Copyright (C) 2014 Chad Albers
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
module Guard
|
18
|
+
class RackUnit
|
19
|
+
class Notifier
|
20
|
+
|
21
|
+
DEFAULT_OPTIONS = {
|
22
|
+
title: 'RackUnit Results',
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(message)
|
26
|
+
@message = message
|
27
|
+
end
|
28
|
+
|
29
|
+
def notify(options)
|
30
|
+
::Guard::Notifier.notify(@message,
|
31
|
+
DEFAULT_OPTIONS.merge(options))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
# Copyright (C) 2014 Chad Albers
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'set'
|
18
|
+
require_relative './notifier'
|
19
|
+
module Guard
|
20
|
+
class RackUnit
|
21
|
+
class RunResult
|
22
|
+
|
23
|
+
def self.create(stdout, stderr)
|
24
|
+
err_byte = stderr.getbyte
|
25
|
+
if err_byte.nil?
|
26
|
+
Success.new(stdout)
|
27
|
+
else
|
28
|
+
puts stdout.readlines
|
29
|
+
stderr.ungetbyte(err_byte)
|
30
|
+
Failure.new(stderr)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Pending
|
35
|
+
def issue_notification; end
|
36
|
+
def paths; Set[]; end
|
37
|
+
def successful?; false; end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Success
|
41
|
+
|
42
|
+
def initialize(result_io)
|
43
|
+
@message = "Success"
|
44
|
+
result_io.each_line do |line|
|
45
|
+
puts @message = line
|
46
|
+
end
|
47
|
+
@message.chomp!
|
48
|
+
end
|
49
|
+
|
50
|
+
def issue_notification
|
51
|
+
Notifier.new(@message).notify NOTIFY_OPTIONS
|
52
|
+
end
|
53
|
+
|
54
|
+
def paths; Set[]; end
|
55
|
+
|
56
|
+
def successful?; true; end
|
57
|
+
|
58
|
+
private
|
59
|
+
NOTIFY_OPTIONS = {image: :success, priority: -2}.freeze
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
class Failure
|
65
|
+
|
66
|
+
def initialize(stderr)
|
67
|
+
@message = "Failed"
|
68
|
+
@failed_paths_set = Set[]
|
69
|
+
stderr.each_line do |line|
|
70
|
+
puts line # give user some feedback
|
71
|
+
|
72
|
+
if line =~ ERROR_REGEX
|
73
|
+
# an exception as raised
|
74
|
+
@message = stderr.readline
|
75
|
+
puts @message
|
76
|
+
@message = "ERROR: #{@message.strip}"
|
77
|
+
stderr.each_line{|line| puts line}
|
78
|
+
else
|
79
|
+
match_data = line.match(FAILURE_REGEX)
|
80
|
+
unless match_data.nil?
|
81
|
+
@failed_paths_set.add(match_data[1])
|
82
|
+
else
|
83
|
+
# the last line should have the results summary
|
84
|
+
@message = line
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
@message.chomp!
|
89
|
+
end
|
90
|
+
|
91
|
+
def issue_notification
|
92
|
+
Notifier.new(@message).notify NOTIFY_OPTIONS
|
93
|
+
end
|
94
|
+
|
95
|
+
def paths
|
96
|
+
@failed_paths_set || Set[]
|
97
|
+
end
|
98
|
+
|
99
|
+
def successful?; false; end
|
100
|
+
|
101
|
+
private
|
102
|
+
FAILURE_REGEX = /\Alocation:.+path:(.+)>/
|
103
|
+
NOTIFY_OPTIONS = {image: :failed, priority: 2}.freeze
|
104
|
+
ERROR_REGEX = /context#{Regexp.escape('...')}:/
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
# Copyright (C) 2014 Chad Albers
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require_relative './run_result'
|
18
|
+
require_relative './command'
|
19
|
+
|
20
|
+
module Guard
|
21
|
+
class RackUnit
|
22
|
+
class Runner
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@last_run_result = pending_result
|
26
|
+
end
|
27
|
+
|
28
|
+
# This class runs a Command (which will be customizable)
|
29
|
+
# And produces a run result
|
30
|
+
def run(paths = [])
|
31
|
+
return pending_result if paths.empty?
|
32
|
+
path_set = PathSet.new(@last_run_result.paths, Set.new(paths))
|
33
|
+
@last_run_result = Command.new.execute(path_set.to_a)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def pending_result
|
38
|
+
RunResult::Pending.new
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
class PathSet
|
46
|
+
|
47
|
+
def initialize(previous_set, new_set)
|
48
|
+
@set = previous_set | select_existing(new_set)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_a
|
52
|
+
@set.to_a
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def select_existing(paths)
|
57
|
+
paths.select{|path| File.exists?(path)}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Guard::RackUnit
|
2
|
+
# Copyright (C) 2014 Chad Albers
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'guard'
|
18
|
+
module Guard
|
19
|
+
class RackUnit
|
20
|
+
VERSION = '1.0.0'
|
21
|
+
end
|
22
|
+
end
|