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 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