turbo_tests 1.2.2 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92f70e0d1c7d01b5f9d17960ddcf2418c8c4fa620e8c46d0bb81df21b9193452
4
- data.tar.gz: 0a21dd64949d53a8045e167754f19b698356f2a5c088f6ad56b83297d42932a6
3
+ metadata.gz: ed1e26234763e4fd425447465a01b091f3b895c5c315beaf06e03b96225a2b28
4
+ data.tar.gz: c37798c25bab9b136a0768795d3f2c45d65de8d6b2a2c48a9e1fd7ed30a6a5eb
5
5
  SHA512:
6
- metadata.gz: 0d0e28e448d9b3f6e8dc5f47ef6b6308d3a8a08ffdcdcacdddc20f979af2a1f6d7e0827afbe5cc1fd09293efa3abed703cf7c0082a5d5cc400dd32fb9c9672a0
7
- data.tar.gz: 6efbeb21524f9973d759914766cf0966f4b3da0d0c68dab75c4efe0c412808df6a979324793ce3de21ff77745ca7f5bffda605f3a6e73635839e14e84b1d4658
6
+ metadata.gz: 3381d49ecc0e38cdfa0a71cce9961d29bfb41b0ccb4d1b9abae13ac4202c90ea59f8b3951292e8a1f89d298d8ffbd426bead4a8135c51b8b675793018eadb239
7
+ data.tar.gz: '085230889cbe5749e66d140ecd32fa3ba0bfc4ab7bbf117e1b400cf48e65e1590de959ffaac6cfb7c95faf4b5db09147282eb26deb4c021f749a9fb9c269fc70'
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
+ --tty
3
4
  --require spec_helper
data/Gemfile CHANGED
@@ -3,4 +3,4 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in turbo_tests.gemspec
4
4
  gemspec
5
5
 
6
- gem "rake", "~> 12.0"
6
+ gem "rake", "~> 13.0"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- turbo_tests (1.2.2)
4
+ turbo_tests (1.2.3)
5
5
  bundler
6
6
  parallel_tests (~> 3.3)
7
7
  rspec (~> 3.10.0)
@@ -13,12 +13,12 @@ GEM
13
13
  diff-lcs (1.4.4)
14
14
  method_source (1.0.0)
15
15
  parallel (1.20.1)
16
- parallel_tests (3.5.0)
16
+ parallel_tests (3.5.2)
17
17
  parallel
18
18
  pry (0.14.0)
19
19
  coderay (~> 1.1)
20
20
  method_source (~> 1.0)
21
- rake (12.3.3)
21
+ rake (13.0.3)
22
22
  rspec (3.10.0)
23
23
  rspec-core (~> 3.10.0)
24
24
  rspec-expectations (~> 3.10.0)
@@ -37,8 +37,8 @@ PLATFORMS
37
37
  ruby
38
38
 
39
39
  DEPENDENCIES
40
- pry (~> 0.13)
41
- rake (~> 12.0)
40
+ pry (~> 0.14)
41
+ rake (~> 13.0)
42
42
  turbo_tests!
43
43
 
44
44
  BUNDLED WITH
@@ -0,0 +1,5 @@
1
+ RSpec.describe "Fixture of spec file with errors outside of examples" do
2
+ it("passes") { expect(2 * 2).to eql(4) }
3
+
4
+ 1 / 0
5
+ end
data/lib/turbo_tests.rb CHANGED
@@ -23,14 +23,14 @@ module TurboTests
23
23
  klass =
24
24
  Class.new(FakeException) {
25
25
  define_singleton_method(:name) do
26
- obj["class_name"]
26
+ obj[:class_name]
27
27
  end
28
28
  }
29
29
 
30
30
  klass.new(
31
- obj["backtrace"],
32
- obj["message"],
33
- FakeException.from_obj(obj["cause"])
31
+ obj[:backtrace],
32
+ obj[:message],
33
+ FakeException.from_obj(obj[:cause])
34
34
  )
35
35
  end
36
36
  end
