thumblemonks-protest 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/protest.rb +16 -25
- data/lib/protest/assertion.rb +6 -8
- data/lib/protest/context.rb +15 -19
- data/lib/protest/macros.rb +1 -1
- data/lib/protest/report.rb +64 -0
- data/protest.gemspec +2 -1
- data/test/context_test.rb +19 -14
- metadata +2 -1
data/lib/protest.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'protest/report'
|
1
2
|
require 'protest/context'
|
2
3
|
require 'protest/assertion'
|
3
4
|
require 'protest/macros'
|
4
5
|
|
5
6
|
module Protest
|
7
|
+
#
|
8
|
+
# Initializing logic
|
6
9
|
def self.contexts
|
7
10
|
@contexts ||= []
|
8
11
|
end
|
@@ -17,40 +20,28 @@ module Protest
|
|
17
20
|
contexts.delete(context)
|
18
21
|
end
|
19
22
|
|
20
|
-
def self.run(
|
21
|
-
|
22
|
-
start
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
counter = 0
|
28
|
-
@contexts.each do |context|
|
29
|
-
context.failures.each do |failure|
|
30
|
-
counter += 1
|
31
|
-
message = ["##{counter} - #{context} asserted #{failure}"]
|
32
|
-
message += failure.backtrace
|
33
|
-
writer.puts message.join("\n") + "\n\n"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
assertions = @contexts.inject(0) { |acc, context| acc + context.assertions.length }
|
37
|
-
writer.puts "#{@contexts.length} contexts, #{assertions} assertions: #{"%0.6f" % running_time} seconds"
|
23
|
+
def self.run(report=nil)
|
24
|
+
report ||= TextReport.new
|
25
|
+
report.start
|
26
|
+
@contexts.each { |context| context.run(report) }
|
27
|
+
report.stop
|
28
|
+
report.results
|
29
|
+
at_exit { exit false unless report.passed? }
|
38
30
|
end
|
39
31
|
|
40
32
|
#
|
41
|
-
#
|
33
|
+
# Exception
|
42
34
|
|
43
35
|
class Failure < Exception
|
44
|
-
|
36
|
+
attr_reader :assertion
|
37
|
+
def initialize(message, assertion)
|
38
|
+
super(message)
|
45
39
|
@assertion = assertion
|
46
|
-
self
|
47
40
|
end
|
48
|
-
|
49
|
-
def to_s; "#{@assertion}: #{super}"; end
|
50
41
|
end
|
51
42
|
class Error < Failure
|
52
|
-
def initialize(message, e)
|
53
|
-
super(message)
|
43
|
+
def initialize(message, assertion, e)
|
44
|
+
super(message, assertion)
|
54
45
|
set_backtrace(e.backtrace)
|
55
46
|
end
|
56
47
|
end
|
data/lib/protest/assertion.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Protest
|
2
|
+
|
2
3
|
class Assertion
|
3
4
|
def initialize(description, &block)
|
4
5
|
@description = description
|
@@ -8,18 +9,14 @@ module Protest
|
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
11
|
-
def to_s; @description; end
|
12
|
-
|
13
12
|
def assert_block(&block)
|
14
13
|
@block = block if block_given?
|
15
14
|
self
|
16
15
|
end
|
17
16
|
|
18
|
-
def run(binding_scope)
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def failure(message) raise(Protest::Failure, message); end
|
17
|
+
def run(binding_scope) @block.call(binding_scope); end
|
18
|
+
def failure(message) raise Protest::Failure.new(message, self); end
|
19
|
+
def to_s; @description; end
|
23
20
|
end # Assertion
|
24
21
|
|
25
22
|
class Denial < Assertion
|
@@ -30,5 +27,6 @@ module Protest
|
|
30
27
|
else
|
31
28
|
failure("expected to fail, but did not")
|
32
29
|
end
|
33
|
-
end
|
30
|
+
end # Denial
|
31
|
+
|
34
32
|
end # Protest
|
data/lib/protest/context.rb
CHANGED
@@ -1,47 +1,43 @@
|
|
1
1
|
module Protest
|
2
2
|
class Context
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :description, :assertions
|
4
4
|
def initialize(description, parent=nil)
|
5
5
|
@description = description
|
6
6
|
@assertions = []
|
7
|
-
@failures = []
|
8
7
|
@parent = parent
|
9
8
|
@setup = nil
|
9
|
+
bootstrap(self)
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
def context(description, &block)
|
17
|
-
Protest.context(description, self, &block)
|
12
|
+
def bootstrap(binder)
|
13
|
+
@parent.bootstrap(binder) if @parent
|
14
|
+
binder.instance_eval(&@setup) if @setup
|
18
15
|
end
|
19
16
|
|
20
17
|
def setup(&block)
|
21
18
|
@setup = block
|
22
|
-
self.
|
19
|
+
self.instance_eval(&block)
|
23
20
|
end
|
24
21
|
|
22
|
+
def to_s; @to_s ||= [@parent.to_s, @description].join(' ').strip; end
|
23
|
+
def context(description, &block) Protest.context(description, self, &block); end
|
24
|
+
|
25
25
|
def asserts(description, &block) new_assertion(Assertion, description, &block); end
|
26
26
|
def denies(description, &block) new_assertion(Denial, description, &block); end
|
27
27
|
|
28
|
-
def run(
|
28
|
+
def run(report)
|
29
29
|
assertions.each do |assertion|
|
30
30
|
begin
|
31
|
-
assertion.run(self)
|
32
|
-
writer.print '.'
|
31
|
+
result = assertion.run(self)
|
33
32
|
rescue Protest::Failure => e
|
34
|
-
|
33
|
+
result = e
|
35
34
|
rescue Exception => e
|
36
|
-
|
35
|
+
result = Protest::Error.new("errored with #{e}", assertion, e)
|
36
|
+
ensure
|
37
|
+
report.record self, result
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
40
|
-
|
41
|
-
def bootstrap(binder)
|
42
|
-
@parent.bootstrap(binder) if @parent
|
43
|
-
binder.instance_eval(&@setup) if @setup
|
44
|
-
end
|
45
41
|
private
|
46
42
|
def new_assertion(klass, description, &block)
|
47
43
|
(assertions << klass.new(description, &block)).last
|
data/lib/protest/macros.rb
CHANGED
@@ -27,7 +27,7 @@ module Protest
|
|
27
27
|
expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
|
28
28
|
assert_block do |scope|
|
29
29
|
actual = scope.instance_eval(&block)
|
30
|
-
actual =~ expected || failure("expected #{expected.inspect}
|
30
|
+
actual =~ expected || failure("expected #{expected.inspect} to match #{actual.inspect}")
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end # AssertionMacros
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Protest
|
2
|
+
class Report
|
3
|
+
attr_reader :bad_results, :passes, :failures, :errors
|
4
|
+
def initialize
|
5
|
+
@bad_results = []
|
6
|
+
@passes, @failures, @errors = 0, 0 ,0
|
7
|
+
end
|
8
|
+
|
9
|
+
def passed?; failures + errors == 0; end
|
10
|
+
def assertions; passes + failures + errors; end
|
11
|
+
|
12
|
+
def start; @start = Time.now; end
|
13
|
+
def stop; @stop = Time.now; end
|
14
|
+
def time_taken; @stop - @start; end
|
15
|
+
|
16
|
+
def record(context, object)
|
17
|
+
case object
|
18
|
+
when Protest::Error then
|
19
|
+
@bad_results << [context, object]
|
20
|
+
@errors += 1
|
21
|
+
errored
|
22
|
+
when Protest::Failure then
|
23
|
+
@bad_results << [context, object]
|
24
|
+
@failures += 1
|
25
|
+
failed
|
26
|
+
else
|
27
|
+
@passes += 1
|
28
|
+
passed
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end # Report
|
32
|
+
|
33
|
+
class TextReport < Report
|
34
|
+
def initialize(writer=nil)
|
35
|
+
super()
|
36
|
+
@writer ||= STDOUT
|
37
|
+
end
|
38
|
+
|
39
|
+
def passed; @writer.print('.'); end
|
40
|
+
def failed; @writer.print('F'); end
|
41
|
+
def errored; @writer.print('E'); end
|
42
|
+
|
43
|
+
def results
|
44
|
+
@writer.puts "\n\n"
|
45
|
+
bad_results.each_with_index do |recorded, idx|
|
46
|
+
ctx, failure = recorded
|
47
|
+
@writer.puts "#%d - %s asserted %s: %s" % [idx + 1, ctx.to_s, failure.assertion.to_s, failure.to_s]
|
48
|
+
@writer.puts " " + failure.backtrace.join("\n ") + "\n\n"
|
49
|
+
end
|
50
|
+
format = "%d assertions, %d failures, %d errors in %s seconds"
|
51
|
+
@writer.puts format % [assertions, failures, errors, ("%0.6f" % time_taken)]
|
52
|
+
end
|
53
|
+
private
|
54
|
+
def bad_results_stack
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class NilReport < Report
|
59
|
+
def passed; end
|
60
|
+
def failed; end
|
61
|
+
def errored; end
|
62
|
+
def results; end
|
63
|
+
end
|
64
|
+
end # Protest
|
data/protest.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "protest"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.5"
|
4
4
|
s.date = "2009-06-29"
|
5
5
|
s.summary = "An extremely fast, expressive, and context-driven unit-testing framework"
|
6
6
|
s.email = %w[gus@gusg.us]
|
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
lib/protest/assertion.rb
|
21
21
|
lib/protest/context.rb
|
22
22
|
lib/protest/macros.rb
|
23
|
+
lib/protest/report.rb
|
23
24
|
protest.gemspec
|
24
25
|
]
|
25
26
|
|
data/test/context_test.rb
CHANGED
@@ -10,18 +10,16 @@ context "any context" do
|
|
10
10
|
|
11
11
|
context "that doesn't have passing tests" do
|
12
12
|
setup do
|
13
|
+
@report = Protest::NilReport.new
|
14
|
+
@context.asserts("a") { true }
|
13
15
|
@context.asserts("b") { false }
|
14
16
|
@context.asserts("c") { raise Exception, "blah" }
|
15
|
-
@context.run(
|
17
|
+
@context.run(@report)
|
16
18
|
end
|
17
19
|
|
18
|
-
asserts("that
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
asserts("that unexpected errors are captured").equals(1) do
|
23
|
-
@context.failures.select{|e| e.class == Protest::Error}.length
|
24
|
-
end
|
20
|
+
asserts("that passes are disctinct").equals(1) { @report.passes }
|
21
|
+
asserts("that failures are captured").equals(1) { @report.failures }
|
22
|
+
asserts("that unexpected errors are captured").equals(2) { @report.errors }
|
25
23
|
end # that doesn't have passing tests
|
26
24
|
end # any context
|
27
25
|
|
@@ -40,7 +38,7 @@ context "test context" do
|
|
40
38
|
asserts("assertion count").equals(2) { test_context.assertions.length }
|
41
39
|
|
42
40
|
asserts("setup runs only once").equals(2) do
|
43
|
-
test_context.run(
|
41
|
+
test_context.run(Protest::NilReport.new)
|
44
42
|
test_context.instance_variable_get(:@test_counter)
|
45
43
|
end
|
46
44
|
end
|
@@ -48,24 +46,27 @@ end
|
|
48
46
|
#
|
49
47
|
# Nested Context
|
50
48
|
|
51
|
-
inner_nested_context = nil
|
49
|
+
inner_nested_context, other_nested_context = nil, nil
|
52
50
|
nested_context = context "foo" do
|
53
51
|
setup do
|
54
52
|
@test_counter = 0
|
53
|
+
@foo = "bar"
|
55
54
|
end
|
56
55
|
asserts("a block returns true") { @test_counter += 1; true }
|
57
56
|
|
58
57
|
inner_nested_context = context("baz") do
|
59
58
|
setup { @test_counter += 10 }
|
60
59
|
end # A CONTEXT THAT IS DEQUEUED
|
60
|
+
|
61
|
+
other_nested_context = context("bum") {} # A CONTEXT THAT IS DEQUEUED
|
61
62
|
end # A CONTEXT THAT IS DEQUEUED
|
62
63
|
|
63
64
|
context "nested context" do
|
64
65
|
setup do
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
[nested_context, inner_nested_context, other_nested_context].each do |c|
|
67
|
+
Protest.dequeue_context(c)
|
68
|
+
c.run(Protest::NilReport.new)
|
69
|
+
end
|
69
70
|
end
|
70
71
|
|
71
72
|
asserts("inner context inherits parent context setup").equals(10) do
|
@@ -75,4 +76,8 @@ context "nested context" do
|
|
75
76
|
asserts("nested context name").equals("foo baz") do
|
76
77
|
inner_nested_context.to_s
|
77
78
|
end
|
79
|
+
|
80
|
+
asserts("inner context without setup is still bootstrapped").equals("bar") do
|
81
|
+
other_nested_context.instance_variable_get(:@foo)
|
82
|
+
end
|
78
83
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thumblemonks-protest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Knowlden
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- lib/protest/assertion.rb
|
30
30
|
- lib/protest/context.rb
|
31
31
|
- lib/protest/macros.rb
|
32
|
+
- lib/protest/report.rb
|
32
33
|
- protest.gemspec
|
33
34
|
has_rdoc: false
|
34
35
|
homepage: http://github.com/thumblemonks/protest
|