guard-rspec 2.1.0 → 4.7.3

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.hound.yml +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +5 -0
  6. data/.rubocop_todo.yml +40 -0
  7. data/.travis.yml +14 -0
  8. data/CONTRIBUTING.md +38 -0
  9. data/Gemfile +25 -0
  10. data/Guardfile +28 -0
  11. data/{LICENSE → LICENSE.txt} +4 -2
  12. data/README.md +99 -114
  13. data/Rakefile +38 -0
  14. data/gemfiles/Gemfile.rspec-2.99 +6 -0
  15. data/gemfiles/Gemfile.rspec-3.4 +6 -0
  16. data/gemfiles/common +9 -0
  17. data/guard-rspec.gemspec +25 -0
  18. data/lib/guard/rspec/command.rb +71 -0
  19. data/lib/guard/rspec/deprecator.rb +86 -0
  20. data/lib/guard/rspec/dsl.rb +72 -0
  21. data/lib/guard/rspec/inspectors/base_inspector.rb +73 -0
  22. data/lib/guard/rspec/inspectors/factory.rb +23 -0
  23. data/lib/guard/rspec/inspectors/focused_inspector.rb +39 -0
  24. data/lib/guard/rspec/inspectors/keeping_inspector.rb +97 -0
  25. data/lib/guard/rspec/inspectors/simple_inspector.rb +21 -0
  26. data/lib/guard/rspec/notifier.rb +55 -0
  27. data/lib/guard/rspec/options.rb +37 -0
  28. data/lib/guard/rspec/results.rb +23 -0
  29. data/lib/guard/rspec/rspec_process.rb +93 -0
  30. data/lib/guard/rspec/runner.rb +71 -174
  31. data/lib/guard/rspec/templates/Guardfile +49 -17
  32. data/lib/guard/rspec/version.rb +1 -1
  33. data/lib/guard/rspec.rb +30 -59
  34. data/lib/guard/rspec_defaults.rb +5 -0
  35. data/lib/guard/rspec_formatter.rb +147 -0
  36. data/lib/guard/rspec_formatter_results_path.rb +29 -0
  37. data/spec/acceptance/fixtures/succeeding_spec.rb +4 -0
  38. data/spec/acceptance/formatter_spec.rb +46 -0
  39. data/spec/lib/guard/rspec/command_spec.rb +95 -0
  40. data/spec/lib/guard/rspec/deprecator_spec.rb +101 -0
  41. data/spec/lib/guard/rspec/inspectors/base_inspector_spec.rb +144 -0
  42. data/spec/lib/guard/rspec/inspectors/factory_spec.rb +45 -0
  43. data/spec/lib/guard/rspec/inspectors/focused_inspector_spec.rb +140 -0
  44. data/spec/lib/guard/rspec/inspectors/keeping_inspector_spec.rb +200 -0
  45. data/spec/lib/guard/rspec/inspectors/shared_examples.rb +121 -0
  46. data/spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb +59 -0
  47. data/spec/lib/guard/rspec/notifier_spec.rb +90 -0
  48. data/spec/lib/guard/rspec/results_spec.rb +66 -0
  49. data/spec/lib/guard/rspec/rspec_process_spec.rb +152 -0
  50. data/spec/lib/guard/rspec/runner_spec.rb +372 -0
  51. data/spec/lib/guard/rspec/template_spec.rb +78 -0
  52. data/spec/lib/guard/rspec_formatter_spec.rb +277 -0
  53. data/spec/lib/guard/rspec_spec.rb +91 -0
  54. data/spec/spec_helper.rb +145 -0
  55. metadata +103 -42
  56. data/lib/guard/rspec/formatter.rb +0 -56
  57. data/lib/guard/rspec/inspector.rb +0 -72
