tet 1.5.1 → 2.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/tests.rb +54 -30
  3. data/lib/tet.rb +106 -261
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 71c11439cad3f5c481d5242bc65ade2c8780804f
4
- data.tar.gz: 6a410a5f775aa07096ed3b56e06cf85baddbdb33
3
+ metadata.gz: b9c5b7e8d1240ae647425b19d13b5307bedf74a5
4
+ data.tar.gz: 930d474cc6a5f09b57bc590a5d8ef97a00c7622d
5
5
  SHA512:
6
- metadata.gz: 4bcdfe86d7625c0a0a259a75f61b76165f0bb5dcb20400edaa950207553eaa451af2a219f5b7075c56caabdd46abf0fc573d006e2a3682b70fad43d41027bc2f
7
- data.tar.gz: b24180e3e816c9e165d857547072f73e5f4d2a0cbe6d9f5a010e4017a9bf1a95392374228110d45b05241442e3793ebe12bbb9a7a3e8625dd9020c076f17e624
6
+ metadata.gz: 8d6f765630b953aa0d691d4aa09d02297e4460eb48bd71d4841b1ca7393a39bb88429541bae25494798b69fc26cd10b0aab7c578fa527edc092dc5db67ce8746
7
+ data.tar.gz: d5ccdc37e41471a2ad0cf5d2cf6c4e029db2bae7f1db7d8b8346091b797fe90143a601d74adce29e56465cc241fda87393d47a03f0ef465450d7c5636dd5f785
@@ -7,45 +7,69 @@
7
7
 
8
8
  require_relative './tet'
9
9
 
10
- class IntendedError < StandardError; end
11
- class IntendedErrorChild < IntendedError; end
10
+ SHOW_EXPECTED = false
12
11
 
13
- puts ".!?!!?!!!!!!.!.??????? (expected result)"
12
+ def prefix string
13
+ if SHOW_EXPECTED
14
+ TetCore.break_after_print
15
+ print string
16
+ end
17
+ end
14
18
 
15
- assert('true blocks pass') { true }
16
- assert('false blocks fail') { false }
19
+ def pass; prefix 'PASS = '; end
20
+ def fail; prefix 'FAIL = '; end
21
+ def err; prefix 'ERROR = '; end
17
22
 
18
- assert('errors are caught') { raise IntendedError }
23
+ def test_between
24
+ assert('Intentional failure') { false }
25
+ yield
26
+ assert('') { true }
27
+ yield
28
+ assert('Intentional failure') { false }
29
+ end
19
30
 
20
- group '#group' do
21
- assert('first failure') { false }
22
- assert('second failure') { false }
31
+ class IntendedError < StandardError; end
23
32
 
24
- group('errors are caught') { raise IntendedError }
25
- end
33
+ group('#assert') do
34
+ pass; assert('True assertions pass') { true }
35
+ fail; assert('False assertions fail') { false }
36
+ err; assert('Errors are caught') { raise IntendedError.new('error message') }
26
37
 
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 }
38
+ puts 'Multiple passes in a row show on one line:'
39
+ assert('') { true }
40
+ assert('') { true }
41
+ assert('') { true }
34
42
  end
35
43
 
36
- group '#err' do
37
- err('errors pass') { raise IntendedError }
38
- err('non-errors fail') { true }
44
+ group('#group') do
45
+ group('Can nest') do
46
+ fail; assert('Intentional failure') { false }
47
+ end
39
48
 
40
- err('correct errors pass', expect: IntendedError) { raise IntendedError }
41
- err('incorrect errors err', expect: IntendedErrorChild) { raise IntendedError }
49
+ group('Can have classes as names') do
50
+ group(String) do
51
+ fail; assert('Intentional failure') { false }
52
+ end
53
+ end
42
54
  end
