test_launcher 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/README.md +101 -18
  4. data/Rakefile +6 -0
  5. data/bin/test_launcher +8 -35
  6. data/lib/test_launcher.rb +22 -0
  7. data/lib/test_launcher/cli/input_parser.rb +73 -0
  8. data/lib/test_launcher/frameworks.rb +32 -0
  9. data/lib/test_launcher/frameworks/base.rb +54 -0
  10. data/lib/test_launcher/frameworks/implementation/collection.rb +27 -0
  11. data/lib/test_launcher/{tests/minitest → frameworks/implementation}/consolidator.rb +30 -32
  12. data/lib/test_launcher/frameworks/implementation/locator.rb +74 -0
  13. data/lib/test_launcher/frameworks/implementation/test_case.rb +65 -0
  14. data/lib/test_launcher/frameworks/minitest.rb +67 -0
  15. data/lib/test_launcher/frameworks/rspec.rb +50 -0
  16. data/lib/test_launcher/rubymine.rb +26 -0
  17. data/lib/test_launcher/rubymine/launcher.rb +55 -0
  18. data/lib/test_launcher/search/git.rb +64 -0
  19. data/lib/test_launcher/shell/runner.rb +19 -22
  20. data/lib/test_launcher/version.rb +1 -1
  21. data/test/install +3 -0
  22. data/test/test_helper.rb +32 -17
  23. data/test/test_launcher/fixtures/minitest/test/class_1_test.rb +17 -0
  24. data/test/test_launcher/fixtures/minitest/test/class_2_test.rb +5 -0
  25. data/test/test_launcher/fixtures/minitest/test/different_root/bin/spring +1 -0
  26. data/test/test_launcher/fixtures/minitest/test/different_root/test/different_root_test.rb +2 -0
  27. data/test/test_launcher/fixtures/rspec/spec/class_1_spec.rb +16 -0
  28. data/test/test_launcher/fixtures/rspec/spec/class_2_spec.rb +6 -0
  29. data/test/test_launcher/fixtures/rspec/spec/different_root/spec/different_root_spec.rb +2 -0
  30. data/test/test_launcher/minitest_integration_test.rb +67 -0
  31. data/test/test_launcher/rspec_integration_test.rb +67 -0
  32. data/test/test_launcher/rubymine_test.rb +48 -0
  33. data/test_launcher.gemspec +2 -3
  34. metadata +39 -44
  35. data/bin/test_runner +0 -1
  36. data/lib/test_launcher/searchers/git_searcher.rb +0 -38
  37. data/lib/test_launcher/tests/minitest/finder.rb +0 -50
  38. data/lib/test_launcher/tests/minitest/wrappers/multiple_files.rb +0 -23
  39. data/lib/test_launcher/tests/minitest/wrappers/multiple_roots.rb +0 -23
  40. data/lib/test_launcher/tests/minitest/wrappers/single_file.rb +0 -29
  41. data/lib/test_launcher/tests/minitest/wrappers/single_root.rb +0 -27
  42. data/lib/test_launcher/tests/minitest/wrappers/single_test.rb +0 -37
  43. data/lib/test_launcher/utils/path.rb +0 -32
  44. data/lib/test_launcher/utils/pluralize.rb +0 -14
  45. data/test/test_launcher/searchers/git_searcher_test.rb +0 -50
  46. data/test/test_launcher/tests/minitest/consolidator_integration_test.rb +0 -191
  47. data/test/test_launcher/tests/minitest/finder_test.rb +0 -71
@@ -0,0 +1,27 @@
1
+ require "delegate"
2
+
3
+ module TestLauncher
4
+ module Frameworks
5
+ module Implementation
6
+ class Collection < SimpleDelegator
7
+ alias :results :__getobj__
8
+
9
+ def file_count
10
+ results.group_by(&:file).size
11
+ end
12
+
13
+ def one_example?
14
+ examples_found? && results.size == 1
15
+ end
16
+
17
+ def examples_found?
18
+ results.any?(&:is_example?)
19
+ end
20
+
21
+ def last_edited
22
+ results.sort_by(&:mtime).last
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,15 +1,7 @@
1
- require "test_launcher/utils/path"
2
-
3
- require "test_launcher/tests/minitest/wrappers/single_test"
4
- require "test_launcher/tests/minitest/wrappers/single_file"
5
- require "test_launcher/tests/minitest/wrappers/multiple_files"
6
- require "test_launcher/utils/pluralize"
7
-
8
1
  module TestLauncher