@@ -1,202 +1,99 @@
1
- require 'drb/drb'
2
- require 'rspec'
1
+ require "guard/rspec_defaults"
2
+
3
+ require "guard/rspec/inspectors/factory"
4
+ require "guard/rspec/command"
5
+ require "guard/rspec/notifier"
6
+ require "guard/rspec/results"
7
+ require "guard/rspec/rspec_process"
3
8
 
4
9
  module Guard
5
- class RSpec
10
+ class RSpec < Plugin
6
11
  class Runner
7
-
8
- FAILURE_EXIT_CODE = 2
9
-
10
- def initialize(options = {})
11
- @options = {
12
- :bundler => true,
13
- :binstubs => false,
14
- :rvm => nil,
15
- :cli => nil,
16
- :env => nil,
17
- :notification => true,
18
- :turnip => false,
19
- :zeus => false
20
- }.merge(options)
21
-
22
- deprecations_warnings
23
- end
24
-
25
- def run(paths, options = {})
26
- return false if paths.empty?
27
-
28
- message = options[:message] || "Running: #{paths.join(' ')}"
29
- UI.info(message, :reset => true)
30
-
31
- options = @options.merge(options)
32
-
33
- if drb_used?
34
- run_via_drb(paths, options)
35
- else
36
- run_via_shell(paths, options)
37
- end
38
- end
39
-
40
- def rspec_executable
41
- @rspec_executable ||= binstubs? ? "#{binstubs}/rspec" : "rspec"
42
- end
43
-
44
- def failure_exit_code_supported?
45
- @failure_exit_code_supported ||= begin
46
- cmd_parts = []
47
- cmd_parts << "bundle exec" if bundle_exec?
48
- cmd_parts << rspec_executable
49
- cmd_parts << "--help"
50
- `#{cmd_parts.join(' ')}`.include? "--failure-exit-code"
51
- end
52
- end
53
-
54
- def parsed_or_default_formatter
55
- @parsed_or_default_formatter ||= begin
56
- # Use RSpec's parser to parse formatters
57
- formatters = ::RSpec::Core::ConfigurationOptions.new([]).parse_options()[:formatters]
58
- # Use a default formatter if none exists.
59
- # RSpec's parser returns an array in the format [[formatter, output], ...], so match their format
60
- formatters = [['progress']] if formatters.nil? || formatters.empty?
61
- # Construct a matching command line option, including output target
62
- formatters.map { |formatter| "-f #{formatter.join ' -o '}" }.join ' '
63
- end
64
- end
65
-
66
- private
67
-
68
- def environment_variables
69
- return if @options[:env].nil?
70
- "export " + @options[:env].map {|key, value| "#{key}=#{value}"}.join(' ') + ';'
71
- end
72
-
73
- def rspec_arguments(paths, options)
74
- arg_parts = []
75
- arg_parts << options[:cli]
76
- if @options[:notification]
77
- arg_parts << parsed_or_default_formatter unless options[:cli] =~ formatter_regex
78
- arg_parts << "-r #{File.dirname(__FILE__)}/formatter.rb"
79
- arg_parts << "-f Guard::RSpec::Formatter --out /dev/null"
12
+ class NoCmdOptionError < RuntimeError
13
+ def initialize
14
+ super "No cmd option specified, unable to run specs!"
80
15
  end
81
- arg_parts << "--failure-exit-code #{FAILURE_EXIT_CODE}" if failure_exit_code_supported?
82
- arg_parts << "-r turnip/rspec" if @options[:turnip]
83
- arg_parts << paths.join(' ')
84
-
85
- arg_parts.compact.join(' ')
86
16
  end
87
17
 
