rocksteady 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|