43
55
 
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 } }
56
+ group('#error') do
57
+ fail; error('True errors fail', expect: StandardError) { true }
58
+ fail; error('False errors fail', expect: StandardError) { false }
59
+
60
+ err; error('Unexpected errors err', expect: NoMethodError) do
61
+ raise IntendedError.new('should expect different error')
62
+ end
63
+
64
+ pass; error('Expected errors pass', expect: IntendedError) do
65
+ raise IntendedError.new('SHOULD NOT BE SEEN')
66
+ end
51
67
  end
68
+
69
+ puts
70
+ puts "Testing #puts between different results:"
71
+ test_between { puts 'Test of #puts' }
72
+
73
+ puts
74
+ puts "Testing #p between different results:"
75
+ test_between { p 'Test of #p' }
data/lib/tet.rb CHANGED
@@ -5,299 +5,144 @@
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 a block of tests.
9
- def group label, &block
10
- Tet.run(
11
- label: label,
12
- test: block
13
- )
14
- end
15
-
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) do
36
- (expect >= caught.class) ? Tet.passed : Tet.erred(caught, expect)
37
- end
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.
8
+ module TetCore
9
+ INDENT_DEPTH = 4
10
+ SEPERATOR = ' | '
11
+ FAIL_HEADER = 'FAIL: '
12
+ ERROR_HEADER = 'ERROR: '
13
+
14
+ @prev_printed = false
15
+ @messages = []
16
+ @counts = Struct.new(:tests, :fails, :errors)
17
+ .new(0, 0, 0)
18
+
19
+ # Output totals at the end of the test run.
46
20
  at_exit do
47
- puts
48
- puts_report 'Failures', failure_report
49
- puts_report 'Exceptions', error_report
50
- puts_report 'Statistics', statistics_report
21
+ puts "\nTests: #{@counts.tests} Fails: #{@counts.fails} Errors: #{@counts.errors}"
51
22
  end
52
23
 
53
-
54
-
55
- # An exception class to distinguish errors from incorrectly written tests.
56
- class TestError < StandardError; end
57
-
58
-
59
-
60
- # Object oriented ways to do string formatting.
61
- module StringFormatting
62
- refine String do
63
- def indent
64
- gsub(/^/, ' ')
65
- end
66
-
67
- def to_label_string
68
- self
69
- end
70
- end
71
-
72
- refine Module do
73
- def to_label_string
74
- name
75
- end
24
+ class << self
25
+ # Record an assertion.
26
+ def assert
27
+ @counts.tests += 1
76
28
  end
77
29
 
78
- refine Object do
79
- def to_label_string
80
- inspect
81
- end
30
+ # Record a passing assertion.
31
+ def pass
32
+ checked_print ?.
82
33
  end
83
- end
84
-
85
- using StringFormatting
86
34
 
87
-
88
-
89
- # Helpers for building test methods.
90
- class << self
91
- # Call when an assertion has passed.
92
- def passed
93
- print_now ?.
94
- Data.add_note(:pass)
35
+ # Record a failing assertion.
36
+ def fail
37
+ @counts.fails += 1
38
+ puts_failure_data(header: FAIL_HEADER)
95
39
  end
96
40
 
97
- # Call when an assertion has failed.
98
- def failed
99
- print_now ?!
100
- Data.add_note(:fail)
41
+ # Run a block under a certain name, catching and printing errors.
42
+ def group name
43
+ @messages << name
44
+ yield
45
+ rescue StandardError => caught
46
+ @counts.errors += 1
47
+ puts_failure_data(
48
+ header: ERROR_HEADER,
49
+ additional_info: error_to_info_array(caught)
50
+ )
51
+ ensure
52
+ @messages.pop
101
53
  end
102
54
 
103
- # Call when an assertion has erred.
104
- def erred caught, expected = nil
105
- print_now ??
106
- Data.add_note(:error, caught: caught, expected: expected)
55
+ # Add a line break if the last output was a #print instead of a #puts.
56
+ def break_after_print
57
+ checked_puts if @prev_printed
107
58
  end
108
59
 
