dtest 0.0.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/lib/dtest/core.rb ADDED
@@ -0,0 +1,257 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'singleton'
4
+ require 'time'
5
+ require 'rexml/document'
6
+ require 'rexml/cdata'
7
+
8
+ require 'dtest/failure'
9
+ require 'dtest/util'
10
+
11
+ require 'dtest/result'
12
+ require 'dtest/test'
13
+ require 'dtest/global'
14
+
15
+
16
+ module DTest
17
+
18
+ class Abort < Exception
19
+ end
20
+
21
+ class AbortTest < Abort
22
+ def to_s
23
+ "AbortTest #{super}"
24
+ end
25
+ end
26
+
27
+ class AbortTestCase < Abort
28
+ def to_s
29
+ "AbortTestCase #{super}"
30
+ end
31
+ end
32
+
33
+ class AbortGlobal < Abort
34
+ def to_s
35
+ "AbortGlobal #{super}"
36
+ end
37
+ end
38
+
39
+ class Context
40
+ def initialize(let = nil)
41
+ # テスト記述側(ブロック)から__stateが参照されないのが前提
42
+ @__state = {:let => let}
43
+ end
44
+
45
+ def call(state, block)
46
+ @__state = @__state.merge(state)
47
+ begin
48
+ instance_eval(&block)
49
+ rescue AbortTest, AbortTestCase, AbortGlobal => e
50
+ # スルー
51
+ raise e
52
+ rescue StandardError, Exception => e
53
+ # ブロック内の例外はabortとして処理する 
54
+ catch_exception(e)
55
+ abort_assert
56
+ end
57
+ end
58
+
59
+ def set(name, val)
60
+ return if @__state[:let] == nil
61
+
62
+ # set variable
63
+ @__state[:let].instance_variable_set("@#{name}", val)
64
+ # define getter method
65
+ @__state[:let].instance_eval <<-EOS
66
+ def #{name}
67
+ @#{name}
68
+ end
69
+ EOS
70
+ end
71
+
72
+ # :let value getter
73
+ def method_missing(name, *args, &block)
74
+ if @__state[:let] && @__state[:let].public_methods.map(&:to_sym).include?(name)
75
+ # getter value
76
+ @__state[:let].send(name)
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ # value-parameterized test parameters
83
+ def param
84
+ @__state[:parameter]
85
+ end
86
+
87
+ # abort type when assertion failed
88
+ def assert_failure?
89
+ @__state[:option][:assert_abort]
90
+ end
91
+
92
+ public
93
+ # expect/assert
94
+ def expect_true(condition, message = nil)
95
+ failed_true(message) unless condition
96
+ end
97
+
98
+ def expect_false(condition, message = nil)
99
+ failed_false(message) if condition
100
+ end
101
+
102
+ def expect_equal(expected, actual, message = nil)
103
+ failed_equal(expected, actual, message) unless expected == actual
104
+ end
105
+
106
+ def expect_not_equal(expected, actual, message = nil)
107
+ failed_equal(expected, actual, message) if expected == actual
108
+ end
109
+
110
+ def assert_true(condition, message = nil)
111
+ unless condition
112
+ failed_true(message)
113
+ abort_assert
114
+ end
115
+ end
116
+
117
+ def assert_false(condition, message = nil)
118
+ if condition
119
+ failed_false(message)
120
+ abort_assert
121
+ end
122
+ end
123
+
124
+ def assert_equal(expected, actual, message = nil)
125
+ unless expected == actual
126
+ failed_equal(expected, actual, message)
127
+ abort_assert
128
+ end
129
+ end
130
+
131
+ def assert_not_equal(expected, actual, message = nil)
132
+ if expected == actual
133
+ failed_equal(expected, actual, message)
134
+ abort_assert
135
+ end
136
+ end
137
+
138
+ def assert_error(*errors, &block)
139
+ begin
140
+ block.call
141
+ rescue *errors => actual_error
142
+ raised_expected_error = true
143
+ rescue RuntimeError, Exception => actual_error
144
+ raised_expected_error = false
145
+ else
146
+ str = "exception expected but none was thrown\n"
147
+ failed(str)
148
+ return
149
+ end
150
+
151
+ unless raised_expected_error
152
+ str = "exception expected #{errors.to_s} but #{actual_error.inspect}\n"
153
+ failed(str)
154
+ end
155
+ end
156
+
157
+ # abort
158
+ def abort_if(condition, message = nil)
159
+ str = "Abort"
160
+ str += ": #{message}\n" if message
161
+ failed(str)
162
+ raise AbortTest.new(str) if condition
163
+ end
164
+
165
+ def abort_case_if(condition, message = nil)
166
+ str = "Abort TestCase"
167
+ str += ": #{message}\n" if message
168
+ failed(str)
169
+ raise AbortTestCase.new(str) if condition
170
+ end
171
+
172
+ def abort_global_if(condition, message = nil)
173
+ str = "Abort global"
174
+ str += ": #{message}\n" if message
175
+ failed(str)
176
+ raise AbortGlobal.new(str) if condition
177
+ end
178
+
179
+ private #internal methods
180
+ def failed_equal(expected, actual, message = nil)
181
+ str = <<END
182
+ expected: #{expected.inspect}
183
+ got: #{actual.inspect}
184
+ END
185
+ str += "#{message}\n" if message
186
+ failed(str, 3)
187
+ end
188
+
189
+ def failed_true(message = nil)
190
+ str = "condition must be true\n"
191
+ str += "#{message}\n" if message
192
+ failed(str, 3)
193
+ end
194
+
195
+ def failed_false(message = nil)
196
+ str = "condition must be false\n"
197
+ str += "#{message}\n" if message
198
+ failed(str, 3)
199
+ end
200
+
201
+ def catch_exception(e)
202
+ str = "Exception #{e.inspect}\n"
203
+ add_failure(str, e.backtrace.first)
204
+ end
205
+
206
+ def add_failure(error, backtrace)
207
+ if @__state[:result]
208
+ @__state[:result] << (Test::FailureMessage.new(@__state[:parent], @__state[:name], error, backtrace))
209
+ end
210
+ end
211
+
212
+ # push failure message
213
+ def failed(error, level = 2)
214
+ add_failure(error, caller(level).first)
215
+ end
216
+
217
+ def abort_assert
218
+ case assert_failure?
219
+ when :global
220
+ raise AbortGlobal.new
221
+ when :testcase
222
+ raise AbortTestCase.new
223
+ else
224
+ raise AbortTest.new
225
+ end
226
+ end
227
+ end # class Context
228
+
229
+
230
+ class Block
231
+ attr_reader :name
232
+ attr_accessor :parent, :result, :parameter
233
+
234
+ def initialize(name, option, &block)
235
+ @name = name
236
+ @parent = nil
237
+ @option = option
238
+ @block = block
239
+ @result = nil # caller result object
240
+ @parameter = nil
241
+ end
242
+
243
+ def call(context, name = nil)
244
+ @parent = name if name
245
+ context.call({
246
+ :parent => @parent,
247
+ :name => @name,
248
+ :option => @option,
249
+ :result => @result,
250
+ :parameter => @parameter,
251
+ }, @block)
252
+ end
253
+
254
+ end # class Context
255
+
256
+ end # module DTest
257
+
data/lib/dtest/dsl.rb ADDED
@@ -0,0 +1,19 @@
1
+
2
+ require 'dtest/core'
3
+
4
+ module DTest
5
+ module DSL
6
+ def TestCase(name, options = {}, &block)
7
+ manager = Test::Manager::instance
8
+ manager.instance_eval(&block)
9
+ manager.add(name)
10
+ end
11
+
12
+ def GlobalHarness(&block)
13
+ manager = Global::Manager::instance
14
+ manager.instance_eval(&block)
15
+ end
16
+ end # module DSL
17
+ end # module DTest
18
+
19
+ include DTest::DSL
@@ -0,0 +1,27 @@
1
+
2
+ module DTest
3
+ def self.parse_caller(at)
4
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
5
+ file = $1
6
+ line = $2.to_i
7
+ method = $3
8
+ [file, line, method]
9
+ else
10
+ nil
11
+ end
12
+ end
13
+
14
+ def self.failure_line(backtrace)
15
+ file, line, method = parse_caller(backtrace)
16
+ if file && line && File.exists?(file)
17
+ [file, line, File.readlines(file)[line - 1].strip]
18
+ else
19
+ [file, line, "Unable to find #{file} to read failed line"]
20
+ end
21
+ end
22
+
23
+ def self.failure_caller(level)
24
+ failure_line(caller(level).first)
25
+ end
26
+ end # DTest
27
+
@@ -0,0 +1,91 @@
1
+
2
+ module DTest
3
+ module Global
4
+ class Harness
5
+ include Hook
6
+
7
+ attr_accessor :global
8
+ attr_accessor :before, :after
9
+
10
+ def initialize
11
+ @before = []
12
+ @after = []
13
+ end
14
+
15
+ def execute_after(list, context)
16
+ begin
17
+ exec(list, context)
18
+ rescue StandardError, Exception => e
19
+ # にぎりつぶす
20
+ end
21
+ end
22
+
23
+ def start(testcases)
24
+ # Progress
25
+ context = Context.new
26
+ Progress.setUpGlobal(testcases)
27
+
28
+ global_result = Test::GlobalResult.new(testcases)
29
+
30
+ @before.each {|b| b.result = global_result.before_failure }
31
+ @after.each {|b| b.result = global_result.after_failure }
32
+
33
+ global_result.timer {
34
+ begin
35
+ # execute before
36
+ exec(@before, context)
37
+
38
+ # execute cases
39
+ testcases.each do |testcase|
40
+ execute_testcase(global_result, testcase)
41
+ end
42
+ rescue AbortGlobal => e
43
+ # finish
44
+ rescue StandardError, Exception => e
45
+ # Blockでエラー処理しているので、にぎりつぶす
46
+ ensure
47
+ execute_after(@after, context)
48
+ Progress.tearDownGlobal
49
+ end
50
+ }
51
+
52
+ global_result
53
+ end
54
+
55
+ def execute_testcase(global_result, testcase)
56
+ begin
57
+ # execute TestCases
58
+ testcase.execute(global_result)
59
+ rescue AbortTest, AbortTestCase => e
60
+ # にぎりつぶす
61
+ end
62
+ end
63
+ end # class Harness
64
+
65
+ class Manager
66
+ include Singleton
67
+ attr_accessor :harness
68
+
69
+ def initialize
70
+ clear
71
+ end
72
+
73
+ def clear
74
+ remove_instance_var
75
+ @harness = Harness.new
76
+ end
77
+
78
+ def before(option = {}, &block)
79
+ b = Block.new("before", option, &block)
80
+ b.parent = 'Global'
81
+ @harness.before << b
82
+ end
83
+
84
+ def after(option = {}, &block)
85
+ b = Block.new("after", option, &block)
86
+ b.parent = 'Global'
87
+ @harness.after << b
88
+ end
89
+ end # class Manager
90
+ end # module Global
91
+ end # module DTest
@@ -0,0 +1,166 @@
1
+
2
+ require 'dtest/report'
3
+
4
+ module DTest
5
+ class Progress
6
+ def self.test_str(test_size)
7
+ test_str = test_size > 1 ? 'tests' : 'test'
8
+ "#{test_size} #{test_str}"
9
+ end
10
+
11
+ def self.case_str(testcase_size)
12
+ testcase_str = testcase_size > 1 ? 'cases' : 'case'
13
+ "#{testcase_size} #{testcase_str}"
14
+ end
15
+
16
+ def self.error_str(size)
17
+ str = 'error'
18
+ str += 's' if size > 0
19
+ "#{size} #{str}"
20
+ end
21
+
22
+ def self.setUpGlobal(testcase)
23
+ test_size = testcase.inject(0) { |sum, t| sum += t.test.size}
24
+ Report.tag :global, test_str(test_size) + " from " + case_str(testcase.size)
25
+ Report.tag :global, "Global test environment set-up."
26
+ puts ''
27
+ end
28
+
29
+ def self.tearDownGlobal
30
+ Report.tag :global, "Global test environment tear-down."
31
+ end
32
+
33
+ def self.setUpTestCase(name, size)
34
+ Report.tag :line, test_str(size) + " from #{name}"
35
+ Report.tag :testcase, "#{name} set-up."
36
+ end
37
+
38
+ def self.tearDownTestCase(name, size, elapsed)
39
+ Report.tag :testcase, "#{name} tear-down."
40
+ Report.tag :line, test_str(size) + " executed from #{name} (#{elapsed} seconds)"
41
+ puts ''
42
+ end
43
+
44
+ def self.test(casename, name)
45
+ Report.left :run, "#{casename}.#{name}"
46
+ end
47
+
48
+ def self.test_success(casename, name)
49
+ Report.right :ok, "#{casename}.#{name}"
50
+ end
51
+
52
+ def self.test_fail(casename, name)
53
+ Report.right :fail, "#{casename}.#{name}"
54
+ end
55
+
56
+ def self.print_result(gresult)
57
+ # collect TestCase failures
58
+ cases = []
59
+ gresult.result.each do |result|
60
+ # test
61
+ test = []
62
+ result.result.each do |r|
63
+ unless r.empty? && r.ba_empty?
64
+ test << r
65
+ end
66
+ end
67
+
68
+ unless result.ba_empty? && test.empty?
69
+ cases << {
70
+ :case => result,
71
+ :test => test,
72
+ }
73
+ end
74
+ end
75
+
76
+ global_failed = !gresult.ba_empty?
77
+
78
+ #####################
79
+ # Output error status
80
+ split = global_failed || !cases.empty?
81
+ Report.split if split
82
+
83
+ # Report Global
84
+ if global_failed
85
+ err = []
86
+ err << 'before' unless gresult.before_failure.empty?
87
+ err << 'after' unless gresult.after_failure.empty?
88
+ Report.tag :global, "Global failure"
89
+ Report.tag :empty, " " + err.join(', ')
90
+ end
91
+
92
+ # Report TestCase
93
+ unless cases.empty?
94
+ str = cases.inject([]) {|a, s| a << s[:case].name }.join(', ')
95
+ Report.tag :testcase, "#{case_str(cases.size)}"
96
+ Report.tag :empty, " #{str}"
97
+ end
98
+
99
+ # Report test each cases
100
+ cases.each do |x|
101
+ c = x[:case]
102
+ t = x[:test]
103
+ err = []
104
+ err << 'beforeCase' unless c.before_failure.empty?
105
+ err << 'afterCase' unless c.after_failure.empty?
106
+ err = t.inject(err) {|a, s| a << s.name }
107
+ Report.tag :test, "#{c.name}: #{error_str(err.size)}"
108
+ Report.tag :empty, " #{err.join(', ')}"
109
+ end
110
+
111
+
112
+
113
+ ##################
114
+ # Output failures
115
+ Report.split if split
116
+
117
+ # Global before/after failure
118
+ if global_failed
119
+ # before
120
+ Report.tag :fail, 'Global.before' unless gresult.before_failure.empty?
121
+ print_failure(gresult.before_failure)
122
+
123
+ # after
124
+ Report.tag :fail, 'Global.after' unless gresult.after_failure.empty?
125
+ print_failure(gresult.after_failure)
126
+ end
127
+
128
+ # Report testcase and test failures
129
+ cases.each do |x|
130
+ c = x[:case]
131
+ casename = c.name
132
+ #Report.tag :testcase, "#{casename}"
133
+ # testcase before/after
134
+ Report.tag :fail, "#{casename}.beforeCase" unless c.before_failure.empty?
135
+ print_failure(c.before_failure)
136
+ Report.tag :fail, "#{casename}.afterCase" unless c.after_failure.empty?
137
+ print_failure(c.after_failure)
138
+
139
+ # test before/test/after
140
+ x[:test].each do |t|
141
+ name = t.name
142
+ Report.tag :fail, "#{casename}.#{name}.before" unless t.before_failure.empty?
143
+ print_failure(t.before_failure)
144
+ Report.tag :fail, "#{casename}.#{name}" unless t.failure.empty?
145
+ print_failure(t)
146
+ Report.tag :fail, "#{casename}.#{name}.after" unless t.after_failure.empty?
147
+ print_failure(t.after_failure)
148
+ end
149
+ end
150
+
151
+ puts ""
152
+ puts "Finished in #{gresult.elapsed} seconds"
153
+ puts "--------------------------------"
154
+ Report.tag :passed, gresult.passed
155
+ Report.tag :failed, gresult.failed
156
+ Report.tag :tested, gresult.executed
157
+ Report.tag :untest, gresult.untested
158
+ end
159
+
160
+ def self.print_failure(failure)
161
+ failure.failure.each do |msg|
162
+ msg.print
163
+ end
164
+ end
165
+ end # class Progress
166
+ end # module DTest