synthesis 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING CHANGED
File without changes
data/README CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  == Philosophy
4
4
 
5
- Currently we believe that developers are writing unnecessary functional tests to cover uncertainty about the validity of simulated interactions in their unit tests. In other words, we cannot be certain that all our unit tests 'join up'. If it were possible to correlate the simulated interactions in our unit tests, then we should be able to do away with the need to write large numbers of slow and annoying functional tests (apart from those which interact with the boundaries of the SUT).
5
+ Currently we believe that developers are writing unnecessary <em>dependency wired</em> tests to cover uncertainty about the validity of simulated interactions in their <em>dependency neutral</em> tests. In other words, we cannot be certain that all our simulated interaction based tests 'join up'. If it were possible to correlate the simulated interactions in our tests, then we should be able to do away with the need to write large numbers of complex, slow and brittle wired tests (apart from those which interact with the boundaries of the SUT).
6
+
7
+ Synthesis combines lightweight, <em>dependency neutral</em> tests to build confidence that the system under test is complete and reduces the need for large, overarching tests.
6
8
 
7
9
  == Installation
8
10
 
@@ -43,13 +45,13 @@ To use with Test::Unit and Mocha, ignoring Array and Hash:
43
45
  t.ignore Array, Hash
44
46
  end
45
47
 
46
- To use with RSpec, running all specs in the <tt>specs</tt> directory:
48
+ To use with RSpec, running all specs in the <tt>spec</tt> directory:
47
49
 
48
50
  require "synthesis/task"
49
51
 
50
52
  Synthesis::Task.new do |t|
51
53
  t.adapter = :rspec
52
- t.pattern = 'specs/*_spec.rb'
54
+ t.pattern = 'spec/**/*_spec.rb'
53
55
  end
54
56
 
55
57
  To use with Expectations, redirecting output to a file
data/Rakefile CHANGED
@@ -91,7 +91,7 @@ end
91
91
 
92
92
  gem_spec = Gem::Specification.new do |s|
93
93
  s.name = 'synthesis'
94
- s.version = '0.0.7'
94
+ s.version = '0.0.8'
95
95
  s.platform = Gem::Platform::RUBY
96
96
  s.rubyforge_project = "synthesis"
97
97
  s.summary, s.description = 'A tool for Synthesized Testing'
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
6
6
 
7
7
  module Synthesis
8
8
  class ExpectationsAdapter < Adapter
9
-
10
- ignore_instances_of Class::AnyInstance
11
- expectation_class Mocha::Expectation
12
- intercept :method => :expects, :on => Object
13
-
14
9
  def run
15
10
  fail_unless { Expectations::SuiteRunner.instance.suite.execute }
16
11
  end
12
+
13
+ def collect_expectations
14
+ ignore_instances_of Class::AnyInstance
15
+ Object.extend(ExpectationRecordEnabled)
16
+ Object.record_expectations_on(:expects)
17
+ Mocha::Expectation.extend(ExpectationInterceptor)
18
+ Mocha::Expectation.intercept_expected_argument_types_on(:with)
19
+ Mocha::Expectation.intercept_expected_return_values_on(:returns)
20
+ end
21
+
22
+ def stop_collecting_expectations
23
+ Mocha::Expectation.reset!
24
+ Object.reset!
25
+ end
17
26
  end
18
27
  end
19
28
 
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
6
6
 
7
7
  module Synthesis
8
8
  class MochaAdapter < Adapter
9
-
10
- ignore_instances_of Class::AnyInstance
11
- expectation_class Mocha::Expectation
12
- intercept :method => :expects, :on => Object
13
-
14
9
  def run
15
10
  Test::Unit.run = true # Yes means no...
16
11
  fail_unless { Test::Unit::AutoRunner.run }
17
12
  end
13
+
14
+ def collect_expectations
15
+ ignore_instances_of Class::AnyInstance
16
+ Object.extend(ExpectationRecordEnabled)
17
+ Object.record_expectations_on(:expects)
18
+ Mocha::Expectation.extend(ExpectationInterceptor)
19
+ Mocha::Expectation.intercept_expected_argument_types_on(:with)
20
+ Mocha::Expectation.intercept_expected_return_values_on(:returns)
21
+ end
22
+
23
+ def stop_collecting_expectations
24
+ Mocha::Expectation.reset!
25
+ Object.reset!
26
+ end
18
27
  end
19
28
  end
@@ -6,14 +6,23 @@ require File.dirname(__FILE__) + "/../../synthesis"
6
6
 
7
7
  module Synthesis
8
8
  class RSpecAdapter < Adapter
9
-
10
- ignore_instances_of Spec::Mocks::Mock
11
- expectation_class Spec::Mocks::MessageExpectation
12
- intercept :method => :should_receive, :on => Spec::Mocks::Methods
13
-
14
9
  def run
15
10
  rspec_options.files.clear
16
11
  fail_unless { rspec_options.run_examples }
17
12
  end