@@ -40,11 +40,11 @@ module TurboTests
40
40
  class FakeExecutionResult
41
41
  def self.from_obj(obj)
42
42
  new(
43
- obj["example_skipped?"],
44
- obj["pending_message"],
45
- obj["status"].to_sym,
46
- obj["pending_fixed?"],
47
- FakeException.from_obj(obj["exception"])
43
+ obj[:example_skipped?],
44
+ obj[:pending_message],
45
+ obj[:status].to_sym,
46
+ obj[:pending_fixed?],
47
+ FakeException.from_obj(obj[:exception])
48
48
  )
49
49
  end
50
50
  end
@@ -52,24 +52,24 @@ module TurboTests
52
52
  FakeExample = Struct.new(:execution_result, :location, :description, :full_description, :metadata, :location_rerun_argument)
53
53
  class FakeExample
54
54
  def self.from_obj(obj)
55
- metadata = obj["metadata"]
55
+ metadata = obj[:metadata]
56
56
 
57
- metadata["shared_group_inclusion_backtrace"].map! do |frame|
57
+ metadata[:shared_group_inclusion_backtrace].map! do |frame|
58
58
  RSpec::Core::SharedExampleGroupInclusionStackFrame.new(
59
- frame["shared_group_name"],
60
- frame["inclusion_location"]
59
+ frame[:shared_group_name],
60
+ frame[:inclusion_location]
61
61
  )
62
62
  end
63
63
 
64
- metadata[:shared_group_inclusion_backtrace] = metadata.delete("shared_group_inclusion_backtrace")
64
+ metadata[:shared_group_inclusion_backtrace] = metadata.delete(:shared_group_inclusion_backtrace)
65
65
 
66
66
  new(
67
- FakeExecutionResult.from_obj(obj["execution_result"]),
68
- obj["location"],
69
- obj["description"],
70
- obj["full_description"],
67
+ FakeExecutionResult.from_obj(obj[:execution_result]),
68
+ obj[:location],
69
+ obj[:description],
70
+ obj[:full_description],
71
71
  metadata,
72
- obj["location_rerun_argument"]
72
+ obj[:location_rerun_argument]
73
73
  )
74
74
  end
75
75
 
@@ -30,6 +30,7 @@ module TurboTests
30
30
  :example_group_started,
31
31
  :example_group_finished,
32
32
  :example_pending,
33
+ :message,
33
34
  :seed
34
35
  )
35
36
 
@@ -39,59 +40,65 @@ module TurboTests
39
40
  @output = output
40
41
  end
41
42
 
42
-
43
43
  def start(notification)
44
44
  output_row(
45
- "type" => :load_summary,
46
- "summary" => load_summary_to_json(notification)
45
+ type: :load_summary,
46
+ summary: load_summary_to_json(notification)
47
47
  )
48
48
  end
49
49
 
50
50
  def example_group_started(notification)
51
51
  output_row(
52
- "type" => :group_started,
53
- "group" => group_to_json(notification)
52
+ type: :group_started,
53
+ group: group_to_json(notification)
54
54
  )
55
55
  end
56
56
 
57
57
  def example_group_finished(notification)
58
58
  output_row(
59
- "type" => :group_finished,
60
- "group" => group_to_json(notification)
59
+ type: :group_finished,
60
+ group: group_to_json(notification)
61
61
  )
62
62
  end
63
63
 
64
64
  def example_passed(notification)
65
65
  output_row(
66
- "type" => :example_passed,
67
- "example" => example_to_json(notification.example)
66
+ type: :example_passed,
67
+ example: example_to_json(notification.example)
68
68
  )
69
69
  end
70
70
 
71
71
  def example_pending(notification)
72
72
  output_row(
73
- "type" => :example_pending,
74
- "example" => example_to_json(notification.example)
73
+ type: :example_pending,
74
+ example: example_to_json(notification.example)
75
75
  )
76
76
  end
77
77
 
78
78
  def example_failed(notification)