109
- # Run a block as a test.
110
- def run label:,
111
- truthy: -> { },
112
- falsy: -> { },
113
- error: ->(caught) { erred(caught) },
114
- no_nest: false,
115
- test:
116
-
117
- Data.with_label(label) do
118
- nesting_guard(no_nest) do
119
- begin
120
- test.call ? truthy.call : falsy.call
121
- rescue TestError => caught
122
- erred(caught)
123
- rescue StandardError => caught
124
- error.call(caught)
125
- end
126
- end
127
- end
128
-
129
- nil
130
- end
60
+ # Alias original #puts and #p methods.
61
+ alias_method :real_puts, :puts
62
+ alias_method :real_p, :p
63
+ public :real_puts
64
+ public :real_p
131
65
 
132
- private
66
+ private
133
67
 
134
- # Print out a string immediately. Calling #print alone may have a delay.
135
- def print_now string
136
- print string
137
- $stdout.flush
138
- end
68
+ # Output data related to a failure.
69
+ def puts_failure_data header:, additional_info: []
70
+ break_after_print
71
+ checked_puts header + @messages.join(SEPERATOR)
139
72
 
140
- # Print out a report to stdout.
141
- def puts_report header, content
142
- unless content.empty?
143
- puts
144
- puts "#{header}:"
145
- puts content.indent
73
+ additional_info.each.with_index do |text, index|
74
+ checked_puts indent(text, index + 1)
146
75
  end
147
76
  end
148
77
 
149
- # Render a string with details about all of the test run.
150
- def statistics_report
151
- pass_count = Data.count_type(:pass)
152
- fail_count = Data.count_type(:fail)
153
- error_count = Data.count_type(:error)
154
-
155
- output = []
156
- output << "Errors: #{error_count}" unless error_count.zero?
157
- output << "Failed: #{fail_count}" unless fail_count.zero?
158
- output << "Passed: #{pass_count}" unless pass_count.zero?
159
-
160
- output.join(?\n)
78
+ # Perform a #puts and then set a flag for future line breaks.
79
+ def checked_puts object = ''
80
+ real_puts object
81
+ @prev_printed = false
161
82
  end
162
83
 
163
- # Render a string with details about all of the failures encountered.
164
- def failure_report
165
- errors = Data.select_type(:error)
166
-
167
- Data.select_type(:fail, :error)
168
- .map do |node|
169
- node.label
170
- .tap do |label|
171
- if node.type == :error
172
- error_note = "EXCEPTION_#{1 + errors.index(node)}"
173
- expected = node.data[:expected]
174
- error_note << " (expected: #{expected}" if expected
175
-
176
- label << error_note
177
- end
178
- end
179
- .map(&:to_label_string)
180
- .join(' :: ')
181
- end
182
- .join(?\n)
84
+ # Perform a #print and then set a flag for future line breaks.
85
+ def checked_print object = ''
86
+ print object
87
+ @prev_printed = true
183
88
  end
184
89
 
185
- # Render a string with details about all of the errors encountered.
186
- def error_report
187
- Data.select_type(:error)
188
- .map.with_index do |node, index|
189
- error = node.data[:caught]
190
- message = error.to_s
191
- message << " (#{error.class})" if message != error.class.to_s
192
- summary = "#{index + 1}. #{message}"
193
-
194
- error.backtrace
195
- .reject { |line| line.start_with?(__FILE__) }
196
- .map { |line| line.indent }
197
- .unshift(summary)
198
- .join(?\n)
199
- end
200
- .join("\n\n")
90
+ # Indent each line of a given string by a given amount.
91
+ def indent string, amount
92
+ indent_string = ' ' * amount * INDENT_DEPTH
93
+ string.gsub(/^/, indent_string)
201
94
  end
202
95
 
