test_launcher 1.5.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -37
  3. data/lib/test_launcher/cli.rb +1 -3
  4. data/lib/test_launcher/cli/input_parser.rb +10 -6
  5. data/lib/test_launcher/frameworks/base.rb +11 -0
  6. data/lib/test_launcher/frameworks/{elixir.rb → ex_unit.rb} +16 -20
  7. data/lib/test_launcher/frameworks/minitest.rb +29 -17
  8. data/lib/test_launcher/frameworks/rspec.rb +14 -7
  9. data/lib/test_launcher/queries.rb +47 -31
  10. data/lib/test_launcher/shell/runner.rb +2 -1
  11. data/lib/test_launcher/version.rb +1 -1
  12. data/test/test_helpers/integration_helper.rb +6 -13
  13. data/test/test_helpers/mocks.rb +1 -0
  14. data/test/test_helpers/mocks/searcher_mock.rb +3 -1
  15. data/test/test_launcher/ex_unit_integration_test.rb +392 -0
  16. data/test/test_launcher/fixtures/minitest/test/class_1_test.rb +4 -15
  17. data/test/test_launcher/fixtures/minitest/test/class_2_test.rb +7 -3
  18. data/test/test_launcher/frameworks/minitest/runner_test.rb +10 -10
  19. data/test/test_launcher/frameworks/minitest/searcher_test.rb +11 -10
  20. data/test/test_launcher/frameworks/rspec/runner_test.rb +5 -5
  21. data/test/test_launcher/frameworks/rspec/searcher_test.rb +4 -4
  22. data/test/test_launcher/minitest_integration_test.rb +533 -38
  23. data/test/test_launcher/queries/example_name_query_test.rb +5 -1
  24. data/test/test_launcher/queries/line_number_query_test.rb +7 -7
  25. data/test/test_launcher/queries/multi_term_query_test.rb +0 -12
  26. data/test/test_launcher/rspec_integration_test.rb +458 -35
  27. metadata +5 -5
  28. data/test/test_launcher/queries/specified_name_query_test.rb +0 -112
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 26993369cd4119c8bfba37182fbd4f95d9b4607d
4
- data.tar.gz: e2cc315b236f0ced3a052e3fc609b382b5ade67d
3
+ metadata.gz: ccdfde0aec34dd6c9644f3985b2fec6c8b80d65a
4
+ data.tar.gz: a580b9f1354854325582f98d02a42626065cca23
5
5
  SHA512:
6
- metadata.gz: dfddbd2cafefd68989321ab4f4b469cbccc35f5e6ab1f8a9105a282ea81720cd495ac4cb778a51e4f347fd7c43d1aee743aa0dc5d9a150b3bb250485ae304108
7
- data.tar.gz: ba0ad8e49131fdbe06862a163f02f453c5dacfe8f4c9d45645eb1b3f0dd99fae5dae5566cf6dbf277fe616cc02123fad97f4713c05c67f37eb0498a53b1e6183
6
+ metadata.gz: 264cb91173593078f956c8a76e527494cd033a3d647d31d1527528eef1afb84d0273707004c55d0057bfdc7f15411384be3da46af1197d942fb05a8ee55b1be7
7
+ data.tar.gz: 509301a76e17513abaeb8952230a43844392f09ca0d7ae520b8909faedde428a485bf02c44ba40b17c5a9ee371f0890eae053bfd56ea1996d1fc7984aed02241
data/README.md CHANGED
@@ -1,8 +1,25 @@
1
- #Test Launcher -->
1
+ # Test Launcher -->
2
2
 
3
- Test Launcher takes some input and tries to figure out what test you want to run. It makes running tests on the command line much easier. Test Launcher always outputs the command that it has decided to run so that you can verify that it is running the test you want it to run.
3
+ Test Launcher takes a search query and tries to figure out what test you want to run. It makes running tests on the command line easy! Super bonus!
4
4
 
