oktest 1.0.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ###
4
- ### $Release: 1.0.0 $
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
  ###
data/lib/oktest.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ###
4
- ### $Release: 1.0.0 $
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.0.0 $'.split()[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, subclass: true, &b)
273
+ return raise?(errcls, errmsg, _subclass: true, &b)
268
274
  end
269
275
 
270
- def raise?(errcls=nil, errmsg=nil, subclass: false, &b)
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 'subclass: true' specified.
293
- elsif subclass && exc.class < errcls
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 'subclass: true' specified.
332
- elsif subclass && exc.class < errcls
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
- #; [!vxoy1] creates new scope object.
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 = filename.sub(/\A\.\//, '')
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
- scope.each_child {|c| c.accept_visitor(self, depth+1, scope) }
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
- topic.each_child {|c| c.accept_visitor(self, depth+1, topic) }
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
- #; [!ef5v7] '-s simple' or '-ss' option prints test results in simple mode.
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> : report style (verbose/simple/plain/quiet, or v/s/p/q)
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