203
- # Check and set a flag to prevent test blocks from nesting
204
- def nesting_guard guard_state
205
- raise TestError, 'assertions can not be nested' if @nesting_banned
206
- @nesting_banned = guard_state
207
- yield
208
- ensure
209
- @nesting_banned = false
96
+ # Convert an error object into an array of strings describing the error.
97
+ def error_to_info_array error
98
+ [
99
+ "#{error.class}: #{error.message}",
100
+ error.backtrace
101
+ .reject { |path| path.match?(/^#{__FILE__}:/) }
102
+ .join(?\n)
103
+ ]
210
104
  end
211
105
  end
106
+ end
212
107
 
108
+ # Group together assertions and groups under a given name.
109
+ def group name
110
+ TetCore.group(name) { yield }
111
+ end
213
112
 
214
- # Because tests are represented as series of nested groups containing
215
- # assertions, the test data is stored in a tree.
216
- module Tree
217
- module Basics
218
- include Enumerable
219
-
220
- # Create and append a new node to this node, returning the new node.
221
- def append_node type, data
222
- Node.new(type: type, data: data, parent: self)
223
- .tap { |node| @children << node }
224
- end
225
-
226
- # Recursively iterate over each node in the test data tree in the order
227
- # that they were added.
228
- def each &block
229
- @children.each do |node|
230
- block.call(node)
231
- node.each(&block)
232
- end
233
- end
234
- end
235
-
236
- # The root of the tree with methods to terminate Node methods which search
237
- # up the tree, as well as methods for enumerating over the test data, adding
238
- # labels and adding nodes.
239
- class Root
240
- include Basics
241
-
242
- def initialize
243
- @children = []
244
- @current_node = self
245
- end
246
-
247
- # Terminate Node#label.
248
- def label
249
- []
250
- end
251
-
252
- # Run a block where any notes added will be inside a new label node.
253
- def with_label label
254
- previous = @current_node
255
- @current_node = add_note(:label, label)
256
- yield
257
- ensure
258
- @current_node = previous
259
- self
260
- end
261
-
262
- # Add a node to note some data within the node that is currently active.
263
- def add_note type, data = nil
264
- @current_node.append_node(type, data)
265
- end
266
-
267
- # Get an array of all nodes of some type in the order they were added.
268
- def select_type *types
269
- select { |node| types.include?(node.type) }
270
- end
271
-
272
- # Count the number of nodes of a given type.
273
- def count_type type
274
- select_type(type).size
275
- end
276
- end
277
-
278
- # A node to store data about that some point in the test tree
279
- class Node
280
- include Basics
281
- attr_reader :data, :type
113
+ # Assert that a given block will return true.
114
+ def assert name
115
+ TetCore.group(name) do
116
+ TetCore.assert
282
117
 
283
- def initialize parent:, type:, data: nil
284
- @type = type
285
- @data = data
286
- @parent = parent
287
- @children = []
288
- end
118
+ yield ? TetCore.pass : TetCore.fail
119
+ end
120
+ end
289
121
 
290
- # Generate the array of labels that identify this point in the tree.
291
- def label
292
- if @type == :label
293
- @parent.label + [@data]
294
- else
295
- @parent.label
296
- end
297
- end
122
+ # Assert that a given block will raise an expected error.
123
+ def error name, expect:
124
+ TetCore.group(name) do
125
+ begin
126
+ TetCore.assert
127
+ yield
128
+ rescue expect
129
+ TetCore.pass
130
+ else
131
+ TetCore.fail
298
132
  end
299
133
  end
134
+ end
135
+
136
+ # A redefined version of #puts which ensures a line break before the printed
137
+ # object (for easier debugging).
138
+ def puts object = ''
139
+ TetCore.break_after_print
140
+ TetCore.real_puts(object)
141
+ end
300
142
 
301
- # The instance of the Tree data structure that stores all of the test results.
302
- Data = Tree::Root.new
143
+ # A redefined version of #p which ensures a line break before the printed
144
+ # object (for easier debugging).
145
+ def p object
146
+ TetCore.break_after_print
147
+ TetCore.real_p(object)
303
148
  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.5.1
4
+ version: 2.0.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-03-10 00:00:00.000000000 Z
11
+ date: 2017-12-03 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
@@ -32,7 +32,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - ">="
34
34
  - !ruby/object:Gem::Version
35
- version: 2.1.3
35
+ version: 2.4.0
36
36
  required_rubygems_version: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="