9
- module Tests
10
- module Minitest
11
- class Consolidator < Struct.new(:search_results, :shell, :run_all)
12
- include Utils::Pluralize
2
+ module Frameworks
3
+ module Implementation
4
+ class Consolidator < Struct.new(:search_results, :shell, :runner, :run_all)
13
5
 
14
6
  def self.consolidate(*args)
15
7
  new(*args).consolidate
@@ -17,47 +9,44 @@ module TestLauncher
17
9
 
18
10
  def consolidate
19
11
  if search_results.empty?
20
- shell.warn "Could not find any tests."
21
- exit
22
- end
23
-
24
- if methods_found? && one_result?
12
+ nil
13
+ elsif one_example?
25
14
  shell.notify "Found #{methods_count_phrase} in #{file_count_phrase}."
26
- Wrappers::SingleTest.new(search_results.first)
27
- elsif methods_found? && same_file?
15
+ runner.single_example(search_results.first)
16
+ elsif examples_found? && same_file?
28
17
  shell.notify "Multiple test methods match in 1 file."
29
- Wrappers::SingleFile.new(search_results.first[:file])
30
- elsif methods_found? && run_last_edited?
18
+ runner.single_example(search_results.first)
19
+ elsif examples_found? && run_last_edited?
31
20
  shell.notify "Found #{methods_count_phrase} in #{file_count_phrase}."
32
21
  shell.notify "Running most recently edited. Run with '--all' to run all the tests."
33
- Wrappers::SingleTest.new(last_edited)
22
+ runner.single_example(last_edited)
34
23
  elsif files_found? && same_file?
35
24
  shell.notify "Found #{file_count_phrase}."
36
- Wrappers::SingleFile.new(search_results.first[:file])
25
+ runner.single_file(search_results.first)
37
26
  elsif files_found? && run_last_edited?
38
27
  shell.notify "Found #{file_count_phrase}."
39
28
  shell.notify "Running most recently edited. Run with '--all' to run all the tests."
40
- Wrappers::SingleFile.new(last_edited[:file])
29
+ runner.single_file(last_edited)
41
30
  else
42
31
  shell.notify "Found #{file_count_phrase}."
43
- Wrappers::MultipleFiles.wrap(search_results.map {|r| r[:file] }, shell)
32
+ runner.multiple_files(search_results.uniq {|sr| sr.file})
44
33
  end
45
34
  end
46
35
 
47
36
  def same_file?
48
- file_count == 1
37
+ file_count == 1
49
38
  end
50
39
 
51
- def one_result?
52
- same_file? && search_results.first[:line]
40
+ def one_example?
41
+ search_results.one_example?
53
42
  end
54
43
 
55
- def methods_found?
56
- !! search_results.first[:line]
44
+ def examples_found?
45
+ search_results.examples_found?
57
46
  end
58
47
 
59
48
  def files_found?
60
- ! methods_found?
49
+ ! examples_found?
61
50
  end
62
51
 
63
52
  def run_last_edited?
@@ -65,11 +54,11 @@ module TestLauncher
65
54
  end
66
55
 
67
56
  def last_edited
68
- search_results.sort_by {|r| File.mtime(r[:file])}.last
57
+ search_results.last_edited
69
58
  end
70
59
 
71
60
  def file_count
72
- search_results.group_by {|f| f[:file]}.size
61
+ search_results.file_count
73
62
  end
74
63
 
75
64
  def methods_count_phrase
@@ -79,6 +68,15 @@ module TestLauncher
79
68
  def file_count_phrase
80
69
  pluralize(file_count, "file")
81
70
  end
71
+
72
+ def pluralize(count, singular)
73
+ phrase = "#{count} #{singular}"
74
+ if count == 1
75
+ phrase
76
+ else
77
+ "#{phrase}s"
78
+ end
79
+ end
82
80
  end
83
81
  end
84
82
  end
