riot 0.12.1 → 0.12.2

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 (62) hide show
  1. data/.gitignore +10 -0
  2. data/.yardopts +6 -0
  3. data/CHANGELOG +58 -46
  4. data/Gemfile +4 -0
  5. data/README.markdown +322 -85
  6. data/Rakefile +3 -38
  7. data/lib/riot.rb +74 -11
  8. data/lib/riot/assertion.rb +32 -1
  9. data/lib/riot/assertion_macro.rb +57 -10
  10. data/lib/riot/assertion_macros/any.rb +4 -2
  11. data/lib/riot/assertion_macros/assigns.rb +18 -4
  12. data/lib/riot/assertion_macros/empty.rb +2 -0
  13. data/lib/riot/assertion_macros/equals.rb +4 -0
  14. data/lib/riot/assertion_macros/equivalent_to.rb +5 -1
  15. data/lib/riot/assertion_macros/exists.rb +4 -2
  16. data/lib/riot/assertion_macros/includes.rb +5 -1
  17. data/lib/riot/assertion_macros/kind_of.rb +5 -1
  18. data/lib/riot/assertion_macros/matches.rb +5 -1
  19. data/lib/riot/assertion_macros/nil.rb +3 -1
  20. data/lib/riot/assertion_macros/not_borat.rb +6 -0
  21. data/lib/riot/assertion_macros/raises.rb +13 -7
  22. data/lib/riot/assertion_macros/respond_to.rb +5 -1
  23. data/lib/riot/assertion_macros/same_elements.rb +6 -2
  24. data/lib/riot/assertion_macros/size.rb +5 -1
  25. data/lib/riot/context.rb +58 -10
  26. data/lib/riot/context_helpers.rb +20 -4
  27. data/lib/riot/context_options.rb +14 -4
  28. data/lib/riot/message.rb +87 -6
  29. data/lib/riot/middleware.rb +69 -4
  30. data/lib/riot/reporter.rb +71 -110
  31. data/lib/riot/reporter/dot_matrix.rb +49 -0
  32. data/lib/riot/reporter/io.rb +85 -0
  33. data/lib/riot/reporter/pretty_dot_matrix.rb +38 -0
  34. data/lib/riot/reporter/silent.rb +18 -0
  35. data/lib/riot/reporter/story.rb +52 -0
  36. data/lib/riot/rr.rb +28 -4
  37. data/lib/riot/runnable.rb +53 -0
  38. data/lib/riot/situation.rb +45 -0
  39. data/lib/riot/version.rb +4 -0
  40. data/riot.gemspec +14 -155
  41. data/test/core/assertion_macros/any_test.rb +10 -10
  42. data/test/core/assertion_macros/assigns_test.rb +7 -7
  43. data/test/core/assertion_macros/equivalent_to_test.rb +3 -3
  44. data/test/core/assertion_macros/exists_test.rb +4 -4
  45. data/test/core/assertion_macros/includes_test.rb +2 -2
  46. data/test/core/assertion_macros/kind_of_test.rb +3 -3
  47. data/test/core/assertion_macros/matches_test.rb +2 -2
  48. data/test/core/assertion_macros/nil_test.rb +2 -2
  49. data/test/core/assertion_macros/raises_test.rb +10 -10
  50. data/test/core/assertion_macros/respond_to_test.rb +2 -2
  51. data/test/core/assertion_macros/same_elements_test.rb +4 -4
  52. data/test/core/assertion_macros/size_test.rb +6 -6
  53. data/test/core/context/asserts_with_arguments_test.rb +12 -0
  54. data/test/core/context/using_describe_in_a_test.rb +1 -1
  55. data/test/core/report_test.rb +9 -5
  56. data/test/core/runnable/message_test.rb +10 -6
  57. data/test/teststrap.rb +0 -6
  58. metadata +20 -33
  59. data/TODO.markdown +0 -14
  60. data/VERSION +0 -1
  61. data/test.watchr +0 -70
  62. data/test/benchmark/colorize.rb +0 -39
