qed 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +15 -0
- data/LICENSE +344 -0
- data/MANIFEST +56 -0
- data/README.rdoc +97 -0
- data/bin/qed +150 -0
- data/bin/qedoc +52 -0
- data/demo/01_spec.qed +143 -0
- data/demo/01_spec.yaml +4 -0
- data/demo/qed_helper.rb +1 -0
- data/doc/qedoc/index.html +355 -0
- data/doc/qedoc/jquery.js +19 -0
- data/lib/qed.rb +5 -0
- data/lib/qed/assertion.rb +23 -0
- data/lib/qed/document.rb +188 -0
- data/lib/qed/document/jquery.js +19 -0
- data/lib/qed/document/markup.rb +53 -0
- data/lib/qed/document/template.rhtml +190 -0
- data/lib/qed/doubles/mock.rb +94 -0
- data/lib/qed/doubles/spy.rb +191 -0
- data/lib/qed/doubles/stub.rb +94 -0
- data/lib/qed/expectation.rb +60 -0
- data/lib/qed/grammar/assert.rb +104 -0
- data/lib/qed/grammar/expect.rb +121 -0
- data/lib/qed/grammar/legacy/assert.rb +291 -0
- data/lib/qed/grammar/should.rb +52 -0
- data/lib/qed/reporter/base.rb +101 -0
- data/lib/qed/reporter/dotprogress.rb +63 -0
- data/lib/qed/reporter/summary.rb +67 -0
- data/lib/qed/reporter/verbatim.rb +90 -0
- data/lib/qed/runner.rb +148 -0
- data/lib/qed/script.rb +179 -0
- data/lib/qed/utilities/extract.rb +137 -0
- data/lib/qed/utilities/monitor.rb +23 -0
- data/meta/authors +1 -0
- data/meta/created +1 -0
- data/meta/description +2 -0
- data/meta/homepage +1 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/requires +1 -0
- data/meta/ruby +2 -0
- data/meta/summary +1 -0
- data/meta/title +1 -0
- data/meta/version +1 -0
- metadata +115 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
# = Mock
|
4
|
+
#
|
5
|
+
class Mock < Module
|
6
|
+
attr :object
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super()
|
10
|
+
@table = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
def __table__ ; @table ; end
|
15
|
+
|
16
|
+
# TODO: Ruby has retry, but I need continue!
|
17
|
+
def method_missing(meth, *args, &block)
|
18
|
+
table = @table
|
19
|
+
interface = [meth, args, block_given?]
|
20
|
+
|
21
|
+
table[interface] = nil
|
22
|
+
|
23
|
+
define_method(meth) do |*args|
|
24
|
+
result = super
|
25
|
+
result.assert == table[interface]
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
|
29
|
+
Setter.new(table, interface)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
class Setter
|
34
|
+
def initialize(table, interface)
|
35
|
+
@table = table
|
36
|
+
@interface = interface
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(result)
|
40
|
+
@table[@interface] = result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# = Mock::Delegator
|
45
|
+
#
|
46
|
+
class Delegator
|
47
|
+
instance_methods(true).each{ |m| protected m unless m.to_s =~ /^__/ }
|
48
|
+
|
49
|
+
def initialize(object, mock_module)
|
50
|
+
@instance_delegate = object
|
51
|
+
extend(mock_module)
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_missing(s, *a, &b)
|
55
|
+
@instance_delegate.__send__(s, *a, &b)
|
56
|
+
end
|
57
|
+
end#class Delegator
|
58
|
+
|
59
|
+
end#class Mock
|
60
|
+
|
61
|
+
class ::Object
|
62
|
+
# Create mock object.
|
63
|
+
def mock(mock_module=nil)
|
64
|
+
if mock_module
|
65
|
+
Mock::Delegator.new(self, mock_module)
|
66
|
+
else
|
67
|
+
@_mock ||= Mock.new
|
68
|
+
extend(@_mock)
|
69
|
+
@_mock
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# We can't remove the module per-say. So we have to
|
74
|
+
# just neuter it. This is a very weak solution, but
|
75
|
+
# it will suffice for the moment.
|
76
|
+
#--
|
77
|
+
# TODO: Use Carats for #unmix.
|
78
|
+
#++
|
79
|
+
def remove_mock(mock_module=nil)
|
80
|
+
mock_module ||= @_mock
|
81
|
+
obj = self
|
82
|
+
mod = Module.new
|
83
|
+
mock_module.__table__.each do |interface, result|
|
84
|
+
meth = interface[0]
|
85
|
+
mod.module_eval do
|
86
|
+
define_method(meth, &obj.class.instance_method(meth).bind(obj))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
extend(mod)
|
90
|
+
end
|
91
|
+
end#class ::Object
|
92
|
+
|
93
|
+
end#module Quarry
|
94
|
+
|
@@ -0,0 +1,191 @@
|
|
1
|
+
raise "Spy class is under construction"
|
2
|
+
|
3
|
+
module QED
|
4
|
+
|
5
|
+
# = Spy
|
6
|
+
#
|
7
|
+
# Spy (aka DuckHunter) is a decoy object which is dropped into
|
8
|
+
# methods which records the calls made against it --hence a method probe.
|
9
|
+
# Of course, it is not perfect --an inescapable matter it seems for any
|
10
|
+
# internal probe. There are a couple of issues related to conditionals.
|
11
|
+
# Since the method test for a certain condition against the decoy, how
|
12
|
+
# is the decoy to respond? Thus ceratin paths in the code may never get
|
13
|
+
# exceuted and thus go unmapped. If Ruby had better conditional reflection
|
14
|
+
# (i.e. if 'if', 'case', 'unless', 'when', etc. were true methods) then
|
15
|
+
# this could be fixed by making the Probe reentrant, mapping out variant
|
16
|
+
# true/false/nil replies. The likely insurmountable problem though is the
|
17
|
+
# Halting problem. A probe can cause some methods to complete execution.
|
18
|
+
# It's pretty rare, but it can happen and little can be done about it (I think).
|
19
|
+
#
|
20
|
+
# Note, the alternative to this kind of probe is a program that examines, rather
|
21
|
+
# then executes, the code. This would circumvent the above problems, but run
|
22
|
+
# into difficulties with dynamic evals. It would also be more complicated,
|
23
|
+
# but might prove a better means in the future.
|
24
|
+
#
|
25
|
+
# This script is provided for experimetnal purposes. Please inform the author
|
26
|
+
# if you find ways to improve it or put it to an interesting use.
|
27
|
+
#
|
28
|
+
# == Synopsis
|
29
|
+
#
|
30
|
+
# require 'methodprobe'
|
31
|
+
#
|
32
|
+
# def amethod(x)
|
33
|
+
# x + 1
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# p method(:amethod).signiture
|
37
|
+
# p method(:amethod).signiture(:class)
|
38
|
+
# p method(:amethod).signiture(:pretty)
|
39
|
+
#
|
40
|
+
# produces
|
41
|
+
#
|
42
|
+
# [["+"]]
|
43
|
+
# [{"+"=>[["Fixnum"]]}]
|
44
|
+
# [["+( Fixnum )"]]
|
45
|
+
#
|
46
|
+
class Spy
|
47
|
+
|
48
|
+
def self.duckcall
|
49
|
+
begin
|
50
|
+
yield
|
51
|
+
rescue TypeError => e
|
52
|
+
self.send(e.message)
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :ducks, :decoys
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@ducks, @decoys = {}, {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize_copy(from)
|
64
|
+
initialize
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(aSym, *args)
|
68
|
+
aSymStr = aSym.to_s
|
69
|
+
|
70
|
+
# This will happen the first time
|
71
|
+
@ducks[aSymStr] ||= [] #unless @ducks[aSymStr]
|
72
|
+
@ducks[aSymStr] << args.collect { |a| "#{a.class}" }
|
73
|
+
|
74
|
+
decoy = self.dup
|
75
|
+
|
76
|
+
@decoys[aSymStr] ||= [] #unless @decoys[aSymStr]
|
77
|
+
@decoys[aSymStr] << decoy
|
78
|
+
|
79
|
+
# build proxy?
|
80
|
+
#begin
|
81
|
+
# d = <<-HERE
|
82
|
+
# def self.#{aSymStr}(*args)
|
83
|
+
# # This will happen the subsequent times
|
84
|
+
# @ducks["#{aSymStr}"] << args.collect { |a| #{'"#{a.class}"'} }
|
85
|
+
# @ducks["#{aSymStr}"].uniq!
|
86
|
+
# decoy = self.dup
|
87
|
+
# @decoys["#{aSymStr}"] = [] unless @decoys["#{aSymStr}"]
|
88
|
+
# @decoys["#{aSymStr}"] << decoy
|
89
|
+
# decoy
|
90
|
+
# end
|
91
|
+
# HERE
|
92
|
+
# instance_eval d
|
93
|
+
#rescue SyntaxError
|
94
|
+
# puts "This error may be avoidable by returning the failing duck type as the error message."
|
95
|
+
# raise
|
96
|
+
#end
|
97
|
+
|
98
|
+
decoy
|
99
|
+
end
|
100
|
+
|
101
|
+
end # class MethodProbe
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
class ::Method
|
107
|
+
|
108
|
+
# Outputs migration information.
|
109
|
+
def migration
|
110
|
+
parameters = []; argc = self.arity
|
111
|
+
if argc > 0
|
112
|
+
argc.times { parameters << Quarry::Probe.new }
|
113
|
+
Probe.duckcall { self.call(*parameters) }
|
114
|
+
elsif argc < 0
|
115
|
+
raise "(NYI) method takes unlimited arguments"
|
116
|
+
end
|
117
|
+
return parameters
|
118
|
+
end
|
119
|
+
private :migration
|
120
|
+
|
121
|
+
# Outputs signiture information.
|
122
|
+
def signature(detail=nil)
|
123
|
+
ds = []
|
124
|
+
case detail
|
125
|
+
when :complete, :all, :full
|
126
|
+
ds = migration
|
127
|
+
when :class, :with_class
|
128
|
+
migration.each { |dh| ds << dh.ducks }
|
129
|
+
when :pp, :pretty, :prettyprint, :pretty_print
|
130
|
+
migration.each do |dh|
|
131
|
+
responders = []
|
132
|
+
dh.ducks.each do |responder, argss|
|
133
|
+
argss.each { |args| responders << "#{responder}( #{args.join(',')} )" }
|
134
|
+
end
|
135
|
+
ds << responders
|
136
|
+
end
|
137
|
+
else
|
138
|
+
migration.each { |dh| ds << dh.ducks.keys }
|
139
|
+
end
|
140
|
+
return ds
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
=begin test
|
150
|
+
|
151
|
+
require 'test/unit'
|
152
|
+
|
153
|
+
# " I am a Duck Hunter ! "
|
154
|
+
|
155
|
+
class TC_MethodProbe < Test::Unit::TestCase
|
156
|
+
|
157
|
+
# fixture
|
158
|
+
def amethod(x)
|
159
|
+
x + 1
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_signiture_default
|
163
|
+
assert_nothing_raised {
|
164
|
+
method(:amethod).signature
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_signiture_with_class
|
169
|
+
assert_nothing_raised {
|
170
|
+
method(:amethod).signature(:class)
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
def test_signiture_pp
|
175
|
+
assert_nothing_raised {
|
176
|
+
method(:amethod).signature(:pp)
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_signiture_all
|
181
|
+
assert_nothing_raised {
|
182
|
+
method(:amethod).signature(:complete)
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
=end
|
189
|
+
|
190
|
+
# Copyright (c) 2004,2008 Thomas Sawyer
|
191
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
# = Stub
|
4
|
+
#
|
5
|
+
class Stub < Module
|
6
|
+
attr :object
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super()
|
10
|
+
@table = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
def __table__ ; @table ; end
|
15
|
+
|
16
|
+
#
|
17
|
+
def method_missing(meth, *args, &block)
|
18
|
+
table = @table
|
19
|
+
interface = [meth, args, block_given?]
|
20
|
+
|
21
|
+
table[interface] = nil
|
22
|
+
|
23
|
+
define_method(meth) do |*args|
|
24
|
+
table[interface]
|
25
|
+
end
|
26
|
+
|
27
|
+
Setter.new(table, interface)
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
class Setter
|
32
|
+
def initialize(table, interface)
|
33
|
+
@table = table
|
34
|
+
@interface = interface
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(result)
|
38
|
+
@table[@interface] = result
|
39
|
+
end
|
40
|
+
end#class Setter
|
41
|
+
|
42
|
+
# = Stub::Delegator
|
43
|
+
#
|
44
|
+
class Delegator
|
45
|
+
instance_methods(true).each{ |m| protected m unless m.to_s =~ /^__/ }
|
46
|
+
|
47
|
+
def initialize(object, stub_module)
|
48
|
+
@instance_delegate = object
|
49
|
+
extend(stub_module)
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(s, *a, &b)
|
53
|
+
@instance_delegate.__send__(s, *a, &b)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end#class Stub
|
58
|
+
|
59
|
+
class ::Object
|
60
|
+
|
61
|
+
# Create a new stub.
|
62
|
+
def stub(stub_module=nil)
|
63
|
+
if stub_module
|
64
|
+
Stub::Delegator.new(self, stub_module)
|
65
|
+
else
|
66
|
+
@_stub ||= Stub.new
|
67
|
+
extend(@_stub)
|
68
|
+
@_stub
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# We can't remove the module per-say. So we have to
|
73
|
+
# just neuter it. This is a very weak solution, but
|
74
|
+
# it will suffice for the moment.
|
75
|
+
#--
|
76
|
+
# TODO: Use Carats for #unmix.
|
77
|
+
#++
|
78
|
+
def remove_stub(stub_module=nil)
|
79
|
+
stub_module ||= @_stub
|
80
|
+
obj = self
|
81
|
+
mod = Module.new
|
82
|
+
stub_module.__table__.each do |interface, result|
|
83
|
+
meth = interface[0]
|
84
|
+
mod.module_eval do
|
85
|
+
define_method(meth, &obj.class.instance_method(meth).bind(obj))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
extend(mod)
|
89
|
+
end
|
90
|
+
|
91
|
+
end#class ::Object
|
92
|
+
|
93
|
+
end#module Quarry
|
94
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
require 'qed/assertion'
|
4
|
+
|
5
|
+
# = Expectation
|
6
|
+
#
|
7
|
+
# Expectation is an Assertion Functor.
|
8
|
+
#
|
9
|
+
class Expectation
|
10
|
+
|
11
|
+
hide = instance_methods.select{ |m| m.to_s !~ /^__/ }
|
12
|
+
hide.each{ |m| protected m }
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# New Expectation.
|
17
|
+
#
|
18
|
+
def initialize(delegate, ioc={}) #, backtrace)
|
19
|
+
@delegate = delegate
|
20
|
+
@negate = ioc[:negate]
|
21
|
+
@message = ioc[:message]
|
22
|
+
@backtrace = ioc[:backtrace] || caller #[1..-1]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Converts missing method into an Assertion.
|
26
|
+
#
|
27
|
+
def method_missing(sym, *a, &b)
|
28
|
+
test = @delegate.__send__(sym, *a, &b)
|
29
|
+
|
30
|
+
if (@negate ? test : !test)
|
31
|
+
msg = @message || __msg__(sym, *a, &b)
|
32
|
+
error = Assertion.new(msg)
|
33
|
+
error.set_backtrace(@backtrace)
|
34
|
+
raise error
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Puts together a suitable error message.
|
39
|
+
#
|
40
|
+
def __msg__(m, *a, &b)
|
41
|
+
if @negate
|
42
|
+
"! #{@delegate.inspect} #{m} #{a.collect{|x| x.inspect}.join(',')}"
|
43
|
+
else
|
44
|
+
"#{@delegate.inspect} #{m} #{a.collect{|x| x.inspect}.join(',')}"
|
45
|
+
end
|
46
|
+
#self.class.message(m)[@delegate, *a] )
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: Ultimately better messages would be nice ?
|
50
|
+
#
|
51
|
+
#def self.message(op,&block)
|
52
|
+
# @message ||= {}
|
53
|
+
# block ? @message[op.to_sym] = block : @message[op.to_sym]
|
54
|
+
#end
|
55
|
+
#
|
56
|
+
#message(:==){ |*a| "Expected #{a[0].inspect} to be equal to #{a[1].inspect}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module QED
|
2
|
+
|
3
|
+
require 'qed/expectation'
|
4
|
+
|
5
|
+
module Grammar
|
6
|
+
|
7
|
+
# = Assert Nomenclature
|
8
|
+
#
|
9
|
+
module Assert
|
10
|
+
|
11
|
+
# Assert a operational relationship.
|
12
|
+
#
|
13
|
+
# 4.assert == 3
|
14
|
+
#
|
15
|
+
# If only a single test argument is given then
|
16
|
+
# #assert simple validates that it evalutate to true.
|
17
|
+
# An optional message argument can be given in this
|
18
|
+
# case which will be used instead of the deafult message.
|
19
|
+
#
|
20
|
+
# assert(4==3, "not the same thing")
|
21
|
+
#
|
22
|
+
# In block form, #assert ensures the block evalutes
|
23
|
+
# truthfully, i.e. not as nil or false.
|
24
|
+
#
|
25
|
+
# assert{ 4==3 }
|
26
|
+
#
|
27
|
+
# If an argument is given with a block, #assert compares
|
28
|
+
# the argument to the result of evaluating the block.
|
29
|
+
#
|
30
|
+
# assert(4){ 3 }
|
31
|
+
#
|
32
|
+
# #assert compares the expected value and the actual
|
33
|
+
# value with regular equality <code>#==</code>.
|
34
|
+
#
|
35
|
+
def assert(test=Exception, msg=nil, &block)
|
36
|
+
if block
|
37
|
+
act = block.call
|
38
|
+
if test == Exception
|
39
|
+
raise Assertion.new(msg, caller) unless act
|
40
|
+
else
|
41
|
+
yes = (test == act)
|
42
|
+
msg = "#{exp}.equate? #{act}" unless msg
|
43
|
+
raise Assertion.new(msg, caller) unless yes
|
44
|
+
end
|
45
|
+
elsif test != Exception
|
46
|
+
msg = "failed assertion (no message given)" unless msg
|
47
|
+
raise Assertion.new(msg, caller) unless test
|
48
|
+
else
|
49
|
+
return Expectation.new(self, :backtrace=>caller)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Assert not an operational relationship.
|
54
|
+
# Read it as "assert not".
|
55
|
+
#
|
56
|
+
# 4.assert! == 4
|
57
|
+
#
|
58
|
+
# See #assert.
|
59
|
+
#
|
60
|
+
# AUHTOR'S NOTE: This method would not be necessary
|
61
|
+
# if Ruby would allow +!=+ to be define as a method,
|
62
|
+
# or at least +!+ as a unary method.
|
63
|
+
#
|
64
|
+
def assert!(test=Exception, msg=nil, &block)
|
65
|
+
if block
|
66
|
+
act = block.call
|
67
|
+
if test == Exception
|
68
|
+
raise Assertion.new(msg, caller) if act
|
69
|
+
else
|
70
|
+
yes = (test == act)
|
71
|
+
msg = "#{exp}.equate? #{act}" unless msg
|
72
|
+
raise Assertion.new(msg, caller) if yes
|
73
|
+
end
|
74
|
+
elsif test != Exception
|
75
|
+
msg = "failed assertion (no message given)" unless msg
|
76
|
+
raise Assertion.new(msg, caller) if test
|
77
|
+
else
|
78
|
+
return Expectation.new(self, :negate=>true, :backtrace=>caller)
|
79
|
+
end
|
80
|
+
|
81
|
+
if test
|
82
|
+
msg = "failed assertion (no message given)" unless msg
|
83
|
+
raise Assertion.new(msg, caller) if test
|
84
|
+
else
|
85
|
+
return Expectation.new(self, :negate=>true, :backtrace=>caller)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Same as #assert!.
|
90
|
+
#
|
91
|
+
# 4.refute == 4 #=> Assertion Error
|
92
|
+
#
|
93
|
+
alias_method :refute, :assert!
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class ::Object #:nodoc:
|
100
|
+
include Grammar::Assert
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|