thumblemonks-protest 0.0.4 → 0.0.5
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/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
|