miss_cleo 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/miss_cleo +1 -1
- data/lib/miss_cleo/coverage_map.rb +90 -0
- data/lib/miss_cleo/coverage_map_helper.rb +27 -74
- data/lib/miss_cleo/template_helper.rb +22 -0
- data/lib/miss_cleo/test_configurations/action_view_hook.rb +11 -0
- data/lib/miss_cleo/test_configurations/cucumber_config.rb +3 -1
- data/lib/miss_cleo/test_configurations/rails_action_view_config.rb +10 -0
- data/lib/miss_cleo/test_configurations/rspec_config.rb +7 -2
- data/lib/miss_cleo/version.rb +1 -1
- data/lib/miss_cleo.rb +4 -0
- data/miss_cleo.gemspec +6 -5
- metadata +34 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d918fe13d7cee5691d8fe6cc48589ebd9dd0fcbe
|
4
|
+
data.tar.gz: be479f4e751445144aec9b5d6f106795863cbff5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39a4f91fafa9685d0f8f2f8ec970b46d64310ba21e49a506a136de9464a616526ba0ab33d5ecd32eaa51be8dd4fd71c6bdc2daffb024bdb8978d575f73beab5f
|
7
|
+
data.tar.gz: 5c8f121ab1431fb0d823988443708c0423f4a9ec71117fb7505e47a2635663f25ce0e42847f2549d1f968b7d54f3222235f05877cf79c747c8981dfebc862ddb
|
data/bin/miss_cleo
CHANGED
@@ -0,0 +1,90 @@
|
|
1
|
+
module MissCleo
|
2
|
+
class CoverageMap
|
3
|
+
|
4
|
+
attr_reader :map
|
5
|
+
|
6
|
+
def initialize(map = nil)
|
7
|
+
@map = map || Hash.new { |h, file| h[file] = Hash.new { |i, line| i[line] = [] } }
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_to_coverage_map(test_diffs)
|
11
|
+
test_diffs.each do |test_to_code_map|
|
12
|
+
test_file_and_line = test_to_code_map.first
|
13
|
+
before = test_to_code_map.last["before"]
|
14
|
+
after = test_to_code_map.last["after"]
|
15
|
+
templates = test_to_code_map.last["templates"]
|
16
|
+
|
17
|
+
# calculate the per test coverage
|
18
|
+
delta = diff before, after
|
19
|
+
add_delta_to_map(delta, test_file_and_line)
|
20
|
+
add_templates_to_map(templates, test_file_and_line)
|
21
|
+
end
|
22
|
+
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def tests_for_lines(file, line)
|
27
|
+
begin
|
28
|
+
# NOTE: This is currently how templates and non-templates are being
|
29
|
+
# split. Since I will likely have to cover a bunch of blind spots
|
30
|
+
# manually like this, this probably needs to be rearchitected.
|
31
|
+
if file.include?("app/views")
|
32
|
+
map[file]["0"].uniq || []
|
33
|
+
else
|
34
|
+
map[file][line].uniq || []
|
35
|
+
end
|
36
|
+
rescue
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def diff before, after
|
44
|
+
after.each_with_object({}) do |(file_name,line_cov), res|
|
45
|
+
before_line_cov = before[file_name] || []
|
46
|
+
|
47
|
+
# skip arrays that are exactly the same
|
48
|
+
next if before_line_cov == line_cov
|
49
|
+
|
50
|
+
# subtract the old coverage from the new coverage
|
51
|
+
cov = line_cov.zip(before_line_cov).map do |line_after, line_before|
|
52
|
+
if line_before
|
53
|
+
line_after - line_before
|
54
|
+
else
|
55
|
+
line_after
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# add the "diffed" coverage to the hash
|
60
|
+
res[file_name] = cov
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_delta_to_map(delta, test_file_and_line)
|
65
|
+
delta.each_pair do |file, lines|
|
66
|
+
file_map = map[file]
|
67
|
+
|
68
|
+
lines.each_with_index do |val, i|
|
69
|
+
# skip lines that weren't executed
|
70
|
+
next unless val && val > 0
|
71
|
+
|
72
|
+
# add the test name to the map. Multiple tests can execute the same
|
73
|
+
# line, so we need to use an array.
|
74
|
+
file_map[i + 1] << test_file_and_line
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_templates_to_map(templates, test_file_and_line)
|
80
|
+
templates.each do |template|
|
81
|
+
# NOTE: Currently templates do not know how many lines of code they
|
82
|
+
# are. I have temporarily decided to shove them into the 0 line
|
83
|
+
# to make them a special case. This will probably require
|
84
|
+
# re-architecting
|
85
|
+
map[template][0] << test_file_and_line
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,61 +1,38 @@
|
|
1
1
|
module MissCleo
|
2
2
|
module CoverageMapHelper
|
3
3
|
|
4
|
-
def build_deck(
|
5
|
-
cov_map =
|
6
|
-
|
7
|
-
File.open(
|
4
|
+
def build_deck(file_names)
|
5
|
+
cov_map = ::MissCleo::CoverageMap.new
|
6
|
+
file_names.each do |file_name|
|
7
|
+
File.open(file_name, "r") do |f|
|
8
8
|
f.read
|
9
9
|
end.tap do |contents|
|
10
10
|
begin
|
11
|
-
|
11
|
+
cov_map.add_to_coverage_map(JSON.parse(contents))
|
12
12
|
rescue
|
13
|
-
puts "#{
|
13
|
+
puts "#{file_name} is malformed, this may affect test predictons"
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
File.open("total_coverage_map.json.gz", "w") do |f|
|
19
|
-
f.write(JSON.dump(cov_map).gzip)
|
19
|
+
f.write(JSON.dump(cov_map.map).gzip)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def predict(unzipped_file)
|
24
|
-
|
25
|
-
lines_changed = Set.new
|
26
|
-
|
27
|
-
repo.index.diff.each_patch do |patch|
|
28
|
-
file = patch.delta.old_file[:path]
|
29
|
-
|
30
|
-
patch.each_hunk do |hunk|
|
31
|
-
hunk.each_line do |line|
|
32
|
-
case line.line_origin
|
33
|
-
when :addition
|
34
|
-
lines_changed << [file, line.new_lineno] unless exclude_from_map?(file)
|
35
|
-
when :deletion
|
36
|
-
lines_changed << [file, line.old_lineno] unless exclude_from_map?(file)
|
37
|
-
when :context
|
38
|
-
# do nothing
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
coverage_map = JSON.parse(unzipped_file)
|
24
|
+
coverage_map = ::MissCleo::CoverageMap.new(JSON.parse(unzipped_file))
|
45
25
|
tests_to_run = []
|
46
26
|
lines_changed.each do |file, line|
|
47
|
-
|
48
|
-
tests_to_run << desc
|
49
|
-
end
|
27
|
+
tests_to_run |= coverage_map.tests_for_lines(file, line)
|
50
28
|
end
|
51
|
-
tests_to_run = tests_to_run.uniq.sort.reverse
|
52
29
|
if lines_changed.empty?
|
53
30
|
puts "No line changes detected."
|
54
31
|
elsif tests_to_run.empty?
|
55
32
|
puts "No tests found. May be due to blind spot, new tests you've just written, or the changes may be untested."
|
56
33
|
else
|
57
34
|
puts "Run these tests:"
|
58
|
-
puts tests_to_run.
|
35
|
+
puts tests_to_run.sort.reverse
|
59
36
|
end
|
60
37
|
|
61
38
|
tests_to_run
|
@@ -69,51 +46,27 @@ module MissCleo
|
|
69
46
|
file_name == "db/structure.sql"
|
70
47
|
end
|
71
48
|
|
72
|
-
def build_coverage_map(cov_map, cov_diffs)
|
73
|
-
cov_diffs.each do |test_to_code_map|
|
74
|
-
if test_to_code_map.length == 4 # for Minitest
|
75
|
-
test_file_and_line = test_to_code_map.first(2).join('#')
|
76
|
-
else # for RSpec
|
77
|
-
test_file_and_line = test_to_code_map.first
|
78
|
-
end
|
79
|
-
before, after = test_to_code_map.last(2)
|
80
|
-
|
81
|
-
# calculate the per test coverage
|
82
|
-
delta = diff before, after
|
83
|
-
|
84
|
-
delta.each_pair do |file, lines|
|
85
|
-
file_map = cov_map[file]
|
86
|
-
|
87
|
-
lines.each_with_index do |val, i|
|
88
|
-
# skip lines that weren't executed
|
89
|
-
next unless val && val > 0
|
90
|
-
|
91
|
-
# add the test name to the map. Multiple tests can execute the same
|
92
|
-
# line, so we need to use an array.
|
93
|
-
file_map[i + 1] << test_file_and_line
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def diff before, after
|
100
|
-
after.each_with_object({}) do |(file_name,line_cov), res|
|
101
|
-
before_line_cov = before[file_name] || []
|
102
49
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
50
|
+
def lines_changed
|
51
|
+
@_lines_changed ||= Set.new.tap do |changed_lines|
|
52
|
+
repo = Rugged::Repository.new '.'
|
53
|
+
repo.index.diff.each_patch do |patch|
|
54
|
+
file = patch.delta.old_file[:path]
|
55
|
+
|
56
|
+
patch.each_hunk do |hunk|
|
57
|
+
hunk.each_line do |line|
|
58
|
+
case line.line_origin
|
59
|
+
when :addition
|
60
|
+
changed_lines << [file, line.new_lineno] unless exclude_from_map?(file)
|
61
|
+
when :deletion
|
62
|
+
changed_lines << [file, line.old_lineno] unless exclude_from_map?(file)
|
63
|
+
when :context
|
64
|
+
# do nothing
|
65
|
+
end
|
66
|
+
end
|
112
67
|
end
|
113
68
|
end
|
114
69
|
|
115
|
-
# add the "diffed" coverage to the hash
|
116
|
-
res[file_name] = cov
|
117
70
|
end
|
118
71
|
end
|
119
72
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MissCleo
|
2
|
+
module TemplateHelper
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def template_coverage
|
6
|
+
@template_coverage ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_to_template_coverage(template)
|
10
|
+
template_coverage << trim_path(template)
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset_coverage
|
14
|
+
template_coverage = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def trim_path(path)
|
18
|
+
path.gsub(/#{Regexp.quote(`pwd`.chomp)}\//, "")
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -8,11 +8,13 @@ module MissCleo
|
|
8
8
|
if ENV["COVERAGE"]
|
9
9
|
Coverage.start
|
10
10
|
context.Around do |scenario, execute|
|
11
|
+
MissCleo::TemplateHelper.reset_coverage
|
11
12
|
before = Coverage.peek_result
|
12
13
|
execute.call
|
13
14
|
after = Coverage.peek_result
|
15
|
+
templates = MissCleo::TemplateHelper.template_coverage
|
14
16
|
if file_and_line = scenario.try(:file_colon_line)
|
15
|
-
LOGS << [ file_and_line, CoverageFilter.filter_and_trim(before), CoverageFilter.filter_and_trim(after) ]
|
17
|
+
LOGS << [ file_and_line, { before: CoverageFilter.filter_and_trim(before), after: CoverageFilter.filter_and_trim(after), templates: templates } ]
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
@@ -12,11 +12,16 @@ module MissCleo
|
|
12
12
|
end
|
13
13
|
|
14
14
|
RSpec.configuration.around(:example) do |example|
|
15
|
+
MissCleo::TemplateHelper.reset_coverage
|
15
16
|
before = Coverage.peek_result
|
16
17
|
example.call
|
17
18
|
after = Coverage.peek_result
|
18
|
-
|
19
|
-
|
19
|
+
templates = MissCleo::TemplateHelper.template_coverage
|
20
|
+
LOGS << [ example.location, {
|
21
|
+
before: CoverageFilter.filter_and_trim(before),
|
22
|
+
after: CoverageFilter.filter_and_trim(after),
|
23
|
+
templates: templates
|
24
|
+
} ]
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
data/lib/miss_cleo/version.rb
CHANGED
data/lib/miss_cleo.rb
CHANGED
@@ -8,9 +8,13 @@ require 'gzip'
|
|
8
8
|
|
9
9
|
require "miss_cleo/version"
|
10
10
|
require 'miss_cleo/coverage_filter'
|
11
|
+
require 'miss_cleo/coverage_map'
|
11
12
|
require 'miss_cleo/test_configurations/cucumber_config'
|
12
13
|
require 'miss_cleo/test_configurations/rspec_config'
|
13
14
|
require 'miss_cleo/coverage_map_helper.rb'
|
15
|
+
require 'miss_cleo/test_configurations/action_view_hook'
|
16
|
+
require 'miss_cleo/test_configurations/rails_action_view_config'
|
17
|
+
require 'miss_cleo/template_helper'
|
14
18
|
|
15
19
|
module MissCleo
|
16
20
|
end
|
data/miss_cleo.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Dean Hu", "Lee Ourand", "John Bernier"]
|
10
10
|
spec.email = ["devs@gust.com"]
|
11
11
|
|
12
|
-
spec.summary = %q{
|
12
|
+
spec.summary = %q{Regression Test Selection}
|
13
13
|
spec.description = %q{Miss Cleo frees you from having to run your full test suite to be confident in your recent code changes. Call me now!}
|
14
14
|
spec.homepage = "http://github.com/gust/miss_cleo"
|
15
15
|
spec.license = "MIT"
|
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = ["miss_cleo"]
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.
|
22
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
-
spec.
|
24
|
-
spec.add_runtime_dependency "
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7.6"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.5.0"
|
23
|
+
spec.add_development_dependency "pry", "~> 0.9.12"
|
24
|
+
spec.add_runtime_dependency "rugged", "~> 0.23.3"
|
25
|
+
spec.add_runtime_dependency "gzip", "~> 1.0"
|
25
26
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: miss_cleo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dean Hu
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-02-12 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -18,56 +18,70 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - "~>"
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 1.7.6
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - "~>"
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version:
|
28
|
+
version: 1.7.6
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: rake
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
33
|
- - "~>"
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 10.5.0
|
36
36
|
type: :development
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
40
|
- - "~>"
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version:
|
42
|
+
version: 10.5.0
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: pry
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 0.9.12
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - "~>"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.9.12
|
43
57
|
- !ruby/object:Gem::Dependency
|
44
58
|
name: rugged
|
45
59
|
requirement: !ruby/object:Gem::Requirement
|
46
60
|
requirements:
|
47
|
-
- - "
|
61
|
+
- - "~>"
|
48
62
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
63
|
+
version: 0.23.3
|
50
64
|
type: :runtime
|
51
65
|
prerelease: false
|
52
66
|
version_requirements: !ruby/object:Gem::Requirement
|
53
67
|
requirements:
|
54
|
-
- - "
|
68
|
+
- - "~>"
|
55
69
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
70
|
+
version: 0.23.3
|
57
71
|
- !ruby/object:Gem::Dependency
|
58
72
|
name: gzip
|
59
73
|
requirement: !ruby/object:Gem::Requirement
|
60
74
|
requirements:
|
61
|
-
- - "
|
75
|
+
- - "~>"
|
62
76
|
- !ruby/object:Gem::Version
|
63
|
-
version: '0'
|
77
|
+
version: '1.0'
|
64
78
|
type: :runtime
|
65
79
|
prerelease: false
|
66
80
|
version_requirements: !ruby/object:Gem::Requirement
|
67
81
|
requirements:
|
68
|
-
- - "
|
82
|
+
- - "~>"
|
69
83
|
- !ruby/object:Gem::Version
|
70
|
-
version: '0'
|
84
|
+
version: '1.0'
|
71
85
|
description: Miss Cleo frees you from having to run your full test suite to be confident
|
72
86
|
in your recent code changes. Call me now!
|
73
87
|
email:
|
@@ -90,8 +104,12 @@ files:
|
|
90
104
|
- bin/setup
|
91
105
|
- lib/miss_cleo.rb
|
92
106
|
- lib/miss_cleo/coverage_filter.rb
|
107
|
+
- lib/miss_cleo/coverage_map.rb
|
93
108
|
- lib/miss_cleo/coverage_map_helper.rb
|
109
|
+
- lib/miss_cleo/template_helper.rb
|
110
|
+
- lib/miss_cleo/test_configurations/action_view_hook.rb
|
94
111
|
- lib/miss_cleo/test_configurations/cucumber_config.rb
|
112
|
+
- lib/miss_cleo/test_configurations/rails_action_view_config.rb
|
95
113
|
- lib/miss_cleo/test_configurations/rspec_config.rb
|
96
114
|
- lib/miss_cleo/version.rb
|
97
115
|
- miss_cleo.gemspec
|
@@ -115,9 +133,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
133
|
version: '0'
|
116
134
|
requirements: []
|
117
135
|
rubyforge_project:
|
118
|
-
rubygems_version: 2.
|
136
|
+
rubygems_version: 2.5.1
|
119
137
|
signing_key:
|
120
138
|
specification_version: 4
|
121
|
-
summary:
|
139
|
+
summary: Regression Test Selection
|
122
140
|
test_files: []
|
123
141
|
has_rdoc:
|