79
79
  output_row(
80
- "type" => :example_failed,
81
- "example" => example_to_json(notification.example)
80
+ type: :example_failed,
81
+ example: example_to_json(notification.example)
82
82
  )
83
83
  end
84
84
 
85
85
  def seed(notification)
86
86
  output_row(
87
- "type" => :seed,
88
- "seed" => notification.seed
87
+ type: :seed,
88
+ seed: notification.seed
89
89
  )
90
90
  end
91
91
 
92
92
  def close(notification)
93
93
  output_row(
94
- "type" => :close
94
+ type: :close
95
+ )
96
+ end
97
+
98
+ def message(notification)
99
+ output_row(
100
+ type: :message,
101
+ message: notification.message
95
102
  )
96
103
  end
97
104
 
@@ -100,62 +107,65 @@ module TurboTests
100
107
  def exception_to_json(exception)
101
108
  if exception
102
109
  {
103
- "class_name" => exception.class.name.to_s,
104
- "backtrace" => exception.backtrace,
105
- "message" => exception.message,
106
- "cause" => exception_to_json(exception.cause)
110
+ class_name: exception.class.name.to_s,
111
+ backtrace: exception.backtrace,
112
+ message: exception.message,
113
+ cause: exception_to_json(exception.cause)
107
114
  }
108
115
  end
109
116
  end
110
117
 
111
118
  def execution_result_to_json(result)
112
119
  {
113
- "example_skipped?" => result.example_skipped?,
114
- "pending_message" => result.pending_message,
115
- "status" => result.status,
116
- "pending_fixed?" => result.pending_fixed?,
117
- "exception" => exception_to_json(result.exception)
120
+ example_skipped?: result.example_skipped?,
121
+ pending_message: result.pending_message,
122
+ status: result.status,
123
+ pending_fixed?: result.pending_fixed?,
124
+ exception: exception_to_json(result.exception)
118
125
  }
119
126
  end
120
127
 
121
128
  def stack_frame_to_json(frame)
122
129
  {
123
- "shared_group_name" => frame.shared_group_name,
124
- "inclusion_location" => frame.inclusion_location
130
+ shared_group_name: frame.shared_group_name,
131
+ inclusion_location: frame.inclusion_location
125
132
  }
126
133
  end
127
134
 
128
135
  def example_to_json(example)
129
136
  {
130
- "execution_result" => execution_result_to_json(example.execution_result),
131
- "location" => example.location,
132
- "description" => example.description,
133
- "full_description" => example.full_description,
134
- "metadata" => {
135
- "shared_group_inclusion_backtrace" =>
136
- example.metadata[:shared_group_inclusion_backtrace].map { |frame| stack_frame_to_json(frame) }
137
+ execution_result: execution_result_to_json(example.execution_result),
138
+ location: example.location,
139
+ description: example.description,
140
+ full_description: example.full_description,
141
+ metadata: {
142
+ shared_group_inclusion_backtrace:
143
+ example
144
+ .metadata[:shared_group_inclusion_backtrace]
145
+ .map { |frame| stack_frame_to_json(frame) }
137
146
  },
138
- "location_rerun_argument" => example.location_rerun_argument
147
+ location_rerun_argument: example.location_rerun_argument
139
148
  }
140
149
  end
141
150
 
142
151
  def load_summary_to_json(notification)
143
152
  {
144
153
  count: notification.count,
145
- load_time: notification.load_time
154
+ load_time: notification.load_time,
146
155
  }
147
156
  end
148
157
 
149
158
  def group_to_json(notification)
150
159
  {
151
- "group": {
152
- "description": notification.group.description
160
+ group: {
161
+ description: notification.group.description
153
162
  }
154
163
  }
155
164
  end
156
165
 
157
166
  def output_row(obj)
158
- output.puts ENV["RSPEC_FORMATTER_OUTPUT_ID"] + obj.to_json
167
+ output.puts(obj.to_json)
168
+ output.flush
159
169
  end
160
170
  end
161
171
  end
@@ -28,8 +28,10 @@ module TurboTests
28
28
  @pending_examples = []
29
29
  @failed_examples = []
30
30
  @all_examples = []
31
+ @messages = []
31
32
  @start_time = start_time
32
33
  @load_time = 0
34
+ @errors_outside_of_examples_count = 0
33
35
  end
34
36
 
35
37
  def add(name, outputs)
@@ -76,6 +78,15 @@ module TurboTests
76
78
  @failed_examples << example
77
79
  end
78
80
 
81
+ def message(message)
82
+ delegate_to_formatters(:message, RSpec::Core::Notifications::MessageNotification.new(message))
83
+ @messages << message
84
+ end
85
+
86
+ def error_outside_of_examples
87
+ @errors_outside_of_examples_count += 12
88
+ end
89
+
79
90
  def finish
80
91
  # SEE: https://bit.ly/2NP87Cz
81
92
  end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -97,7 +108,7 @@ module TurboTests
97
108
  @failed_examples,
98
109
  @pending_examples,
99
110
  @load_time,
100
- 0
111
+ @errors_outside_of_examples_count
101
112
  ))
