riot 0.10.11 → 0.10.12.pre

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.
Files changed (40) hide show
  1. data/.gitignore +3 -1
  2. data/CHANGELOG +34 -0
  3. data/README.markdown +81 -456
  4. data/Rakefile +31 -28
  5. data/VERSION +1 -1
  6. data/lib/riot.rb +2 -1
  7. data/lib/riot/assertion.rb +1 -1
  8. data/lib/riot/assertion_macro.rb +51 -5
  9. data/lib/riot/assertion_macros/any.rb +1 -1
  10. data/lib/riot/assertion_macros/assigns.rb +2 -2
  11. data/lib/riot/assertion_macros/empty.rb +1 -1
  12. data/lib/riot/assertion_macros/equals.rb +4 -4
  13. data/lib/riot/assertion_macros/equivalent_to.rb +22 -0
  14. data/lib/riot/assertion_macros/includes.rb +2 -2
  15. data/lib/riot/assertion_macros/kind_of.rb +2 -2
  16. data/lib/riot/assertion_macros/matches.rb +2 -2
  17. data/lib/riot/assertion_macros/nil.rb +1 -1
  18. data/lib/riot/assertion_macros/raises.rb +7 -10
  19. data/lib/riot/assertion_macros/respond_to.rb +3 -2
  20. data/lib/riot/assertion_macros/same_elements.rb +1 -1
  21. data/lib/riot/assertion_macros/size.rb +2 -2
  22. data/lib/riot/context.rb +104 -19
  23. data/lib/riot/message.rb +23 -0
  24. data/lib/riot/rr.rb +35 -0
  25. data/lib/riot/runnable.rb +12 -0
  26. data/lib/riot/situation.rb +4 -0
  27. data/riot.gemspec +20 -4
  28. data/test/assertion_macro_test.rb +30 -0
  29. data/test/assertion_macros/assigns_test.rb +3 -3
  30. data/test/assertion_macros/equals_test.rb +2 -9
  31. data/test/assertion_macros/equivalent_to_test.rb +36 -0
  32. data/test/assertion_macros/respond_to_test.rb +1 -0
  33. data/test/assertion_macros/size_test.rb +6 -6
  34. data/test/assertion_test.rb +1 -1
  35. data/test/benchmark/message_concatenation.rb +82 -0
  36. data/test/context_test.rb +17 -2
  37. data/test/extensions/rrriot_test.rb +69 -0
  38. data/test/message_test.rb +35 -0
  39. data/test/teststrap.rb +1 -1
  40. metadata +27 -4
data/Rakefile CHANGED
@@ -1,47 +1,49 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
- task :default => [:test]
4
+ task :default => ["test:core", "test:assertion_macros"]
5
+ task "test:all" => ["test:core", "test:assertion_macros", "test:extensions"]
5
6
 
6
7
  require 'rake/testtask'
7
- Rake::TestTask.new(:test) do |test|
8
+ Rake::TestTask.new("test:core") do |test|
8
9
  test.libs << 'test'
9
- test.pattern = 'test/**/*_test.rb'
10
+ test.pattern = 'test/*_test.rb'
10
11
  test.warning = true
11
12
  test.verbose = false
12
13
  end
13
14
 