13
+
14
+ def collect_expectations
15
+ ignore_instances_of Spec::Mocks::Mock
16
+ Spec::Mocks::Methods.extend(ExpectationRecordEnabled)
17
+ Spec::Mocks::Methods.record_expectations_on(:should_receive)
18
+ Spec::Mocks::MessageExpectation.extend(ExpectationInterceptor)
19
+ Spec::Mocks::MessageExpectation.intercept_expected_argument_types_on(:with)
20
+ Spec::Mocks::MessageExpectation.intercept_expected_return_values_on(:and_return)
21
+ end
22
+
23
+ def stop_collecting_expectations
24
+ Spec::Mocks::MessageExpectation.reset!
25
+ Spec::Mocks::Methods.reset!
26
+ end
18
27
  end
19
28
  end
@@ -1,7 +1,8 @@
1
1
  module Synthesis
2
- # Subclasses of Adapter must implement the "run" method.
3
- # For example implementations, refer to Synthesis::MochaAdapter and
4
- # Synthesis::RSpecAdapter
2
+ # Subclasses of Adapter must implement the run, collect_expectations and
3
+ # stop_collecting_expectations methods.
4
+ # For example implementations refer to Synthesis::MochaAdapter and
5
+ # Synthesis::RSpecAdapter.
5
6
  class Adapter
6
7
  include Logging
7
8
 
@@ -13,52 +14,22 @@ module Synthesis
13
14
  # once for verifying the collected expectations.
14
15
  def fail_unless(&block)
15
16
  log "Collecting expectations..."
17
+ collect_expectations
16
18
  Dir[@pattern].each { |t| require t }
17
19
  exit -1 unless yield
18
20
  log "Verifying expectation invocations..."
21
+ stop_collecting_expectations
19
22
  ExpectationRecord.record_invocations
20
23
  yield
21
24
  end
22
25
 
26
+ # The type of object representing a mock or stub for the test framework.
27
+ # Objects of this type will be ignored by Synthesis.
28
+ def ignore_instances_of(type)
29
+ Synthesis.const_set(:MOCK_OBJECT, type)
30
+ end
31
+
23
32
  class << self
24
- # The type of object representing a mock or stub for the test framework.
25
- # Objects of this type will be ignored by Synthesis.
26
- def ignore_instances_of(type)
27
- Synthesis.const_set(:MOCK_OBJECT, type)
28
- end
29
-
30
- # The class representing a mock object expectation in the test framework.
31
- # Synthesis will intercept its "with" method in order to collect the
32
- # expectation method parameters being passed to it.
33
- def expectation_class(klass)
34
- klass.class_eval do
35
- attr_accessor :synthesis_expectation
36
-
37
- alias original_with with
38
-
39
- def with(*expected_parameters, &matching_block)
40
- synthesis_expectation.args = expected_parameters if synthesis_expectation
41
- original_with *expected_parameters, &matching_block
42
- end
43
- end
44
- end
45
-
46
- # Accepts a Hash parameter with two entries, :method and :on. These
47
- # represent the name of the method used for declaring a method interaction
48
- # expectation on an object and the name of the object the method is defined
49
- # on respectively. Synthesis will intercept this method in order to collect
50
- # simulated method interactions.
51
- def intercept(params)
52
- method_name, obj = params[:method], params[:on]
53
- obj.send(:alias_method, :original_meth, method_name)
54
- obj.send(:define_method, method_name) do |meth|
55
- s_expectation = Synthesis::ExpectationRecord.add_expectation(self, meth, caller[0])
56
- m_expectation = original_meth(meth)
57
- m_expectation.synthesis_expectation = s_expectation
58
- m_expectation
59
- end
60
- end
61
-
62
33
  def inherited(subclass)
63
34
  @adapter = subclass
64
35
  end
@@ -1,5 +1,5 @@
1
1
  class Class
2
- def expectation(method, track, args = [])
3
- Synthesis::Expectation::Singleton.new(self, method, track, args)
2
+ def expectation(method, track, args = [], return_value = nil)
3
+ Synthesis::Expectation::Singleton.new(self, method, track, args, return_value)
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  module Synthesis
2
2
  module Expectation
3
- def self.new(receiver, method, track, args = [])
4
- receiver.expectation method, track, args
3
+ def self.new(receiver, method, track, args = [], return_values = [])
4
+ receiver.expectation(method, track, args, return_values)
5
5
  end
6
6
 
7
7
  class Expectation
@@ -9,13 +9,18 @@ module Synthesis
9
9
  attr_reader :receiver, :method
10
10
  attr_accessor :args
11
11
 
12
- def initialize(receiver, method, track, args)
12
+ def initialize(receiver, method, track, args, return_values)
13
13
  @receiver, @method, @track, @args = receiver, method, track, args
14
+ @return_values = return_values
14
15
  end
15
16
 
16
17
  def record_invocations
