thumblemonks-protest 0.0.3 → 0.0.4
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 +28 -6
- data/lib/protest/assertion.rb +19 -43
- data/lib/protest/context.rb +12 -10
- data/lib/protest/macros.rb +28 -8
- data/protest.gemspec +2 -2
- data/test/assertion_test.rb +41 -18
- data/test/context_test.rb +2 -2
- metadata +2 -2
data/lib/protest.rb
CHANGED
@@ -20,18 +20,40 @@ module Protest
|
|
20
20
|
def self.run(writer=nil)
|
21
21
|
writer ||= STDOUT
|
22
22
|
start = Time.now
|
23
|
-
failures = @contexts.map { |context| context.run(writer) }.flatten
|
23
|
+
failures = @contexts.map { |context| context.run(writer) }.flatten.compact
|
24
24
|
running_time = Time.now - start
|
25
25
|
|
26
26
|
writer.puts "\n\n"
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
32
36
|
assertions = @contexts.inject(0) { |acc, context| acc + context.assertions.length }
|
33
37
|
writer.puts "#{@contexts.length} contexts, #{assertions} assertions: #{"%0.6f" % running_time} seconds"
|
34
38
|
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Failures
|
42
|
+
|
43
|
+
class Failure < Exception
|
44
|
+
def asserted(assertion)
|
45
|
+
@assertion = assertion
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s; "#{@assertion}: #{super}"; end
|
50
|
+
end
|
51
|
+
class Error < Failure
|
52
|
+
def initialize(message, e)
|
53
|
+
super(message)
|
54
|
+
set_backtrace(e.backtrace)
|
55
|
+
end
|
56
|
+
end
|
35
57
|
end # Protest
|
36
58
|
|
37
59
|
module Kernel
|
data/lib/protest/assertion.rb
CHANGED
@@ -1,58 +1,34 @@
|
|
1
1
|
module Protest
|
2
|
-
class Failure < Exception; end
|
3
|
-
class Error < Exception
|
4
|
-
def initialize(message, e)
|
5
|
-
super(message)
|
6
|
-
set_backtrace(e.backtrace)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
2
|
class Assertion
|
11
3
|
def initialize(description, &block)
|
12
4
|
@description = description
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
assert_block do |scope|
|
6
|
+
actual = scope.instance_eval(&block)
|
7
|
+
actual || failure("expected to be true, not #{actual.inspect}")
|
8
|
+
end
|
16
9
|
end
|
17
10
|
|
18
|
-
def to_s
|
19
|
-
"#{@description}: expected [#{@expectation}]"
|
20
|
-
end
|
11
|
+
def to_s; @description; end
|
21
12
|
|
22
|
-
def
|
23
|
-
@
|
24
|
-
|
13
|
+
def assert_block(&block)
|
14
|
+
@block = block if block_given?
|
15
|
+
self
|
25
16
|
end
|
26
17
|
|
27
|
-
def
|
28
|
-
|
18
|
+
def run(binding_scope)
|
19
|
+
@block.call(binding_scope)
|
29
20
|
end
|
30
21
|
|
31
|
-
def
|
32
|
-
|
33
|
-
expectation(expectation, &block)
|
34
|
-
end
|
22
|
+
def failure(message) raise(Protest::Failure, message); end
|
23
|
+
end # Assertion
|
35
24
|
|
25
|
+
class Denial < Assertion
|
36
26
|
def run(binding_scope)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
assert(@expectation == actual, "#{binding_scope} asserted #{self}, but received [#{actual}] instead")
|
44
|
-
end
|
45
|
-
private
|
46
|
-
def expectation(expectation, &block)
|
47
|
-
@expectation = expectation
|
48
|
-
@block = block if block_given?
|
49
|
-
self
|
27
|
+
super
|
28
|
+
rescue Protest::Failure => e
|
29
|
+
true
|
30
|
+
else
|
31
|
+
failure("expected to fail, but did not")
|
50
32
|
end
|
51
|
-
|
52
|
-
def errored(message, e); raise Error.new(message, e); end
|
53
|
-
|
54
|
-
def assert(evaluation, message)
|
55
|
-
@evaluated_expectation == evaluation || raise(Failure, message)
|
56
|
-
end
|
57
|
-
end # Assertion
|
33
|
+
end
|
58
34
|
end # Protest
|
data/lib/protest/context.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
module Protest
|
2
2
|
class Context
|
3
|
-
attr_reader :assertions, :
|
3
|
+
attr_reader :assertions, :failures
|
4
4
|
def initialize(description, parent=nil)
|
5
5
|
@description = description
|
6
6
|
@assertions = []
|
7
|
-
@
|
7
|
+
@failures = []
|
8
8
|
@parent = parent
|
9
9
|
@setup = nil
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_s
|
13
|
-
[@parent.to_s, @description].join(' ').strip
|
13
|
+
@to_s ||= [@parent.to_s, @description].join(' ').strip
|
14
14
|
end
|
15
15
|
|
16
16
|
def context(description, &block)
|
@@ -22,9 +22,8 @@ module Protest
|
|
22
22
|
self.bootstrap(self)
|
23
23
|
end
|
24
24
|
|
25
|
-
def asserts(description, &block)
|
26
|
-
|
27
|
-
end
|
25
|
+
def asserts(description, &block) new_assertion(Assertion, description, &block); end
|
26
|
+
def denies(description, &block) new_assertion(Denial, description, &block); end
|
28
27
|
|
29
28
|
def run(writer)
|
30
29
|
assertions.each do |assertion|
|
@@ -32,17 +31,20 @@ module Protest
|
|
32
31
|
assertion.run(self)
|
33
32
|
writer.print '.'
|
34
33
|
rescue Protest::Failure => e
|
35
|
-
writer.print 'F'; @
|
36
|
-
rescue
|
37
|
-
writer.print 'E'; @
|
34
|
+
writer.print 'F'; @failures << e.asserted(assertion)
|
35
|
+
rescue Exception => e
|
36
|
+
writer.print 'E'; @failures << Protest::Error.new("errored with #{e}", e).asserted(e)
|
38
37
|
end
|
39
38
|
end
|
40
|
-
@errors
|
41
39
|
end
|
42
40
|
|
43
41
|
def bootstrap(binder)
|
44
42
|
@parent.bootstrap(binder) if @parent
|
45
43
|
binder.instance_eval(&@setup) if @setup
|
46
44
|
end
|
45
|
+
private
|
46
|
+
def new_assertion(klass, description, &block)
|
47
|
+
(assertions << klass.new(description, &block)).last
|
48
|
+
end
|
47
49
|
end # Context
|
48
50
|
end # Protest
|
data/lib/protest/macros.rb
CHANGED
@@ -1,16 +1,36 @@
|
|
1
1
|
module Protest
|
2
|
-
module ContextMacros
|
3
|
-
def denies(description, &block)
|
4
|
-
asserts(description, &block).not
|
5
|
-
end
|
6
|
-
end # ContextMacros
|
7
|
-
|
8
2
|
module AssertionMacros
|
3
|
+
def equals(expected, &block)
|
4
|
+
assert_block do |scope|
|
5
|
+
actual = scope.instance_eval(&block)
|
6
|
+
expected == actual || failure("expected #{expected.inspect}, not #{actual.inspect}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
9
10
|
def nil(&block)
|
10
11
|
equals(nil, &block)
|
11
12
|
end
|
12
|
-
|
13
|
+
|
14
|
+
def raises(expected, &block)
|
15
|
+
assert_block do |scope|
|
16
|
+
begin
|
17
|
+
scope.instance_eval(&block)
|
18
|
+
rescue Exception => e
|
19
|
+
failure("should have raised #{expected}, not #{e.class}") unless expected == e.class
|
20
|
+
else
|
21
|
+
failure("should have raised #{expected}, but raised nothing")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches(expected, &block)
|
27
|
+
expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
|
28
|
+
assert_block do |scope|
|
29
|
+
actual = scope.instance_eval(&block)
|
30
|
+
actual =~ expected || failure("expected #{expected.inspect}, not #{actual.inspect}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end # AssertionMacros
|
13
34
|
end # Protest
|
14
35
|
|
15
|
-
Protest::Context.instance_eval { include Protest::ContextMacros }
|
16
36
|
Protest::Assertion.instance_eval { include Protest::AssertionMacros }
|
data/protest.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "protest"
|
3
|
-
s.version = "0.0.
|
4
|
-
s.date = "2009-06-
|
3
|
+
s.version = "0.0.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]
|
7
7
|
s.homepage = "http://github.com/thumblemonks/protest"
|
data/test/assertion_test.rb
CHANGED
@@ -1,31 +1,54 @@
|
|
1
1
|
require 'protest'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
fake_object = Object.new
|
4
|
+
|
5
|
+
context "basic assertion:" do
|
6
|
+
asserts("its description").equals("i will pass") do
|
5
7
|
Protest::Assertion.new("i will pass").to_s
|
6
8
|
end
|
7
|
-
end # any assertion
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
asserts("
|
12
|
-
|
10
|
+
asserts("true is expected") { Protest::Assertion.new("i will pass") { true }.run(fake_object) }
|
11
|
+
|
12
|
+
asserts("a Failure if not true").raises(Protest::Failure) do
|
13
|
+
Protest::Assertion.new("i will pass") { false }.run(fake_object)
|
14
|
+
end
|
15
|
+
|
16
|
+
asserts("an Exception error is thrown").raises(Exception) do
|
17
|
+
Protest::Assertion.new("error") { raise Exception, "blah" }.run(fake_object)
|
18
|
+
end
|
19
|
+
end # basic assertion
|
13
20
|
|
14
|
-
|
15
|
-
|
21
|
+
context "equals assertion:" do
|
22
|
+
asserts("results equals expectation") do
|
23
|
+
Protest::Assertion.new("i will pass").equals("foo bar") { "foo bar" }.run(fake_object)
|
16
24
|
end
|
17
25
|
|
18
|
-
asserts("
|
19
|
-
Protest::Assertion.new("
|
26
|
+
asserts("a Failure if results don't equal eachother").raises(Protest::Failure) do
|
27
|
+
Protest::Assertion.new("failure").equals("foo") { "bar" }.run(fake_object)
|
20
28
|
end
|
21
|
-
end #
|
29
|
+
end # equals assertion
|
22
30
|
|
23
|
-
context "
|
24
|
-
asserts("
|
25
|
-
|
31
|
+
context "nil assertion:" do
|
32
|
+
asserts("actual result is nil") { Protest::Assertion.new("foo").nil { nil }.run(fake_object) }
|
33
|
+
asserts("a Failure if not nil").raises(Protest::Failure) do
|
34
|
+
Protest::Assertion.new("foo").nil { "a" }.run(fake_object)
|
35
|
+
end
|
36
|
+
end # nil assertion
|
37
|
+
|
38
|
+
context "matching assertion:" do
|
39
|
+
asserts("actual result matches expression").equals(0) do
|
40
|
+
Protest::Assertion.new("foo").matches(%r[.]) { "a" }.run(fake_object)
|
41
|
+
end
|
42
|
+
asserts("a Failure if not nil").raises(Protest::Failure) do
|
43
|
+
Protest::Assertion.new("foo").matches(%r[.]) { "" }.run(fake_object)
|
44
|
+
end
|
45
|
+
asserts("string matches string").equals(0) do
|
46
|
+
Protest::Assertion.new("foo").matches("a") { "a" }.run(fake_object)
|
26
47
|
end
|
48
|
+
end # maching assertion
|
27
49
|
|
28
|
-
|
29
|
-
|
50
|
+
context "a denial" do
|
51
|
+
asserts("that a passing assertion evaluates to false") do
|
52
|
+
Protest::Denial.new("foo") { false }.run(fake_object)
|
30
53
|
end
|
31
|
-
end #
|
54
|
+
end # a denial
|
data/test/context_test.rb
CHANGED
@@ -16,11 +16,11 @@ context "any context" do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
asserts("that failures are captured").equals(1) do
|
19
|
-
@context.
|
19
|
+
@context.failures.select{|e| e.class == Protest::Failure}.length
|
20
20
|
end
|
21
21
|
|
22
22
|
asserts("that unexpected errors are captured").equals(1) do
|
23
|
-
@context.
|
23
|
+
@context.failures.select{|e| e.class == Protest::Error}.length
|
24
24
|
end
|
25
25
|
end # that doesn't have passing tests
|
26
26
|
end # any context
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Knowlden
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-06-
|
12
|
+
date: 2009-06-29 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|