miss_cleo 0.4.5 → 0.4.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 630dc170c4e5fa41d6b7065b750dc9f07616b1bf
4
- data.tar.gz: 3add2d4e3b0a222a3e1f0f9c4736829d6e42c39a
3
+ metadata.gz: ff4da2d11102916af50405184a3d1ee99179c42a
4
+ data.tar.gz: 678874911854e9a25391e862bf25fc0b421a487e
5
5
  SHA512:
6
- metadata.gz: 44af7a7fecb387a9cd5909beb63583889b3d64450de17f2718279c454875894eae901878a35f762aeeea5a0f4c300b0a32860076353a91abd1402cbb97090ca9
7
- data.tar.gz: 986641acb95593f84854d59c17b2873d4a98e15e320a74ebc047ddf80be0983a78b20e4fc20d93950fbcf738a7a6fea1b040d8200bcc2cdf14148a767a74289f
6
+ metadata.gz: 3f4c17cc829f5f124a563f06afcef8e85e962dd57e00edab71371afc2c8368f844ff6df4c1b831f3b400758b493a42a0b19098c8d2eafecc69be00c5774de8c7
7
+ data.tar.gz: 6248365c072bfeef740ad36401decad5942a9474909bbd7a8d37e624407da27b74060c801b7037e5a46f6df5e34a0ac31daf9e4af14594f54920e45e20da4dad
data/bin/miss_cleo CHANGED
@@ -1,18 +1,32 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "miss_cleo"
4
- include ::MissCleo::CoverageMapHelper
5
4
 
6
5
  case ARGV.first
7
6
  when "build_deck"
8
- build_deck(ARGV[1..-1])
7
+ files = ARGV[1..-1]
8
+ puts "Building your deck with #{files}"
9
+ coverage_map = ::MissCleo::CoverageMap.build(files)
10
+ puts "Saving map as zip file"
11
+ ::MissCleo::CoverageMapUtilities::SaveZip.zip(coverage_map)
9
12
  when "predict"
10
13
  unless ARGV[1]
11
14
  puts "Specify a coverage map."
12
15
  puts "e.g. 'miss_cleo predict map.json.gz"
13
16
  else
14
- map = File.open(ARGV[1]).read.gunzip
15
- predict(map)
17
+ raw_map = File.open(ARGV[1]).read.gunzip
18
+ map = ::MissCleo::CoverageMap.new(JSON.parse(raw_map))
19
+ lines_changed = ::MissCleo::DiffDetector.lines_changed
20
+ tests_to_run = ::MissCleo::TestSelector.predict(map, lines_changed)
21
+ if lines_changed.empty?
22
+ puts "No line changes detected."
23
+ elsif tests_to_run.empty?
24
+ puts "No tests found. May be due to blind spot, new tests you've just written, or the changes may be untested."
25
+ else
26
+ puts "Run these tests:"
27
+ puts tests_to_run.sort.reverse
28
+ ::MissCleo::TestRunner.new(tests_to_run, lines_changed).run_tests
29
+ end
16
30
  end
17
31
  else
18
32
  puts "Please run miss_cleo with 'build_deck' or 'predict'."
data/lib/miss_cleo.rb CHANGED
@@ -11,9 +11,13 @@ require 'miss_cleo/coverage_filter'
11
11
  require 'miss_cleo/coverage_map'
12
12
  require 'miss_cleo/test_configurations/cucumber_config'
13
13
  require 'miss_cleo/test_configurations/rspec_config'
14
- require 'miss_cleo/coverage_map_helper.rb'
15
14
  require 'miss_cleo/test_configurations/rails_action_view_config'
16
15
  require 'miss_cleo/template_tracker'
16
+ require 'miss_cleo/tests_to_files_map_logger'
17
+ require 'miss_cleo/coverage_map_utilities/save_zip'
18
+ require 'miss_cleo/test_selector'
19
+ require 'miss_cleo/diff_detector'
20
+ require 'miss_cleo/test_runner'
17
21
 
18
22
  module MissCleo
19
23
  end
@@ -1,17 +1,17 @@
1
1
  module MissCleo
2
- class CoverageFilter
2
+ module CoverageFilter
3
+ module_function
3
4
  GEM_PATHS = ENV["GEM_PATH"].split(":")
4
5
  RUBY_PATH = `which ruby`.chomp.gsub("/bin/ruby","")
5
6
 
6
- def self.filter_core(result_hash)
7
-
7
+ def filter_core(result_hash)
8
8
  # Don't include gems in coverage map
9
9
  result_hash.reject do |file, line|
10
10
  ([RUBY_PATH] + GEM_PATHS).any? { |path| file.include?(path) }
11
11
  end
12
12
  end
13
13
 
14
- def self.normalize_paths(result_hash)
14
+ def normalize_paths(result_hash)
15
15
  normalized_hash = Hash.new