@@ -0,0 +1,49 @@
1
+ module Riot
2
+
3
+ # Outputs in the dot-notion almost everyone should be familiar with, "." implies pass, "F" implues a
4
+ # failure, and "E" implies an error. If ansi-coloring is available, it is used. Error and failure messages
5
+ # are buffered for output until the end.
6
+ class DotMatrixReporter < IOReporter
7
+ # Creates a new DotMatrixReporter and initializes the failure/error details buffer.
8
+ # @param (see Riot::IOReporter#initialize)
9
+ def initialize(writer=STDOUT)
10
+ super
11
+ @details = []
12
+ end
13
+
14
+ # Prints a ".". Prints in green if possible.
15
+ #
16
+ # @param (see Riot::Reporter#pass)
17
+ def pass(description, message)
18
+ print green(".")
19
+ end
20
+
21
+ # Prints a "F" and saves the failure details (including line number and file) for the end.
22
+ # Prints in yellow if possible.
23
+ #
24
+ # @param (see Riot::Reporter#fail)
25
+ def fail(description, message, line, file)
26
+ print yellow("F")
27
+ @details << "FAILURE - #{test_detail(description, message)} #{line_info(line, file)}".strip
28
+ end
29
+
30
+ # Prints a "E" and saves the error details (including backtrace) for the end. Prints in red if possible.
31
+ #
32
+ # @param (see Riot::Reporter#error)
33
+ def error(description, e)
34
+ print red("E")
35
+ @details << "ERROR - #{test_detail(description, format_error(e))}"
36
+ end
37
+
38
+ # (see Riot::Reporter#results)
39
+ def results(time_taken)
40
+ puts "\n#{@details.join("\n\n")}" unless @details.empty?
41
+ super
42
+ end
43
+ private
44
+ def test_detail(description, message)
45
+ "#{current_context.detailed_description} #{description} => #{message}"
46
+ end
47
+ end # DotMatrixReporter
48
+
49
+ end # Riot
@@ -0,0 +1,85 @@
1
+ module Riot
2
+
3
+ # An IOReporter is one that expects to use an IO object to output results to. Thus, whatever is available
4
+ # by an instance of an IO object should be available to whatever is given to this reporter to use.
5
+ #
6
+ # This is an abstract class. You should use some other or define your own sub-class that knows how to
7
+ # handle +pass+, +fail+, and +error+.
8
+ class IOReporter < Reporter
9
+ # Creates a new IOReporter. You can give it your own IO writer or it will default to +STDOUT+.
10
+ #
11
+ # @param [IO] writer the writer to use for results
12
+ def initialize(writer=STDOUT)
13
+ super()
14
+ @writer = writer
15
+ end
16
+
17
+ # (see Riot::Reporter#results)
18
+ def results(time_taken)
19
+ values = [passes, failures, errors, ("%0.6f" % time_taken)]
20
+ puts "\n%d passes, %d failures, %d errors in %s seconds" % values
21
+ end
22
+
23
+ protected
24
+ # Helper that knows how to write output to the writer with a newline.
25
+ #
26
+ # @param [String] message the message to be printed
27
+ def puts(message) @writer.puts(message); end
28
+
29
+ # Helper that knows how to write output to the writer without a newline.
30
+ #
31
+ # @param [String] message the message to be printed
32
+ def print(message) @writer.print(message); end
33
+
34
+ # Takes a line number, the file it corresponds to, and generates a formatted string for use in failure
35
+ # responses.
36
+ #
37
+ # @param [Number] line the line number of the failure
38
+ # @param [String] file the name of the file the failure was in
39
+ # @return [String] formatted failure line
40
+ def line_info(line, file)
41
+ line ? "(on line #{line} in #{file})" : ""
42
+ end
43
+
44
+ # Generates a message for assertions that error out. However, in the additional stacktrace, any mentions
45
+ # of Riot and Rake framework methods calls are removed. Makes for a more readable error response.
46
+ #
47
+ # @param [Exception] e the exception to generate the backtrace from
48
+ # @return [String] the error response message
49
+ def format_error(e)
50
+ format = []
51
+ format << " #{e.class.name} occurred"
52
+ format << "#{e.to_s}"
53
+ filter_backtrace(e.backtrace) { |line| format << " at #{line}" }
54
+
55
+ format.join("\n")
56
+ end
57
+
58
+ # Filters Riot and Rake method calls from an exception backtrace.
59
+ #
60
+ # @param [Array] backtrace an exception's backtrace
61
+ # @param [lambda] &line_handler called each time a good line is found
62
+ def filter_backtrace(backtrace, &line_handler)
63
+ cleansed, bad = [], true
64
+
65
+ # goal is to filter all the riot stuff/rake before the first non riot thing
66
+ backtrace.reverse_each do |bt|
67
+ # make sure we are still in the bad part
68
+ bad = (bt =~ /\/lib\/riot/ || bt =~ /rake_test_loader/) if bad
69
+ yield bt unless bad
70
+ end
71
+ end
72
+
73
+ # Color output
74
+ def red(str); with_color(31, str); end
75
+ def yellow(str); with_color(33, str); end
76
+ def green(str); with_color(32, str); end
77
+
78
+ # for color reference:
79
+ # http://www.pixelbeat.org/docs/terminal_colours/
80
+ def with_color(code,str)
81
+ "\e[#{code}m#{str}\e[0m"
82
+ end
83
+ end # IOReporter
84
+
85
+ end # Riot
@@ -0,0 +1,38 @@
1
+ module Riot
2
+
3
+ # This is essentially just DotMatrix with the legacy DotMatrix formatting, slightly better.
4
+ # Failure and Error outputs are color labeled and are formatted neatly and
5
+ # concisely under each associated label.
6
+ # example:
7
+ # ........FE....
8
+ # FAILURE
9
+ # A failure would have a message like this => expected 1, not 0
10
+ # (on line 26 in test/core/blah.rb)
11
+ # ERROR
12
+ # A reporter asserts this errors => Exception occured
13
+ # at test/core/report_test.rb:24:in `block (2 levels) in <top (required)>'
14
+ #
15
+ class PrettyDotMatrixReporter < DotMatrixReporter
16
+
17
+ # Prints a yellow F and formats the Fail messages a bit better
18
+ # than the default DotMatrixReporter
19
+ def fail(description, message, line, file)
20
+ print yellow('F')
21
+ @details << "#{yellow("FAILURE")}\n #{test_detail(description, message)}\n #{line_info(line, file)}".strip
22
+ end
23
+
24
+ # Prints out an red E and formats the fail message better
25
+ def error(description, e)
26
+ print red('E')
27
+ @details << "#{red("ERROR")}\n #{test_detail(description,"#{e} occured")}\n #{simple_error(e)}"
28
+ end
29
+
30
+ def simple_error(e)
31
+ format = []
32
+ filter_backtrace(e.backtrace) { |line| format << "at #{line}" }
33
+
34
+ format.join("\n")
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,18 @@
1
+ module Riot
2
+
3
+ # Basically, the Null Object pattern; nothing is output from this reporter.
4
+ class SilentReporter < Reporter
5
+ # (see Riot::Reporter#pass)
6
+ def pass(description, message); end
7
+
8
+ # (see Riot::Reporter#fail)
9
+ def fail(description, message, line, file); end
10
+
11
+ # (see Riot::Reporter#error)
12
+ def error(description, e); end
13
+
14
+ # (see Riot::Reporter#results)
15
+ def results(time_taken); end
16
+ end # SilentReporter
17
+
18
+ end # Riot
@@ -0,0 +1,52 @@
1
+ module Riot
2
+
3
+ # For each context that is started and assertion that is run, its description is printed to the console
4
+ # on its own line. Regarding assertions, if ansi-colors are available then passing assertions are printed
5
+ # in green, failing in yellow, and errors in red. Note that backtraces are not reported for errors; see
6
+ # {Riot::VerboseStoryReporter}.
7
+ class StoryReporter < IOReporter
8
+ # Prints the descrition of the context on its own line
9
+ #
10
+ # @param (see Riot::Reporter#describe_context)
11
+ def describe_context(context)
12
+ super
13
+ puts context.detailed_description
14
+ end
15
+
16
+ # Prints the description of the assertion. Prints in green if possible.
17
+ #
18
+ # @param (see Riot::Reporter#pass)
19
+ def pass(description, message)
20
+ puts " + " + green("#{description} #{message}".strip)
21
+ end
22
+
23
+ # Prints the description of the assertion and the line number of the failure. Prints in yellow if
24
+ # possible.
25
+ #
26
+ # @param (see Riot::Reporter#fail)
27
+ def fail(description, message, line, file)
28
+ puts " - " + yellow("#{description}: #{message} #{line_info(line, file)}".strip)
29
+ end
30
+
31
+ # Prints the description of the assertion and the exception message. Prints in red if
32
+ # possible.
33
+ #
34
+ # @param (see Riot::Reporter#error)
35
+ def error(description, e)
36
+ puts " ! " + red("#{description}: #{e.message}")
37
+ end
38
+ end # StoryReporter
39
+
40
+ # Same as {Riot::StoryReporter} except that backtraces are printed for assertions with errors
41
+ class VerboseStoryReporter < StoryReporter
42
+ # Prints the description of the assertion and the exception backtrace. Prints in red if
43
+ # possible.
44
+ #
45
+ # @param (see Riot::Reporter#error)
46
+ def error(description, e)
47
+ super
48
+ puts red(format_error(e))
49
+ end
50
+ end # VerboseStoryReporter
51
+
52
+ end # Riot
@@ -1,13 +1,29 @@
1
1
  require 'rr'