102
113
  delegate_to_formatters(:close,
103
114
  RSpec::Core::Notifications::NullNotification)
@@ -20,6 +20,10 @@ module TurboTests
20
20
  fail_fast = opts.fetch(:fail_fast, nil)
21
21
  count = opts.fetch(:count, nil)
22
22
 
23
+ if verbose
24
+ STDERR.puts "VERBOSE"
25
+ end
26
+
23
27
  reporter = Reporter.from_config(formatters, start_time)
24
28
 
25
29
  new(
@@ -41,12 +45,11 @@ module TurboTests
41
45
  @count = opts[:count]
42
46
  @load_time = 0
43
47
  @load_count = 0
44
-
45
48
  @failure_count = 0
46
- @runtime_log = "tmp/parallel_runtime_rspec.log"
47
49
 
48
50
  @messages = Queue.new
49
51
  @threads = []
52
+ @error = false
50
53
  end
51
54
 
52
55
  def run
@@ -55,17 +58,33 @@ module TurboTests
55
58
  ParallelTests::RSpec::Runner.tests_with_size(@files, {}).size
56
59
  ].min
57
60
 
61
+ use_runtime_info = @files == ["spec"]
62
+
63
+ group_opts = {}
64
+
65
+ if use_runtime_info
66
+ group_opts[:runtime_log] = "tmp/turbo_rspec_runtime.log"
67
+ else
68
+ group_opts[:group_by] = :filesize
69
+ end
70
+
58
71
  tests_in_groups =
59
72
  ParallelTests::RSpec::Runner.tests_in_groups(
60
73
  @files,
61
74
  @num_processes,
62
- runtime_log: @runtime_log
75
+ **group_opts
63
76
  )
64
77
 
78
+ setup_tmp_dir
79
+
80
+ subprocess_opts = {
81
+ record_runtime: use_runtime_info
82
+ }
83
+
65
84
  report_number_of_tests(tests_in_groups)
66
85
 
67
- tests_in_groups.each_with_index do |tests, process_id|
68
- start_regular_subprocess(tests, process_id + 1)
86
+ wait_threads = tests_in_groups.map.with_index do |tests, process_id|
87
+ start_regular_subprocess(tests, process_id + 1, **subprocess_opts)
69
88
  end
70
89
 
71
90
  handle_messages
@@ -74,38 +93,64 @@ module TurboTests
74
93
 
75
94
  @threads.each(&:join)
76
95
 
77
- @reporter.failed_examples.empty?
96
+ @reporter.failed_examples.empty? && wait_threads.map(&:value).all?(&:success?)
78
97
  end
79
98
 
80
- protected
99
+ private
100
+
101
+ def setup_tmp_dir
102
+ begin
103
+ FileUtils.rm_r("tmp/test-pipes")
104
+ rescue Errno::ENOENT
105
+ end
106
+
107
+ FileUtils.mkdir_p("tmp/test-pipes/")
108
+ end
81
109
 
