oktest 1.0.0

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