88
- def rspec_command(paths, options)
89
- cmd_parts = []
90
- cmd_parts << environment_variables
91
- cmd_parts << "rvm #{@options[:rvm].join(',')} exec" if @options[:rvm].respond_to?(:join)
92
- cmd_parts << "bundle exec" if bundle_exec?
93
- cmd_parts << 'zeus' if zeus?
94
- cmd_parts << rspec_executable
95
- cmd_parts << rspec_arguments(paths, options)
96
- cmd_parts.compact.join(' ')
97
- end
98
-
99
- def run_via_shell(paths, options)
100
- success = system(rspec_command(paths, options))
101
-
102
- if @options[:notification] && !drb_used? && !success && rspec_command_exited_with_an_exception?
103
- Notifier.notify("Failed", :title => "RSpec results", :image => :failed, :priority => 2)
104
- end
105
-
106
- success
107
- end
108
-
109
- def rspec_command_exited_with_an_exception?
110
- failure_exit_code_supported? && $?.exitstatus != FAILURE_EXIT_CODE
111
- end
18
+ attr_accessor :options, :inspector, :notifier
112
19
 
113
- # We can optimize this path by hitting up the drb server directly, circumventing the overhead
114
- # of the user's shell, bundler and ruby environment.
115
- def run_via_drb(paths, options)
116
- require "shellwords"
117
- argv = rspec_arguments(paths, options).shellsplit
118
-
119
- # The user can specify --drb-port for rspec, we need to honor it.
120
- if idx = argv.index("--drb-port")
121
- port = argv[idx + 1].to_i
20
+ def initialize(options = {})
21
+ @options = options
22
+ @inspector = Inspectors::Factory.create(@options)
23
+ @notifier = Notifier.new(@options)
24
+ end
25
+
26
+ def run_all
27
+ paths = options[:spec_paths]
28
+ options = @options.merge(@options[:run_all])
29
+ return true if paths.empty?
30
+ Compat::UI.info(options[:message], reset: true)
31
+ _run(paths, options)
32
+ end
33
+
34
+ def run(paths)
35
+ paths = inspector.paths(paths)
36
+ return true if paths.empty?
37
+ Compat::UI.info("Running: #{paths.join(' ')}", reset: true)
38
+ _run(paths, options) do |all_green|
39
+ next false unless all_green
40
+ next true unless options[:all_after_pass]
41
+ run_all
122
42
  end
123
- port = ENV["RSPEC_DRB"] || 8989 unless port && port > 0
124
- ret = drb_service(port.to_i).run(argv, $stderr, $stdout)
125
-
126
- [0, true].include?(ret)
127
- rescue DRb::DRbConnError
128
- # Fall back to the shell runner; we don't want to mangle the environment!
129
- run_via_shell(paths, options)
130
43
  end
131
44
 
132
- def drb_used?
133
- @drb_used ||= @options[:cli] && @options[:cli].include?('--drb')
45
+ def reload
46
+ inspector.reload
134
47
  end
135
48
 
136
- # W we can avoid loading a large chunk of rspec
137
- # just to let DRb know what to do.
138
- #
139
- # For reference:
140
- #
141
- # * RSpec: https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/drb_command_line.rb
142
- def drb_service(port)
143
- require "drb/drb"
144
-
145
- # Make sure we have a listener running
146
- unless @drb_listener_running
147
- begin
148
- DRb.start_service("druby://localhost:0")
149
- rescue SocketError, Errno::EADDRNOTAVAIL
150
- DRb.start_service("druby://:0")
151
- end
152
-
153
- @drb_listener_running = true
154
- end
49
+ private
155
50
 
156
- @drb_services ||= {}
157
- @drb_services[port.to_i] ||= DRbObject.new_with_uri("druby://127.0.0.1:#{port}")
51
+ def _run(paths, options, &block)
52
+ raise NoCmdOptionError unless options[:cmd]
53
+ command = Command.new(paths, options)
54
+ _really_run(command, options, &block)
55
+ rescue RSpecProcess::Failure, NoCmdOptionError => ex
56
+ Compat::UI.error(ex.to_s)
57
+ notifier.notify_failure
58
+ false
158
59
  end
159
60
 