82
- def start_regular_subprocess(tests, process_id)
110
+ def start_regular_subprocess(tests, process_id, **opts)
83
111
  start_subprocess(
84
112
  {"TEST_ENV_NUMBER" => process_id.to_s},
85
113
  @tags.map { |tag| "--tag=#{tag}" },
86
114
  tests,
87
- process_id
115
+ process_id,
116
+ **opts
88
117
  )
89
118
  end
90
119
 
91
- def start_subprocess(env, extra_args, tests, process_id)
120
+ def start_subprocess(env, extra_args, tests, process_id, record_runtime:)
92
121
  if tests.empty?
93
122
  @messages << {
94
- "type" => "exit",
95
- "process_id" => process_id
123
+ type: "exit",
124
+ process_id: process_id
96
125
  }
97
126
  else
98
- require "securerandom"
99
- env["RSPEC_FORMATTER_OUTPUT_ID"] = SecureRandom.uuid
100
- env["RUBYOPT"] = "-I#{File.expand_path("..", __dir__)}"
127
+ tmp_filename = "tmp/test-pipes/subprocess-#{process_id}"
128
+
129
+ begin
130
+ File.mkfifo(tmp_filename)
131
+ rescue Errno::EEXIST
132
+ end
133
+
134
+ env["RUBYOPT"] = ["-I#{File.expand_path("..", __dir__)}", ENV["RUBYOPT"]].compact.join(" ")
135
+ env["RSPEC_SILENCE_FILTER_ANNOUNCEMENTS"] = "1"
136
+
137
+ record_runtime_options =
138
+ if record_runtime
139
+ [
140
+ "--format", "ParallelTests::RSpec::RuntimeLogger",
141
+ "--out", "tmp/turbo_rspec_runtime.log",
142
+ ]
143
+ else
144
+ []
145
+ end
101
146
 
102
147
  command = [
103
148
  ENV["BUNDLE_BIN_PATH"], "exec", "rspec",
104
149
  *extra_args,
105
- "--seed", rand(2**16).to_s,
106
- "--format", "ParallelTests::RSpec::RuntimeLogger",
107
- "--out", @runtime_log,
150
+ "--seed", rand(0xFFFF).to_s,
108
151
  "--format", "TurboTests::JsonRowsFormatter",
152
+ "--out", tmp_filename,
153
+ *record_runtime_options,
109
154
  *tests
110
155
  ]
111
156
 
@@ -118,29 +163,33 @@ module TurboTests
118
163
  STDERR.puts "Process #{process_id}: #{command_str}"
119
164
  end
120
165
 
121
- _stdin, stdout, stderr, _wait_thr = Open3.popen3(env, *command)
166
+ stdin, stdout, stderr, wait_thr = Open3.popen3(env, *command)
167
+ stdin.close
122
168
 
123
169
  @threads <<
124
- Thread.new {
125
- require "json"
126
- stdout.each_line do |line|
127
- result = line.split(env["RSPEC_FORMATTER_OUTPUT_ID"])
128
-
129
- output = result.shift
130
- STDOUT.print(output) unless output.empty?
131
-
132
- message = result.shift
133
- next unless message
134
-
135
- message = JSON.parse(message)
136
- message["process_id"] = process_id
137
- @messages << message
170
+ Thread.new do
171
+ File.open(tmp_filename) do |fd|
172
+ fd.each_line do |line|
173
+ message = JSON.parse(line, symbolize_names: true)
174
+
175
+ message[:process_id] = process_id
176
+ @messages << message
177
+ end
138
178
  end
139
179
 
140
- @messages << {"type" => "exit", "process_id" => process_id}
141
- }
180
+ @messages << {type: "exit", process_id: process_id}
181
+ end
142
182
 
183
+ @threads << start_copy_thread(stdout, STDOUT)
143
184
  @threads << start_copy_thread(stderr, STDERR)