17
- meta_receiver.extend Recordable
18
- meta_receiver.recordable_method @method
18
+ meta_receiver.extend(Recordable)
19
+ meta_receiver.recordable_method(@method)
20
+ end
21
+
22
+ def eql?(other)
23
+ ExpectationMatcher.new(self, other).match?
19
24
  end
20
25
 
21
26
  def ==(other)
@@ -30,10 +35,21 @@ module Synthesis
30
35
  @invoked = true
31
36
  end
32
37
 
33
- protected
38
+ def arg_types
39
+ args.map { |arg| arg.class }
40
+ end
34
41
 
35
- def args_match?(other)
36
- self.args.map { |arg| arg.class } == other.args.map { |arg| arg.class }
42
+ def return_value_types
43
+ @return_values.map { |val| val.class }.uniq
44
+ end
45
+
46
+ def add_return_values(*vals)
47
+ @return_values_defined = true
48
+ @return_values += vals
49
+ end
50
+
51
+ def return_values_defined?
52
+ @return_values_defined
37
53
  end
38
54
  end
39
55
 
@@ -42,19 +58,18 @@ module Synthesis
42
58
  @receiver.__metaclass__
43
59
  end
44
60
 
45
- def eql?(other)
46
- return false unless other.is_a?(Synthesis::Expectation::Singleton)
47
- @receiver.name == other.receiver.name &&
48
- @method == other.method &&
49
- args_match?(other)
50
- end
51
-
52
61
  def hash
53
62
  (@receiver.name.hash * 23) + @method.hash
54
63
  end
55
64
 
65
+ def receiver_class
66
+ @receiver
67
+ end
68
+
56
69
  def to_s
57
- "#{@receiver.name}.#{@method}(#{@args.map { |arg| arg.class } * ', '}) in #{@track}"
70
+ "(#{return_value_types * ", "}) " +
71
+ "#{@receiver.name}.#{@method}(#{@args.map { |arg| arg.class } * ', '})" +
72
+ "in #{@track}"
58
73
  end
59
74
  end
60
75
 
@@ -63,19 +78,19 @@ module Synthesis
63
78
  @receiver.class
64
79
  end
65
80
 
66
- def eql?(other)
67
- return false unless other.is_a?(Synthesis::Expectation::Instance)
68
- meta_receiver.name == other.meta_receiver.name &&
69
- @method == other.method &&
70
- args_match?(other)
71
- end
72
-
73
81
  def hash
74
82
  (meta_receiver.name.hash * 23) + @method.hash
75
83
  end
76
84
 
85
+ def receiver_class
86
+ meta_receiver
87
+ end
88
+
77
89
  def to_s
78
- "#{meta_receiver.name}.new.#{@method}(#{@args.map { |arg| arg.class } * ', '}) in #{@track}"
90
+ "(#{return_value_types * ", "}) " +
91
+ "#{meta_receiver.name}.new.#{@method}" +
92
+ "(#{@args.map { |arg| arg.class } * ', '})" +
93
+ "in #{@track}"
79
94
  end
80
95
  end
81
96
 