160
- def bundler_allowed?
161
- @bundler_allowed ||= File.exist?("#{Dir.pwd}/Gemfile")
162
- end
61
+ def _really_run(cmd, options)
62
+ # TODO: add option to specify the file
63
+ file = _results_file(options[:results_file], options[:chdir])
163
64
 
164
- def bundler?
165
- @bundler ||= bundler_allowed? && @options[:bundler]
166
- end
65
+ process = RSpecProcess.new(cmd, file, options)
66
+ results = process.results
167
67
 
168
- def binstubs?
169
- @binstubs ||= !!@options[:binstubs]
170
- end
68
+ inspector.failed(results.failed_paths)
69
+ notifier.notify(results.summary)
70
+ _open_launchy
171
71
 
172
- def zeus?
173
- @options[:zeus] || false
72
+ all_green = process.all_green?
73
+ return yield all_green if block_given?
74
+ all_green
174
75
  end
175
76
 
176
- def binstubs
177
- @options[:binstubs] == true ? "bin" : @options[:binstubs]
77
+ def _open_launchy
78
+ return unless options[:launchy]
79
+ require "launchy"
80
+ pn = Pathname.new(options[:launchy])
81
+ ::Launchy.open(options[:launchy]) if pn.exist?
178
82
  end
179
83
 
180
- def bundle_exec?
181
- bundler? && !binstubs?
182
- end
84
+ def _results_file(results_file, chdir)
85
+ results_file ||= File.expand_path(RSpecDefaults::TEMPORARY_FILE_PATH)
86
+ return results_file unless Pathname(results_file).relative?
87
+ results_file = File.join(chdir, results_file) if chdir
88
+ return results_file unless Pathname(results_file).relative?
183
89
 
