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.
@@ -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,4 @@
1
+ # This Guardfile was generated from Guard::RackUnit
2
+ guard :rack_unit, all_on_start: true, test_paths: ["tests/"] do
3
+ watch(%r{^tests/.+-tests.rkt$})
4
+ 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