@@ -0,0 +1,57 @@
1
+ module Synthesis
2
+ # Extend by the mock object framework's expectation mechanism to allow
3
+ # Synthesis to tap into it in order to collect simulated method arguments
4
+ # and return values.
5
+ module ExpectationInterceptor
6
+ # Intercept the mock object framework's expectation method for declaring a mocked
7
+ # method's arguments so that Synthesis can record them.
8
+ def intercept_expected_argument_types_on(method_name)
9
+ @original_with = method_name
10
+
11
+ class_eval %(
12
+ alias intercepted_#{@original_with} #{@original_with}
13
+
14
+ def #{@original_with}(*expected_parameters, &matching_block)
15
+ synthesis_expectation.args = expected_parameters if synthesis_expectation
16
+ intercepted_#{@original_with}(*expected_parameters, &matching_block)
17
+ end
18
+ )
19
+ end
20
+
21
+ # Intercept the mock object framework's expectation method for declaring a mocked
22
+ # method's return values so that Synthesis can record them.
23
+ def intercept_expected_return_values_on(method_name)
24
+ @original_returns = method_name
25
+
26
+ class_eval %(
27
+ alias intercepted_#{@original_returns} #{@original_returns}
28
+
29
+ def #{@original_returns}(*values)
30
+ mock_expectation = intercepted_#{@original_returns}(*values)
31
+ synthesis_expectation.add_return_values(*values) if synthesis_expectation
32
+ mock_expectation
33
+ end
34
+ )
35
+ end
36
+
37
+ # Restore the original methods ExpectationInterceptor has rewritten and
38
+ # undefine their intercepted counterparts. Undefine the synthesis_expectation
39
+ # accessors.
40
+ def reset!
41
+ class_eval %(
42
+ alias #{@original_with} intercepted_#{@original_with}
43
+ alias #{@original_returns} intercepted_#{@original_returns}
44
+ undef intercepted_#{@original_with}
45
+ undef intercepted_#{@original_returns}
46
+ undef synthesis_expectation
47
+ undef synthesis_expectation=
48
+ )
49
+ end
50
+
51
+ # Classes extending ExpectationInterceptor will have a synthesis_expectation
52
+ # attribute accessor added to them.
53
+ def self.extended(receiver)
54
+ receiver.send(:attr_accessor, :synthesis_expectation)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
1
+ module Synthesis
2
+ class ExpectationMatcher
3
+ def initialize(expectation_one, expectation_two)
4
+ @expectation_one, @expectation_two = expectation_one, expectation_two
5
+ end
6
+
7
+ def match?
8
+ return false unless @expectation_one.class == @expectation_two.class
9
+ return false unless @expectation_one.receiver_class == @expectation_two.receiver_class
10
+ return false unless @expectation_one.method == @expectation_two.method
11
+ return false unless @expectation_one.arg_types == @expectation_two.arg_types
12
+ return false unless return_values_match?
13
+ true
14
+ end
15
+
16
+ private
17
+
18
+ def return_values_match?
19
+ return true unless @expectation_one.return_values_defined? || @expectation_two.return_values_defined?
20
+ (@expectation_one.return_value_types & @expectation_two.return_value_types).any?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module Synthesis
2
+ # Extend by the mock object framework's construct for declaring a
3
+ # mock object so that Synthesis can tap into it in order to record
4
+ # the expectation.
5
+ module ExpectationRecordEnabled
6
+ # Intercept the mock object framework's method for declaring a mock
7
+ # object so that Synthesis can record it.
8
+ def record_expectations_on(method_name)
9
+ @original_expects = method_name
10
+
11
+ class_eval %(
12
+ alias intercepted_#{@original_expects} #{@original_expects}
13
+
14
+ def #{@original_expects}(meth)
15
+ s_expectation = Synthesis::ExpectationRecord.add_expectation(self, meth, caller[0])
16
+ m_expectation = intercepted_#{@original_expects}(meth)
17
+ m_expectation.synthesis_expectation = s_expectation
18
+ m_expectation
19
+ end
20
+ )
21
+ end
22
+
23
+ # Restore the original methods ExpectationRecordEnabled has rewritten and
24
+ # undefine their intercepted counterparts.
25
+ def reset!
26
+ class_eval %(
27
+ alias #{@original_expects} intercepted_#{@original_expects}
28
+ undef intercepted_#{@original_expects}
29
+ )
30
+ end
31
+ end
32
+ end
@@ -1,7 +1,7 @@
1
1
  module Synthesis
2
2
  class MethodInvocationWatcher
3
- def self.invoked(receiver, method, args = [])
4
- matcher = Expectation.new(receiver, method, nil, args)
3
+ def self.invoked(receiver, method, args = [], return_values = [])
4
+ matcher = Expectation.new(receiver, method, nil, args, return_values)
5
5
  ExpectationRecord[matcher].invoked!
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
1
  class Module
2
- def expectation(method, track, args = [])
3
- Synthesis::Expectation::Singleton.new(self, method, track, args)
2
+ def expectation(method, track, args = [], return_value = nil)
3
+ Synthesis::Expectation::Singleton.new(self, method, track, args, return_value)
4
4
  end
5
5
  end
@@ -3,7 +3,7 @@ class Object
3
3
  class << self; self end
4
4
  end
5
5
 
6
- def expectation(method, track, args = [])
7
- Synthesis::Expectation::Instance.new(self, method, track, args)
6
+ def expectation(method, track, args = [], return_value = nil)
7
+ Synthesis::Expectation::Instance.new(self, method, track, args, return_value)
8
8
  end
9
9
  end
@@ -1,17 +1,17 @@
1
1
  module Synthesis
2
2
  module Recordable
3
3
  def recordable_method(meth)
4
- if method_defined? meth
5
- defined_recordable_method meth
4
+ if method_defined?(meth)
5
+ defined_recordable_method(meth)
6
6
  else
7
- magic_recordable_method meth
7
+ magic_recordable_method(meth)
8
8
  end
9
9
  end
10
10
 
11
11
  protected
12
12
 
13
13
  def defined_recordable_method(meth)
14
- unless method_defined? "__recordable__#{meth}".intern
14
+ unless method_defined?("__recordable__#{meth}".intern)
15
15
  alias_method "__recordable__#{meth}".intern, meth
16
16
  class_eval @@recordable_method_def[meth]
17
17
  end
@@ -19,8 +19,9 @@ module Synthesis
19
19
 
20
20
  @@recordable_method_def = proc { |meth| %(
21
21
  def #{meth}(*args, &block)
22
- MethodInvocationWatcher.invoked(self, "#{meth}".intern, args)
23
- send "__recordable__#{meth}", *args, &block
22
+ return_value = send("__recordable__#{meth}", *args, &block)
23
+ MethodInvocationWatcher.invoked(self, "#{meth}".intern, args, [return_value])
24
+ return_value
24
25
  end
25
26
  )}