5
- Test Launcher works with Minitest. RSpec support is in its infancy. You should try it and let me know!
5
+ You might use Test Launcher for two reasons:
6
+
7
+ 1. You run tests from the command line a lot.
8
+ 2. You work in a ruby app that contains inline gems/engines with their own test suites.
9
+
10
+ It was built for Minitest, but it also has basic support for RSpec and ExUnit.
11
+
12
+ See the __Usages__ section below for some examples.
13
+
14
+ # Installation
15
+
16
+ To install:
17
+
18
+ ```
19
+ gem install test_launcher
20
+ ```
21
+
22
+ Under the hood, it uses git to determine your project root. If you're on an app that's not using git, let me know and I can remove that dependency.
6
23
 
7
24
  ### Usage
8
25
 
@@ -19,6 +36,7 @@ But with Test Launcher, you can just type this:
19
36
  ```
20
37
  test_launcher test_blog_name_thing
21
38
 
39
+ #=> Found 1 example in 1 file
22
40
  #=> ruby -I test test/models/blog_post_test.rb --name=test_blog_name_thing
23
41
  ```
24
42
 
@@ -30,6 +48,22 @@ test_launcher blog_post_test
30
48
  #=> ruby -I test test/models/blog_post_test.rb
31
49
  ```
32
50
 
51
+ Maybe you'd like to run a whole folder?
52
+
53
+ ```
54
+ test_launcher test/models --all
55
+
56
+ #=> ruby -I test -e 'ARGV.each {|f| require(f)}' test/models/blog_post_test.rb test/models/comment_test.rb
57
+ ```
58
+
59
+ You can run specific test methods by line:
60
+ ```
61
+ test_launcher blog_post_test.rb:13
62
+
63
+ #=> Found 1 example in 1 file
64
+ #=> ruby -I test test/models/blog_post_test.rb --name=test_blog_name_thing
65
+ ```
66
+
33
67
  What if you just have the class name for the test?
34
68
 
35
69
  ```
@@ -55,16 +89,27 @@ test_launcher /Users/username/code/my_repo/test/models/blog_post_test.rb
55
89
  #=> ruby -I test test/models/blog_post_test.rb
56
90
  ```
57
91
 
58
- But can it run two files for you?
92
+ Suppose you have multiple files you'd like to run:
59
93
  ```
60
94
  test_launcher blog_post_test.rb comment_test.rb
61
95
 
62
96
  #=> ruby -I test -e 'ARGV.each {|f| require(f)}' test/models/blog_post_test.rb test/models/comment_test.rb
63
97
  ```
64
98
 
99
+ Or maybe you'd like to run all test methods that match a regular expression:
100
+
101
+ ```
102
+ test_launcher 'hello_\w*|goodbye_\w+'
103
+
104
+ #=> Found 2 methods in 2 files.
105
+ #=> bundle exec ruby -I test -e "ARGV.push('--name=/hello_\w*|goodbye_\w+/')" -r /src/test/file_1_test.rb -r /src/test/file_2_test.rb
106
+ ```
107
+
65
108
  ### Inline Gems
66
109
 
67
- Test Launcher will automatically move to the correct subdirectory in order to run the tests. For example, if `thing_test.rb` is within your inline_gem, you can run:
110
+ If you work in an application that has inlined gems/engines, you've probably already experienced pain around running tests. IDEs and editor plugins have a hard time understanding how to run tests for inlined gems when you have opened the project from a parent folder. By looking for Gemfiles and gemspecs, Test Launcher can run your tests in the correct context. If you are using RubyMine in a project with inline gems, see the __RubyMine__ section below.
111
+
112
+ For example, if `thing_test.rb` is within your inline_gem, you can run:
68
113
 
69
114
  ```
70
115
  test_launcher thing_test
@@ -72,7 +117,7 @@ test_launcher thing_test
72
117
  #=> cd /path/to/inline_gem && ruby -I test test/thing_test.rb
