what_to_run 0.1.0 → 1.0.2
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 +5 -13
- data/README.md +38 -10
- data/bin/what_to_run +2 -1
- data/lib/what_to_run.rb +40 -68
- data/lib/what_to_run/cli.rb +52 -0
- data/lib/what_to_run/differ.rb +77 -0
- data/lib/what_to_run/minitest.rb +19 -17
- data/lib/what_to_run/minitest/runner.rb +28 -0
- data/lib/what_to_run/rspec.rb +22 -16
- data/lib/what_to_run/rspec/runner.rb +21 -0
- data/lib/what_to_run/runner.rb +35 -0
- data/lib/what_to_run/tracker.rb +74 -0
- data/lib/what_to_run/version.rb +3 -0
- metadata +39 -12
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
MmE2Yzc1YWVhMjA3NDliZjk4YTQ5NzdlNjljNTg4NzZjMjBjODM1Mg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5628bef322e5ffe04df75062217228f1c462251a
|
4
|
+
data.tar.gz: a3a0beabaabfb83223a0b588acb713a0e56a682f
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NGNjYTRhNTg5MjRkMTdhY2NhNWNhYjBhZDE4M2VjYjEwOTI1MTJhNWFiNWRh
|
11
|
-
NDAxNjFiMjc1NDUyYTI0YTllYzBlNTk0NmQ2MWQ2MWYyM2NiMmQ=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZGZmMzBiYjZkZmUzMzcxNDZhZWU5MjgxZTAwMDQwMjAwOGY0MTQwZTliMDdm
|
14
|
-
MmNjNjczNTdlN2YzNjg5YWUyZWFlYzkwZTlhMTIwMWY0YWQ1MDk1MjRhNjY0
|
15
|
-
MmIxYzM3M2NjYWYxYTM3OTM4ZTgzOTNhOGRlOWRkY2JhYjQzNjI=
|
6
|
+
metadata.gz: bbb66e545238e59c45d64b927b518a9b1b25bfb6cd7e24c17125ac5e00b32f3ead0bbf2346052cbeb7f5be69b10687e2106153303c5377ad7f73924d3a6a38f8
|
7
|
+
data.tar.gz: ff854a098d8f76780a4c7977cdb17ad57d3adfc1a1a1253910e4fca1a3bd2b71b3a2c3864c8145ef4a384830370900cda21ebb8e63b29ff006cc4aace266e702
|
data/README.md
CHANGED
@@ -1,14 +1,30 @@
|
|
1
1
|
# What To Run
|
2
2
|
|
3
|
+
[](https://gitter.im/DyegoCosta/what_to_run?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
|
3
5
|
[](https://travis-ci.org/DyegoCosta/what_to_run)
|
4
6
|
|
5
|
-
What To Run is a lib for regression test selection, use it to predict which tests you should run when you make any modification on your codebase.
|
7
|
+
What To Run is a lib for regression test selection for Ruby projects, use it to predict which tests you should run when you make any modification on your codebase.
|
8
|
+
|
9
|
+
This lib was inspired by [@tenderlove](https://github.com/tenderlove) [blog post](tenderlove-post). Make sure to check it out.
|
10
|
+
|
6
11
|
|
7
|
-
|
12
|
+
From the _[An Empirical Study of Regression Test Selection Techniques](rts-article)_ article:
|
13
|
+
|
14
|
+
> Regression testing is the process of validating modified software to detect whether new errors
|
15
|
+
have been introduced into previously tested code and to provide confidence that modifications
|
16
|
+
are correct. Since regression testing is an expensive process, researchers have proposed
|
17
|
+
regression test selection techniques as a way to reduce some of this expense. These techniques
|
18
|
+
attempt to reduce costs by selecting and running only a subset of the test cases in a program’s
|
19
|
+
existing test suite.
|
20
|
+
|
21
|
+
[rts-article]: https://www.cs.umd.edu/~aporter/Docs/p184-graves.pdf
|
22
|
+
[tenderlove-post]: http://tenderlovemaking.com/2015/02/13/predicting-test-failues.html
|
8
23
|
|
9
24
|
## Requirements
|
10
25
|
|
11
26
|
- Project must be inside a Git repository
|
27
|
+
- CMake to build the gem
|
12
28
|
|
13
29
|
## Installation
|
14
30
|
|
@@ -27,12 +43,12 @@ $ bundle
|
|
27
43
|
Or install it yourself as:
|
28
44
|
|
29
45
|
```
|
30
|
-
gem install what_to_run
|
46
|
+
$ gem install what_to_run
|
31
47
|
```
|
32
48
|
|
33
49
|
## Usage
|
34
50
|
|
35
|
-
Require
|
51
|
+
Require it after requiring your test framework and before load your files to be tested and your test suite config:
|
36
52
|
|
37
53
|
Minitest
|
38
54
|
|
@@ -46,29 +62,41 @@ RSpec
|
|
46
62
|
require 'what_to_run/rspec'
|
47
63
|
```
|
48
64
|
|
49
|
-
Run your tests on a clean git branch
|
65
|
+
Run your full tests suite with COLLECT=1 on a **clean git branch**
|
50
66
|
|
51
67
|
Minitest
|
52
68
|
|
53
69
|
```
|
54
|
-
$
|
70
|
+
$ COLLECT=1 bundle exec rake test
|
55
71
|
```
|
56
72
|
|
57
73
|
RSpec
|
58
74
|
|
59
75
|
```
|
60
|
-
$
|
76
|
+
$ COLLECT=1 bundle exec rspec
|
61
77
|
```
|
62
78
|
|
63
79
|
This will create the initial coverage information. Then make your desired modifications on your code.
|
64
80
|
|
65
|
-
Now to
|
81
|
+
Now to run the tests that could reveal faults do the following
|
82
|
+
|
83
|
+
```
|
84
|
+
$ what_to_run <framework> [options]
|
85
|
+
```
|
86
|
+
|
87
|
+
Supported frameworks are:
|
66
88
|
|
67
89
|
```
|
68
|
-
|
90
|
+
rspec
|
91
|
+
minitest
|
69
92
|
```
|
70
93
|
|
71
|
-
:
|
94
|
+
Options are:
|
95
|
+
|
96
|
+
```
|
97
|
+
-e, --exec EXECUTABLE Alternate test runner executable
|
98
|
+
-h, --help Prints this help
|
99
|
+
```
|
72
100
|
|
73
101
|
## Contributing
|
74
102
|
|
data/bin/what_to_run
CHANGED
data/lib/what_to_run.rb
CHANGED
@@ -1,91 +1,63 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'rugged'
|
3
1
|
require 'set'
|
2
|
+
require 'rugged'
|
3
|
+
|
4
|
+
require_relative 'what_to_run/tracker'
|
4
5
|
|
5
6
|
module WhatToRun
|
6
|
-
|
7
|
+
autoload :CLI, 'what_to_run/cli'
|
8
|
+
autoload :VERSION, 'what_to_run/version'
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
class << self
|
11
|
+
def predict
|
12
|
+
lines_to_run.inject(Set.new) do |tests, (file, line)|
|
13
|
+
tests += Array cov_map[file][line]
|
14
|
+
end
|
12
15
|
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def lines_to_run
|
16
|
-
repo = Rugged::Repository.new('.')
|
17
|
-
lines_to_run = Set.new
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
17
|
+
def lines_to_run
|
18
|
+
repository = Rugged::Repository.discover('.')
|
19
|
+
repository_root = File.expand_path("..", repository.path)
|
20
|
+
lines_to_run = Set.new
|
21
|
+
|
22
|
+
repository.index.diff.each_patch do |patch|
|
23
|
+
file = patch.delta.old_file[:path]
|
24
|
+
file_path = File.join(repository_root, file)
|
25
|
+
|
26
|
+
patch.each_hunk do |hunk|
|
27
|
+
hunk.each_line do |line|
|
28
|
+
case line.line_origin
|
29
|
+
when :addition
|
30
|
+
lines_to_run << [file_path, line.new_lineno]
|
31
|
+
when :deletion
|
32
|
+
lines_to_run << [file_path, line.old_lineno]
|
33
|
+
when :context
|
34
|
+
# do nothing
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
34
|
-
end
|
35
|
-
|
36
|
-
lines_to_run
|
37
|
-
end
|
38
|
-
|
39
|
-
def cov_delta(before, after)
|
40
|
-
after.each_with_object({}) do |(file_name, lines_cov), delta|
|
41
|
-
before_lines_cov = before[file_name]
|
42
|
-
|
43
|
-
# skip arrays that are exactly the same
|
44
|
-
next if before_lines_cov == lines_cov
|
45
39
|
|
46
|
-
|
47
|
-
cov = lines_cov_delta(before_lines_cov, lines_cov)
|
48
|
-
|
49
|
-
# add the "diffed" coverage to the hash
|
50
|
-
delta[file_name] = cov
|
40
|
+
lines_to_run
|
51
41
|
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def cov_map
|
55
|
-
cov_map = Hash.new { |h, file| h[file] = Hash.new { |i, line| i[line] = [] } }
|
56
42
|
|
57
|
-
|
58
|
-
|
59
|
-
before, after = cov_info.last(2)
|
60
|
-
desc = build_test_desc(cov_info)
|
43
|
+
def cov_map
|
44
|
+
schema = Hash.new { |h, file| h[file] = Hash.new { |i, line| i[line] = [] } }
|
61
45
|
|
62
|
-
|
63
|
-
|
64
|
-
delta.each_pair do |file, lines|
|
46
|
+
@cov_map ||= Tracker.read.inject(schema) do |cov_map, (desc, cov_delta)|
|
47
|
+
cov_delta.each_pair do |file, lines|
|
65
48
|
file_map = cov_map[file]
|
66
49
|
|
67
|
-
lines.each_with_index do |
|
68
|
-
file_map[i + 1] << desc if line_executed?(
|
50
|
+
lines.each_with_index do |line, i|
|
51
|
+
file_map[i + 1] << desc if line_executed?(line)
|
69
52
|
end
|
70
53
|
end
|
54
|
+
|
55
|
+
cov_map
|
71
56
|
end
|
72
57
|
end
|
73
58
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
def build_test_desc(cov_info)
|
78
|
-
using_minitest = cov_info.length == 4
|
79
|
-
using_minitest ? cov_info.first(2).join('#') : cov_info.first
|
80
|
-
end
|
81
|
-
|
82
|
-
def line_executed?(line)
|
83
|
-
line.to_i > 0
|
84
|
-
end
|
85
|
-
|
86
|
-
def lines_cov_delta(before_lines_cov, after_lines_cov)
|
87
|
-
after_lines_cov.zip(before_lines_cov).map do |lines_after, lines_before|
|
88
|
-
lines_after ? lines_after - lines_before : lines_after
|
59
|
+
def line_executed?(line)
|
60
|
+
line.to_i > 0
|
89
61
|
end
|
90
62
|
end
|
91
63
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module WhatToRun
|
5
|
+
class CLI
|
6
|
+
def run(argv)
|
7
|
+
framework = Array(argv)[0]
|
8
|
+
|
9
|
+
abort 'Must specify a test framework' unless framework
|
10
|
+
|
11
|
+
options = parse_options!(argv)
|
12
|
+
|
13
|
+
begin
|
14
|
+
runner = load_runner framework.downcase
|
15
|
+
runner.new(options).run
|
16
|
+
rescue LoadError
|
17
|
+
abort "Unsupported test framework: #{framework}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def parse_options!(argv)
|
24
|
+
options = {}
|
25
|
+
|
26
|
+
parser = OptionParser.new do |opts|
|
27
|
+
opts.banner = 'Usage: what_to_run <framework> [options]'
|
28
|
+
|
29
|
+
opts.on('-e', '--exec EXECUTABLE', 'Alternate test runner executable') do |e|
|
30
|
+
options[:exec] = e
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-h', '--help', 'Prints this help') do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
parser.parse!(argv)
|
40
|
+
|
41
|
+
options
|
42
|
+
end
|
43
|
+
|
44
|
+
def load_runner(framework)
|
45
|
+
require "what_to_run/#{framework}/runner"
|
46
|
+
klass_name = "WhatToRun::#{RUNNERS[framework]}::Runner"
|
47
|
+
klass_name.split('::').inject(Object) {|x, y| x.const_get(y.to_sym)}
|
48
|
+
end
|
49
|
+
|
50
|
+
RUNNERS = {'rspec' => 'RSpec', 'minitest' => 'Minitest'}
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module WhatToRun
|
2
|
+
class Differ
|
3
|
+
class << self
|
4
|
+
##
|
5
|
+
# Gives the delta beteween the coverage result
|
6
|
+
# before and after a test run and before starting
|
7
|
+
# the test suite
|
8
|
+
#
|
9
|
+
# Results in the lines that may trigger the test
|
10
|
+
# that gave the after result
|
11
|
+
def coverage_delta(cov_before, cov_after, cov_before_suite)
|
12
|
+
cov_after.each_with_object({}) do |(file_name, lines_cov_after), delta|
|
13
|
+
lines_cov_before = cov_before[file_name]
|
14
|
+
lines_cov_before_suite = cov_before_suite[file_name]
|
15
|
+
|
16
|
+
next unless file_covered?(lines_cov_before, lines_cov_after)
|
17
|
+
|
18
|
+
lines_delta = lines_cov_delta \
|
19
|
+
lines_cov_before_suite, lines_cov_before, lines_cov_after
|
20
|
+
|
21
|
+
delta[file_name] = lines_delta
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# It needs to diff the diff of before and after
|
27
|
+
# with the coverage before the test suite in order
|
28
|
+
# to include the uncovered lines to all tests that
|
29
|
+
# touch this file
|
30
|
+
def lines_cov_delta(before_suite, before, after)
|
31
|
+
delta = diff before_suite, diff(before, after)
|
32
|
+
delta.map(&method(:normalize_cov_result))
|
33
|
+
end
|
34
|
+
|
35
|
+
def diff(before, after)
|
36
|
+
after = Array(after)
|
37
|
+
before = Array(before)
|
38
|
+
|
39
|
+
after.zip(before).map do |lines_after, lines_before|
|
40
|
+
lines_after ? lines_after - lines_before.to_i : lines_after
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# The possible param value that this method might receive
|
46
|
+
# are the following
|
47
|
+
#
|
48
|
+
# @param result positive => should run the test
|
49
|
+
# @param result negative => should run the test
|
50
|
+
# @param result nil => should run the test
|
51
|
+
# @param result zero => should not run the test
|
52
|
+
#
|
53
|
+
# This method will convert negative and nil values to 1,
|
54
|
+
# which will make them represent lines that should be run.
|
55
|
+
# The positive lines can be kept as they are since this mean
|
56
|
+
# they will be run.
|
57
|
+
#
|
58
|
+
# The only exception case is the 0 result which will be kept
|
59
|
+
# as it is so we don't run lines within the not called methods
|
60
|
+
#
|
61
|
+
# This introduces false positives to avoid missing tests that
|
62
|
+
# depends on lines that are evaluated when the file is required
|
63
|
+
#
|
64
|
+
# @return 1 to represent that this line should trigger the current test
|
65
|
+
# @return 0 to represent that this line should not trigger the current test
|
66
|
+
def normalize_cov_result(result)
|
67
|
+
result.nil? || result.to_i < 0 ? 1 : result
|
68
|
+
end
|
69
|
+
|
70
|
+
def file_covered?(cov_before, cov_after)
|
71
|
+
cov_before != cov_after
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private_class_method :lines_cov_delta, :file_covered?
|
76
|
+
end
|
77
|
+
end
|
data/lib/what_to_run/minitest.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
|
-
|
2
|
-
require 'coverage'
|
3
|
-
require 'coverage_peeker'
|
1
|
+
configure = -> do
|
2
|
+
require 'coverage'
|
3
|
+
require 'coverage_peeker'
|
4
|
+
require 'what_to_run/tracker'
|
4
5
|
|
5
|
-
Coverage.start
|
6
|
+
Coverage.start
|
6
7
|
|
7
|
-
require 'minitest'
|
8
|
+
require 'minitest'
|
8
9
|
|
9
|
-
|
10
|
-
LOGS = []
|
10
|
+
WhatToRun::Tracker.start
|
11
11
|
|
12
|
-
Minitest
|
13
|
-
|
14
|
-
}
|
12
|
+
class Minitest::Runnable
|
13
|
+
Minitest.after_run {WhatToRun::Tracker.finish}
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
class << self
|
16
|
+
alias :old_run_one_method :run_one_method
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
def run_one_method(klass, method_name, reporter)
|
19
|
+
before = CoveragePeeker.peek_result
|
20
|
+
old_run_one_method klass, method_name, reporter
|
21
|
+
after = CoveragePeeker.peek_result
|
22
|
+
WhatToRun::Tracker.track "#{klass.name}##{method_name}", before, after
|
23
|
+
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
+
|
28
|
+
configure.call if ENV['COLLECT'] && ENV['COLLECT'] != 'false'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'what_to_run/runner'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module WhatToRun
|
5
|
+
module Minitest
|
6
|
+
class Runner < WhatToRun::Runner
|
7
|
+
DEFAULT_EXECUTABLE = 'bundle exec rake test'.freeze
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
super({exec: DEFAULT_EXECUTABLE}.merge(opts))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def predicted_example_args
|
16
|
+
patterns = predicted_examples.flat_map do |example|
|
17
|
+
"^#{example}$"
|
18
|
+
end
|
19
|
+
|
20
|
+
examples = patterns.join('|')
|
21
|
+
|
22
|
+
name_arg = Shellwords.escape("--name=/#{examples}/")
|
23
|
+
|
24
|
+
"TESTOPTS=\"#{name_arg}\""
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/what_to_run/rspec.rb
CHANGED
@@ -1,21 +1,27 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
configure = -> do
|
2
|
+
require 'coverage'
|
3
|
+
require 'coverage_peeker'
|
4
|
+
require 'what_to_run/tracker'
|
3
5
|
|
4
|
-
|
5
|
-
require 'coverage_peeker'
|
6
|
+
Coverage.start
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
RSpec.configuration.before(:suite) do
|
9
|
+
WhatToRun::Tracker.start
|
10
|
+
end
|
9
11
|
|
10
|
-
RSpec.configuration.after(:suite)
|
11
|
-
|
12
|
-
|
12
|
+
RSpec.configuration.after(:suite) do
|
13
|
+
WhatToRun::Tracker.finish
|
14
|
+
Coverage.result
|
15
|
+
end
|
13
16
|
|
14
|
-
RSpec.configuration.around(:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
RSpec.configuration.around(:each) do |example|
|
18
|
+
before = CoveragePeeker.peek_result
|
19
|
+
example.call
|
20
|
+
after = CoveragePeeker.peek_result
|
21
|
+
|
22
|
+
WhatToRun::Tracker.track \
|
23
|
+
example.metadata[:full_description], before, after
|
24
|
+
end
|
21
25
|
end
|
26
|
+
|
27
|
+
configure.call if ENV['COLLECT'] && ENV['COLLECT'] != 'false'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'what_to_run/runner'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module WhatToRun
|
5
|
+
module RSpec
|
6
|
+
class Runner < WhatToRun::Runner
|
7
|
+
DEFAULT_EXECUTABLE = 'bundle exec rspec'.freeze
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
super({exec: DEFAULT_EXECUTABLE}.merge(opts))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def predicted_example_args
|
16
|
+
args = predicted_examples.flat_map { |example| ['-e', example] }
|
17
|
+
Shellwords.join(args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'what_to_run'
|
2
|
+
|
3
|
+
module WhatToRun
|
4
|
+
##
|
5
|
+
# Abstract base spec runner
|
6
|
+
class Runner
|
7
|
+
attr_reader :executable, :collect
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
@executable = opts.fetch(:exec)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
if predicted_examples.empty?
|
15
|
+
exit 0
|
16
|
+
else
|
17
|
+
Kernel.exec command
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def command
|
24
|
+
"#{executable} #{predicted_example_args}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def predicted_example_args
|
28
|
+
fail NotImplementedError, 'Subclass must override #predicted_example_args'
|
29
|
+
end
|
30
|
+
|
31
|
+
def predicted_examples
|
32
|
+
@predicted_examples ||= WhatToRun.predict
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'coverage_peeker'
|
5
|
+
require_relative 'differ'
|
6
|
+
|
7
|
+
module WhatToRun
|
8
|
+
class Tracker
|
9
|
+
FileUtils.mkdir_p('.what_to_run')
|
10
|
+
|
11
|
+
DB_NAME = 'run_log'.freeze
|
12
|
+
|
13
|
+
DB = SQLite3::Database.open \
|
14
|
+
".what_to_run/#{DB_NAME}#{ENV['TEST_ENV_NUMBER']}.db"
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def start
|
18
|
+
DB.execute 'drop table if exists coverage'
|
19
|
+
|
20
|
+
DB.execute <<-SQL
|
21
|
+
create table if not exists coverage (
|
22
|
+
description varchar,
|
23
|
+
log blob
|
24
|
+
)
|
25
|
+
SQL
|
26
|
+
|
27
|
+
@before_suite = CoveragePeeker.peek_result
|
28
|
+
end
|
29
|
+
|
30
|
+
def track(description, before, after)
|
31
|
+
coverage = Marshal.dump \
|
32
|
+
Differ.coverage_delta(before, after, @before_suite)
|
33
|
+
|
34
|
+
DB.execute 'insert into coverage VALUES(?, ?)',
|
35
|
+
[description, SQLite3::Blob.new(coverage)]
|
36
|
+
end
|
37
|
+
|
38
|
+
def finish
|
39
|
+
DB.close
|
40
|
+
end
|
41
|
+
|
42
|
+
def read
|
43
|
+
query = 'select description, log from coverage'
|
44
|
+
|
45
|
+
unless additional_databases.empty?
|
46
|
+
attach_databases!
|
47
|
+
query += union_query
|
48
|
+
end
|
49
|
+
|
50
|
+
DB.execute(query).map { |row| [row[0], Marshal.load(row[1])] }
|
51
|
+
end
|
52
|
+
|
53
|
+
def additional_databases
|
54
|
+
count = Dir['.what_to_run/*.db'].length
|
55
|
+
(2..count).map { |n| "#{DB_NAME}#{n}" }
|
56
|
+
end
|
57
|
+
|
58
|
+
def attach_databases!
|
59
|
+
additional_databases.each do |db|
|
60
|
+
DB.execute "attach '.what_to_run/#{db}.db' as #{db}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def union_query
|
65
|
+
additional_databases.inject('') do |query, db|
|
66
|
+
query += " union select description, log from #{db}.coverage"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private_class_method :additional_databases,
|
72
|
+
:attach_databases!, :union_query
|
73
|
+
end
|
74
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: what_to_run
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Patterson
|
@@ -9,48 +9,68 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake-compiler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '0.9'
|
21
|
-
- -
|
21
|
+
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 0.9.0
|
24
24
|
type: :development
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
|
-
- - ~>
|
28
|
+
- - "~>"
|
29
29
|
- !ruby/object:Gem::Version
|
30
30
|
version: '0.9'
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 0.9.0
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: rugged
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.21'
|
41
|
-
- -
|
41
|
+
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: 0.21.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
46
|
version_requirements: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
|
-
- - ~>
|
48
|
+
- - "~>"
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '0.21'
|
51
|
-
- -
|
51
|
+
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: 0.21.0
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: sqlite3
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.3'
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 1.3.10
|
64
|
+
type: :runtime
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - "~>"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '1.3'
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.3.10
|
54
74
|
description: Predict which tests are likely to fail after you’ve changed the code
|
55
75
|
email: dyego@dyegocosta.com
|
56
76
|
executables:
|
@@ -66,8 +86,15 @@ files:
|
|
66
86
|
- ext/coverage_peeker/extconf.rb
|
67
87
|
- lib/coverage_peeker.rb
|
68
88
|
- lib/what_to_run.rb
|
89
|
+
- lib/what_to_run/cli.rb
|
90
|
+
- lib/what_to_run/differ.rb
|
69
91
|
- lib/what_to_run/minitest.rb
|
92
|
+
- lib/what_to_run/minitest/runner.rb
|
70
93
|
- lib/what_to_run/rspec.rb
|
94
|
+
- lib/what_to_run/rspec/runner.rb
|
95
|
+
- lib/what_to_run/runner.rb
|
96
|
+
- lib/what_to_run/tracker.rb
|
97
|
+
- lib/what_to_run/version.rb
|
71
98
|
homepage: https://github.com/DyegoCosta/what_to_run
|
72
99
|
licenses:
|
73
100
|
- MIT
|
@@ -78,12 +105,12 @@ require_paths:
|
|
78
105
|
- lib
|
79
106
|
required_ruby_version: !ruby/object:Gem::Requirement
|
80
107
|
requirements:
|
81
|
-
- -
|
108
|
+
- - ">="
|
82
109
|
- !ruby/object:Gem::Version
|
83
110
|
version: 1.9.3
|
84
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
112
|
requirements:
|
86
|
-
- -
|
113
|
+
- - ">="
|
87
114
|
- !ruby/object:Gem::Version
|
88
115
|
version: '0'
|
89
116
|
requirements: []
|