dfect 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/api/classes/Dfect.html +138 -132
- data/doc/api/classes/Kernel.html +322 -0
- data/doc/api/created.rid +1 -1
- data/doc/api/files/lib/dfect/mini_rb.html +77 -0
- data/doc/api/files/lib/dfect/spec_rb.html +75 -0
- data/doc/api/files/lib/dfect/unit_rb.html +73 -0
- data/doc/api/files/lib/dfect_rb.html +1 -1
- data/doc/api/panel/search_index.js +1 -1
- data/doc/api/panel/tree.js +1 -1
- data/doc/history.erb +49 -1
- data/doc/index.erb +5 -0
- data/doc/index.xhtml +187 -127
- data/doc/intro.erb +48 -12
- data/doc/setup.erb +7 -18
- data/doc/usage.erb +5 -0
- data/lib/dfect.rb +27 -14
- data/lib/dfect/mini.rb +91 -0
- data/lib/dfect/spec.rb +39 -0
- data/lib/dfect/unit.rb +105 -0
- data/rakefile +3 -3
- data/test/dfect.rb +9 -21
- metadata +11 -4
data/doc/intro.erb
CHANGED
@@ -1,6 +1,33 @@
|
|
1
|
+
%#--
|
2
|
+
%# Copyright 2009 Suraj N. Kurapati
|
3
|
+
%# See the LICENSE file for details.
|
4
|
+
%#++
|
5
|
+
|
1
6
|
% api_url = './api/index.html'
|
2
7
|
% src_url = 'http://github.com/sunaku/' + $program
|
3
8
|
% git_url = 'http://git-scm.com'
|
9
|
+
<%
|
10
|
+
competitors = {
|
11
|
+
'assert{ 2.0 }' => 'http://assert2.rubyforge.org',
|
12
|
+
'Verify' => 'http://www.ruby-forum.com/topic/183354',
|
13
|
+
'Testy' => 'http://github.com/ahoward/testy/tree/master',
|
14
|
+
'minitest' => 'http://blog.zenspider.com/minitest',
|
15
|
+
'Context' => 'http://github.com/jeremymcanally/context',
|
16
|
+
'Shoulda' => 'http://thoughtbot.com/projects/shoulda',
|
17
|
+
'Bacon' => 'http://chneukirchen.org/repos/bacon/README',
|
18
|
+
'test-spec' => 'http://test-spec.rubyforge.org/test-spec',
|
19
|
+
'RSpec' => 'http://rspec.info',
|
20
|
+
'Test::Unit' => 'http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/index.html',
|
21
|
+
}
|
22
|
+
|
23
|
+
def competitors.link_for key
|
24
|
+
if val = self[key]
|
25
|
+
"[#{key}](#{val})"
|
26
|
+
else
|
27
|
+
raise key
|
28
|
+
end
|
29
|
+
end
|
30
|
+
%>
|
4
31
|
|
5
32
|
%|chapter "Introduction"
|
6
33
|
%|project
|
@@ -14,16 +41,9 @@
|
|
14
41
|
* It is implemented in a mere <%= `sloccount lib`[/^\d+/] %> lines of code.
|
15
42
|
|
16
43
|
These features distinguish <%= $project %> from the competition:
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
* [minitest](http://blog.zenspider.com/minitest)
|
21
|
-
* [Context](http://github.com/jeremymcanally/context)
|
22
|
-
* [Shoulda](http://thoughtbot.com/projects/shoulda)
|
23
|
-
* [Bacon](http://chneukirchen.org/repos/bacon/README)
|
24
|
-
* [test-spec](http://test-spec.rubyforge.org/test-spec)
|
25
|
-
* [RSpec](http://rspec.info)
|
26
|
-
* [Test::Unit](http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/index.html)
|
44
|
+
|
45
|
+
%|competitors.keys.sort_by {|name| name.downcase }.each do |name|
|
46
|
+
* <%= competitors.link_for name %>
|
27
47
|
|
28
48
|
%|paragraph "Etymology"
|
29
49
|
<%= $project %> is named after the D F E C T methods it provides.
|
@@ -32,10 +52,26 @@
|
|
32
52
|
|
33
53
|
This wordplay is similar to [Mnesia](http://www.erlang.org/doc/apps/mnesia/index.html)'s play on the word "amnesia", whereby the intentional omission of the letter "A" indicates forgetfulness---the key characteristic of having amnesia. Clever!
|
34
54
|
|
55
|
+
%|section "Motivation"
|
56
|
+
The basic premise of <%= $project %> is that, when a failure occurs, I want to be put inside an interactive debugger where I have the freedom to properly scrutinize the state of my program and determine the root cause of the failure.
|
57
|
+
|
58
|
+
Other testing libraries do not fulfill this need. Instead, they simply report each failed assertion along with a stack trace (if I am lucky) and abruptly terminate my program.
|
59
|
+
|
60
|
+
This deliberate separation of *fault* (my program being in an erroneous state) and *cause* (the source code of my program which caused the fault) reduces me to a primitive and laborious investigative technique known as "[printf debugging](http://oopweb.com/CPP/Documents/DebugCPP/Volume/techniques.html#PRINTF)".
|
61
|
+
|
62
|
+
If you are not the least bit *unsettled* by those two words, then recall your first encounter with [IRB, the interactive Ruby shell](http://tryruby.hobix.com/): remember how you would enter code expressions and IRB would *instantly* evaluate them and show you the result?
|
63
|
+
|
64
|
+
What an immense productivity boost! A *stark contrast* to the endless toil of wrapping every such experiment in standard boilerplate (`public static void`...), saving the result to a correctly named file, invoking the C/C++/Java compiler, and finally executing the binary---only to be greeted by a [segfault](http://en.wikipedia.org/wiki/Segmentation_fault). ;-)
|
65
|
+
|
66
|
+
I exaggerate, for the sake of entertainment, of course. But my point is that the Ruby testing libraries of today have (thus far) limited our productivity by orphaning us from the nurturing environment of IRB and shooing us off to a barren desert of antiquated techniques. How cruel!
|
67
|
+
|
68
|
+
And that, I say, is why <%= $project %> is essential to Ruby developers today. It reunites us with our playful, interactive, *real-time* IRB roots and, with unwavering tenacity, enables us to investigate failures *productively*!
|
69
|
+
|
35
70
|
%|section "Logistics"
|
36
71
|
* <%= xref "History", "Release notes" %> --- history of project releases.
|
37
72
|
* [Source code](<%= src_url %>) --- obtain via [Git](<%= git_url %>) or browse online.
|
38
73
|
* [API reference](<%= api_url %>) --- documentation for source code.
|
74
|
+
* [Project home](<%= $website %>) --- the <%= $project %> project home page.
|
39
75
|
|
40
76
|
To get help or provide feedback, simply
|
41
77
|
<%= xref "License", "contact the author(s)" %>.
|
@@ -80,8 +116,8 @@
|
|
80
116
|
</tbody>
|
81
117
|
</table>
|
82
118
|
|
83
|
-
|
84
|
-
|
119
|
+
%|paragraph "License"
|
120
|
+
%< "../LICENSE"
|
85
121
|
|
86
122
|
%|section "Credits"
|
87
123
|
<%= $project %> is made possible by
|
data/doc/setup.erb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
%#--
|
2
|
+
%# Copyright 2009 Suraj N. Kurapati
|
3
|
+
%# See the LICENSE file for details.
|
4
|
+
%#++
|
5
|
+
|
1
6
|
%|chapter "Setup"
|
2
7
|
%|section "Requirements"
|
3
8
|
Your system needs the following software to run <%= $project %>.
|
@@ -13,22 +18,6 @@
|
|
13
18
|
|
14
19
|
gem install <%= $program %>
|
15
20
|
|
16
|
-
|
17
|
-
You will see the following items inside <%= $project %>'s installation
|
18
|
-
directory:
|
19
|
-
|
20
|
-
* <tt>lib/</tt>
|
21
|
-
|
22
|
-
* <tt><%= $program %>.rb</tt> --- the main <%= $project %> library.
|
23
|
-
|
24
|
-
* <tt><%= $program %>/</tt>
|
25
|
-
|
26
|
-
* <tt>auto.rb</tt> --- automates test execution.
|
27
|
-
|
28
|
-
* <tt>doc/</tt>
|
29
|
-
|
30
|
-
* <tt>api/</tt> --- API reference documentation.
|
31
|
-
|
32
|
-
* <tt>index.erb</tt> --- source of this user manual.
|
21
|
+
If you want to develop <%= $project %>, run this command:
|
33
22
|
|
34
|
-
|
23
|
+
gem install <%= $program %> --development
|
data/doc/usage.erb
CHANGED
data/lib/dfect.rb
CHANGED
@@ -12,8 +12,16 @@ require 'yaml'
|
|
12
12
|
# Work around this by representing a class by its name.
|
13
13
|
#
|
14
14
|
class Class #:nodoc: all
|
15
|
+
alias __to_yaml__ to_yaml
|
16
|
+
undef to_yaml
|
17
|
+
|
15
18
|
def to_yaml opts = {}
|
16
|
-
|
19
|
+
begin
|
20
|
+
__to_yaml__
|
21
|
+
rescue TypeError => e
|
22
|
+
warn e
|
23
|
+
self.name.to_yaml opts
|
24
|
+
end
|
17
25
|
end
|
18
26
|
end
|
19
27
|
|
@@ -77,7 +85,7 @@ module Dfect
|
|
77
85
|
# during assertion failures so
|
78
86
|
# the user can investigate them.
|
79
87
|
#
|
80
|
-
# The default value is
|
88
|
+
# The default value is true.
|
81
89
|
#
|
82
90
|
# [:quiet]
|
83
91
|
# Do not print the report
|
@@ -219,7 +227,7 @@ module Dfect
|
|
219
227
|
# T( "computers do not doublethink" ) { 2 + 2 != 5 } # passes
|
220
228
|
#
|
221
229
|
def T message = nil, &block
|
222
|
-
|
230
|
+
assert_yield :assert, message, &block
|
223
231
|
end
|
224
232
|
|
225
233
|
##
|
@@ -245,7 +253,7 @@ module Dfect
|
|
245
253
|
# T!( "computers do not doublethink" ) { 2 + 2 == 5 } # passes
|
246
254
|
#
|
247
255
|
def T! message = nil, &block
|
248
|
-
|
256
|
+
assert_yield :negate, message, &block
|
249
257
|
end
|
250
258
|
|
251
259
|
##
|
@@ -270,7 +278,7 @@ module Dfect
|
|
270
278
|
# T?( "computers do not doublethink" ) { 2 + 2 != 5 } # => true
|
271
279
|
#
|
272
280
|
def T? message = nil, &block
|
273
|
-
|
281
|
+
assert_yield :sample, message, &block
|
274
282
|
end
|
275
283
|
|
276
284
|
alias F T!
|
@@ -519,16 +527,21 @@ module Dfect
|
|
519
527
|
##
|
520
528
|
# Executes all tests defined thus far and stores the results in #report.
|
521
529
|
#
|
522
|
-
|
530
|
+
# ==== Parameters
|
531
|
+
#
|
532
|
+
# [continue]
|
533
|
+
# If true, results from previous executions will not be cleared.
|
534
|
+
#
|
535
|
+
def run continue = true
|
523
536
|
# clear previous results
|
524
|
-
|
525
|
-
|
526
|
-
|
537
|
+
unless continue
|
538
|
+
@exec_stats.clear
|
539
|
+
@exec_trace.clear
|
540
|
+
@test_stack.clear
|
541
|
+
end
|
527
542
|
|
528
543
|
# make new results
|
529
|
-
catch
|
530
|
-
execute
|
531
|
-
end
|
544
|
+
catch(:stop_dfect_execution) { execute }
|
532
545
|
|
533
546
|
# print new results
|
534
547
|
puts @report.to_yaml unless @options[:quiet]
|
@@ -544,7 +557,7 @@ module Dfect
|
|
544
557
|
|
545
558
|
private
|
546
559
|
|
547
|
-
def
|
560
|
+
def assert_yield mode, message = nil, &block
|
548
561
|
raise ArgumentError, 'block must be given' unless block
|
549
562
|
|
550
563
|
message ||=
|
@@ -886,7 +899,7 @@ module Dfect
|
|
886
899
|
#:startdoc:
|
887
900
|
end
|
888
901
|
|
889
|
-
@options = {:debug =>
|
902
|
+
@options = {:debug => true, :quiet => false}
|
890
903
|
|
891
904
|
@exec_stats = Hash.new {|h,k| h[k] = 0 }
|
892
905
|
@exec_trace = []
|
data/lib/dfect/mini.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# MiniTest emulation layer.
|
2
|
+
#--
|
3
|
+
# Copyright 2009 Suraj N. Kurapati
|
4
|
+
# See the LICENSE file for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'dfect'
|
8
|
+
require 'dfect/unit'
|
9
|
+
require 'dfect/spec'
|
10
|
+
|
11
|
+
module Kernel
|
12
|
+
instance_methods(false).each do |meth|
|
13
|
+
if meth =~ /^assert_not/
|
14
|
+
alias_method 'refute' + $', meth
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
{
|
20
|
+
:must => :assert,
|
21
|
+
:wont => :refute,
|
22
|
+
}.
|
23
|
+
each do |outer, inner|
|
24
|
+
file, line = __FILE__, __LINE__ ; eval %{
|
25
|
+
class Object
|
26
|
+
def #{outer}_be_close_to other, message = nil
|
27
|
+
#{inner}_in_delta self, other, nil, message
|
28
|
+
end
|
29
|
+
|
30
|
+
def #{outer}_be_empty message = nil
|
31
|
+
#{inner}_empty self, message
|
32
|
+
end
|
33
|
+
|
34
|
+
def #{outer}_be_instance_of _class, message = nil
|
35
|
+
#{inner}_instance_of _class, self, message
|
36
|
+
end
|
37
|
+
|
38
|
+
def #{outer}_be_kind_of _class, message = nil
|
39
|
+
#{inner}_kind_of _class, self, message
|
40
|
+
end
|
41
|
+
|
42
|
+
def #{outer}_be_nil message = nil
|
43
|
+
#{inner}_nil self, message
|
44
|
+
end
|
45
|
+
|
46
|
+
def #{outer}_be_same_as other, message = nil
|
47
|
+
#{inner}_same self, other, message
|
48
|
+
end
|
49
|
+
|
50
|
+
def #{outer}_be_within_delta other, delta = nil, message = nil
|
51
|
+
#{inner}_in_delta self, other, delta, message
|
52
|
+
end
|
53
|
+
|
54
|
+
alias #{outer}_be_within_epsilon #{outer}_be_within_delta
|
55
|
+
|
56
|
+
def #{outer}_equal expected, message = nil
|
57
|
+
#{inner}_equal expected, self, message
|
58
|
+
end
|
59
|
+
|
60
|
+
def #{outer}_include item, message = nil
|
61
|
+
#{inner}_include item, self, message
|
62
|
+
end
|
63
|
+
|
64
|
+
def #{outer}_match pattern, message = nil
|
65
|
+
#{inner}_match pattern, self, message
|
66
|
+
end
|
67
|
+
|
68
|
+
def #{outer}_raise *args, &block
|
69
|
+
#{inner}_raise(*args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def #{outer}_respond_to query, message = nil
|
73
|
+
#{inner}_respond_to self, query, message
|
74
|
+
end
|
75
|
+
|
76
|
+
def #{outer}_send query, *args
|
77
|
+
#{inner}_send self, query, *args
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Proc
|
82
|
+
def #{outer}_raise *args
|
83
|
+
#{inner}_raise(*args, &self)
|
84
|
+
end
|
85
|
+
|
86
|
+
def #{outer}_throw symbol, message = nil
|
87
|
+
#{inner}_throw symbol, message, &self
|
88
|
+
end
|
89
|
+
end
|
90
|
+
}, binding, file, line
|
91
|
+
end
|
data/lib/dfect/spec.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# RSpec emulation layer.
|
2
|
+
#--
|
3
|
+
# Copyright 2009 Suraj N. Kurapati
|
4
|
+
# See the LICENSE file for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'dfect'
|
8
|
+
require 'delegate'
|
9
|
+
|
10
|
+
module Kernel
|
11
|
+
def describe *args, &block
|
12
|
+
Dfect.D args.join(' '), &block
|
13
|
+
end
|
14
|
+
|
15
|
+
alias context describe
|
16
|
+
alias it describe
|
17
|
+
|
18
|
+
def before what, &block
|
19
|
+
meth =
|
20
|
+
case what
|
21
|
+
when :each then :<
|
22
|
+
when :all then :<<
|
23
|
+
else raise ArgumentError, what
|
24
|
+
end
|
25
|
+
|
26
|
+
Dfect.send meth, &block
|
27
|
+
end
|
28
|
+
|
29
|
+
def after what, &block
|
30
|
+
meth =
|
31
|
+
case what
|
32
|
+
when :each then :>
|
33
|
+
when :all then :>>
|
34
|
+
else raise ArgumentError, what
|
35
|
+
end
|
36
|
+
|
37
|
+
Dfect.send meth, &block
|
38
|
+
end
|
39
|
+
end
|
data/lib/dfect/unit.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Test::Unit emulation layer.
|
2
|
+
#--
|
3
|
+
# Copyright 2009 Suraj N. Kurapati
|
4
|
+
# See the LICENSE file for details.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'dfect'
|
8
|
+
|
9
|
+
module Kernel
|
10
|
+
def setup &block
|
11
|
+
Dfect.<(&block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown &block
|
15
|
+
Dfect.>(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
[
|
19
|
+
[:assert, nil, nil ],
|
20
|
+
[:assert_not, '!', 'not '],
|
21
|
+
].
|
22
|
+
each do |prefix, polarity, action|
|
23
|
+
file, line = __FILE__, __LINE__ ; eval %{
|
24
|
+
def #{prefix} boolean, message = nil
|
25
|
+
Dfect.T#{polarity}(message) { boolean }
|
26
|
+
end
|
27
|
+
|
28
|
+
def #{prefix}_block message = nil, &block
|
29
|
+
Dfect.T#{polarity}(&block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def #{prefix}_empty collection, message = nil
|
33
|
+
message ||= 'collection must #{action}be empty'
|
34
|
+
Dfect.T#{polarity}(message) { collection.empty? }
|
35
|
+
end
|
36
|
+
|
37
|
+
def #{prefix}_equal actual, expected, message = nil
|
38
|
+
message ||= 'actual must #{action}equal expected'
|
39
|
+
Dfect.T#{polarity}(message) { actual == expected }
|
40
|
+
end
|
41
|
+
|
42
|
+
def #{prefix}_in_delta expected, actual, delta = nil, message = nil
|
43
|
+
message ||= 'actual must #{action}be within delta of expected'
|
44
|
+
delta ||= 0.001
|
45
|
+
|
46
|
+
Dfect.T#{polarity}(message) { Math.abs(expected - actual) <= Math.abs(delta) }
|
47
|
+
end
|
48
|
+
|
49
|
+
alias #{prefix}_in_epsilon #{prefix}_in_delta
|
50
|
+
|
51
|
+
def #{prefix}_include item, collection, message = nil
|
52
|
+
message ||= 'collection must #{action}include item'
|
53
|
+
Dfect.T#{polarity}(messsage) { collection.include? item }
|
54
|
+
end
|
55
|
+
|
56
|
+
def #{prefix}_instance_of _class, object, message = nil
|
57
|
+
message ||= 'object must #{action}be an instance of class'
|
58
|
+
Dfect.T#{polarity}(message) { object.instance_of? _class }
|
59
|
+
end
|
60
|
+
|
61
|
+
def #{prefix}_kind_of _class, object, message = nil
|
62
|
+
message ||= 'object must #{action}be a kind of class'
|
63
|
+
Dfect.T#{polarity}(message) { object.kind_of? _class }
|
64
|
+
end
|
65
|
+
|
66
|
+
def #{prefix}_nil object, message = nil
|
67
|
+
message ||= 'object must #{action}be nil'
|
68
|
+
Dfect.T#{polarity}(message) { object == nil }
|
69
|
+
end
|
70
|
+
|
71
|
+
def #{prefix}_match pattern, string, message = nil
|
72
|
+
message ||= 'string must #{action}match pattern'
|
73
|
+
Dfect.T#{polarity}(message) { string =~ pattern }
|
74
|
+
end
|
75
|
+
|
76
|
+
def #{prefix}_same expected, actual, message = nil
|
77
|
+
message ||= 'actual must #{action}be same as expected'
|
78
|
+
Dfect.T#{polarity}(message) { actual.equal? expected }
|
79
|
+
end
|
80
|
+
|
81
|
+
def #{prefix}_operator object, operator, operand, message = nil
|
82
|
+
message ||= 'object must #{action}support operator with operand'
|
83
|
+
Dfect.T#{polarity} { object.__send__ operator, operand }
|
84
|
+
end
|
85
|
+
|
86
|
+
def #{prefix}_raise *args, &block
|
87
|
+
Dfect.E#{polarity}(args.pop, *args, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def #{prefix}_respond_to object, query, message = nil
|
91
|
+
message ||= 'object must #{action}respond to query'
|
92
|
+
Dfect.T#{polarity}(message) { object.respond_to? query }
|
93
|
+
end
|
94
|
+
|
95
|
+
def #{prefix}_throw symbol, message = nil, &block
|
96
|
+
Dfect.C#{polarity}(message, symbol, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
def #{prefix}_send object, query, *args
|
100
|
+
response = object.__send__(query, *args)
|
101
|
+
Dfect.T#{polarity} { response }
|
102
|
+
end
|
103
|
+
}, binding, file, line
|
104
|
+
end
|
105
|
+
end
|