cbratest 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 16bd9373d934ef9e11eda3fef3e7c1c565684bcf
4
+ data.tar.gz: 5d9433bf389ac8b5e5aa4e17beb66ec5759d2849
5
+ SHA512:
6
+ metadata.gz: 7b13cdfad482952919d2206676069c9651883dbb03fb21db1d679ff74b7d4318e7c96e4d60b582a7b88803f15055d871ac0fce81d1f5288738c9d3974c14d8ae
7
+ data.tar.gz: 494e91e3e8855674531303bc69d75985ee7d40b1e31858009457599580082e87013384ecdbce3c057f485fa26d7c28bd82c1e800300dad5d62451bdb20b663c8
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cbratest.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Stephan Hagemann
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # cbratest [![Build Status](https://travis-ci.org/shageman/cbratest.svg?branch=master)](https://travis-ci.org/shageman/cbratest) [![Gem Version](https://badge.fury.io/rb/cbratest.svg)](http://badge.fury.io/rb/cbratest) [![Code Climate](https://codeclimate.com/github/shageman/cbratest.png)](https://codeclimate.com/github/shageman/cbratest) [![Dependency Status](https://gemnasium.com/shageman/cbratest.svg)](https://gemnasium.com/shageman/cbratest)
2
+
3
+ Prints a list of the components that have changed since the last commit and for which tests need to be run. Uses the dependencies within component-based Ruby/Rails applications (#cbra) to also print all transitively affected components.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cbratest'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cbratest
18
+
19
+ ## Usage
20
+
21
+ cbratest [OPTION] [application path]
22
+
23
+ Test runner employing the structure of Component-based Ruby/Rails apps to optimize what needs to run.
24
+
25
+ Options are...
26
+ -h, -H, --help Display this help message.
27
+
28
+ -r, --results DEFAULT Display the directories of the components in need of running tests
29
+ -v, --verbose Verbose output of all parts of the calculation
30
+
31
+ ## Example
32
+
33
+ There are sample #cbra folder structures in `spec/examples`. Here is an example run when changing a file in component `C`:
34
+
35
+ ± |master ✗| → bin/cbratest ~/workspace/cbratest/spec/examples/letters/A
36
+ /Users/stephan/workspace/cbratest/spec/examples/letters/B/test.sh
37
+ /Users/stephan/workspace/cbratest/spec/examples/letters/C/test.sh
38
+ /Users/stephan/workspace/cbratest/spec/examples/letters/A/test.sh
39
+
40
+ This output can be used to run the necessary tests like so:
41
+
42
+ bin/cbratest ~/workspace/cbratest/spec/examples/letters/A | xargs -n1 /bin/bash
43
+
44
+ In verbose mode one can check the correctness of cbratest's calculation:
45
+
46
+ ± |master ✗| → bin/cbratest -v ~/workspace/cbra/cbratest/spec/examples/letters/A
47
+ All components
48
+ B /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/B
49
+ C /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/C
50
+ D /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/D
51
+ E1 /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/E1
52
+ E2 /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/E2
53
+ F /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/F
54
+ A /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/A
55
+
56
+ Changes since last commit
57
+ /Users/stephan/workspace/cbra/cbratest/README.md
58
+ /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/C/Gemfile
59
+
60
+ Directly affected components
61
+ C /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/C
62
+
63
+ Transitively affected components
64
+ B /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/B
65
+ C /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/C
66
+ A /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/A
67
+
68
+ Test scripts to run
69
+ /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/B/test.sh
70
+ /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/C/test.sh
71
+ /Users/stephan/workspace/cbra/cbratest/spec/examples/letters/A/test.sh
72
+
73
+ ## Todos
74
+ * make algorithm work for structures where a gem is in a sub folder of another gem (only the inner gem should be diectly affected)
75
+ * allow for other test runners to be specified
76
+ * optionally check for changes since origin/master
77
+ * optionally allow branch to compare against to be specified
78
+
79
+ ## License
80
+
81
+ Copyright (c) 2014 Stephan Hagemann, stephan.hagemann@gmail.com, [@shageman](http://twitter.com/shageman)
82
+
83
+ Released under the MIT license. See LICENSE file for details.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/cbratest ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require_relative "../lib/cbratest"
4
+
5
+ def help_text
6
+ <<-USAGE
7
+ cbratest [OPTION] [application path]
8
+
9
+ Test runner employing the structure of Component-based Ruby/Rails apps to optimize what needs to run.
10
+
11
+ Options are...
12
+ -h, -H, --help Display this help message.
13
+
14
+ -r, --results DEFAULT Display the directories of the components in need of running tests
15
+ -v, --verbose Verbose output of all parts of the calculation
16
+ USAGE
17
+ end
18
+
19
+ option = "-r"
20
+ path = nil
21
+
22
+ case ARGV.size
23
+ when 0
24
+ when 1
25
+ if ARGV[0].start_with? "-"
26
+ option = ARGV[0]
27
+ else
28
+ path = ARGV[0]
29
+ end
30
+ when 2
31
+ option = ARGV[0]
32
+ path = ARGV[1]
33
+ else
34
+ puts "Incorrect invocation. Please see help:\n\n"
35
+ puts help_text
36
+ exit 1
37
+ end
38
+
39
+ if option
40
+ if %w(--help -h -H).include? option
41
+ puts help_text
42
+ elsif %w(-r --results).include? option
43
+ Cbratest::Runner.new(false).run path
44
+ elsif %w(-v --verbose).include? option
45
+ Cbratest::Runner.new(true).run path
46
+ end
47
+ end
data/cbratest.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'cbratest'
7
+ spec.version = '0.1.0'
8
+ spec.authors = ['Stephan Hagemann']
9
+ spec.email = ['stephan.hagemann@gmail.com']
10
+ spec.summary = %q{A test runner for your #cbra application}
11
+ spec.description = %q{A test runner that optimizes the tests it runs based on the recent changes of your application. Makes use of #cbra dependencies to infer which parts of the application are definitely unaffected. Science! BOOM!}
12
+ spec.homepage = 'https://github.com/shageman/cbradeps'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = %w(
16
+ bin/cbratest
17
+ cbratest.gemspec
18
+ Gemfile
19
+ lib/cbratest/affected_component_finder.rb
20
+ lib/cbratest/gemfile_scraper.rb
21
+ lib/cbratest/tests_to_run_selector.rb
22
+ lib/cbratest/transitive_affected_component_finder.rb
23
+ lib/cbratest.rb
24
+ LICENSE
25
+ Rakefile
26
+ README.md
27
+ )
28
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
29
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
30
+ spec.require_paths = ['lib']
31
+
32
+ spec.add_development_dependency 'bundler', '~> 1.5'
33
+ spec.add_development_dependency 'rake'
34
+ spec.add_development_dependency 'rspec'
35
+ end
@@ -0,0 +1,15 @@
1
+ module Cbratest
2
+ class AffectedComponentFinder
3
+ def find(components, changes)
4
+ components.inject({}) do |memo, component|
5
+ memo[component] = false
6
+ changes.each do |change|
7
+ if change.start_with? component[:options][:path]
8
+ memo[component] = true
9
+ end
10
+ end
11
+ memo
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,93 @@
1
+ module Cbratest
2
+ class GemfileScraper
3
+ def initialize(root_path)
4
+ @root_path = root_path
5
+ end
6
+
7
+ def name
8
+ Pathname.new(@root_path).basename.to_s
9
+ end
10
+
11
+ def to_s
12
+ {name: name, options: {path: @root_path}}
13
+ end
14
+
15
+ def cbra_dependencies
16
+ dirdep = direct_dependencies
17
+ transitive_cbra_dependencies.select do |dep|
18
+ dirdep.include?(dep[:name]) || dep[:options][:direct]
19
+ end
20
+ end
21
+
22
+ def transitive_cbra_dependencies
23
+ gem_dependencies.inject([]) do |memo, dep|
24
+ if !!dep[:options][:path]
25
+ absolute_dep = dep.clone
26
+ absolute_dep[:options][:path] = File.expand_path(File.join(@root_path, dep[:options][:path]))
27
+ memo << dep
28
+ end
29
+ memo
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def raw_gemspec
36
+ path = File.expand_path(File.join(@root_path, "#{underscore(name)}.gemspec"))
37
+ File.exist?(path) ? File.read(path) : ""
38
+ end
39
+
40
+ def direct_dependencies
41
+ raw_gemspec.split("\n").inject([]) do |memo, line|
42
+ match = line.match(/add_(?:development_)?dependency\s+["']([^'"]+)["']/)
43
+ memo << match[1] if match
44
+ memo
45
+ end
46
+ end
47
+
48
+ def raw_gemfile
49
+ path = File.expand_path(File.join(@root_path, "Gemfile"))
50
+ File.read(path)
51
+ end
52
+
53
+ def gem_dependencies
54
+ raw_gemfile.split("\n").inject([]) do |memo, line|
55
+ match = line.match(/gem\s+["']([^'"]+)["'](.*)/)
56
+ if match
57
+ memo << {name: match[1], options: OptionParser.new(match[2]).parse}
58
+ end
59
+ memo
60
+ end
61
+ end
62
+
63
+ def underscore(string)
64
+ string.gsub(/::/, '/').
65
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
66
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
67
+ tr("-", "_").
68
+ downcase
69
+ end
70
+
71
+ class OptionParser
72
+ def initialize(options)
73
+ @options = options
74
+ end
75
+
76
+ def parse
77
+ {}.merge(path).merge(direct)
78
+ end
79
+
80
+ private
81
+
82
+ def path
83
+ match = @options.match(/path(?:\s*=>|:)\s+["']([^'"]+)["']/)
84
+ match ? {path: match[1]} : {}
85
+ end
86
+
87
+ def direct
88
+ match = @options.match(/direct(?:\s*=>|:)\s+true/)
89
+ match ? {direct: true} : {}
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,10 @@
1
+ module Cbratest
2
+ class TestsToRunSelector
3
+ def list(affected)
4
+ affected.keys.inject([]) do |memo, key|
5
+ memo << File.join(key[:options][:path], "test.sh") if affected[key]
6
+ memo
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Cbratest
2
+ class TransitiveAffectedComponentFinder
3
+ def find(affected)
4
+ all_affected = {}
5
+ affected.keys.each do |key|
6
+ gem = GemfileScraper.new(key[:options][:path])
7
+ if gem.transitive_cbra_dependencies.any? { |dep| affected[dep] }
8
+ all_affected[key] = true
9
+ else
10
+ all_affected[key] = affected[key] || false
11
+ end
12
+ end
13
+ all_affected
14
+ end
15
+ end
16
+ end
data/lib/cbratest.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'set'
2
+ require 'pathname'
3
+ require 'graphviz'
4
+
5
+ module Cbratest
6
+ require_relative "cbratest/gemfile_scraper"
7
+ require_relative "cbratest/affected_component_finder"
8
+ require_relative "cbratest/transitive_affected_component_finder"
9
+ require_relative "cbratest/tests_to_run_selector"
10
+
11
+ class Runner
12
+ def initialize(verbose_output)
13
+ @verbose_output = verbose_output
14
+ end
15
+
16
+ def run(root_path)
17
+ path = root_path || current_path
18
+ app = GemfileScraper.new(path)
19
+
20
+ outputs "All components"
21
+ cbra_deps = app.transitive_cbra_dependencies.to_set
22
+ components = cbra_deps << app.to_s
23
+ outputs component_out(components.to_a)
24
+
25
+
26
+ outputs "\nChanges since last commit"
27
+ root_dir = `cd #{path} && git rev-parse --show-toplevel`.chomp
28
+ changes = `cd #{root_dir} && git status -s -u`.split("\n").map { |file| File.join(root_dir, file.sub(/^.../, "")) }
29
+ outputs changes
30
+
31
+
32
+ outputs "\nDirectly affected components"
33
+ affected = AffectedComponentFinder.new.find(components, changes)
34
+ outputs affected_out(affected)
35
+
36
+ outputs "\nTransitively affected components"
37
+ all_affected = TransitiveAffectedComponentFinder.new.find(affected)
38
+ outputs affected_out(all_affected)
39
+
40
+ outputs "\nTest scripts to run"
41
+ outputs all_in_need_of_running = TestsToRunSelector.new.list(all_affected), true
42
+ end
43
+
44
+ private
45
+
46
+ def outputs(arg, even_non_verbose = false)
47
+ output(arg) if even_non_verbose || @verbose_output
48
+ end
49
+
50
+ def output(arg)
51
+ puts arg
52
+ end
53
+
54
+ def component_out(array)
55
+ array.map do |component|
56
+ "#{component[:name].ljust(max_key_width(array))} #{component[:options][:path]}"
57
+ end
58
+ end
59
+
60
+ def affected_out(hash)
61
+ hash.keys.inject([]) do |memo, component|
62
+ memo << "#{component[:name].ljust(max_key_width(hash.keys))} #{component[:options][:path]}" if hash[component]
63
+ memo
64
+ end
65
+ end
66
+
67
+ def max_key_width(array)
68
+ array.inject(0) do |memo, component|
69
+ [memo, component[:name].length].max
70
+ end + 2
71
+ end
72
+ end
73
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cbratest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephan Hagemann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: 'A test runner that optimizes the tests it runs based on the recent changes
56
+ of your application. Makes use of #cbra dependencies to infer which parts of the
57
+ application are definitely unaffected. Science! BOOM!'
58
+ email:
59
+ - stephan.hagemann@gmail.com
60
+ executables:
61
+ - cbratest
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - bin/cbratest
66
+ - cbratest.gemspec
67
+ - Gemfile
68
+ - lib/cbratest/affected_component_finder.rb
69
+ - lib/cbratest/gemfile_scraper.rb
70
+ - lib/cbratest/tests_to_run_selector.rb
71
+ - lib/cbratest/transitive_affected_component_finder.rb
72
+ - lib/cbratest.rb
73
+ - LICENSE
74
+ - Rakefile
75
+ - README.md
76
+ homepage: https://github.com/shageman/cbradeps
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.0.3
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: 'A test runner for your #cbra application'
100
+ test_files: []