spicycode-micronaut 0.0.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 (47) hide show
  1. data/LICENSE +20 -0
  2. data/README +17 -0
  3. data/Rakefile +55 -0
  4. data/examples/example_helper.rb +19 -0
  5. data/examples/lib/micronaut/example_group_example.rb +116 -0
  6. data/examples/lib/micronaut/example_runner_example.rb +5 -0
  7. data/examples/lib/micronaut/expectations/differs/default_example.rb +126 -0
  8. data/examples/lib/micronaut/expectations/extensions/object_example.rb +78 -0
  9. data/examples/lib/micronaut/expectations/fail_with_example.rb +71 -0
  10. data/examples/lib/micronaut/expectations/wrap_expectation_example.rb +30 -0
  11. data/examples/lib/micronaut_example.rb +8 -0
  12. data/lib/autotest/discover.rb +3 -0
  13. data/lib/autotest/micronaut.rb +43 -0
  14. data/lib/micronaut/example_group.rb +100 -0
  15. data/lib/micronaut/example_runner.rb +108 -0
  16. data/lib/micronaut/example_world.rb +13 -0
  17. data/lib/micronaut/exceptions.rb +7 -0
  18. data/lib/micronaut/expectations/differs/default.rb +67 -0
  19. data/lib/micronaut/expectations/handler.rb +52 -0
  20. data/lib/micronaut/expectations/object_extensions.rb +62 -0
  21. data/lib/micronaut/expectations/string_and_symbol_extensions.rb +17 -0
  22. data/lib/micronaut/expectations/wrap_expectation.rb +51 -0
  23. data/lib/micronaut/expectations.rb +57 -0
  24. data/lib/micronaut/extensions/kernel.rb +15 -0
  25. data/lib/micronaut/matchers/be.rb +203 -0
  26. data/lib/micronaut/matchers/be_close.rb +37 -0
  27. data/lib/micronaut/matchers/change.rb +148 -0
  28. data/lib/micronaut/matchers/eql.rb +43 -0
  29. data/lib/micronaut/matchers/equal.rb +43 -0
  30. data/lib/micronaut/matchers/errors.rb +5 -0
  31. data/lib/micronaut/matchers/exist.rb +22 -0
  32. data/lib/micronaut/matchers/generated_descriptions.rb +36 -0
  33. data/lib/micronaut/matchers/has.rb +34 -0
  34. data/lib/micronaut/matchers/have.rb +150 -0
  35. data/lib/micronaut/matchers/include.rb +77 -0
  36. data/lib/micronaut/matchers/match.rb +41 -0
  37. data/lib/micronaut/matchers/method_missing.rb +9 -0
  38. data/lib/micronaut/matchers/operator_matcher.rb +72 -0
  39. data/lib/micronaut/matchers/raise_error.rb +132 -0
  40. data/lib/micronaut/matchers/respond_to.rb +46 -0
  41. data/lib/micronaut/matchers/satisfy.rb +47 -0
  42. data/lib/micronaut/matchers/simple_matcher.rb +132 -0
  43. data/lib/micronaut/matchers/throw_symbol.rb +100 -0
  44. data/lib/micronaut/matchers.rb +141 -0
  45. data/lib/micronaut/mocking/with_mocha.rb +23 -0
  46. data/lib/micronaut.rb +42 -0
  47. metadata +120 -0
