oktest 1.0.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.
@@ -0,0 +1,547 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 1.0.0 $
5
+ ### $Copyright: copyright(c) 2011-2021 kuwata-lab.com all rights reserved $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ require_relative './initialize'
10
+
11
+
12
+ module ReporterTestHelper
13
+
14
+ def plain2colored(str)
15
+ str = str.gsub(/<R>(.*?)<\/R>/) { Oktest::Color.red($1) }
16
+ str = str.gsub(/<G>(.*?)<\/G>/) { Oktest::Color.green($1) }
17
+ str = str.gsub(/<B>(.*?)<\/B>/) { Oktest::Color.blue($1) }
18
+ str = str.gsub(/<Y>(.*?)<\/Y>/) { Oktest::Color.yellow($1) }
19
+ str = str.gsub(/<b>(.*?)<\/b>/) { Oktest::Color.bold($1) }
20
+ return str
21
+ end
22
+
23
+ def edit_actual(output)
24
+ bkup = output.dup
25
+ output = output.gsub(/^.*\r/, '')
26
+ output = output.gsub(/^ .*(_test\.tmp:\d+)/, ' \1')
27
+ output = output.gsub(/^ .*test.reporter_test\.rb:.*\n( .*\n)*/, "%%%\n")
28
+ output = output.sub(/ in \d+\.\d\d\ds/, ' in 0.000s')
29
+ return output
30
+ end
31
+
32
+ def edit_expected(expected)
33
+ expected = expected.gsub(/^ (.*:\d+)(:in `block .*)/, ' \1') if RUBY_VERSION < "1.9"
34
+ expected = plain2colored(expected)
35
+ return expected
36
+ end
37
+
38
+ end
39
+
40
+
41
+ class BaseReporter_TC < TC
42
+ include ReporterTestHelper
43
+
44
+ def new_topic_and_spec()
45
+ sc = Oktest::ScopeNode.new(nil, "foo.rb")
46
+ t1 = Oktest::TopicNode.new(sc, 'Example')
47
+ t2 = Oktest::TopicNode.new(t1, Array)
48
+ t3 = Oktest::TopicNode.new(t2, 'When some condition')
49
+ spec = Oktest::SpecLeaf.new(t3, "1+1 shoould be 2.") { nil }
50
+ return t3, spec
51
+ end
52
+
53
+ describe '#enter_all()' do
54
+ it "[!pq3ia] initalizes counter by zero." do
55
+ r = Oktest::BaseReporter.new
56
+ c = r.instance_eval { @counts }
57
+ assert_eq c, {}
58
+ #
59
+ r.enter_all(nil)
60
+ c = r.instance_eval { @counts }
61
+ assert_eq c, {:PASS=>0, :FAIL=>0, :ERROR=>0, :SKIP=>0, :TODO=>0}
62
+ end
63
+ end
64
+
65
+ describe '#exit_all()' do
66
+ it "[!wjp7u] prints footer with elapsed time." do
67
+ r = Oktest::BaseReporter.new
68
+ r.enter_all(nil)
69
+ r.instance_eval { @start_at = Time.now - 7.0 }
70
+ sout, serr = capture do
71
+ r.exit_all(nil)
72
+ end
73
+ assert_eq sout, "## total:0 (pass:0, fail:0, error:0, skip:0, todo:0) in 7.00s\n"
74
+ assert_eq serr, ""
75
+ end
76
+ end
77
+
78
+ describe '#exit_spec()' do
79
+ it "[!r6yge] increments counter according to status." do
80
+ begin; 1/0
81
+ rescue => exc
82
+ end
83
+ r = Oktest::BaseReporter.new
84
+ r.enter_all(nil)
85
+ topic1, spec1 = new_topic_and_spec()
86
+ #
87
+ r.exit_spec(spec1, 1, :PASS, nil, topic1)
88
+ assert_eq r.counts, {:PASS=>1, :FAIL=>0, :ERROR=>0, :SKIP=>0, :TODO=>0}
89
+ #
90
+ r.exit_spec(spec1, 1, :FAIL, exc, topic1)
91
+ assert_eq r.counts, {:PASS=>1, :FAIL=>1, :ERROR=>0, :SKIP=>0, :TODO=>0}
92
+ #
93
+ r.exit_spec(spec1, 1, :ERROR, exc, topic1)
94
+ assert_eq r.counts, {:PASS=>1, :FAIL=>1, :ERROR=>1, :SKIP=>0, :TODO=>0}
95
+ #
96
+ r.exit_spec(spec1, 1, :SKIP, nil, topic1)
97
+ assert_eq r.counts, {:PASS=>1, :FAIL=>1, :ERROR=>1, :SKIP=>1, :TODO=>0}
98
+ #
99
+ r.exit_spec(spec1, 1, :TODO, nil, topic1)
100
+ assert_eq r.counts, {:PASS=>1, :FAIL=>1, :ERROR=>1, :SKIP=>1, :TODO=>1}
101
+ end
102
+ it "[!nupb4] keeps exception info when status is FAIL or ERROR." do
103
+ begin; 1/0
104
+ rescue => exc
105
+ end
106
+ r = Oktest::BaseReporter.new
107
+ r.enter_all(nil)
108
+ topic1, spec1 = new_topic_and_spec()
109
+ counts = r.instance_variable_get('@counts')
110
+ #
111
+ r.exit_spec(spec1, 1, :PASS, nil, topic1)
112
+ r.exit_spec(spec1, 1, :FAIL, exc, topic1)
113
+ r.exit_spec(spec1, 1, :ERROR, exc, topic1)
114
+ r.exit_spec(spec1, 1, :SKIP, nil, topic1)
115
+ r.exit_spec(spec1, 1, :TODO, nil, topic1)
116
+ #
117
+ exceptions = r.instance_variable_get('@exceptions')
118
+ assert_eq exceptions.length, 2
119
+ assert_eq exceptions[0][1], :FAIL
120
+ assert_eq exceptions[1][1], :ERROR
121
+ end
122
+ end
123
+
124
+ describe '#reset_counts()' do
125
+ it "[!oc29s] clears counters to zero." do
126
+ r = Oktest::BaseReporter.new
127
+ r.instance_eval do
128
+ @counts = {:PASS=>5, :FAIL=>4, :ERROR=>3, :SKIP=>2, :TODO=>1}
129
+ end
130
+ r.__send__(:reset_counts)
131
+ assert_eq r.instance_variable_get('@counts'), {:PASS=>0, :FAIL=>0, :ERROR=>0, :SKIP=>0, :TODO=>0}
132
+ end
133
+ end
134
+
135
+ describe '#print_exc_message()' do
136
+ def error_msg()
137
+ return ("something failed\n"\
138
+ " expect: foo\n"\
139
+ " actual: bar\n")
140
+ end
141
+ it "[!hr7jn] prints detail of assertion failed." do
142
+ errmsg = error_msg()
143
+ exc = Oktest::AssertionFailed.new(errmsg)
144
+ r = Oktest::BaseReporter.new
145
+ sout, serr = capture do
146
+ r.__send__(:print_exc_message, exc, :FAIL)
147
+ end
148
+ assert_eq sout, errmsg
149
+ assert_eq serr, ""
150
+ end
151
+ it "[!pd41p] prints detail of exception." do
152
+ errmsg = error_msg()
153
+ exc = Oktest::AssertionFailed.new(errmsg)
154
+ r = Oktest::BaseReporter.new
155
+ sout, serr = capture do
156
+ r.__send__(:print_exc_message, exc, :ERROR)
157
+ end
158
+ assert_eq sout, "Oktest::AssertionFailed: "+errmsg
159
+ assert_eq serr, ""
160
+ end
161
+ end
162
+
163
+ describe '#print_exc_backtrace()' do
164
+ it "[!ocxy6] prints backtrace info and lines in file." do
165
+ begin
166
+ if true
167
+ if true
168
+ lineno = __LINE__ + 1
169
+ raise Oktest::AssertionFailed, "something failed."
170
+ end
171
+ end
172
+ rescue Oktest::AssertionFailed => exc
173
+ end
174
+ #
175
+ expected = <<END
176
+ test/reporter_test.rb:#{lineno}:in `block (2 levels) in <class:BaseReporter_TC>'
177
+ raise Oktest::AssertionFailed, "something failed."
178
+ END
179
+ #
180
+ r = Oktest::BaseReporter.new
181
+ sout, serr = capture do
182
+ r.__send__(:print_exc_backtrace, exc, :FAIL)
183
+ end
184
+ assert sout.start_with?(expected), "traceback not matched"
185
+ assert_eq serr, ""
186
+ end
187
+ it "[!jbped] skips backtrace of oktest.rb when assertion failure." do
188
+ begin
189
+ eval "raise Oktest::AssertionFailed, 'dummie'", binding(), "lib/oktest.rb", 100
190
+ rescue Oktest::AssertionFailed => exc
191
+ end
192
+ #
193
+ status = :FAIL
194
+ sout, serr = capture do
195
+ Oktest::BaseReporter.new.__send__(:print_exc_backtrace, exc, status)
196
+ end
197
+ assert sout !~ /lib\/oktest\.rb:/, "should skip but not"
198
+ assert_eq serr, ""
199
+ end
200
+ it "[!cfkzg] don't skip first backtrace entry when error." do
201
+ begin
202
+ eval "raise Oktest::AssertionFailed, 'dummie'", binding(), "lib/oktest.rb", 100
203
+ rescue Oktest::AssertionFailed => exc
204
+ end
205
+ #
206
+ status = :ERROR
207
+ sout, serr = capture do
208
+ Oktest::BaseReporter.new.__send__(:print_exc_backtrace, exc, status)
209
+ end
210
+ assert sout =~ /lib\/oktest\.rb:100/, "should not skip but does"
211
+ assert_eq serr, ""
212
+ end
213
+ end
214
+
215
+ describe '#print_exc()' do
216
+ it "[!5ara3] prints exception info of assertion failure." do
217
+ begin
218
+ if true
219
+ lineno = __LINE__ + 1
220
+ raise Oktest::AssertionFailed, 'dummie:43201'
221
+ end
222
+ rescue Oktest::AssertionFailed => exc
223
+ end
224
+ #
225
+ expected = <<END
226
+ [<R>Fail</R>] <b>Example > Array > When some condition > 1+1 shoould be 2.</b>
227
+ #{__FILE__}:#{lineno}:in `block (2 levels) in <class:BaseReporter_TC>'
228
+ raise Oktest::AssertionFailed, 'dummie:43201'
229
+ END
230
+ #
231
+ topic1, spec1 = new_topic_and_spec()
232
+ status = :FAIL
233
+ sout, serr = capture do
234
+ Oktest::BaseReporter.new.__send__(:print_exc, spec1, status, exc, topic1)
235
+ end
236
+ assert sout.start_with?(plain2colored(expected)), "not matched"
237
+ assert_eq serr, ""
238
+ end
239
+ it "[!pcpy4] prints exception info of error." do
240
+ begin
241
+ if true
242
+ lineno = __LINE__ + 1
243
+ 1/0
244
+ end
245
+ rescue ZeroDivisionError => exc
246
+ end
247
+ #
248
+ expected = <<END
249
+ [<R>ERROR</R>] <b>Example > Array > When some condition > 1+1 shoould be 2.</b>
250
+ #{__FILE__}:#{lineno}:in `/'
251
+ 1/0
252
+ END
253
+ #
254
+ topic1, spec1 = new_topic_and_spec()
255
+ status = :ERROR
256
+ sout, serr = capture do
257
+ Oktest::BaseReporter.new.__send__(:print_exc, spec1, status, exc, topic1)
258
+ end
259
+ assert sout.start_with?(plain2colored(expected)), "not matched"
260
+ assert_eq serr, ""
261
+ end
262
+ end
263
+
264
+ describe '#print_exception()' do
265
+ def new_reporter_with_exceptions(exc)
266
+ topic1, spec1 = new_topic_and_spec()
267
+ r = Oktest::BaseReporter.new
268
+ r.instance_eval do
269
+ if exc
270
+ @exceptions = [
271
+ [spec1, :FAIL, exc, topic1],
272
+ [spec1, :ERROR, exc, topic1],
273
+ ]
274
+ end
275
+ end
276
+ return r
277
+ end
278
+ it "[!fbr16] prints assertion failures and excerptions with separator." do
279
+ begin
280
+ raise Oktest::AssertionFailed, 'dummie'
281
+ rescue Oktest::AssertionFailed => exc
282
+ end
283
+ r = new_reporter_with_exceptions(exc)
284
+ sep = "----------------------------------------------------------------------\n"
285
+ expected1 = "[<R>Fail</R>] <b>Example > Array > When some condition > 1+1 shoould be 2.</b>\n"
286
+ expected2 = "[<R>ERROR</R>] <b>Example > Array > When some condition > 1+1 shoould be 2.</b>\n"
287
+ #
288
+ sout, serr = capture { r.__send__(:print_exceptions) }
289
+ assert sout.start_with?(sep + plain2colored(expected1)), "not matched"
290
+ assert sout.include?(sep + plain2colored(expected2)), "not matched"
291
+ assert sout.end_with?(sep), "not matched"
292
+ assert_eq serr, ""
293
+ end
294
+ it "[!2s9r2] prints nothing when no fails nor errors." do
295
+ r = new_reporter_with_exceptions(nil)
296
+ sout, serr = capture { r.__send__(:print_exceptions) }
297
+ assert_eq sout, ""
298
+ assert_eq serr, ""
299
+ end
300
+ it "[!ueeih] clears exceptions." do
301
+ begin 1/0
302
+ rescue => exc
303
+ end
304
+ r = new_reporter_with_exceptions(exc)
305
+ assert ! r.instance_variable_get('@exceptions').empty?
306
+ sout, serr = capture { r.__send__(:print_exceptions) }
307
+ assert r.instance_variable_get('@exceptions').empty?
308
+ end
309
+ end
310
+
311
+ describe '#footer()' do
312
+ def new_footer(elapsed=0.5)
313
+ r = Oktest::BaseReporter.new
314
+ r.enter_all(nil)
315
+ r.instance_eval do
316
+ @counts = {:PASS=>5, :FAIL=>4, :ERROR=>3, :SKIP=>2, :TODO=>1}
317
+ end
318
+ return r.__send__(:footer, elapsed)
319
+ end
320
+
321
+ it "[!iy4uo] calculates total count of specs." do
322
+ ft = new_footer()
323
+ assert ft =~ /total:15 /, "failed to calculate total counts."
324
+ end
325
+
326
+ it "[!2nnma] includes count of each status." do
327
+ ft = new_footer()
328
+ assert ft =~ /pass:5\b/ , "failed to count passed status."
329
+ assert ft =~ /fail:4\b/ , "failed to count failed status."
330
+ assert ft =~ /error:3\b/ , "failed to count error status."
331
+ assert ft =~ /skip:2\b/ , "failed to count skipped status."
332
+ assert ft =~ /todo:1\b/ , "failed to count todo status."
333
+ end
334
+
335
+ it "[!fp57l] includes elapsed time." do
336
+ ft = new_footer()
337
+ assert ft =~ / in 0.500s$/, "failed to embed elapsed time."
338
+ end
339
+
340
+ it "[!r5y02] elapsed time format is adjusted along to time length." do
341
+ assert new_footer( 0.5) =~ / in 0.500s$/ , "failed to embed elapsed time."
342
+ assert new_footer( 6.5) =~ / in 6.50s$/ , "failed to embed elapsed time."
343
+ assert new_footer( 17.5) =~ / in 17.5s$/ , "failed to embed elapsed time."
344
+ assert new_footer( 61.5) =~ / in 1:01.5s$/ , "failed to embed elapsed time."
345
+ assert new_footer( 610.5) =~ / in 10:10.5s$/ , "failed to embed elapsed time."
346
+ assert new_footer( 3600.5) =~ / in 1:00:00.5s$/ , "failed to embed elapsed time."
347
+ assert new_footer( 36000.5) =~ / in 10:00:00.5s$/ , "failed to embed elapsed time."
348
+ assert new_footer(360000.5) =~ / in 100:00:00.5s$/ , "failed to embed elapsed time."
349
+ end
350
+
351
+ it "[!gx0n2] builds footer line." do
352
+ expected = "## total:15 (<B>pass:5</B>, <R>fail:4</R>, <R>error:3</R>, <Y>skip:2</Y>, <Y>todo:1</Y>) in 0.500s"
353
+ assert new_footer(), plain2colored(expected)
354
+ end
355
+ end
356
+
357
+ describe '#spec_path()' do
358
+ it "[!dv6fu] returns path string from top topic to current spec." do
359
+ sc = Oktest::ScopeNode.new(nil, "foo.rb")
360
+ t1 = Oktest::TopicNode.new(sc, 'Example')
361
+ t2 = Oktest::TopicNode.new(t1, Array)
362
+ t3 = Oktest::TopicNode.new(t2, 'When some condition')
363
+ s1 = Oktest::SpecLeaf.new(t3, "1+1 shoould be 2.") { nil }
364
+ path = Oktest::BaseReporter.new.__send__(:spec_path, s1, t3)
365
+ assert_eq path, "Example > Array > When some condition > 1+1 shoould be 2."
366
+ end
367
+ end
368
+
369
+ end
370
+
371
+
372
+ class Reporter_TC < TC
373
+ include ReporterTestHelper
374
+
375
+ INPUT = <<'END'
376
+ require 'oktest'
377
+
378
+ Oktest.scope do
379
+
380
+ topic "Parent" do
381
+
382
+ topic "Child1" do
383
+ spec "1+1 should be 2" do
384
+ ok {1+1} == 2
385
+ end
386
+ spec "1-1 should be 0" do
387
+ ok {1-1} == 0
388
+ end
389
+ end
390
+
391
+ topic "Child2" do
392
+ spec "1*1 should be 1" do
393
+ ok {1*1} == 2
394
+ end
395
+ spec "1/1 should be 1" do
396
+ ok {1/0} == 1
397
+ end
398
+ end
399
+
400
+ topic "Child3" do
401
+ spec "skip example" do
402
+ skip_when true, "a certain condition"
403
+ end
404
+ spec "todo example"
405
+ end
406
+
407
+ case_when "x is negative" do
408
+ spec "x*x is positive." do
409
+ x = -2
410
+ ok {x*x} > 0
411
+ end
412
+ end
413
+
414
+ case_else do
415
+ spec "x*x is also positive." do
416
+ x = 2
417
+ ok {x*x} > 0
418
+ end
419
+ end
420
+
421
+ end
422
+
423
+ end
424
+
425
+ END
426
+
427
+ ERROR_PART = <<'END'
428
+ ----------------------------------------------------------------------
429
+ [<R>Fail</R>] <b>Parent > Child2 > 1*1 should be 1</b>
430
+ _test.tmp:18:in `block (4 levels) in <top (required)>'
431
+ ok {1*1} == 2
432
+ %%%
433
+ $<actual> == $<expected>: failed.
434
+ $<actual>: 1
435
+ $<expected>: 2
436
+ ----------------------------------------------------------------------
437
+ [<R>ERROR</R>] <b>Parent > Child2 > 1/1 should be 1</b>
438
+ _test.tmp:21:in `/'
439
+ ok {1/0} == 1
440
+ %%%
441
+ ZeroDivisionError: divided by 0
442
+ ----------------------------------------------------------------------
443
+ END
444
+
445
+ FOOTER = <<'END'
446
+ ## total:8 (<B>pass:4</B>, <R>fail:1</R>, <R>error:1</R>, <Y>skip:1</Y>, <Y>todo:1</Y>) in 0.000s
447
+ END
448
+
449
+ VERBOSE_PART = <<'END'
450
+ * <b>Parent</b>
451
+ * <b>Child1</b>
452
+ - [<B>pass</B>] 1+1 should be 2
453
+ - [<B>pass</B>] 1-1 should be 0
454
+ * <b>Child2</b>
455
+ - [<R>Fail</R>] 1*1 should be 1
456
+ - [<R>ERROR</R>] 1/1 should be 1
457
+ END
458
+ VERBOSE_PART2 = <<'END'
459
+ * <b>Child3</b>
460
+ - [<Y>Skip</Y>] skip example <Y>(reason: a certain condition)</Y>
461
+ - [<Y>TODO</Y>] todo example
462
+ - <b>When x is negative</b>
463
+ - [<B>pass</B>] x*x is positive.
464
+ - <b>Else</b>
465
+ - [<B>pass</B>] x*x is also positive.
466
+ END
467
+ VERBOSE_OUTPUT = VERBOSE_PART + ERROR_PART + VERBOSE_PART2 + FOOTER
468
+
469
+ SIMPLE_PART = <<'END'
470
+ _test.tmp: <B>.</B><B>.</B><R>f</R><R>E</R><Y>s</Y><Y>t</Y><B>.</B><B>.</B>
471
+ END
472
+ SIMPLE_OUTPUT = SIMPLE_PART + ERROR_PART + FOOTER
473
+
474
+ PLAIN_PART = <<'END'
475
+ <B>.</B><B>.</B><R>f</R><R>E</R><Y>s</Y><Y>t</Y><B>.</B><B>.</B>
476
+ END
477
+ PLAIN_OUTPUT = PLAIN_PART + ERROR_PART + FOOTER
478
+
479
+ QUIET_PART = <<'END'
480
+ <R>f</R><R>E</R><Y>s</Y><Y>t</Y>
481
+ END
482
+ QUIET_OUTPUT = QUIET_PART + ERROR_PART + FOOTER
483
+
484
+ def default_test
485
+ end
486
+
487
+ def setup
488
+ @filename = "_test.tmp"
489
+ File.write(@filename, INPUT)
490
+ @_color_enabled = Oktest::Config.color_enabled
491
+ Oktest::Config.color_enabled = true
492
+ end
493
+
494
+ def teardown
495
+ Oktest::Config.color_enabled = @_color_enabled
496
+ File.unlink(@filename) if @filename && File.exist?(@filename)
497
+ end
498
+
499
+ def run(*opts)
500
+ return capture { Oktest::MainApp.main(opts) }
501
+ end
502
+
503
+ end
504
+
505
+
506
+ class VerboseReporter_TC < Reporter_TC
507
+
508
+ it "[!6o9nw] reports topic name and spec desc." do
509
+ sout, serr = run("-sv", @filename)
510
+ assert_eq edit_actual(sout), edit_expected(VERBOSE_OUTPUT)
511
+ assert_eq serr, ""
512
+ end
513
+
514
+ end
515
+
516
+
517
+ class SimpleReporter_TC < Reporter_TC
518
+
519
+ it "[!xfd5o] reports filename." do
520
+ sout, serr = run("-ss", @filename)
521
+ assert_eq edit_actual(sout), edit_expected(SIMPLE_OUTPUT)
522
+ assert_eq serr, ""
523
+ end
524
+
525
+ end
526
+
527
+
528
+ class PlainReporter_TC < Reporter_TC
529
+
530
+ it "[!w842j] reports progress." do
531
+ sout, serr = run("-sp", @filename)
532
+ assert_eq edit_actual(sout), edit_expected(PLAIN_OUTPUT)
533
+ assert_eq serr, ""
534
+ end
535
+
536
+ end
537
+
538
+
539
+ class QuietReporter_TC < Reporter_TC
540
+
541
+ it "[!0z4im] reports all statuses except PASS status." do
542
+ sout, serr = run("-sq", @filename)
543
+ assert_eq edit_actual(sout), edit_expected(QUIET_OUTPUT)
544
+ assert_eq serr, ""
545
+ end
546
+
547
+ end