test_launcher 1.5.0 → 1.5.1
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 +4 -4
- data/README.md +18 -0
- data/bin/test_launcher +2 -5
- data/lib/test_launcher/cli/input_parser.rb +37 -12
- data/lib/test_launcher/cli/query.rb +30 -0
- data/lib/test_launcher/cli/request.rb +61 -0
- data/lib/test_launcher/cli.rb +44 -0
- data/lib/test_launcher/frameworks/base.rb +26 -7
- data/lib/test_launcher/frameworks/elixir.rb +84 -0
- data/lib/test_launcher/frameworks/implementation/test_case.rb +4 -7
- data/lib/test_launcher/frameworks/minitest.rb +80 -23
- data/lib/test_launcher/frameworks/rspec.rb +38 -13
- data/lib/test_launcher/queries.rb +346 -0
- data/lib/test_launcher/rubymine/launcher.rb +1 -12
- data/lib/test_launcher/rubymine/request.rb +15 -0
- data/lib/test_launcher/rubymine.rb +20 -13
- data/lib/test_launcher/search/ag.rb +96 -0
- data/lib/test_launcher/search/git.rb +6 -2
- data/lib/test_launcher/search.rb +18 -0
- data/lib/test_launcher/shell/runner.rb +2 -1
- data/lib/test_launcher/version.rb +1 -1
- data/lib/test_launcher.rb +0 -26
- data/test/test_helper.rb +2 -1
- data/test/test_helpers/integration_helper.rb +40 -0
- data/test/test_helpers/mock.rb +59 -0
- data/test/test_helpers/mock_searcher.rb +1 -0
- data/test/test_helpers/mocks/searcher_mock.rb +82 -0
- data/test/test_helpers/mocks.rb +76 -0
- data/test/test_launcher/cli/input_parser_test.rb +72 -0
- data/test/test_launcher/frameworks/minitest/runner_test.rb +72 -0
- data/test/test_launcher/frameworks/minitest/searcher_test.rb +109 -0
- data/test/test_launcher/frameworks/rspec/runner_test.rb +76 -0
- data/test/test_launcher/frameworks/rspec/searcher_test.rb +54 -0
- data/test/test_launcher/minitest_integration_test.rb +31 -40
- data/test/test_launcher/queries/example_name_query_test.rb +217 -0
- data/test/test_launcher/queries/full_regex_query_test.rb +153 -0
- data/test/test_launcher/queries/generic_query_test.rb +23 -0
- data/test/test_launcher/queries/line_number_query_test.rb +107 -0
- data/test/test_launcher/queries/multi_term_query_test.rb +138 -0
- data/test/test_launcher/queries/path_query_test.rb +192 -0
- data/test/test_launcher/queries/search_query_test.rb +54 -0
- data/test/test_launcher/queries/single_term_query_test.rb +36 -0
- data/test/test_launcher/queries/specified_name_query_test.rb +112 -0
- data/test/test_launcher/rspec_integration_test.rb +27 -41
- data/test/test_launcher/search/git_test.rb +2 -0
- metadata +49 -10
- data/lib/test_launcher/frameworks/implementation/collection.rb +0 -36
- data/lib/test_launcher/frameworks/implementation/consolidator.rb +0 -83
- data/lib/test_launcher/frameworks/implementation/locator.rb +0 -118
- data/lib/test_launcher/frameworks.rb +0 -20
- data/lib/test_launcher/request.rb +0 -36
- data/test/test_launcher/frameworks/implementation/locator_test.rb +0 -166
@@ -0,0 +1,346 @@
|
|
1
|
+
module TestLauncher
|
2
|
+
module Queries
|
3
|
+
class CommandFinder
|
4
|
+
def initialize(request)
|
5
|
+
@request = request
|
6
|
+
end
|
7
|
+
|
8
|
+
def specified_name
|
9
|
+
commandify(SpecifiedNameQuery)
|
10
|
+
end
|
11
|
+
|
12
|
+
def multi_search_term
|
13
|
+
commandify(MultiTermQuery)
|
14
|
+
end
|
15
|
+
|
16
|
+
def by_path
|
17
|
+
commandify(PathQuery)
|
18
|
+
end
|
19
|
+
|
20
|
+
def example_name
|
21
|
+
commandify(ExampleNameQuery)
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_full_regex
|
25
|
+
commandify(FullRegexQuery)
|
26
|
+
end
|
27
|
+
|
28
|
+
def single_search_term
|
29
|
+
commandify(SingleTermQuery)
|
30
|
+
end
|
31
|
+
|
32
|
+
def full_search
|
33
|
+
commandify(SearchQuery)
|
34
|
+
end
|
35
|
+
|
36
|
+
def generic_search
|
37
|
+
commandify(GenericQuery)
|
38
|
+
end
|
39
|
+
|
40
|
+
def line_number
|
41
|
+
commandify(LineNumberQuery)
|
42
|
+
end
|
43
|
+
|
44
|
+
def request
|
45
|
+
@request
|
46
|
+
end
|
47
|
+
|
48
|
+
def commandify(klass)
|
49
|
+
klass.new(
|
50
|
+
request,
|
51
|
+
self
|
52
|
+
).command
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class BaseQuery
|
57
|
+
attr_reader :shell, :searcher, :request
|
58
|
+
def initialize(request, command_finder)
|
59
|
+
@request = request
|
60
|
+
@command_finder = command_finder
|
61
|
+
end
|
62
|
+
|
63
|
+
def command
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def test_cases
|
70
|
+
raise NotImplementedError
|
71
|
+
end
|
72
|
+
|
73
|
+
def runner
|
74
|
+
request.runner
|
75
|
+
end
|
76
|
+
|
77
|
+
def shell
|
78
|
+
request.shell
|
79
|
+
end
|
80
|
+
|
81
|
+
def searcher
|
82
|
+
request.searcher
|
83
|
+
end
|
84
|
+
|
85
|
+
def one_file?
|
86
|
+
file_count == 1
|
87
|
+
end
|
88
|
+
|
89
|
+
def file_count
|
90
|
+
@file_count ||= test_cases.map {|tc| tc.file }.uniq.size
|
91
|
+
end
|
92
|
+
|
93
|
+
def most_recently_edited_test_case
|
94
|
+
@most_recently_edited_test_case ||= test_cases.sort_by(&:mtime).last
|
95
|
+
end
|
96
|
+
|
97
|
+
def pluralize(count, singular)
|
98
|
+
phrase = "#{count} #{singular}"
|
99
|
+
if count == 1
|
100
|
+
phrase
|
101
|
+
else
|
102
|
+
"#{phrase}s"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def command_finder
|
107
|
+
@command_finder
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class SpecifiedNameQuery < BaseQuery
|
112
|
+
def command
|
113
|
+
return unless file
|
114
|
+
|
115
|
+
shell.notify("Found matching test.")
|
116
|
+
runner.single_example(test_case)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_case
|
120
|
+
request.test_case(
|
121
|
+
file: file,
|
122
|
+
example: request.example_name,
|
123
|
+
request: request,
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def file
|
128
|
+
if potential_files.size == 0
|
129
|
+
shell.warn("Could not locate file: #{request.search_string}")
|
130
|
+
elsif potential_files.size > 1
|
131
|
+
shell.warn("Too many files matched: #{request.search_string}")
|
132
|
+
else
|
133
|
+
potential_files.first
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def potential_files
|
138
|
+
@potential_files ||= searcher.test_files(request.search_string)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class MultiTermQuery < BaseQuery
|
143
|
+
def command
|
144
|
+
return if test_cases.empty?
|
145
|
+
|
146
|
+
shell.notify("Found #{pluralize(file_count, "file")}.")
|
147
|
+
runner.multiple_files(test_cases)
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_cases
|
151
|
+
@test_cases ||= files.map { |file_path|
|
152
|
+
request.test_case(
|
153
|
+
file: file_path,
|
154
|
+
request: request,
|
155
|
+
)
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
def files
|
160
|
+
if found_files.any? {|files_array| files_array.empty? }
|
161
|
+
if !found_files.all? {|files_array| files_array.empty? }
|
162
|
+
shell.warn("It looks like you're searching for multiple files, but we couldn't identify them all.")
|
163
|
+
end
|
164
|
+
[]
|
165
|
+
else
|
166
|
+
found_files.flatten.uniq
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def found_files
|
171
|
+
@found_files ||= queries.map {|query|
|
172
|
+
searcher.test_files(query)
|
173
|
+
}
|
174
|
+
end
|
175
|
+
|
176
|
+
def queries
|
177
|
+
@queries ||= request.search_string.split(" ")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class PathQuery < BaseQuery
|
182
|
+
def command
|
183
|
+
return if test_cases.empty?
|
184
|
+
|
185
|
+
if one_file?
|
186
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
187
|
+
runner.single_file(test_cases.first)
|
188
|
+
elsif request.run_all?
|
189
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
190
|
+
runner.multiple_files(test_cases)
|
191
|
+
else
|
192
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
193
|
+
shell.notify "Running most recently edited. Run with '--all' to run all the tests."
|
194
|
+
runner.single_file(most_recently_edited_test_case)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_cases
|
199
|
+
@test_cases ||= files_found_by_path.map { |file_path|
|
200
|
+
request.test_case(file: file_path, request: request)
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
204
|
+
def files_found_by_path
|
205
|
+
@files_found_by_path ||= searcher.test_files(request.search_string)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class ExampleNameQuery < BaseQuery
|
210
|
+
def command
|
211
|
+
return if test_cases.empty?
|
212
|
+
|
213
|
+
if one_example?
|
214
|
+
shell.notify("Found 1 example in 1 file.")
|
215
|
+
runner.single_example(test_cases.first)
|
216
|
+
elsif one_file?
|
217
|
+
shell.notify("Found #{test_cases.size} examples in 1 file.")
|
218
|
+
runner.multiple_examples_same_file(test_cases) # it will regex with the query
|
219
|
+
elsif request.run_all?
|
220
|
+
shell.notify "Found #{pluralize(test_cases.size, "example")} in #{pluralize(file_count, "file")}."
|
221
|
+
runner.multiple_files(test_cases)
|
222
|
+
else
|
223
|
+
shell.notify "Found #{pluralize(test_cases.size, "example")} in #{pluralize(file_count, "file")}."
|
224
|
+
shell.notify "Running most recently edited. Run with '--all' to run all the tests."
|
225
|
+
runner.single_example(most_recently_edited_test_case) # let it regex the query
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def test_cases
|
230
|
+
@test_cases ||=
|
231
|
+
examples_found_by_name.map { |grep_result|
|
232
|
+
request.test_case(
|
233
|
+
file: grep_result[:file],
|
234
|
+
example: request.search_string,
|
235
|
+
line_number: grep_result[:line_number],
|
236
|
+
request: request
|
237
|
+
)
|
238
|
+
}
|
239
|
+
end
|
240
|
+
|
241
|
+
def examples_found_by_name
|
242
|
+
@examples_found_by_name ||= searcher.examples(request.search_string)
|
243
|
+
end
|
244
|
+
|
245
|
+
def one_example?
|
246
|
+
test_cases.size == 1
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class FullRegexQuery < BaseQuery
|
251
|
+
def command
|
252
|
+
return if test_cases.empty?
|
253
|
+
|
254
|
+
if one_file?
|
255
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
256
|
+
runner.single_file(test_cases.first)
|
257
|
+
elsif request.run_all?
|
258
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
259
|
+
runner.multiple_files(test_cases)
|
260
|
+
else
|
261
|
+
shell.notify "Found #{pluralize(file_count, "file")}."
|
262
|
+
shell.notify "Running most recently edited. Run with '--all' to run all the tests."
|
263
|
+
runner.single_file(most_recently_edited_test_case)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_cases
|
268
|
+
@test_cases ||=
|
269
|
+
files_found_by_full_regex
|
270
|
+
.uniq { |grep_result| grep_result[:file] }
|
271
|
+
.map { |grep_result|
|
272
|
+
request.test_case(
|
273
|
+
file: grep_result[:file],
|
274
|
+
request: request
|
275
|
+
)
|
276
|
+
}
|
277
|
+
end
|
278
|
+
|
279
|
+
def files_found_by_full_regex
|
280
|
+
@files_found_by_full_regex ||= searcher.grep(request.search_string)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class LineNumberQuery < BaseQuery
|
285
|
+
LINE_SPLIT_REGEX = /\A(?<file>.*):(?<line_number>\d+)\Z/
|
286
|
+
|
287
|
+
def command
|
288
|
+
match = request.search_string.match(LINE_SPLIT_REGEX)
|
289
|
+
return unless match
|
290
|
+
|
291
|
+
search_result = searcher.by_line(match[:file], match[:line_number].to_i)
|
292
|
+
return unless search_result
|
293
|
+
|
294
|
+
if search_result[:line_number]
|
295
|
+
shell.notify("Found 1 example on line #{search_result[:line_number]}.")
|
296
|
+
runner.single_example(request.test_case(file: search_result[:file], line_number: search_result[:line_number], example: search_result[:example_name], request: request))
|
297
|
+
else
|
298
|
+
shell.notify("Found file, but line is not inside an example.")
|
299
|
+
runner.single_file(request.test_case(file: search_result[:file], request: request))
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class SingleTermQuery < BaseQuery
|
306
|
+
def command
|
307
|
+
[
|
308
|
+
:by_path,
|
309
|
+
:example_name,
|
310
|
+
:from_full_regex,
|
311
|
+
]
|
312
|
+
.each { |command_type|
|
313
|
+
command = command_finder.public_send(command_type)
|
314
|
+
return command if command
|
315
|
+
}
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
class SearchQuery < BaseQuery
|
321
|
+
def command
|
322
|
+
{
|
323
|
+
multi_search_term: request.search_string.include?(" "),
|
324
|
+
line_number: request.search_string.include?(":"),
|
325
|
+
single_search_term: true
|
326
|
+
}.each {|command_type, valid|
|
327
|
+
next unless valid
|
328
|
+
|
329
|
+
command = command_finder.public_send(command_type)
|
330
|
+
return command if command
|
331
|
+
}
|
332
|
+
nil
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
class GenericQuery < BaseQuery
|
337
|
+
def command
|
338
|
+
if request.example_name
|
339
|
+
command_finder.specified_name
|
340
|
+
else
|
341
|
+
command_finder.full_search
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
@@ -1,19 +1,8 @@
|
|
1
|
-
require "test_launcher/request"
|
2
1
|
require "test_launcher/frameworks/minitest"
|
3
2
|
require "test_launcher/shell/runner"
|
4
3
|
|
5
4
|
module TestLauncher
|
6
5
|
module Rubymine
|
7
|
-
class MinimalRequest
|
8
|
-
def initialize(disable_spring:)
|
9
|
-
@disable_spring = disable_spring
|
10
|
-
end
|
11
|
-
|
12
|
-
def disable_spring?
|
13
|
-
@disable_spring
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
6
|
class Launcher
|
18
7
|
def initialize(args:, shell:, request:)
|
19
8
|
@args = args
|
@@ -40,7 +29,7 @@ module TestLauncher
|
|
40
29
|
|
41
30
|
def command
|
42
31
|
if test_case.is_example?
|
43
|
-
Frameworks::Minitest::Runner.new.single_example(test_case
|
32
|
+
Frameworks::Minitest::Runner.new.single_example(test_case)
|
44
33
|
else
|
45
34
|
Frameworks::Minitest::Runner.new.single_file(test_case)
|
46
35
|
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require "test_launcher/rubymine/launcher"
|
2
1
|
require "test_launcher/shell/runner"
|
3
|
-
require "test_launcher/
|
2
|
+
require "test_launcher/rubymine/launcher"
|
3
|
+
require "test_launcher/rubymine/request"
|
4
4
|
|
5
5
|
# To allow us to simply specify our run configuration as:
|
6
6
|
#
|
@@ -20,15 +20,22 @@ require "test_launcher/request"
|
|
20
20
|
# So we throw them in the same bucket and let the launcher figure it
|
21
21
|
# out. It doesn't matter since we will `exec` a new command anyway.
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
module TestLauncher
|
24
|
+
module Rubymine
|
25
|
+
def self.launch
|
26
|
+
shell = TestLauncher::Shell::Runner.new(log_path: "/dev/null")
|
27
|
+
|
28
|
+
request = Request.new(
|
29
|
+
disable_spring: ENV["DISABLE_SPRING"]
|
30
|
+
)
|
31
|
+
|
32
|
+
Launcher.new(
|
33
|
+
args: [$0].concat(ARGV),
|
34
|
+
shell: shell,
|
35
|
+
request: request
|
36
|
+
).launch
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
29
40
|
|
30
|
-
TestLauncher::Rubymine
|
31
|
-
args: [$0].concat(ARGV),
|
32
|
-
shell: TestLauncher::Shell::Runner.new(log_path: "/dev/null"),
|
33
|
-
request: dummy_request
|
34
|
-
).launch
|
41
|
+
TestLauncher::Rubymine.launch
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "test_launcher/base_error"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Search
|
5
|
+
class Ag
|
6
|
+
NotInRepoError = Class.new(BaseError)
|
7
|
+
class Interface
|
8
|
+
attr_reader :shell
|
9
|
+
|
10
|
+
def initialize(shell)
|
11
|
+
@shell = shell
|
12
|
+
end
|
13
|
+
|
14
|
+
def ls_files(pattern)
|
15
|
+
shell.run("ag -g '.*#{pattern_to_regex(pattern)}.*'")
|
16
|
+
end
|
17
|
+
|
18
|
+
def grep(regex, file_pattern)
|
19
|
+
shell.run("ag '#{regex}' --file-search-regex '#{pattern_to_regex(file_pattern)}'")
|
20
|
+
end
|
21
|
+
|
22
|
+
def root_path
|
23
|
+
shell.run("git rev-parse --show-toplevel").first.tap do
|
24
|
+
if $? != 0
|
25
|
+
raise NotInRepoError, "test_launcher must be used in a git repository"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def pattern_to_regex(pattern)
|
31
|
+
pattern.gsub("*", ".*")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :interface
|
36
|
+
|
37
|
+
def initialize(shell, interface=Interface.new(shell))
|
38
|
+
@interface = interface
|
39
|
+
Dir.chdir(root_path) # MOVE ME!
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_files(pattern)
|
43
|
+
relative_pattern = strip_system_path(pattern)
|
44
|
+
interface.ls_files(relative_pattern).map {|f| system_path(f)}
|
45
|
+
end
|
46
|
+
|
47
|
+
def grep(regex, file_pattern: '*')
|
48
|
+
results = interface.grep(regex, file_pattern)
|
49
|
+
results.map do |result|
|
50
|
+
interpret_grep_result(result)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def interpret_grep_result(grep_result)
|
58
|
+
splits = grep_result.split(/:/)
|
59
|
+
file = splits.shift.strip
|
60
|
+
line_number = splits.shift.strip.to_i
|
61
|
+
# we rejoin on ':' because our
|
62
|
+
# code may have colons inside of it.
|
63
|
+
#
|
64
|
+
# example:
|
65
|
+
# path/to/file:126: run_method(a: A, b: B)
|
66
|
+
#
|
67
|
+
# so shift the first one out, then
|
68
|
+
# rejoin the rest
|
69
|
+
line = splits.join(':').strip
|
70
|
+
|
71
|
+
# TODO: Oh goodness, why is this not a class
|
72
|
+
{
|
73
|
+
:file => system_path(file),
|
74
|
+
:line_number => line_number.to_i,
|
75
|
+
:line => line,
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def system_path(file)
|
80
|
+
File.join(root_path, file)
|
81
|
+
end
|
82
|
+
|
83
|
+
def strip_system_path(file)
|
84
|
+
file.sub(/^#{root_path}\//, '')
|
85
|
+
end
|
86
|
+
|
87
|
+
def root_path
|
88
|
+
@root_path ||= interface.root_path
|
89
|
+
end
|
90
|
+
|
91
|
+
def shell
|
92
|
+
@shell
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -17,7 +17,7 @@ module TestLauncher
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def grep(regex, file_pattern)
|
20
|
-
shell.run("git grep --untracked --extended-regexp '#{regex}' -- '#{file_pattern}'")
|
20
|
+
shell.run("git grep --line-number --untracked --extended-regexp '#{regex}' -- '#{file_pattern}'")
|
21
21
|
end
|
22
22
|
|
23
23
|
def root_path
|
@@ -48,23 +48,27 @@ module TestLauncher
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
|
51
52
|
private
|
52
53
|
|
53
54
|
def interpret_grep_result(grep_result)
|
54
55
|
splits = grep_result.split(/:/)
|
55
56
|
file = splits.shift.strip
|
57
|
+
line_number = splits.shift.strip.to_i
|
56
58
|
# we rejoin on ':' because our
|
57
59
|
# code may have colons inside of it.
|
58
60
|
#
|
59
61
|
# example:
|
60
|
-
# path/to/file: run_method(a: A, b: B)
|
62
|
+
# path/to/file:126: run_method(a: A, b: B)
|
61
63
|
#
|
62
64
|
# so shift the first one out, then
|
63
65
|
# rejoin the rest
|
64
66
|
line = splits.join(':').strip
|
65
67
|
|
68
|
+
# TODO: Oh goodness, why is this not a class
|
66
69
|
{
|
67
70
|
:file => system_path(file),
|
71
|
+
:line_number => line_number.to_i,
|
68
72
|
:line => line,
|
69
73
|
}
|
70
74
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test_launcher/search/ag'
|
2
|
+
require 'test_launcher/search/git'
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Search
|
6
|
+
def self.searcher(shell)
|
7
|
+
`which ag`
|
8
|
+
implementation =
|
9
|
+
if $?.success?
|
10
|
+
Search::Ag
|
11
|
+
else
|
12
|
+
Search::Git
|
13
|
+
end
|
14
|
+
|
15
|
+
implementation.new(shell)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -11,7 +11,7 @@ module TestLauncher
|
|
11
11
|
attr_accessor :log_path, :queue
|
12
12
|
private :log_path, :queue
|
13
13
|
|
14
|
-
def initialize(log_path:)
|
14
|
+
def initialize(log_path: "/dev/null")
|
15
15
|
@log_path = log_path
|
16
16
|
%x{echo "" > #{log_path}}
|
17
17
|
end
|
@@ -25,6 +25,7 @@ module TestLauncher
|
|
25
25
|
|
26
26
|
def exec(cmd)
|
27
27
|
notify cmd
|
28
|
+
$stdout.flush
|
28
29
|
Bundler.clean_exec(cmd)
|
29
30
|
end
|
30
31
|
|
data/lib/test_launcher.rb
CHANGED
@@ -1,27 +1 @@
|
|
1
1
|
require "test_launcher/version"
|
2
|
-
|
3
|
-
require "test_launcher/base_error"
|
4
|
-
require "test_launcher/shell/runner"
|
5
|
-
require "test_launcher/search/git"
|
6
|
-
require "test_launcher/frameworks"
|
7
|
-
|
8
|
-
module TestLauncher
|
9
|
-
def self.launch(request)
|
10
|
-
shell = Shell::Runner.new(log_path: "/tmp/test_launcher.log")
|
11
|
-
searcher = Search::Git.new(shell)
|
12
|
-
|
13
|
-
command = Frameworks.locate(
|
14
|
-
request: request,
|
15
|
-
shell: shell,
|
16
|
-
searcher: searcher
|
17
|
-
)
|
18
|
-
|
19
|
-
if command
|
20
|
-
shell.exec command
|
21
|
-
else
|
22
|
-
shell.warn "No tests found."
|
23
|
-
end
|
24
|
-
rescue BaseError => e
|
25
|
-
shell.warn(e)
|
26
|
-
end
|
27
|
-
end
|
data/test/test_helper.rb
CHANGED
@@ -4,6 +4,7 @@ require "mocha/mini_test"
|
|
4
4
|
require "pry"
|
5
5
|
|
6
6
|
require "test_launcher"
|
7
|
+
require "test_launcher/shell/runner"
|
7
8
|
|
8
9
|
class TestLauncher::Shell::Runner
|
9
10
|
def exec(cmd)
|
@@ -48,7 +49,7 @@ class TestCase < Minitest::Test
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def recall(method)
|
51
|
-
instance_variable_get(:"@#{method}")
|
52
|
+
instance_variable_get(:"@#{method}") || []
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "test_launcher/search/git"
|
2
|
+
require "test_launcher/shell/runner"
|
3
|
+
|
4
|
+
require "test_launcher/cli"
|
5
|
+
require "test_helpers/mocks"
|
6
|
+
|
7
|
+
module TestLauncher
|
8
|
+
module IntegrationHelper
|
9
|
+
include DefaultMocks
|
10
|
+
|
11
|
+
class IntegrationShell < Shell::Runner
|
12
|
+
def exec(string)
|
13
|
+
raise "Cannot exec twice!" if defined?(@exec)
|
14
|
+
@exec = string
|
15
|
+
end
|
16
|
+
|
17
|
+
def recall_exec
|
18
|
+
@exec
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def system_path(relative_dir)
|
25
|
+
File.join(Dir.pwd, relative_dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
def launch(search_string, run_all: false, framework:, name: nil)
|
29
|
+
argv = [search_string, "--framework", framework]
|
30
|
+
argv << "--all" if run_all
|
31
|
+
argv.concat(["--name", name]) if name
|
32
|
+
env = {}
|
33
|
+
CLI.launch(argv, env, shell: shell_mock)
|
34
|
+
end
|
35
|
+
|
36
|
+
def shell_mock
|
37
|
+
@shell_mock ||= IntegrationShell.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|