16
16
  result_hash.each do |key, value|
17
17
  trimmed_key = key.gsub(/#{Regexp.quote(`pwd`.chomp)}\//, "")
@@ -21,11 +21,13 @@ module MissCleo
21
21
  normalized_hash
22
22
  end
23
23
 
24
- def self.filter_and_trim(result_hash)
24
+ def filter_and_trim(result_hash)
25
25
  filtered = filter_core(result_hash)
26
26
  normalize_paths(filtered)
27
27
  end
28
28
 
29
+ private_class_method :filter_core
30
+ private_class_method :normalize_paths
29
31
  end
30
32
 
31
33
  end
@@ -1,6 +1,23 @@
1
1
  module MissCleo
2
2
  class CoverageMap
3
3
 
4
+ def self.build(file_names)
5
+ cov_map = self.new
6
+ file_names.each do |file_name|
7
+ File.open(file_name, "r") do |f|
8
+ f.read
9
+ end.tap do |contents|
10
+ begin
11
+ cov_map.add_to_coverage_map(JSON.parse(contents))
12
+ rescue
13
+ puts "#{file_name} is malformed, this may affect test predictons"
14
+ end
15
+ end
16
+ end
17
+
18
+ cov_map
19
+ end
20
+
4
21
  attr_reader :map
5
22
 
6
23
  def initialize(map = nil)
@@ -0,0 +1,13 @@
1
+ module MissCleo
2
+ module CoverageMapUtilities
3
+ module SaveZip
4
+ module_function
5
+
6
+ def zip(coverage_map)
7
+ File.open("total_coverage_map.json.gz", "w") do |f|
8
+ f.write(JSON.dump(coverage_map.map).gzip)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,35 @@
1
+ module MissCleo
2
+ module DiffDetector
3
+ module_function
4
+ def lines_changed
5
+ Set.new.tap do |changed_lines|
6
+ repo = Rugged::Repository.new '.'
7
+ repo.index.diff.each_patch do |patch|
8
+ file = patch.delta.old_file[:path]
9
+
10
+ patch.each_hunk do |hunk|
11
+ hunk.each_line do |line|
12
+ case line.line_origin
13
+ when :addition
14
+ changed_lines << [file, line.new_lineno] unless exclude_from_map?(file)
15
+ when :deletion
16
+ changed_lines << [file, line.old_lineno] unless exclude_from_map?(file)
17
+ when :context
18
+ # do nothing
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+
28
+ def exclude_from_map?(file_name)
29
+ # Let's add a configuration for ignored files
30
+ file_name == "db/structure.sql"
31
+ end
32
+
33
+ private_class_method :exclude_from_map?
34
+ end
35
+ end
@@ -2,27 +2,25 @@ module MissCleo
2
2
  module TestConfigurations
3
3
  class CucumberConfig
4
4
  CUCUMBER_MAP = "cucumber_map.json"
5
- LOGS = []
6
5
 
7
6
  def self.setup_hooks(context)
8
- if ENV["COVERAGE"]
9
- MissCleo::TemplateTracker.initialize_tracker
10
- ActionView::Template.prepend MissCleo::TestConfigurations::ActionViewConfig if defined? ActionView::Template
11
- Coverage.start
12
- context.Around do |scenario, execute|
13
- MissCleo::TemplateTracker.clear_templates
14
- before = Coverage.peek_result
15
- execute.call
16
- after = Coverage.peek_result
17
- templates = MissCleo::TemplateTracker.templates.uniq
18
- if file_and_line = scenario.try(:file_colon_line)
19
- LOGS << [ file_and_line, { before: CoverageFilter.filter_and_trim(before), after: CoverageFilter.filter_and_trim(after), templates: templates } ]
20
- end
7
+ map_logger = MissCleo::TestsToFilesMapLogger.new(CUCUMBER_MAP)
8
+ MissCleo::TemplateTracker.initialize_tracker
9
+ ActionView::Template.prepend MissCleo::TestConfigurations::ActionViewConfig if defined? ActionView::Template
10
+ Coverage.start
11
+ context.Around do |scenario, execute|
12
+ MissCleo::TemplateTracker.clear_templates
13
+ before = Coverage.peek_result
14
+ execute.call
15
+ after = Coverage.peek_result
16
+ templates = MissCleo::TemplateTracker.templates.uniq
17
+ if file_and_line = scenario.try(:file_colon_line)
18
+ map_logger.add_to_log(file_and_line, before, after, templates)
21
19
  end
20
+ end
22
21
 
23
- at_exit do
24
- File.open(CUCUMBER_MAP, 'w') { |f| f.write JSON.dump LOGS }
25
- end
22
+ at_exit do
23
+ map_logger.export_logs
26
24
  end
27
25
  end
28
26
  end
@@ -2,29 +2,23 @@ module MissCleo
2
2
  module TestConfigurations
3
3
  class RspecConfig
4
4
  RSPEC_MAP = "rspec_map.json"
5
- LOGS = []
6
5
 
7
- def self.setup_hooks(context)
8
- if ENV["COVERAGE"]
9
- MissCleo::TemplateTracker.initialize_tracker
10
- ActionView::Template.prepend MissCleo::TestConfigurations::ActionViewConfig if defined? ActionView::Template
11
- Coverage.start
12
- RSpec.configuration.after(:suite) do
13
- File.open(RSPEC_MAP, 'w') { |f| f.write JSON.dump LOGS }
14
- end
6
+ def self.setup_hooks
7
+ logger = MissCleo::TestsToFilesMapLogger.new(RSPEC_MAP)
8
+ MissCleo::TemplateTracker.initialize_tracker
9
+ ActionView::Template.prepend MissCleo::TestConfigurations::ActionViewConfig if defined? ActionView::Template
10
+ Coverage.start
11
+ RSpec.configuration.after(:suite) do
12
+ logger.export_logs
13
+ end
15
14
 
16
- RSpec.configuration.around(:example) do |example|
17
- MissCleo::TemplateTracker.clear_templates
18
- before = Coverage.peek_result
19
- example.call
20
- after = Coverage.peek_result
21
- templates = MissCleo::TemplateTracker.templates.uniq
22
- LOGS << [ example.location, {
23
- before: CoverageFilter.filter_and_trim(before),
24
- after: CoverageFilter.filter_and_trim(after),
25
- templates: templates
26
- } ]
27
- end
15
+ RSpec.configuration.around(:example) do |example|
16
+ MissCleo::TemplateTracker.clear_templates
17
+ before = Coverage.peek_result
18
+ example.call
19
+ after = Coverage.peek_result
20
+ templates = MissCleo::TemplateTracker.templates.uniq
21
+ logger.add_to_log(example.location, before, after, templates)
28
22
  end
29
23
  end
30
24
  end
@@ -0,0 +1,38 @@
1
+ module MissCleo
2
+ class TestRunner
3
+ def initialize(tests_to_run, lines_changed)
4
+ @tests_to_run = tests_to_run
5
+ @lines_changed = lines_changed
6
+ end
7
+
8
+ def run_tests
9
+ # come up with a better way to register tests and test execution
10
+ exec_command = "bundle exec"
11
+ cukes = tests_to_run.select { |test| test.match(/^feature/) }
12
+ specs = tests_to_run.select { |test| test.match(/^\.\/spec/) }
13
+ run_cukes = cukes.empty? ? "" : "#{exec_command} cucumber #{cukes.join(" ")}"
14
+ run_specs = specs.empty? ? "" : "#{exec_command} rspec #{specs.join(" ")}"
15
+ spec_line = specs.empty? ? nil : "(s)pecs"
16
+ cuke_line = cukes.empty? ? nil : "(c)ukes"
17
+ both_line = specs.empty? || cukes.empty? ? nil : "(B)oth cukes and specs"
18
+ puts "Run: " + [spec_line, cuke_line, both_line].compact.join(", ") + " or (n)othing:"
19
+ response = STDIN.gets.chomp
20
+ case response
21
+ when "s"
22
+ puts "Running only specs..."
23
+ exec run_specs
24
+ when "c"
25
+ puts "Running only cukes..."
26
+ exec run_cukes
27
+ when "n"
28
+ else
29
+ puts "Running both specs and cukes..."
30
+ exec [run_cukes, run_specs].join("; ")
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :tests_to_run, :lines_changed
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ module MissCleo
2
+ module TestSelector
3
+ module_function
4
+
5
+ def predict(coverage_map, lines_changed)
6
+ tests_to_run = []
7
+ lines_changed.each do |file, line|
8
+ tests_to_run |= coverage_map.tests_for_lines(file, line)
9
+ end
10
+
11
+ tests_to_run.sort.reverse
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ module MissCleo
2
+ class TestsToFilesMapLogger
3
+
4
+ def initialize(file_name)
5
+ @logs = []
6
+ @file_name = file_name
7
+ end
8
+
9
+ def add_to_log(test_file_and_line, lines_run_before, lines_run_after, templates)
10
+ logs << [ test_file_and_line, {
11
+ before: filter(lines_run_before),
12
+ after: filter(lines_run_after),
13
+ templates: templates
14
+ }]
15
+ end
16
+
17
+ def export_logs
18
+ File.open(file_name, 'w') { |f| f.write JSON.dump logs }
19
+ end
20
+
21
+ private
22
+
23
+ attr_accessor :logs
24
+ attr_reader :file_name
25
+
26
+ def filter(lines_run)
27
+ MissCleo::CoverageFilter.filter_and_trim(lines_run)
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module MissCleo
2
- VERSION = "0.4.5"
2
+ VERSION = "0.4.6"
3
3
  end
data/miss_cleo.gemspec CHANGED
@@ -17,10 +17,12 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
18
  spec.executables = ["miss_cleo"]
19
19
  spec.require_paths = ["lib"]
20
+ spec.required_ruby_version = '>= 2.3.0'
20
21
 
21
- spec.add_development_dependency "bundler", "~> 1.7.6"
22
+ spec.add_development_dependency "bundler"
22
23
  spec.add_development_dependency "rake", "~> 10.5.0"
23
24
  spec.add_development_dependency "pry", "~> 0.9.12"
25
+ spec.add_development_dependency "rspec"
24
26
  spec.add_runtime_dependency "rugged", "~> 0.23.3"
25
27
  spec.add_runtime_dependency "gzip", "~> 1.0"
26
28
  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.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dean Hu
@@ -10,22 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-04-08 00:00:00.000000000 Z
13
+ date: 2016-04-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 1.7.6
21
+ version: '0'
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: 1.7.6
28
+ version: '0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rake
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -54,6 +54,20 @@ dependencies:
54
54
  - - "~>"
55
55
  - !ruby/object:Gem::Version
56
56
  version: 0.9.12
57
+ - !ruby/object:Gem::Dependency
58
+ name: rspec
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
57
71
  - !ruby/object:Gem::Dependency
58
72
  name: rugged
59
73
  requirement: !ruby/object:Gem::Requirement
@@ -105,11 +119,15 @@ files:
105
119
  - lib/miss_cleo.rb
106
120
  - lib/miss_cleo/coverage_filter.rb
107
121
  - lib/miss_cleo/coverage_map.rb
108
- - lib/miss_cleo/coverage_map_helper.rb
122
+ - lib/miss_cleo/coverage_map_utilities/save_zip.rb
123
+ - lib/miss_cleo/diff_detector.rb
109
124
  - lib/miss_cleo/template_tracker.rb
110
125
  - lib/miss_cleo/test_configurations/cucumber_config.rb
111
126
  - lib/miss_cleo/test_configurations/rails_action_view_config.rb
112
127
  - lib/miss_cleo/test_configurations/rspec_config.rb
128
+ - lib/miss_cleo/test_runner.rb
129
+ - lib/miss_cleo/test_selector.rb
130
+ - lib/miss_cleo/tests_to_files_map_logger.rb
113
131
  - lib/miss_cleo/version.rb
114
132
  - miss_cleo.gemspec
115
133
  homepage: http://github.com/gust/miss_cleo
@@ -124,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
124
142
  requirements:
125
143
  - - ">="
126
144
  - !ruby/object:Gem::Version
127
- version: '0'
145
+ version: 2.3.0
128
146
  required_rubygems_version: !ruby/object:Gem::Requirement
129
147
  requirements:
130
148
  - - ">="
@@ -132,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
150
  version: '0'
133
151
  requirements: []
134
152
  rubyforge_project:
135
- rubygems_version: 2.4.3
153
+ rubygems_version: 2.5.1
136
154
  signing_key:
137
155
  specification_version: 4
138
156
  summary: Regression Test Selection
@@ -1,73 +0,0 @@
1
- module MissCleo
2
- module CoverageMapHelper
3
-
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
- f.read
9
- end.tap do |contents|
10
- begin
11
- cov_map.add_to_coverage_map(JSON.parse(contents))
12
- rescue
13
- puts "#{file_name} is malformed, this may affect test predictons"
14
- end
15
- end
16
- end
17
-
18
- File.open("total_coverage_map.json.gz", "w") do |f|
19
- f.write(JSON.dump(cov_map.map).gzip)
20
- end
21
- end
22
-
23
- def predict(unzipped_file)
24
- coverage_map = ::MissCleo::CoverageMap.new(JSON.parse(unzipped_file))
25
- tests_to_run = []
26
- lines_changed.each do |file, line|
27
- tests_to_run |= coverage_map.tests_for_lines(file, line)
28
- end
29
- if lines_changed.empty?
30
- puts "No line changes detected."
31
- elsif tests_to_run.empty?
32
- puts "No tests found. May be due to blind spot, new tests you've just written, or the changes may be untested."
33
- else
34
- puts "Run these tests:"
35
- puts tests_to_run.sort.reverse
36
- end
37
-
38
- tests_to_run
39
- end
40
-
41
-
42
- private
43
-
44
- def exclude_from_map?(file_name)
45
- # Let's add a configuration for ignored files
46
- file_name == "db/structure.sql"
47
- end
48
-
49
-
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
67
- end
68
- end
69
-
70
- end
71
- end
72
- end
73
- end