73
118
  ```
74
119
 
75
- You don't have to run Test Launcher from the root of your project either. It will figure things out.
120
+ You don't have to run Test Launcher from the root of your project either. It will figure things out, even when using the `--all` flag!
76
121
 
77
122
  ### Spring preloader
78
123
 
@@ -86,6 +131,29 @@ test_launcher springified_test
86
131
 
87
132
  Test Launcher will not use spring if the `DISABLE_SPRING=1` environment variable is set.
88
133
 
134
+ ### Priorities
135
+
136
+ Test Launcher searches for tests based on your input.
137
+
138
+ Suppose you type `test_launcher thing`. It will run tests using this priority preference:
139
+
140
+ 1. A single test file
141
+ - matches on `thing_test.rb`
142
+
143
+ 1. A single, specific test method name or partial name
144
+ - `def test_the_thing`
145
+
146
+ 1. Multiple test method names in the same file
147
+ - `def test_the_thing` and `def test_the_other_thing`
148
+
149
+ 1. Any test file based on a generic search
150
+ - runs `stuff_test.rb` because it found the word `thing` inside of it
151
+
152
+ If your looks like it's specifying a line number (e.g. `file_test.rb:17`), that search will be preferred.
153
+
154
+ Any time it matches multiple files, it will default to running the most recently edited file. You can append `--all` if you want to run all matching tests, even if they are in different engines/gems!
155
+
156
+
89
157
  ### Running all tests you've changed:
90
158
 
91
159
  This will find all uncommitted `*_test.rb` files and pass them to test_launcher to be run. Use this before you commit so you don't accidentally commit a test you've broken.
@@ -119,18 +187,7 @@ tdiff origin/master
119
187
 
120
188
  Super fun!
121
189
 
122
- #Installation
123
-
124
- To install:
125
-
126
- ```
127
- gem install test_launcher
128
- ```
129
-
130
- Under the hood it uses git to search for files and to grep, so it will only work in git repositories.
131
-
132
-
133
- #Setup
190
+ ### Setup
134
191
 
135
192
  This gem installs one executable called `test_launcher`.
136
193
 
@@ -149,25 +206,6 @@ alias t='NOEXEC_DISABLE=1 test_launcher'
149
206
 
150
207
  Now you can just type `t` instead of `test_launcher`. Much nicer!
151
208
 
152
- #Usage
153
-
154
- Test Launcher searches for tests based on your input.
155
-
156
- Suppose you type `test_launcher thing`. It will run tests using this priority preference:
157
-
158
- 1. A single, specific test method name or partial name
159
- - `def test_the_thing`
160
-
161
- 1. Multiple test method names in the same file
162
- - `def test_the_thing` and `def test_the_other_thing`
163
-
164
- 1. A single test file
165
- - matches on `thing_test.rb`
166
-
167
- 1. Any test file based on a generic search
168
- - runs `stuff_test.rb` because it found the word `thing` inside of it
169
-
170
- Any time it matches multiple files, it will default to running the most recently edited file. You can append `--all` if you want to run all matching tests, even if they are in different engines/gems!
171
209
 
172
210
  # RubyMine Support
173
211
 
@@ -230,3 +268,9 @@ I suggest that if you are using RVM, you may as well make this your alias:
230
268
  ```
231
269
  alias t='NOEXEC_DISABLE=1 test_launcher'