@@ -0,0 +1,74 @@
1
+ require "test_launcher/frameworks/implementation/test_case"
2
+ require "test_launcher/frameworks/implementation/collection"
3
+
4
+ module TestLauncher
5
+ module Frameworks
6
+ module Implementation
7
+ class Locator < Struct.new(:query, :searcher)
8
+ private :query, :searcher
9
+
10
+ def prioritized_results
11
+ Collection.new(_prioritized_results)
12
+ end
13
+
14
+ def _prioritized_results
15
+ return files_found_by_absolute_path unless files_found_by_absolute_path.empty?
16
+
17
+ return examples_found_by_name unless examples_found_by_name.empty?
18
+
19
+ return files_found_by_file_name unless files_found_by_file_name.empty?
20
+
21
+ return files_found_by_full_regex unless files_found_by_full_regex.empty?
22
+
23
+ []
24
+ end
25
+
26
+ private
27
+
28
+ def files_found_by_absolute_path
29
+ return [] unless query.match(/^\//)
30
+
31
+ relative_file_path = query.sub(Dir.pwd, '').sub(/^\//, '')
32
+ [ build_result(file: relative_file_path) ]
33
+ end
34
+
35
+ def examples_found_by_name
36
+ @examples_found_by_name ||= full_regex_search(regex_pattern).map {|r| build_result(file: r[:file], query: query)}
37
+ end
38
+
39
+ def files_found_by_file_name
40
+ @files_found_by_file_name ||= searcher.find_files(query).select { |f| f.match(file_name_regex) }.map {|f| build_result(file: f) }
41
+ end
42
+
43
+ def files_found_by_full_regex
44
+ # we ignore the matched line since we don't know what to do with it
45
+ @files_found_by_full_regex ||= full_regex_search(query).map {|r| build_result(file: r[:file]) }
46
+ end
47
+
48
+ def full_regex_search(regex)
49
+ searcher.grep(regex, file_pattern: file_name_pattern)
50
+ end
51
+
52
+ def build_result(file:, query: nil)
53
+ test_case_class.from_search(file: file, query: query)
54
+ end
55
+
56
+ def file_name_regex
57
+ raise NotImplementedError
58
+ end
59
+
60
+ def file_name_pattern
61
+ raise NotImplementedError
62
+ end
63
+
64
+ def regex_pattern
65
+ raise NotImplementedError
66
+ end
67
+
68
+ def test_case_class
69
+ raise NotImplementedError
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,65 @@
1
+ require "pathname"
2
+
3
+ module TestLauncher
4
+ module Frameworks
5
+ module Implementation
6
+ class TestCase
7
+ attr_reader :file, :example
8
+
9
+ def self.from_search(file:, query: nil)
10
+ new(file: file, example: query)
11
+ end
12
+
13
+ def initialize(file:, example: nil)
14
+ @file = file
15
+ @example = example
16
+ end
17
+
18
+ def is_example?
19
+ !example.nil?
20
+ end
21
+
22
+ def mtime
23
+ @mtime ||= File.mtime(file)
24
+ end
25
+
26
+ def app_root
27
+ path = exploded_path[0...exploded_path.rindex(test_root_folder_name)]
28
+ File.join("/", path)
29
+ end
30
+
31
+ def test_root
32
+ File.join(app_root, test_root_folder_name)
33
+ end
34
+
35
+ def relative_test_path
36
+ path = exploded_path[exploded_path.rindex(test_root_folder_name)..-1]
37
+ File.join(path)
38
+ end
39
+
40
+ def spring_enabled?
41
+ return false if ENV['DISABLE_SPRING']
42
+
43
+ [
44
+ "bin/spring",
45
+ "bin/testunit"
46
+ ].any? {|f|
47
+ File.exist?(File.join(app_root, f))
48
+ }
49
+ end
50
+
51
+ def runner
52
+ raise NotImplementedError
53
+ end
54
+
55
+ def test_root_folder_name
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def exploded_path
60
+ Pathname.new(file).each_filename.to_a
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,67 @@
1
+ require "test_launcher/frameworks/base"
2
+
3
+ module TestLauncher
4
+ module Frameworks
5
+ module Minitest
6
+
7
+ def self.active?
8
+ ! Dir.glob("**/test/**/*_test.rb").empty?
9
+ end
10
+
11
+ class Runner < Base::Runner
12
+ def single_example(test_case, exact_match: false)
13
+
14
+ name =
15
+ if exact_match
16
+ "--name=#{test_case.example}"
17
+ else
18
+ "--name=/#{test_case.example}/"
19
+ end
20
+
21
+ %{cd #{test_case.app_root} && #{test_case.runner} #{test_case.relative_test_path} #{name}}
22
+ end
23
+
24
+ def one_or_more_files(test_cases)
25
+ %{cd #{test_cases.first.app_root} && #{test_cases.first.runner} #{test_cases.map(&:relative_test_path).join(" ")}}
26
+ end
27
+ end
28
+
29
+ class Locator < Base::Locator
30
+ private
31
+
32
+ def file_name_regex
33
+ /.*_test\.rb/
34
+ end
35
+
36
+ def file_name_pattern
37
+ "*_test.rb"
38
+ end
39
+
40
+ def regex_pattern
41
+ "^\s*def test_.*#{query.sub(/^test_/, "")}.*"
42
+ end
43
+
44
+ def test_case_class
45
+ TestCase
46
+ end
47
+ end
48
+
49
+ class TestCase < Base::TestCase
50
+
51
+ def runner
52
+ if spring_enabled?
53
+ "bundle exec spring testunit"
54
+ elsif is_example?
55
+ "bundle exec ruby -I test"
56
+ else
57
+ "bundle exec ruby -I test -e 'ARGV.each {|f| require(File.join(Dir.pwd, f))}'"
58
+ end
59
+ end
60
+
61
+ def test_root_folder_name
62
+ "test"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,50 @@
1
+ require "shellwords"
2
+
3
+ require "test_launcher/frameworks/base"
4
+
5
+ module TestLauncher
6
+ module Frameworks
7
+ module RSpec
8
+
9
+ def self.active?
10
+ ! Dir.glob("**/*_spec.rb").empty?
11
+ end
12
+
13
+ class Runner < Base::Runner
14
+ def single_example(test_case)
15
+ %{cd #{test_case.app_root} && rspec #{test_case.relative_test_path} --example #{Shellwords.escape(test_case.example)}}
16
+ end
17
+
18
+ def one_or_more_files(test_cases)
19
+ %{cd #{test_cases.first.app_root} && rspec #{test_cases.map(&:relative_test_path).join(" ")}}
20
+ end
21
+ end
22
+
23
+ class Locator < Base::Locator
24
+ private
25
+
26
+ def file_name_regex
27
+ /.*_spec\.rb/
28
+ end
29
+
30
+ def file_name_pattern
31
+ '*_spec.rb'
32
+ end
33
+
34
+ def regex_pattern
35
+ "^\s*(it|context|(RSpec.)?describe) .*#{query}.* do.*"
36
+ end
37
+
38
+ def test_case_class
39
+ TestCase
40
+ end
41
+ end
42
+
43
+ class TestCase < Base::TestCase
44
+ def test_root_folder_name
45
+ "spec"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ require "test_launcher/rubymine/launcher"
2
+ require "test_launcher/shell/runner"
3
+
4
+ # To allow us to simply specify our run configuration as:
5
+ #
6
+ # -r test_launcher/rubymine
7
+ #
8
+ # we need to put the currently executing script in with the args.
9
+ #
10
+ # Consider the following examples:
11
+ #
12
+ # ruby -r test_launcher/rubymine /path/to/test.rb
13
+ #
14
+ # vs
15
+ #
16
+ # ruby -r test_launcher/rubymine spring testunit /path/to/test.rb
17
+ #
18
+ # In one case, our test to run is $0 and in another case it's an ARGV.
19
+ # So we throw them in the same bucket and let the launcher figure it
20
+ # out. It doesn't matter since we will `exec` a new command anyway.
21
+
22
+
23
+ TestLauncher::Rubymine::Launcher.new(
24
+ args: [$0].concat(ARGV),
25
+ shell: TestLauncher::Shell::Runner.new(log_path: "/dev/null")
26
+ ).launch
@@ -0,0 +1,55 @@
1
+ require "test_launcher/frameworks/minitest"
2
+ require "test_launcher/shell/runner"
3
+
4
+ module TestLauncher
5
+ module Rubymine
6
+ class Launcher
7
+ def initialize(args:, shell:)
8
+ @args = args
9
+ @shell = shell
10
+ end
11
+
12
+ def launch
13
+ if args.any? {|a| a.match("ruby-debug-ide")}
14
+ shell.puts "test_launcher: hijacking and debugging"
15
+
16
+ debug_command = "cd #{test_case.app_root} && ruby -I test #{args.join(" ")}"
17
+ shell.puts debug_command
18
+ shell.exec debug_command
19
+ else
20
+ shell.puts "test_launcher: hijacking and running:"
21
+ shell.puts command
22
+
23
+ shell.exec command
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def command
30
+ if test_case.is_example?
31
+ Frameworks::Minitest::Runner.new.single_example(test_case, exact_match: true)
32
+ else
33
+ Frameworks::Minitest::Runner.new.single_file(test_case)
34
+ end
35
+ end
36
+
37
+ def test_case
38
+ @test_case ||=
39
+ if args[-1].match('--name=')
40
+ Frameworks::Minitest::TestCase.new(file: args[-2], example: args[-1][/--name=(.*)/, 1])
41
+ else
42
+ Frameworks::Minitest::TestCase.new(file: args[-1])
43
+ end
44
+ end
45
+
46
+ def args
47
+ @args
48
+ end
49
+
50
+ def shell
51
+ @shell
52
+ end
53
+ end
54
+ end
55
+ end