guard-rackunit 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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