oktest 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|