what_to_run 0.1.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Join the chat at https://gitter.im/DyegoCosta/what_to_run](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/DyegoCosta/what_to_run?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
|
+
|
3
5
|
[![Build Status](https://travis-ci.org/DyegoCosta/what_to_run.svg?branch=master)](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: []
|