gmalamid-synthesis 0.1.8 → 0.2.0
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.
- data/README +20 -1
- data/Rakefile +16 -12
- data/lib/synthesis/adapter/mocha.rb +1 -0
- data/lib/synthesis/adapter/rspec.rb +1 -0
- data/lib/synthesis/expectation.rb +15 -10
- data/lib/synthesis/expectation_interceptor.rb +22 -0
- data/lib/synthesis/expectation_record.rb +3 -13
- data/lib/synthesis/formatter/dot.rb +91 -0
- data/lib/synthesis/formatter/text.rb +32 -0
- data/lib/synthesis/formatter.rb +21 -0
- data/lib/synthesis/method_invocation_watcher.rb +8 -1
- data/lib/synthesis/reporter.rb +7 -18
- data/lib/synthesis/runner.rb +2 -1
- data/lib/synthesis/task.rb +4 -2
- data/lib/synthesis/util/mock_instance/rspec.rb +1 -1
- data/lib/synthesis.rb +1 -0
- data/synthesis.gemspec +5 -2
- metadata +6 -3
data/README
CHANGED
@@ -34,6 +34,8 @@ Synthesis can be setup to ignore certain classes or modules when collecting expe
|
|
34
34
|
|
35
35
|
If +pattern+ is not specified, it will default to <tt>test/**/*_test.rb</tt>
|
36
36
|
|
37
|
+
As of version 0.2.0, Synthesis has a +DOT+ formatter which, when used, will output text in the DOT graph description language, producing system visualizations as specified by the simulated interactions in the system's tests. The output of the +DOT+ formatter can be used with tools like Graphviz( http://www.graphviz.org/ ).
|
38
|
+
|
37
39
|
== Usage examples
|
38
40
|
|
39
41
|
To use with Test::Unit and Mocha, ignoring Array and Hash:
|
@@ -54,7 +56,7 @@ To use with RSpec, running all specs in the <tt>spec</tt> directory:
|
|
54
56
|
t.pattern = 'spec/**/*_spec.rb'
|
55
57
|
end
|
56
58
|
|
57
|
-
To use with Expectations, redirecting output to a file
|
59
|
+
To use with Expectations, redirecting output to a file:
|
58
60
|
|
59
61
|
require "synthesis/task"
|
60
62
|
|
@@ -62,6 +64,23 @@ To use with Expectations, redirecting output to a file
|
|
62
64
|
t.adapter = :expectations
|
63
65
|
t.out = File.new "synthesis.test.txt", "a"
|
64
66
|
end
|
67
|
+
|
68
|
+
To output a DOT graph:
|
69
|
+
|
70
|
+
require "synthesis/task"
|
71
|
+
|
72
|
+
Synthesis::Task.new do |t|
|
73
|
+
t.formatter = :dot
|
74
|
+
end
|
75
|
+
|
76
|
+
To use Synthesis with Rails:
|
77
|
+
|
78
|
+
require "synthesis/task"
|
79
|
+
|
80
|
+
Synthesis::Task.new do |t|
|
81
|
+
RAILS_ENV = "test"
|
82
|
+
t.pattern = 'test/**/*_test.rb'
|
83
|
+
end
|
65
84
|
|
66
85
|
== Utilities
|
67
86
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "rake/testtask"
|
3
|
-
require "rake/gempackagetask"
|
4
3
|
require 'rake/rdoctask'
|
5
4
|
require 'rake/contrib/sshpublisher'
|
6
5
|
require File.dirname(__FILE__) + "/lib/synthesis/task"
|
@@ -27,15 +26,17 @@ Rake::TestTask.new('test:spec') do |t|
|
|
27
26
|
t.pattern = 'test/synthesis/adapter/rspec/*_test.rb'
|
28
27
|
end
|
29
28
|
|
29
|
+
# Synthesis test_project tasks
|
30
|
+
|
30
31
|
Synthesis::Task.new do |t|
|
31
32
|
t.pattern = 'test_project/mocha/test/*_test.rb'
|
32
33
|
t.ignored = [Array, Hash]
|
33
34
|
# t.out = File.new('synthesis.test.txt', 'a')
|
34
35
|
end
|
35
36
|
|
36
|
-
Synthesis::Task.new('synthesis:
|
37
|
-
t.
|
38
|
-
t.
|
37
|
+
Synthesis::Task.new('synthesis:test:graph') do |t|
|
38
|
+
t.pattern = 'test_project/mocha/test/*_test.rb'
|
39
|
+
t.formatter = :dot
|
39
40
|
end
|
40
41
|
|
41
42
|
Synthesis::Task.new('synthesis:spec') do |t|
|
@@ -43,6 +44,17 @@ Synthesis::Task.new('synthesis:spec') do |t|
|
|
43
44
|
t.pattern = 'test_project/rspec/*_spec.rb'
|
44
45
|
end
|
45
46
|
|
47
|
+
Synthesis::Task.new('synthesis:spec:graph') do |t|
|
48
|
+
t.adapter = :rspec
|
49
|
+
t.pattern = 'test_project/rspec/*_spec.rb'
|
50
|
+
t.formatter = :dot
|
51
|
+
end
|
52
|
+
|
53
|
+
Synthesis::Task.new('synthesis:expectations') do |t|
|
54
|
+
t.adapter = :expectations
|
55
|
+
t.pattern = 'test_project/expectations/test/*_test.rb'
|
56
|
+
end
|
57
|
+
|
46
58
|
namespace :test_project do
|
47
59
|
task :all do
|
48
60
|
STDOUT.puts `rake test_project:mocha`
|
@@ -78,14 +90,6 @@ task :publish_rdoc do
|
|
78
90
|
Rake::SshDirPublisher.new("gmalamid@rubyforge.org", "/var/www/gforge-projects/synthesis", "doc").upload
|
79
91
|
end
|
80
92
|
|
81
|
-
Rake::GemPackageTask.new(GEMSPEC) do |t|
|
82
|
-
t.need_zip = false
|
83
|
-
t.need_tar = false
|
84
|
-
end
|
85
|
-
|
86
|
-
desc "Remove rdoc and package artefacts"
|
87
|
-
task :clean => %w[clobber_package clobber_rdoc]
|
88
|
-
|
89
93
|
task(:lf) {p Dir["lib/**/*rb"]}
|
90
94
|
|
91
95
|
task(:check_gemspec) do
|
@@ -16,6 +16,7 @@ module Synthesis
|
|
16
16
|
Object.extend(ExpectationRecorder)
|
17
17
|
Object.record_expectations_on(:expects)
|
18
18
|
Mocha::Expectation.extend(ExpectationInterceptor)
|
19
|
+
Mocha::Expectation.intercept_test_subject_on(:invoke)
|
19
20
|
Mocha::Expectation.intercept_expected_arguments_on(:with)
|
20
21
|
Mocha::Expectation.intercept_expected_return_values_on(:returns)
|
21
22
|
Mocha::Expectation.intercept_expected_return_values_on(:raises)
|
@@ -26,6 +26,7 @@ module Synthesis
|
|
26
26
|
Spec::Mocks::Methods.extend(ExpectationRecorder)
|
27
27
|
Spec::Mocks::Methods.record_expectations_on(:should_receive)
|
28
28
|
Spec::Mocks::MessageExpectation.extend(ExpectationInterceptor)
|
29
|
+
Spec::Mocks::MessageExpectation.intercept_test_subject_on(:invoke)
|
29
30
|
Spec::Mocks::MessageExpectation.intercept_expected_arguments_on(:with)
|
30
31
|
Spec::Mocks::MessageExpectation.intercept_expected_return_values_on(:and_return)
|
31
32
|
Spec::Mocks::MessageExpectation.intercept_expected_return_values_on(:and_raise)
|
@@ -20,10 +20,19 @@ module Synthesis
|
|
20
20
|
meta_receiver.recordable_method(@method)
|
21
21
|
end
|
22
22
|
|
23
|
+
def add_test_subject(test_subject)
|
24
|
+
(@callers ||= []) << test_subject
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_subject
|
28
|
+
@callers[0]
|
29
|
+
end
|
30
|
+
|
23
31
|
def explode
|
24
32
|
if @return_values.size > 1
|
25
33
|
@return_values.map do |v|
|
26
34
|
expectation = self.class.new(@receiver, @method, @track, @args, [])
|
35
|
+
expectation.add_test_subject(@callers.shift)
|
27
36
|
expectation.add_return_values(v)
|
28
37
|
expectation
|
29
38
|
end
|
@@ -79,11 +88,9 @@ module Synthesis
|
|
79
88
|
@receiver
|
80
89
|
end
|
81
90
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
"in #{@track}"
|
86
|
-
end
|
91
|
+
def receiver_repr
|
92
|
+
@receiver.name
|
93
|
+
end
|
87
94
|
end
|
88
95
|
|
89
96
|
class Instance < Expectation
|
@@ -99,11 +106,9 @@ module Synthesis
|
|
99
106
|
meta_receiver
|
100
107
|
end
|
101
108
|
|
102
|
-
def
|
103
|
-
"
|
104
|
-
|
105
|
-
"in #{@track}"
|
106
|
-
end
|
109
|
+
def receiver_repr
|
110
|
+
"#{meta_receiver.name}.new"
|
111
|
+
end
|
107
112
|
end
|
108
113
|
|
109
114
|
class NilExpectation < Expectation
|
@@ -3,6 +3,26 @@ module Synthesis
|
|
3
3
|
# Synthesis to tap into it in order to collect simulated method arguments
|
4
4
|
# and return values.
|
5
5
|
module ExpectationInterceptor
|
6
|
+
# Intercept the actual mock proxy to record the test subject so that
|
7
|
+
# Synthesis can track which object is being tested
|
8
|
+
def intercept_test_subject_on(method_name)
|
9
|
+
(@original_methods ||= []) << method_name
|
10
|
+
|
11
|
+
class_eval do
|
12
|
+
alias_method "intercepted_#{method_name}", method_name
|
13
|
+
|
14
|
+
define_method(:get_invoke_method_name) {method_name}
|
15
|
+
|
16
|
+
def temp_invoke(*expected_parameters, &matching_block)
|
17
|
+
synthesis_expectation.add_test_subject(caller(2)) if synthesis_expectation
|
18
|
+
send("intercepted_#{get_invoke_method_name}", *expected_parameters, &matching_block)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method method_name, :temp_invoke
|
22
|
+
undef temp_invoke
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
6
26
|
# Intercept the mock object framework's expectation method for declaring a mocked
|
7
27
|
# method's arguments so that Synthesis can record them.
|
8
28
|
def intercept_expected_arguments_on(method_name)
|
@@ -66,6 +86,8 @@ module Synthesis
|
|
66
86
|
class_eval do
|
67
87
|
remove_method :synthesis_expectation
|
68
88
|
remove_method :synthesis_expectation=
|
89
|
+
remove_method :get_invoke_method_name
|
90
|
+
remove_method :get_method_name
|
69
91
|
end
|
70
92
|
end
|
71
93
|
|
@@ -61,19 +61,9 @@ module Synthesis
|
|
61
61
|
def untested_expectations
|
62
62
|
expectations.select { |e| !e.invoked? }
|
63
63
|
end
|
64
|
-
|
65
|
-
def
|
66
|
-
|
67
|
-
tested_expectations.each { |e| log e }
|
68
|
-
end
|
69
|
-
|
70
|
-
def print_untested_expectations
|
71
|
-
log; log "Untested Expectations: "
|
72
|
-
untested_expectations.each { |e| log e }
|
73
|
-
end
|
74
|
-
|
75
|
-
def print_ignored
|
76
|
-
log; log "Ignoring: #{ignored.to_a * ', '}"
|
64
|
+
|
65
|
+
def has_untested_expectations?
|
66
|
+
untested_expectations.any?
|
77
67
|
end
|
78
68
|
|
79
69
|
private
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "parse_tree"
|
2
|
+
require "sexp"
|
3
|
+
require "sexp_processor"
|
4
|
+
|
5
|
+
module Synthesis
|
6
|
+
class DotFormatter < Formatter
|
7
|
+
def initialize
|
8
|
+
Expectation::Expectation.send(:include, ExpectationReportFormat::Dot)
|
9
|
+
end
|
10
|
+
|
11
|
+
def digraph
|
12
|
+
puts "digraph synthesis_expectations {"
|
13
|
+
puts " rankdir=LR;"
|
14
|
+
puts " size=\"8,10\";"
|
15
|
+
puts " ratio=\"fill\";"
|
16
|
+
puts " node [shape = circle];"
|
17
|
+
puts " edge [color = green]"
|
18
|
+
report_tested_expectations
|
19
|
+
puts
|
20
|
+
puts " edge [color = red]"
|
21
|
+
report_untested_expectations
|
22
|
+
puts "}"
|
23
|
+
end
|
24
|
+
alias format_failure digraph
|
25
|
+
alias format_success digraph
|
26
|
+
end
|
27
|
+
|
28
|
+
module ExpectationReportFormat
|
29
|
+
module Dot
|
30
|
+
def to_report
|
31
|
+
" \"#{test_subject_name}\" -> \"#{receiver_class}\" " +
|
32
|
+
"[ label = \"(#{return_value_type}) #{method}(#{arg_types * ', '})\" ];"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def test_subject_name
|
38
|
+
filename, line, method = test_subject[1].split(':')
|
39
|
+
method = method.scan(/`(.*)'/)[0][0]
|
40
|
+
ruby = File.read(filename)
|
41
|
+
parser = ParseTree.new
|
42
|
+
sexp = parser.parse_tree_for_string(ruby, filename).first
|
43
|
+
sexp = Sexp.from_array(sexp)
|
44
|
+
return DotProcessor.process(sexp, method)
|
45
|
+
rescue
|
46
|
+
filename ? "#{filename} (#{line})" : "?"
|
47
|
+
end
|
48
|
+
|
49
|
+
class DotProcessor < SexpProcessor
|
50
|
+
attr_accessor :method
|
51
|
+
attr_reader :klazz
|
52
|
+
|
53
|
+
def self.process(exp, method)
|
54
|
+
analyzer = self.new
|
55
|
+
analyzer.method = method
|
56
|
+
analyzer.process(exp)
|
57
|
+
analyzer.klazz
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize
|
61
|
+
super
|
62
|
+
self.strict = false
|
63
|
+
self.auto_shift_type = true
|
64
|
+
@ancestors = []
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_module(exp)
|
68
|
+
name = exp.shift
|
69
|
+
@ancestors.push name
|
70
|
+
result = s(:module, name, process(exp.shift))
|
71
|
+
@ancestors.pop
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_class(exp)
|
76
|
+
name = exp.shift
|
77
|
+
@ancestors.push name
|
78
|
+
result = s(:class, name, exp.shift, process(exp.shift))
|
79
|
+
@ancestors.pop
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_defn(exp)
|
84
|
+
name = exp.shift
|
85
|
+
@klazz = @ancestors * '::' if name == method.to_sym
|
86
|
+
s(:defn, name, process(exp.shift), process(exp.shift))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Synthesis
|
2
|
+
class TextFormatter < Formatter
|
3
|
+
include Logging
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
Expectation::Expectation.send(:include, ExpectationReportFormat::Text)
|
7
|
+
end
|
8
|
+
|
9
|
+
def format_success
|
10
|
+
log; log "Verified #{ExpectationRecord.expectations.size} expectations"
|
11
|
+
log "SUCCESS."
|
12
|
+
end
|
13
|
+
|
14
|
+
def format_failure
|
15
|
+
log; log "Tested Expectations: "
|
16
|
+
report_tested_expectations
|
17
|
+
log; log "Untested Expectations: "
|
18
|
+
report_untested_expectations
|
19
|
+
log "Ignoring: #{ExpectationRecord.ignored.to_a * ', '}"
|
20
|
+
log; log "FAILED."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ExpectationReportFormat
|
25
|
+
module Text
|
26
|
+
def to_report
|
27
|
+
"(#{return_value_type}) #{receiver_repr}.#{@method}" +
|
28
|
+
"(#{arg_types * ', '}) in #{@track}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Synthesis
|
2
|
+
class Formatter
|
3
|
+
def report_tested_expectations
|
4
|
+
ExpectationRecord.tested_expectations.each { |e| puts e.to_report }
|
5
|
+
end
|
6
|
+
|
7
|
+
def report_untested_expectations
|
8
|
+
ExpectationRecord.untested_expectations.each { |e| puts e.to_report }
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def load
|
13
|
+
@formatter.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def inherited(subclass)
|
17
|
+
@formatter = subclass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,8 +1,15 @@
|
|
1
1
|
module Synthesis
|
2
2
|
class MethodInvocationWatcher
|
3
3
|
def self.invoked(receiver, method, args = [], return_values = [])
|
4
|
+
# cal = caller.clone
|
5
|
+
# cal.shift # ignore first (eval)
|
6
|
+
# path_from_spec = []
|
7
|
+
# begin
|
8
|
+
# c = cal.shift
|
9
|
+
# path_from_spec.unshift c # unless c =~ /(\(eval\)|gems)/
|
10
|
+
# end until c =~ /(spec|test)/
|
4
11
|
matcher = Expectation.new(receiver, method, nil, args, return_values)
|
5
|
-
ExpectationRecord[matcher].invoked!
|
12
|
+
ExpectationRecord[matcher].invoked! # path_from_spec
|
6
13
|
end
|
7
14
|
end
|
8
15
|
end
|
data/lib/synthesis/reporter.rb
CHANGED
@@ -1,24 +1,13 @@
|
|
1
1
|
module Synthesis
|
2
2
|
class Reporter
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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?
|
3
|
+
def self.report
|
4
|
+
formatter = Formatter.load
|
5
|
+
if ExpectationRecord.has_untested_expectations?
|
6
|
+
formatter.format_failure
|
7
|
+
return -1
|
21
8
|
end
|
9
|
+
formatter.format_success
|
10
|
+
0
|
22
11
|
end
|
23
12
|
end
|
24
13
|
end
|
data/lib/synthesis/runner.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Synthesis
|
2
2
|
class Runner
|
3
|
-
def self.run(adapter, pattern)
|
3
|
+
def self.run(adapter, pattern, formatter)
|
4
4
|
require "synthesis/adapter/#{adapter}"
|
5
|
+
require "synthesis/formatter/#{formatter}"
|
5
6
|
Adapter.load(pattern).run
|
6
7
|
at_exit { Reporter.report unless $! }
|
7
8
|
end
|
data/lib/synthesis/task.rb
CHANGED
@@ -6,7 +6,8 @@ require File.dirname(__FILE__) + "/../synthesis/logging"
|
|
6
6
|
module Synthesis
|
7
7
|
class Task < Rake::TaskLib
|
8
8
|
include Logging
|
9
|
-
attr_accessor :verbose, :pattern, :ruby_opts
|
9
|
+
attr_accessor :verbose, :pattern, :ruby_opts
|
10
|
+
attr_accessor :adapter, :out, :ignored, :libs, :formatter
|
10
11
|
|
11
12
|
def initialize(name='synthesis:test')
|
12
13
|
@name, @ignored, @libs = name, [], []
|
@@ -14,6 +15,7 @@ module Synthesis
|
|
14
15
|
@pattern ||= 'test/**/*_test.rb'
|
15
16
|
@ruby_opts ||= []
|
16
17
|
@adapter ||= :mocha
|
18
|
+
@formatter ||= :text
|
17
19
|
define
|
18
20
|
end
|
19
21
|
|
@@ -37,7 +39,7 @@ module Synthesis
|
|
37
39
|
require File.dirname(__FILE__) + "/../synthesis/runner"
|
38
40
|
Synthesis::Logging.const_set(:OUT, @out) if @out
|
39
41
|
Synthesis::ExpectationRecord.ignore(*@ignored)
|
40
|
-
Synthesis::Runner.run(@adapter, @pattern)
|
42
|
+
Synthesis::Runner.run(@adapter, @pattern, @formatter)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
self
|
data/lib/synthesis.rb
CHANGED
data/synthesis.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
GEMSPEC =Gem::Specification.new do |s|
|
2
2
|
s.name = 'synthesis'
|
3
|
-
s.version = '0.
|
3
|
+
s.version = '0.2.0'
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.rubyforge_project = "synthesis"
|
6
6
|
s.summary, s.description = 'A tool for Synthesized Testing'
|
7
|
-
s.authors = 'Stuart Caborn, George Malamidis'
|
7
|
+
s.authors = 'Stuart Caborn, George Malamidis, Danilo Sato'
|
8
8
|
s.email = 'george@nutrun.com'
|
9
9
|
s.homepage = 'http://synthesis.rubyforge.org'
|
10
10
|
s.has_rdoc = true
|
@@ -24,6 +24,9 @@ GEMSPEC =Gem::Specification.new do |s|
|
|
24
24
|
"lib/synthesis/expectation_interceptor.rb",
|
25
25
|
"lib/synthesis/expectation_matcher.rb",
|
26
26
|
"lib/synthesis/expectation_record.rb",
|
27
|
+
"lib/synthesis/formatter/dot.rb",
|
28
|
+
"lib/synthesis/formatter/text.rb",
|
29
|
+
"lib/synthesis/formatter.rb",
|
27
30
|
"lib/synthesis/expectation_recorder.rb",
|
28
31
|
"lib/synthesis/logging.rb",
|
29
32
|
"lib/synthesis/method_invocation_watcher.rb",
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gmalamid-synthesis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Stuart Caborn, George Malamidis
|
7
|
+
- Stuart Caborn, George Malamidis, Danilo Sato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-10-
|
12
|
+
date: 2008-10-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -36,6 +36,9 @@ files:
|
|
36
36
|
- lib/synthesis/expectation_interceptor.rb
|
37
37
|
- lib/synthesis/expectation_matcher.rb
|
38
38
|
- lib/synthesis/expectation_record.rb
|
39
|
+
- lib/synthesis/formatter/dot.rb
|
40
|
+
- lib/synthesis/formatter/text.rb
|
41
|
+
- lib/synthesis/formatter.rb
|
39
42
|
- lib/synthesis/expectation_recorder.rb
|
40
43
|
- lib/synthesis/logging.rb
|
41
44
|
- lib/synthesis/method_invocation_watcher.rb
|