oktest 1.0.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.
data/test/tc.rb ADDED
@@ -0,0 +1,115 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ## micro test case class
4
+ class TC
5
+
6
+ COUNTS = {:ok => 0, :fail => 0, :error => 0}
7
+
8
+ def self.report_result()
9
+ ok, fail, error = COUNTS[:ok], COUNTS[:fail], COUNTS[:error]
10
+ COUNTS.keys.each {|k| COUNTS[k] = 0 }
11
+ red = proc {|s| "\033[0;31m#{s}\033[0m" }
12
+ fail_s = "fail: #{fail}" ; fail_s = red.call(fail_s) if fail > 0
13
+ error_s = "error: #{error}" ; error_s = red.call(error_s) if error > 0
14
+ STDOUT.puts "## total: #{ok+fail+error} (ok: #{ok}, #{fail_s}, #{error_s})"
15
+ end
16
+
17
+ def self.describe(target, &b)
18
+ prev, @curr_target = @curr_target, target
19
+ yield
20
+ ensure
21
+ @curr_target = prev
22
+ end
23
+
24
+ def self.curr_target()
25
+ @curr_target
26
+ end
27
+
28
+ def self.it(spec, &b)
29
+ t = @curr_target
30
+ print "[#{self.name}#{t ? ' > ' : ''}#{t}] #{spec} ... " unless ENV['TC_QUIET']
31
+ obj = self.new
32
+ obj.setup()
33
+ begin
34
+ obj.instance_eval(&b)
35
+ rescue => exc
36
+ if exc.is_a?(AssertionFailed)
37
+ COUNTS[:fail] += 1; puts "FAILED!" unless ENV['TC_QUIET']
38
+ else
39
+ COUNTS[:error] += 1; puts "ERROR!" unless ENV['TC_QUIET']
40
+ end
41
+ puts " #{exc.class.name}: #{exc.message}"
42
+ exc.backtrace.each do |bt|
43
+ puts " #{bt}" if bt.index(__FILE__) == nil
44
+ end
45
+ else
46
+ COUNTS[:ok] += 1; puts "ok." unless ENV['TC_QUIET']
47
+ ensure
48
+ obj.teardown()
49
+ end
50
+ end
51
+
52
+ def setup
53
+ end
54
+
55
+ def teardown
56
+ end
57
+
58
+ class AssertionFailed < StandardError
59
+ end
60
+
61
+ def assert(cond, msg="assertion failed")
62
+ raise msg unless cond
63
+ end
64
+
65
+ def assert_eq(actual, expected)
66
+ return if actual == expected
67
+ multiline_p = actual.is_a?(String) && actual =~ /\n/ \
68
+ && expected.is_a?(String) && expected =~ /\n/
69
+ errmsg = (multiline_p \
70
+ ? "$<actual> == $<expected> : failed.\n" +
71
+ " $<actual>: <<END\n#{actual}END\n" +
72
+ " $<expected>: <<END\n#{expected}END"
73
+ : "$<actual> == $<expected> : failed.\n" +
74
+ " $<actual>: #{actual.inspect}\n" +
75
+ " $<expected>: #{expected.inspect}")
76
+ raise AssertionFailed, errmsg
77
+ end
78
+
79
+ def assert_exc(errcls, errmsg=nil, &b)
80
+ begin
81
+ yield
82
+ rescue NoMemoryError, SystemExit, SyntaxError, SignalException
83
+ raise
84
+ rescue Exception => exc
85
+ exc.class == errcls or
86
+ raise AssertionFailed, "#{errcls} should be raised but got #{exc.inspect}"
87
+ errmsg.nil? || errmsg === exc.message or
88
+ raise AssertionFailed, ("invalid error message.\n"\
89
+ " $<actual>: #{exc.message.inspect}\n"\
90
+ " $<expected>: #{errmsg.inspect}")
91
+ return exc
92
+ else
93
+ raise AssertionFailed, "#{errcls.name} should be raised but not."
94
+ end
95
+ end
96
+
97
+ def capture(input="", tty: true, &b)
98
+ require 'stringio' unless defined?(StringIO)
99
+ stdin, stdout, stderr = $stdin, $stdout, $stderr
100
+ $stdin = sin = StringIO.new(input)
101
+ $stdout = sout = StringIO.new
102
+ $stderr = serr = StringIO.new
103
+ def sin.tty? ; true; end if tty
104
+ def sout.tty?; true; end if tty
105
+ def sout.tty?; true; end if tty
106
+ yield
107
+ return sout.string, serr.string
108
+ ensure
109
+ $stdin, $stdout, $stderr = stdin, stdout, stderr
110
+ end
111
+
112
+ end
113
+
114
+
115
+ at_exit { TC.report_result() }
data/test/util_test.rb ADDED
@@ -0,0 +1,258 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 1.0.0 $
5
+ ### $Copyright: copyright(c) 2011-2021 kuwata-lab.com all rights reserved $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ require_relative './initialize'
10
+
11
+
12
+ class Util_TC < TC
13
+ include Oktest::Util
14
+
15
+ describe Oktest::Util do
16
+
17
+ describe '.file_line()' do
18
+ it "[!4z65g] returns nil if file not exist or not a file." do
19
+ assert_eq Oktest::Util.file_line("not-exist-file", 1), nil
20
+ assert_eq Oktest::Util.file_line(".", 1), nil
21
+ end
22
+ it "[!162e1] returns line string." do
23
+ lineno = __LINE__ + 2
24
+ _ = <<END
25
+ U6XYR-SH08J
26
+ END
27
+ assert_eq Oktest::Util.file_line(__FILE__, lineno), "U6XYR-SH08J\n"
28
+ end
29
+ it "[!4a2ji] caches recent file content for performance reason." do
30
+ _ = Oktest::Util.file_line(__FILE__, 1)
31
+ c = Oktest::Util.instance_variable_get('@__cache')
32
+ assert c.is_a?(Array), "array object expected."
33
+ assert_eq c[0], __FILE__
34
+ assert_eq c[1][0], "# -*- coding: utf-8 -*-\n"
35
+ assert_eq c[1][11], "class Util_TC < TC\n"
36
+ #
37
+ data1 = c[1]
38
+ _ = Oktest::Util.file_line(__FILE__, 1)
39
+ c2 = Oktest::Util.instance_variable_get('@__cache')
40
+ assert c2[1].equal?(data1), "cache object changed unexpectedly."
41
+ end
42
+ it "[!wtrl5] recreates cache data if other file requested." do
43
+ _ = Oktest::Util.file_line(__FILE__, 1)
44
+ c = Oktest::Util.instance_variable_get('@__cache')
45
+ data1 = c[1]
46
+ #
47
+ otherfile = File.join(File.dirname(__FILE__), "initialize.rb")
48
+ _ = Oktest::Util.file_line(otherfile, 1)
49
+ c3 = Oktest::Util.instance_variable_get('@__cache')
50
+ assert_eq c3[0], otherfile
51
+ assert ! c3[1].equal?(data1), "cache object should be recreated, but not."
52
+ end
53
+ end
54
+
55
+ describe '.required_param_names_of_block()' do
56
+ it "[!a9n46] returns nil if argument is nil." do
57
+ assert_eq required_param_names_of_block(nil), nil
58
+ end
59
+ it "[!7m81p] returns empty array if block has no parameters." do
60
+ pr = proc { nil }
61
+ assert_eq required_param_names_of_block(pr), []
62
+ end
63
+ it "[!n3g63] returns parameter names of block." do
64
+ pr = proc {|x, y, z| nil }
65
+ assert_eq required_param_names_of_block(pr), [:x, :y, :z]
66
+ end
67
+ it "[!d5kym] collects only normal parameter names." do
68
+ pr = proc {|x, y, z=1, *rest, a: 1, b: 2, &blk| nil }
69
+ assert_eq required_param_names_of_block(pr), [:x, :y]
70
+ pr = proc {|a: 1, b: 2, &blk| nil }
71
+ assert_eq required_param_names_of_block(pr), []
72
+ pr = proc {|*rest, &blk| nil }
73
+ assert_eq required_param_names_of_block(pr), []
74
+ end
75
+ end
76
+
77
+ describe '.strfold()' do
78
+ it "[!wb7m8] returns string as it is if string is not long." do
79
+ s = "*" * 79
80
+ assert_eq strfold(s, 80), s
81
+ s = "*" * 80
82
+ assert_eq strfold(s, 80), s
83
+ end
84
+ it "[!a2igb] shorten string if it is enough long." do
85
+ expected = "*" * 77 + "..."
86
+ s = "*" * 81
87
+ assert_eq strfold(s, 80), expected
88
+ end
89
+ it "[!0gjye] supports non-ascii characters." do
90
+ expected = "あ" * 38 + "..."
91
+ s = "あ" * 41
92
+ assert_eq strfold(s, 80), expected
93
+ #
94
+ expected = "x" + "あ" * 37 + "..."
95
+ s = "x" + "あ" * 40
96
+ assert_eq strfold(s, 80), expected
97
+ end
98
+ end
99
+
100
+ describe '.hhmmss()' do
101
+ it "[!shyl1] converts 400953.444 into '111:22:33.4'." do
102
+ x = 111*60*60 + 22*60 + 33.444
103
+ assert_eq x, 400953.444
104
+ assert_eq hhmmss(x), "111:22:33.4"
105
+ end
106
+ it "[!vyi2v] converts 5025.678 into '1:23:45.7'." do
107
+ x = 1*60*60 + 23*60 + 45.678
108
+ assert_eq x, 5025.678
109
+ assert_eq hhmmss(x), "1:23:45.7"
110
+ end
111
+ it "[!pm4xf] converts 754.888 into '12:34.9'." do
112
+ x = 12*60 + 34.888
113
+ assert_eq x, 754.888
114
+ assert_eq hhmmss(x), "12:34.9"
115
+ end
116
+ it "[!lwewr] converts 83.444 into '1:23.4'." do
117
+ x = 1*60 + 23.444
118
+ assert_eq x, 83.444
119
+ assert_eq hhmmss(x), "1:23.4"
120
+ end
121
+ it "[!ijx52] converts 56.8888 into '56.9'." do
122
+ x = 56.8888
123
+ assert_eq hhmmss(x), "56.9"
124
+ end
125
+ it "[!2kra2] converts 9.777 into '9.78'." do
126
+ x = 9.777
127
+ assert_eq hhmmss(x), "9.78"
128
+ end
129
+ it "[!4aomb] converts 0.7777 into '0.778'." do
130
+ x = 0.7777
131
+ assert_eq hhmmss(x), "0.778"
132
+ end
133
+ end
134
+
135
+ describe '.hhmmss()' do
136
+ it "[!wf4ns] calculates unified diff from two text strings." do
137
+ s1 = <<'END'
138
+ Haruhi
139
+ Mikuru
140
+ Yuki
141
+ END
142
+ s2 = <<'END'
143
+ Haruhi
144
+ Michiru
145
+ Yuki
146
+ END
147
+ expected = <<'END'
148
+ --- old
149
+ +++ new
150
+ @@ -1,4 +1,4 @@
151
+ Haruhi
152
+ -Mikuru
153
+ +Michiru
154
+ Yuki
155
+ END
156
+ diff = Oktest::Util.unified_diff(s1, s2)
157
+ assert_eq diff, expected
158
+ end
159
+ end
160
+
161
+ describe '.unified_diff()' do
162
+ it "[!rnx4f] checks whether text string ends with newline char." do
163
+ s1 = <<'END'
164
+ Haruhi
165
+ Mikuru
166
+ Yuki
167
+ END
168
+ s2 = s1
169
+ #
170
+ expected1 = <<'END'
171
+ --- old
172
+ +++ new
173
+ @@ -1,4 +1,4 @@
174
+ Haruhi
175
+ Mikuru
176
+ -Yuki\ No newline at end of string
177
+ +Yuki
178
+ END
179
+ diff = Oktest::Util.unified_diff(s1.chomp, s2)
180
+ assert_eq diff, expected1
181
+ #
182
+ expected2 = <<'END'
183
+ --- old
184
+ +++ new
185
+ @@ -1,4 +1,4 @@
186
+ Haruhi
187
+ Mikuru
188
+ -Yuki
189
+ +Yuki\ No newline at end of string
190
+ END
191
+ diff = Oktest::Util.unified_diff(s1, s2.chomp)
192
+ assert_eq diff, expected2
193
+ end
194
+ end
195
+
196
+ describe '.diff_unified()' do
197
+ it "[!ulyq5] returns unified diff string of two text strings." do
198
+ s1 = <<'END'
199
+ Haruhi
200
+ Mikuru
201
+ Yuki
202
+ END
203
+ s2 = <<'END'
204
+ Haruhi
205
+ Michiru
206
+ Yuki
207
+ END
208
+ expected = <<'END'
209
+ --- old
210
+ +++ new
211
+ @@ -1,3 +1,3 @@
212
+ Haruhi
213
+ -Mikuru
214
+ +Michiru
215
+ Yuki
216
+ END
217
+ diff = Oktest::Util.diff_unified(s1, s2)
218
+ assert_eq diff, expected
219
+ end
220
+ it "[!6tgum] detects whether char at end of file is newline or not." do
221
+ s1 = <<'END'
222
+ Haruhi
223
+ Mikuru
224
+ Yuki
225
+ END
226
+ s2 = s1
227
+ #
228
+ expected1 = <<'END'
229
+ --- old
230
+ +++ new
231
+ @@ -1,3 +1,3 @@
232
+ Haruhi
233
+ Mikuru
234
+ -Yuki
235
+
236
+ +Yuki
237
+ END
238
+ diff = Oktest::Util.diff_unified(s1.chomp, s2)
239
+ assert_eq diff, expected1
240
+ #
241
+ expected2 = <<'END'
242
+ --- old
243
+ +++ new
244
+ @@ -1,3 +1,3 @@
245
+ Haruhi
246
+ Mikuru
247
+ -Yuki
248
+ +Yuki
249
+
250
+ END
251
+ diff = Oktest::Util.diff_unified(s1, s2.chomp)
252
+ assert_eq diff, expected2
253
+ end
254
+ end
255
+
256
+ end
257
+
258
+ end
@@ -0,0 +1,292 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ ###
4
+ ### $Release: 1.0.0 $
5
+ ### $Copyright: copyright(c) 2011-2021 kuwata-lab.com all rights reserved $
6
+ ### $License: MIT License $
7
+ ###
8
+
9
+ require_relative './initialize'
10
+
11
+
12
+ class Visitor_TC < TC
13
+
14
+ class DummyVisitor0 < Oktest::Visitor
15
+ def initialize
16
+ @log = []
17
+ end
18
+ attr_reader :log
19
+ def visit_scope(spec, depth, parent)
20
+ indent = depth >= 0 ? " " * depth : ""
21
+ @log << "#{indent}scope: #{spec.filename} {\n"
22
+ super
23
+ @log << "#{indent}}\n"
24
+ end
25
+ def visit_topic(topic, depth, parent)
26
+ indent = depth >= 0 ? " " * depth : ""
27
+ @log << "#{indent}topic: #{topic.target} {\n"
28
+ super
29
+ @log << "#{indent}}\n"
30
+ end
31
+ def visit_spec(spec, depth, parent)
32
+ indent = depth >= 0 ? " " * depth : ""
33
+ @log << "#{indent}spec: #{spec.desc} {\n"
34
+ super
35
+ @log << "#{indent}}\n"
36
+ end
37
+ end
38
+
39
+ def prepare()
40
+ Oktest.scope do
41
+ topic 'Example1' do
42
+ topic 'sample1-1' do
43
+ spec("1+1 should be 2") { ok {1+1} == 2 }
44
+ spec("1-1 should be 0") { ok {1-1} == 0 }
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def setup
51
+ end
52
+
53
+ def teardown
54
+ Oktest::THE_GLOBAL_SCOPE.clear_children()
55
+ end
56
+
57
+ describe '#visit_spec()' do
58
+ it "[!9f7i9] do something on spec." do
59
+ expected = <<'END'
60
+ spec: sample {
61
+ }
62
+ END
63
+ sp = Oktest::SpecLeaf.new(nil, "sample")
64
+ visitor = DummyVisitor0.new
65
+ visitor.visit_spec(sp, 0, nil)
66
+ assert_eq visitor.log.join(), expected
67
+ end
68
+ end
69
+
70
+ describe '#visit_topic()' do
71
+ it "[!mu3fn] visits each child of topic." do
72
+ expected = <<'END'
73
+ topic: example {
74
+ spec: sample {
75
+ }
76
+ }
77
+ END
78
+ to = Oktest::TopicNode.new(nil, "example")
79
+ sp = Oktest::SpecLeaf.new(to, "sample")
80
+ visitor = DummyVisitor0.new
81
+ visitor.visit_topic(to, 0, nil)
82
+ assert_eq visitor.log.join(), expected
83
+ end
84
+ end
85
+
86
+ describe '#visit_scope()' do
87
+ it "[!hebhz] visits each child scope." do
88
+ expected = <<'END'
89
+ scope: file.rb {
90
+ topic: example {
91
+ }
92
+ spec: sample {
93
+ }
94
+ }
95
+ END
96
+ sc = Oktest::ScopeNode.new(nil, "file.rb")
97
+ to = Oktest::TopicNode.new(sc, "example")
98
+ sp = Oktest::SpecLeaf.new(sc, "sample")
99
+ visitor = DummyVisitor0.new
100
+ visitor.visit_scope(sc, 0, nil)
101
+ assert_eq visitor.log.join(), expected
102
+ end
103
+ end
104
+
105
+ describe '#start()' do
106
+ it "[!8h8qf] start visiting tree." do
107
+ expected = <<'END'
108
+ scope: test/visitor_test.rb {
109
+ topic: Example1 {
110
+ topic: sample1-1 {
111
+ spec: 1+1 should be 2 {
112
+ }
113
+ spec: 1-1 should be 0 {
114
+ }
115
+ }
116
+ }
117
+ }
118
+ END
119
+ prepare()
120
+ visitor = DummyVisitor0.new
121
+ visitor.start()
122
+ assert_eq visitor.log.join(), expected
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+
129
+ class Traverser_TC < TC
130
+
131
+ class MyTraverser < Oktest::Traverser
132
+ def on_scope(filename, tag, depth)
133
+ print " " * depth if depth >= 0
134
+ print "* scope: #{filename}"
135
+ print " (tag: #{tag})" if tag
136
+ print "\n"
137
+ yield
138
+ end
139
+ def on_topic(target, tag, depth)
140
+ print " " * depth
141
+ print "+ topic: #{target}"
142
+ print " (tag: #{tag})" if tag
143
+ print "\n"
144
+ yield
145
+ end
146
+ def on_case(desc, tag, depth)
147
+ print " " * depth
148
+ print "- case: #{desc}"
149
+ print " (tag: #{tag})" if tag
150
+ print "\n"
151
+ yield
152
+ end
153
+ def on_spec(desc, tag, depth)
154
+ print " " * depth
155
+ print "- spec: #{desc}"
156
+ print " (tag: #{tag})" if tag
157
+ print "\n"
158
+ end
159
+ end
160
+
161
+ def prepare()
162
+ Oktest.scope do
163
+ topic 'Example' do
164
+ topic Integer, tag: 'cls' do
165
+ spec "1+1 should be 2." do ok {1+1} == 2 end
166
+ spec "1-1 should be 0." do ok {1-1} == 0 end
167
+ case_when 'negative...' do
168
+ spec "abs() returns sign-reversed value." do ok {-3.abs()} == 3 end
169
+ end
170
+ case_else do
171
+ spec "abs() returns positive value." do ok {4.abs()} == 4 end
172
+ end
173
+ end
174
+ topic Float, tag: 'cls' do
175
+ spec "1*1 should be 1.", tag: 'err' do ok {1*1} == 2 end # fail
176
+ spec "1/1 should be 1.", tag: 'err' do ok {1/0} == 1 end # error
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ def teardown()
183
+ Oktest::THE_GLOBAL_SCOPE.clear_children()
184
+ end
185
+
186
+
187
+ describe '#start()' do
188
+ it "[!5zonp] visits topics and specs and calls callbacks." do
189
+ expected = <<'END'
190
+ * scope: test/visitor_test.rb
191
+ + topic: Example
192
+ + topic: Integer (tag: cls)
193
+ - spec: 1+1 should be 2.
194
+ - spec: 1-1 should be 0.
195
+ - case: When negative...
196
+ - spec: abs() returns sign-reversed value.
197
+ - case: Else
198
+ - spec: abs() returns positive value.
199
+ + topic: Float (tag: cls)
200
+ - spec: 1*1 should be 1. (tag: err)
201
+ - spec: 1/1 should be 1. (tag: err)
202
+ END
203
+ prepare()
204
+ sout, serr = capture { MyTraverser.new.start() }
205
+ assert_eq sout, expected
206
+ assert_eq serr, ""
207
+ end
208
+ it "[!gkopz] doesn't change Oktest::THE_GLOBAL_SCOPE." do
209
+ prepare()
210
+ n = Oktest::THE_GLOBAL_SCOPE.each_child.to_a.length
211
+ sout, serr = capture do
212
+ MyTraverser.new.start()
213
+ end
214
+ assert_eq Oktest::THE_GLOBAL_SCOPE.each_child.to_a.length, n
215
+ end
216
+ end
217
+
218
+ describe '#visit_scope()' do
219
+ it "[!ledj3] calls on_scope() callback on scope." do
220
+ expected = <<'END'
221
+ * scope: test/visitor_test.rb
222
+ * scope: test/visitor_test.rb
223
+ END
224
+ Oktest.scope do
225
+ end
226
+ Oktest.scope do
227
+ end
228
+ sout, serr = capture { MyTraverser.new.start() }
229
+ assert_eq sout, expected
230
+ assert_eq serr, ""
231
+ end
232
+ end
233
+
234
+ describe '#visit_topic()' do
235
+ it "[!x8r9w] calls on_topic() callback on topic." do
236
+ expected = <<'END'
237
+ * scope: test/visitor_test.rb
238
+ + topic: Parent
239
+ + topic: Child
240
+ END
241
+ Oktest.scope do
242
+ topic 'Parent' do
243
+ topic 'Child' do
244
+ end
245
+ end
246
+ end
247
+ sout, serr = capture { MyTraverser.new.start() }
248
+ assert_eq sout, expected
249
+ assert_eq serr, ""
250
+ end
251
+ it "[!qh0q3] calls on_case() callback on case_when or case_else." do
252
+ expected = <<'END'
253
+ * scope: test/visitor_test.rb
254
+ + topic: Parent
255
+ - case: When some condition
256
+ - case: Else
257
+ END
258
+ Oktest.scope do
259
+ topic 'Parent' do
260
+ case_when 'some condition' do
261
+ end
262
+ case_else do
263
+ end
264
+ end
265
+ end
266
+ sout, serr = capture { MyTraverser.new.start() }
267
+ assert_eq sout, expected
268
+ assert_eq serr, ""
269
+ end
270
+ end
271
+
272
+ describe '#visit_spec()' do
273
+ it "[!41uyj] calls on_spec() callback." do
274
+ expected = <<'END'
275
+ * scope: test/visitor_test.rb
276
+ + topic: Example
277
+ - spec: sample #1
278
+ - spec: sample #2
279
+ END
280
+ Oktest.scope do
281
+ topic 'Example' do
282
+ spec "sample #1" do ok {1+1} == 2 end
283
+ spec "sample #2" do ok {1-1} == 0 end
284
+ end
285
+ end
286
+ sout, serr = capture { MyTraverser.new.start() }
287
+ assert_eq sout, expected
288
+ assert_eq serr, ""
289
+ end
290
+ end
291
+
292
+ end