oktest 1.0.0 → 1.1.1
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.
- checksums.yaml +4 -4
- data/README.md +287 -51
- data/Rakefile.rb +1 -1
- data/lib/oktest.rb +393 -19
- data/oktest.gemspec +2 -2
- data/test/assertion_test.rb +70 -5
- data/test/filter_test.rb +2 -2
- data/test/fixture_test.rb +1 -1
- data/test/generator_test.rb +1 -1
- data/test/helper_test.rb +1 -1
- data/test/initialize.rb +8 -1
- data/test/mainapp_test.rb +42 -5
- data/test/matcher_test.rb +424 -0
- data/test/misc_test.rb +1 -1
- data/test/node_test.rb +34 -1
- data/test/reporter_test.rb +34 -6
- data/test/runner_test.rb +90 -9
- data/test/util_test.rb +1 -1
- data/test/visitor_test.rb +1 -1
- metadata +3 -2
data/Rakefile.rb
CHANGED
data/lib/oktest.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.
|
4
|
+
### $Release: 1.1.1 $
|
5
5
|
### $Copyright: copyright(c) 2011-2021 kuwata-lab.com all rights reserved $
|
6
6
|
### $License: MIT License $
|
7
7
|
###
|
8
8
|
|
9
|
+
require 'set'
|
10
|
+
|
9
11
|
|
10
12
|
module Oktest
|
11
13
|
|
12
14
|
|
13
|
-
VERSION = '$Release: 1.
|
15
|
+
VERSION = '$Release: 1.1.1 $'.split()[1]
|
14
16
|
|
15
17
|
|
16
18
|
class OktestError < StandardError
|
@@ -114,6 +116,10 @@ module Oktest
|
|
114
116
|
|
115
117
|
def ===(expected)
|
116
118
|
__done()
|
119
|
+
#; [!mjh4d] raises error when combination of 'not_ok()' and matcher object.
|
120
|
+
if @bool == false && @actual.is_a?(Matcher)
|
121
|
+
raise OktestError, "negative `===` is not available with matcher object."
|
122
|
+
end
|
117
123
|
#; [!42f6a] raises assertion error when failed.
|
118
124
|
#; [!vhvyu] is avaialbe with NOT.
|
119
125
|
__assert(@bool == (@actual === expected)) {
|
@@ -264,10 +270,10 @@ module Oktest
|
|
264
270
|
|
265
271
|
def raise!(errcls=nil, errmsg=nil, &b)
|
266
272
|
#; [!8k6ee] compares error class by '.is_a?' instead of '=='.
|
267
|
-
return raise?(errcls, errmsg,
|
273
|
+
return raise?(errcls, errmsg, _subclass: true, &b)
|
268
274
|
end
|
269
275
|
|
270
|
-
def raise?(errcls=nil, errmsg=nil,
|
276
|
+
def raise?(errcls=nil, errmsg=nil, _subclass: false, &b)
|
271
277
|
__done()
|
272
278
|
#; [!2rnni] 1st argument can be error message string or rexp.
|
273
279
|
if errmsg.nil? && ! errcls.nil? && ! (errcls.is_a?(Class) && errcls <= Exception)
|
@@ -289,8 +295,8 @@ module Oktest
|
|
289
295
|
#; [!lq6jv] compares error class with '==' operator, not '.is_a?'.
|
290
296
|
elsif exc.class == errcls # not `exc.is_a?(errcls)`
|
291
297
|
nil
|
292
|
-
#; [!hwg0z] compares error class with '.is_a?' if '
|
293
|
-
elsif
|
298
|
+
#; [!hwg0z] compares error class with '.is_a?' if '_subclass: true' specified.
|
299
|
+
elsif _subclass && exc.class < errcls
|
294
300
|
nil
|
295
301
|
#; [!4n3ed] reraises if exception is not matched to specified error class.
|
296
302
|
else
|
@@ -328,8 +334,8 @@ module Oktest
|
|
328
334
|
#; [!smprc] compares error class with '==' operator, not '.is_a?'.
|
329
335
|
elsif exc.class == errcls # not `exc.is_a?(errcls)`
|
330
336
|
__assert(false) { "#{errcls.inspect} should not be raised but got #{exc.inspect}." }
|
331
|
-
#; [!34nd8] compares error class with '.is_a?' if '
|
332
|
-
elsif
|
337
|
+
#; [!34nd8] compares error class with '.is_a?' if '_subclass: true' specified.
|
338
|
+
elsif _subclass && exc.class < errcls
|
333
339
|
__assert(false) { "#{errcls.inspect} should not be raised but got #{exc.inspect}." }
|
334
340
|
#; [!shxne] reraises exception if different from specified error class.
|
335
341
|
else
|
@@ -548,6 +554,246 @@ module Oktest
|
|
548
554
|
end
|
549
555
|
|
550
556
|
|
557
|
+
class Matcher
|
558
|
+
|
559
|
+
def initialize(actual)
|
560
|
+
@actual = actual
|
561
|
+
end
|
562
|
+
|
563
|
+
def ===(expected)
|
564
|
+
#; [!spybn] raises NotImplementedError.
|
565
|
+
raise NotImplementedError.new("#{self.class.name}#===(): not implemented yet.")
|
566
|
+
end
|
567
|
+
|
568
|
+
def ==(expected)
|
569
|
+
#; [!ymt1b] raises OktestError.
|
570
|
+
raise OktestError, "JSON(): use `===` instead of `==`."
|
571
|
+
end
|
572
|
+
|
573
|
+
def fail(errmsg)
|
574
|
+
#; [!8qpsd] raises assertion error.
|
575
|
+
raise Oktest::FAIL_EXCEPTION, errmsg
|
576
|
+
end
|
577
|
+
|
578
|
+
end
|
579
|
+
|
580
|
+
|
581
|
+
class JsonMatcher < Matcher
|
582
|
+
|
583
|
+
def ===(expected)
|
584
|
+
#; [!4uf1o] raises assertion error when JSON not matched.
|
585
|
+
_compare([], @actual, expected)
|
586
|
+
#; [!0g0u4] returns true when JSON matched.
|
587
|
+
return true
|
588
|
+
end
|
589
|
+
|
590
|
+
private
|
591
|
+
|
592
|
+
def _compare?(path, a, e)
|
593
|
+
#; [!nkvqo] returns true when nothing raised.
|
594
|
+
#; [!57m2j] returns false when assertion error raised.
|
595
|
+
_compare(path, a, e)
|
596
|
+
return true
|
597
|
+
rescue FAIL_EXCEPTION
|
598
|
+
return false
|
599
|
+
end
|
600
|
+
|
601
|
+
def _compare(path, a, e)
|
602
|
+
if a.is_a?(Hash) && e.is_a?(Hash)
|
603
|
+
_compare_hash(path, a, e)
|
604
|
+
elsif a.is_a?(Array) && e.is_a?(Array)
|
605
|
+
_compare_array(path, a, e)
|
606
|
+
elsif e.is_a?(Enumerator)
|
607
|
+
_compare_enumerator(path, a, e)
|
608
|
+
elsif e.is_a?(OR)
|
609
|
+
_compare_or(path, a, e)
|
610
|
+
elsif e.is_a?(AND)
|
611
|
+
_compare_and(path, a, e)
|
612
|
+
else
|
613
|
+
_compare_value(path, a, e)
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def _compare_value(path, a, e)
|
618
|
+
#; [!1ukbv] scalar value matches to integer, string, bool, and so son.
|
619
|
+
#; [!8o55d] class object matches to instance object.
|
620
|
+
#; [!s625d] regexp object matches to string value.
|
621
|
+
#; [!aqkk0] range object matches to scalar value.
|
622
|
+
#; [!a7bfs] Set object matches to enum value.
|
623
|
+
e === a or fail <<"END"
|
624
|
+
$<JSON>#{_path(path)}: $<expected> === $<actual> : failed.
|
625
|
+
$<actual>: #{a.inspect}
|
626
|
+
$<expected>: #{e.inspect}
|
627
|
+
END
|
628
|
+
#; [!4ymj2] fails when actual value is not matched to item class of range object.
|
629
|
+
if e.is_a?(Range)
|
630
|
+
expected_class = (e.begin || e.end).class
|
631
|
+
a.is_a?(expected_class) or fail <<"END"
|
632
|
+
$<JSON>#{_path(path)}: expected #{expected_class.name} value, but got #{a.class.name} value.
|
633
|
+
$<actual>: #{a.inspect}
|
634
|
+
$<expected>: #{e.inspect}
|
635
|
+
END
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
def _compare_array(path, a, e)
|
640
|
+
#; [!bz74w] fails when array lengths are different.
|
641
|
+
a.length == e.length or fail <<"END"
|
642
|
+
$<JSON>#{_path(path)}: $<actual>.length == $<expected>.length : failed.
|
643
|
+
$<actual>.length: #{a.length}
|
644
|
+
$<expected>.length: #{e.length}
|
645
|
+
$<actual>: #{a.inspect}
|
646
|
+
$<expected>: #{e.inspect}
|
647
|
+
END
|
648
|
+
#; [!lh6d6] compares array items recursively.
|
649
|
+
path.push(nil)
|
650
|
+
i = -1
|
651
|
+
a.zip(e) do |a2, e2|
|
652
|
+
path[-1] = (i += 1)
|
653
|
+
_compare(path, a2, e2)
|
654
|
+
end
|
655
|
+
path.pop()
|
656
|
+
end
|
657
|
+
|
658
|
+
def _compare_hash(path, a, e)
|
659
|
+
#; [!rkv0z] compares two hashes with converting keys into string.
|
660
|
+
a2 = {}; a.each {|k, v| a2[k.to_s] = v }
|
661
|
+
e2 = {}; e.each {|k, v| e2[k.to_s] = v }
|
662
|
+
#; [!fmxyg] compares hash objects recursively.
|
663
|
+
path.push(nil)
|
664
|
+
a2.each_key do |k|
|
665
|
+
path[-1] = k
|
666
|
+
if e2.key?(k)
|
667
|
+
_compare(path, a2[k], e2[k])
|
668
|
+
#; [!jbyv6] key 'aaa?' represents optional key.
|
669
|
+
elsif e2.key?("#{k}?")
|
670
|
+
_compare(path, a2[k], e2["#{k}?"]) unless a2[k].nil?
|
671
|
+
#; [!uc4ag] key '*' matches to any key name.
|
672
|
+
elsif e2.key?("*")
|
673
|
+
_compare(path, a2[k], e2["*"])
|
674
|
+
#; [!mpbvu] fails when unexpected key exists in actual hash.
|
675
|
+
else
|
676
|
+
fail <<"END"
|
677
|
+
$<JSON>#{_path(path)}: unexpected key.
|
678
|
+
$<actual>: #{a2[k].inspect}
|
679
|
+
END
|
680
|
+
end
|
681
|
+
end
|
682
|
+
path.pop()
|
683
|
+
#; [!4oasq] fails when expected key not exist in actual hash.
|
684
|
+
(e2.keys - a2.keys).each do |k|
|
685
|
+
k =~ /\?\z/ || k == "*" or fail <<"END"
|
686
|
+
$<JSON>#{_path(path)}: key \"#{k}\" expected but not found.
|
687
|
+
$<actual>.keys: #{a2.keys.sort.inspect[1...-1]}
|
688
|
+
$<expected>.keys: #{e2.keys.sort.inspect[1...-1]}
|
689
|
+
END
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
def _compare_enumerator(path, a, e)
|
694
|
+
#; [!ljrmc] fails when expected is an Enumerator object and actual is not an array.
|
695
|
+
e2 = e.first
|
696
|
+
a.is_a?(Array) or fail <<"END"
|
697
|
+
$<JSON>#{_path(path)}: Array value expected but got #{a.class.name} value.
|
698
|
+
$<actual>: #{a.inspect}
|
699
|
+
$<expected>: [#{e2.inspect}].each
|
700
|
+
END
|
701
|
+
#; [!sh5cg] Enumerator object matches to repeat of rule.
|
702
|
+
path.push(nil)
|
703
|
+
a.each_with_index do |a2, i|
|
704
|
+
path[-1] = i
|
705
|
+
_compare(path, a2, e2)
|
706
|
+
end
|
707
|
+
path.pop()
|
708
|
+
end
|
709
|
+
|
710
|
+
def _compare_or(path, a, e)
|
711
|
+
#; [!eqr3b] `OR()` matches to any of arguments.
|
712
|
+
#; [!5ybfg] `OR()` can contain `AND()`.
|
713
|
+
passed = e.items.any? {|e2| _compare?(path, a, e2) }
|
714
|
+
passed or fail <<"END"
|
715
|
+
$<JSON>#{_path(path)}: $<expected> === $<actual> : failed.
|
716
|
+
$<actual>: #{a.inspect}
|
717
|
+
$<expected>: OR(#{e.items.collect(&:inspect).join(', ')})
|
718
|
+
END
|
719
|
+
end
|
720
|
+
|
721
|
+
def _compare_and(path, a, e)
|
722
|
+
#; [!4hk96] `AND()` matches to all of arguments.
|
723
|
+
#; [!scx22] `AND()` can contain `OR()`.
|
724
|
+
failed = e.items.find {|e2| ! _compare?(path, a, e2) }
|
725
|
+
! failed or fail <<"END"
|
726
|
+
$<JSON>#{_path(path)}: $<expected> === $<actual> : failed.
|
727
|
+
$<actual>: #{a.inspect}
|
728
|
+
$<expected>: AND(#{failed.inspect})
|
729
|
+
END
|
730
|
+
end
|
731
|
+
|
732
|
+
def _path(path)
|
733
|
+
#return path.collect {|x| "/#{x}" }.join()
|
734
|
+
return path.collect {|x| "[#{x.inspect}]" }.join()
|
735
|
+
end
|
736
|
+
|
737
|
+
protected
|
738
|
+
|
739
|
+
class OR
|
740
|
+
def initialize(*items)
|
741
|
+
@items = items
|
742
|
+
end
|
743
|
+
attr_reader :items
|
744
|
+
def inspect()
|
745
|
+
#; [!2mu33] returns 'OR(...)' string.
|
746
|
+
return "OR(#{@items.collect(&:inspect).join(', ')})"
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
750
|
+
class AND
|
751
|
+
def initialize(*items)
|
752
|
+
@items = items
|
753
|
+
end
|
754
|
+
attr_reader :items
|
755
|
+
def inspect()
|
756
|
+
#; [!w43ag] returns 'AND(...)' string.
|
757
|
+
return "AND(#{@items.collect(&:inspect).join(', ')})"
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
class Enum < Set
|
762
|
+
alias === include? # Ruby 2.4 or older doesn't have 'Set#==='.
|
763
|
+
def inspect()
|
764
|
+
#; [!fam11] returns 'Enum(...)' string.
|
765
|
+
return "Enum(#{self.collect(&:inspect).join(', ')})"
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
class Length
|
770
|
+
def initialize(expected)
|
771
|
+
@expected = expected
|
772
|
+
end
|
773
|
+
def ===(actual)
|
774
|
+
#; [!03ozi] compares length of actual value with expected value.
|
775
|
+
return @expected === actual.length
|
776
|
+
end
|
777
|
+
def inspect()
|
778
|
+
#; [!nwv3e] returns 'Length(n)' string.
|
779
|
+
return "Length(#{@expected.inspect})"
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
class Any
|
784
|
+
def ===(actual)
|
785
|
+
#; [!mzion] returns true in any case.
|
786
|
+
true
|
787
|
+
end
|
788
|
+
def inspect()
|
789
|
+
#; [!6f0yv] returns 'Any()' string.
|
790
|
+
return "Any()"
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
end
|
795
|
+
|
796
|
+
|
551
797
|
class Context
|
552
798
|
## * Context class is separated from ScopeNode, TopicNode, and SpecLeaf.
|
553
799
|
## * `topic()` and `spec()` creates subclass of Context class,
|
@@ -891,11 +1137,20 @@ module Oktest
|
|
891
1137
|
end
|
892
1138
|
|
893
1139
|
def self.scope(tag: nil, &block)
|
894
|
-
#; [!
|
895
|
-
#; [!rsimc] adds scope object as child of THE_GLOBAL_SCOPE.
|
1140
|
+
#; [!kem4y] detects test script filename.
|
896
1141
|
location = caller(1).first # caller() makes performance slower, but necessary.
|
897
1142
|
filename = location =~ /:\d+/ ? $` : nil
|
898
|
-
filename
|
1143
|
+
#; [!6ullm] changes test script filename from absolute path to relative path.
|
1144
|
+
if filename
|
1145
|
+
pwd = Dir.pwd()
|
1146
|
+
if filename.start_with?(pwd)
|
1147
|
+
filename = filename[pwd.length..-1].sub(/\A\//, '')
|
1148
|
+
elsif filename.start_with?('./')
|
1149
|
+
filename = filename[2..-1]
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
#; [!vxoy1] creates new scope object.
|
1153
|
+
#; [!rsimc] adds scope object as child of THE_GLOBAL_SCOPE.
|
899
1154
|
scope = ScopeNode.new(THE_GLOBAL_SCOPE, filename, tag: tag)
|
900
1155
|
#; [!jmc4q] raises error when nested called.
|
901
1156
|
self.__scope(scope, &block)
|
@@ -1107,6 +1362,41 @@ module Oktest
|
|
1107
1362
|
return Benry::Recorder.new
|
1108
1363
|
end
|
1109
1364
|
|
1365
|
+
def JSON(actual)
|
1366
|
+
#; [!n0k03] creates JsonMatcher object.
|
1367
|
+
return JsonMatcher.new(actual)
|
1368
|
+
end
|
1369
|
+
|
1370
|
+
def Enum(*values)
|
1371
|
+
#; [!fbfr0] creates Enum object which is a subclass of Set.
|
1372
|
+
return JsonMatcher::Enum.new(values)
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
def Bool()
|
1376
|
+
#; [!vub5j] creates a set of true and false.
|
1377
|
+
return Enum(true, false)
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def OR(*args)
|
1381
|
+
#; [!9e8im] creates `OR` object.
|
1382
|
+
return JsonMatcher::OR.new(*args)
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def AND(*args)
|
1386
|
+
#; [!38jln] creates `AND` object.
|
1387
|
+
return JsonMatcher::AND.new(*args)
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def Length(n)
|
1391
|
+
#; [!qqas3] creates Length object.
|
1392
|
+
return JsonMatcher::Length.new(n)
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
def Any()
|
1396
|
+
#; [!dlo1o] creates an 'Any' object.
|
1397
|
+
return JsonMatcher::Any.new
|
1398
|
+
end
|
1399
|
+
|
1110
1400
|
end
|
1111
1401
|
|
1112
1402
|
|
@@ -1206,11 +1496,25 @@ module Oktest
|
|
1206
1496
|
@reporter.exit_all(self)
|
1207
1497
|
end
|
1208
1498
|
|
1499
|
+
def _spec_first(node)
|
1500
|
+
a1, a2 = node.each_child.partition {|c|
|
1501
|
+
c.is_a?(SpecLeaf) || (c.is_a?(TopicNode) && c._prefix != '*')
|
1502
|
+
}
|
1503
|
+
return a1+a2
|
1504
|
+
end
|
1505
|
+
private :_spec_first
|
1506
|
+
|
1209
1507
|
def visit_scope(scope, depth, parent)
|
1210
1508
|
@reporter.enter_scope(scope) unless scope.equal?(THE_GLOBAL_SCOPE)
|
1211
1509
|
#; [!5anr7] calls before_all and after_all blocks.
|
1212
1510
|
call_before_all_block(scope)
|
1213
|
-
|
1511
|
+
#; [!c5cw0] run specs and case_when in advance of specs and topics when SimpleReporter.
|
1512
|
+
if @reporter.order_policy() == :spec_first
|
1513
|
+
_spec_first(scope).each {|c| c.accept_visitor(self, depth+1, scope) }
|
1514
|
+
else
|
1515
|
+
scope.each_child {|c| c.accept_visitor(self, depth+1, scope) }
|
1516
|
+
end
|
1517
|
+
#
|
1214
1518
|
call_after_all_block(scope)
|
1215
1519
|
@reporter.exit_scope(scope) unless scope.equal?(THE_GLOBAL_SCOPE)
|
1216
1520
|
end
|
@@ -1219,7 +1523,13 @@ module Oktest
|
|
1219
1523
|
@reporter.enter_topic(topic, depth)
|
1220
1524
|
#; [!i3yfv] calls 'before_all' and 'after_all' blocks.
|
1221
1525
|
call_before_all_block(topic)
|
1222
|
-
|
1526
|
+
#; [!p3a5o] run specs and case_when in advance of specs and topics when SimpleReporter.
|
1527
|
+
if @reporter.order_policy() == :spec_first
|
1528
|
+
_spec_first(topic).each {|c| c.accept_visitor(self, depth+1, topic) }
|
1529
|
+
else
|
1530
|
+
topic.each_child {|c| c.accept_visitor(self, depth+1, topic) }
|
1531
|
+
end
|
1532
|
+
#
|
1223
1533
|
call_after_all_block(topic)
|
1224
1534
|
@reporter.exit_topic(topic, depth)
|
1225
1535
|
end
|
@@ -1420,6 +1730,7 @@ module Oktest
|
|
1420
1730
|
def exit_spec(spec, depth, status, error, parent); end
|
1421
1731
|
#
|
1422
1732
|
def counts; {}; end
|
1733
|
+
def order_policy(); nil; end # :spec_first or nil
|
1423
1734
|
|
1424
1735
|
end
|
1425
1736
|
|
@@ -1430,7 +1741,7 @@ module Oktest
|
|
1430
1741
|
CHARS = { :PASS=>'.', :FAIL=>'f', :ERROR=>'E', :SKIP=>'s', :TODO=>'t' }
|
1431
1742
|
|
1432
1743
|
|
1433
|
-
def initialize
|
1744
|
+
def initialize()
|
1434
1745
|
@exceptions = []
|
1435
1746
|
@counts = {}
|
1436
1747
|
end
|
@@ -1573,6 +1884,10 @@ module Oktest
|
|
1573
1884
|
|
1574
1885
|
LABELS = { :PASS=>'pass', :FAIL=>'Fail', :ERROR=>'ERROR', :SKIP=>'Skip', :TODO=>'TODO' }
|
1575
1886
|
|
1887
|
+
def enter_scope(scope)
|
1888
|
+
puts "## #{scope.filename}"
|
1889
|
+
end
|
1890
|
+
|
1576
1891
|
def enter_topic(topic, depth)
|
1577
1892
|
super
|
1578
1893
|
puts "#{' ' * (depth - 1)}#{topic._prefix} #{Color.topic(topic.target)}"
|
@@ -1606,6 +1921,64 @@ module Oktest
|
|
1606
1921
|
|
1607
1922
|
|
1608
1923
|
class SimpleReporter < BaseReporter
|
1924
|
+
#; [!jxa1b] reports topics and progress.
|
1925
|
+
|
1926
|
+
def initialize()
|
1927
|
+
super
|
1928
|
+
@_nl = true
|
1929
|
+
end
|
1930
|
+
|
1931
|
+
def order_policy()
|
1932
|
+
:spec_first
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
def _nl()
|
1936
|
+
(puts(); @_nl = true) unless @_nl
|
1937
|
+
end
|
1938
|
+
private :_nl
|
1939
|
+
|
1940
|
+
def _nl_off()
|
1941
|
+
@_nl = false
|
1942
|
+
end
|
1943
|
+
private :_nl_off
|
1944
|
+
|
1945
|
+
def enter_scope(scope)
|
1946
|
+
_nl()
|
1947
|
+
puts "## #{scope.filename}"
|
1948
|
+
end
|
1949
|
+
|
1950
|
+
def exit_scope(scope)
|
1951
|
+
_nl()
|
1952
|
+
print_exceptions()
|
1953
|
+
end
|
1954
|
+
|
1955
|
+
def enter_topic(topic, depth)
|
1956
|
+
super
|
1957
|
+
return if topic._prefix == '-'
|
1958
|
+
_nl()
|
1959
|
+
print "#{' ' * (depth - 1)}#{topic._prefix} #{Color.topic(topic.target)}: "
|
1960
|
+
$stdout.flush()
|
1961
|
+
_nl_off()
|
1962
|
+
end
|
1963
|
+
|
1964
|
+
def exit_topic(topic, depth)
|
1965
|
+
super
|
1966
|
+
return if topic._prefix == '-'
|
1967
|
+
_nl()
|
1968
|
+
print_exceptions()
|
1969
|
+
end
|
1970
|
+
|
1971
|
+
def exit_spec(spec, depth, status, error, parent)
|
1972
|
+
super
|
1973
|
+
print Color.status(status, CHARS[status] || '?')
|
1974
|
+
$stdout.flush
|
1975
|
+
_nl_off()
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
|
1981
|
+
class CompactReporter < BaseReporter
|
1609
1982
|
#; [!xfd5o] reports filename.
|
1610
1983
|
|
1611
1984
|
def enter_scope(scope)
|
@@ -1672,14 +2045,13 @@ module Oktest
|
|
1672
2045
|
REPORTER_CLASSES = {
|
1673
2046
|
'verbose' => VerboseReporter, 'v' => VerboseReporter,
|
1674
2047
|
'simple' => SimpleReporter, 's' => SimpleReporter,
|
2048
|
+
'compact' => CompactReporter, 'c' => CompactReporter,
|
1675
2049
|
'plain' => PlainReporter, 'p' => PlainReporter,
|
1676
2050
|
'quiet' => QuietReporter, 'q' => QuietReporter,
|
1677
2051
|
}
|
1678
2052
|
|
1679
2053
|
|
1680
2054
|
def self.run(reporter: nil, style: nil)
|
1681
|
-
#; [!kfi8b] do nothing when 'Oktest.scope()' not called.
|
1682
|
-
return unless THE_GLOBAL_SCOPE.has_child?
|
1683
2055
|
#; [!6xn3t] creates reporter object according to 'style:' keyword arg.
|
1684
2056
|
klass = (style ? REPORTER_CLASSES[style] : REPORTER_CLASS) or
|
1685
2057
|
raise ArgumentError, "#{style.inspect}: unknown style."
|
@@ -2145,7 +2517,7 @@ END
|
|
2145
2517
|
return 0
|
2146
2518
|
end
|
2147
2519
|
#; [!65vdx] prints help message if no arguments specified.
|
2148
|
-
if filenames.empty?
|
2520
|
+
if filenames.empty? && !THE_GLOBAL_SCOPE.has_child?
|
2149
2521
|
puts help_message()
|
2150
2522
|
return 0
|
2151
2523
|
end
|
@@ -2176,7 +2548,8 @@ END
|
|
2176
2548
|
Config.auto_run = false
|
2177
2549
|
#; [!18qpe] runs test scripts.
|
2178
2550
|
#; [!0qd92] '-s verbose' or '-sv' option prints test results in verbose mode.
|
2179
|
-
#; [!
|
2551
|
+
#; [!zfdr5] '-s simple' or '-ss' option prints test results in simple mode.
|
2552
|
+
#; [!ef5v7] '-s compact' or '-sc' option prints test results in compact mode.
|
2180
2553
|
#; [!244te] '-s plain' or '-sp' option prints test results in plain mode.
|
2181
2554
|
#; [!ai61w] '-s quiet' or '-sq' option prints test results in quiet mode.
|
2182
2555
|
n_errors = Oktest.run(:style=>opts.style)
|
@@ -2207,6 +2580,7 @@ END
|
|
2207
2580
|
}
|
2208
2581
|
parser.on('-F PATTERN') {|val|
|
2209
2582
|
#; [!71h2x] '-F ...' option will be error.
|
2583
|
+
#; [!j01y7] if filerting by '-F' matched nothing, then prints zero result.
|
2210
2584
|
val =~ /\A(topic|spec|tag|sid)(=|!=)/ or
|
2211
2585
|
raise OptionParser::InvalidArgument, val
|
2212
2586
|
opts.filter = val
|
@@ -2237,7 +2611,7 @@ END
|
|
2237
2611
|
Usage: %{command} [<options>] [<file-or-directory>...]
|
2238
2612
|
-h, --help : show help
|
2239
2613
|
--version : print version
|
2240
|
-
-s <STYLE>
|
2614
|
+
-s <REPORT-STYLE> : verbose/simple/compact/plain/quiet, or v/s/c/p/q
|
2241
2615
|
-F <PATTERN> : filter topic or spec with pattern (see below)
|
2242
2616
|
--color[={on|off}] : enable/disable output coloring forcedly
|
2243
2617
|
-C, --create : print test code skeleton
|