26
27
 
@@ -30,8 +31,9 @@ module Synthesis
30
31
 
31
32
  @@recordable_magic_method_def = proc { |meth| %(
32
33
  def #{meth}(*args)
33
- MethodInvocationWatcher.invoked(self, "#{meth}".intern, args)
34
- method_missing(:#{meth}, *args)
34
+ return_value = method_missing(:#{meth}, *args)
35
+ MethodInvocationWatcher.invoked(self, "#{meth}".intern, args, [return_value])
36
+ return_value
35
37
  end
36
38
 
37
39
  def __recordable__#{meth}() raise "Don't ever call me" end
@@ -0,0 +1,24 @@
1
+ module Synthesis
2
+ class Reporter
3
+ class << self
4
+ include Logging
5
+
6
+ def report
7
+ if failed?
8
+ ExpectationRecord.print_tested_expectations
9
+ ExpectationRecord.print_untested_expectations
10
+ ExpectationRecord.print_ignored
11
+ log; log "FAILED."
12
+ return -1
13
+ end
14
+ log; log "Verified #{ExpectationRecord.expectations.size} expectations"
15
+ log "SUCCESS."
16
+ 0
17
+ end
18
+
19
+ def failed?
20
+ ExpectationRecord.untested_expectations.any?
21
+ end
22
+ end
23
+ end
24
+ end
@@ -3,8 +3,7 @@ module Synthesis
3
3
  def self.run(adapter, pattern)
4
4
  require "synthesis/adapter/#{adapter}"
5
5
  Adapter.load(pattern).run
6
+ exit Reporter.report unless $!
6
7
  end
7
8
  end
8
9
  end
9
-
10
- at_exit { exit Synthesis.report! unless $! }
data/lib/synthesis.rb CHANGED
@@ -10,5 +10,8 @@ require "synthesis/module"
10
10
  require "synthesis/expectation_record"
11
11
  require "synthesis/method_invocation_watcher"
12
12
  require "synthesis/expectation"
13
- require "synthesis/report"
13
+ require "synthesis/expectation_matcher"
14
+ require "synthesis/expectation_interceptor"
15
+ require "synthesis/expectation_record_enabled"
16
+ require "synthesis/reporter"
14
17
  require "synthesis/adapter"
@@ -1,2 +1,3 @@
1
1
  %w(test/unit rubygems mocha).each { |l| require l }
2
- require File.dirname(__FILE__) + "/../../../../lib/synthesis/adapter/mocha"
2
+ require File.dirname(__FILE__) + "/../../../../lib/synthesis/adapter/mocha"
3
+ Synthesis::MochaAdapter.new(nil).collect_expectations
@@ -2,17 +2,24 @@ require File.dirname(__FILE__) + "/helper"
2
2
 
3
3
  class MochaExpectationTest < Test::Unit::TestCase
4
4
  def setup
5
- @mocha_expectation = Mocha::Expectation.new :mock, :blah
5
+ @mocha_expectation = Mocha::Expectation.new(:mock, :blah)
6
+ @synthesis_expectation = Synthesis::Expectation.new(Hash, :method, :track, [], [1])
6
7
  end
7
8
 
8
9
  def test_holds_synthesis_expectation
9
- @mocha_expectation.synthesis_expectation = 1
10
- assert_equal 1, @mocha_expectation.synthesis_expectation
10
+ @mocha_expectation.synthesis_expectation = @synthesis_expectation
11
+ assert_equal(@synthesis_expectation, @mocha_expectation.synthesis_expectation)
11
12
  end
12
13
 
13
14
  def test_with_passes_expected_params_to_synthesis_expectation
14
- @mocha_expectation.synthesis_expectation = Synthesis::Expectation.new Hash, :foo, :track
15
- @mocha_expectation.with 1, 2
15
+ @mocha_expectation.synthesis_expectation = @synthesis_expectation
16
+ @mocha_expectation.with(1, 2)
16
17
  assert_equal([1, 2], @mocha_expectation.synthesis_expectation.args)
17
18
  end
19
+
20
+ def test_passes_return_values_to_synthesis_expectation
21
+ @mocha_expectation.synthesis_expectation = @synthesis_expectation
22
+ @mocha_expectation.returns(:rock)
23
+ assert_equal([Fixnum, Symbol], @synthesis_expectation.return_value_types)
24
+ end
18
25
  end
@@ -1,2 +1,3 @@
1
1
  require "test/unit"
2
- require File.dirname(__FILE__) + "/../../../../lib/synthesis/adapter/rspec"
2
+ require File.dirname(__FILE__) + "/../../../../lib/synthesis/adapter/rspec"
3
+ Synthesis::RSpecAdapter.new(nil).collect_expectations
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ module Synthesis
4
+ class ExpectationMatcherTest < Test::Unit::TestCase
5
+ def test_match
6
+ exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
7
+ exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
8
+ assert(ExpectationMatcher.new(exp1, exp2).match?)
9
+ end
10
+
11
+ def test_no_match_based_on_expectation_type
12
+ exp1 = Expectation.new(Array, :foo, :track, [Array, Hash], [Array, Hash])
13
+ exp2 = Expectation.new(Array.new, :foo, :track, [Array, Hash], [Array, Hash])
14
+ assert(!ExpectationMatcher.new(exp1, exp2).match?)
15
+ end
16
+
17
+ def test_no_match_based_on_receiver_type
18
+ exp1 = Expectation.new(Array.new, :foo, :track, [Array, Hash], [Array, Hash])
19
+ exp2 = Expectation.new(Hash.new, :foo, :track, [Array, Hash], [Array, Hash])
20
+ assert(!ExpectationMatcher.new(exp1, exp2).match?)
21
+ end
22
+
23
+ def test_no_match_based_on_method_name
24
+ exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [Array, Hash])
25
+ exp2 = Expectation.new(Object.new, :bar, :track, [Array, Hash], [Array, Hash])
26
+ assert(!ExpectationMatcher.new(exp1, exp2).match?)
27
+ end
28
+
29
+ def test_no_match_based_on_arg_types
30
+ exp1 = Expectation.new(Object.new, :foo, :track, [1, "a"], [Array, Hash])
31
+ exp2 = Expectation.new(Object.new, :foo, :track, [1, 2], [Array, Hash])
32
+ assert(!ExpectationMatcher.new(exp1, exp2).match?)
33
+ end
34
+
35
+ def test_no_match_based_on_return_value_types
36
+ exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
37
+ exp1.add_return_values(:sym)
38
+ exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], ["string"])
39
+ assert(!ExpectationMatcher.new(exp1, exp2).match?)
40
+ end
41
+
42
+ def test_match_based_on_intersection_of_return_value_types
43
+ exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [[1], {:a => :b}])
44
+ exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash], [[4], {:a => 9}])
45
+ assert(ExpectationMatcher.new(exp1, exp2).match?)
46
+ end
47
+
48
+ def test_ignores_return_value_types_unless_return_values_defined
49
+ exp1 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
50
+ exp2 = Expectation.new(Object.new, :foo, :track, [Array, Hash])
51
+ assert(ExpectationMatcher.new(exp1, exp2).match?)
52
+ end
53
+ end
54
+ end
@@ -14,17 +14,17 @@ module Synthesis
14
14
 
