dfect 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CREDITS CHANGED
@@ -1,2 +1,20 @@
1
- - François Beausoleil <http://github.com/francois>
2
- - Iñaki Baz Castillo <http://github.com/ibc>
1
+ %#----------------------------------------------------------------------------
2
+ ## AUTHORS
3
+ %#----------------------------------------------------------------------------
4
+
5
+ Suraj N. Kurapati
6
+
7
+ %#----------------------------------------------------------------------------
8
+ ## CREDITS
9
+ %#----------------------------------------------------------------------------
10
+
11
+ François Beausoleil,
12
+ Iñaki Baz Castillo,
13
+ Sean O'Halpin
14
+
15
+ %#----------------------------------------------------------------------------
16
+ ## LICENSE
17
+ %#----------------------------------------------------------------------------
18
+
19
+ %# See the file named "LICENSE".
20
+ %< "LICENSE"
data/bin/dfect CHANGED
@@ -1,57 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
- #
3
- # Usage:
4
- #
5
- # dfect [Options] FILE_OR_GLOB...
6
- #
7
- # FILE_OR_GLOB : A file or file globbing pattern that
8
- # describes a set of files to evaluate.
9
- #
10
- #
11
- # Options:
12
- #
13
- # -d, --debug : Launch interactive debugger
14
- # during assertion failures.
15
- #
16
- # -q, --quiet : Do not show execution report.
17
- #
18
- # -h, --help : Show this message and exit.
19
- #
20
- # -v, --version : Show version number and exit.
21
- #
22
2
 
23
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
24
3
  require 'dfect'
25
4
 
26
- # parse command line
27
5
  if ARGV.delete('-h') or ARGV.delete('--help')