185
+
186
+ @threads << Thread.new {
187
+ unless wait_thr.value.success?
188
+ @messages << {type: "error"}
189
+ end
190
+ }
191
+
192
+ wait_thr
144
193
  end
145
194
  end
146
195
 
@@ -149,6 +198,7 @@ module TurboTests
149
198
  loop do
150
199
  msg = src.readpartial(4096)
151
200
  rescue EOFError
201
+ src.close
152
202
  break
153
203
  else
154
204
  dst.write(msg)
@@ -161,40 +211,47 @@ module TurboTests
161
211
 
162
212
  loop do
163
213
  message = @messages.pop
164
- case message["type"]
214
+ case message[:type]
165
215
  when "example_passed"
166
- example = FakeExample.from_obj(message["example"])
216
+ example = FakeExample.from_obj(message[:example])
167
217
  @reporter.example_passed(example)
168
218
  when "group_started"
169
- @reporter.group_started(message["group"].to_struct)
219
+ @reporter.group_started(message[:group].to_struct)
170
220
  when "group_finished"
171
221
  @reporter.group_finished
172
222
  when "example_pending"
173
- example = FakeExample.from_obj(message["example"])
223
+ example = FakeExample.from_obj(message[:example])
174
224
  @reporter.example_pending(example)
175
225
  when "load_summary"
176
- message = message["summary"]
226
+ message = message[:summary]
177
227
  # NOTE: notifications order and content is not guaranteed hence the fetch
178
228
  # and count increment tracking to get the latest accumulated load time
179
- @reporter.load_time = message["load_time"] if message.fetch("count", 0) > @load_count
229
+ @reporter.load_time = message[:load_time] if message.fetch(:count, 0) > @load_count
180
230
  when "example_failed"
181
- example = FakeExample.from_obj(message["example"])
231
+ example = FakeExample.from_obj(message[:example])
182
232
  @reporter.example_failed(example)
183
233
  @failure_count += 1
184
234
  if fail_fast_met
185
235
  @threads.each(&:kill)
186
236
  break
187
237
  end
238
+ when "message"
239
+ @reporter.message(message[:message])
188
240
  when "seed"
189
241
  when "close"
242
+ when "error"
243
+ @reporter.error_outside_of_examples
244
+ @error = true
190
245
  when "exit"
191
246
  exited += 1
192
247
  if exited == @num_processes
193
248
  break
194
249
  end
195
250
  else
196
- warn("Unhandled message in main process: #{message}")
251
+ STDERR.puts("Unhandled message in main process: #{message}")
197
252
  end
253
+
254
+ STDOUT.flush
198
255
  end
199
256
  rescue Interrupt
200
257
  end
@@ -203,8 +260,6 @@ module TurboTests
203
260
  !@fail_fast.nil? && @fail_fast >= @failure_count
204
261
  end
205
262
 
206
- private
207
-
208
263
  def report_number_of_tests(groups)
209
264
  name = ParallelTests::RSpec::Runner.test_file_name
210
265
 
@@ -1,3 +1,3 @@
1
1
  module TurboTests
2
- VERSION = "1.2.2"
2
+ VERSION = "1.2.3"
3
3
  end
data/turbo_tests.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.add_dependency "rspec", "~> 3.10.0"
20
20
  spec.add_dependency "parallel_tests", "~> 3.3"
21
21
 
22
- spec.add_development_dependency "pry", "~> 0.13"
22
+ spec.add_development_dependency "pry", "~> 0.14"
23
23
 
24
24
  spec.add_runtime_dependency "bundler"
25
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilya Zub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-02 00:00:00.000000000 Z
11
+ date: 2021-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.13'
47
+ version: '0.14'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.13'
54
+ version: '0.14'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +86,7 @@ files:
86
86
  - README.md
87
87
  - Rakefile
88
88
  - bin/turbo_tests
89
+ - fixtures/rspec/errors_outside_of_examples_spec.rb
89
90
  - lib/turbo_tests.rb
90
91
  - lib/turbo_tests/cli.rb
91
92
  - lib/turbo_tests/json_rows_formatter.rb