184
- def deprecations_warnings
185
- [:color, :drb, [:fail_fast, "fail-fast"], [:formatter, "format"]].each do |option|
186
- key, value = option.is_a?(Array) ? option : [option, option.to_s]
187
- if @options.key?(key)
188
- @options.delete(key)
189
- UI.info %{DEPRECATION WARNING: The :#{key} option is deprecated. Pass standard command line argument "--#{value}" to RSpec with the :cli option.}
190
- end
90
+ unless Pathname(results_file).absolute?
91
+ msg = "Guard::RSpec: The results file %s is not an absolute path."\
92
+ " Please provide an absolute path to avoid issues."
93
+ Compat::UI.warning(format(msg, results_file.inspect))
191
94
  end
192
- if @options.key?(:version)
193
- @options.delete(:version)
194
- UI.info %{DEPRECATION WARNING: The :version option is deprecated. Only RSpec 2 is now supported.}
195
- end
196
- end
197
95
 
198
- def formatter_regex
199
- @formatter_regex ||= /(?:^|\s)(?:-f\s*|--format(?:=|\s+))([\w:]+)/
96
+ File.expand_path(results_file)
200
97
  end
201
98
  end
202
99
  end
@@ -1,21 +1,53 @@
1
- guard 'rspec' do
2
- watch(%r{^spec/.+_spec\.rb$})
3
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
- watch('spec/spec_helper.rb') { "spec" }
5
-
6
- # Rails example
7
- watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
8
- watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
9
- watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
10
- watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
11
- watch('config/routes.rb') { "spec/routing" }
12
- watch('app/controllers/application_controller.rb') { "spec/controllers" }
13
-
14
- # Capybara request specs
15
- watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
1
+ # Note: The cmd option is now required due to the increasing number of ways
2
+ # rspec may be run, below are examples of the most common uses.
3
+ # * bundler: 'bundle exec rspec'
4
+ # * bundler binstubs: 'bin/rspec'
5
+ # * spring: 'bin/rspec' (This will use spring if running and you have
6
+ # installed the spring binstubs per the docs)
7
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
8
+ # * 'just' rspec: 'rspec'
9
+
10
+ guard :rspec, cmd: "bundle exec rspec" do
11
+ require "guard/rspec/dsl"
12
+ dsl = Guard::RSpec::Dsl.new(self)
13
+
14
+ # Feel free to open issues for suggestions and improvements
15
+
16
+ # RSpec files
17
+ rspec = dsl.rspec
18
+ watch(rspec.spec_helper) { rspec.spec_dir }
19
+ watch(rspec.spec_support) { rspec.spec_dir }
20
+ watch(rspec.spec_files)
21
+
22
+ # Ruby files
23
+ ruby = dsl.ruby
24
+ dsl.watch_spec_files_for(ruby.lib_files)
25
+
26
+ # Rails files
27
+ rails = dsl.rails(view_extensions: %w(erb haml slim))
28
+ dsl.watch_spec_files_for(rails.app_files)
29
+ dsl.watch_spec_files_for(rails.views)
30
+
31
+ watch(rails.controllers) do |m|
32
+ [
33
+ rspec.spec.call("routing/#{m[1]}_routing"),
34
+ rspec.spec.call("controllers/#{m[1]}_controller"),
35
+ rspec.spec.call("acceptance/#{m[1]}")
36
+ ]
37
+ end
38
+
39
+ # Rails config changes
40
+ watch(rails.spec_helper) { rspec.spec_dir }
41
+ watch(rails.routes) { "#{rspec.spec_dir}/routing" }
42
+ watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
43
+
44
+ # Capybara features specs
45
+ watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
46
+ watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
16
47
 
17
48
  # Turnip features and steps
18
49
  watch(%r{^spec/acceptance/(.+)\.feature$})
19
- watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
50
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
51
+ Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
52
+ end
20
53
  end
21
-
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module RSpecVersion
3
- VERSION = "2.1.0"
3
+ VERSION = "4.7.3".freeze
4
4
  end
5
5
  end
data/lib/guard/rspec.rb CHANGED
@@ -1,80 +1,51 @@
1
- require 'guard'
2
- require 'guard/guard'
1
+ require "guard/compat/plugin"
2
+
3
+ require "guard/rspec/options"
4
+ require "guard/rspec/deprecator"
5
+ require "guard/rspec/runner"
6
+
7
+ # NOTE: To avoid 'superclass mismatch for class RSpec' errors,
8
+ # every file has to have
9
+ #
10
+ # class RSpec < Plugin
11
+ #
12
+ # and not just
13
+ #
14
+ # class RSpec
3
15
 
4
16
  module Guard
5
- class RSpec < Guard
6
- autoload :Runner, 'guard/rspec/runner'
7
- autoload :Inspector, 'guard/rspec/inspector'
17
+ class RSpec < Plugin
18
+ attr_accessor :options, :runner
8
19
 
9
- def initialize(watchers = [], options = {})
20
+ def initialize(options = {})
10
21
  super
11
- @options = {
12
- :all_after_pass => true,
13
- :all_on_start => true,
14
- :keep_failed => true,
15
- :spec_paths => ["spec"],
16
- :run_all => {}
17
- }.merge(options)
18
- @last_failed = false
19
- @failed_paths = []
20
-
21
- @inspector = Inspector.new(@options)
22
- @runner = Runner.new(@options)
22
+ @options = Options.with_defaults(options)
23
+ Deprecator.warns_about_deprecated_options(@options)
24
+ @runner = Runner.new(@options)
23
25
  end
24
26
 
25
- # Call once when guard starts
26
27
  def start
27
- UI.info "Guard::RSpec is running"
28
- run_all if @options[:all_on_start]
28
+ Compat::UI.info "Guard::RSpec is running"
29
+ run_all if options[:all_on_start]
29
30
  end
30
31
 
31
32
  def run_all
32
- passed = @runner.run(@inspector.spec_paths, @options[:run_all].merge(:message => 'Running all specs'))
33
-
34
- unless @last_failed = !passed
35
- @failed_paths = []
36
- else
37
- throw :task_has_failed
38
- end
33
+ _throw_if_failed { runner.run_all }
39
34
  end
40
35
 
41
36
  def reload
42
- @failed_paths = []
37
+ runner.reload
43
38
  end
44
39
 
45
- def run_on_changes(paths)
46
- paths += @failed_paths if @options[:keep_failed]
47
- paths = @inspector.clean(paths)
48
-
49
- if passed = @runner.run(paths)
50
- remove_failed(paths)
51
-
52
- # run all the specs if the run before this one failed
53
- if @last_failed && @options[:all_after_pass]
54
- @last_failed = false
55
- run_all
56
- end
57
- else
58
- @last_failed = true
59
- add_failed(paths)
60
-
61
- throw :task_has_failed
62
- end
40
+ def run_on_modifications(paths)
41
+ return false if paths.empty?
42
+ _throw_if_failed { runner.run(paths) }
63
43
  end
64
44
 
65
- private
45
+ private
66
46
 
67
- def run(paths)
47
+ def _throw_if_failed
48
+ throw :task_has_failed unless yield
68
49
  end
69
-
70
- def remove_failed(paths)
71
- @failed_paths -= paths if @options[:keep_failed]
72
- end
73
-
74
- def add_failed(paths)
75
- @failed_paths += paths if @options[:keep_failed]
76
- end
77
-
78
50
  end
79
51
  end
80
-
@@ -0,0 +1,5 @@
1
+ module Guard
2
+ class RSpecDefaults
3
+ TEMPORARY_FILE_PATH = "tmp/rspec_guard_result".freeze
4
+ end
5
+ end
@@ -0,0 +1,147 @@
1
+ # NOTE: This class only exists for RSpec and should not be used by
2
+ # other classes in this project!
3
+
4
+ require "pathname"
5
+ require "fileutils"
6
+
7
+ require "rspec"
8
+ require "rspec/core/formatters/base_formatter"
9
+
10
+ require_relative "rspec_formatter_results_path"
11
+
12
+ module Guard
13
+ class RSpecFormatter < ::RSpec::Core::Formatters::BaseFormatter
14
+ UNSUPPORTED_PATTERN =
15
+ "Your RSpec.configuration.pattern uses characters "\
16
+ "unsupported by your Ruby version (File::FNM_EXTGLOB is undefined)".freeze
17
+
18
+ class Error < RuntimeError
19
+ class UnsupportedPattern < Error
20
+ def initialize(msg = UNSUPPORTED_PATTERN)
21
+ super
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.rspec_3?
27
+ ::RSpec::Core::Version::STRING.split(".").first == "3"
28
+ end
29
+
30
+ if rspec_3?
31
+ ::RSpec::Core::Formatters.register self, :dump_summary, :example_failed
32
+
33
+ def example_failed(failure)
34
+ examples.push failure.example
35
+ end
36
+
37
+ def examples
38
+ @examples ||= []
39
+ end
40
+ end
41
+
42
+ class << self
43
+ # rspec issue https://github.com/rspec/rspec-core/issues/793
44
+ def extract_spec_location(metadata)
45
+ root_metadata = metadata
46
+ location = metadata[:location]
47
+
48
+ until spec_path?(location)
49
+ unless (metadata = _extract_group(metadata))
50
+ STDERR.puts "no spec file location in #{root_metadata.inspect}"
51
+ return root_metadata[:location]
52
+ end
53
+
54
+ # rspec issue https://github.com/rspec/rspec-core/issues/1243
55
+ location = first_colon_separated_entry(metadata[:location])
56
+ end
57
+
58
+ location
59
+ end
60
+
61
+ def spec_path?(path)
62
+ pattern = ::RSpec.configuration.pattern
63
+
64
+ flags = supported_fnmatch_flags(pattern)
65
+ path ||= ""
66
+ path = path.sub(/:\d+\z/, "")
67
+ path = Pathname.new(path).cleanpath.to_s
68
+ stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}"
69
+ File.fnmatch(stripped, path, flags)
70
+ end
71
+
72
+ private
73
+
74
+ def first_colon_separated_entry(entries)
75
+ (entries || "").split(":").first
76
+ end
77
+
78
+ def supported_fnmatch_flags(pattern)
79
+ flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
80
+
81
+ # ruby >= 2
82
+ return flags |= File::FNM_EXTGLOB if File.const_defined?(:FNM_EXTGLOB)
83
+
84
+ raise Error::UnsupportedPattern if pattern =~ /[{}]/
85
+
86
+ flags
87
+ end
88
+
89
+ def _extract_group(metadata)
90
+ metadata[:parent_example_group] || metadata[:example_group]
91
+ end
92
+ end
93
+
94
+ def dump_summary(*args)
95
+ return write_summary(*args) unless self.class.rspec_3?
96
+
97
+ notification = args[0]
98
+ write_summary(
99
+ notification.duration,
100
+ notification.example_count,
101
+ notification.failure_count,
102
+ notification.pending_count
103
+ )
104
+ end
105
+
106
+ private
107
+
108
+ # Write summary to temporary file for runner
109
+ def write_summary(duration, total, failures, pending)
110
+ _write do |f|
111
+ f.puts _message(total, failures, pending, duration)
112
+ f.puts _failed_paths.join("\n") if failures > 0
113
+ end
114
+ end
115
+
116
+ def _write(&block)
117
+ file = RSpecFormatterResultsPath.new.path
118
+ if ENV['GUARD_RSPEC_DEBUGGING'] == '1'
119
+ msg = "Guard::RSpec: using results file: #{file.inspect}"
120
+ STDERR.puts format(msg, file)
121
+ end
122
+ FileUtils.mkdir_p(File.dirname(file))
123
+ File.open(file, "w", &block)
124
+ end
125
+
126
+ def _failed_paths
127
+ klass = self.class
128
+ failed = examples.select { |example| _status_failed?(example) }
129
+ failed.map { |e| klass.extract_spec_location(e.metadata) }.sort.uniq
130
+ end
131
+
132
+ def _message(example_count, failure_count, pending_count, duration)
133
+ message = "#{example_count} examples, #{failure_count} failures"
134
+ message << " (#{pending_count} pending)" if pending_count > 0
135
+ message << " in #{duration.round(4)} seconds"
136
+ message
137
+ end
138
+
139
+ def _status_failed?(example)
140
+ if self.class.rspec_3?
141
+ example.execution_result.status.to_s == "failed"
142
+ else
143
+ example.execution_result[:status].to_s == "failed"
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "rspec_defaults"
2
+
3
+ module Guard
4
+ # Just a wrapper class for the results file filename
5
+ class RSpecFormatterResultsPath
6
+ WIKI_ENV_WARN_URL =
7
+ "https://github.com/guard/guard-rspec/wiki/Warning:-no-environment".
8
+ freeze
9
+
10
+ NO_ENV_WARNING_MSG =
11
+ "no environment passed - see #{WIKI_ENV_WARN_URL}".freeze
12
+
13
+ NO_RESULTS_VALUE_MSG =
14
+ ":results_file value unknown (using defaults)".freeze
15
+
16
+ attr_reader :path
17
+
18
+ def initialize
19
+ path = ENV["GUARD_RSPEC_RESULTS_FILE"]
20
+ if path.nil?
21
+ STDERR.puts("Guard::RSpec: Warning: #{NO_ENV_WARNING_MSG}\n" \
22
+ "Guard::RSpec: Warning: #{NO_RESULTS_VALUE_MSG}")
23
+ path = RSpecDefaults::TEMPORARY_FILE_PATH
24
+ end
25
+
26
+ @path = File.expand_path(path)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ RSpec.describe "succeeding spec" do
2
+ it "works" do
3
+ end
4
+ end