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