28
- puts
29
- puts Dfect.inspect
30
- puts Dfect::TAGLINE
31
- puts
32
- puts
33
- puts File.read(__FILE__)[/^#(\r?\n)(.*?)\1\1/m, 2].gsub(/^# ?/, '')
34
- puts
35
- puts
36
- puts 'Read the user manual for more information:'
37
- puts
38
- puts " #{Dfect::INSTDIR}/doc/index.html"
39
- puts
40
- puts " #{Dfect::WEBSITE}"
41
- puts
6
+ # try to display UNIX version of help manual
7
+ man_path = File.join(Dfect::INSTDIR, 'man')
8
+ unless system 'man', '-M', man_path, '-a', 'dfect'
9
+ # try to display HTML version of help manual
10
+ man_html = man_path + '.html'
11
+ unless %w[$BROWSER open start].any? {|b| system "#{b} #{man_html}" }
12
+ # no luck; direct user to project website
13
+ puts "See #{Dfect::WEBSITE}"
14
+ end
15
+ end
42
16
  exit
43
17
  elsif ARGV.delete('-v') or ARGV.delete('--version')
44
18
  puts Dfect::VERSION
45
19
  exit
46
20
  end
47
21
 
48
- if ARGV.delete('-d') or ARGV.delete('--debug')
49
- Dfect.options[:debug] = true
50
- end
51
-
52
- if ARGV.delete('-q') or ARGV.delete('--quiet')
53
- Dfect.options[:quiet] = true
54
- end
22
+ Dfect.options[:debug] = ARGV.delete('-d') || ARGV.delete('--debug')
23
+ Dfect.options[:quiet] = ARGV.delete('-q') || ARGV.delete('--quiet')
55
24
 
56
25
  require 'dfect/auto'
57
26
  ARGV.each {|glob| Dir[glob].each {|test| load test } }
@@ -0,0 +1,24 @@
1
+ # Provides full name aliases for Dfect's default abbreviated vocabulary.
2
+
3
+ require 'dfect'
4
+
5
+ module Dfect
6
+ full_names = {
7
+ 'D' => 'Describe',
8
+ 'T' => 'True',
9
+ 'F' => 'False',
10
+ 'E' => 'Error',
11
+ 'C' => 'Catch',
12
+ 'S' => 'Share',
13
+ 'L' => 'Log',
14
+ }
15
+
16
+ instance_methods(false).each do |meth_name|
17
+ if full_name = meth_name.to_s.sub!(/^[A-Z]/) {|abbr| full_names[abbr] }
18
+ alias_method full_name, meth_name
19
+ end
20
+ end
21
+
22
+ # for hooks
23
+ Describe = D
24
+ end
@@ -18,12 +18,12 @@ module Dfect
18
18
  ##
19
19
  # Number of this release of this project.
20
20
  #
21
- VERSION = "2.1.0"
21
+ VERSION = "2.2.0"
22
22
 
23
23
  ##
24
24
  # Date of this release of this project.
25
25
  #
26
- RELDATE = "2010-03-31"
26
+ RELDATE = "2010-04-28"
27
27
 
28
28
  ##
29
29
  # Description of this release of this project.
@@ -74,29 +74,20 @@ module Dfect
74
74
  # }
75
75
  #
76
76
  DEVTIME = {
77
- "inochi" => [ "~> 2" ], # for managing this project
77
+ "inochi" => [ ">= 3.0.0", "< 4" ],
78
78
  }
79
79
 
80
- ##
81
- # Loads the correct version (as defined by the {RUNTIME} or {DEVTIME}
82
- # constant in this module) of the given gem or the gem that contains
83
- # the given library.
84
- #
85
- def self.require gem_name_or_library
86
- # prepare the correct version of the gem for loading
87
- if respond_to? :gem
88
- gem_name = gem_name_or_library.to_s.sub(%r{/.*$}, '')
89
- if gem_version = RUNTIME[gem_name] || DEVTIME[gem_name]
80
+ # establish gem version dependencies
81
+ if respond_to? :gem
82
+ [RUNTIME, DEVTIME].each do |deps|
83
+ deps.each do |gem_name, gem_version|
90
84
  begin
91
- gem gem_name, *gem_version
85
+ gem gem_name, *Array(gem_version)
92
86
  rescue LoadError => error
93
87
  warn "#{self.inspect}: #{error}"
94
88
  end
95
89
  end
96
90
  end
97
-
98
- # do the loading
99
- super
100
91
  end
101
92
 
102
93
  end
@@ -0,0 +1,947 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv='content-type' value='text/html;charset=utf8'>
5
+ <meta name='generator' value='Ronn/v0.5'>
6
+ <title>dfect(1) - Assertion testing library for Ruby</title>
7
+ <style type='text/css'>
8
+ body {margin:0}
9
+ #man, #man code, #man pre, #man tt, #man kbd, #man samp {
10
+ font-family:consolas,monospace;
11
+ font-size:16px;
12
+ line-height:1.3;
13
+ color:#343331;
14
+ background:#fff; }
15
+ #man { max-width:87ex; margin:0 3ex 1ex 1ex; text-align:justify; }
16
+ #man div.man-navigation {
17
+ position:fixed;
18
+ top:0;
19
+ left:91ex;
20
+ height:100%;
21
+ width:100%;
22
+ padding:1ex 0 0 2ex;
23
+ border-left:0.5ex solid #DCDCDC;
24
+ background-color: #F5F5F5;
25
+ }
26
+ #man div.man-navigation a { display:block; margin-bottom:1.5ex }
27
+ #man h1, #man h2, #man h3 { color:#232221;clear:left }
28
+ #man h1 { font-size:28px; margin:15px 0 30px 0; text-align:center }
29
+ #man h2 { font-size:18px; margin-bottom:0; margin-top:10px; line-height:1.3; }
30
+ #man h3 { font-size:16px; margin:0 0 0 4ex; }
31
+ #man p, #man ul, #man ol, #man dl, #man pre { margin:0 0 18px 0; }
32
+ #man pre {
33
+ color:#333231;
34
+ background:#edeceb;
35
+ padding:5px 7px;
36
+ margin:0px 0 20px 0;
37
+ border-left:2ex solid #ddd}
38
+ #man pre + h2, #man pre + h3 {
39
+ margin-top:22px;
40
+ }
41
+ #man h2 + pre, #man h3 + pre {
42
+ margin-top:5px;
43
+ }
44
+ #man > p, #man > ul, #man > ol, #man > dl, #man > pre { margin-left:8ex; }
45
+ #man dt { margin:0; clear:left }
46
+ #man dt.flush { float:left; width:8ex }
47
+ #man dd { margin:0 0 0 9ex }
48
+ #man code, #man strong, #man b { font-weight:bold; color:#131211; }
49
+ #man pre code { font-weight:normal; color:#232221; background:inherit }
50
+ #man em, var, u {
51
+ font-style:normal; color:#333231; border-bottom:1px solid #999; }
52
+ #man h1.man-title { display:none; }
53
+ #man ol.man, #man ol.man li { margin:2px 0 10px 0; padding:0;
54
+ float:left; width:33%; list-style-type:none;
55
+ text-transform:uppercase; font-size:18px; color:#999;
56
+ letter-spacing:1px;}
57
+ #man ol.man { width:100%; }
58
+ #man ol.man li.tl { text-align:left }
59
+ #man ol.man li.tc { text-align:center;letter-spacing:4px }
60
+ #man ol.man li.tr { text-align:right; float: right }
61
+ #man ol.man a { color:#999 }
62
+ #man ol.man a:hover { color:#333231 }
63
+ #man img { display:block; margin:auto }
64
+ </style>
65
+ <style type='text/css' media='print'>
66
+ #man { max-width:none }
67
+ #man div.man-navigation { display:none }
68
+ #man a[href]:not([href^="#"]):not([data-bare-link]):after {
69
+ content:" " attr(href);
70
+ }
71
+ </style>
72
+ </head>
73
+ <body>
74
+ <div id='man'>
75
+
76
+ <div class='man-navigation'><a href='#NAME'>NAME</a> <a href='#SYNOPSIS'>SYNOPSIS</a> <a href='#DESCRIPTION'>DESCRIPTION</a> <a href='#OPTIONS'>OPTIONS</a> <a href='#TESTS'>TESTS</a> <a href='#ASSERTIONS'>ASSERTIONS</a> <a href='#EMULATION'>EMULATION</a> <a href='#EXAMPLES'>EXAMPLES</a> <a href='#HACKING'>HACKING</a> <a href='#VERSIONS'>VERSIONS</a> <a href='#AUTHORS'>AUTHORS</a> <a href='#CREDITS'>CREDITS</a> <a href='#LICENSE'>LICENSE</a> <a href='#SEE-ALSO'>SEE ALSO</a></div>
77
+
78
+ <h1 class='man-title'>dfect(1)</h1>
79
+
80
+ <ol class='head man'>
81
+ <li class='tl'>dfect(1)</li>
82
+ <li class='tc'>Version 2.2.0</li>
83
+ <li class='tr'>dfect(1)</li>
84
+ </ol>
85
+
86
+ <h2 id='NAME'>NAME</h2>
87
+ <p><code>dfect</code> - Assertion testing library for Ruby</p>
88
+
89
+ <p>Dfect is an assertion testing library for Ruby that emphasizes a simple
90
+ assertion vocabulary, instant debuggability of failures, and flexibility in
91
+ composing tests.</p>
92
+
93
+ <h3 id="Features">Features</h3>
94
+
95
+ <ul>
96
+ <li><p>Adds only 7 mnemonic method names to your memory:</p>
97
+
98
+ <p><code>D</code>escribe, <code>T</code>rue, <code>F</code>alse, <code>E</code>rror, <code>C</code>atch, <code>S</code>hare, and <code>L</code>og.</p></li>
99
+ <li><p>Lets you debug assertion failures interactively.</p></li>
100
+ <li><p>Lets you nest tests, assertions, and execution hooks.</p></li>
101
+ <li><p>Maintains a detailed report of assertion failures.</p></li>
102
+ <li><p>Implemented in 375 lines of code.</p></li>
103
+ </ul>
104
+
105
+
106
+ <h3 id="Resources">Resources</h3>
107
+
108
+ <dl>
109
+ <dt>Issue tracker (report bugs, request features, get help)</dt><dd><p><a href="http://github.com/sunaku/dfect/issues" data-bare-link="true">http://github.com/sunaku/dfect/issues</a></p></dd>
110
+ <dt>Source code (browse online or obtain with <a href="http://git-scm.com">Git</a>)</dt><dd><p><a href="http://github.com/sunaku/dfect" data-bare-link="true">http://github.com/sunaku/dfect</a></p></dd>
111
+ <dt>API documentation</dt><dd><p><a href="http://snk.tuxfamily.org/lib/dfect/api/" data-bare-link="true">http://snk.tuxfamily.org/lib/dfect/api/</a></p></dd>
112
+ <dt>Announcements feed</dt><dd><p><a href="http://snk.tuxfamily.org/lib/dfect/ann.xml" data-bare-link="true">http://snk.tuxfamily.org/lib/dfect/ann.xml</a></p></dd>
113
+ <dt>Official website</dt><dd><p><a href="http://snk.tuxfamily.org/lib/ember/" data-bare-link="true">http://snk.tuxfamily.org/lib/ember/</a></p></dd>
114
+ </dl>
115
+
116
+
117
+ <h3 id="Setup">Setup</h3>
118
+
119
+ <p>Prerequisites:</p>
120
+
121
+ <ul>
122
+ <li><p><a href="http://ruby-lang.org">Ruby</a> 1.8.6 or newer.</p></li>
123
+ <li><p><a href="http://rubygems.org">RubyGems</a> 1.3.6 or newer.</p></li>
124
+ <li><p><a href="http://github.com/mark-moseley/ruby-debug">ruby-debug</a> will be used to help you debug assertion
125
+ failures interactively if it is installed. Otherwise, the
126
+ standard interactive Ruby shell (IRB) will be used instead.</p></li>
127
+ </ul>
128
+
129
+
130
+ <p>Installing:</p>
131
+
132
+ <pre><code>gem install dfect
133
+ </code></pre>
134
+
135
+ <p>Upgrading:</p>
136
+
137
+ <pre><code>gem update dfect
138
+ </code></pre>
139
+
140
+ <p>Removing:</p>
141
+
142
+ <pre><code>gem uninstall dfect
143
+ </code></pre>
144
+
145
+ <h2 id="SYNOPSIS">SYNOPSIS</h2>
146
+
147
+ <p><code>dfect</code> [<var>OPTIONS</var>] (<var>FILE</var>|<var>GLOB</var>) ...</p>
148
+
149
+ <h2 id="DESCRIPTION">DESCRIPTION</h2>
150
+
151
+ <p>Evaluates the given <var>FILE</var>s and also files matching the given <var>GLOB</var> patterns.</p>
152
+
153
+ <p>The exit status of this command reflects the number of errors and assertion
154
+ failures up to a maximum of 255 (to avoid 8-bit unsigned integer overflow).</p>
155
+
156
+ <h2 id="OPTIONS">OPTIONS</h2>
157
+
158
+ <dl>
159
+ <dt><code>-d</code>, <code>--debug</code></dt><dd><p>Launch interactive debugger upon assertion failures.</p></dd>
160
+ <dt><code>-q</code>, <code>--quiet</code></dt><dd><p>Do not print execution report.</p></dd>
161
+ <dt><code>-h</code>, <code>--help</code></dt><dd><p>Display this manual and exit.</p></dd>
162
+ <dt><code>-v</code>, <code>--version</code></dt><dd><p>Print version number and exit.</p></dd>
163
+ </dl>
164
+
165
+
166
+ <h2 id="TESTS">TESTS</h2>
167
+
168
+ <p>The <code>D()</code> method defines a new Dfect <strong>test</strong>, which is analagous to the
169
+ concept of <strong>test case</strong> in <a href="http://en.wikipedia.org/wiki/XUnit">xUnit</a> or <strong>describe</strong> in <a href="http://rspec.info">RSpec</a>. A test may
170
+ contain nested tests.</p>
171
+
172
+ <pre><code>D "outer test" do
173
+ # assertions and logic here
174
+
175
+ D "inner test" do
176
+ # more assertions and logic here
177
+ end
178
+ end
179
+ </code></pre>
180
+
181
+ <h3 id="Execution">Execution</h3>
182
+
183
+ <p>Tests are executed in depth-first order.</p>
184
+
185
+ <p>You can configure the test execution process using:</p>
186
+
187
+ <pre><code>Dfect.options = your_options_hash
188
+ </code></pre>
189
+
190
+ <p>You can execute all tests defined thus far using:</p>
191
+
192
+ <pre><code>Dfect.run
193
+ </code></pre>
194
+
195
+ <p>You can stop the execution at any time using:</p>
196
+
197
+ <pre><code>Dfect.stop
198
+ </code></pre>
199
+
200
+ <p>You can view the results of execution using:</p>
201
+
202
+ <pre><code>puts Dfect.report.to_yaml
203
+ </code></pre>
204
+
205
+ <p>You can mix-in the <code>Dfect</code> module into your program and execute all tests
206
+ defined by your program before it terminates by simply adding the following
207
+ line at the top of your program:</p>
208
+
209
+ <pre><code>require 'dfect/auto'
210
+ </code></pre>
211
+
212
+ <p>See the API documentation for more information and examples.</p>
213
+
214
+ <h3 id="Hooks">Hooks</h3>
215
+
216
+ <p>The <code>D()</code> method provides several entry points (hooks) into the test
217
+ execution process:</p>
218
+
219
+ <pre><code>D "outer test" do
220
+ D .&lt; { puts "before each nested test" }
221
+ D .&gt; { puts "after each nested test" }
222
+ D .&lt;&lt; { puts "before all nested tests" }
223
+ D .&gt;&gt; { puts "after all nested tests" }
224
+
225
+ D "inner test" do
226
+ # assertions and logic here
227
+ end
228
+ end
229
+ </code></pre>
230
+
231
+ <p>A hook method may be called multiple times. Each call registers
232
+ additional logic to execute during the hook:</p>
233
+
234
+ <pre><code>D .&lt; { puts "do something" }
235
+ D .&lt; { puts "do something more!" }
236
+ </code></pre>
237
+
238
+ <h3 id="Logging">Logging</h3>
239
+
240
+ <p>The <code>L()</code> method lets you insert log messages, composed of arbitrary
241
+ Ruby objects, into the test execution report.</p>
242
+
243
+ <h3 id="Sharing">Sharing</h3>
244
+
245
+ <dl>
246
+ <dt class="flush"><code>S()</code></dt><dd><p>Mechanism for sharing code. When called with a block, it shares the given
247
+ block (under a given identifier) for injection into other tests. When
248
+ called without a block, it injects a previously shared block (under a given
249
+ identifier) into the environment where it is called.</p></dd>
250
+ <dt class="flush"><code>S!()</code></dt><dd><p>Combination of the two uses of the <code>S()</code> method: it lets you simultaneously
251
+ share a block of code while injecting it into the environment where that
252
+ method is called.</p></dd>
253
+ <dt class="flush"><code>S?()</code></dt><dd><p>Checks whether any code has been shared under a given identifier.</p></dd>
254
+ </dl>
255
+
256
+
257
+ <h3 id="Insulation">Insulation</h3>
258
+
259
+ <p>The <code>D!()</code> method defines a new test that is explicitly insulated from
260
+ the tests that contain it and also from the top-level Ruby environment.
261
+ Root-level calls to the <code>D()</code> method are insulated by default.</p>
262
+
263
+ <p>Inside an insulated test, you are free to mix-in any modules your test
264
+ logic needs and also define your own constants, methods, and classes.</p>
265
+
266
+ <h2 id="ASSERTIONS">ASSERTIONS</h2>
267
+
268
+ <p>The following methods accept a block parameter and assert something about the
269
+ result of executing that block. They also accept an optional message, which
270
+ is shown in failure reports (see <strong>Failures</strong> below) if they fail.</p>
271
+
272
+ <dl>
273
+ <dt class="flush"><code>T()</code></dt><dd><p>assert true (not <code>nil</code> and not <code>false</code>)</p></dd>
274
+ <dt class="flush"><code>F()</code></dt><dd><p>assert not true (<code>nil</code> or <code>false</code>)</p></dd>
275
+ <dt class="flush"><code>E()</code></dt><dd><p>assert that an execption is raised</p></dd>
276
+ <dt class="flush"><code>C()</code></dt><dd><p>assert that a symbol is thrown</p></dd>
277
+ </dl>
278
+
279
+
280
+ <h3 id="Negation">Negation</h3>
281
+
282
+ <p>The following methods are the <em>opposite</em> of normal assertions. They also
283
+ accept an optional message, which is shown in failure reports (see
284
+ <strong>Failures</strong> below) if they fail.</p>
285
+
286
+ <dl>
287
+ <dt class="flush"><code>T!()</code></dt><dd><p>same as <code>F()</code></p></dd>
288
+ <dt class="flush"><code>F!()</code></dt><dd><p>same as <code>T()</code></p></dd>
289
+ <dt class="flush"><code>E!()</code></dt><dd><p>assert that an exception is <em>not</em> raised</p></dd>
290
+ <dt class="flush"><code>C!()</code></dt><dd><p>assert that a symbol is <em>not</em> thrown</p></dd>
291
+ </dl>
292
+
293
+
294
+ <h3 id="Sampling">Sampling</h3>
295
+
296
+ <p>The following methods let you <em>check the outcome</em> of an assertion without
297
+ recording a success or failure in the test execution report.</p>
298
+
299
+ <dl>
300
+ <dt class="flush"><code>T?()</code></dt><dd><p>returns true if <code>T()</code> passes; false otherwise</p></dd>
301
+ <dt class="flush"><code>F?()</code></dt><dd><p>returns true if <code>F()</code> passes; false otherwise</p></dd>
302
+ <dt class="flush"><code>E?()</code></dt><dd><p>returns true if <code>E()</code> passes; false otherwise</p></dd>
303
+ <dt class="flush"><code>C?()</code></dt><dd><p>returns true if <code>C()</code> passes; false otherwise</p></dd>
304
+ </dl>
305
+
306
+
307
+ <h3 id="Failures">Failures</h3>
308
+
309
+ <p>When an assertion fails, details about the failure will be shown:</p>
310
+
311
+ <pre><code>- fail: block must yield true (!nil &amp;&amp; !false)
312
+ code: |-
313
+ [12..22] in test/simple.rb
314
+ 12
315
+ 13 D "with more nested tests" do
316
+ 14 x = 5
317
+ 15
318
+ 16 T { x &gt; 2 } # passes
319
+ =&gt; 17 F { x &gt; 2 } # fails
320
+ 18 E { x.hello } # passes
321
+ 19 end
322
+ 20 end
323
+ 21
324
+ 22 # equivalent of before(:each) or setup()
325
+ vars:
326
+ x: 5
327
+ y: 83
328
+ call:
329
+ - test/simple.rb:17
330
+ - test/simple.rb:3
331
+ </code></pre>
332
+
333
+ <p>You will then be placed into a debugger to investigate the failure if
334
+ the <code>:debug</code> option is enabled in the <code>Dfect.options</code> hash.</p>
335
+
336
+ <p>Details about all assertion failures and a trace of all tests executed
337
+ are stored by Dfect and provided by the <code>Dfect.report()</code> method.</p>
338
+
339
+ <h2 id="EMULATION">EMULATION</h2>
340
+
341
+ <p>Dfect provides emulation layers for several popular testing libraries:</p>
342
+
343
+ <dl>
344
+ <dt>dfect/unit</dt><dd><p><a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">Test::Unit</a> emulation layer</p></dd>
345
+ <dt>dfect/mini</dt><dd><p><a href="http://blog.zenspider.com/minitest/">Minitest</a> emulation layer</p></dd>
346
+ <dt>dfect/spec</dt><dd><p><a href="http://rspec.info">RSpec</a> emulation layer</p></dd>
347
+ </dl>
348
+
349
+
350
+ <p>Simply load one of these emulation layers into your test suite and you can
351
+ write your tests using the familiar syntax of that testing library. See
352
+ their source code for more information.</p>
353
+
354
+ <h2 id="EXAMPLES">EXAMPLES</h2>
355
+
356
+ <p>Begin by loading Dfect into your program:</p>
357
+
358
+ <pre><code>require 'rubygems' # might not be necessary; see HACKING
359
+ require 'dfect'
360
+ </code></pre>
361
+
362
+ <p>You now have access to the <code>Dfect</code> module, which provides methods that can
363
+ be either mixed-in or called directly, according to your preference:</p>
364
+
365
+ <pre><code>Dfect.D "hello" do # D() is a class method
366
+ puts "world"
367
+ end
368
+
369
+ # the above is same as:
370
+
371
+ include Dfect # mix-in the Dfect API
372
+
373
+ D "hello" do # D() is an instance method
374
+ puts "world"
375
+ end
376
+ </code></pre>
377
+
378
+ <h3 id="Logging-information-in-the-execution-report">Logging information in the execution report</h3>
379
+
380
+ <p>When the following test is run:</p>
381
+
382
+ <pre><code>require 'dfect/auto'
383
+
384
+ D 'Wizard' do
385
+ L 'Preparing spell to defeat mortal foes...'
386
+ end
387
+
388
+ D 'Magician' do
389
+ L 'Preparing rabbits to pull from hat...', rand(15)
390
+ end
391
+
392
+ D 'Calculator' do
393
+ L Math::PI, [1, 2, 3, ['a', 'b', 'c']], {:foo =&gt; 'bar!'}
394
+ end
395
+ </code></pre>
396
+
397
+ <p>Dfect will output the following:</p>
398
+
399
+ <pre><code>---
400
+ - Wizard:
401
+ - Preparing spell to defeat mortal foes...
402
+ - Magician:
403
+ - Preparing rabbits to pull from hat...
404
+ - 0
405
+ - Calculator:
406
+ - 3.14159265358979
407
+ - - 1
408
+ - 2
409
+ - 3
410
+ - - a
411
+ - b
412
+ - c
413
+ - foo: bar!
414
+ ---
415
+ time: 0.000182276
416
+ </code></pre>
417
+
418
+ <h3 id="Sharing-code-between-tests">Sharing code between tests</h3>
419
+
420
+ <p>When the following test is run:</p>
421
+
422
+ <pre><code>require 'dfect/auto'
423
+
424
+ S :knowledge do
425
+ L 'Knowledge is power!'
426
+ end
427
+
428
+ D 'Healer' do
429
+ S :knowledge
430
+ end
431
+
432
+ D 'Warrior' do
433
+ S! :strength do
434
+ L 'Strength is power!'
435
+ end
436
+ end
437
+
438
+ D 'Wizard' do
439
+ S :knowledge
440
+ S :strength
441
+ end
442
+
443
+ D 'King' do
444
+ T { S? :knowledge }
445
+ T { S? :strength }
446
+ F { S? :power }
447
+ L 'Power is power!'
448
+ end
449
+ </code></pre>
450
+
451
+ <p>Dfect will output the following:</p>
452
+
453
+ <pre><code>---
454
+ - Healer:
455
+ - Knowledge is power!
456
+ - Warrior:
457
+ - Strength is power!
458
+ - Wizard:
459
+ - Knowledge is power!
460
+ - Strength is power!
461
+ - King:
462
+ - Power is power!
463
+ ---
464
+ pass: 3
465
+ time: 0.000436242
466
+ </code></pre>
467
+
468
+ <h3 id="Insulated-and-uninsulated-tests">Insulated and uninsulated tests</h3>
469
+
470
+ <p>When the following test is run:</p>
471
+
472
+ <pre><code>require 'dfect/auto'
473
+
474
+ D "a root-level test" do
475
+ @outside = 1
476
+ T { defined? @outside }
477
+ T { @outside == 1 }
478
+
479
+ D "an inner, non-insulated test" do
480
+ T { defined? @outside }
481
+ T { @outside == 1 }
482
+ end
483
+
484
+ D! "an inner, insulated test" do
485
+ F { defined? @outside }
486
+ F { @outside == 1 }
487
+
488
+ @inside = 2
489
+ T { defined? @inside }
490
+ T { @inside == 2 }
491
+ end
492
+
493
+ F { defined? @inside }
494
+ F { @inside == 2 }
495
+ end
496
+ </code></pre>
497
+
498
+ <p>Dfect will output the following:</p>
499
+
500
+ <pre><code>---
501
+ - a root-level test:
502
+ - an inner, non-insulated test: []
503
+
504
+ - an inner, insulated test: []
505
+
506
+ ---
507
+ pass: 10
508
+ time: 0.000504211
509
+ </code></pre>
510
+
511
+ <h2 id="HACKING">HACKING</h2>
512
+
513
+ <p>This section is meant for people who want to develop Dfect's source code.</p>
514
+
515
+ <h3 id="Prerequisites">Prerequisites</h3>
516
+
517
+ <p>Install Ruby libraries necessary for development:</p>
518
+
519
+ <pre><code>gem install dfect --development
520
+ </code></pre>
521
+
522
+ <h3 id="Infrastructure">Infrastructure</h3>
523
+
524
+ <p><a href="http://snk.tuxfamily.org/lib/inochi/">Inochi</a> serves as the project infrastructure for Dfect. It handles tasks
525
+ such as building this help manual and API documentation, and packaging,
526
+ announcing, and publishing new releases. See its help manual and list of
527
+ tasks to get started:</p>
528
+
529
+ <pre><code>inochi --help # display help manual
530
+ inochi --tasks # list available tasks
531
+ </code></pre>
532
+
533
+ <h3 id="-LOAD_PATH-setup">$LOAD_PATH setup</h3>
534
+
535
+ <p>Ensure that the <code>lib/</code> directory is listed in Ruby's <code>$LOAD_PATH</code> before you
536
+ use any libraries therein or run any executables in the <code>bin/</code> directory.</p>
537
+
538
+ <p>This can be achieved by passing an option to Ruby:</p>
539
+
540
+ <pre><code>ruby -Ilib bin/dfect
541
+ irb -Ilib -r dfect
542
+ </code></pre>
543
+
544
+ <p>Or by setting the <code>$RUBYLIB</code> environment variable:</p>
545
+
546
+ <pre><code>export RUBYLIB=lib # bash, ksh, zsh
547
+ setenv RUBYLIB lib # csh
548
+ set -x RUBYLIB lib # fish
549
+
550
+ ruby bin/dfect
551
+ irb -r dfect
552
+ </code></pre>
553
+
554
+ <p>Or by installing the <a href="http://github.com/chneukirchen/rup">ruby-wrapper</a> tool.</p>
555
+
556
+ <h3 id="RubyGems-setup">RubyGems setup</h3>
557
+
558
+ <p>If you use Ruby 1.8 or older, then ensure that RubyGems is activated before
559
+ you use any libraries in the <code>lib/</code> directory or run any executables in the
560
+ <code>bin/</code> directory.</p>
561
+
562
+ <p>This can be achieved by passing an option to Ruby:</p>
563
+
564
+ <pre><code>ruby -rubygems bin/dfect
565
+ irb -rubygems -r dfect
566
+ </code></pre>
567
+
568
+ <p>Or by setting the <code>$RUBYOPT</code> environment variable:</p>
569
+
570
+ <pre><code>export RUBYOPT=-rubygems # bash, ksh, zsh
571
+ setenv RUBYOPT -rubygems # csh
572
+ set -x RUBYOPT -rubygems # fish
573
+ </code></pre>
574
+
575
+ <h3 id="Running-tests">Running tests</h3>
576
+
577
+ <p>Simply execute the included test runner, which sets up Ruby's <code>$LOAD_PATH</code> for
578
+ testing, loads the included <code>test/test_helper.rb</code> file, and then evaluates all
579
+ <code>test/**/*_test.rb</code> files:</p>
580
+
581
+ <pre><code>test/runner
582
+ </code></pre>
583
+
584
+ <p>Its exit status will indicate whether all tests have passed. It may also
585
+ print additional pass/fail information depending on the testing library used
586
+ in the <code>test/test_helper.rb</code> file.</p>
587
+
588
+ <h3 id="Contributing">Contributing</h3>
589
+
590
+ <p>Fork this project on GitHub (see <strong>Resources</strong> above) and send a pull request.</p>
591
+
592
+ <h2 id="VERSIONS">VERSIONS</h2>
593
+
594
+ <p>This section contains release notes of current and past releases.</p>
595
+
596
+ <h3 id="Version-2-2-0-2010-04-28-">Version 2.2.0 (2010-04-28)</h3>
597
+
598
+ <p>This release adds a UNIX manual page and a sub-library for full method names.</p>
599
+
600
+ <p>New features:</p>
601
+
602
+ <ul>
603
+ <li><p>Add <code>dfect/full</code> sub-library that provides full name aliases to Dfect's
604
+ abbreviated vocabulary:</p>
605
+
606
+ <p><code>D</code>escribe, <code>T</code>rue, <code>F</code>alse, <code>E</code>rror, <code>C</code>atch, <code>S</code>hare, and <code>L</code>og.</p></li>
607
+ <li><p>Run <code>dfect --help</code> to see the UNIX manual page!</p></li>
608
+ </ul>
609
+
610
+
611
+ <p>Housekeeping:</p>
612
+
613
+ <ul>
614
+ <li>Upgrade to Inochi 3.0.0 and revise the help manual.</li>
615
+ </ul>
616
+
617
+
618
+ <h3 id="Version-2-1-0-2010-03-31-">Version 2.1.0 (2010-03-31)</h3>
619
+
620
+ <p>This release adds a command-line test runner and performs some minor
621
+ housekeeping.</p>
622
+
623
+ <p>New features:</p>
624
+
625
+ <ul>
626
+ <li>Add <code>bin/dfect</code> executable as command-line interface to this library.</li>
627
+ </ul>
628
+
629
+
630
+ <p>Housekeeping:</p>
631
+
632
+ <ul>
633
+ <li><p>Do not <code>require 'rubygems'</code> before loading the "ruby-debug" library.</p></li>
634
+ <li><p>Upgrade to Inochi 2.0.0-rc2 for managing this project.</p></li>
635
+ </ul>
636
+
637
+
638
+ <h3 id="Version-2-0-0-2010-03-21-">Version 2.0.0 (2010-03-21)</h3>
639
+
640
+ <p>This release adds the ability to insulate tests from each other, share code
641
+ between them, makes the order of parameters consistent in the API, improves
642
+ user interactivity, fixes some bugs, and revises the user manual.</p>
643
+
644
+ <p>Incompatible changes:</p>
645
+
646
+ <ul>
647
+ <li><p>Root-level calls to the <code>Dfect::D()</code>
648
+ method are automatically insulated now.</p></li>
649
+ <li><p>The <code>Dfect::E()</code> methods now expects its optional message
650
+ parameter to be the <em>last parameter</em> in the parameter list.</p></li>
651
+ <li><p>The <code>Dfect::C()</code> methods now expect their first parameter to
652
+ be a symbol instead of the optional message to be shown in
653
+ case of assertion failure.</p></li>
654
+ <li><p>The <code>Dfect::R()</code> has been renamed to <code>Dfect::L()</code>,
655
+ which is a mnemonic for "Logging".</p></li>
656
+ <li><p>Shorten names of hash keys in the execution trace for brevity
657
+ and rename <code>:raise</code> key in report statistics to <code>:error</code>.</p></li>
658
+ <li><p>Only the most helpful subset of the failure details is shown before
659
+ placing the user into a debugger because they can query the omitted
660
+ information (on demand) inside the debugger.</p></li>
661
+ <li><p>The execution trace is only shown if all tests passed in <code>Dfect::run()</code>.</p></li>
662
+ <li><p>The <code>:debug</code> option is now set to Ruby's <code>$DEBUG</code> global by default.</p></li>
663
+ </ul>
664
+
665
+
666
+ <p>New features:</p>
667
+
668
+ <ul>
669
+ <li><p>Print failures as they occur instead of waiting until the end.</p></li>
670
+ <li><p>Allow passing condition as argument to true/false assertions instead
671
+ of requiring the condition to be passed as a code block, and also fall
672
+ back to the binding of inner-most enclosing test or hook when
673
+ debugging or constructing a failure report for an assertion that was
674
+ not given a block.</p>
675
+
676
+ <p>This allows you to reduce "line noise" in your tests:</p>
677
+
678
+ <pre><code>D "Lottery" do
679
+ winning_ticket = rand()
680
+
681
+ D "My chances of winning" do
682
+ my_ticket = rand()
683
+ F my_ticket == winning_ticket, "I won?! Dream on."
684
+ end
685
+ end
686
+ </code></pre></li>
687
+ <li><p>Add <code>Dfect::S()</code> methods for sharing code between tests.</p></li>
688
+ <li><p>Add <code>Dfect::D!()</code> method to explicitly insulate a test from other
689
+ tests, the top-level Ruby environment, and the code being tested.</p></li>
690
+ <li><p>Add <code>Dfect::info()</code> method which returns the details of
691
+ the failure that is currently being debugged by the user.</p></li>
692
+ <li><p>Add instance variables to the <code>:vars</code> section of a failure report.</p></li>
693
+ <li><p>Add <code>setup!()</code> and <code>teardown!()</code> methods for before-all and
694
+ after-all hooks in the dfect/unit emulation library.</p></li>
695
+ <li><p>Add test execution time to statistics hash (under the <code>:time</code> key).</p></li>
696
+ </ul>
697
+
698
+
699
+ <p>Bug fixes:</p>
700
+
701
+ <ul>
702
+ <li><p>Do not print any output when <code>:quiet</code> option is active.</p></li>
703
+ <li><p>Allow passing multiple strings/objects to <code>Dfect::D()</code> like in RSpec.</p></li>
704
+ <li><p>Make before and after hook methods mixin-able like assertions.</p></li>
705
+ <li><p>Do not assume that <code>Module#to_s</code> is the same as <code>Module#name</code>.</p></li>
706
+ </ul>
707
+
708
+
709
+ <p>Housekeeping:</p>
710
+
711
+ <ul>
712
+ <li><p>Upgrade to Inochi 2.0.0-rc1 for managing this project.</p></li>
713
+ <li><p>Make emulation libraries modify Dfect module instead of Kernel.</p></li>
714
+ <li><p>Do not pollute the user's output with our <code>Class#to_yaml</code> workaround.</p></li>
715
+ <li><p>Remove "Motivation" section from user manual. It was too fanatic!</p></li>
716
+ </ul>
717
+
718
+
719
+ <h3 id="Version-1-1-0-2009-10-27-">Version 1.1.0 (2009-10-27)</h3>
720
+
721
+ <p>This release adds a new method for emitting status messages and does some
722
+ internal housekeeping.</p>
723
+
724
+ <p>Thank you:</p>
725
+
726
+ <ul>
727
+ <li>Iñaki Baz Castillo used Dfect and suggested new features.</li>
728
+ </ul>
729
+
730
+
731
+ <p>New features:</p>
732
+
733
+ <ul>
734
+ <li>Add <code>Dfect::S()</code> method for adding status messages to the
735
+ execution report. This feature was <a href="http://github.com/sunaku/dfect/issues/1">requested
736
+ by</a> Iñaki Baz Castillo.</li>
737
+ </ul>
738
+
739
+
740
+ <p>Housekeeping:</p>
741
+
742
+ <ul>
743
+ <li><p>Remove unused require of 'delegate' standard library in 'dfect/spec'
744
+ RSpec emulation layer.</p></li>
745
+ <li><p>Mention emulation layers for popular testing libraries.</p></li>
746
+ <li><p>Mention that assertions take an optional message parameter.</p></li>
747
+ <li><p>Replace sample unit test with Dfect test suite.</p></li>
748
+ <li><p>Upgrade user manual to ERBook 9.0.0.</p></li>
749
+ </ul>
750
+
751
+
752
+ <h3 id="Version-1-0-1-2009-10-07-">Version 1.0.1 (2009-10-07)</h3>
753
+
754
+ <p>This release fixes a bug in the <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">Test::Unit</a> emulation library and revises the
755
+ user manual.</p>
756
+
757
+ <p>Bug fixes:</p>
758
+
759
+ <ul>
760
+ <li>The parameters for the <code>assert_equal()</code> method in the
761
+ dfect/unit library were in the wrong order.</li>
762
+ </ul>
763
+
764
+
765
+ <p>Housekeeping:</p>
766
+
767
+ <ul>
768
+ <li><p>Revise user manual to better fit jQuery UI tabs.</p></li>
769
+ <li><p>Justify the use of <code>eval()</code> in emulation libraries.</p></li>
770
+ <li><p>Use simpler Copyright reminder at the top of every file.</p></li>
771
+ <li><p>Make SLOC count in user manual reflect the <em>core</em> library only.</p></li>
772
+ <li><p>Mark code spans with <code>{:lang=ruby}</code> instead of HTML <code>&lt;code/&gt;</code> tags.</p></li>
773
+ <li><p>Open source is for fun, so <a href="http://loiclemeur.com/english/2009/03/never-criticize-your-competitors.html">be nice</a> and speak of "related works" instead of "competitors".</p></li>
774
+ </ul>
775
+
776
+
777
+ <h3 id="Version-1-0-0-2009-05-03-">Version 1.0.0 (2009-05-03)</h3>
778
+
779
+ <p>This release improves default choices, adds emulation layers to mimic other
780
+ testing libraries, and fixes some bugs.</p>
781
+
782
+ <p>Incompatible changes:</p>
783
+
784
+ <ul>
785
+ <li><p>The <code>:debug</code> option is now enabled by default and is no longer linked to
786
+ the value of <code>$DEBUG</code>.</p></li>
787
+ <li><p><code>Dfect.run()</code> now appends to previous results by default.</p>
788
+
789
+ <p>This behavior can be disabled by passing <code>false</code> to the method.</p></li>
790
+ </ul>
791
+
792
+
793
+ <p>New features:</p>
794
+
795
+ <ul>
796
+ <li>Add emulation layers to mimic other testing libraries:
797
+
798
+ <ul>
799
+ <li>dfect/unit --- <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">Test::Unit</a></li>
800
+ <li>dfect/mini --- <a href="http://blog.zenspider.com/minitest/">Minitest</a></li>
801
+ <li>dfect/spec --- <a href="http://rspec.info">RSpec</a></li>
802
+ </ul>
803
+ </li>
804
+ </ul>
805
+
806
+
807
+ <p>Bug fixes:</p>
808
+
809
+ <ul>
810
+ <li>Do not blindly replace <code>Class#to_yaml</code>; it might be fixed someday.</li>
811
+ </ul>
812
+
813
+
814
+ <p>Housekeeping:</p>
815
+
816
+ <ul>
817
+ <li><p>Add "Motivation" section in user manual to promote interactive
818
+ debugging.</p></li>
819
+ <li><p>Add brief History of this project's inception.</p></li>
820
+ <li><p>Remove redundant assertions for F!() and T!() methods in test suite.</p></li>
821
+ <li><p>Add copyright notice at the top of every file.</p></li>
822
+ </ul>
823
+
824
+
825
+ <h3 id="Version-0-1-0-2009-04-28-">Version 0.1.0 (2009-04-28)</h3>
826
+
827
+ <p>This release adds new variations to assertion methods, fixes several bugs,
828
+ and improves test coverage.</p>
829
+
830
+ <p>Thank you:</p>
831
+
832
+ <ul>
833
+ <li>François Beausoleil contributed patches for both code <em>and</em> tests! :-)</li>
834
+ </ul>
835
+
836
+
837
+ <p>New features:</p>
838
+
839
+ <ul>
840
+ <li><p>Added negation (m!) and sampling (m?) variations to normal assertion
841
+ methods. These new methods implement assertion functionality missing so
842
+ far (previously we could not assert that a given exception was NOT thrown)
843
+ and thereby allow us to fully test Dfect using itself.</p></li>
844
+ <li><p>Added documentation on how to insulate tests from the global Ruby
845
+ namespace.</p></li>
846
+ </ul>
847
+
848
+
849
+ <p>Bug fixes:</p>
850
+
851
+ <ul>
852
+ <li><p>The <code>E()</code> method did not consider the case where a block does not raise
853
+ anything as a failure. ---<em>François Beausoleil</em></p></li>
854
+ <li><p>When creating a report about an assertion failure, an exception would be
855
+ thrown if any local variables pointed to an empty array.</p></li>
856
+ <li><p>The <code>Dfect::&lt;()</code> method broke the inheritance-checking behavior of the &lt;
857
+ class method.</p>
858
+
859
+ <p>Added a bypass to the originial behavior so that <code>RCov::XX</code> can properly
860
+ generate a report about code that uses Dfect.</p></li>
861
+ <li><p>Added workaround for YAML error when serializing a class object:</p>
862
+
863
+ <pre><code>TypeError: can't dump anonymous class Class
864
+ </code></pre></li>
865
+ </ul>
866
+
867
+
868
+ <p>Housekeeping:</p>
869
+
870
+ <ul>
871
+ <li>Filled the big holes in test coverage. Everything except the runtime
872
+ debugging logic is now covered by the unit tests.</li>
873
+ </ul>
874
+
875
+
876
+ <h3 id="Version-0-0-0-2009-04-13-">Version 0.0.0 (2009-04-13)</h3>
877
+
878
+ <p>For the longest time, I took <a href="http://ruby-doc.org/stdlib/libdoc/test/unit/rdoc/classes/Test/Unit.html">Test::Unit</a> and <a href="http://rspec.info">RSpec</a> for granted. They were
879
+ the epitomy of modern Ruby practice; the insurmountable status quo;
880
+ immortalized in books, conferences, and blogs alike.</p>
881
+
882
+ <p>Why would <em>anyone</em> think of using anything remotely different, let alone be
883
+ foolish enough to write an alternative testing library when these are clearly
884
+ <em>good enough</em>?</p>
885
+
886
+ <p>Recent experiments in assertion testing libraries smashed my world view:</p>
887
+
888
+ <ul>
889
+ <li><a href="http://assert2.rubyforge.org">assert{ 2.0 }</a></li>
890
+ <li><a href="http://github.com/ahoward/testy/tree/master">Testy</a></li>
891
+ <li><a href="http://www.ruby-forum.com/topic/183354">Verify</a></li>
892
+ </ul>
893
+
894
+
895
+ <p>The status quo was certainly <em>not</em> "good enough", as I had so blindly
896
+ believed all these years. In fact, they were <em>verbose</em> behemoths that chose
897
+ to encode endless permutations of conjecture into methods.</p>
898
+
899
+ <p>Empowered by this revelation and inspired by <a href="http://www.ruby-forum.com/topic/183354#801895">Sean O'Halpin's musing</a> on alternative names for
900
+ assertion methods, I rose to challenge the status quo.</p>
901
+
902
+ <p>And so I present to you the first public release of "Dfect".</p>
903
+
904
+ <h2 id="AUTHORS">AUTHORS</h2>
905
+
906
+ <p>Suraj N. Kurapati</p>
907
+
908
+ <h2 id="CREDITS">CREDITS</h2>
909
+
910
+ <p>François Beausoleil,
911
+ Iñaki Baz Castillo,
912
+ Sean O'Halpin</p>
913
+
914
+ <h2 id="LICENSE">LICENSE</h2>
915
+
916
+ <p>(the ISC license)</p>
917
+
918
+ <p>Copyright 2009 Suraj N. Kurapati <a href="&#x6d;&#97;&#x69;&#x6c;&#x74;&#111;&#x3a;&#115;&#x75;&#110;&#97;&#x6b;&#117;&#64;&#103;&#x6d;&#x61;&#105;&#108;&#46;&#x63;&#111;&#x6d;" data-bare-link="true">&#115;&#x75;&#x6e;&#x61;&#107;&#x75;&#64;&#x67;&#109;&#x61;&#105;&#x6c;&#46;&#99;&#111;&#109;</a></p>
919
+
920
+ <p>Permission to use, copy, modify, and/or distribute this software for any
921
+ purpose with or without fee is hereby granted, provided that the above
922
+ copyright notice and this permission notice appear in all copies.</p>
923
+
924
+ <p>THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
925
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
926
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
927
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
928
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
929
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
930
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
931
+
932
+ <h2 id="SEE-ALSO">SEE ALSO</h2>
933
+
934
+ <p><a href="http://assert2.rubyforge.org">assert{ 2.0 }</a>,
935
+ <a href="http://github.com/ahoward/testy/tree/master">Testy</a>,
936
+ <a href="http://www.ruby-forum.com/topic/183354">Verify</a></p>
937
+
938
+
939
+ <ol class='foot man'>
940
+ <li class='tl'></li>
941
+ <li class='tc'>April 2010</li>
942
+ <li class='tr'>dfect(1)</li>
943
+ </ol>
944
+
945
+ </div>
946
+ </body>
947
+ </html>