test_launcher 0.0.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 +7 -0
- data/.gitignore +16 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +2 -0
- data/bin/test_launcher +39 -0
- data/bin/test_runner +1 -0
- data/lib/test_launcher.rb +4 -0
- data/lib/test_launcher/searchers/git_searcher.rb +38 -0
- data/lib/test_launcher/shell/color.rb +26 -0
- data/lib/test_launcher/shell/runner.rb +66 -0
- data/lib/test_launcher/tests/minitest/consolidator.rb +85 -0
- data/lib/test_launcher/tests/minitest/finder.rb +50 -0
- data/lib/test_launcher/tests/minitest/wrappers/multiple_files.rb +23 -0
- data/lib/test_launcher/tests/minitest/wrappers/multiple_roots.rb +23 -0
- data/lib/test_launcher/tests/minitest/wrappers/single_file.rb +29 -0
- data/lib/test_launcher/tests/minitest/wrappers/single_root.rb +27 -0
- data/lib/test_launcher/tests/minitest/wrappers/single_test.rb +37 -0
- data/lib/test_launcher/utils/path.rb +32 -0
- data/lib/test_launcher/utils/pluralize.rb +14 -0
- data/lib/test_launcher/version.rb +3 -0
- data/test/test_helper.rb +45 -0
- data/test/test_launcher/searchers/git_searcher_test.rb +50 -0
- data/test/test_launcher/tests/minitest/consolidator_integration_test.rb +191 -0
- data/test/test_launcher/tests/minitest/finder_test.rb +71 -0
- data/test_launcher.gemspec +23 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bbd6981a7b53e9af503b959d87bd3dc21bc312a0
|
4
|
+
data.tar.gz: 1c2978d49096d3af9cc704c30d83eb1c554cff48
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 30a4da05547da73f41746d1a839052493938964094700ea009a9ff37c3506a8d1e9ec348a4cbe6c62fba22ff4587e0bea2fc7b8e9714dff7b7f99468e2ce4a24
|
7
|
+
data.tar.gz: 6d3fb76d08ef634a12108f74643e86f58e88b601b493e07f7ce72b2ef029a1fcb6562b8ffc5c162af223aa6f693fb49ca7523d1261c951defd923ba821b9490d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Pete Kinnecom
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# TestLauncher
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'test_launcher'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install test_launcher
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/test_launcher/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/test_launcher
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "test_launcher/searchers/git_searcher"
|
5
|
+
require "test_launcher/tests/minitest/finder"
|
6
|
+
require "test_launcher/tests/minitest/consolidator"
|
7
|
+
require "test_launcher/shell/runner"
|
8
|
+
|
9
|
+
class CLI < Thor
|
10
|
+
|
11
|
+
desc "find", <<-DESC
|
12
|
+
Find tests and run them. By trying to match an individual test or the name of a test file(s).
|
13
|
+
DESC
|
14
|
+
option :all, type: :boolean, desc: %{Run all tests found!}, default: false
|
15
|
+
|
16
|
+
def find(input)
|
17
|
+
if input == '--help'
|
18
|
+
`test_launcher --help find`
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
shell = TestLauncher::Shell::Runner.new(
|
23
|
+
log_path: '/tmp/test_launcher.log',
|
24
|
+
working_directory: '.',
|
25
|
+
|
26
|
+
)
|
27
|
+
|
28
|
+
searcher = TestLauncher::Searchers::GitSearcher.new(shell)
|
29
|
+
search_results = TestLauncher::Tests::Minitest::Finder.find(input, searcher)
|
30
|
+
|
31
|
+
test_wrapper = TestLauncher::Tests::Minitest::Consolidator.consolidate(search_results, shell, options[:all])
|
32
|
+
|
33
|
+
shell.exec test_wrapper.to_command
|
34
|
+
end
|
35
|
+
|
36
|
+
default_task :find
|
37
|
+
end
|
38
|
+
|
39
|
+
CLI.start(ARGV)
|
data/bin/test_runner
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts "`test_runner` no longer exists. Please use `test_launcher` instead (update any aliases you may have)"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module TestLauncher
|
2
|
+
module Searchers
|
3
|
+
class GitSearcher < Struct.new(:shell)
|
4
|
+
|
5
|
+
def find_files(pattern)
|
6
|
+
shell.run("git ls-files '*#{pattern}*'")
|
7
|
+
end
|
8
|
+
|
9
|
+
def grep(regex, file_pattern: '*')
|
10
|
+
results = shell.run("git grep --untracked '#{regex}' -- '#{file_pattern}'")
|
11
|
+
results.map do |result|
|
12
|
+
interpret_grep_result(result)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def interpret_grep_result(grep_result)
|
19
|
+
splits = grep_result.split(/:/)
|
20
|
+
file = splits.shift.strip
|
21
|
+
# we rejoin on ':' because our
|
22
|
+
# code may have colons inside of it.
|
23
|
+
#
|
24
|
+
# example:
|
25
|
+
# path/to/file: run_method(a: A, b: B)
|
26
|
+
#
|
27
|
+
# so shift the first one out, then
|
28
|
+
# rejoin the rest
|
29
|
+
line = splits.join(':').strip
|
30
|
+
|
31
|
+
{
|
32
|
+
:file => file,
|
33
|
+
:line => line,
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TestLauncher
|
2
|
+
module Shell
|
3
|
+
module Color
|
4
|
+
CODES = {
|
5
|
+
red: 31,
|
6
|
+
green: 32,
|
7
|
+
yellow: 33,
|
8
|
+
pink: 35,
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
CODES.each do |color, code|
|
12
|
+
self.send(:define_method, color) do |string|
|
13
|
+
colorize(string, code)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# colorization
|
20
|
+
def colorize(string, color_code)
|
21
|
+
"\e[#{color_code}m#{string}\e[0m"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "test_launcher/shell/color"
|
2
|
+
require "test_launcher/utils/path"
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Shell
|
6
|
+
class Runner
|
7
|
+
include Color
|
8
|
+
|
9
|
+
CommandFailureError = Class.new(StandardError)
|
10
|
+
|
11
|
+
attr_accessor :working_directory, :log_path, :queue
|
12
|
+
private :working_directory, :log_path, :queue
|
13
|
+
|
14
|
+
def initialize(log_path:, working_directory:)
|
15
|
+
@working_directory = working_directory
|
16
|
+
@log_path = log_path
|
17
|
+
|
18
|
+
%x{echo "" > #{log_path}}
|
19
|
+
Dir.chdir(%x[ git rev-parse --show-toplevel ].chomp)
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(cmd, dir: working_directory, &block)
|
23
|
+
command = "cd #{Utils::Path.relative_join(dir)} && #{cmd}"
|
24
|
+
handle_output_for(command)
|
25
|
+
|
26
|
+
shell_out(command).split("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
def exec(cmd)
|
30
|
+
notify cmd
|
31
|
+
Kernel.exec cmd
|
32
|
+
end
|
33
|
+
|
34
|
+
def warn(msg)
|
35
|
+
log msg.to_s
|
36
|
+
print "#{red(msg.to_s)}\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify(msg)
|
40
|
+
log msg.to_s
|
41
|
+
print "#{yellow(msg.to_s)}\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def confirm?(question)
|
45
|
+
warn "#{question} [Yn]"
|
46
|
+
answer = STDIN.gets.strip.downcase
|
47
|
+
return answer != 'n'
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def log(msg)
|
53
|
+
%x{echo "#{msg.to_s}" >> #{log_path}}
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_output_for(cmd)
|
57
|
+
log(cmd)
|
58
|
+
end
|
59
|
+
|
60
|
+
def shell_out(command)
|
61
|
+
%x{ set -o pipefail && #{command} 2>> #{log_path} | tee -a #{log_path} }.chomp
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,85 @@
|
|
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
|
+
module TestLauncher
|
9
|
+
module Tests
|
10
|
+
module Minitest
|
11
|
+
class Consolidator < Struct.new(:search_results, :shell, :run_all)
|
12
|
+
include Utils::Pluralize
|
13
|
+
|
14
|
+
def self.consolidate(*args)
|
15
|
+
new(*args).consolidate
|
16
|
+
end
|
17
|
+
|
18
|
+
def consolidate
|
19
|
+
if search_results.empty?
|
20
|
+
shell.warn "Could not find any tests."
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
if methods_found? && one_result?
|
25
|
+
shell.notify "Found #{methods_count_phrase} in #{file_count_phrase}."
|
26
|
+
Wrappers::SingleTest.new(search_results.first)
|
27
|
+
elsif methods_found? && same_file?
|
28
|
+
shell.notify "Multiple test methods match in 1 file."
|
29
|
+
Wrappers::SingleFile.new(search_results.first[:file])
|
30
|
+
elsif methods_found? && run_last_edited?
|
31
|
+
shell.notify "Found #{methods_count_phrase} in #{file_count_phrase}."
|
32
|
+
shell.notify "Running most recently edited. Run with '--all' to run all the tests."
|
33
|
+
Wrappers::SingleTest.new(last_edited)
|
34
|
+
elsif files_found? && same_file?
|
35
|
+
shell.notify "Found #{file_count_phrase}."
|
36
|
+
Wrappers::SingleFile.new(search_results.first[:file])
|
37
|
+
elsif files_found? && run_last_edited?
|
38
|
+
shell.notify "Found #{file_count_phrase}."
|
39
|
+
shell.notify "Running most recently edited. Run with '--all' to run all the tests."
|
40
|
+
Wrappers::SingleFile.new(last_edited[:file])
|
41
|
+
else
|
42
|
+
shell.notify "Found #{file_count_phrase}."
|
43
|
+
Wrappers::MultipleFiles.wrap(search_results.map {|r| r[:file] }, shell)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def same_file?
|
48
|
+
file_count == 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def one_result?
|
52
|
+
same_file? && search_results.first[:line]
|
53
|
+
end
|
54
|
+
|
55
|
+
def methods_found?
|
56
|
+
!! search_results.first[:line]
|
57
|
+
end
|
58
|
+
|
59
|
+
def files_found?
|
60
|
+
! methods_found?
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_last_edited?
|
64
|
+
! run_all
|
65
|
+
end
|
66
|
+
|
67
|
+
def last_edited
|
68
|
+
search_results.sort_by {|r| File.mtime(r[:file])}.last
|
69
|
+
end
|
70
|
+
|
71
|
+
def file_count
|
72
|
+
search_results.group_by {|f| f[:file]}.size
|
73
|
+
end
|
74
|
+
|
75
|
+
def methods_count_phrase
|
76
|
+
pluralize(search_results.size, "test method")
|
77
|
+
end
|
78
|
+
|
79
|
+
def file_count_phrase
|
80
|
+
pluralize(file_count, "file")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "test_launcher/searchers/git_searcher"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Tests
|
5
|
+
module Minitest
|
6
|
+
class Finder < Struct.new(:query, :searcher)
|
7
|
+
|
8
|
+
def self.find(query, searcher)
|
9
|
+
new(query, searcher).find
|
10
|
+
end
|
11
|
+
|
12
|
+
def find
|
13
|
+
return tests_found_by_absolute_path if query.match(/^\//)
|
14
|
+
|
15
|
+
return tests_found_by_name unless tests_found_by_name.empty?
|
16
|
+
|
17
|
+
return tests_found_by_file_name unless tests_found_by_file_name.empty?
|
18
|
+
|
19
|
+
return tests_found_by_full_regex unless tests_found_by_full_regex.empty?
|
20
|
+
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def tests_found_by_absolute_path
|
27
|
+
relative_file_path = query.sub(Dir.pwd, '').sub(/^\//, '')
|
28
|
+
[ {file: relative_file_path} ]
|
29
|
+
end
|
30
|
+
|
31
|
+
def tests_found_by_name
|
32
|
+
@tests_found_by_name ||= full_regex_search("^\s*def .*#{query}.*")
|
33
|
+
end
|
34
|
+
|
35
|
+
def tests_found_by_file_name
|
36
|
+
@tests_found_by_file_name ||= searcher.find_files(query).select { |f| f.match(/_test\.rb/) }.map {|f| {file: f} }
|
37
|
+
end
|
38
|
+
|
39
|
+
def tests_found_by_full_regex
|
40
|
+
# we ignore the matched line since we don't know what to do with it
|
41
|
+
@tests_found_by_full_regex ||= full_regex_search(query).map {|t| {file: t[:file]} }
|
42
|
+
end
|
43
|
+
|
44
|
+
def full_regex_search(regex)
|
45
|
+
searcher.grep(regex, file_pattern: '*_test.rb')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_launcher/tests/minitest/wrappers/single_root"
|
2
|
+
require "test_launcher/tests/minitest/wrappers/multiple_roots"
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Tests
|
6
|
+
module Minitest
|
7
|
+
module Wrappers
|
8
|
+
module MultipleFiles
|
9
|
+
|
10
|
+
def self.wrap(files, shell)
|
11
|
+
wrapper = MultipleRoots.new(files, shell)
|
12
|
+
|
13
|
+
if wrapper.roots.size > 1
|
14
|
+
wrapper
|
15
|
+
else
|
16
|
+
wrapper.roots.first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_launcher/utils/path"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Tests
|
5
|
+
module Minitest
|
6
|
+
module Wrappers
|
7
|
+
class MultipleRoots
|
8
|
+
|
9
|
+
attr_reader :roots, :shell
|
10
|
+
|
11
|
+
def initialize(files, shell)
|
12
|
+
@shell = shell
|
13
|
+
@roots = files.map {|f| SingleFile.new(f)}.group_by {|f| f.app_root}.map {|root, _files| SingleRoot.new(_files, shell)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_command
|
17
|
+
roots.map(&:to_command).join("; cd -;\n\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "test_launcher/utils/path"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Tests
|
5
|
+
module Minitest
|
6
|
+
module Wrappers
|
7
|
+
class SingleFile < Struct.new(:file)
|
8
|
+
|
9
|
+
def to_command
|
10
|
+
%{cd #{File.join(app_root)} && ruby -I test #{File.join(relative_test_path)}}
|
11
|
+
end
|
12
|
+
|
13
|
+
def app_root
|
14
|
+
exploded_path = Utils::Path.split(file)
|
15
|
+
|
16
|
+
path = exploded_path[0...exploded_path.rindex("test")]
|
17
|
+
File.join(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def relative_test_path
|
21
|
+
exploded_path = Utils::Path.split(file)
|
22
|
+
path = exploded_path[exploded_path.rindex("test")..-1]
|
23
|
+
File.join(path)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test_launcher/utils/path"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Tests
|
5
|
+
module Minitest
|
6
|
+
module Wrappers
|
7
|
+
class SingleRoot
|
8
|
+
|
9
|
+
attr_reader :files, :shell
|
10
|
+
|
11
|
+
def initialize(files, shell)
|
12
|
+
@shell = shell
|
13
|
+
@files = files.map {|f| f.is_a?(SingleFile) ? f : SingleFile.new(f)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_command
|
17
|
+
%{cd #{app_root} && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + "/" + file) }' #{files.map(&:relative_test_path).join(" ")}}
|
18
|
+
end
|
19
|
+
|
20
|
+
def app_root
|
21
|
+
files.first.app_root
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "test_launcher/utils/path"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Tests
|
5
|
+
module Minitest
|
6
|
+
module Wrappers
|
7
|
+
class SingleTest
|
8
|
+
|
9
|
+
attr_reader :file, :name
|
10
|
+
private :file, :name
|
11
|
+
|
12
|
+
def initialize(file:, line:)
|
13
|
+
@file = file
|
14
|
+
@name = line[/\s*def\s+(.*)/, 1]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_command
|
18
|
+
%{cd #{app_root} && ruby -I test #{relative_test_path} --name=/#{name}/}
|
19
|
+
end
|
20
|
+
|
21
|
+
def app_root
|
22
|
+
exploded_path = Utils::Path.split(file)
|
23
|
+
|
24
|
+
path = exploded_path[0...exploded_path.rindex("test")]
|
25
|
+
File.join(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def relative_test_path
|
29
|
+
exploded_path = Utils::Path.split(file)
|
30
|
+
path = exploded_path[exploded_path.rindex("test")..-1]
|
31
|
+
File.join(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module TestLauncher
|
4
|
+
module Utils
|
5
|
+
module Path
|
6
|
+
|
7
|
+
def self.split(filename)
|
8
|
+
self.prepend_pwd(Pathname.new(filename).each_filename.to_a)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.path_for(filename)
|
12
|
+
self.split(File.split(file)[0])
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.join(*array)
|
16
|
+
File.join(*array)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.prepend_pwd(dirs)
|
20
|
+
if dirs[0] == "."
|
21
|
+
dirs
|
22
|
+
else
|
23
|
+
["."] + dirs
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.relative_join(array)
|
28
|
+
join(prepend_pwd(array))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.split(File.dirname(__FILE__))[0], 'lib'))
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "mocha/mini_test"
|
4
|
+
|
5
|
+
class TestCase < Minitest::Test
|
6
|
+
class DummyShell
|
7
|
+
|
8
|
+
def method_missing(method, *args)
|
9
|
+
instance_variable_set(:"@#{method}", [args])
|
10
|
+
|
11
|
+
self.class.send(:define_method, method) do |*a|
|
12
|
+
if ! instance_variable_get(:"@#{method}")
|
13
|
+
instance_variable_set(:"@#{method}", [a])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def recall(method)
|
19
|
+
instance_variable_get(:"@#{method}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def assert_notified(string)
|
24
|
+
unless dummy_shell.recall(:notify).flatten.include?(string)
|
25
|
+
flunk <<-FLUNK
|
26
|
+
could not find: "#{string}"
|
27
|
+
shell.notify called with:
|
28
|
+
#{dummy_shell.recall(:notify)}
|
29
|
+
FLUNK
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def assert_paragraphs_equal(expected, actual)
|
34
|
+
expecteds = expected.split("\n").map {|l| l.to_s.strip}
|
35
|
+
actuals = actual.split("\n").map {|l| l.to_s.strip}
|
36
|
+
|
37
|
+
assert_equal expecteds, actuals
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def dummy_shell
|
43
|
+
@dummy_shell ||= DummyShell.new
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "test_launcher/searchers/git_searcher"
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Searchers
|
6
|
+
class MockShell
|
7
|
+
def run(cmd)
|
8
|
+
if cmd == "git ls-files '*file_pattern*'"
|
9
|
+
[
|
10
|
+
"file/path/one.rb",
|
11
|
+
"another_file/path/two.rb",
|
12
|
+
]
|
13
|
+
elsif cmd == "git grep --untracked 'regex' -- 'file_pattern'"
|
14
|
+
[
|
15
|
+
"file/path/one.rb: some_lines(of_code)",
|
16
|
+
"another_file/path/two.rb: Class::Thing::Stuff.new",
|
17
|
+
]
|
18
|
+
else
|
19
|
+
raise ArgumentError.new("unmocked command")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class GitSearcherTest < TestCase
|
25
|
+
|
26
|
+
def test_find_files
|
27
|
+
searcher = GitSearcher.new(MockShell.new)
|
28
|
+
|
29
|
+
assert_equal ["file/path/one.rb", "another_file/path/two.rb"], searcher.find_files("file_pattern")
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_grep
|
33
|
+
searcher = GitSearcher.new(MockShell.new)
|
34
|
+
|
35
|
+
expected = [
|
36
|
+
{
|
37
|
+
file: "file/path/one.rb",
|
38
|
+
line: "some_lines(of_code)",
|
39
|
+
},
|
40
|
+
{
|
41
|
+
file: "another_file/path/two.rb",
|
42
|
+
line: "Class::Thing::Stuff.new",
|
43
|
+
}
|
44
|
+
]
|
45
|
+
|
46
|
+
assert_equal expected, searcher.grep("regex", file_pattern: "file_pattern")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "test_launcher/tests/minitest/consolidator"
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Tests
|
6
|
+
module Minitest
|
7
|
+
class ConsolidatorIntegrationTest < TestCase
|
8
|
+
|
9
|
+
def test_single_test_method__no_sub_dir
|
10
|
+
search_results = [
|
11
|
+
{
|
12
|
+
file: "test/dir/file_test.rb",
|
13
|
+
line: "def test_launcher",
|
14
|
+
},
|
15
|
+
]
|
16
|
+
|
17
|
+
consolidator = Consolidator.new(search_results, dummy_shell, false)
|
18
|
+
assert_equal "cd . && ruby -I test test/dir/file_test.rb --name=/test_launcher/", consolidator.consolidate.to_command
|
19
|
+
assert_notified "Found 1 test method in 1 file."
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_single_test_method__with_sub_dir
|
23
|
+
search_results = [
|
24
|
+
{
|
25
|
+
file: "engines/lawnmower/test/dir/file_test.rb",
|
26
|
+
line: "def test_launcher",
|
27
|
+
}
|
28
|
+
]
|
29
|
+
|
30
|
+
consolidator = Consolidator.new(search_results, dummy_shell, false)
|
31
|
+
assert_equal "cd ./engines/lawnmower && ruby -I test test/dir/file_test.rb --name=/test_launcher/", consolidator.consolidate.to_command
|
32
|
+
assert_notified "Found 1 test method in 1 file."
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_multiple_test_methods_in_same_file
|
36
|
+
search_results = [
|
37
|
+
{
|
38
|
+
file: "test/dir/file_test.rb",
|
39
|
+
line: "def test_launcher",
|
40
|
+
},
|
41
|
+
{
|
42
|
+
file: "test/dir/file_test.rb",
|
43
|
+
line: "def test_launcher_again",
|
44
|
+
},
|
45
|
+
{
|
46
|
+
file: "test/dir/file_test.rb",
|
47
|
+
line: "def test_launcher_some_more",
|
48
|
+
},
|
49
|
+
]
|
50
|
+
|
51
|
+
consolidator = Consolidator.new(search_results, dummy_shell, false)
|
52
|
+
|
53
|
+
assert_equal "cd . && ruby -I test test/dir/file_test.rb --name=/test_launcher/", consolidator.consolidate.to_command
|
54
|
+
assert_notified "Found 3 test methods in 1 file."
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_multiple_test_methods_in_different_files__last_edited
|
58
|
+
search_results = [
|
59
|
+
{
|
60
|
+
file: "test/dir/file_test.rb",
|
61
|
+
line: "def test_launcher",
|
62
|
+
},
|
63
|
+
{
|
64
|
+
file: "test/other_dir/different_test.rb",
|
65
|
+
line: "def test_launcher",
|
66
|
+
},
|
67
|
+
]
|
68
|
+
|
69
|
+
consolidator = Consolidator.new(search_results, dummy_shell, false)
|
70
|
+
|
71
|
+
File.expects(:mtime).with("test/dir/file_test.rb").returns(Time.new(2014, 01, 01))
|
72
|
+
File.expects(:mtime).with("test/other_dir/different_test.rb").returns(Time.new(2013, 01, 01))
|
73
|
+
|
74
|
+
assert_equal "cd . && ruby -I test test/dir/file_test.rb --name=/test_launcher/", consolidator.consolidate.to_command
|
75
|
+
assert_notified "Found 2 test methods in 2 files."
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_multiple_test_methods_in_different_files__run_all__same_root
|
79
|
+
search_results = [
|
80
|
+
{
|
81
|
+
file: "test/dir/file_test.rb",
|
82
|
+
line: "def test_launcher",
|
83
|
+
},
|
84
|
+
{
|
85
|
+
file: "test/other_dir/different_test.rb",
|
86
|
+
line: "def test_launcher",
|
87
|
+
},
|
88
|
+
]
|
89
|
+
|
90
|
+
consolidator = Consolidator.new(search_results, dummy_shell, true)
|
91
|
+
|
92
|
+
assert_equal %{cd . && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + "/" + file) }' test/dir/file_test.rb test/other_dir/different_test.rb}, consolidator.consolidate.to_command
|
93
|
+
assert_notified "Found 2 files."
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_multiple_test_methods_in_different_files__run_all__different_roots
|
97
|
+
search_results = [
|
98
|
+
{
|
99
|
+
file: "engine1/test/dir/file_test.rb",
|
100
|
+
line: "def test_launcher",
|
101
|
+
},
|
102
|
+
{
|
103
|
+
file: "engine2/root2/test/other_dir/different_test.rb",
|
104
|
+
line: "def test_launcher",
|
105
|
+
},
|
106
|
+
]
|
107
|
+
|
108
|
+
consolidator = Consolidator.new(search_results, dummy_shell, true)
|
109
|
+
expected = <<-SHELL
|
110
|
+
cd ./engine1 && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + \"/\" + file) }' test/dir/file_test.rb; cd -;
|
111
|
+
|
112
|
+
cd ./engine2/root2 && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + \"/\" + file) }' test/other_dir/different_test.rb
|
113
|
+
SHELL
|
114
|
+
|
115
|
+
assert_paragraphs_equal expected, consolidator.consolidate.to_command
|
116
|
+
assert_notified "Found 2 files."
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_one_file_found
|
120
|
+
search_results = [
|
121
|
+
{
|
122
|
+
file: "engine1/test/dir/file_test.rb",
|
123
|
+
},
|
124
|
+
]
|
125
|
+
|
126
|
+
consolidator = Consolidator.new(search_results, dummy_shell, true)
|
127
|
+
|
128
|
+
assert_equal "cd ./engine1 && ruby -I test test/dir/file_test.rb", consolidator.consolidate.to_command
|
129
|
+
assert_notified "Found 1 file."
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_multiple_files__last_edited
|
133
|
+
search_results = [
|
134
|
+
{
|
135
|
+
file: "test/dir/file_test.rb",
|
136
|
+
},
|
137
|
+
{
|
138
|
+
file: "test/other_dir/different_test.rb",
|
139
|
+
},
|
140
|
+
]
|
141
|
+
|
142
|
+
consolidator = Consolidator.new(search_results, dummy_shell, false)
|
143
|
+
|
144
|
+
File.expects(:mtime).with("test/dir/file_test.rb").returns(Time.new(2014, 01, 01))
|
145
|
+
File.expects(:mtime).with("test/other_dir/different_test.rb").returns(Time.new(2013, 01, 01))
|
146
|
+
|
147
|
+
assert_equal "cd . && ruby -I test test/dir/file_test.rb", consolidator.consolidate.to_command
|
148
|
+
assert_notified "Found 2 files."
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_multiple_files__run_all__same_root
|
152
|
+
search_results = [
|
153
|
+
{
|
154
|
+
file: "engine1/test/dir/file_test.rb",
|
155
|
+
},
|
156
|
+
{
|
157
|
+
file: "engine1/test/other_dir/different_test.rb",
|
158
|
+
},
|
159
|
+
]
|
160
|
+
|
161
|
+
consolidator = Consolidator.new(search_results, dummy_shell, true)
|
162
|
+
expected = %{cd ./engine1 && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + \"/\" + file) }' test/dir/file_test.rb test/other_dir/different_test.rb}
|
163
|
+
|
164
|
+
assert_equal expected, consolidator.consolidate.to_command
|
165
|
+
assert_notified "Found 2 files."
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_multiple_files__run_all__different_roots
|
169
|
+
search_results = [
|
170
|
+
{
|
171
|
+
file: "engine1/test/dir/file_test.rb",
|
172
|
+
},
|
173
|
+
{
|
174
|
+
file: "engine2/root2/test/other_dir/different_test.rb",
|
175
|
+
},
|
176
|
+
]
|
177
|
+
|
178
|
+
consolidator = Consolidator.new(search_results, dummy_shell, true)
|
179
|
+
expected = <<-SHELL
|
180
|
+
cd ./engine1 && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + \"/\" + file) }' test/dir/file_test.rb; cd -;
|
181
|
+
|
182
|
+
cd ./engine2/root2 && ruby -I test -e 'ARGV.each { |file| require(Dir.pwd + \"/\" + file) }' test/other_dir/different_test.rb
|
183
|
+
SHELL
|
184
|
+
|
185
|
+
assert_paragraphs_equal expected, consolidator.consolidate.to_command
|
186
|
+
assert_notified "Found 2 files."
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "test_launcher/tests/minitest/finder"
|
3
|
+
|
4
|
+
module TestLauncher
|
5
|
+
module Tests
|
6
|
+
module Minitest
|
7
|
+
class FinderTest < TestCase
|
8
|
+
|
9
|
+
def test_find__absolute_path
|
10
|
+
Dir.expects(:pwd).returns("/absolute/path/root")
|
11
|
+
results = Finder.find("/absolute/path/root/test/dir/file_test.rb", "searcher_stub")
|
12
|
+
assert_equal [{file: "test/dir/file_test.rb"}], results
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_find__found_by_name
|
16
|
+
expected = [{file: "test/file.rb", line: "def test_name"}]
|
17
|
+
searcher_mock = mock do
|
18
|
+
stubs(:grep).with("^\s*def .*test_name.*", file_pattern: "*_test.rb").returns(expected)
|
19
|
+
end
|
20
|
+
|
21
|
+
assert_equal expected, Finder.find("test_name", searcher_mock)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_find__found_by_file_name
|
25
|
+
searcher_mock = mock do
|
26
|
+
stubs(:grep).returns([])
|
27
|
+
stubs(:find_files).with("file_query").returns([
|
28
|
+
"dir/test/non_test_file_query.rb",
|
29
|
+
"dir/thing/file_query_test.rb",
|
30
|
+
"other_dir/other_thing/other_file_query_test.rb",
|
31
|
+
])
|
32
|
+
end
|
33
|
+
|
34
|
+
expected = [
|
35
|
+
{ file: "dir/thing/file_query_test.rb" },
|
36
|
+
{ file: "other_dir/other_thing/other_file_query_test.rb" },
|
37
|
+
]
|
38
|
+
|
39
|
+
assert_equal expected, Finder.find("file_query", searcher_mock)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_find__found_by_full_regex
|
43
|
+
searcher_mock = mock do
|
44
|
+
stubs(:grep).returns([])
|
45
|
+
stubs(:find_files).returns([])
|
46
|
+
|
47
|
+
stubs(:grep).with("full_regex_search", file_pattern: "*_test.rb").returns([
|
48
|
+
{ file: "path/to/file_test.rb", line: "random_match"},
|
49
|
+
{ file: "path/to/other_file_test.rb", line: "random_match_2"}
|
50
|
+
])
|
51
|
+
end
|
52
|
+
|
53
|
+
expected = [
|
54
|
+
{file: "path/to/file_test.rb"},
|
55
|
+
{file: "path/to/other_file_test.rb"}
|
56
|
+
]
|
57
|
+
assert_equal expected, Finder.find("full_regex_search", searcher_mock)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_find__nothing_found
|
61
|
+
searcher_mock = mock do
|
62
|
+
stubs(:grep).returns([])
|
63
|
+
stubs(:find_files).returns([])
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_equal [], Finder.find("query", searcher_mock)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'test_launcher/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "test_launcher"
|
8
|
+
spec.version = TestLauncher::VERSION
|
9
|
+
spec.authors = ["Pete Kinnecom"]
|
10
|
+
spec.email = ["pete.kinnecom@appfolio.com"]
|
11
|
+
spec.summary = %q{Easily run tests}
|
12
|
+
spec.description = %q{no really}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: test_launcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pete Kinnecom
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: no really
|
42
|
+
email:
|
43
|
+
- pete.kinnecom@appfolio.com
|
44
|
+
executables:
|
45
|
+
- test_launcher
|
46
|
+
- test_runner
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- ".gitignore"
|
51
|
+
- Gemfile
|
52
|
+
- LICENSE.txt
|
53
|
+
- README.md
|
54
|
+
- Rakefile
|
55
|
+
- bin/test_launcher
|
56
|
+
- bin/test_runner
|
57
|
+
- lib/test_launcher.rb
|
58
|
+
- lib/test_launcher/searchers/git_searcher.rb
|
59
|
+
- lib/test_launcher/shell/color.rb
|
60
|
+
- lib/test_launcher/shell/runner.rb
|
61
|
+
- lib/test_launcher/tests/minitest/consolidator.rb
|
62
|
+
- lib/test_launcher/tests/minitest/finder.rb
|
63
|
+
- lib/test_launcher/tests/minitest/wrappers/multiple_files.rb
|
64
|
+
- lib/test_launcher/tests/minitest/wrappers/multiple_roots.rb
|
65
|
+
- lib/test_launcher/tests/minitest/wrappers/single_file.rb
|
66
|
+
- lib/test_launcher/tests/minitest/wrappers/single_root.rb
|
67
|
+
- lib/test_launcher/tests/minitest/wrappers/single_test.rb
|
68
|
+
- lib/test_launcher/utils/path.rb
|
69
|
+
- lib/test_launcher/utils/pluralize.rb
|
70
|
+
- lib/test_launcher/version.rb
|
71
|
+
- test/test_helper.rb
|
72
|
+
- test/test_launcher/searchers/git_searcher_test.rb
|
73
|
+
- test/test_launcher/tests/minitest/consolidator_integration_test.rb
|
74
|
+
- test/test_launcher/tests/minitest/finder_test.rb
|
75
|
+
- test_launcher.gemspec
|
76
|
+
homepage: ''
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.3
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Easily run tests
|
100
|
+
test_files:
|
101
|
+
- test/test_helper.rb
|
102
|
+
- test/test_launcher/searchers/git_searcher_test.rb
|
103
|
+
- test/test_launcher/tests/minitest/consolidator_integration_test.rb
|
104
|
+
- test/test_launcher/tests/minitest/finder_test.rb
|
105
|
+
has_rdoc:
|