tet 1.4.1 → 1.5.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.
- checksums.yaml +4 -4
- data/lib/tests.rb +32 -58
- data/lib/tet.rb +221 -158
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97ce2949a018ea80d5012fc9dd00268098dd0ece
|
4
|
+
data.tar.gz: fefa2a09bfc0adf45730b15be19785812e75d535
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c60b11a370e690471156621bce8884a3daaaa13bab34e0bb80cb0a9c8e52fb11b24d9f2231f42ff80d0aa65804e25000069095324bbfb3f0f2d125d5dfc0dc6c
|
7
|
+
data.tar.gz: '0719db9dedda2b62d2a413dd3b3342a28c87cff1855fb41b300e165aef30d2998156e67c3b153f1b7ef765f1cf1831e920b570efa613fb056e41d323aba95ab5'
|
data/lib/tests.rb
CHANGED
@@ -1,77 +1,51 @@
|
|
1
|
-
# Copyright (C)
|
1
|
+
# Copyright (C) 2017 Colin Fulton
|
2
2
|
# All rights reserved.
|
3
3
|
#
|
4
4
|
# This software may be modified and distributed under the
|
5
5
|
# terms of the three-clause BSD license. See LICENSE.txt
|
6
6
|
# (located in root directory of this project) for details.
|
7
7
|
|
8
|
-
require_relative
|
9
|
-
require_relative "./test_helpers"
|
8
|
+
require_relative './tet'
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
class IntendedError < StandardError; end
|
11
|
+
class IntendedErrorChild < IntendedError; end
|
13
12
|
|
14
|
-
|
15
|
-
assert("passing returns true") { result.equal?(true) }
|
13
|
+
puts ".!?!!?!!!!!!.!.??????? (expected result)"
|
16
14
|
|
17
|
-
|
18
|
-
|
15
|
+
assert('true blocks pass') { true }
|
16
|
+
assert('false blocks fail') { false }
|
19
17
|
|
20
|
-
|
21
|
-
assert("falsy blocks fail") { nil }
|
22
|
-
assert("empty assertions fail") { }
|
18
|
+
assert('errors are caught') { raise IntendedError }
|
23
19
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
20
|
+
group '#group' do
|
21
|
+
assert('first failure') { false }
|
22
|
+
assert('second failure') { false }
|
28
23
|
|
29
|
-
|
30
|
-
assert("errors are caught and count as failures") { not_a_method }
|
31
|
-
end
|
24
|
+
group('errors are caught') { raise IntendedError }
|
32
25
|
end
|
33
26
|
|
34
|
-
group
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
assert(
|
41
|
-
|
42
|
-
return_value = should_err { group { raise "Example Error" } }
|
43
|
-
assert("returns nil when the block throws an error") { return_value.nil? }
|
44
|
-
|
45
|
-
group 'fails when empty' do
|
46
|
-
should_fail { group('group without content') { } }
|
47
|
-
end
|
48
|
-
|
49
|
-
group "can have classes for names" do
|
50
|
-
group String do
|
51
|
-
should_fail { assert { false } }
|
52
|
-
end
|
53
|
-
end
|
27
|
+
group 'anything can be a label' do
|
28
|
+
assert(:a_symbol) { false }
|
29
|
+
assert(Class) { false }
|
30
|
+
assert(Enumerable) { false }
|
31
|
+
assert(true) { false }
|
32
|
+
assert({x: 1, y: 2}) { false }
|
33
|
+
assert(nil) { false }
|
54
34
|
end
|
55
35
|
|
56
|
-
group
|
57
|
-
err(
|
58
|
-
|
59
|
-
err("specify an exception class", expect: NameError) { not_a_method }
|
60
|
-
err("specify a parent exception class", expect: Exception) { not_a_method }
|
36
|
+
group '#err' do
|
37
|
+
err('errors pass') { raise IntendedError }
|
38
|
+
err('non-errors fail') { true }
|
61
39
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
result = should_fail { err { 1 + 1 } }
|
66
|
-
assert("failing returns false") { result.equal?(false) }
|
67
|
-
|
68
|
-
should_fail do
|
69
|
-
err("no errors fails") { 1 + 1 }
|
70
|
-
err("empty assertions fail") { }
|
71
|
-
err("wrong class fails", expect: ArgumentError) { not_a_method }
|
40
|
+
err('correct errors pass', expect: IntendedError) { raise IntendedError }
|
41
|
+
err('incorrect errors err', expect: IntendedErrorChild) { raise IntendedError }
|
42
|
+
end
|
72
43
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
44
|
+
group 'should not nest' do
|
45
|
+
assert('#assert in #assert') { assert { true } }
|
46
|
+
assert('#group in #assert') { group('') { 'empty #group' } }
|
47
|
+
assert('#err in #assert') { err { raise IntendedError } }
|
48
|
+
err('#assert in #err') { assert { true } }
|
49
|
+
err('#group in #err') { group('') { 'empty #group' } }
|
50
|
+
err('#err in #err') { err { raise IntendedError } }
|
77
51
|
end
|
data/lib/tet.rb
CHANGED
@@ -1,222 +1,285 @@
|
|
1
|
-
# Copyright (C)
|
1
|
+
# Copyright (C) 2017 Colin Fulton
|
2
2
|
# All rights reserved.
|
3
3
|
#
|
4
4
|
# This software may be modified and distributed under the
|
5
5
|
# terms of the three-clause BSD license. See LICENSE.txt
|
6
6
|
# (located in root directory of this project) for details.
|
7
7
|
|
8
|
-
# Label
|
9
|
-
def group
|
10
|
-
|
8
|
+
# Label a block of tests
|
9
|
+
def group label, &block
|
10
|
+
Tet.run(
|
11
|
+
label: label,
|
12
|
+
test: block
|
13
|
+
)
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
# Assert that a block will return a truthy value
|
17
|
+
def assert label = '', &block
|
18
|
+
Tet.run(
|
19
|
+
label: label,
|
20
|
+
test: block,
|
21
|
+
no_nest: true,
|
22
|
+
truthy: -> { Tet.passed },
|
23
|
+
falsy: -> { Tet.failed }
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Assert that a block will err
|
28
|
+
def err label = '', expect: StandardError, &block
|
29
|
+
Tet.run(
|
30
|
+
label: label,
|
31
|
+
test: block,
|
32
|
+
no_nest: true,
|
33
|
+
truthy: -> { Tet.failed },
|
34
|
+
falsy: -> { Tet.failed },
|
35
|
+
error: ->(caught) {
|
36
|
+
(expect >= caught.class) ? Tet.passed : Tet.erred(caught, expect)
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
# A namespace for all of the helper methods and classes.
|
44
|
+
module Tet
|
45
|
+
# Print all the reports after all the tests have run
|
46
|
+
at_exit do
|
47
|
+
unless Stats.empty?
|
48
|
+
puts
|
49
|
+
puts_report 'Failures', Messages.failure_report
|
50
|
+
puts_report 'Exceptions', Messages.error_report
|
51
|
+
puts_report 'Statistics', Stats.report
|
17
52
|
end
|
18
53
|
end
|
19
|
-
end
|
20
54
|
|
21
|
-
# Declare that a block will return a truthy value.
|
22
|
-
# If it doesn't or if it has an error, the test will be logged as failing.
|
23
|
-
def assert name = nil
|
24
|
-
Tet.in_group(name) do
|
25
|
-
result = false
|
26
55
|
|
27
|
-
Tet.stop_nesting("NESTED IN ASSERT: #{name}") do
|
28
|
-
begin
|
29
|
-
result = yield
|
30
56
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
57
|
+
# An exception class to distinguish errors from incorrectly written tests
|
58
|
+
class TestError < StandardError; end
|
59
|
+
|
60
|
+
# Object oriented way to do string formatting
|
61
|
+
module StringFormatting
|
62
|
+
refine String do
|
63
|
+
def indent
|
64
|
+
gsub(/^/, ' ')
|
39
65
|
end
|
40
66
|
|
41
|
-
|
67
|
+
def to_label
|
68
|
+
self
|
69
|
+
end
|
42
70
|
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Declare that a block will have an error.
|
46
|
-
# If it doesn't the test will be logged as failing.
|
47
|
-
def err name = nil, expect: StandardError
|
48
|
-
Tet.in_group(name) do
|
49
|
-
result = false
|
50
71
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
Tet.log_fail
|
55
|
-
rescue StandardError => error_object
|
56
|
-
if expect >= error_object.class
|
57
|
-
result = true
|
58
|
-
Tet.log_pass
|
59
|
-
else
|
60
|
-
Tet.log_wrong_error(expected: expect, got: error_object)
|
61
|
-
end
|
72
|
+
refine Module do
|
73
|
+
def to_label
|
74
|
+
name
|
62
75
|
end
|
63
76
|
end
|
64
77
|
|
65
|
-
|
78
|
+
refine Object do
|
79
|
+
def to_label
|
80
|
+
inspect
|
81
|
+
end
|
82
|
+
end
|
66
83
|
end
|
67
|
-
end
|
68
84
|
|
69
|
-
|
70
|
-
|
71
|
-
PassChar = "."
|
72
|
-
FailChar = "F"
|
73
|
-
ErrorChar = "!"
|
74
|
-
Indent = " "
|
75
|
-
|
76
|
-
@messages = []
|
77
|
-
@current_group = []
|
78
|
-
@test_count = 0
|
79
|
-
@fail_count = 0
|
80
|
-
@err_count = 0
|
81
|
-
@nested_ban = false
|
85
|
+
using StringFormatting
|
86
|
+
|
82
87
|
|
88
|
+
|
89
|
+
# Helpers for building test methods
|
83
90
|
class << self
|
84
|
-
|
91
|
+
# Call when an assertion has passed
|
92
|
+
def passed
|
93
|
+
Stats.passed
|
94
|
+
end
|
85
95
|
|
86
|
-
#
|
87
|
-
def
|
88
|
-
|
96
|
+
# Call when an assertion has failed
|
97
|
+
def failed
|
98
|
+
Stats.failed
|
99
|
+
Messages.failed
|
100
|
+
end
|
89
101
|
|
90
|
-
|
102
|
+
# Call when an assertion has erred
|
103
|
+
def erred caught, expected = nil
|
104
|
+
Stats.erred
|
105
|
+
Messages.erred(caught, expected)
|
106
|
+
end
|
91
107
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
108
|
+
# Run a block as a test
|
109
|
+
def run label:,
|
110
|
+
truthy: -> { },
|
111
|
+
falsy: -> { },
|
112
|
+
error: ->(caught) { erred(caught) },
|
113
|
+
no_nest: false,
|
114
|
+
test:
|
115
|
+
|
116
|
+
Messages.with_label(label) do
|
117
|
+
nesting_guard(no_nest) do
|
118
|
+
begin
|
119
|
+
test.call ? truthy.call : falsy.call
|
120
|
+
rescue TestError => caught
|
121
|
+
erred(caught)
|
122
|
+
rescue StandardError => caught
|
123
|
+
error.call(caught)
|
124
|
+
end
|
97
125
|
end
|
98
|
-
rescue StandardError => error_object
|
99
|
-
log_error error_object, "ERROR IN GROUP"
|
100
126
|
end
|
101
127
|
|
102
|
-
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
103
132
|
|
104
|
-
|
133
|
+
# Print out a report to stdout
|
134
|
+
def puts_report header, content
|
135
|
+
unless content.empty?
|
136
|
+
puts
|
137
|
+
puts "#{header}:"
|
138
|
+
puts content.indent
|
139
|
+
end
|
105
140
|
end
|
106
141
|
|
107
|
-
|
108
|
-
|
142
|
+
# Check and set a flag to prevent test blocks from nesting
|
143
|
+
def nesting_guard guard_state
|
144
|
+
raise TestError, 'assertions can not be nested' if @nesting_banned
|
145
|
+
@nesting_banned = guard_state
|
109
146
|
yield
|
110
|
-
|
147
|
+
ensure
|
148
|
+
@nesting_banned = false
|
111
149
|
end
|
150
|
+
end
|
112
151
|
|
113
|
-
# Log a passing test.
|
114
|
-
def log_pass
|
115
|
-
print_now PassChar
|
116
152
|
|
117
|
-
@test_count += 1
|
118
|
-
end
|
119
153
|
|
120
|
-
|
121
|
-
|
122
|
-
|
154
|
+
# Tracks and reports statistics about the tests that have run
|
155
|
+
module Stats
|
156
|
+
Counts = { passed: 0, failed: 0, erred: 0 }
|
157
|
+
Marks = { passed: ?., failed: ?!, erred: ?? }
|
123
158
|
|
124
|
-
|
125
|
-
|
159
|
+
class << self
|
160
|
+
# Call when an assertion has passed
|
161
|
+
def passed
|
162
|
+
log :passed
|
163
|
+
end
|
126
164
|
|
165
|
+
# Call when an assertion has failed
|
166
|
+
def failed
|
167
|
+
log :failed
|
168
|
+
end
|
127
169
|
|
128
|
-
|
129
|
-
|
130
|
-
|
170
|
+
# Call when an assertion has erred
|
171
|
+
def erred
|
172
|
+
log :erred
|
173
|
+
end
|
131
174
|
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
group.shift
|
136
|
-
current_section = current_section.last
|
175
|
+
# Returns true if no statistics have been logged
|
176
|
+
def empty?
|
177
|
+
Counts.values.inject(&:+).zero?
|
137
178
|
end
|
138
179
|
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
180
|
+
# Returns a string with statistics about the tests that have been run
|
181
|
+
def report
|
182
|
+
errors = Counts[:erred]
|
183
|
+
fails = Counts[:failed]
|
184
|
+
passes = Counts[:passed]
|
185
|
+
|
186
|
+
output = []
|
187
|
+
output << "Errors: #{errors}" unless errors.zero?
|
188
|
+
output << "Failed: #{fails}"
|
189
|
+
output << "Passed: #{passes}"
|
190
|
+
|
191
|
+
output.join(?\n)
|
144
192
|
end
|
145
193
|
|
146
|
-
|
147
|
-
current_section.concat(messages)
|
148
|
-
end
|
194
|
+
private
|
149
195
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
log_fail *messages, *format_error(error_object), letter: ErrorChar
|
154
|
-
end
|
196
|
+
# Log an event and print a mark to show progress is being made on tests
|
197
|
+
def log type
|
198
|
+
Counts[type] += 1
|
155
199
|
|
156
|
-
|
157
|
-
|
158
|
-
|
200
|
+
print Marks[type]
|
201
|
+
$stdout.flush
|
202
|
+
end
|
159
203
|
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
# Tracks and reports messages for each test
|
208
|
+
module Messages
|
209
|
+
Labels = []
|
210
|
+
Results = []
|
211
|
+
Errors = []
|
212
|
+
|
213
|
+
class << self
|
214
|
+
# Add a label to all subsequent messages
|
215
|
+
def with_label label
|
216
|
+
Labels.push(label)
|
217
|
+
yield
|
218
|
+
ensure
|
219
|
+
Labels.pop
|
220
|
+
end
|
221
|
+
|
222
|
+
# Call when an assertion has failed
|
223
|
+
def failed
|
224
|
+
add_result!
|
225
|
+
end
|
160
226
|
|
161
|
-
|
162
|
-
|
163
|
-
|
227
|
+
# Call when an assertion has passed
|
228
|
+
def erred caught, expected = nil
|
229
|
+
number = Stats::Counts[:erred]
|
230
|
+
label = "EXCEPTION_#{number}"
|
231
|
+
label << " (expected: #{expected})" if expected
|
164
232
|
|
165
|
-
|
233
|
+
with_label(label) { add_result! }
|
166
234
|
|
167
|
-
|
168
|
-
print "all good!"
|
169
|
-
else
|
170
|
-
print "#{plural @fail_count, 'fail'}"
|
171
|
-
print " (including #{plural @err_count, 'error'})" unless @err_count.zero?
|
235
|
+
Errors.push(error_message(caught, number))
|
172
236
|
end
|
173
237
|
|
174
|
-
|
238
|
+
# Returns a string with details about all failed tests
|
239
|
+
def failure_report
|
240
|
+
Results.join(?\n)
|
241
|
+
end
|
175
242
|
|
176
|
-
|
177
|
-
|
178
|
-
|
243
|
+
# Returns a string with details about all errors
|
244
|
+
def error_report
|
245
|
+
Errors.join("\n\n")
|
179
246
|
end
|
180
|
-
end
|
181
247
|
|
182
|
-
|
248
|
+
private
|
183
249
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
]
|
193
|
-
end
|
250
|
+
# Add a message for a new result using the current labels
|
251
|
+
def add_result!
|
252
|
+
Results.push(
|
253
|
+
Labels.map { |raw| raw.to_label }
|
254
|
+
.reject(&:empty?)
|
255
|
+
.join(' :: ')
|
256
|
+
)
|
257
|
+
end
|
194
258
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
.map { |part| indent(part, amount + 1) }
|
205
|
-
.join("\n")
|
259
|
+
def format_label label
|
260
|
+
case label
|
261
|
+
when Module
|
262
|
+
label.name
|
263
|
+
when String
|
264
|
+
label.to_s
|
265
|
+
else
|
266
|
+
label.inspect
|
267
|
+
end
|
206
268
|
end
|
207
|
-
end
|
208
269
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
270
|
+
# Format an error into a message string
|
271
|
+
def error_message error, number
|
272
|
+
error_message = error.to_s
|
273
|
+
error_class = error.class.to_s
|
274
|
+
error_message << " (#{error.class})" if error_message != error_class
|
275
|
+
|
276
|
+
summary = "#{number}. #{error_message}"
|
277
|
+
details = error.backtrace
|
278
|
+
.reject { |line| line.start_with?(__FILE__) }
|
279
|
+
.map { |line| line.indent }
|
213
280
|
|
214
|
-
|
215
|
-
|
216
|
-
print string
|
217
|
-
$stdout.flush
|
281
|
+
details.unshift(summary).join(?\n)
|
282
|
+
end
|
218
283
|
end
|
219
284
|
end
|
220
|
-
|
221
|
-
at_exit { Tet.render_result }
|
222
285
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Colin Fulton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A very minimal test framework designed for simple projects. A couple
|
14
14
|
of features, relatively nice looking output, and nothing else. Does the world need
|
@@ -40,7 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
40
|
version: '0'
|
41
41
|
requirements: []
|
42
42
|
rubyforge_project:
|
43
|
-
rubygems_version: 2.
|
43
|
+
rubygems_version: 2.6.8
|
44
44
|
signing_key:
|
45
45
|
specification_version: 4
|
46
46
|
summary: Barely a test framework
|