@@ -0,0 +1,100 @@
1
+ class Micronaut::ExampleGroup
2
+ include Micronaut::Matchers
3
+ include Micronaut::Mocking::WithMocha
4
+
5
+ attr_reader :name, :description, :examples, :before_parts, :after_parts
6
+
7
+ def initialize(const_or_name, description=nil)
8
+ @name, @description = const_or_name.to_s, description
9
+ @examples, @before_parts, @after_parts = [], {:each => [], :all => []}, {:each => [], :all => []}
10
+ end
11
+
12
+ def before(type = :each, &block)
13
+ @before_parts[type] << block
14
+ end
15
+
16
+ def before_each_parts
17
+ @before_parts[:each]
18
+ end
19
+
20
+ def before_all_parts
21
+ @before_parts[:all]
22
+ end
23
+
24
+ def after(type = :each, &block)
25
+ @after_parts[type] << block
26
+ end
27
+
28
+ def after_each_parts
29
+ @after_parts[:each]
30
+ end
31
+
32
+ def after_all_parts
33
+ @after_parts[:all]
34
+ end
35
+
36
+ def it(example_description, &example_block)
37
+ @examples << [example_description, example_block]
38
+ end
39
+
40
+ def run
41
+ before_all_parts.each { |part| part.call }
42
+
43
+ @examples.each do |example_description, example_block|
44
+
45
+ before_each_parts.each { |part| part.call }
46
+
47
+ example_block.call
48
+
49
+ after_each_parts.each { |part| part.call }
50
+
51
+ end
52
+
53
+ after_all_parts.each { |part| part.call }
54
+ end
55
+
56
+ def run_group_using(runner)
57
+ result = ''
58
+
59
+ begin
60
+ @passed = nil
61
+
62
+ before_all_parts.each { |part| part.call }
63
+
64
+ @examples.each do |example_description, example_block|
65
+
66
+ setup_mocks
67
+
68
+ before_each_parts.each { |part| part.call }
69
+
70
+ if example_block.nil?
71
+ result << 'P'
72
+ else
73
+ example_block.call
74
+ end
75
+
76
+ result << '.'
77
+
78
+ after_each_parts.each { |part| part.call }
79
+
80
+ verify_mocks
81
+
82
+ teardown_mocks
83
+ end
84
+
85
+ @passed = true
86
+ rescue Exception => e
87
+ result << runner.complain(self.class, self.name, e)
88
+ @passed = false
89
+ ensure
90
+ begin
91
+ after_all_parts.each { |part| part.call }
92
+ rescue Exception => e
93
+ result << runner.complain(self.class, self.name, e)
94
+ end
95
+ end
96
+
97
+ result
98
+ end
99
+
100
+ end
@@ -0,0 +1,108 @@
1
+ module Micronaut
2
+ class ExampleRunner
3
+
4
+ attr_accessor :report, :failures, :errors
5
+ attr_accessor :example_count
6
+
7
+ @@installed_at_exit ||= false
8
+ @@out = $stdout
9
+
10
+ def initialize
11
+ @report = []
12
+ @errors, @failures = 0, 0
13
+ @verbose = false
14
+ end
15
+
16
+ def self.autorun
17
+ at_exit {
18
+ exit_code = Micronaut::ExampleRunner.new.run(ARGV)
19
+ exit false if exit_code && exit_code != 0
20
+ } unless @@installed_at_exit
21
+ @@installed_at_exit = true
22
+ end
23
+
24
+ def self.output=(stream)
25
+ @@out = stream
26
+ end
27
+
28
+ def location(e)
29
+ e.backtrace.find { |s|
30
+ s !~ /in .(assert|refute|flunk|pass|fail|raise)/
31
+ }.sub(/:in .*$/, '')
32
+ end
33
+
34
+ def complain(cls, method_in_question, e)
35
+ bt = Micronaut::filter_backtrace(e.backtrace).join("\n ")
36
+
37
+ e = case e.class.to_s
38
+ when 'Micronaut::Exceptions::ExpectationNotMetError', 'Mocha::ExpectationError' then
39
+ @failures += 1
40
+ "Failure:\n#{method_in_question}(#{cls}) [#{location(e)}]:\n#{e.message}\n #{bt}\n"
41
+ else
42
+ puts e.to_s
43
+ @errors += 1
44
+ "Error:\n#{method_in_question}(#{cls}):\n#{e.class}: #{e.message}\n #{bt}\n"
45
+ end
46
+ @report << e
47
+ e[0, 1]
48
+ end
49
+
50
+ ##
51
+ # Top level driver, controls all output and filtering.
52
+ def run(args = [])
53
+ @verbose = args.delete('-v')
54
+
55
+ filter = if args.first =~ /^(-n|--name)$/ then
56
+ args.shift
57
+ arg = args.shift
58
+ arg =~ /\/(.*)\// ? Regexp.new($1) : arg
59
+ else
60
+ /./ # anything - ^example_ already filtered by #examples
61
+ end
62
+
63
+ @@out.puts "Loading examples from #{$0}\nStarted"
64
+
65
+ start = Time.now
66
+ run_examples filter
67
+
68
+ @@out.puts
69
+ @@out.puts "Finished in #{'%.6f' % (Time.now - start)} seconds."
70
+
71
+ @report.each_with_index do |msg, i|
72
+ @@out.puts "\n%3d) %s" % [i + 1, msg]
73
+ end
74
+
75
+ @@out.puts
76
+
77
+ format = "%d examples, %d failures, %d errors"
78
+ @@out.puts format % [example_count, failures, errors]
79
+
80
+ return failures + errors if @example_count > 0 # or return nil...
81
+ end
82
+
83
+ def run_examples(filter = /./)
84
+ @example_count = 0
85
+ old_sync, @@out.sync = @@out.sync, true if @@out.respond_to?(:sync=)
86
+
87
+ Micronaut::ExampleWorld.example_groups.each do |example_group|
88
+ # example_group.examples.grep(filter).each do |test|
89
+ # example_group.examples.each do |example|
90
+ @@out.print "#{example_group.name}: " if @verbose
91
+
92
+ t = Time.now if @verbose
93
+ result = example_group.run_group_using(self)
94
+
95
+ @example_count += example_group.examples.size
96
+
97
+ @@out.print "%.6fs: " % (Time.now - t) if @verbose
98
+ @@out.print result
99
+ @@out.puts if @verbose
100
+ # end
101
+ end
102
+
103
+ @@out.sync = old_sync if @@out.respond_to? :sync=
104
+ @example_count
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,13 @@
1
+ class Micronaut::ExampleWorld
2
+
3
+ def self.reset
4
+ @@example_groups = []
5
+ end
6
+
7
+ reset
8
+
9
+ def self.example_groups
10
+ @@example_groups
11
+ end
12
+
13
+ end
@@ -0,0 +1,7 @@
1
+ module Micronaut
2
+ class Exceptions
3
+
4
+ class ExpectationNotMetError < ::StandardError; end
5
+
6
+ end
7
+ end
@@ -0,0 +1,67 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'diff/lcs' #necessary due to loading bug on some machines - not sure why - DaC
4
+ require 'diff/lcs/hunk'
5
+ rescue LoadError ; raise "You must gem install diff-lcs to use diffing" ; end
6
+
7
+ require 'pp'
8
+
9
+ module Micronaut
10
+ module Expectations
11
+ module Differs
12
+
13
+ # TODO add some rdoc
14
+ class Default
15
+ def initialize(diff_type = :unified, context_lines = 3)
16
+ @format = diff_type
17
+ @context_lines = context_lines
18
+ end
19
+
20
+ # This is snagged from diff/lcs/ldiff.rb (which is a commandline tool)
21
+ def diff_as_string(data_new, data_old)
22
+ data_old = data_old.split(/\n/).map! { |e| e.chomp }
23
+ data_new = data_new.split(/\n/).map! { |e| e.chomp }
24
+ output = ""
25
+ diffs = Diff::LCS.diff(data_old, data_new)
26
+ return output if diffs.empty?
27
+ oldhunk = hunk = nil
28
+ file_length_difference = 0
29
+ diffs.each do |piece|
30
+ begin
31
+ hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, context_lines,
32
+ file_length_difference)
33
+ file_length_difference = hunk.file_length_difference
34
+ next unless oldhunk
35
+ # Hunks may overlap, which is why we need to be careful when our
36
+ # diff includes lines of context. Otherwise, we might print
37
+ # redundant lines.
38
+ if (context_lines > 0) and hunk.overlaps?(oldhunk)
39
+ hunk.unshift(oldhunk)
40
+ else
41
+ output << oldhunk.diff(format)
42
+ end
43
+ ensure
44
+ oldhunk = hunk
45
+ output << "\n"
46
+ end
47
+ end
48
+ #Handle the last remaining hunk
49
+ output << oldhunk.diff(format) << "\n"
50
+ end
51
+
52
+ def diff_as_object(target,expected)
53
+ diff_as_string(PP.pp(target,""), PP.pp(expected,""))
54
+ end
55
+
56
+ protected
57
+ def format
58
+ @format
59
+ end
60
+
61
+ def context_lines
62
+ @context_lines
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,52 @@
1
+ module Micronaut
2
+ module Expectations
3
+ class InvalidMatcherError < ArgumentError; end
4
+
5
+ class ExpectationMatcherHandler
6
+ class << self
7
+ def handle_matcher(actual, matcher, &block)
8
+ ::Micronaut::Matchers.last_should = "should"
9
+ return Micronaut::Matchers::PositiveOperatorMatcher.new(actual) if matcher.nil?
10
+
11
+ unless matcher.respond_to?(:matches?)
12
+ raise InvalidMatcherError, "Expected a matcher, got #{matcher.inspect}."
13
+ end
14
+
15
+ match = matcher.matches?(actual, &block)
16
+ ::Micronaut::Matchers.last_matcher = matcher
17
+ Micronaut::Expectations.fail_with(matcher.failure_message) unless match
18
+ match
19
+ end
20
+ end
21
+ end
22
+
23
+ class NegativeExpectationMatcherHandler
24
+ class << self
25
+ def handle_matcher(actual, matcher, &block)
26
+ ::Micronaut::Matchers.last_should = "should not"
27
+ return Micronaut::Matchers::NegativeOperatorMatcher.new(actual) if matcher.nil?
28
+
29
+ unless matcher.respond_to?(:matches?)
30
+ raise InvalidMatcherError, "Expected a matcher, got #{matcher.inspect}."
31
+ end
32
+
33
+ unless matcher.respond_to?(:negative_failure_message)
34
+ Micronaut::Expectations.fail_with(
35
+ <<-EOF
36
+ Matcher does not support should_not.
37
+ See Micronaut::Matchers for more information
38
+ about matchers.
39
+ EOF
40
+ )
41
+ end
42
+ match = matcher.matches?(actual, &block)
43
+ ::Micronaut::Matchers.last_matcher = matcher
44
+ Micronaut::Expectations.fail_with(matcher.negative_failure_message) if match
45
+ match
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+
@@ -0,0 +1,62 @@
1
+ module Micronaut
2
+ module Expectations
3
+ # We add #should and #should_not to every Object (and, implicitly, every Class).
4
+ module ObjectExtensions
5
+ # :call-seq:
6
+ # should(matcher)
7
+ # should == expected
8
+ # should === expected
9
+ # should =~ expected
10
+ #
11
+ # receiver.should(matcher)
12
+ # => Passes if matcher.matches?(receiver)
13
+ #
14
+ # receiver.should == expected #any value
15
+ # => Passes if (receiver == expected)
16
+ #
17
+ # receiver.should === expected #any value
18
+ # => Passes if (receiver === expected)
19
+ #
20
+ # receiver.should =~ regexp
21
+ # => Passes if (receiver =~ regexp)
22
+ #
23
+ # See Micronaut::Matchers for more information about matchers
24
+ #
25
+ # == Warning
26
+ #
27
+ # NOTE that this does NOT support receiver.should != expected.
28
+ # Instead, use receiver.should_not == expected
29
+ def should(matcher=nil, &block)
30
+ ExpectationMatcherHandler.handle_matcher(self, matcher, &block)
31
+ end
32
+
33
+ # :call-seq:
34
+ # should_not(matcher)
35
+ # should_not == expected
36
+ # should_not === expected
37
+ # should_not =~ expected
38
+ #
39
+ # receiver.should_not(matcher)
40
+ # => Passes unless matcher.matches?(receiver)
41
+ #
42
+ # receiver.should_not == expected
43
+ # => Passes unless (receiver == expected)
44
+ #
45
+ # receiver.should_not === expected
46
+ # => Passes unless (receiver === expected)
47
+ #
48
+ # receiver.should_not =~ regexp
49
+ # => Passes unless (receiver =~ regexp)
50
+ #
51
+ # See Micronaut::Matchers for more information about matchers
52
+ def should_not(matcher=nil, &block)
53
+ NegativeExpectationMatcherHandler.handle_matcher(self, matcher, &block)
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+
60
+ class Object
61
+ include Micronaut::Expectations::ObjectExtensions
62
+ end
@@ -0,0 +1,17 @@
1
+ module Micronaut
2
+ module Expectations
3
+ module StringHelpers
4
+ def starts_with?(prefix)
5
+ to_s[0..(prefix.to_s.length - 1)] == prefix.to_s
6
+ end
7
+ end
8
+ end
9
+ end
10
+
11
+ class String
12
+ include Micronaut::Expectations::StringHelpers
13
+ end
14
+
15
+ class Symbol
16
+ include Micronaut::Expectations::StringHelpers
17
+ end
@@ -0,0 +1,51 @@
1
+ module Micronaut
2
+ module Matchers
3
+
4
+ # wraps an expectation in a block that will return true if the
5
+ # expectation passes and false if it fails (without bubbling up
6
+ # the failure).
7
+ #
8
+ # == Examples
9
+ #
10
+ # def eat_cheese(cheese)
11
+ # simple_matcher do |mouse, matcher|
12
+ # matcher.negative_failure_message = "expected #{mouse} not to eat cheese"
13
+ # wrap_expectation do |matcher|
14
+ # assert_eats_cheese(mouse)
15
+ # end
16
+ # end
17
+ # end
18
+ #
19
+ # describe Mouse do
20
+ # it "eats cheese" do
21
+ # Mouse.new.should eat_cheese
22
+ # end
23
+ # end
24
+ #
25
+ # You might be wondering "why would I do this if I could just say"
26
+ # assert_eats_cheese?", a fair question, indeed. You might prefer
27
+ # to replace the word assert with something more aligned with the
28
+ # rest of your code examples. You are using rspec, after all.
29
+ #
30
+ # The other benefit you get is that you can use the negative version
31
+ # of the matcher:
32
+ #
33
+ # describe Cat do
34
+ # it "does not eat cheese" do
35
+ # Cat.new.should_not eat_cheese
36
+ # end
37
+ # end
38
+ #
39
+ # So in the event there is no assert_does_not_eat_cheese available,
40
+ # you're all set!
41
+ def wrap_expectation(matcher, &block)
42
+ begin
43
+ block.call(matcher)
44
+ return true
45
+ rescue Exception => e
46
+ matcher.failure_message = e.message
47
+ return false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,57 @@
1
+ require 'micronaut/matchers'
2
+ require 'micronaut/expectations/object_extensions'
3
+ require 'micronaut/expectations/string_and_symbol_extensions'
4
+ require 'micronaut/expectations/handler'
5
+ require 'micronaut/expectations/wrap_expectation'
6
+
7
+ module Micronaut
8
+
9
+ # Micronaut::Expectations lets you set expectations on your objects.
10
+ #
11
+ # result.should == 37
12
+ # team.should have(11).players_on_the_field
13
+ #
14
+ # == How Expectations work.
15
+ #
16
+ # Micronaut::Expectations adds two methods to Object:
17
+ #
18
+ # should(matcher=nil)
19
+ # should_not(matcher=nil)
20
+ #
21
+ # Both methods take an optional Expression Matcher (See Micronaut::Matchers).
22
+ #
23
+ # When +should+ receives an Expression Matcher, it calls <tt>matches?(self)</tt>. If
24
+ # it returns +true+, the spec passes and execution continues. If it returns
25
+ # +false+, then the spec fails with the message returned by <tt>matcher.failure_message</tt>.
26
+ #
27
+ # Similarly, when +should_not+ receives a matcher, it calls <tt>matches?(self)</tt>. If
28
+ # it returns +false+, the spec passes and execution continues. If it returns
29
+ # +true+, then the spec fails with the message returned by <tt>matcher.negative_failure_message</tt>.
30
+ #
31
+ # Micronaut ships with a standard set of useful matchers, and writing your own
32
+ # matchers is quite simple. See Micronaut::Matchers for details.
33
+ module Expectations
34
+ class << self
35
+ attr_accessor :differ
36
+
37
+ # raises a Micronaut::Exceptions::ExpectationNotMetError with message
38
+ #
39
+ # When a differ has been assigned and fail_with is passed
40
+ # <code>expected</code> and <code>target</code>, passes them
41
+ # to the differ to append a diff message to the failure message.
42
+ def fail_with(message, expected=nil, target=nil) # :nodoc:
43
+ if Array === message && message.length == 3
44
+ message, expected, target = message[0], message[1], message[2]
45
+ end
46
+ unless (differ.nil? || expected.nil? || target.nil?)
47
+ if expected.is_a?(String)
48
+ message << "\nDiff:" << self.differ.diff_as_string(target.to_s, expected)
49
+ elsif !target.is_a?(Proc)
50
+ message << "\nDiff:" << self.differ.diff_as_object(target, expected)
51
+ end
52
+ end
53
+ Kernel::raise(Micronaut::Exceptions::ExpectationNotMetError.new(message))
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,15 @@
1
+ module Micronaut
2
+ module Extensions
3
+ module Kernel
4
+
5
+ def describe(name_or_const, desc=nil, &describe_block)
6
+ example_group = Micronaut::ExampleGroup.new(name_or_const, desc)
7
+ example_group.instance_eval(&describe_block)
8
+ Micronaut::ExampleWorld.example_groups << example_group
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+
15
+ include Micronaut::Extensions::Kernel