15
15
  def test_watches_expectation_invocations
16
16
  c = Class.new { def foo; end }
17
- ExpectationRecord.add_expectation c.new, :foo, :track
17
+ ExpectationRecord.add_expectation(c.new, :foo, :track)
18
18
  ExpectationRecord.record_invocations
19
19
  a = c.new
20
- MethodInvocationWatcher.expects(:invoked).with(a, :foo, [])
20
+ MethodInvocationWatcher.expects(:invoked).with(a, :foo, [], [nil])
21
21
  a.foo
22
22
  end
23
23
 
24
24
  def test_finds_expectation
25
- ExpectationRecord.add_expectation Object.new, :foo, :track
26
- expected = ExpectationRecord.expectations.to_a[0]
27
- matcher = Expectation.new Object.new, :foo, :track
25
+ expected = ExpectationRecord.add_expectation(Object.new, :foo, :track)
26
+ expected.add_return_values(20)
27
+ matcher = Expectation.new(Object.new, :foo, :track, [], [20])
28
28
  actual = ExpectationRecord[matcher]
29
29
  assert_equal(expected, actual)
30
30
  end
@@ -37,15 +37,16 @@ module Synthesis
37
37
  end
38
38
 
39
39
  def test_does_not_add_expectation_for_ignored_class
40
- ExpectationRecord.ignore Hash
41
- ExpectationRecord.add_expectation Hash, :foo, :track
40
+ ExpectationRecord.ignore(Hash)
41
+ ExpectationRecord.add_expectation(Hash, :foo, :track)
42
42
  assert ExpectationRecord.expectations.empty?
43
43
  end
44
44
 
45
45
  def test_returns_added_expectation_on_add
46
- expected = Expectation.new Hash, :foo, :track
47
- actual = ExpectationRecord.add_expectation Hash, :foo, :track
48
- assert_equal expected, actual
46
+ expected = Expectation.new(Hash, :foo, :track, [], [:return_val])
47
+ actual = ExpectationRecord.add_expectation(Hash, :foo, :track)
48
+ actual.add_return_values(:return_val)
49
+ assert_equal(expected, actual)
49
50
  end
50
51
  end
51
52
  end
@@ -3,77 +3,76 @@ require File.dirname(__FILE__) + "/helper"
3
3
  module Synthesis
4
4
  class ExpectationTest < Test::Unit::TestCase
5
5
  def test_creates_singleton_method_expectation_given_class