232
270
  ```
271
+
272
+ # What's going on in there?
273
+
274
+ Test Launcher began life as a bash script patching together `ag`, `awk`, `grep`, and all sorts of insanity. After looking up how to do an if/else in bash for the millionth time, I decided to rewrite it in Ruby.
275
+
276
+ Test Launcher was developed using "BDD" (Bug Driven Development). Because I use it so heavily, I'd pop over and hack something precisely at the moment I needed it (often breaking all sorts of other things). I've since added some tests, but I reserve the right to break anything at anytime if only for old times' sake.
@@ -22,9 +22,7 @@ module TestLauncher
22
22
  end
23
23
  end
24
24
 
25
- def self.launch(argv, env, shell: Shell::Runner.new(log_path: "/tmp/test_launcher.log"))
26
- searcher = Search.searcher(shell)
27
-
25
+ def self.launch(argv, env, shell: Shell::Runner.new(log_path: "/tmp/test_launcher.log"), searcher: Search.searcher(shell))
28
26
  requests = CLI::InputParser.new(
29
27
  argv,
30
28
  env
@@ -4,7 +4,7 @@ require "test_launcher/version"
4
4
 
5
5
  require "test_launcher/frameworks/rspec"
6
6
  require "test_launcher/frameworks/minitest"
7
- require "test_launcher/frameworks/elixir"
7
+ require "test_launcher/frameworks/ex_unit"
8
8
  require "test_launcher/cli/request"
9
9
 
10
10
  module TestLauncher
@@ -46,10 +46,10 @@ VERSION: #{TestLauncher::VERSION}
46
46
  [Frameworks::RSpec]
47
47
  elsif @options[:framework] == "minitest"
48
48
  [Frameworks::Minitest]
49
- elsif @options[:framework] == "elixir"
50
- [Frameworks::Elixir]
49
+ elsif @options[:framework] == "ex_unit"
50
+ [Frameworks::ExUnit]
51
51
  else
52
- [Frameworks::Minitest, Frameworks::RSpec, Frameworks::Elixir]
52
+ [Frameworks::Minitest, Frameworks::RSpec, Frameworks::ExUnit]
53
53
  end
54
54
 
55
55
  frameworks.map {|framework|
@@ -89,13 +89,17 @@ VERSION: #{TestLauncher::VERSION}
89
89
  exit
90
90
  end
91
91
 
92
- opts.on("-f", "--framework framework", "The testing framework being used. Valid options: ['minitest', 'rspec', 'elixir', 'guess']. Defaults to 'guess'") do |framework|
92
+ opts.on("-f", "--framework framework", "The testing framework being used. Valid options: ['minitest', 'rspec', 'ex_unit', 'guess']. Defaults to 'guess'") do |framework|
93
93
  options[:framework] = framework
94
94
  end
95
95
 
96
- opts.on("-n", "--name name", "Minitest name of testcase to run") do |name|
96
+ opts.on("-n", "--name name", "Name of testcase/example to run. This will pass through to the selected framework without verifying that the example actually exists. This option really only exists to work with tooling that will automatically run your tests. You shouldn't have much need for this.") do |name|
97
97
  options[:name] = name
98
98
  end
99
+
100
+ opts.on("--example example", "alias of name") do |example|
101
+ options[:name] = example
102
+ end
99
103
  end
100
104
  end
101
105
  end
@@ -38,6 +38,10 @@ module TestLauncher
38
38
  end
39
39
 
40
40
  class Runner
41
+ def by_line_number(test_case)
42
+ raise NotImplementedError
43
+ end
44
+
41
45
  def single_example(test_case)
42
46
  raise NotImplementedError
43
47
  end
@@ -54,6 +58,13 @@ module TestLauncher
54
58
  one_or_more_files([test_case])
55
59
  end
56
60
 
61
+ def multiple_examples(collection)
62
+ collection
63
+ .group_by(&:app_root)
64
+ .map { |_root, test_cases| multiple_examples_same_root(test_cases) }
65
+ .join("; cd -;\n\n")
66
+ end
67
+
57
68
  def multiple_files(collection)
58
69
  collection
59
70
  .group_by(&:app_root)
@@ -3,7 +3,7 @@ require "test_launcher/base_error"
3
3
 
4
4
  module TestLauncher
5
5
  module Frameworks
6
- module Elixir
6
+ module ExUnit
7
7
  def self.active?
8
8
  # Do not do this outside of the shell.
9
9
  ! Dir.glob("**/test/**/*.exs").empty?
@@ -25,13 +25,11 @@ module TestLauncher
25
25
  MultipleByLineMatches = Class.new(BaseError)
26
26
 
27
27
  def by_line(file_pattern, line_number)
28
- files = test_files(file_pattern)
29
- return unless files.any?
30
- raise multiple_files_error if files.size > 1
31
-
32
- {
33
- file: files.first,
34
- line_number: line_number
28
+ test_files(file_pattern).map {|file|
29
+ {
30
+ file: file,
31
+ line_number: line_number
32
+ }
35
33
  }
36
34
  end
37
35
 
@@ -48,25 +46,23 @@ module TestLauncher
48
46
  def example_name_regex(query="")
49
47
  "^\s*test\s+\".*#{query}.*\"\s+do"
50
48
  end
51
-
52
- def multiple_files_error
53
- MultipleByLineMatches.new(<<-MSG)
54
- It looks like you are running a line number in a test file.
55
-
56
- Multiple files have been found that match your query.
57
-
58
- This case is not supported.
59
- MSG
60
- end
61
49
  end
62
50
 
63
51
  class Runner < Base::Runner
64
- def single_example(test_case)
52
+ def by_line_number(test_case)
65
53
  %{cd #{test_case.app_root} && mix test #{test_case.file}:#{test_case.line_number}}
66
54
  end
67
55
 
56
+ def single_example(test_case)
57
+ by_line_number(test_case)
58
+ end
59
+
68
60
  def multiple_examples_same_file(test_cases)
69
- one_or_more_files(test_cases.first)
61
+ one_or_more_files(test_cases.uniq {|tc| tc.file})
62
+ end
63
+
64
+ def multiple_examples_same_root(test_cases)
65
+ one_or_more_files(test_cases.uniq {|tc| tc.file})
70
66
  end
71
67
 
72
68
  def one_or_more_files(test_cases)
@@ -27,7 +27,7 @@ module TestLauncher
27
27
 
28
28
  def by_line(file_pattern, line_number)
29
29
  files = test_files(file_pattern)
30
- return unless files.any?
30
+ return [] unless files.any?
31
31
  raise multiple_files_error if files.size > 1
32
32
  #
33
33
  file = files.first
@@ -39,16 +39,16 @@ module TestLauncher
39
39
  .min_by {|r| line_number - r[:line_number]}
40
40
 
41
41
  if best_result
42
- {
42
+ [{
43
43
  file: best_result[:file],
44
44
  example_name: best_result[:line].match(/(test_\w+)/)[1],
45
45
  line_number: best_result[:line_number]
46
- }
46
+ }]
47
47
  else
48
48
  # line number outside of example. Run whole file
49
- {
49
+ [{
50
50
  file: grep_results.first[:file]
51
- }
51
+ }]
52
52
  end
53
53
  end
54
54
 
@@ -72,26 +72,36 @@ module TestLauncher
72
72
 
73
73
  def multiple_files_error
74
74
  MultipleByLineMatches.new(<<-MSG)
75
- It looks like you are running a line number in a test file.
75
+ It looks like you are running a line number in a test file.
76
76
 
77
- Multiple files have been found that match your query.
77
+ Multiple files have been found that match your query.
78
78
 
79
- This case is not supported.
79
+ This case is not supported for Minitest.
80
+
81
+ Open an issue on https://github.com/petekinnecom/test_launcher if this is something you have run into at least 3 times. :)
80
82
  MSG
81
83
  end
82
84
 
83
85
  end
84
86
 
85
87
  class Runner < Base::Runner
88
+ def by_line_number(test_case)
89
+ if test_case.example
90
+ single_example(test_case)
91
+ else
92
+ single_file(test_case)
93
+ end
94
+ end
95
+
86
96
  def single_example(test_case, name: test_case.example, exact_match: false)
87
- regex_string =
97
+ name_arg =
88
98
  if exact_match
89
- name
99
+ Shellwords.escape(name)
90
100
  else
91
101
  "/#{Shellwords.escape(name)}/"
92
102
  end
93
103
 
94
- %{cd #{test_case.app_root} && #{test_case.runner} #{test_case.file} --name=#{regex_string}}
104
+ %{cd #{test_case.app_root} && #{test_case.example_runner} #{test_case.file} --name=#{name_arg}}
95
105
  end
96
106
 
97
107
  def multiple_examples_same_file(test_cases)
@@ -99,23 +109,25 @@ module TestLauncher
99
109
  single_example(test_cases.first)
100
110
  end
101
111
 
112
+ def multiple_examples_same_root(test_cases)
113
+ %{cd #{test_cases.first.app_root} && bundle exec ruby -I test -e "ARGV.push('--name=/#{test_cases.first.example}/')" #{test_cases.map {|tc| "-r #{tc.file}"}.uniq.join(" ")}}
114
+ end
115
+
102
116
  def one_or_more_files(test_cases)
103
- %{cd #{test_cases.first.app_root} && #{test_cases.first.multiple_files_runner} #{test_cases.map(&:file).join(" ")}}
117
+ %{cd #{test_cases.first.app_root} && #{test_cases.first.file_runner} #{test_cases.map(&:file).uniq.join(" ")}}
104
118
  end
105
119
  end
106
120
 
107
121
  class TestCase < Base::TestCase
108
- def runner
122
+ def example_runner
109
123
  if spring_enabled?
110
124
  "bundle exec spring testunit"
111
- elsif is_example?
112
- "bundle exec ruby -I test"
113
125
  else
114
- "bundle exec ruby -I test -e 'ARGV.each {|f| require(f)}'"
126
+ "bundle exec ruby -I test"
115
127
  end
116
128
  end
117
129
 
118
- def multiple_files_runner
130
+ def file_runner
119
131
  if spring_enabled?
120
132
  "bundle exec spring testunit"
121
133
  else
@@ -25,12 +25,12 @@ module TestLauncher
25
25
 
26
26
  def by_line(file_pattern, line_number)
27
27
  files = test_files(file_pattern)
28
- return unless files.any?
29
- raise multiple_files_error if files.size > 1
30
28
 
31
- {
32
- file: files.first,
33
- line_number: line_number
29
+ files.map {|file|
30
+ {
31
+ file: file,
32
+ line_number: line_number
33
+ }
34
34
  }
35
35
  end
36
36
 
@@ -50,8 +50,12 @@ module TestLauncher
50
50
  end
51
51
 
52
52
  class Runner < Base::Runner
53
+ def by_line_number(test_case)
54
+ %{cd #{test_case.app_root} && bundle exec rspec #{test_case.file}:#{test_case.line_number}}
55
+ end
56
+
53
57
  def single_example(test_case, **_)
54
- %{cd #{test_case.app_root} && rspec #{test_case.file} --example #{Shellwords.escape(test_case.example)}}
58
+ %{cd #{test_case.app_root} && bundle exec rspec #{test_case.file} --example #{Shellwords.escape(test_case.example)}}
55
59
  end
56
60
 
57
61
  def multiple_examples_same_file(test_cases)
@@ -59,9 +63,12 @@ module TestLauncher
59
63
  single_example(test_case)
60
64
  end
61
65
 
66
+ def multiple_examples_same_root(test_cases)
67
+ one_or_more_files(test_cases.uniq {|tc| tc.file})
68
+ end
62
69
 
63
70
  def one_or_more_files(test_cases)
64
- %{cd #{test_cases.first.app_root} && rspec #{test_cases.map(&:file).join(" ")}}
71
+ %{cd #{test_cases.first.app_root} && bundle exec rspec #{test_cases.map(&:file).join(" ")}}
65
72
  end
66
73
  end
67
74