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.
- data/.gitignore +3 -1
- data/CHANGELOG +34 -0
- data/README.markdown +81 -456
- data/Rakefile +31 -28
- data/VERSION +1 -1
- data/lib/riot.rb +2 -1
- data/lib/riot/assertion.rb +1 -1
- data/lib/riot/assertion_macro.rb +51 -5
- data/lib/riot/assertion_macros/any.rb +1 -1
- data/lib/riot/assertion_macros/assigns.rb +2 -2
- data/lib/riot/assertion_macros/empty.rb +1 -1
- data/lib/riot/assertion_macros/equals.rb +4 -4
- data/lib/riot/assertion_macros/equivalent_to.rb +22 -0
- data/lib/riot/assertion_macros/includes.rb +2 -2
- data/lib/riot/assertion_macros/kind_of.rb +2 -2
- data/lib/riot/assertion_macros/matches.rb +2 -2
- data/lib/riot/assertion_macros/nil.rb +1 -1
- data/lib/riot/assertion_macros/raises.rb +7 -10
- data/lib/riot/assertion_macros/respond_to.rb +3 -2
- data/lib/riot/assertion_macros/same_elements.rb +1 -1
- data/lib/riot/assertion_macros/size.rb +2 -2
- data/lib/riot/context.rb +104 -19
- data/lib/riot/message.rb +23 -0
- data/lib/riot/rr.rb +35 -0
- data/lib/riot/runnable.rb +12 -0
- data/lib/riot/situation.rb +4 -0
- data/riot.gemspec +20 -4
- data/test/assertion_macro_test.rb +30 -0
- data/test/assertion_macros/assigns_test.rb +3 -3
- data/test/assertion_macros/equals_test.rb +2 -9
- data/test/assertion_macros/equivalent_to_test.rb +36 -0
- data/test/assertion_macros/respond_to_test.rb +1 -0
- data/test/assertion_macros/size_test.rb +6 -6
- data/test/assertion_test.rb +1 -1
- data/test/benchmark/message_concatenation.rb +82 -0
- data/test/context_test.rb +17 -2
- data/test/extensions/rrriot_test.rb +69 -0
- data/test/message_test.rb +35 -0
- data/test/teststrap.rb +1 -1
- 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(:
|
8
|
+
Rake::TestTask.new("test:core") do |test|
|
8
9
|
test.libs << 'test'
|
9
|
-
test.pattern = 'test
|
10
|
+
test.pattern = 'test/*_test.rb'
|
10
11
|
test.warning = true
|
11
12
|
test.verbose = false
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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 "
|
38
|
-
task :
|
39
|
-
|
40
|
-
|
41
|
-
|
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.
|
1
|
+
0.10.12.pre
|
data/lib/riot.rb
CHANGED
@@ -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
|
|
data/lib/riot/assertion.rb
CHANGED
data/lib/riot/assertion_macro.rb
CHANGED
@@ -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
|
-
|
7
|
-
def
|
8
|
-
|
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'
|
@@ -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(
|
19
|
+
fail expected_message(variable).to_be_assigned_a_value
|
20
20
|
elsif !expected_value.nil? && expected_value != actual_value
|
21
|
-
fail(
|
21
|
+
fail expected_message(variable).to_be_equal_to(expected_value).not(actual_value)
|
22
22
|
else
|
23
23
|
pass
|
24
24
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Riot
|
2
|
-
# Asserts that the result of the test equals the expected value. Using the
|
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
|
12
|
-
pass(
|
11
|
+
if expected == actual
|
12
|
+
pass new_message.is_equal_to(expected)
|
13
13
|
else
|
14
|
-
fail(
|
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(
|
11
|
+
pass new_message.includes(expected)
|
12
12
|
else
|
13
|
-
fail(
|
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(
|
10
|
+
pass new_message.is_a_kind_of(expected)
|
11
11
|
else
|
12
|
-
fail(
|
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(
|
11
|
+
pass(new_message.matches(expected))
|
12
12
|
else
|
13
|
-
fail(
|
13
|
+
fail(expected_message(expected).to_match(actual))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
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
|
17
|
+
fail should_have_message.raised(expected_class).but.raised_nothing
|
18
18
|
elsif expected_class != actual_exception.class
|
19
|
-
fail
|
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
|
21
|
+
fail expected_message(expected_message).for_message.not(actual_message)
|
22
22
|
else
|
23
|
-
|
24
|
-
|
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(
|
11
|
+
pass(new_message.responds_to(expected))
|
11
12
|
else
|
12
|
-
fail(
|
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(
|
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 =
|
12
|
-
expected === actual.size ? pass(
|
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
|
data/lib/riot/context.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
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
|
-
|
14
|
-
|
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
|
-
|
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
|
-
|
82
|
+
# Add a teardown block.
|
83
|
+
def teardown(&definition)
|
84
|
+
(@teardowns << Setup.new(&definition)).last
|
85
|
+
end
|
34
86
|
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
40
|
-
|
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,
|
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
|
-
|
53
|
-
|
54
|
-
|
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 <<
|
152
|
+
(@assertions << assertion_class.new(description, &definition)).last
|
68
153
|
end
|
69
154
|
end # Context
|
70
155
|
end # Riot
|