rocksteady 0.8.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.
- data/CHANGELOG +1 -0
- data/Manifest +13 -0
- data/README.rdoc +143 -0
- data/Rakefile +16 -0
- data/lib/rocksteady.rb +5 -0
- data/lib/rocksteady/corpus.rb +83 -0
- data/lib/rocksteady/helpers.rb +38 -0
- data/lib/rocksteady/output.rb +15 -0
- data/lib/rocksteady/scenario.rb +31 -0
- data/lib/rocksteady/session.rb +79 -0
- data/lib/rocksteady/tasks.rb +71 -0
- data/lib/rocksteady/version.rb +78 -0
- data/rocksteady.gemspec +50 -0
- data/test/rocksteady_test.rb +95 -0
- metadata +106 -0
data/CHANGELOG
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
v0.8.0. Initial release. Basic functionality and tests.
|
data/Manifest
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
lib/rocksteady/corpus.rb
|
3
|
+
lib/rocksteady/helpers.rb
|
4
|
+
lib/rocksteady/output.rb
|
5
|
+
lib/rocksteady/scenario.rb
|
6
|
+
lib/rocksteady/session.rb
|
7
|
+
lib/rocksteady/tasks.rb
|
8
|
+
lib/rocksteady/version.rb
|
9
|
+
lib/rocksteady.rb
|
10
|
+
Manifest
|
11
|
+
Rakefile
|
12
|
+
README.rdoc
|
13
|
+
test/rocksteady_test.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
= Rocksteady
|
2
|
+
|
3
|
+
Often you need to test bits of code you write with bits of code other people write, across varying revisions. This is tedious, requiring a lot of custom work and infrastructure, so you probably don't do it enough. I know I don't, and it's a bad habit.
|
4
|
+
|
5
|
+
Rocksteady is meant to ease the process of switching out different versions of inter-dependent code, providing you with a simple _scenario_ metaphor to write build and test code within. It's test framework agnostic (it just uses exit codes), and extremely flexible.
|
6
|
+
|
7
|
+
Below you can read an example of testing different versions of Rails against different versions of a plugin; more in-depth examples can be found on the wiki (http://github.com/bruce/rocksteady/wikis/examples).
|
8
|
+
|
9
|
+
<i>Please let me know how you're using Rocksteady; it's nice to have feedback</i>.
|
10
|
+
|
11
|
+
Bruce Williams (http://codefluency.com)
|
12
|
+
http://github.com/bruce
|
13
|
+
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
Get it from RubyForge (once released):
|
18
|
+
sudo gem install rocksteady
|
19
|
+
|
20
|
+
=== Dependencies
|
21
|
+
|
22
|
+
* rake
|
23
|
+
sudo gem install rake
|
24
|
+
* ruport
|
25
|
+
sudo gem install ruport
|
26
|
+
* mojombo-grit >= 0.8.0
|
27
|
+
sudo gem install mojombo-grit -s http://gems.github.com
|
28
|
+
* (git in your PATH)
|
29
|
+
|
30
|
+
== Defining Scenarios
|
31
|
+
|
32
|
+
Create a new directory, and toss a <tt>Rakefile</tt> in it. Make it look like this:
|
33
|
+
|
34
|
+
require 'rubygems'
|
35
|
+
require 'rocksteady'
|
36
|
+
|
37
|
+
Now, point it at the git repos that are holding the code you'd like to test. You could <tt>clone</tt> the repos somewhere in this directory, if you'd like. For this example, we'll be testing a Rails plugin we've written against various versions of Rails, so it like look something like this (assuming we cloned bare <tt>.git</tt> repos into the current directory)
|
38
|
+
|
39
|
+
repos 'rails.git', 'our_plugin.git'
|
40
|
+
|
41
|
+
Okay, now let just create a single scenario. In general, a scenario will be some set of configuration you'd like to test. For this example we won't be doing anything fancy (just a simple drop in <tt>vendor/plugins</tt>) so it might look like this:
|
42
|
+
|
43
|
+
scenario "Installed in vendor/plugins" do
|
44
|
+
generate_rails_app
|
45
|
+
install_plugin
|
46
|
+
verify_loads_environment
|
47
|
+
end
|
48
|
+
|
49
|
+
You see 3 things need to happen in the scenario; we've broken these out for clarity and just define them underneath:
|
50
|
+
|
51
|
+
First, let's generate a fresh Rails app for testing with:
|
52
|
+
|
53
|
+
def generate_rails_app
|
54
|
+
ruby "#{rails_path}/railties/bin/rails rails_app"
|
55
|
+
end
|
56
|
+
|
57
|
+
A few notes on what you see above:
|
58
|
+
|
59
|
+
0. <tt>ruby</tt> is simply a <tt>rake</tt> convenience method that calls out to a new <tt>ruby</tt> interpreter
|
60
|
+
0. <tt>rails_path</tt> is the absolute path to a fresh clone of the <tt>rails.git</tt> repo we defined at the beginning of the file, checked-out to the reference point we're currently checking (more on how that's set later). <tt>*_path</tt> convenience methods are generated for all the repos you've defined; you'll see the other one in <tt>install_plugin</tt>.
|
61
|
+
0. We're calling out to the <tt>rails</tt> executable in the checked-out source directly so we can generate an accurate skeleton app for that version. The <tt>rails_app</tt> argument is just the name of the directory where it will be generated.
|
62
|
+
|
63
|
+
It's worth noting that when scenarios are being run, the current working directory is changed for you automatically; right now you're actually sitting in <tt>build/<timestamp>/scenarios/installed_in_vendor_plugins</tt>, where <tt>rails_app</tt> will be generated as a subdirectory.
|
64
|
+
|
65
|
+
Let's install the plugin now:
|
66
|
+
|
67
|
+
def install_plugin
|
68
|
+
cp_r our_plugin_path, 'rails_app/vendor/plugins'
|
69
|
+
end
|
70
|
+
|
71
|
+
0. <tt>cp_r</tt> is another <tt>rake</tt> convenience method that does a recursive copy.
|
72
|
+
0. <tt>our_plugin_path</tt> is the <tt>*_path</tt> method generated automatically for our plugin repo.
|
73
|
+
|
74
|
+
So, that was simple; for this quick example we just copy the plugin directly in.
|
75
|
+
|
76
|
+
Now let's do something that just verifies the Rails app code will load successfully across the standard environments, just by printing the Rails version from <tt>script/runner</tt>:
|
77
|
+
|
78
|
+
def verify_loads_environment
|
79
|
+
Dir.chdir 'rails_app' do
|
80
|
+
%w(test development production).each do |env|
|
81
|
+
ENV['RAILS_ENV'] = env
|
82
|
+
ruby "script/runner 'p Rails::VERSION'"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
The great thing about the convenience methods that <tt>rake</tt> provides (eg, <tt>ruby</tt>, <tt>sh</tt>, <tt>cp_r</tt>, etc) is that the raise an exception if the system call they make returns a bad exit code; the scenario automatically catches them and assigns a _failure_ to the scenario. If you're not using these convenience methods and need to assign a failure, you can raise an exception manually to get the same result.
|
88
|
+
|
89
|
+
== Running against arbitrary references
|
90
|
+
|
91
|
+
Here are some examples on how we could run the scenario, with plain English explanations:
|
92
|
+
|
93
|
+
Run all scenarios, with all repos set to <tt>master</tt>:
|
94
|
+
|
95
|
+
$ rake rocksteady
|
96
|
+
|
97
|
+
Run just the first scenario (you can use <tt>rake -T</tt> to see them all), with all repos set to <tt>master</tt>:
|
98
|
+
|
99
|
+
$ rake rocksteady:scenario:1
|
100
|
+
|
101
|
+
The same, but setting <tt>rails</tt> to <tt>v2.0.2</tt>:
|
102
|
+
|
103
|
+
$ rake rocksteady:scenario:1 REFS=rails:v2.0.2
|
104
|
+
|
105
|
+
and now with <tt>our_plugin</tt> set to the <tt>experimental</tt> branch:
|
106
|
+
|
107
|
+
$ rake rocksteady:scenario:1 REFS=rails:v2.0.2,our_plugin:experimental
|
108
|
+
|
109
|
+
You can also use full references:
|
110
|
+
|
111
|
+
$ rake rocksteady:scenario:1 REFS=rails:refs/tags/v2.0.2,our_plugin:refs/heads/experimental
|
112
|
+
|
113
|
+
== Output
|
114
|
+
|
115
|
+
Currently the output is a simple text-based table. You'll need to refer to the output in the terminal to find _where_ your failures occur. More granular, labelled scenario-level logging is on the roadmap.
|
116
|
+
|
117
|
+
== License
|
118
|
+
|
119
|
+
Copyright (c) 2008 Bruce R. Williams
|
120
|
+
|
121
|
+
Permission is hereby granted, free of charge, to any person
|
122
|
+
obtaining a copy of this software and associated documentation
|
123
|
+
files (the "Software"), to deal in the Software without
|
124
|
+
restriction, including without limitation the rights to use,
|
125
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
126
|
+
copies of the Software, and to permit persons to whom the
|
127
|
+
Software is furnished to do so, subject to the following
|
128
|
+
conditions:
|
129
|
+
|
130
|
+
The above copyright notice and this permission notice shall be
|
131
|
+
included in all copies or substantial portions of the Software.
|
132
|
+
|
133
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
134
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
135
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
136
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
137
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
138
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
139
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
140
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
141
|
+
|
142
|
+
|
143
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'echoe'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) << "/lib/rocksteady/version"
|
5
|
+
|
6
|
+
Echoe.new 'rocksteady' do |p|
|
7
|
+
p.version = Rocksteady::Version::STRING
|
8
|
+
p.author = "Bruce Williams"
|
9
|
+
p.email = 'bruce@codefluency.com'
|
10
|
+
p.project = 'codefluency'
|
11
|
+
p.summary = "Run arbitrary scenarios across disparate sets of git repo revisions"
|
12
|
+
p.url = "http://github.com/bruce/rocksteady"
|
13
|
+
p.dependencies = ['rake', ['mojombo-grit', '>= 0.8.0'], 'ruport']
|
14
|
+
p.include_rakefile = true
|
15
|
+
end
|
16
|
+
|
data/lib/rocksteady.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mojombo-grit'
|
3
|
+
|
4
|
+
module Rocksteady
|
5
|
+
|
6
|
+
class Corpus
|
7
|
+
|
8
|
+
attr_reader :app
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
# SCENARIOS
|
14
|
+
|
15
|
+
def scenarios
|
16
|
+
@scenarios ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_scenario(*args, &block)
|
20
|
+
scenario = Scenario.new(self, *args, &block)
|
21
|
+
scenarios << scenario
|
22
|
+
scenario
|
23
|
+
end
|
24
|
+
|
25
|
+
# REPOS
|
26
|
+
|
27
|
+
def repos
|
28
|
+
@repos ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_repos(*paths)
|
32
|
+
paths.each do |path|
|
33
|
+
repo = Grit::Repo.new(path)
|
34
|
+
repos[name_of(repo)] = repo
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# REFS
|
39
|
+
|
40
|
+
def refs
|
41
|
+
@refs ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_refs!
|
45
|
+
repos.each_key do |repo_name|
|
46
|
+
refs[repo_name] ||= 'master'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def verify_refs!
|
51
|
+
refs.each do |repo_name, ref|
|
52
|
+
repo = repos[repo_name]
|
53
|
+
unless repo.log(ref).any?
|
54
|
+
raise ArgumentError, "Could not find #{repo_name} ref `#{ref}'"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def session
|
60
|
+
@session ||= Session.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def schedule
|
64
|
+
@schedule ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
#######
|
68
|
+
private
|
69
|
+
#######
|
70
|
+
|
71
|
+
# Extract the canonical name of the git repository
|
72
|
+
def name_of(repo)
|
73
|
+
case repo.path
|
74
|
+
when /#{File::SEPARATOR}\.git$/
|
75
|
+
File.basename(File.dirname(repo.path))
|
76
|
+
else
|
77
|
+
File.basename(repo.path, '.git')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Rocksteady
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
|
5
|
+
def corpus
|
6
|
+
@corpus ||= Rocksteady::Corpus.new(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def repos(*paths)
|
10
|
+
corpus.add_repos(*paths.flatten)
|
11
|
+
end
|
12
|
+
|
13
|
+
def scenario(opts, &block)
|
14
|
+
title, deps = if opts.is_a?(Hash)
|
15
|
+
[opts.keys.first, Array(opts.values.first)]
|
16
|
+
else
|
17
|
+
[opts, []]
|
18
|
+
end
|
19
|
+
scenario = corpus.add_scenario(title, &block)
|
20
|
+
generate_scenario_task scenario, deps
|
21
|
+
end
|
22
|
+
|
23
|
+
#######
|
24
|
+
private
|
25
|
+
#######
|
26
|
+
|
27
|
+
# Create the scenario task
|
28
|
+
def generate_scenario_task(scenario, deps)
|
29
|
+
deps.unshift 'rocksteady:refs:check'
|
30
|
+
desc scenario.title
|
31
|
+
task "rocksteady:scenario:#{corpus.scenarios.size}" => deps do |t|
|
32
|
+
scenario.schedule!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ruport'
|
3
|
+
|
4
|
+
# FIXME: This is a total hackjob.
|
5
|
+
# TODO: Support other output types
|
6
|
+
at_exit do
|
7
|
+
corpus.session.run! do |scenarios|
|
8
|
+
table = Ruport::Data::Table.new :column_names => ['Scenario', corpus.session.title] do |t|
|
9
|
+
scenarios.sort_by { |s| s.title }.each do |scenario|
|
10
|
+
t << [scenario.title, scenario.result.is_a?(Exception) ? "FAIL" : 'PASS']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
puts table
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Rocksteady
|
2
|
+
|
3
|
+
class Scenario
|
4
|
+
|
5
|
+
attr_reader :corpus, :title, :operation
|
6
|
+
def initialize(corpus, title, &operation)
|
7
|
+
@corpus = corpus
|
8
|
+
@title = title
|
9
|
+
@operation = operation
|
10
|
+
end
|
11
|
+
|
12
|
+
def schedule!
|
13
|
+
corpus.schedule << self
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@name ||= title.gsub(/[^[:alnum:]]+/, '_').downcase
|
18
|
+
end
|
19
|
+
|
20
|
+
def result
|
21
|
+
@result ||= begin
|
22
|
+
operation.call
|
23
|
+
rescue Exception => e
|
24
|
+
e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias :run! :result
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Rocksteady
|
2
|
+
|
3
|
+
class Session
|
4
|
+
|
5
|
+
attr_reader :corpus
|
6
|
+
def initialize(corpus)
|
7
|
+
@corpus = corpus
|
8
|
+
end
|
9
|
+
|
10
|
+
def title
|
11
|
+
@title ||= corpus.refs.map { |k, v| "#{k} `#{v}'" }.join(' vs ')
|
12
|
+
end
|
13
|
+
|
14
|
+
def timestamp
|
15
|
+
@timestamp ||= Time.now.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def run!
|
19
|
+
return if corpus.schedule.empty?
|
20
|
+
create_timestamp_directory
|
21
|
+
clone_repos
|
22
|
+
corpus.schedule.each do |scenario|
|
23
|
+
focus_on scenario do
|
24
|
+
scenario.run!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
yield corpus.schedule
|
28
|
+
end
|
29
|
+
|
30
|
+
#######
|
31
|
+
private
|
32
|
+
#######
|
33
|
+
|
34
|
+
def timestamp_directory
|
35
|
+
File.expand_path("build/#{timestamp}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_timestamp_directory
|
39
|
+
mkdir_p timestamp_directory
|
40
|
+
end
|
41
|
+
|
42
|
+
# FIXME: Cleanup
|
43
|
+
def clone_repos
|
44
|
+
Dir.chdir timestamp_directory do
|
45
|
+
mkdir_p 'repos'
|
46
|
+
corpus.refs.each do |repo_name, ref|
|
47
|
+
repo = corpus.repos[repo_name]
|
48
|
+
path = File.expand_path("repos/#{repo_name}")
|
49
|
+
(class << corpus.app; self; end).send(:define_method, "#{repo_name}_path") { path }
|
50
|
+
sh "git clone -q '#{repo.path}' '#{path}'"
|
51
|
+
Dir.chdir path do
|
52
|
+
sh "git checkout -q '#{ref}'"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def focus_on(scenario, &block)
|
59
|
+
directory = directory_for(scenario)
|
60
|
+
(class << corpus.app; self; end).send(:define_method, "scenario_log") { logfile_for(scenario) }
|
61
|
+
mkdir_p directory
|
62
|
+
Dir.chdir(directory, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
#######
|
66
|
+
private
|
67
|
+
#######
|
68
|
+
|
69
|
+
def logfile_for(scenario)
|
70
|
+
File.join(directory_for(scenario), 'scenario.log')
|
71
|
+
end
|
72
|
+
|
73
|
+
def directory_for(scenario)
|
74
|
+
File.join("#{timestamp_directory}/scenarios/#{scenario.name}")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
namespace :rocksteady do
|
5
|
+
|
6
|
+
desc "Remove build files"
|
7
|
+
task :clean do
|
8
|
+
rm_rf 'build' rescue nil
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :refs do
|
12
|
+
|
13
|
+
task :check => :default do
|
14
|
+
begin
|
15
|
+
corpus.verify_refs!
|
16
|
+
rescue ArgumentError => e
|
17
|
+
abort e.message
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :add_from_env do
|
22
|
+
corpus.default_refs!
|
23
|
+
end
|
24
|
+
|
25
|
+
task :add_from_env => 'rocksteady:repos:check' do
|
26
|
+
if ENV['REFS']
|
27
|
+
pairs = ENV['REFS'].split(',').map
|
28
|
+
pairs.each do |pair|
|
29
|
+
repo_name, ref = pair.split(':')
|
30
|
+
ref ||= 'master'
|
31
|
+
if (repo = corpus.repos[repo_name])
|
32
|
+
corpus.refs[repo_name] = ref
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
namespace :repos do
|
41
|
+
|
42
|
+
desc "Show configured source repositories"
|
43
|
+
task :show => :check do
|
44
|
+
corpus.repos.sort_by { |k, v| k }.each do |name, repo|
|
45
|
+
puts "#{name}: #{repo.path}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
task :check => :add_from_env do
|
50
|
+
unless corpus.repos.any?
|
51
|
+
abort "Could not find repositories.\nSet ENV['REPOS'] or use `repos' method in Rakefile to set repo paths."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
task :add_from_env do
|
56
|
+
if ENV['REPOS']
|
57
|
+
paths = ENV['REPOS'].split(',')
|
58
|
+
corpus.add_repos(*paths)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "Run all corpus scenarios"
|
67
|
+
task :rocksteady => 'rocksteady:refs:check' do
|
68
|
+
corpus.scenarios.each do |scenario|
|
69
|
+
scenario.schedule!
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# (The MIT License)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2008 Jamis Buck <jamis@37signals.com>,
|
4
|
+
# with modifications by Bruce Williams <bruce@codefluency.com>
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# 'Software'), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
20
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
21
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
22
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
23
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
module Rocksteady
|
25
|
+
|
26
|
+
# A class for describing the current version of a library. The version
|
27
|
+
# consists of three parts: the +major+ number, the +minor+ number, and the
|
28
|
+
# +tiny+ (or +patch+) number.
|
29
|
+
class Version
|
30
|
+
|
31
|
+
include Comparable
|
32
|
+
|
33
|
+
# A convenience method for instantiating a new Version instance with the
|
34
|
+
# given +major+, +minor+, and +tiny+ components.
|
35
|
+
def self.[](major, minor, tiny)
|
36
|
+
new(major, minor, tiny)
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :major, :minor, :tiny
|
40
|
+
|
41
|
+
# Create a new Version object with the given components.
|
42
|
+
def initialize(major, minor, tiny)
|
43
|
+
@major, @minor, @tiny = major, minor, tiny
|
44
|
+
end
|
45
|
+
|
46
|
+
# Compare this version to the given +version+ object.
|
47
|
+
def <=>(version)
|
48
|
+
to_i <=> version.to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
# Converts this version object to a string, where each of the three
|
52
|
+
# version components are joined by the '.' character. E.g., 2.0.0.
|
53
|
+
def to_s
|
54
|
+
@to_s ||= [@major, @minor, @tiny].join(".")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Converts this version to a canonical integer that may be compared
|
58
|
+
# against other version objects.
|
59
|
+
def to_i
|
60
|
+
@to_i ||= @major * 1_000_000 + @minor * 1_000 + @tiny
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_a
|
64
|
+
[@major, @minor, @tiny]
|
65
|
+
end
|
66
|
+
|
67
|
+
MAJOR = 0
|
68
|
+
MINOR = 8
|
69
|
+
TINY = 0
|
70
|
+
|
71
|
+
# The current version as a Version instance
|
72
|
+
CURRENT = new(MAJOR, MINOR, TINY)
|
73
|
+
# The current version as a String
|
74
|
+
STRING = CURRENT.to_s
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
data/rocksteady.gemspec
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
# Gem::Specification for Rocksteady-0.8.0
|
3
|
+
# Originally generated by Echoe
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = %q{rocksteady}
|
7
|
+
s.version = "0.8.0"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Bruce Williams"]
|
13
|
+
s.date = %q{2008-06-07}
|
14
|
+
s.description = %q{Run arbitrary scenarios across disparate sets of git repo revisions}
|
15
|
+
s.email = %q{bruce@codefluency.com}
|
16
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/rocksteady/corpus.rb", "lib/rocksteady/helpers.rb", "lib/rocksteady/output.rb", "lib/rocksteady/scenario.rb", "lib/rocksteady/session.rb", "lib/rocksteady/tasks.rb", "lib/rocksteady/version.rb", "lib/rocksteady.rb", "README.rdoc"]
|
17
|
+
s.files = ["CHANGELOG", "lib/rocksteady/corpus.rb", "lib/rocksteady/helpers.rb", "lib/rocksteady/output.rb", "lib/rocksteady/scenario.rb", "lib/rocksteady/session.rb", "lib/rocksteady/tasks.rb", "lib/rocksteady/version.rb", "lib/rocksteady.rb", "Manifest", "Rakefile", "README.rdoc", "test/rocksteady_test.rb", "rocksteady.gemspec"]
|
18
|
+
s.has_rdoc = true
|
19
|
+
s.homepage = %q{http://github.com/bruce/rocksteady}
|
20
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rocksteady", "--main", "README.rdoc"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubyforge_project = %q{codefluency}
|
23
|
+
s.rubygems_version = %q{1.1.1}
|
24
|
+
s.summary = %q{Run arbitrary scenarios across disparate sets of git repo revisions}
|
25
|
+
s.test_files = ["test/rocksteady_test.rb"]
|
26
|
+
|
27
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
28
|
+
s.add_dependency(%q<mojombo-grit>, [">= 0.8.0"])
|
29
|
+
s.add_dependency(%q<ruport>, [">= 0"])
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# # Original Rakefile source (requires the Echoe gem):
|
34
|
+
#
|
35
|
+
# require 'rubygems'
|
36
|
+
# require 'echoe'
|
37
|
+
#
|
38
|
+
# require File.dirname(__FILE__) << "/lib/rocksteady/version"
|
39
|
+
#
|
40
|
+
# Echoe.new 'rocksteady' do |p|
|
41
|
+
# p.version = Rocksteady::Version::STRING
|
42
|
+
# p.author = "Bruce Williams"
|
43
|
+
# p.email = 'bruce@codefluency.com'
|
44
|
+
# p.project = 'codefluency'
|
45
|
+
# p.summary = "Run arbitrary scenarios across disparate sets of git repo revisions"
|
46
|
+
# p.url = "http://github.com/bruce/rocksteady"
|
47
|
+
# p.dependencies = ['rake', ['mojombo-grit', '>= 0.8.0'], 'ruport']
|
48
|
+
# p.include_rakefile = true
|
49
|
+
# end
|
50
|
+
#
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'Shoulda'
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__) << "/../lib")
|
6
|
+
require 'rocksteady/corpus'
|
7
|
+
require 'rocksteady/helpers'
|
8
|
+
|
9
|
+
class RSTarget
|
10
|
+
include Rocksteady::Helpers
|
11
|
+
end
|
12
|
+
|
13
|
+
class RocksteadyTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
context "Rocksteady" do
|
16
|
+
|
17
|
+
tmp_dir = File.dirname(__FILE__) << "/tmp"
|
18
|
+
|
19
|
+
repo_dirs = (1..3).to_a.map do |n|
|
20
|
+
"#{tmp_dir}/repo#{n}"
|
21
|
+
end
|
22
|
+
|
23
|
+
setup do
|
24
|
+
@r = RSTarget.new
|
25
|
+
repo_dirs.each do |dir|
|
26
|
+
FileUtils.mkdir_p dir
|
27
|
+
Dir.chdir(dir) do
|
28
|
+
system "git init > /dev/null 2>&1"
|
29
|
+
File.open('stub', 'w') { |f| f.puts 'nothing' }
|
30
|
+
system 'git add . > /dev/null 2>&1'
|
31
|
+
system 'git commit -m "stub" > /dev/null 2>&1'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
teardown do
|
37
|
+
FileUtils.rm_rf tmp_dir
|
38
|
+
end
|
39
|
+
|
40
|
+
populate = lambda { @r.corpus.add_repos(*Dir["#{tmp_dir}/*"]) }
|
41
|
+
|
42
|
+
context "Helpers" do
|
43
|
+
should "have corpus accessor added as a helper" do
|
44
|
+
assert @r.respond_to?(:corpus)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "Core" do
|
49
|
+
|
50
|
+
should "start without repos" do
|
51
|
+
assert @r.corpus.repos.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
should "start without refs" do
|
55
|
+
assert @r.corpus.refs.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
should "add repos by paths" do
|
59
|
+
instance_eval(&populate)
|
60
|
+
assert @r.corpus.repos.size == repo_dirs.size
|
61
|
+
assert @r.corpus.repos.keys.all? { |r| r =~ /^repo\d+$/ }
|
62
|
+
assert @r.corpus.repos.values.all? { |r| r.is_a?(Grit::Repo) }
|
63
|
+
assert @r.corpus.refs.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
should "default refs for existing repos without explicit ref" do
|
67
|
+
instance_eval(&populate)
|
68
|
+
assert @r.corpus.refs.empty?
|
69
|
+
@r.corpus.refs['repo1'] = explicit = 'explicit-ref-for-this-repo'
|
70
|
+
@r.corpus.default_refs!
|
71
|
+
masters, explicits = @r.corpus.refs.partition { |k, v| v == 'master' }
|
72
|
+
assert_equal 2, masters.size
|
73
|
+
assert_equal 1, explicits.size
|
74
|
+
end
|
75
|
+
|
76
|
+
should "allow verification of refs" do
|
77
|
+
instance_eval(&populate)
|
78
|
+
assert @r.corpus.refs.empty?
|
79
|
+
@r.corpus.default_refs!
|
80
|
+
assert_nothing_raised do
|
81
|
+
@r.corpus.verify_refs!
|
82
|
+
end
|
83
|
+
@r.corpus.refs.clear
|
84
|
+
@r.corpus.refs['repo1'] = 'this-does-not-exist'
|
85
|
+
@r.corpus.default_refs!
|
86
|
+
assert_raises ArgumentError do
|
87
|
+
@r.corpus.verify_refs!
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rocksteady
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bruce Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-06-07 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: mojombo-grit
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 0.8.0
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: ruport
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0"
|
41
|
+
version:
|
42
|
+
description: Run arbitrary scenarios across disparate sets of git repo revisions
|
43
|
+
email: bruce@codefluency.com
|
44
|
+
executables: []
|
45
|
+
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
extra_rdoc_files:
|
49
|
+
- CHANGELOG
|
50
|
+
- lib/rocksteady/corpus.rb
|
51
|
+
- lib/rocksteady/helpers.rb
|
52
|
+
- lib/rocksteady/output.rb
|
53
|
+
- lib/rocksteady/scenario.rb
|
54
|
+
- lib/rocksteady/session.rb
|
55
|
+
- lib/rocksteady/tasks.rb
|
56
|
+
- lib/rocksteady/version.rb
|
57
|
+
- lib/rocksteady.rb
|
58
|
+
- README.rdoc
|
59
|
+
files:
|
60
|
+
- CHANGELOG
|
61
|
+
- lib/rocksteady/corpus.rb
|
62
|
+
- lib/rocksteady/helpers.rb
|
63
|
+
- lib/rocksteady/output.rb
|
64
|
+
- lib/rocksteady/scenario.rb
|
65
|
+
- lib/rocksteady/session.rb
|
66
|
+
- lib/rocksteady/tasks.rb
|
67
|
+
- lib/rocksteady/version.rb
|
68
|
+
- lib/rocksteady.rb
|
69
|
+
- Manifest
|
70
|
+
- Rakefile
|
71
|
+
- README.rdoc
|
72
|
+
- test/rocksteady_test.rb
|
73
|
+
- rocksteady.gemspec
|
74
|
+
has_rdoc: true
|
75
|
+
homepage: http://github.com/bruce/rocksteady
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --line-numbers
|
79
|
+
- --inline-source
|
80
|
+
- --title
|
81
|
+
- Rocksteady
|
82
|
+
- --main
|
83
|
+
- README.rdoc
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
91
|
+
version:
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: "0"
|
97
|
+
version:
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project: codefluency
|
101
|
+
rubygems_version: 1.1.1
|
102
|
+
signing_key:
|
103
|
+
specification_version: 2
|
104
|
+
summary: Run arbitrary scenarios across disparate sets of git repo revisions
|
105
|
+
test_files:
|
106
|
+
- test/rocksteady_test.rb
|