6
- expectation = Expectation.new Class.new, :foo, :track
7
- assert_kind_of Expectation::Singleton, expectation
6
+ expectation = Expectation.new(Class.new, :foo, :track)
7
+ assert_kind_of(Expectation::Singleton, expectation)
8
8
  end
9
9
 
10
10
  def test_creates_instance_method_expectation_given_instance
11
- expectation = Expectation.new Class.new.new, :foo, :track
12
- assert_kind_of Expectation::Instance, expectation
11
+ expectation = Expectation.new(Class.new.new, :foo, :track)
12
+ assert_kind_of(Expectation::Instance, expectation)
13
13
  end
14
14
 
15
15
  def test_creates_singleton_method_expectation_given_module
16
- expectation = Expectation.new Module.new, :foo, :track
17
- assert_kind_of Expectation::Singleton, expectation
16
+ expectation = Expectation.new(Module.new, :foo, :track)
17
+ assert_kind_of(Expectation::Singleton, expectation)
18
18
  end
19
19
 
20
20
  def test_records_instance_receiver_method_invocations
21
21
  receiver = Class.new { def foo;end }
22
- expectation = Expectation.new receiver.new, :foo, :track
22
+ expectation = Expectation.new(receiver.new, :foo, :track)
23
23
  expectation.record_invocations
24
- assert_respond_to receiver.new, :__recordable__foo
24
+ assert_respond_to(receiver.new, :__recordable__foo)
25
25
  end
26
26
 
27
27
  def test_records_singleton_receiver_method_invocations
28
28
  receiver = Class.new { def self.foo;end }
29
- expectation = Expectation.new receiver, :foo, :track
29
+ expectation = Expectation.new(receiver, :foo, :track, [], [:return])
30
30
  expectation.record_invocations
31
- assert_respond_to receiver, :__recordable__foo
31
+ assert_respond_to(receiver, :__recordable__foo)
32
32
  end
33
-
34
- def test_equality
35
- exp1 = Expectation.new Object.new, :foo, :track
36
- exp2 = Expectation.new Object.new, :foo, :track
37
- assert_equal exp1, exp2
33
+
34
+ def test_make_sure_equality_works_with_uniq
35
+ expectations = [
36
+ Expectation.new(String, :new, [], [:arg1, :arg2], [:return]),
37
+ Expectation.new(String, :new, [], [:arg1, :arg2], [:return])
38
+ ]
39
+ assert_equal(1, expectations.uniq.size)
38
40
  end
39
-
40
- def test_non_equality
41
- exp1 = Expectation.new Hash.new, :foo, :track
42
- exp2 = Expectation.new Array.new, :foo, :track
43
- assert_not_equal exp1, exp2
41
+
42
+ def test_singleton_receiver_class
43
+ assert_equal(String, Expectation.new(String, :new, :track, [], []).receiver_class)
44
44
  end
45
45
 
46
- def test_equality_based_on_same_type_args
47
- exp1 = Expectation.new Object.new, :foo, :track, [1, 2]
48
- exp2 = Expectation.new Object.new, :foo, :track, [1, 2]
49
- assert_equal exp1, exp2
46
+ def test_instance_receiver_class
47
+ assert_equal(String, Expectation.new(String.new, :new, :track, [], []).receiver_class)
50
48
  end
51
49
 
52
- def test_args_based_non_equality_based_on_different_type_args
53
- exp1 = Expectation.new Object.new, :foo, :track, [1, Hash.new]
54
- exp2 = Expectation.new Object.new, :foo, :track, [1, 2]
55
- assert_not_equal exp1, exp2
50
+ def test_arg_types
51
+ expectation = Expectation.new(String.new, :new, :track, [:sym, "str"], [])
52
+ assert_equal([Symbol, String], expectation.arg_types)
56
53
  end
57
-
58
- def test_make_sure_equality_works_with_uniq
59
- arr = [
60
- Expectation.new(String, :new, []),
61
- Expectation.new(String, :new, [])
62
- ]
63
- assert_equal(1, arr.uniq.size)
54
+
55
+ def test_return_value_types
56
+ expectation = Expectation.new(String.new, :new, :track, [], [:sym, "str"])
57
+ assert_equal([Symbol, String], expectation.return_value_types)
64
58
  end
65
-
66
- def test_does_not_blow_up_on_name_error
67
- expectation = Expectation.new Hash, :bad, :track
68
- silent(expectation) do
69
- assert_nothing_raised(NameError) { expectation.record_invocations }
70
- end
59
+
60
+ def test_adds_return_value
61
+ expectation = Expectation.new(String.new, :new, :track, [], [:sym])
62
+ expectation.add_return_values("str")
63
+ actual = expectation.instance_variable_get(:@return_values)
64
+ assert_equal([:sym, "str"], actual)
71
65
  end
