tack 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +5 -1
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/bin/tack +59 -0
- data/lib/tack.rb +30 -0
- data/lib/tack/adapters/adapter.rb +25 -0
- data/lib/tack/adapters/rspec_adapter.rb +106 -0
- data/lib/tack/adapters/test_unit_adapter.rb +114 -0
- data/lib/tack/formatters/basic_summary.rb +22 -0
- data/lib/tack/formatters/print_failures.rb +43 -0
- data/lib/tack/formatters/profiler.rb +39 -0
- data/lib/tack/formatters/progress_bar.rb +36 -0
- data/lib/tack/formatters/total_time.rb +23 -0
- data/lib/tack/middleware.rb +22 -0
- data/lib/tack/runner.rb +50 -0
- data/lib/tack/test_pattern.rb +21 -0
- data/lib/tack/test_set.rb +39 -0
- data/test/acceptance/rspec_test.rb +134 -0
- data/test/acceptance/test_unit_test.rb +139 -0
- data/test/tack_test.rb +4 -2
- data/test/test_helper.rb +3 -0
- metadata +35 -6
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= tack
|
2
2
|
|
3
|
-
|
3
|
+
USE AT YOUR OWN RISK. This is highly experimental and the interface is changing rapidly.
|
4
4
|
|
5
5
|
== Note on Patches/Pull Requests
|
6
6
|
|
@@ -13,6 +13,10 @@ Description goes here.
|
|
13
13
|
bump version in a commit by itself I can ignore when I pull)
|
14
14
|
* Send me a pull request. Bonus points for topic branches.
|
15
15
|
|
16
|
+
== Acknowledgements
|
17
|
+
|
18
|
+
Tack is heavily inspired by Rack and Faraday and borrows ideas and code from both. Early versions of the Test::Unit and RSpec adapters borrowed code from Hydra.
|
19
|
+
|
16
20
|
== Copyright
|
17
21
|
|
18
22
|
Copyright (c) 2010 Ben Brinckerhoff. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -10,7 +10,9 @@ begin
|
|
10
10
|
gem.email = "ben@bbrinck.com"
|
11
11
|
gem.homepage = "http://github.com/bhb/tack"
|
12
12
|
gem.authors = ["Ben Brinckerhoff"]
|
13
|
+
gem.add_dependency "test-unit", "~> 1.0" if RUBY_VERSION=~/1\.9/
|
13
14
|
gem.add_development_dependency "shoulda"
|
15
|
+
gem.add_development_dependency "test-construct"
|
14
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
17
|
end
|
16
18
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
data/bin/tack
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
|
4
|
+
require 'tack'
|
5
|
+
require 'pp'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
def require_ruby_debug
|
9
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
10
|
+
require 'ruby-debug'
|
11
|
+
end
|
12
|
+
|
13
|
+
options = {}
|
14
|
+
option_parser = OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: tack [options] [file]"
|
16
|
+
opts.on("-I","--include PATH", "specify $LOAD_PATH (may be used more than once)") do |path|
|
17
|
+
options[:include] = path.split(":")
|
18
|
+
end
|
19
|
+
opts.on("-n", "--name PATTERN", "run only tests that match pattern") do |pattern|
|
20
|
+
if pattern=~/^\/.*\/$/
|
21
|
+
options[:pattern] = Regexp.new(pattern[1..-2])
|
22
|
+
else
|
23
|
+
options[:pattern] = pattern
|
24
|
+
end
|
25
|
+
end
|
26
|
+
opts.on("-u", "--debugger", "Enable ruby-debugging.") do
|
27
|
+
require_ruby_debug
|
28
|
+
end
|
29
|
+
opts.on_tail("-h","--help", "Show this message") do
|
30
|
+
puts opts
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
args = ARGV
|
36
|
+
option_parser.parse! args
|
37
|
+
options[:paths] = ARGV
|
38
|
+
|
39
|
+
if includes = options[:include]
|
40
|
+
$LOAD_PATH.unshift *includes
|
41
|
+
end
|
42
|
+
|
43
|
+
runner = Tack::Runner.new(:root => Dir.pwd) do |runner|
|
44
|
+
runner.use Tack::Formatters::Profiler, :tests => 3
|
45
|
+
runner.use Tack::Formatters::TotalTime
|
46
|
+
runner.use Tack::Formatters::PrintFailures
|
47
|
+
runner.use Tack::Formatters::BasicSummary
|
48
|
+
runner.use Tack::Formatters::ProgressBar
|
49
|
+
end
|
50
|
+
|
51
|
+
set = Tack::TestSet.new(Dir.pwd)
|
52
|
+
tests = set.tests_for(options[:paths], Tack::TestPattern.new(options[:pattern]))
|
53
|
+
|
54
|
+
runner.run(tests)
|
55
|
+
|
56
|
+
exit 0
|
57
|
+
|
58
|
+
|
59
|
+
|
data/lib/tack.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
+
|
4
|
+
module Tack
|
5
|
+
|
6
|
+
autoload :Runner, 'tack/runner'
|
7
|
+
autoload :TestSet, 'tack/test_set'
|
8
|
+
autoload :Middleware, 'tack/middleware'
|
9
|
+
autoload :TestPattern, 'tack/test_pattern'
|
10
|
+
|
11
|
+
|
12
|
+
module Adapters
|
13
|
+
|
14
|
+
autoload :Adapter, 'tack/adapters/adapter'
|
15
|
+
autoload :RSpecAdapter, 'tack/adapters/rspec_adapter'
|
16
|
+
autoload :TestUnitAdapter, 'tack/adapters/test_unit_adapter'
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module Formatters
|
21
|
+
|
22
|
+
autoload :BasicSummary, 'tack/formatters/basic_summary'
|
23
|
+
autoload :ProgressBar, 'tack/formatters/progress_bar'
|
24
|
+
autoload :Profiler, 'tack/formatters/profiler'
|
25
|
+
autoload :TotalTime, 'tack/formatters/total_time'
|
26
|
+
autoload :PrintFailures, 'tack/formatters/print_failures'
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Adapters
|
4
|
+
|
5
|
+
class Adapter
|
6
|
+
|
7
|
+
def self.for(path)
|
8
|
+
# Using a simple path-based heuristic for now
|
9
|
+
case path
|
10
|
+
when /test.rb$/
|
11
|
+
TestUnitAdapter.new
|
12
|
+
when /spec.rb$/
|
13
|
+
RSpecAdapter.new
|
14
|
+
else
|
15
|
+
raise "Cannot determine an adapter for path #{path}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'spec/runner/formatter/base_formatter'
|
3
|
+
|
4
|
+
if defined?(Spec)
|
5
|
+
module Spec
|
6
|
+
module Runner
|
7
|
+
class << self
|
8
|
+
# stop the auto-run at_exit
|
9
|
+
def run
|
10
|
+
return 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Spec
|
18
|
+
module Runner
|
19
|
+
module Formatter
|
20
|
+
# Stolen from Hydra for now
|
21
|
+
class TackFormatter < BaseFormatter
|
22
|
+
|
23
|
+
attr_accessor :results
|
24
|
+
|
25
|
+
def initialize(options)
|
26
|
+
io = StringIO.new # suppress output
|
27
|
+
super(options, io)
|
28
|
+
@results = { :passed => [],
|
29
|
+
:failed => [],
|
30
|
+
:pending => []}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Stifle the output of pending examples
|
34
|
+
def example_pending(example)
|
35
|
+
@results[:pending] << {
|
36
|
+
:description => example.description,
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def example_passed(example)
|
41
|
+
@results[:passed] << {
|
42
|
+
:description => example.description,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def example_failed(example, counter, error=nil)
|
47
|
+
@results[:failed] <<
|
48
|
+
{
|
49
|
+
:description => example.description,
|
50
|
+
:failure => build_failure(example, error)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def build_failure(example, error)
|
57
|
+
case error.exception
|
58
|
+
when Spec::Expectations::ExpectationNotMetError
|
59
|
+
{ :message => error.exception.message,
|
60
|
+
:backtrace => error.exception.backtrace}
|
61
|
+
else
|
62
|
+
{ :message => "#{error.exception.class} was raised: #{error.exception.message}",
|
63
|
+
:backtrace => error.exception.backtrace}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module Tack
|
73
|
+
|
74
|
+
module Adapters
|
75
|
+
|
76
|
+
class RSpecAdapter
|
77
|
+
|
78
|
+
def tests_for(file, pattern)
|
79
|
+
Spec::Runner.options.instance_variable_set(:@formatters, [Spec::Runner::Formatter::TackFormatter.new(Spec::Runner.options.formatter_options)])
|
80
|
+
Spec::Runner.options.instance_variable_set(:@example_groups, [])
|
81
|
+
Spec::Runner.options.instance_variable_set(:@files, [file])
|
82
|
+
Spec::Runner.options.instance_variable_set(:@files_loaded, false)
|
83
|
+
runner = Spec::Runner::ExampleGroupRunner.new(Spec::Runner.options)
|
84
|
+
runner.load_files([file])
|
85
|
+
example_groups = runner.send(:example_groups)
|
86
|
+
examples = example_groups.inject([]) do |arr, group|
|
87
|
+
arr += group.examples
|
88
|
+
end
|
89
|
+
examples.map {|example| [file, example.description]}.select {|file,description| description.match(pattern)}
|
90
|
+
end
|
91
|
+
|
92
|
+
def run(file, test)
|
93
|
+
Spec::Runner.options.instance_variable_set(:@examples, [test])
|
94
|
+
Spec::Runner.options.instance_variable_set(:@example_groups, [])
|
95
|
+
Spec::Runner.options.instance_variable_set(:@files, [file])
|
96
|
+
Spec::Runner.options.instance_variable_set(:@files_loaded, false)
|
97
|
+
formatter = Spec::Runner::Formatter::TackFormatter.new(Spec::Runner.options.formatter_options)
|
98
|
+
Spec::Runner.options.instance_variable_set(:@formatters, [formatter])
|
99
|
+
Spec::Runner.options.run_examples
|
100
|
+
formatter.results
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
if RUBY_VERSION=~/1.9/
|
2
|
+
gem 'test-unit', '~> 1.0'
|
3
|
+
end
|
4
|
+
require 'test/unit'
|
5
|
+
require 'test/unit/testresult'
|
6
|
+
|
7
|
+
Test::Unit.run = true
|
8
|
+
|
9
|
+
module Tack
|
10
|
+
|
11
|
+
module Adapters
|
12
|
+
|
13
|
+
class TestUnitAdapter
|
14
|
+
|
15
|
+
def tests_for(file, pattern)
|
16
|
+
require file
|
17
|
+
classes = test_classes_for(file)
|
18
|
+
classes.inject([]) do |tests, klass|
|
19
|
+
tests += test_methods(klass).map {|method_name| [file, method_name.to_s]}.select {|file, method_name| method_name.match(pattern)}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(path, description)
|
24
|
+
results = { :passed => [],
|
25
|
+
:failed => [],
|
26
|
+
:pending => []}
|
27
|
+
require(path)
|
28
|
+
# Note that this won't work if there are multiple classes in a file
|
29
|
+
klass = test_classes_for(path).first
|
30
|
+
test = klass.new(description)
|
31
|
+
result = Test::Unit::TestResult.new
|
32
|
+
|
33
|
+
result.add_listener(Test::Unit::TestResult::FAULT) do |failure|
|
34
|
+
results[:failed] << build_result(description, failure)
|
35
|
+
end
|
36
|
+
|
37
|
+
test.run(result) do |started,name|
|
38
|
+
# We do nothing here
|
39
|
+
# but this method requires a block
|
40
|
+
end
|
41
|
+
if result.passed?
|
42
|
+
results[:passed] << build_result(description)
|
43
|
+
end
|
44
|
+
results
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def build_result(description, failure=nil)
|
50
|
+
{ :description => description,
|
51
|
+
:failure => build_failure(failure) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_failure(failure)
|
55
|
+
return {} if failure.nil?
|
56
|
+
case failure
|
57
|
+
when Test::Unit::Error
|
58
|
+
{ :message => "#{failure.exception.class} was raised: #{failure.exception.message}",
|
59
|
+
:backtrace => failure.exception.backtrace }
|
60
|
+
else
|
61
|
+
{ :message => failure.message,
|
62
|
+
:backtrace => failure.location }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_classes_for(file)
|
67
|
+
# taken from from hydra
|
68
|
+
#code = ""
|
69
|
+
# File.open(file) {|buffer| code = buffer.read}
|
70
|
+
code = File.read(file)
|
71
|
+
matches = code.scan(/class\s+([\S]+)/)
|
72
|
+
klasses = matches.collect do |c|
|
73
|
+
begin
|
74
|
+
if c.first.respond_to? :constantize
|
75
|
+
c.first.constantize
|
76
|
+
else
|
77
|
+
eval(c.first)
|
78
|
+
end
|
79
|
+
rescue NameError
|
80
|
+
# means we could not load [c.first], but thats ok, its just not
|
81
|
+
# one of the classes we want to test
|
82
|
+
nil
|
83
|
+
rescue SyntaxError
|
84
|
+
# see above
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
return klasses.select{|k| k.respond_to? 'suite'}
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_methods(test_class)
|
92
|
+
test_class.instance_methods.select do |method_name|
|
93
|
+
method_name =~ /^test./ &&
|
94
|
+
(test_class.instance_method(method_name).arity == 0 ||
|
95
|
+
test_class.instance_method(method_name).arity == -1
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_test_classes
|
101
|
+
test_classes = []
|
102
|
+
ObjectSpace.each_object(Class) do |klass|
|
103
|
+
if(Test::Unit::TestCase > klass)
|
104
|
+
test_classes << klass
|
105
|
+
end
|
106
|
+
end
|
107
|
+
test_classes
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Formatters
|
4
|
+
|
5
|
+
class BasicSummary
|
6
|
+
include Middleware
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_suite(tests)
|
13
|
+
returning @app.run_suite(tests) do |results|
|
14
|
+
puts "%d tests, %d failures, %d pending" % [results.values.flatten.length, results[:failed].length, results[:pending].length]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Formatters
|
4
|
+
|
5
|
+
class PrintFailures
|
6
|
+
include Middleware
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_suite(tests)
|
13
|
+
returning @app.run_suite(tests) do |results|
|
14
|
+
results[:failed].each_with_index do |result, index|
|
15
|
+
print_failure(index+1, result)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def format_backtrace(backtrace)
|
23
|
+
return "" if backtrace.nil?
|
24
|
+
"["+backtrace.map { |line| backtrace_line(line) }.join("\n")+"]:"
|
25
|
+
end
|
26
|
+
|
27
|
+
def backtrace_line(line)
|
28
|
+
line.sub(/\A([^:]+:\d+)$/, '\\1:')
|
29
|
+
end
|
30
|
+
|
31
|
+
def print_failure(counter, result)
|
32
|
+
puts
|
33
|
+
puts "#{counter.to_s})"
|
34
|
+
puts result[:description]
|
35
|
+
puts format_backtrace(result[:failure][:backtrace])
|
36
|
+
puts result[:failure][:message]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Formatters
|
4
|
+
|
5
|
+
class Profiler
|
6
|
+
include Middleware
|
7
|
+
|
8
|
+
def initialize(app, args)
|
9
|
+
@app = app
|
10
|
+
@num_tests = args.fetch(:tests) { 10 }
|
11
|
+
@times = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_suite(tests)
|
15
|
+
returning @app.run_suite(tests) do |results|
|
16
|
+
puts "\n\nTop #{@num_tests} slowest examples:\n"
|
17
|
+
@times = @times.sort_by do |description, time|
|
18
|
+
time
|
19
|
+
end.reverse
|
20
|
+
@times[0..@num_tests-1].each do |description, time|
|
21
|
+
print "%.7f" % time
|
22
|
+
puts " #{description}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_test(file, description)
|
28
|
+
time = Time.now
|
29
|
+
returning @app.run_test(file,description) do
|
30
|
+
@times << [description, Time.now - time]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Formatters
|
4
|
+
|
5
|
+
class ProgressBar
|
6
|
+
include Middleware
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_suite(tests)
|
13
|
+
returning @app.run_suite(tests) do
|
14
|
+
puts
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_test(file, description)
|
19
|
+
returning @app.run_test(file, description) do |result|
|
20
|
+
result[:passed].each do
|
21
|
+
print "."
|
22
|
+
end
|
23
|
+
result[:pending].each do
|
24
|
+
print "P"
|
25
|
+
end
|
26
|
+
result[:failed].each do
|
27
|
+
print "F"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Formatters
|
4
|
+
|
5
|
+
class TotalTime
|
6
|
+
include Middleware
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_suite(tests)
|
13
|
+
time = Time.now
|
14
|
+
returning @app.run_suite(tests) do
|
15
|
+
puts "Finished in %.7f seconds." % (Time.now - time)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
module Middleware
|
4
|
+
|
5
|
+
def run_suite(tests)
|
6
|
+
@app.run_suite(tests)
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_test(file, description)
|
10
|
+
@app.run_test(file, description)
|
11
|
+
end
|
12
|
+
|
13
|
+
# not necessary for the middleware API, but handy for implementing
|
14
|
+
# middleware methods
|
15
|
+
def returning(value)
|
16
|
+
yield(value)
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/tack/runner.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
class Runner
|
4
|
+
|
5
|
+
def initialize(args)
|
6
|
+
if(args.is_a?(Hash))
|
7
|
+
@root_dir = args.fetch(:root)
|
8
|
+
else
|
9
|
+
@root_dir = args
|
10
|
+
end
|
11
|
+
@handlers = []
|
12
|
+
yield self if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(tests)
|
16
|
+
to_app if @start_app.nil?
|
17
|
+
@start_app.run_suite(tests)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_suite(tests)
|
21
|
+
results = { :passed => [],
|
22
|
+
:failed => [],
|
23
|
+
:pending => []}
|
24
|
+
tests.each do |path, description|
|
25
|
+
result = @start_app.run_test(path, description)
|
26
|
+
results[:passed] += result[:passed]
|
27
|
+
results[:failed] += result[:failed]
|
28
|
+
results[:pending] += result[:pending]
|
29
|
+
end
|
30
|
+
results
|
31
|
+
end
|
32
|
+
|
33
|
+
def run_test(path, description)
|
34
|
+
adapter = Adapters::Adapter.for(path)
|
35
|
+
adapter.run(path, description)
|
36
|
+
end
|
37
|
+
|
38
|
+
def use(middleware, *args, &block)
|
39
|
+
@handlers << lambda { |app|
|
40
|
+
middleware.new(app, *args, &block) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_app
|
44
|
+
inner_app = self
|
45
|
+
@start_app = @handlers.reverse.inject(inner_app) { |a, e| e.call(a) }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
class TestPattern < Regexp
|
4
|
+
|
5
|
+
DEFAULT = /.*/
|
6
|
+
|
7
|
+
def initialize(pattern=nil)
|
8
|
+
pattern = case pattern
|
9
|
+
when nil
|
10
|
+
DEFAULT
|
11
|
+
when String, Regexp
|
12
|
+
pattern
|
13
|
+
else
|
14
|
+
DEFAULT
|
15
|
+
end
|
16
|
+
super(pattern)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Tack
|
2
|
+
|
3
|
+
class TestSet
|
4
|
+
|
5
|
+
def initialize(root_dir)
|
6
|
+
@root_dir = root_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def tests_for(paths, pattern=TestPattern.new)
|
10
|
+
paths = Array(paths).map { |path| path.to_s}
|
11
|
+
files = paths.inject([]) do |files, path|
|
12
|
+
if File.directory?(path)
|
13
|
+
files += Dir[File.join(path,"**/*")].select {|f| valid_test_file?(f)}
|
14
|
+
else
|
15
|
+
files << path
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
files.inject([]) do |tests, file|
|
20
|
+
adapter = Adapters::Adapter.for(file)
|
21
|
+
tests += adapter.tests_for(file, pattern)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def valid_test_file?(path)
|
28
|
+
return false if File.directory?(path)
|
29
|
+
case path
|
30
|
+
when /_test.rb$/, /_spec.rb$/
|
31
|
+
true
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RSpecTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def with_rspec_context(args)
|
6
|
+
body = args.fetch(:body)
|
7
|
+
describe = args.fetch(:describe)
|
8
|
+
within_construct(false) do |c|
|
9
|
+
file_name = 'fake_spec.rb'
|
10
|
+
c.file file_name do
|
11
|
+
<<-EOS
|
12
|
+
describe #{describe} do
|
13
|
+
|
14
|
+
#{body}
|
15
|
+
|
16
|
+
end
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
path = c+file_name.to_s
|
20
|
+
yield path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
should "grab all specs" do
|
25
|
+
body = <<-EOS
|
26
|
+
specify "something" do
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should do something" do
|
30
|
+
end
|
31
|
+
EOS
|
32
|
+
with_rspec_context :describe => String, :body => body do |path|
|
33
|
+
set = Tack::TestSet.new(path.parent)
|
34
|
+
tests = set.tests_for(path)
|
35
|
+
assert_equal 2, tests.length
|
36
|
+
assert_equal [path.to_s, "something"], tests.sort.last
|
37
|
+
assert_equal [path.to_s, "should do something"], tests.sort.first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
should "find specs that match substring" do
|
42
|
+
body = <<-EOS
|
43
|
+
specify "something" do
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does nothing" do
|
47
|
+
end
|
48
|
+
EOS
|
49
|
+
with_rspec_context :describe => String, :body => body do |path|
|
50
|
+
set = Tack::TestSet.new(path.parent)
|
51
|
+
tests = set.tests_for(path, "some")
|
52
|
+
assert_equal 1, tests.length
|
53
|
+
assert_equal [path.to_s, "something"], tests.sort.first
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
should "find specs that match regular expression" do
|
58
|
+
body = <<-EOS
|
59
|
+
specify "something" do
|
60
|
+
end
|
61
|
+
|
62
|
+
it "does nothing" do
|
63
|
+
end
|
64
|
+
EOS
|
65
|
+
with_rspec_context :describe => String, :body => body do |path|
|
66
|
+
set = Tack::TestSet.new(path.parent)
|
67
|
+
tests = set.tests_for(path, /does/)
|
68
|
+
assert_equal 1, tests.length
|
69
|
+
assert_equal [path.to_s, "does nothing"], tests.sort.first
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
should "run failing spec" do
|
74
|
+
body = <<-EOS
|
75
|
+
specify "append length is sum of component string lengths" do
|
76
|
+
("ab"+"cd").length.should == ("ab".length - "cd".length)
|
77
|
+
end
|
78
|
+
EOS
|
79
|
+
with_rspec_context :describe => String, :body => body do |path|
|
80
|
+
set = Tack::TestSet.new(path.parent)
|
81
|
+
tests = set.tests_for(path)
|
82
|
+
runner = Tack::Runner.new(path.parent)
|
83
|
+
results = runner.run(tests)
|
84
|
+
|
85
|
+
assert_equal 0, results[:passed].length
|
86
|
+
assert_equal 1, results[:failed].length
|
87
|
+
result = results[:failed].first
|
88
|
+
assert_equal "append length is sum of component string lengths", result[:description]
|
89
|
+
assert_equal "expected: 0,\n got: 4 (using ==)", result[:failure][:message]
|
90
|
+
assert_kind_of Array, result[:failure][:backtrace]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
should "run spec that raises error" do
|
95
|
+
body = <<-EOS
|
96
|
+
specify "append length is sum of component string lengths" do
|
97
|
+
raise "failing!"
|
98
|
+
end
|
99
|
+
EOS
|
100
|
+
with_rspec_context :describe => String, :body => body do |path|
|
101
|
+
set = Tack::TestSet.new(path.parent)
|
102
|
+
tests = set.tests_for(path)
|
103
|
+
runner = Tack::Runner.new(path.parent)
|
104
|
+
results = runner.run(tests)
|
105
|
+
|
106
|
+
assert_equal 0, results[:passed].length
|
107
|
+
assert_equal 1, results[:failed].length
|
108
|
+
|
109
|
+
result = results[:failed].first
|
110
|
+
assert_equal "append length is sum of component string lengths", result[:description]
|
111
|
+
assert_match /was raised/, result[:failure][:message]
|
112
|
+
assert_kind_of Array, result[:failure][:backtrace]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
should "run successful spec" do
|
117
|
+
body = <<-EOS
|
118
|
+
specify "append length is sum of component string lengths" do
|
119
|
+
("ab"+"cd").length.should == ("ab".length + "cd".length)
|
120
|
+
end
|
121
|
+
EOS
|
122
|
+
with_rspec_context :describe => String, :body => body do |path|
|
123
|
+
set = Tack::TestSet.new(path.parent)
|
124
|
+
tests = set.tests_for(path)
|
125
|
+
runner = Tack::Runner.new(path.parent)
|
126
|
+
results = runner.run(tests)
|
127
|
+
|
128
|
+
assert_equal 1, results[:passed].length
|
129
|
+
assert_equal 0, results[:failed].length
|
130
|
+
assert_equal "append length is sum of component string lengths", results[:passed].first[:description]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestUnitTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def remove_test_class_definition(class_name)
|
6
|
+
Object.send(:remove_const, class_name) if Object.const_defined?(class_name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def with_test_class(args)
|
10
|
+
body = args.fetch(:body)
|
11
|
+
class_name = args.fetch(:class_name) { :FakeTest }
|
12
|
+
within_construct(false) do |c|
|
13
|
+
begin
|
14
|
+
file = c.file 'fake_test.rb' do
|
15
|
+
<<-EOS
|
16
|
+
require 'test/unit'
|
17
|
+
|
18
|
+
class #{class_name} < Test::Unit::TestCase
|
19
|
+
|
20
|
+
#{body}
|
21
|
+
|
22
|
+
end
|
23
|
+
EOS
|
24
|
+
end
|
25
|
+
path = c + file.to_s
|
26
|
+
yield file.to_s, path
|
27
|
+
ensure
|
28
|
+
remove_test_class_definition(class_name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should "grab all tests" do
|
34
|
+
body =<<-EOS
|
35
|
+
def test_one
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_two
|
39
|
+
end
|
40
|
+
EOS
|
41
|
+
with_test_class(:body => body) do |file_name, path|
|
42
|
+
set = Tack::TestSet.new(path.parent)
|
43
|
+
tests = set.tests_for(path)
|
44
|
+
assert_equal 2, tests.length
|
45
|
+
assert_equal [file_name, "test_one"], tests.sort.first
|
46
|
+
assert_equal [file_name, "test_two"], tests.sort.last
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
should "find tests that match substring" do
|
51
|
+
body=<<-EOS
|
52
|
+
def test_one
|
53
|
+
end
|
54
|
+
def test_two
|
55
|
+
end
|
56
|
+
EOS
|
57
|
+
with_test_class(:body => body) do |file_name, path|
|
58
|
+
set = Tack::TestSet.new(path.parent)
|
59
|
+
tests = set.tests_for(path, "two")
|
60
|
+
assert_equal 1, tests.length
|
61
|
+
assert_equal [file_name, "test_two"], tests.sort.first
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
should "find tests that match regular expression" do
|
66
|
+
body=<<-EOS
|
67
|
+
def test_one
|
68
|
+
end
|
69
|
+
def test_two
|
70
|
+
end
|
71
|
+
EOS
|
72
|
+
with_test_class(:body => body) do |file_name, path|
|
73
|
+
set = Tack::TestSet.new(path.parent)
|
74
|
+
tests = set.tests_for(path, /two/)
|
75
|
+
assert_equal 1, tests.length
|
76
|
+
assert_equal [file_name, "test_two"], tests.sort.first
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
should "run failing test" do
|
81
|
+
body =<<-EOS
|
82
|
+
def test_append_length
|
83
|
+
assert_equal ("ab".length - "cd".length), ("ab"+"cd").length
|
84
|
+
end
|
85
|
+
EOS
|
86
|
+
with_test_class(:body => body) do |file_name, path|
|
87
|
+
set = Tack::TestSet.new(path.parent)
|
88
|
+
tests = set.tests_for(path)
|
89
|
+
runner = Tack::Runner.new(path.parent)
|
90
|
+
results = runner.run(tests)
|
91
|
+
|
92
|
+
assert_equal 0, results[:passed].length
|
93
|
+
assert_equal 1, results[:failed].length
|
94
|
+
result = results[:failed].first
|
95
|
+
assert_equal "test_append_length", result[:description]
|
96
|
+
assert_match /expected but was/, result[:failure][:message]
|
97
|
+
assert_kind_of Array, result[:failure][:backtrace]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
should "run test with error" do
|
102
|
+
body =<<-EOS
|
103
|
+
def test_append_length
|
104
|
+
raise "failing!"
|
105
|
+
end
|
106
|
+
EOS
|
107
|
+
with_test_class(:body => body) do |file_name, path|
|
108
|
+
set = Tack::TestSet.new(path.parent)
|
109
|
+
tests = set.tests_for(path)
|
110
|
+
runner = Tack::Runner.new(path.parent)
|
111
|
+
results = runner.run(tests)
|
112
|
+
|
113
|
+
assert_equal 0, results[:passed].length
|
114
|
+
assert_equal 1, results[:failed].length
|
115
|
+
result = results[:failed].first
|
116
|
+
assert_equal "test_append_length", result[:description]
|
117
|
+
assert_match /was raised/, result[:failure][:message]
|
118
|
+
assert_kind_of Array, result[:failure][:backtrace]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
should "run successful test" do
|
123
|
+
body =<<-EOS
|
124
|
+
def test_append_length
|
125
|
+
assert_equal ("ab".length + "cd".length), ("ab"+"cd").length
|
126
|
+
end
|
127
|
+
EOS
|
128
|
+
with_test_class(:body => body) do |file_name, path|
|
129
|
+
set = Tack::TestSet.new(path.parent)
|
130
|
+
tests = set.tests_for(path)
|
131
|
+
runner = Tack::Runner.new(path.parent)
|
132
|
+
results = runner.run(tests)
|
133
|
+
assert_equal 1, results[:passed].length
|
134
|
+
assert_equal 0, results[:failed].length
|
135
|
+
assert_equal "test_append_length", results[:passed].first[:description]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
data/test/tack_test.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class TackTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
|
4
|
+
|
5
|
+
should "do whatever" do
|
6
|
+
# nothing here for now
|
6
7
|
end
|
8
|
+
|
7
9
|
end
|
data/test/test_helper.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
3
|
require 'shoulda'
|
4
|
+
require 'construct'
|
5
|
+
require 'ruby-debug'
|
4
6
|
|
5
7
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
6
8
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
7
9
|
require 'tack'
|
8
10
|
|
9
11
|
class Test::Unit::TestCase
|
12
|
+
include Construct::Helpers
|
10
13
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Ben Brinckerhoff
|
@@ -14,8 +14,8 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
18
|
-
default_executable:
|
17
|
+
date: 2010-06-07 00:00:00 -06:00
|
18
|
+
default_executable: tack
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: shoulda
|
@@ -29,10 +29,22 @@ dependencies:
|
|
29
29
|
version: "0"
|
30
30
|
type: :development
|
31
31
|
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: test-construct
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :development
|
43
|
+
version_requirements: *id002
|
32
44
|
description: A Rack-inspired interface for testing libraries
|
33
45
|
email: ben@bbrinck.com
|
34
|
-
executables:
|
35
|
-
|
46
|
+
executables:
|
47
|
+
- tack
|
36
48
|
extensions: []
|
37
49
|
|
38
50
|
extra_rdoc_files:
|
@@ -45,7 +57,22 @@ files:
|
|
45
57
|
- README.rdoc
|
46
58
|
- Rakefile
|
47
59
|
- VERSION
|
60
|
+
- bin/tack
|
48
61
|
- lib/tack.rb
|
62
|
+
- lib/tack/adapters/adapter.rb
|
63
|
+
- lib/tack/adapters/rspec_adapter.rb
|
64
|
+
- lib/tack/adapters/test_unit_adapter.rb
|
65
|
+
- lib/tack/formatters/basic_summary.rb
|
66
|
+
- lib/tack/formatters/print_failures.rb
|
67
|
+
- lib/tack/formatters/profiler.rb
|
68
|
+
- lib/tack/formatters/progress_bar.rb
|
69
|
+
- lib/tack/formatters/total_time.rb
|
70
|
+
- lib/tack/middleware.rb
|
71
|
+
- lib/tack/runner.rb
|
72
|
+
- lib/tack/test_pattern.rb
|
73
|
+
- lib/tack/test_set.rb
|
74
|
+
- test/acceptance/rspec_test.rb
|
75
|
+
- test/acceptance/test_unit_test.rb
|
49
76
|
- test/tack_test.rb
|
50
77
|
- test/test_helper.rb
|
51
78
|
has_rdoc: true
|
@@ -79,5 +106,7 @@ signing_key:
|
|
79
106
|
specification_version: 3
|
80
107
|
summary: A Rack-inspired interface for testing libraries
|
81
108
|
test_files:
|
109
|
+
- test/acceptance/rspec_test.rb
|
110
|
+
- test/acceptance/test_unit_test.rb
|
82
111
|
- test/tack_test.rb
|
83
112
|
- test/test_helper.rb
|