2
2
 
3
3
  module Riot
4
+ # Enables inherent RR support in Riot. When required in, all contexts and assertions are adapted to have
5
+ # RR support.
4
6
  module RR
5
7
 
8
+ # Basically, provides a {Riot::Situation} that has been adapted to RR. This means that any of the
9
+ # RR methods that would typically be used: +mock+, +stub+, +verify+, etc. will be available to any
10
+ # setup, yeardown, helper, hookup, or assertion.
6
11
  class Situation < Riot::Situation
7
12
  include ::RR::Adapters::RRMethods
8
13
  end # Situation
9
14
 
15
+ # Binds the {Riot::Assertion} to RR so that successes and failures found by RR are inherently handled
16
+ # during an assertion evaluation. In effect, if RR suggests failure during validation, the assertion
17
+ # will fail and report these failures.
10
18
  class Assertion < Riot::Assertion
19
+ # Adds RR support to {Riot::Assertion#run}. The basic flow is to:
20
+ # * run the test as normal
21
+ # * ask RR to verify mock results
22
+ # * report any errors or return the result of the assertion as normal
23
+ # * reset RR so that the next assertion in the context can be verified cleanly.
24
+ #
25
+ # @param (see Riot::Assertion#run)
26
+ # @return (see Riot::Assertion#run)
11
27
  def run(situation)
