oktest 1.0.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +443 -35
- data/Rakefile.rb +5 -2
- data/benchmark/Rakefile.rb +4 -0
- data/lib/oktest.rb +549 -72
- data/oktest.gemspec +3 -3
- data/test/assertion_test.rb +70 -5
- data/test/filter_test.rb +2 -2
- data/test/fixture_test.rb +14 -1
- data/test/generator_test.rb +1 -1
- data/test/helper_test.rb +3 -3
- data/test/initialize.rb +8 -1
- data/test/mainapp_test.rb +95 -42
- data/test/matcher_test.rb +424 -0
- data/test/misc_test.rb +5 -5
- data/test/node_test.rb +27 -4
- data/test/reporter_test.rb +56 -29
- data/test/runner_test.rb +97 -25
- data/test/tc.rb +12 -0
- data/test/util_test.rb +71 -1
- data/test/utilhelper_test.rb +84 -0
- data/test/visitor_test.rb +1 -1
- metadata +9 -8
data/lib/oktest.rb
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
3
|
###
|
4
|
-
### $Release: 1.0
|
4
|
+
### $Release: 1.2.0 $
|
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.0
|
15
|
+
VERSION = '$Release: 1.2.0 $'.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,261 @@ 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
|
+
|
797
|
+
module UtilHelper
|
798
|
+
|
799
|
+
def partial_regexp(pattern, begin_='\A', end_='\z', mark="{== ==}")
|
800
|
+
#; [!9drtn] is available in both topic and spec blocks.
|
801
|
+
return Util.partial_regexp(pattern, begin_, end_, mark)
|
802
|
+
end
|
803
|
+
|
804
|
+
def partial_regexp!(pattern, begin_='\A', end_='\z', mark="{== ==}")
|
805
|
+
#; [!wo4hp] is available in both topic and spec blocks.
|
806
|
+
return Util.partial_regexp!(pattern, begin_, end_, mark)
|
807
|
+
end
|
808
|
+
|
809
|
+
end
|
810
|
+
|
811
|
+
|
551
812
|
class Context
|
552
813
|
## * Context class is separated from ScopeNode, TopicNode, and SpecLeaf.
|
553
814
|
## * `topic()` and `spec()` creates subclass of Context class,
|
@@ -555,6 +816,8 @@ module Oktest
|
|
555
816
|
## * `scope()` instanciates those subclasses, and run spec blocks
|
556
817
|
## in that instance objects.
|
557
818
|
|
819
|
+
extend UtilHelper
|
820
|
+
|
558
821
|
class << self
|
559
822
|
attr_accessor :__node
|
560
823
|
end
|
@@ -587,26 +850,29 @@ module Oktest
|
|
587
850
|
return to
|
588
851
|
end
|
589
852
|
|
590
|
-
def self.spec(desc, tag: nil, &block)
|
853
|
+
def self.spec(desc, tag: nil, fixture: nil, &block)
|
591
854
|
node = @__node
|
592
855
|
node.is_a?(Node) or raise "internal error: node=#{node.inspect}" # for debug
|
856
|
+
#; [!4vkbl] error when `fixture:` keyword arg is not a Hash object.
|
857
|
+
fixture.nil? || fixture.is_a?(Hash) or
|
858
|
+
raise ArgumentError, "spec(fixture: #{fixture.inspect}): fixture argument should be a Hash object, but got #{fixture.class.name} object."
|
593
859
|
#; [!ala78] provides raising TodoException block if block not given.
|
594
860
|
block ||= proc { raise TodoException, "not implemented yet" }
|
595
861
|
#; [!x48db] keeps called location only when block has parameters.
|
596
862
|
if block.parameters.empty?
|
597
863
|
location = nil
|
598
864
|
else
|
599
|
-
location =
|
865
|
+
location = caller_locations(1, 1).first
|
600
866
|
end
|
601
867
|
#; [!c8c8o] creates new spec object.
|
602
|
-
spec = SpecLeaf.new(node, desc, tag: tag, location: location, &block)
|
868
|
+
spec = SpecLeaf.new(node, desc, tag: tag, fixture: fixture, location: location, &block)
|
603
869
|
return spec
|
604
870
|
end
|
605
871
|
|
606
872
|
def self.fixture(name, &block)
|
607
873
|
#; [!8wfrq] registers fixture factory block.
|
608
874
|
#; [!y3ks3] retrieves block parameter names.
|
609
|
-
location =
|
875
|
+
location = caller_locations(1, 1).first
|
610
876
|
@__node.register_fixture_block(name, location, &block)
|
611
877
|
self
|
612
878
|
end
|
@@ -730,6 +996,7 @@ module Oktest
|
|
730
996
|
context = @context_class.new()
|
731
997
|
#; [!9hbxn] context object has 'ok()' method.
|
732
998
|
context.extend SpecHelper
|
999
|
+
context.extend UtilHelper
|
733
1000
|
return context
|
734
1001
|
end
|
735
1002
|
|
@@ -758,7 +1025,7 @@ module Oktest
|
|
758
1025
|
return @hooks[key]
|
759
1026
|
end
|
760
1027
|
|
761
|
-
def _repr(depth=0, buf=
|
1028
|
+
def _repr(depth=0, buf=String.new)
|
762
1029
|
#; [!bt5j8] builds debug string.
|
763
1030
|
if depth < 0
|
764
1031
|
id_str = "%x" % self.object_id
|
@@ -833,16 +1100,17 @@ module Oktest
|
|
833
1100
|
|
834
1101
|
class SpecLeaf < Item
|
835
1102
|
|
836
|
-
def initialize(parent, desc, tag: nil, location: nil, &block)
|
1103
|
+
def initialize(parent, desc, tag: nil, fixture: nil, location: nil, &block)
|
837
1104
|
#@parent = parent # not keep parent node to avoid recursive reference
|
838
1105
|
@desc = desc
|
839
1106
|
@tag = tag
|
1107
|
+
@fixture = fixture
|
840
1108
|
@location = location # necessary when raising fixture not found error
|
841
1109
|
@block = block
|
842
1110
|
parent.add_child(self) if parent
|
843
1111
|
end
|
844
1112
|
|
845
|
-
attr_reader :desc, :tag, :location, :block
|
1113
|
+
attr_reader :desc, :tag, :fixture, :location, :block
|
846
1114
|
|
847
1115
|
def _prefix
|
848
1116
|
'-'
|
@@ -863,7 +1131,7 @@ module Oktest
|
|
863
1131
|
nil
|
864
1132
|
end
|
865
1133
|
|
866
|
-
def _repr(depth=0, buf=
|
1134
|
+
def _repr(depth=0, buf=String.new) #:nodoc:
|
867
1135
|
#; [!6nsgy] builds debug string.
|
868
1136
|
buf << " " * depth << "- #{@desc}"
|
869
1137
|
buf << " (tag: #{@tag.inspect})" if @tag
|
@@ -892,8 +1160,8 @@ module Oktest
|
|
892
1160
|
|
893
1161
|
def self.scope(tag: nil, &block)
|
894
1162
|
#; [!kem4y] detects test script filename.
|
895
|
-
location =
|
896
|
-
filename = location
|
1163
|
+
location = caller_locations(1, 1).first
|
1164
|
+
filename = location.path.to_s
|
897
1165
|
#; [!6ullm] changes test script filename from absolute path to relative path.
|
898
1166
|
if filename
|
899
1167
|
pwd = Dir.pwd()
|
@@ -929,7 +1197,7 @@ module Oktest
|
|
929
1197
|
#; [!bc3l2] records invoked location.
|
930
1198
|
#; [!mqtdy] not record invoked location when `Config.ok_location == false`.
|
931
1199
|
if Config.ok_location
|
932
|
-
location =
|
1200
|
+
location = caller_locations(1, 1).first
|
933
1201
|
else
|
934
1202
|
location = nil
|
935
1203
|
end
|
@@ -944,7 +1212,7 @@ module Oktest
|
|
944
1212
|
#; [!agmx8] records invoked location.
|
945
1213
|
#; [!a9508] not record invoked location when `Config.ok_location == false`.
|
946
1214
|
if Config.ok_location
|
947
|
-
location =
|
1215
|
+
location = caller_locations(1, 1).first
|
948
1216
|
else
|
949
1217
|
location = nil
|
950
1218
|
end
|
@@ -970,7 +1238,7 @@ module Oktest
|
|
970
1238
|
#; [!wxcsp] raises error when fixture not found.
|
971
1239
|
unless tuple
|
972
1240
|
exc = FixtureNotFoundError.new("`#{name.inspect}`: fixture not found.")
|
973
|
-
exc.set_backtrace([
|
1241
|
+
exc.set_backtrace([caller_locations(1, 1).first.to_s])
|
974
1242
|
raise exc
|
975
1243
|
end
|
976
1244
|
#; [!m4ava] calls fixture block and returns result of it.
|
@@ -980,7 +1248,7 @@ module Oktest
|
|
980
1248
|
end
|
981
1249
|
|
982
1250
|
def TODO()
|
983
|
-
location =
|
1251
|
+
location = caller_locations(1, 1).first # ex: "foo_test.rb:123:in ...."
|
984
1252
|
@__TODO = location
|
985
1253
|
end
|
986
1254
|
|
@@ -1116,6 +1384,41 @@ module Oktest
|
|
1116
1384
|
return Benry::Recorder.new
|
1117
1385
|
end
|
1118
1386
|
|
1387
|
+
def JSON(actual)
|
1388
|
+
#; [!n0k03] creates JsonMatcher object.
|
1389
|
+
return JsonMatcher.new(actual)
|
1390
|
+
end
|
1391
|
+
|
1392
|
+
def Enum(*values)
|
1393
|
+
#; [!fbfr0] creates Enum object which is a subclass of Set.
|
1394
|
+
return JsonMatcher::Enum.new(values)
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
def Bool()
|
1398
|
+
#; [!vub5j] creates a set of true and false.
|
1399
|
+
return Enum(true, false)
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
def OR(*args)
|
1403
|
+
#; [!9e8im] creates `OR` object.
|
1404
|
+
return JsonMatcher::OR.new(*args)
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
def AND(*args)
|
1408
|
+
#; [!38jln] creates `AND` object.
|
1409
|
+
return JsonMatcher::AND.new(*args)
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
def Length(n)
|
1413
|
+
#; [!qqas3] creates Length object.
|
1414
|
+
return JsonMatcher::Length.new(n)
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
def Any()
|
1418
|
+
#; [!dlo1o] creates an 'Any' object.
|
1419
|
+
return JsonMatcher::Any.new
|
1420
|
+
end
|
1421
|
+
|
1119
1422
|
end
|
1120
1423
|
|
1121
1424
|
|
@@ -1215,11 +1518,25 @@ module Oktest
|
|
1215
1518
|
@reporter.exit_all(self)
|
1216
1519
|
end
|
1217
1520
|
|
1521
|
+
def _spec_first(node)
|
1522
|
+
a1, a2 = node.each_child.partition {|c|
|
1523
|
+
c.is_a?(SpecLeaf) || (c.is_a?(TopicNode) && c._prefix != '*')
|
1524
|
+
}
|
1525
|
+
return a1+a2
|
1526
|
+
end
|
1527
|
+
private :_spec_first
|
1528
|
+
|
1218
1529
|
def visit_scope(scope, depth, parent)
|
1219
1530
|
@reporter.enter_scope(scope) unless scope.equal?(THE_GLOBAL_SCOPE)
|
1220
1531
|
#; [!5anr7] calls before_all and after_all blocks.
|
1221
1532
|
call_before_all_block(scope)
|
1222
|
-
|
1533
|
+
#; [!c5cw0] run specs and case_when in advance of specs and topics when SimpleReporter.
|
1534
|
+
if @reporter.order_policy() == :spec_first
|
1535
|
+
_spec_first(scope).each {|c| c.accept_visitor(self, depth+1, scope) }
|
1536
|
+
else
|
1537
|
+
scope.each_child {|c| c.accept_visitor(self, depth+1, scope) }
|
1538
|
+
end
|
1539
|
+
#
|
1223
1540
|
call_after_all_block(scope)
|
1224
1541
|
@reporter.exit_scope(scope) unless scope.equal?(THE_GLOBAL_SCOPE)
|
1225
1542
|
end
|
@@ -1228,7 +1545,13 @@ module Oktest
|
|
1228
1545
|
@reporter.enter_topic(topic, depth)
|
1229
1546
|
#; [!i3yfv] calls 'before_all' and 'after_all' blocks.
|
1230
1547
|
call_before_all_block(topic)
|
1231
|
-
|
1548
|
+
#; [!p3a5o] run specs and case_when in advance of specs and topics when SimpleReporter.
|
1549
|
+
if @reporter.order_policy() == :spec_first
|
1550
|
+
_spec_first(topic).each {|c| c.accept_visitor(self, depth+1, topic) }
|
1551
|
+
else
|
1552
|
+
topic.each_child {|c| c.accept_visitor(self, depth+1, topic) }
|
1553
|
+
end
|
1554
|
+
#
|
1232
1555
|
call_after_all_block(topic)
|
1233
1556
|
@reporter.exit_topic(topic, depth)
|
1234
1557
|
end
|
@@ -1246,7 +1569,8 @@ module Oktest
|
|
1246
1569
|
begin
|
1247
1570
|
params = Util.required_param_names_of_block(spec.block)
|
1248
1571
|
values = params.nil? || params.empty? ? [] \
|
1249
|
-
: get_fixture_values(params, node, spec, context
|
1572
|
+
: get_fixture_values(params, node, spec, context, spec.location,
|
1573
|
+
spec.fixture ? spec.fixture.dup : {})
|
1250
1574
|
spec.run_block_in_context_object(context, *values)
|
1251
1575
|
rescue NoMemoryError => exc; raise exc
|
1252
1576
|
rescue SignalException => exc; raise exc
|
@@ -1271,7 +1595,7 @@ module Oktest
|
|
1271
1595
|
exc = TODO_EXCEPTION.new("#{exc.class} raised because not implemented yet")
|
1272
1596
|
end
|
1273
1597
|
location = context.__TODO
|
1274
|
-
exc.set_backtrace([location])
|
1598
|
+
exc.set_backtrace([location.to_s])
|
1275
1599
|
end
|
1276
1600
|
#; [!dihkr] calls 'at_end' blocks, even when exception raised.
|
1277
1601
|
begin
|
@@ -1285,8 +1609,8 @@ module Oktest
|
|
1285
1609
|
|
1286
1610
|
private
|
1287
1611
|
|
1288
|
-
def get_fixture_values(names, node, spec, context)
|
1289
|
-
return THE_FIXTURE_MANAGER.get_fixture_values(names, node, spec, context)
|
1612
|
+
def get_fixture_values(names, node, spec, context, location=nil, resolved=nil)
|
1613
|
+
return THE_FIXTURE_MANAGER.get_fixture_values(names, node, spec, context, location, resolved)
|
1290
1614
|
end
|
1291
1615
|
|
1292
1616
|
def _call_blocks_parent_first(node, name, context)
|
@@ -1342,51 +1666,53 @@ module Oktest
|
|
1342
1666
|
|
1343
1667
|
class FixtureManager
|
1344
1668
|
|
1345
|
-
def get_fixture_values(names, node, spec, context, location
|
1669
|
+
def get_fixture_values(names, node, spec, context, location, resolved={}, _resolving=[])
|
1346
1670
|
#; [!w6ffs] resolves 'this_topic' fixture name as target objec of current topic.
|
1347
|
-
|
1671
|
+
resolved[:this_topic] = node.target if !resolved.key?(:this_topic) && node.topic?
|
1348
1672
|
#; [!ja2ew] resolves 'this_spec' fixture name as description of current spec.
|
1349
|
-
|
1673
|
+
resolved[:this_spec] = spec.desc if !resolved.key?(:this_spec)
|
1350
1674
|
#; [!v587k] resolves fixtures.
|
1351
|
-
location ||= spec.location
|
1352
1675
|
return names.collect {|name|
|
1353
1676
|
#; [!np4p9] raises error when loop exists in dependency.
|
1354
1677
|
! _resolving.include?(name) or
|
1355
1678
|
raise _looped_dependency_error(name, _resolving, location)
|
1356
|
-
get_fixture_value(name, node, spec, context, location,
|
1679
|
+
get_fixture_value(name, node, spec, context, location, resolved, _resolving)
|
1357
1680
|
}
|
1358
1681
|
end
|
1359
1682
|
|
1360
|
-
def get_fixture_value(name, node, spec, context, location
|
1361
|
-
return
|
1362
|
-
location ||= spec.location
|
1683
|
+
def get_fixture_value(name, node, spec, context, location, resolved={}, _resolving=[])
|
1684
|
+
return resolved[name] if resolved.key?(name)
|
1363
1685
|
tuple = node.get_fixture_block(name)
|
1364
1686
|
if tuple
|
1365
1687
|
block, param_names, location = tuple
|
1366
1688
|
#; [!2esaf] resolves fixture dependencies.
|
1367
1689
|
if param_names
|
1368
1690
|
_resolving << name
|
1369
|
-
args = get_fixture_values(param_names, node, spec, context, location,
|
1691
|
+
args = get_fixture_values(param_names, node, spec, context, location, resolved, _resolving)
|
1370
1692
|
(popped = _resolving.pop) == name or
|
1371
1693
|
raise "** assertion failed: name=#{name.inspect}, resolvng[-1]=#{popped.inspect}"
|
1372
|
-
#; [!4xghy] calls fixture block with context object as self.
|
1373
|
-
val = context.instance_exec(*args, &block)
|
1374
1694
|
else
|
1375
|
-
|
1695
|
+
args = []
|
1376
1696
|
end
|
1697
|
+
#; [!gyyst] overwrites keyword params by fixture values.
|
1698
|
+
kwnames = Util.keyword_param_names_of_block(block)
|
1699
|
+
kwargs = {}
|
1700
|
+
kwnames.each {|name| kwargs[name] = resolved[name] if resolved.key?(name) }
|
1701
|
+
#; [!4xghy] calls fixture block with context object as self.
|
1702
|
+
val = context.instance_exec(*args, **kwargs, &block)
|
1377
1703
|
#; [!8t3ul] caches fixture value to call fixture block only once per spec.
|
1378
|
-
|
1704
|
+
resolved[name] = val
|
1379
1705
|
return val
|
1380
1706
|
elsif node.parent
|
1381
1707
|
#; [!4chb9] traverses parent topics if fixture not found in current topic.
|
1382
|
-
return get_fixture_value(name, node.parent, spec, context, location,
|
1708
|
+
return get_fixture_value(name, node.parent, spec, context, location, resolved, _resolving)
|
1383
1709
|
elsif ! node.equal?(THE_GLOBAL_SCOPE)
|
1384
1710
|
#; [!wt3qk] suports global scope.
|
1385
|
-
return get_fixture_value(name, THE_GLOBAL_SCOPE, spec, context, location,
|
1711
|
+
return get_fixture_value(name, THE_GLOBAL_SCOPE, spec, context, location, resolved, _resolving)
|
1386
1712
|
else
|
1387
1713
|
#; [!nr79z] raises error when fixture not found.
|
1388
1714
|
exc = FixtureNotFoundError.new("#{name}: fixture not found. (spec: #{spec.desc})")
|
1389
|
-
exc.set_backtrace([location]) if location
|
1715
|
+
exc.set_backtrace([location.to_s]) if location
|
1390
1716
|
raise exc
|
1391
1717
|
end
|
1392
1718
|
end
|
@@ -1401,7 +1727,7 @@ module Oktest
|
|
1401
1727
|
loop = s1.empty? ? s2 : "#{s1}->#{s2}"
|
1402
1728
|
#location = $1 if location =~ /(.*:\d+)/
|
1403
1729
|
exc = LoopedDependencyError.new("fixture dependency is looped: #{loop}")
|
1404
|
-
exc.set_backtrace([location])
|
1730
|
+
exc.set_backtrace([location.to_s])
|
1405
1731
|
return exc
|
1406
1732
|
end
|
1407
1733
|
|
@@ -1429,6 +1755,7 @@ module Oktest
|
|
1429
1755
|
def exit_spec(spec, depth, status, error, parent); end
|
1430
1756
|
#
|
1431
1757
|
def counts; {}; end
|
1758
|
+
def order_policy(); nil; end # :spec_first or nil
|
1432
1759
|
|
1433
1760
|
end
|
1434
1761
|
|
@@ -1439,7 +1766,7 @@ module Oktest
|
|
1439
1766
|
CHARS = { :PASS=>'.', :FAIL=>'f', :ERROR=>'E', :SKIP=>'s', :TODO=>'t' }
|
1440
1767
|
|
1441
1768
|
|
1442
|
-
def initialize
|
1769
|
+
def initialize()
|
1443
1770
|
@exceptions = []
|
1444
1771
|
@counts = {}
|
1445
1772
|
end
|
@@ -1544,7 +1871,7 @@ module Oktest
|
|
1544
1871
|
end
|
1545
1872
|
lines = []
|
1546
1873
|
msg.each_line {|line| lines << line }
|
1547
|
-
puts lines.shift.chomp
|
1874
|
+
puts Color.errmsg(lines.shift.chomp)
|
1548
1875
|
puts lines.join.chomp unless lines.empty?
|
1549
1876
|
puts exc.diff if exc.respond_to?(:diff) && exc.diff # for oktest.rb
|
1550
1877
|
end
|
@@ -1582,6 +1909,10 @@ module Oktest
|
|
1582
1909
|
|
1583
1910
|
LABELS = { :PASS=>'pass', :FAIL=>'Fail', :ERROR=>'ERROR', :SKIP=>'Skip', :TODO=>'TODO' }
|
1584
1911
|
|
1912
|
+
def enter_scope(scope)
|
1913
|
+
puts "## #{scope.filename}"
|
1914
|
+
end
|
1915
|
+
|
1585
1916
|
def enter_topic(topic, depth)
|
1586
1917
|
super
|
1587
1918
|
puts "#{' ' * (depth - 1)}#{topic._prefix} #{Color.topic(topic.target)}"
|
@@ -1606,15 +1937,73 @@ module Oktest
|
|
1606
1937
|
$stdout.flush
|
1607
1938
|
end
|
1608
1939
|
label = Color.status(status, LABELS[status] || '???')
|
1609
|
-
msg = "#{' ' * (depth - 1)}- [#{label}] #{spec.desc}"
|
1940
|
+
msg = ["#{' ' * (depth - 1)}- [#{label}] #{spec.desc}"]
|
1610
1941
|
msg << " " << Color.reason("(reason: #{error.message})") if status == :SKIP
|
1611
|
-
puts msg
|
1942
|
+
puts msg.join()
|
1612
1943
|
end
|
1613
1944
|
|
1614
1945
|
end
|
1615
1946
|
|
1616
1947
|
|
1617
1948
|
class SimpleReporter < BaseReporter
|
1949
|
+
#; [!jxa1b] reports topics and progress.
|
1950
|
+
|
1951
|
+
def initialize()
|
1952
|
+
super
|
1953
|
+
@_nl = true
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
def order_policy()
|
1957
|
+
:spec_first
|
1958
|
+
end
|
1959
|
+
|
1960
|
+
def _nl()
|
1961
|
+
(puts(); @_nl = true) unless @_nl
|
1962
|
+
end
|
1963
|
+
private :_nl
|
1964
|
+
|
1965
|
+
def _nl_off()
|
1966
|
+
@_nl = false
|
1967
|
+
end
|
1968
|
+
private :_nl_off
|
1969
|
+
|
1970
|
+
def enter_scope(scope)
|
1971
|
+
_nl()
|
1972
|
+
puts "## #{scope.filename}"
|
1973
|
+
end
|
1974
|
+
|
1975
|
+
def exit_scope(scope)
|
1976
|
+
_nl()
|
1977
|
+
print_exceptions()
|
1978
|
+
end
|
1979
|
+
|
1980
|
+
def enter_topic(topic, depth)
|
1981
|
+
super
|
1982
|
+
return if topic._prefix == '-'
|
1983
|
+
_nl()
|
1984
|
+
print "#{' ' * (depth - 1)}#{topic._prefix} #{Color.topic(topic.target)}: "
|
1985
|
+
$stdout.flush()
|
1986
|
+
_nl_off()
|
1987
|
+
end
|
1988
|
+
|
1989
|
+
def exit_topic(topic, depth)
|
1990
|
+
super
|
1991
|
+
return if topic._prefix == '-'
|
1992
|
+
_nl()
|
1993
|
+
print_exceptions()
|
1994
|
+
end
|
1995
|
+
|
1996
|
+
def exit_spec(spec, depth, status, error, parent)
|
1997
|
+
super
|
1998
|
+
print Color.status(status, CHARS[status] || '?')
|
1999
|
+
$stdout.flush
|
2000
|
+
_nl_off()
|
2001
|
+
end
|
2002
|
+
|
2003
|
+
end
|
2004
|
+
|
2005
|
+
|
2006
|
+
class CompactReporter < BaseReporter
|
1618
2007
|
#; [!xfd5o] reports filename.
|
1619
2008
|
|
1620
2009
|
def enter_scope(scope)
|
@@ -1681,14 +2070,13 @@ module Oktest
|
|
1681
2070
|
REPORTER_CLASSES = {
|
1682
2071
|
'verbose' => VerboseReporter, 'v' => VerboseReporter,
|
1683
2072
|
'simple' => SimpleReporter, 's' => SimpleReporter,
|
2073
|
+
'compact' => CompactReporter, 'c' => CompactReporter,
|
1684
2074
|
'plain' => PlainReporter, 'p' => PlainReporter,
|
1685
2075
|
'quiet' => QuietReporter, 'q' => QuietReporter,
|
1686
2076
|
}
|
1687
2077
|
|
1688
2078
|
|
1689
2079
|
def self.run(reporter: nil, style: nil)
|
1690
|
-
#; [!kfi8b] do nothing when 'Oktest.scope()' not called.
|
1691
|
-
return unless THE_GLOBAL_SCOPE.has_child?
|
1692
2080
|
#; [!6xn3t] creates reporter object according to 'style:' keyword arg.
|
1693
2081
|
klass = (style ? REPORTER_CLASSES[style] : REPORTER_CLASS) or
|
1694
2082
|
raise ArgumentError, "#{style.inspect}: unknown style."
|
@@ -1737,6 +2125,13 @@ module Oktest
|
|
1737
2125
|
return param_names
|
1738
2126
|
end
|
1739
2127
|
|
2128
|
+
def keyword_param_names_of_block(block)
|
2129
|
+
#; [!p6qqp] returns keyword param names of proc object.
|
2130
|
+
names = []
|
2131
|
+
block.parameters.each {|kind, name| names << name if kind == :key }
|
2132
|
+
return names
|
2133
|
+
end
|
2134
|
+
|
1740
2135
|
def strfold(str, width=80, mark='...')
|
1741
2136
|
#; [!wb7m8] returns string as it is if string is not long.
|
1742
2137
|
return str if str.bytesize <= width
|
@@ -1837,6 +2232,70 @@ module Oktest
|
|
1837
2232
|
end
|
1838
2233
|
end
|
1839
2234
|
|
2235
|
+
class PartialRegexp < Regexp
|
2236
|
+
attr_accessor :pattern_string, :begin, :end, :mark
|
2237
|
+
def inspect()
|
2238
|
+
#; [!uyh31] returns function call style string if @pattern_string is set.
|
2239
|
+
if @pattern_string
|
2240
|
+
c = @pattern_string.end_with?("\n") ? "" : ".chomp"
|
2241
|
+
p = @pattern_string.chomp
|
2242
|
+
b = @begin == '\A' ? "'\\A'" : @begin.inspect
|
2243
|
+
e = @end == '\z' ? "'\\z'" : @end.inspect
|
2244
|
+
m = mark == "{== ==}" ? "" : ", #{mark.inspect}"
|
2245
|
+
return "partial_regexp(<<PREXP#{c}, #{b}, #{e}#{m})\n#{p}\nPREXP\n"
|
2246
|
+
#; [!ts9v4] returns regexp literal style string if @pattern_string is not set.
|
2247
|
+
else
|
2248
|
+
s = super
|
2249
|
+
s = s.gsub(/([^\\](?:\\\\)*)((?:\\n)+)/) {
|
2250
|
+
$1 + ("\\n\n" * ($2.length / 2))
|
2251
|
+
}
|
2252
|
+
if s =~ /\n/
|
2253
|
+
s = s.sub(/\A\/(\\A)?/, "/\\1\n")
|
2254
|
+
s = s + "x" # `/.../x` means multiline regexp
|
2255
|
+
end
|
2256
|
+
return s
|
2257
|
+
end
|
2258
|
+
end
|
2259
|
+
end
|
2260
|
+
|
2261
|
+
def partial_regexp!(pattern, begin_='\A', end_='\z', mark="{== ==}")
|
2262
|
+
#; [!peyu4] returns PartialRegexp object which inspect string is function call styel.
|
2263
|
+
regexp = partial_regexp(pattern, begin_, end_, mark)
|
2264
|
+
regexp.pattern_string = pattern
|
2265
|
+
regexp.begin = begin_; regexp.end = end_; regexp.mark = mark
|
2266
|
+
return regexp
|
2267
|
+
end
|
2268
|
+
|
2269
|
+
def partial_regexp(pattern, begin_='\A', end_='\z', mark="{== ==}")
|
2270
|
+
mark_rexp = PARTIAL_REGEXP_CACHE[mark]
|
2271
|
+
if mark_rexp.nil?
|
2272
|
+
#; [!ostkw] raises error if mark has no space or has more than two spaces.
|
2273
|
+
pair = mark.split()
|
2274
|
+
pair.length == 2 or
|
2275
|
+
raise ArgumentError.new("#{mark.inspect}: mark should contain only one space (ex: `{== ==}`).")
|
2276
|
+
open = Regexp.escape(pair[0])
|
2277
|
+
close = Regexp.escape(pair[1])
|
2278
|
+
mark_rexp = Regexp.compile("#{open}(.*?)#{close}")
|
2279
|
+
PARTIAL_REGEXP_CACHE[mark] = mark_rexp
|
2280
|
+
end
|
2281
|
+
#; [!wn524] returns PartialRegexp object which inspect string is regexp literal style.
|
2282
|
+
pos = 0
|
2283
|
+
buf = []
|
2284
|
+
buf << begin_ if begin_
|
2285
|
+
pattern.scan(mark_rexp) do
|
2286
|
+
m = Regexp.last_match
|
2287
|
+
text = pattern[pos, m.begin(0) - pos]
|
2288
|
+
buf << Regexp.escape(text) << $1.strip()
|
2289
|
+
pos = m.end(0)
|
2290
|
+
end
|
2291
|
+
rest = pos == 0 ? pattern : pattern[pos..-1]
|
2292
|
+
buf << Regexp.escape(rest) unless rest.empty?
|
2293
|
+
buf << end_ if end_
|
2294
|
+
return PartialRegexp.compile(buf.join())
|
2295
|
+
end
|
2296
|
+
|
2297
|
+
PARTIAL_REGEXP_CACHE = {} # :nodoc:
|
2298
|
+
|
1840
2299
|
end
|
1841
2300
|
|
1842
2301
|
|
@@ -1979,24 +2438,35 @@ module Oktest
|
|
1979
2438
|
module_function
|
1980
2439
|
|
1981
2440
|
def normal s; return s; end
|
1982
|
-
def bold s; return "\e[0;1m#{s}\e[
|
1983
|
-
|
1984
|
-
def
|
1985
|
-
def
|
1986
|
-
def
|
1987
|
-
def
|
1988
|
-
def
|
1989
|
-
def
|
1990
|
-
def
|
2441
|
+
def bold s; return "\e[0;1m#{s}\e[0m"; end
|
2442
|
+
|
2443
|
+
def black s; return "\e[0;30m#{s}\e[0m"; end
|
2444
|
+
def red s; return "\e[0;31m#{s}\e[0m"; end
|
2445
|
+
def green s; return "\e[0;32m#{s}\e[0m"; end
|
2446
|
+
def yellow s; return "\e[0;33m#{s}\e[0m"; end
|
2447
|
+
def blue s; return "\e[0;34m#{s}\e[0m"; end
|
2448
|
+
def magenta s; return "\e[0;35m#{s}\e[0m"; end
|
2449
|
+
def cyan s; return "\e[0;36m#{s}\e[0m"; end
|
2450
|
+
def white s; return "\e[0;37m#{s}\e[0m"; end
|
2451
|
+
|
2452
|
+
def black_b s; return "\e[1;30m#{s}\e[0m"; end # bold
|
2453
|
+
def red_b s; return "\e[1;31m#{s}\e[0m"; end # bold
|
2454
|
+
def green_b s; return "\e[1;32m#{s}\e[0m"; end # bold
|
2455
|
+
def yellow_b s; return "\e[1;33m#{s}\e[0m"; end # bold
|
2456
|
+
def blue_b s; return "\e[1;34m#{s}\e[0m"; end # bold
|
2457
|
+
def magenta_b s; return "\e[1;35m#{s}\e[0m"; end # bold
|
2458
|
+
def cyan_b s; return "\e[1;36m#{s}\e[0m"; end # bold
|
2459
|
+
def white_b s; return "\e[1;37m#{s}\e[0m"; end # bold
|
1991
2460
|
|
1992
2461
|
def topic s; Config.color_enabled ? bold(s) : s; end
|
1993
2462
|
def spec s; Config.color_enabled ? normal(s) : s; end
|
1994
|
-
def pass s; Config.color_enabled ?
|
2463
|
+
def pass s; Config.color_enabled ? cyan(s) : s; end
|
1995
2464
|
def fail s; Config.color_enabled ? red(s) : s; end
|
1996
|
-
def error s; Config.color_enabled ?
|
2465
|
+
def error s; Config.color_enabled ? red_b(s) : s; end # bold
|
1997
2466
|
def skip s; Config.color_enabled ? yellow(s) : s; end
|
1998
2467
|
def todo s; Config.color_enabled ? yellow(s) : s; end
|
1999
2468
|
def reason s; Config.color_enabled ? yellow(s) : s; end
|
2469
|
+
def errmsg s; Config.color_enabled ? red(s) : s; end
|
2000
2470
|
|
2001
2471
|
def status(status, s)
|
2002
2472
|
#; [!yev5y] returns string containing color escape sequence.
|
@@ -2131,6 +2601,11 @@ END
|
|
2131
2601
|
color_enabled = nil
|
2132
2602
|
opts = Options.new
|
2133
2603
|
parser = option_parser(opts)
|
2604
|
+
#; [!v5xie] parses $OKTEST_RB environment variable.
|
2605
|
+
if ENV.key?('OKTEST_RB')
|
2606
|
+
parser.parse(ENV['OKTEST_RB'].split())
|
2607
|
+
end
|
2608
|
+
#
|
2134
2609
|
filenames = parser.parse(args)
|
2135
2610
|
#; [!9973n] '-h' or '--help' option prints help message.
|
2136
2611
|
if opts.help
|
@@ -2142,8 +2617,8 @@ END
|
|
2142
2617
|
puts VERSION
|
2143
2618
|
return 0
|
2144
2619
|
end
|
2145
|
-
#; [!dk8eg] '-
|
2146
|
-
if opts.
|
2620
|
+
#; [!dk8eg] '-S' or '--skeleton' option prints test code skeleton.
|
2621
|
+
if opts.skeleton
|
2147
2622
|
print SKELETON
|
2148
2623
|
return 0
|
2149
2624
|
end
|
@@ -2154,7 +2629,7 @@ END
|
|
2154
2629
|
return 0
|
2155
2630
|
end
|
2156
2631
|
#; [!65vdx] prints help message if no arguments specified.
|
2157
|
-
if filenames.empty?
|
2632
|
+
if filenames.empty? && !THE_GLOBAL_SCOPE.has_child?
|
2158
2633
|
puts help_message()
|
2159
2634
|
return 0
|
2160
2635
|
end
|
@@ -2185,7 +2660,8 @@ END
|
|
2185
2660
|
Config.auto_run = false
|
2186
2661
|
#; [!18qpe] runs test scripts.
|
2187
2662
|
#; [!0qd92] '-s verbose' or '-sv' option prints test results in verbose mode.
|
2188
|
-
#; [!
|
2663
|
+
#; [!zfdr5] '-s simple' or '-ss' option prints test results in simple mode.
|
2664
|
+
#; [!ef5v7] '-s compact' or '-sc' option prints test results in compact mode.
|
2189
2665
|
#; [!244te] '-s plain' or '-sp' option prints test results in plain mode.
|
2190
2666
|
#; [!ai61w] '-s quiet' or '-sq' option prints test results in quiet mode.
|
2191
2667
|
n_errors = Oktest.run(:style=>opts.style)
|
@@ -2201,7 +2677,7 @@ END
|
|
2201
2677
|
private
|
2202
2678
|
|
2203
2679
|
class Options #:nodoc:
|
2204
|
-
attr_accessor :help, :version, :style, :filter, :color, :
|
2680
|
+
attr_accessor :help, :version, :style, :filter, :color, :skeleton, :generate, :faster
|
2205
2681
|
end
|
2206
2682
|
|
2207
2683
|
def option_parser(opts)
|
@@ -2216,6 +2692,7 @@ END
|
|
2216
2692
|
}
|
2217
2693
|
parser.on('-F PATTERN') {|val|
|
2218
2694
|
#; [!71h2x] '-F ...' option will be error.
|
2695
|
+
#; [!j01y7] if filerting by '-F' matched nothing, then prints zero result.
|
2219
2696
|
val =~ /\A(topic|spec|tag|sid)(=|!=)/ or
|
2220
2697
|
raise OptionParser::InvalidArgument, val
|
2221
2698
|
opts.filter = val
|
@@ -2227,7 +2704,7 @@ END
|
|
2227
2704
|
#; [!dptgn] '--color' is same as '--color=on'.
|
2228
2705
|
opts.color = val || 'on'
|
2229
2706
|
}
|
2230
|
-
parser.on('-
|
2707
|
+
parser.on('-S', '--skeleton') { opts.skeleton = true }
|
2231
2708
|
parser.on('-G', '--generate[=styleoption]') {|val|
|
2232
2709
|
val.nil? || val == 'unaryop' or
|
2233
2710
|
raise OptionParser::InvalidArgument, val
|
@@ -2242,16 +2719,16 @@ END
|
|
2242
2719
|
return HELP_MESSAGE % {command: command}
|
2243
2720
|
end
|
2244
2721
|
|
2245
|
-
HELP_MESSAGE = <<'END'
|
2722
|
+
HELP_MESSAGE = <<'END'.gsub(/^#.*\n/, '')
|
2246
2723
|
Usage: %{command} [<options>] [<file-or-directory>...]
|
2247
2724
|
-h, --help : show help
|
2248
2725
|
--version : print version
|
2249
|
-
-s <STYLE>
|
2726
|
+
-s <REPORT-STYLE> : verbose/simple/compact/plain/quiet, or v/s/c/p/q
|
2250
2727
|
-F <PATTERN> : filter topic or spec with pattern (see below)
|
2251
2728
|
--color[={on|off}] : enable/disable output coloring forcedly
|
2252
|
-
-
|
2729
|
+
-S, --skeleton : print test code skeleton
|
2253
2730
|
-G, --generate : generate test code skeleton from ruby file
|
2254
|
-
--faster : make 'ok{}' faster (for very large project)
|
2731
|
+
# --faster : make 'ok{}' faster (for very large project)
|
2255
2732
|
|
2256
2733
|
Filter examples:
|
2257
2734
|
$ oktest -F topic=Hello # filter by topic
|