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 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(writer=nil)
21
- writer ||= STDOUT
22
- start = Time.now
23
- failures = @contexts.map { |context| context.run(writer) }.flatten.compact
24
- running_time = Time.now - start
25
-
26
- writer.puts "\n\n"
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
- # Failures
33
+ # Exception
42
34
 
43
35
  class Failure < Exception
44
- def asserted(assertion)
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
@@ -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
- @block.call(binding_scope)
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
@@ -1,47 +1,43 @@
1
1
  module Protest
2
2
  class Context
3
- attr_reader :assertions, :failures
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 to_s
13
- @to_s ||= [@parent.to_s, @description].join(' ').strip
14
- end
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.bootstrap(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(writer)
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
- writer.print 'F'; @failures << e.asserted(assertion)
33
+ result = e
35
34
  rescue Exception => e
36
- writer.print 'E'; @failures << Protest::Error.new("errored with #{e}", e).asserted(e)
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
@@ -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}, not #{actual.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.4"
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(StringIO.new)
17
+ @context.run(@report)
16
18
  end
17
19
 
18
- asserts("that failures are captured").equals(1) do
19
- @context.failures.select{|e| e.class == Protest::Failure}.length
20
- end
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(StringIO.new)
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
- Protest.dequeue_context(nested_context)
66
- Protest.dequeue_context(inner_nested_context)
67
- nested_context.run(StringIO.new)
68
- inner_nested_context.run(StringIO.new)
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
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