72
-
73
- def silent(object)
74
- object.silence!
75
- yield
76
- object.speak!
66
+
67
+ def test_return_values_defined_when_return_values_added
68
+ expectation = Expectation.new(String.new, :new, :track, [], [:sym])
69
+ expectation.add_return_values("str")
70
+ assert(expectation.return_values_defined?)
71
+ end
72
+
73
+ def test_return_values_not_defined_when_no_return_values_added
74
+ expectation = Expectation.new(String.new, :new, :track, [], [:sym])
75
+ assert(!expectation.return_values_defined?)
77
76
  end
78
77
  end
79
78
  end
@@ -3,8 +3,8 @@ require File.dirname(__FILE__) + "/helper"
3
3
  module Synthesis
4
4
  class MethodInvocationWatcherTest < Test::Unit::TestCase
5
5
  def test_marks_expectation_invoked
6
- ExpectationRecord.add_expectation(Hash, :to_s, [])
7
- MethodInvocationWatcher.invoked(Hash, :to_s, [])
6
+ ExpectationRecord.add_expectation(Hash, :to_s, :track, []).add_return_values(1)
7
+ MethodInvocationWatcher.invoked(Hash, :to_s, [], [1])
8
8
  expectation = ExpectationRecord.expectations.to_a.first
9
9
  assert expectation.invoked?
10
10
  end
@@ -14,7 +14,7 @@ module Synthesis
14
14
  foo.extend Recordable
15
15
  foo.recordable_method(:b)
16
16
  bar = foo.new
17
- MethodInvocationWatcher.expects(:invoked).with(bar, :b, [])
17
+ MethodInvocationWatcher.expects(:invoked).with(bar, :b, [], [nil])
18
18
  bar.b
19
19
  end
20
20
 
@@ -59,7 +59,7 @@ module Synthesis
59
59
  foo.extend Recordable
60
60
  foo.recordable_method(:b)
61
61
  bar = foo.new
62
- MethodInvocationWatcher.expects(:invoked).with(bar, :b, [])
62
+ MethodInvocationWatcher.expects(:invoked).with(bar, :b, [], [:magic!])
63
63
  bar.b
64
64
  end
65
65
  end
@@ -5,7 +5,7 @@ class DataBranderTest < Test::Unit::TestCase
5
5
  def test_saves_branded_to_pstore
6
6
  pstore = Storage.new 'whatever'
7
7
  pstore.expects(:save).with('METAL - rock')
8
- DataBrander.new(pstore).save_branded 'rock'
8
+ DataBrander.new(pstore).save_branded('rock')
9
9
  end
10
10
 
11
11
  def test_ignores_total_ducks
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synthesis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Caborn, George Malamidis
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-03-10 00:00:00 +00:00
12
+ date: 2008-04-13 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -29,13 +29,16 @@ files:
29
29
  - lib/synthesis/adapter.rb
30
30
  - lib/synthesis/class.rb
31
31
  - lib/synthesis/expectation.rb
32
+ - lib/synthesis/expectation_interceptor.rb
33
+ - lib/synthesis/expectation_matcher.rb
32
34
  - lib/synthesis/expectation_record.rb
35
+ - lib/synthesis/expectation_record_enabled.rb
33
36
  - lib/synthesis/logging.rb
34
37
  - lib/synthesis/method_invocation_watcher.rb
35
38
  - lib/synthesis/module.rb
36
39
  - lib/synthesis/object.rb
37
40
  - lib/synthesis/recordable.rb
38
- - lib/synthesis/report.rb
41
+ - lib/synthesis/reporter.rb
39
42
  - lib/synthesis/runner.rb
40
43
  - lib/synthesis/task.rb
41
44
  - lib/synthesis.rb
@@ -49,6 +52,7 @@ files:
49
52
  - test/synthesis/adapter/rspec/message_expectation_test.rb
50
53
  - test/synthesis/adapter/rspec/methods_test.rb
51
54
  - test/synthesis/class_test.rb
55
+ - test/synthesis/expectation_matcher_test.rb
52
56
  - test/synthesis/expectation_record_test.rb
53
57
  - test/synthesis/expectation_test.rb
54
58
  - test/synthesis/helper.rb
@@ -95,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
99
  requirements: []
96
100
 
97
101
  rubyforge_project: synthesis
98
- rubygems_version: 1.0.1
102
+ rubygems_version: 1.1.1
99
103
  signing_key:
100
104
  specification_version: 2
101
105
  summary: A tool for Synthesized Testing
@@ -1,22 +0,0 @@
1
- module Synthesis
2
- extend Logging
3
-
4
- def report!
5
- if failed?
6
- ExpectationRecord.print_tested_expectations
7
- ExpectationRecord.print_untested_expectations
8
- ExpectationRecord.print_ignored
9
- log; log "FAILED."
10
- return -1
11
- end
12
- log; log "Verified #{ExpectationRecord.expectations.size} expectations"
13
- log "SUCCESS."
14
- 0
15
- end
16
-
17
- def failed?
18
- ExpectationRecord.untested_expectations.any?
19
- end
20
-
21
- module_function :report!, :failed?
22
- end