14
- desc "Run all of them fancy benchmarks, Howard!"
15
- task :benchmarks do
16
- Dir["test/benchmark/*.rb"].each do |file|
17
- puts ">> Running #{file}"
18
- puts %x[ruby #{file}]
19
- end
20
- end
21
-
22
- desc "Run Flog against library (except tests)"
23
- task :flog do
24
- puts %x[find ./lib -name *.rb | xargs flog]
15
+ Rake::TestTask.new("test:assertion_macros") do |test|
16
+ test.libs << 'test'
17
+ test.pattern = 'test/assertion_macros/*_test.rb'
18
+ test.warning = true
19
+ test.verbose = false
25
20
  end
26
21
 
27
- desc "Run Flay against library (except tests)"
28
- task :flay do
29
- puts %x[find ./lib -name *.rb | xargs flay]
22
+ Rake::TestTask.new("test:extensions") do |test|
23
+ test.libs << 'test'
24
+ test.pattern = 'test/extensions/*_test.rb'
25
+ test.warning = true
26
+ test.verbose = false
30
27
  end
31
28
 
32
- desc "Run Roodi against library (except tests)"
33
- task :roodi do
34
- puts %x[find ./lib -name *.rb | xargs roodi]
29
+ begin
30
+ require 'yard'
31
+ require 'yard/rake/yardoc_task'
32
+ YARD::Rake::YardocTask.new do |t|
33
+ extra_files = %w(MIT-LICENSE)
34
+ t.files = ['lib/**/*.rb']
35
+ t.options = ["--files=#{extra_files.join(',')}"]
36
+ end
37
+ rescue LoadError
38
+ # YARD isn't installed
35
39
  end
36
40
 
37
- desc "Stats on lines of code and test"
38
- task :stats do
39
- loc = %x[find ./lib -name *.rb | xargs cat | wc -l].strip.to_i
40
- lotc = %x[find ./test -name *.rb | xargs cat | wc -l].strip.to_i
41
- total, ratio = (loc + lotc), (lotc / loc.to_f)
42
-
43
- fmt = " Code: %d\n Test: %d\n -----\n Total: %d Ratio (test/code): %f"
44
- puts fmt % [loc, lotc, loc + lotc, ratio]
41
+ desc "Run all of them fancy benchmarks, Howard!"
42
+ task :benchmarks do
43
+ Dir["test/benchmark/*.rb"].each do |file|
44
+ puts ">> Running #{file}"
45
+ puts %x[ruby #{file}]
46
+ end
45
47
  end
46
48
 
47
49
  #
@@ -56,6 +58,7 @@ begin
56
58
  gem.email = "gus@gusg.us"
57
59
  gem.homepage = "http://github.com/thumblemonks/riot"
58
60
  gem.authors = ["Justin 'Gus' Knowlden"]
61
+ gem.add_dependency 'rr'
59
62
  gem.add_dependency 'term-ansicolor'
60
63
  end
61
64
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.11
1
+ 0.10.12.pre
@@ -7,7 +7,7 @@ require 'riot/assertion_macro'
7
7
 
8
8
  module Riot
9
9
  def self.context(description, context_class = Context, &definition)
10
- root_contexts << context_class.new(description, &definition)
10
+ (root_contexts << context_class.new(description, &definition)).last
11
11
  end
12
12
 
13
13
  def self.root_contexts; @root_contexts ||= []; end
@@ -20,6 +20,7 @@ module Riot
20
20
  the_reporter
21
21
  end
22
22
 
23
+ # This means you don't want to see any output from Riot. A "quiet riot" as Envy5 put it.
23
24
  def self.silently!; @silent = true; end
24
25
  def self.silently?; defined?(@silent) && @silent == true end
25
26
 
@@ -1,7 +1,7 @@
1
1
  module Riot
2
2
  class Assertion < RunnableBlock
3
3
  class << self
4
- def macros; @macros ||= {}; end
4
+ def macros; @@macros ||= {}; end
5
5
 
6
6
  def register_macro(name, assertion_macro, expect_exception=false)
7
7
  macros[name.to_s] = assertion_macro.new
@@ -1,15 +1,54 @@
1
+ require 'riot/message'
2
+
1
3
  module Riot
4
+ # The base class for all assertion macros.
5
+ #
6
+ # == Using macros
7
+ #
8
+ # Macros are applied to the return value of assertions. For example, the
9
+ # `empty` macro asserts that the value is, well, empty, e.g.
10
+ #
11
+ # asserts(:comments).empty?
12
+ #
13
+ #
14
+ # == Writing your own macros
15
+ #
16
+ # Macros are added by subclassing {AssertionMacro}. For example, here's
17
+ # the implementation of `empty`:
18
+ #
19
+ # class EmptyMacro < AssertionMacro
20
+ # register :empty
21
+ #
22
+ # def evaluate(actual)
23
+ # actual.length == 0 ? pass : fail(expected_message(actual).to_be_empty)
24
+ # end
25
+ # end
26
+ #
2
27
  class AssertionMacro
3
28
  class << self
29
+ # Whether the macro expects an exception to be thrown.
4
30
  attr_reader :expects_exception
5
31
 
6
- def default; @default_macro ||= new; end
7
- def expects_exception!; @expects_exception = true; end
8
- def register(name); Assertion.register_macro name, self; end
32
+ # The default macro.
33
+ def default
34
+ @default_macro ||= new
35
+ end
36
+
37
+ # Specify that the macro expects an exception to be thrown by the assertion.
38
+ def expects_exception!
39
+ @expects_exception = true
40
+ end
41
+
42
+ # Register the macro under the given name.
43
+ #
44
+ # @param [Symbol] name the name of the macro
45
+ def register(name)
46
+ Assertion.register_macro name, self
47
+ end
9
48
  end
10
49
 
11
- def pass(message=nil) [:pass, message]; end
12
- def fail(message) [:fail, message]; end
50
+ def pass(message=nil) [:pass, message.to_s]; end
51
+ def fail(message) [:fail, message.to_s]; end
13
52
  def error(e) [:error, e]; end
14
53
 
15
54
  def expects_exception?; self.class.expects_exception; end
@@ -17,6 +56,12 @@ module Riot
17
56
  def evaluate(actual)
18
57
  actual ? pass : fail("Expected non-false but got #{actual.inspect} instead")
19
58
  end
59
+
60
+ # Messaging
61
+
62
+ def new_message(*phrases) Message.new(*phrases); end
63
+ def should_have_message(*phrases) new_message.should_have(*phrases); end
64
+ def expected_message(*phrases) new_message.expected(*phrases); end
20
65
  end
21
66
  end
22
67
 
@@ -24,6 +69,7 @@ require 'riot/assertion_macros/any'
24
69
  require 'riot/assertion_macros/assigns'
25
70
  require 'riot/assertion_macros/empty'
26
71
  require 'riot/assertion_macros/equals'
72
+ require 'riot/assertion_macros/equivalent_to'
27
73
  require 'riot/assertion_macros/exists'
28
74
  require 'riot/assertion_macros/includes'
29
75
  require 'riot/assertion_macros/kind_of'
@@ -6,7 +6,7 @@ module Riot
6
6
  register :any
7
7
 
8
8
  def evaluate(actual)
9
- actual.any? ? pass("is not empty") : fail("expected #{actual.inspect} to have items")
9
+ actual.any? ? pass("is not empty") : fail(expected_message(actual).to_have_items)
10
10
  end
11
11
  end
12
12
  end
@@ -16,9 +16,9 @@ module Riot
16
16
  variable_name = "@#{variable}"
17
17
  actual_value = actual.instance_variable_defined?(variable_name) ? actual.instance_variable_get(variable_name) : nil
18
18
  if actual_value.nil?
19
- fail("expected @#{variable} to be assigned a value")
19
+ fail expected_message(variable).to_be_assigned_a_value
20
20
  elsif !expected_value.nil? && expected_value != actual_value
21
- fail(%Q[expected @#{variable} to be equal to #{expected_value.inspect}, not #{actual_value.inspect}])
21
+ fail expected_message(variable).to_be_equal_to(expected_value).not(actual_value)
22
22
  else
23
23
  pass
24
24
  end
@@ -7,7 +7,7 @@ module Riot
7
7
  register :empty
8
8
 
9
9
  def evaluate(actual)
10
- actual.length == 0 ? pass : fail("expected #{actual.inspect} to be empty")
10
+ actual.length == 0 ? pass : fail(expected_message(actual).to_be_empty)
11
11
  end
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  module Riot
2
- # Asserts that the result of the test equals the expected value. Using the +===+ operator to assert
2
+ # Asserts that the result of the test equals the expected value. Using the +==+ operator to assert
3
3
  # equality.
4
4
  # asserts("test") { "foo" }.equals("foo")
5
5
  # should("test") { "foo" }.equals("foo")
@@ -8,10 +8,10 @@ module Riot
8
8
  register :equals
9
9
 
10
10
  def evaluate(actual, expected)
11
- if expected === actual
12
- pass("is equal to #{expected.inspect}")
11
+ if expected == actual
12
+ pass new_message.is_equal_to(expected)
13
13
  else
14
- fail("expected #{expected.inspect}, not #{actual.inspect}")
14
+ fail expected_message(expected).not(actual)
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,22 @@
1
+ module Riot
2
+ # Asserts that the result of the test is equivalent to the expected value. Using the +===+ operator.
3
+ #
4
+ # asserts("test") { "foo" }.equivalent_to(String)
5
+ # should("test") { "foo" }.equivalent_to("foo")
6
+ # asserts("test") { "foo" }.equivalent_to { "foo" }
7
+ #
8
+ # Underneath the hood, this assertion macro says:
9
+ #
10
+ # expected === actual
11
+ class EquivalentToMacro < AssertionMacro
12
+ register :equivalent_to
13
+
14
+ def evaluate(actual, expected)
15
+ if expected === actual
16
+ pass new_message.is_equivalent_to(expected)
17
+ else
18
+ fail expected_message(actual).to_be_equivalent_to(expected)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -8,9 +8,9 @@ module Riot
8
8
 
9
9
  def evaluate(actual, expected)
10
10
  if actual.include?(expected)
11
- pass("includes #{expected.inspect}")
11
+ pass new_message.includes(expected)
12
12
  else
13
- fail("expected #{actual.inspect} to include #{expected.inspect}")
13
+ fail expected_message(actual).to_include(expected)
14
14
  end
15
15
  end
16
16
  end
@@ -7,9 +7,9 @@ module Riot
7
7
 
8
8
  def evaluate(actual, expected)
9
9
  if actual.kind_of?(expected)
10
- pass("is a kind of #{expected.inspect}")
10
+ pass new_message.is_a_kind_of(expected)
11
11
  else
12
- fail("expected kind of #{expected}, not #{actual.class.inspect}")
12
+ fail expected_message.kind_of(expected).not(actual.class)
13
13
  end
14
14
  end
15
15
  end
@@ -8,9 +8,9 @@ module Riot
8
8
  def evaluate(actual, expected)
9
9
  expected = %r[#{Regexp.escape(expected)}] if expected.kind_of?(String)
10
10
  if actual =~ expected
11
- pass("matches #{expected.inspect}")
11
+ pass(new_message.matches(expected))
12
12
  else
13
- fail("expected #{expected.inspect} to match #{actual.inspect}")
13
+ fail(expected_message(expected).to_match(actual))
14
14
  end
15
15
  end
16
16
  end
@@ -6,7 +6,7 @@ module Riot
6
6
  register :nil
7
7
 
8
8
  def evaluate(actual)
9
- actual.nil? ? pass("is nil") : fail("expected nil, not #{actual.inspect}")
9
+ actual.nil? ? pass("is nil") : fail(expected_message.nil.not(actual))
10
10
  end
11
11
  end
12
12
  end
@@ -14,18 +14,15 @@ module Riot
14
14
  def evaluate(actual_exception, expected_class, expected_message=nil)
15
15
  actual_message = actual_exception && actual_exception.message
16
16
  if actual_exception.nil?
17
- fail("should have raised #{expected_class}, but raised nothing")
17
+ fail should_have_message.raised(expected_class).but.raised_nothing
18
18
  elsif expected_class != actual_exception.class
19
- fail("should have raised #{expected_class}, not #{actual_exception.class}")
19
+ fail should_have_message.raised(expected_class).not(actual_exception.class)
20
20
  elsif expected_message && !(actual_message.to_s =~ %r[#{expected_message}])
21
- fail("expected #{expected_message.inspect} for message, not #{actual_message.inspect}")
21
+ fail expected_message(expected_message).for_message.not(actual_message)
22
22
  else
23
- if expected_message
24
- pass("raises #{expected_class.inspect} with message #{expected_message.inspect}")
25
- else
26
- pass("raises #{expected_class.inspect}")
27
- end
23
+ message = new_message.raises(expected_class)
24
+ pass(expected_message ? message.with_message(expected_message) : message)
28
25
  end
29
- end
30
- end
26
+ end # evaluate
27
+ end # RaisesMacro
31
28
  end
@@ -4,12 +4,13 @@ module Riot
4
4
  # should("test") { "foo" }.respond_to(:to_s)
5
5
  class RespondToMacro < AssertionMacro
6
6
  register :respond_to
7
+ register :responds_to
7
8
 
8
9
  def evaluate(actual, expected)
9
10
  if actual.respond_to?(expected)
10
- pass("responds to #{expected.inspect}")
11
+ pass(new_message.responds_to(expected))
11
12
  else
12
- fail("expected method #{expected.inspect} is not defined")
13
+ fail(expected_message.method(expected).is_not_defined)
13
14
  end
14
15
  end
15
16
  end
@@ -8,7 +8,7 @@ module Riot
8
8
  def evaluate(actual, expected)
9
9
  require 'set'
10
10
  same = (Set.new(expected) == Set.new(actual))
11
- same ? pass : fail("expected elements #{expected.inspect} to match #{actual.inspect}")
11
+ same ? pass : fail(expected_message.elements(expected).to_match(actual))
12
12
  end
13
13
  end
14
14
  end
@@ -8,8 +8,8 @@ module Riot
8
8
  register :size
9
9
 
10
10
  def evaluate(actual, expected)
11
- failure_message = "size of #{actual.inspect} expected to be #{expected} but is #{actual.size}"
12
- expected === actual.size ? pass("is of size #{expected}") : fail(failure_message)
11
+ failure_message = expected_message.size_of(actual).to_be(expected).not(actual.size)
12
+ expected === actual.size ? pass(new_message.is_of_size(expected)) : fail(failure_message)
13
13
  end
14
14
  end
15
15
  end
@@ -1,19 +1,68 @@
1
1
  module Riot
2
2
  RootContext = Struct.new(:setups, :teardowns)
3
3
 
4
+ module ContextHelpers
5
+ def assertion_class; Assertion; end
6
+ def situation_class; Situation; end
7
+ end
8
+
9
+ # You make your assertions within a Context. The context stores setup and
10
+ # teardown blocks, and allows for nesting and refactoring into helpers.
4
11
  class Context
12
+ include ContextHelpers
13
+ # The description of the context.
14
+ #
15
+ # @return [String]
5
16
  attr_reader :description
6
- def initialize(description, parent=RootContext.new([],[]), &definition)
7
- @parent = parent
17
+
18
+ # The parent context.
19
+ #
20
+ # @return [Riot::Context]
21
+ attr_reader :parent
22
+
23
+ def initialize(description, parent=nil, &definition)
24
+ @parent = parent || RootContext.new([],[])
8
25
  @description = description
9
- @contexts, @setups, @assertions, @teardowns = [], [], [], []
26
+ @contexts, @helpers, @setups, @assertions, @teardowns = [], [], [], [], []
10
27
  self.instance_eval(&definition)
11
28
  end
12
29
 
13
- def setups; @parent.setups + @setups; end
14
- def teardowns; @parent.teardowns + @teardowns; end
30
+ # Create a new test context.
31
+ #
32
+ # @param [String] description
33
+ def context(description, &definition)
34
+ new_context(description, self.class, &definition)
35
+ end
36
+
37
+ # Returns an ordered list of the setup blocks for the context.
38
+ #
39
+ # @return [Array[Riot::RunnableBlock]]
40
+ def setups
41
+ @parent.setups + @setups
42
+ end
43
+
44
+ # Returns an ordered list of the teardown blocks for the context.
45
+ #
46
+ # @return [Array[Riot::RunnableBlock]]
47
+ def teardowns
48
+ @parent.teardowns + @teardowns
49
+ end
15
50
 
16
- def setup(&definition) (@setups << Setup.new(&definition)).last; end
51
+ # Add a setup block.
52
+ #
53
+ # A setup block defines the topic of the context. There can be multiple setup
54
+ # blocks; each can access the previous topic through the +topic+ attribute.
55
+ #
56
+ # context "A string" do
57
+ # setup { "foo" }
58
+ # setup { topic * 2 }
59
+ # asserts(:length).equals(6)
60
+ # end
61
+ def setup(&definition)
62
+ (@setups << Setup.new(&definition)).last
63
+ end
64
+
65
+ def helper(name, &block); (@helpers << Helper.new(name, &block)).last; end
17
66
 
18
67
  # A setup shortcut that returns the original topic so you don't have to. Good for nested setups. Instead
19
68
  # of doing this in your context:
@@ -30,32 +79,68 @@ module Riot
30
79
  setup { self.instance_eval(&definition); topic }
31
80
  end
32
81
 
33
- def teardown(&definition) (@teardowns << Setup.new(&definition)).last; end
82
+ # Add a teardown block.
83
+ def teardown(&definition)
84
+ (@teardowns << Setup.new(&definition)).last
85
+ end
34
86
 
35
- def asserts(what, &definition) new_assertion("asserts", what, &definition); end
36
- def should(what, &definition) new_assertion("should", what, &definition); end
37
- def asserts_topic(what="topic"); asserts(what) { topic }; end
87
+ # Makes an assertion.
88
+ #
89
+ # In the most basic form, an assertion requires a descriptive name and a block.
90
+ #
91
+ # asserts("#size is equals to 2") { topic.size == 2 }
92
+ #
93
+ # However, several shortcuts are available. Assertion macros can be added to the
94
+ # end, automating a number of common assertion patterns, e.g.
95
+ #
96
+ # asserts("#size") { topic.size }.equals(2)
97
+ #
98
+ # Furthermore, the pattern of testing an attribute on the topic is codified as
99
+ #
100
+ # asserts(:size).equals(2)
101
+ #
102
+ # Passing a Symbol to +asserts+ enables this behaviour. For more information on
103
+ # assertion macros, see {Riot::AssertionMacro}.
104
+ #
105
+ # @param [String, Symbol] what the property being tested
106
+ def asserts(what, &definition)
107
+ new_assertion("asserts", what, &definition)
108
+ end
38
109
 
39
- def context(description, &definition)
40
- @contexts << self.class.new("#{@description} #{description}", self, &definition)
110
+ def should(what, &definition)
111
+ new_assertion("should", what, &definition)
41
112
  end
42
-
113
+
114
+ # Makes an assertion on the topic itself, e.g.
115
+ #
116
+ # asserts_topic.matches(/^ab+/)
117
+ def asserts_topic(what="that it")
118
+ asserts(what) { topic }
119
+ end
120
+
43
121
  def run(reporter)
44
122
  reporter.describe_context(self) unless @assertions.empty?
45
- local_run(reporter, Situation.new)
123
+ local_run(reporter, situation_class.new)
46
124
  run_sub_contexts(reporter)
47
125
  reporter
48
126
  end
49
- private
50
127
 
51
128
  def local_run(reporter, situation)
52
- (setups + @assertions + teardowns).each do |runnable|
53
- reporter.report(runnable.to_s, runnable.run(situation))
54
- end
129
+ runnables.each { |runnable| reporter.report(runnable.to_s, runnable.run(situation)) }
130
+ end
131
+
132
+ private
133
+
134
+ def runnables
135
+ setups + @helpers + @assertions + teardowns
55
136
  end
56
137
 
57
138
  def run_sub_contexts(reporter) @contexts.each { |ctx| ctx.run(reporter) }; end
58
139
 
140
+ def new_context(description, klass, &definition)
141
+ (@contexts << klass.new("#{@description} #{description}", self, &definition)).last
142
+ end
143
+
59
144
  def new_assertion(scope, what, &definition)
60
145
  if what.kind_of?(Symbol)
61
146
  definition ||= lambda { topic.send(what) }
@@ -64,7 +149,7 @@ module Riot
64
149
  description = "#{scope} #{what}"
65
150
  end
66
151
 
67
- (@assertions << Assertion.new(description, &definition)).last
152
+ (@assertions << assertion_class.new(description, &definition)).last
68
153
  end
69
154
  end # Context
70
155
  end # Riot