miss_cleo 0.3.7 → 0.4.0
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 +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:
|