dtest 0.0.1

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