oktest 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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