12
28
  result = super
13
29
  situation.verify
@@ -19,14 +35,22 @@ module Riot
19
35
  end
20
36
  end # Assertion
21
37
 
22
- module ContextHelpers
23
- private
38
+ # Redefines the classes {Riot::Context} will use when creating new assertions and situations to be the
39
+ # ones provided RR support. See {Riot::RR::Assertion} and {Riot::RR::Situation}.
40
+ module ContextClassOverrides
41
+ # (see Riot::ContextClassOverrides#assertion_class)
24
42
  def assertion_class; Riot::RR::Assertion; end
43
+
44
+ # (see Riot::ContextClassOverrides#situation_class)
25
45
  def situation_class; Riot::RR::Situation; end
26
- end # ContextHelpers
46
+ end # ContextClassOverrides
27
47
 
48
+ # A convenience method for telling {Riot::RR::ContextClassOverrides} to mix itself into {Riot::Context}.
49
+ # Thus, enabling RR support in Riot.
50
+ #
51
+ # @param [Class] context_class the class representing the Context to bind to
28
52
  def self.enable(context_class)
29
- context_class.instance_eval { include Riot::RR::ContextHelpers }
53
+ context_class.instance_eval { include Riot::RR::ContextClassOverrides }
30
54
  end
31
55
 
32
56
  end # RR
@@ -1,30 +1,83 @@
1
1
  module Riot
2
+ # A runnable block is pretty much an anonymous block decorator. It's goal is to accept a block, a
3
+ # description for what the block is intended to be for, and provide a +run+ method expects a
4
+ # {Riot::Situation} instance. Any time a {Riot::Situation} is provided to +run+, a {Riot::RunnableBlock}
5
+ # promises to evaluate itself against the situation in some way.
6
+ #
7
+ # Intent is to sub-class {Riot::RunnableBlock} with specific mechanisms for contorting the
8
+ # {Riot::Situation}.
2
9
  class RunnableBlock
10
+
11
+ # The decorated block.
3
12
  attr_reader :definition
13
+
14
+ # Creates a new RunnableBlock.
15
+ #
16
+ # @param [String] description a description of what this block is for
17
+ # @param [lambda] &definition the block to decorate
4
18
  def initialize(description, &definition)
5
19
  @description, @definition = description, definition || proc { false }
6
20
  end
7
21
 
22
+ # Given a {Riot::Situation}, eval the provided block against it and then return a status tuple.
23
+ #
24
+ # @param [Riot::Situation] situation An instance of a {Riot::Situation}
25
+ # @return [Array<Symbol[, String]>] array containing at least an evaluation state
26
+ def run(situation)
27
+ raise "Define your own run"
28
+ end
29
+
30
+ # String representation of this block, which is basically the description.
31
+ #
32
+ # @return [String]
8
33
  def to_s; @description; end
9
34
  end # RunnableBlock
10
35
 
36
+ # Used to decorate a setup, hookup, a teardown or anything else that is about context administration.
11
37
  class Setup < RunnableBlock
12
38
  def initialize(&definition)
13
39
  super("setup", &definition)
14
40
  end
15
41
 
