riot 0.10.11 → 0.10.12.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|