spicycode-micronaut 0.0.2

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