42
+ # Calls {Riot::Situation#setup} with the predefined block at {Riot::Context} run-time. Though this is
43
+ # like every other kind of {Riot::RunnableBlock}, +run+ will not return a meaningful state, which means
44
+ # the reporter will likely not report anything.
45
+ #
46
+ # @param [Riot::Situation] situation the situation for the current {Riot::Context} run
47
+ # @return [Array<Symbol>] array containing the evaluation state
16
48
  def run(situation)
17
49
  situation.setup(&definition)
18
50
  [:setup]
19
51
  end
20
52
  end # Setup
21
53
 
54
+ # Used to decorate a helper. A helper generally ends up being a glorified method that can be referenced
55
+ # from within a setup, teardown, hookup, other helpers, assertion blocks, and assertion macro blocks;
56
+ # basically anywhere the {Riot::Situation} instance is available.
57
+ #
58
+ # context "Making dinner" do
59
+ # helper(:easy_bake) do |thing|
60
+ # EasyBake.new(thing, :ingredients => [:ketchup, :chocolate, :syrup])
61
+ # end
62
+ #
63
+ # setup do
64
+ # easy_bake(:meatloaf)
65
+ # end
66
+ #
67
+ # asserts(:food_poisoning_probabilty).equals(0.947)
68
+ # end # Making dinner
22
69
  class Helper < RunnableBlock
23
70
  def initialize(name, &definition)
24
71
  super("helper #{name}", &definition)
25
72
  @name = name
26
73
  end
27
74
 
75
+ # Calls {Riot::Situation#helper} with the predefined helper name and block at {Riot::Context} run-time.
76
+ # Though this is like every other kind of {Riot::RunnableBlock}, +run+ will not return a meaningful
77
+ # state, which means the reporter will likely not report anything.
78
+ #
79
+ # @param [Riot::Situation] situation the situation for the current {Riot::Context} run
80
+ # @return [Array<Symbol>] array containing the evaluation state
28
81
  def run(situation)
29
82
  situation.helper(@name, &definition)
30
83
  [:helper]
@@ -1,17 +1,62 @@
1
1
  module Riot
2
+ # A {Riot::Situation} is virtually a stack frame for a single context run. The intent is for all blocks to
3
+ # be evaluated against an instance of a Situation. Given the following superfluous context:
4
+ #
5
+ # context "Foo" do # block-1
6
+ #
7
+ # setup do # block-2
8
+ # {:bar => "baz"}
9
+ # end
10
+ #
11
+ # asserts "its hash" do # block-3
12
+ # topic
13
+ # end.equals do # block-4
14
+ # {:bar => "baz"}
15
+ # end
16
+ #
17
+ # end # Foo
18
+ #
19
+ # In this example, block-1 will be evaluated against a {Riot::Context} instance. Whereas block-2, block-3,
20
+ # and block-4 will all be evaluated against the same Situation instance. Situation instances (situations)
21
+ # are bound to a single context run; they are not shared across context runs, regardless of their position
22
+ # in the test tree structure.
23
+ #
24
+ # What is gained from doing it this way is:
25
+ # * variables, methods, etc. set in one situation do not contaminate any others
26
+ # * variables, methods, etc. defined during a context run do not stick with the context itself
27
+ # * which means that testing state is independent of the test definitions themselves
2
28
  class Situation
29
+
30
+ # Returns the currrently tracked value of +topic+
31
+ #
32
+ # @return [Object] whatever the topic is currently set to
3
33
  def topic
4
34
  @_topic
5
35
  end
6
36
 
37
+ # This is where a setup block is actually evaluated and the +topic+ tracked.
38
+ #
39
+ # @param [lambda] &block a setup block
40
+ # @return [Object] the current topic value
7
41
  def setup(&block)
8
42
  @_topic = self.instance_eval(&block)
9
43
  end
10
44
 
45
+ # This is where a defined helper is born. A method is defined against the current instance of +self+.
46
+ # This method will not be defined on any other instances of Situation created afterwards.
47
+ #
48
+ # @param [Symbol, String] name the name of the helper being defined
49
+ # @param [lambda] &block the code to execute whennever the helper is called
11
50
  def helper(name, &block)
12
51
  (class << self; self; end).send(:define_method, name, &block)
13
52
  end
14
53
 
54
+ # Anonymously evaluates any block given to it against the current instance of +self+. This is how
55
+ # {Riot::Assertion assertion} and {Riot::AssertionMAcro assertion macro} blocks are evaluated,
56
+ # for instance.
57
+ #
58
+ # @param [lambda] &block the block to evaluate against +self+
59
+ # @return [Object] whatever the block would have returned
15
60
  def evaluate(&block)
16
61
  self.instance_eval(&block)
17
62
  end