sus 0.8.0 → 0.9.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bin/sus +11 -4
- data/bin/sus-parallel +2 -3
- data/lib/sus/assertions.rb +59 -46
- data/lib/sus/base.rb +12 -5
- data/lib/sus/be.rb +7 -1
- data/lib/sus/be_within.rb +2 -2
- data/lib/sus/config.rb +51 -10
- data/lib/sus/expect.rb +2 -2
- data/lib/sus/file.rb +5 -3
- data/lib/sus/filter.rb +1 -2
- data/lib/sus/it.rb +2 -2
- data/lib/sus/let.rb +4 -4
- data/lib/sus/loader.rb +11 -0
- data/lib/sus/output/backtrace.rb +73 -0
- data/lib/sus/output/buffered.rb +34 -9
- data/lib/sus/output/null.rb +6 -0
- data/lib/sus/output/text.rb +5 -0
- data/lib/sus/output.rb +8 -3
- data/lib/sus/receive.rb +26 -6
- data/lib/sus/registry.rb +5 -1
- data/lib/sus/respond_to.rb +8 -5
- data/lib/sus/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -2
- metadata.gz.sig +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91a7f4c78628a0b0dd8486ac342ce0719f54a53b39915ebcc782870dc8e35d49
|
4
|
+
data.tar.gz: e81aaca35038343af7caae894d9b1e03cb75ea34efbd6a68ddcf901426f26097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe9d621882e49b0d320bf7e3e4cdc415d08b2318c09756274f5c9ad1ae60f3914a794418485330e6f99611672c23b348815a8b07f6cb524aa32852b9314f81d0
|
7
|
+
data.tar.gz: 78bd614fb8aa7f6a8ae1d2d2db59958cf12851a0d3f2f199a1737b1f4255e72d1958b40de37c489b29065d7535ef9adb244c43fe57bc5bdc241498b8b3e398b3
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/bin/sus
CHANGED
@@ -4,13 +4,20 @@ require_relative '../lib/sus/config'
|
|
4
4
|
config = Sus::Config.load
|
5
5
|
|
6
6
|
require_relative '../lib/sus'
|
7
|
-
|
8
|
-
assertions = Sus::Assertions.default(output: Sus::Output::Null.new)
|
7
|
+
registry = config.registry
|
9
8
|
|
10
|
-
config.
|
9
|
+
if config.verbose?
|
10
|
+
output = config.output
|
11
|
+
verbose = true
|
12
|
+
else
|
13
|
+
output = Sus::Output::Null.new
|
14
|
+
verbose = false
|
15
|
+
end
|
16
|
+
|
17
|
+
assertions = Sus::Assertions.default(output: output, verbose: verbose)
|
11
18
|
|
12
19
|
config.before_tests(assertions)
|
13
|
-
|
20
|
+
registry.call(assertions)
|
14
21
|
config.after_tests(assertions)
|
15
22
|
|
16
23
|
if assertions.failed.any?
|
data/bin/sus-parallel
CHANGED
@@ -16,10 +16,9 @@ require 'etc'
|
|
16
16
|
count = Etc.nprocessors
|
17
17
|
|
18
18
|
loader = Thread.new do
|
19
|
-
|
20
|
-
config.prepare(filter)
|
19
|
+
registry = config.registry
|
21
20
|
|
22
|
-
|
21
|
+
registry.each do |child|
|
23
22
|
guard.synchronize{progress.expand}
|
24
23
|
jobs << child
|
25
24
|
end
|
data/lib/sus/assertions.rb
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
require_relative 'output'
|
3
3
|
require_relative 'clock'
|
4
4
|
|
5
|
+
require_relative 'output/backtrace'
|
6
|
+
|
5
7
|
module Sus
|
6
8
|
class Assertions
|
7
9
|
def self.default(**options)
|
8
|
-
self.new(**options
|
10
|
+
self.new(**options)
|
9
11
|
end
|
10
12
|
|
11
|
-
def initialize(target: nil, output: Output.buffered, inverted: false, isolated: false, measure: false, verbose: false)
|
13
|
+
def initialize(identity: nil, target: nil, output: Output.buffered, inverted: false, isolated: false, measure: false, verbose: false)
|
14
|
+
# In theory, the target could carry the identity of the assertion group, but it's not really necessary, so we just handle it explicitly and pass it into any nested assertions.
|
15
|
+
@identity = identity
|
12
16
|
@target = target
|
13
17
|
@output = output
|
14
18
|
@inverted = inverted
|
@@ -50,7 +54,7 @@ module Sus
|
|
50
54
|
attr :count
|
51
55
|
|
52
56
|
def inspect
|
53
|
-
"\#<#{self.class} #{
|
57
|
+
"\#<#{self.class} #{@passed.size} passed #{@failed.size} failed #{@deferred.size} deferred>"
|
54
58
|
end
|
55
59
|
|
56
60
|
def total
|
@@ -58,8 +62,6 @@ module Sus
|
|
58
62
|
end
|
59
63
|
|
60
64
|
def print(output, verbose: @verbose)
|
61
|
-
self
|
62
|
-
|
63
65
|
if verbose && @target
|
64
66
|
@target.print(output)
|
65
67
|
output.write(": ")
|
@@ -107,21 +109,19 @@ module Sus
|
|
107
109
|
end
|
108
110
|
|
109
111
|
def assert(condition, message = nil)
|
110
|
-
# sleep 0.5
|
111
|
-
|
112
112
|
@count += 1
|
113
113
|
|
114
114
|
if condition
|
115
115
|
@passed << self
|
116
116
|
|
117
|
-
@
|
118
|
-
@output.puts(:indent, :passed, pass_prefix, message || "assertion")
|
117
|
+
if @inverted || @verbose
|
118
|
+
@output.puts(:indent, :passed, pass_prefix, message || "assertion", Output::Backtrace.first(@identity))
|
119
119
|
end
|
120
120
|
else
|
121
121
|
@failed << self
|
122
122
|
|
123
|
-
@
|
124
|
-
@output.puts(:indent, :failed, fail_prefix, message || "assertion")
|
123
|
+
if !@inverted || @verbose
|
124
|
+
@output.puts(:indent, :failed, fail_prefix, message || "assertion", Output::Backtrace.first(@identity))
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
@@ -138,37 +138,51 @@ module Sus
|
|
138
138
|
|
139
139
|
# This resolves all deferred assertions in order.
|
140
140
|
def resolve!
|
141
|
-
|
142
|
-
block.
|
141
|
+
@output.indented do
|
142
|
+
while block = @deferred.shift
|
143
|
+
block.call(self)
|
144
|
+
end
|
143
145
|
end
|
144
146
|
end
|
145
147
|
|
146
148
|
def fail(error)
|
147
149
|
@failed << self
|
148
150
|
|
149
|
-
|
150
|
-
|
151
|
-
|
151
|
+
lines = error.message.split(/\r?\n/)
|
152
|
+
|
153
|
+
@output.puts(:indent, :error, fail_prefix, "Unhandled exception ", :value, error.class, ":", :reset, " ", lines.shift)
|
154
|
+
|
155
|
+
lines.each do |line|
|
156
|
+
@output.puts(:indent, "| ", line)
|
152
157
|
end
|
158
|
+
|
159
|
+
@output.write(Output::Backtrace.for(error, @identity))
|
153
160
|
end
|
154
161
|
|
155
|
-
def nested(target, isolated: false, inverted: false, **options)
|
162
|
+
def nested(target, identity: @identity, isolated: false, inverted: false, **options)
|
156
163
|
result = nil
|
157
|
-
output = Output::Buffered.new
|
158
164
|
|
159
|
-
output
|
160
|
-
|
161
|
-
|
165
|
+
# Isolated assertions need to have buffered output so they can be replayed if they fail:
|
166
|
+
if isolated
|
167
|
+
output = @output.buffered
|
168
|
+
else
|
169
|
+
output = @output
|
170
|
+
end
|
171
|
+
|
172
|
+
output.puts(:indent, target)
|
162
173
|
|
163
|
-
assertions = self.class.new(target: target, output: output, isolated: isolated, inverted: inverted, **options)
|
174
|
+
assertions = self.class.new(identity: identity, target: target, output: output, isolated: isolated, inverted: inverted, verbose: @verbose, **options)
|
164
175
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
176
|
+
@clock&.start!
|
177
|
+
|
178
|
+
output.indented do
|
179
|
+
begin
|
180
|
+
result = yield(assertions)
|
181
|
+
rescue StandardError => error
|
182
|
+
assertions.fail(error)
|
183
|
+
ensure
|
184
|
+
@clock&.stop!
|
185
|
+
end
|
172
186
|
end
|
173
187
|
|
174
188
|
self.add(assertions)
|
@@ -178,13 +192,15 @@ module Sus
|
|
178
192
|
|
179
193
|
def add(assertions)
|
180
194
|
# If the assertions should be an isolated group, make sure any deferred assertions are resolved:
|
181
|
-
if assertions.isolated
|
195
|
+
if assertions.isolated and assertions.deferred?
|
182
196
|
assertions.resolve!
|
183
197
|
end
|
184
198
|
|
185
199
|
if assertions.deferred?
|
186
200
|
self.defer do
|
201
|
+
output.puts(:indent, assertions.target)
|
187
202
|
assertions.resolve!
|
203
|
+
|
188
204
|
self.add!(assertions)
|
189
205
|
end
|
190
206
|
else
|
@@ -204,10 +220,6 @@ module Sus
|
|
204
220
|
# Otherwise, we append all child assertions into the parent assertions:
|
205
221
|
append!(assertions)
|
206
222
|
end
|
207
|
-
|
208
|
-
@output.indented do
|
209
|
-
@output.append(assertions.output)
|
210
|
-
end
|
211
223
|
end
|
212
224
|
|
213
225
|
def merge!(assertions)
|
@@ -216,17 +228,17 @@ module Sus
|
|
216
228
|
if assertions.passed?
|
217
229
|
@passed << assertions
|
218
230
|
|
219
|
-
if @verbose
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
end
|
231
|
+
# if @verbose
|
232
|
+
# @output.write(:indent, :passed, pass_prefix, :reset)
|
233
|
+
# self.print(@output, verbose: false)
|
234
|
+
# @output.puts
|
235
|
+
# end
|
224
236
|
else
|
225
237
|
@failed << assertions
|
226
238
|
|
227
|
-
@output.write(:indent, :failed, fail_prefix, :reset)
|
228
|
-
self.print(@output, verbose: false)
|
229
|
-
@output.puts
|
239
|
+
# @output.write(:indent, :failed, fail_prefix, :reset)
|
240
|
+
# self.print(@output, verbose: false)
|
241
|
+
# @output.puts
|
230
242
|
end
|
231
243
|
end
|
232
244
|
|
@@ -236,10 +248,11 @@ module Sus
|
|
236
248
|
@failed.concat(assertions.failed)
|
237
249
|
@deferred.concat(assertions.deferred)
|
238
250
|
|
239
|
-
if @verbose
|
240
|
-
|
241
|
-
|
242
|
-
|
251
|
+
# if @verbose
|
252
|
+
# @output.write(:indent)
|
253
|
+
# self.print(@output, verbose: false)
|
254
|
+
# @output.puts
|
255
|
+
# end
|
243
256
|
end
|
244
257
|
|
245
258
|
def pass_prefix
|
data/lib/sus/base.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
|
2
2
|
require_relative 'context'
|
3
|
+
require_relative 'loader'
|
3
4
|
|
4
5
|
module Sus
|
6
|
+
# The base test case class. We need to be careful about what local state is stored.
|
5
7
|
class Base
|
6
8
|
def initialize(assertions)
|
7
|
-
@
|
9
|
+
@__assertions__ = assertions
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"\#<Sus::Base for #{self.class.description.inspect}>"
|
8
14
|
end
|
9
15
|
|
10
16
|
def before
|
@@ -22,19 +28,20 @@ module Sus
|
|
22
28
|
end
|
23
29
|
|
24
30
|
def assert(...)
|
25
|
-
@
|
31
|
+
@__assertions__.assert(...)
|
26
32
|
end
|
27
33
|
|
28
34
|
def refute(...)
|
29
|
-
@
|
35
|
+
@__assertions__.refute(...)
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
|
-
def self.base(description =
|
39
|
+
def self.base(description = nil)
|
34
40
|
base = Class.new(Base)
|
41
|
+
|
35
42
|
base.extend(Context)
|
36
43
|
base.description = description
|
37
|
-
|
44
|
+
|
38
45
|
return base
|
39
46
|
end
|
40
47
|
end
|
data/lib/sus/be.rb
CHANGED
@@ -6,7 +6,9 @@ module Sus
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def print(output)
|
9
|
-
|
9
|
+
operation, *arguments = *@arguments
|
10
|
+
|
11
|
+
output.write("be ", :be, operation.to_s, :reset, " ", :variable, arguments.map(&:inspect).join, :reset)
|
10
12
|
end
|
11
13
|
|
12
14
|
def call(assertions, subject)
|
@@ -56,5 +58,9 @@ module Sus
|
|
56
58
|
Be
|
57
59
|
end
|
58
60
|
end
|
61
|
+
|
62
|
+
def be_a(klass)
|
63
|
+
Be.new(:is_a?, klass)
|
64
|
+
end
|
59
65
|
end
|
60
66
|
end
|
data/lib/sus/be_within.rb
CHANGED
@@ -7,7 +7,7 @@ module Sus
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def print(output)
|
10
|
-
output.write("be within ", :variable, @range)
|
10
|
+
output.write("be within ", :variable, @range, :reset)
|
11
11
|
end
|
12
12
|
|
13
13
|
def call(assertions, subject)
|
@@ -30,7 +30,7 @@ module Sus
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def print(output)
|
33
|
-
output.write("be within ", :variable, @tolerance)
|
33
|
+
output.write("be within ", :variable, @tolerance, :reset)
|
34
34
|
end
|
35
35
|
|
36
36
|
def call(assertions, subject)
|
data/lib/sus/config.rb
CHANGED
@@ -21,6 +21,8 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
require_relative 'clock'
|
24
|
+
require_relative 'registry'
|
25
|
+
require_relative 'loader'
|
24
26
|
|
25
27
|
module Sus
|
26
28
|
class Config
|
@@ -34,7 +36,7 @@ module Sus
|
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
def self.load(root: Dir.pwd,
|
39
|
+
def self.load(root: Dir.pwd, arguments: ARGV)
|
38
40
|
derived = Class.new(self)
|
39
41
|
|
40
42
|
if path = self.path(root)
|
@@ -43,15 +45,32 @@ module Sus
|
|
43
45
|
derived.prepend(config)
|
44
46
|
end
|
45
47
|
|
46
|
-
|
48
|
+
options = {
|
49
|
+
verbose: !!arguments.delete('--verbose')
|
50
|
+
}
|
51
|
+
|
52
|
+
return derived.new(root, arguments, **options)
|
47
53
|
end
|
48
54
|
|
49
|
-
def initialize(root, paths)
|
55
|
+
def initialize(root, paths, verbose: false)
|
50
56
|
@root = root
|
51
57
|
@paths = paths
|
58
|
+
@verbose = verbose
|
59
|
+
|
52
60
|
@clock = Clock.new
|
53
61
|
end
|
54
62
|
|
63
|
+
attr :root
|
64
|
+
attr :paths
|
65
|
+
|
66
|
+
def verbose?
|
67
|
+
@verbose
|
68
|
+
end
|
69
|
+
|
70
|
+
def partial?
|
71
|
+
@paths.any?
|
72
|
+
end
|
73
|
+
|
55
74
|
def output
|
56
75
|
@output ||= Sus::Output.default
|
57
76
|
end
|
@@ -62,8 +81,28 @@ module Sus
|
|
62
81
|
return Dir.glob(DEFAULT_TEST_PATTERN, base: @root)
|
63
82
|
end
|
64
83
|
|
65
|
-
def
|
84
|
+
def registry
|
85
|
+
@registry ||= self.load_registry
|
86
|
+
end
|
87
|
+
|
88
|
+
def base
|
89
|
+
Sus.base
|
90
|
+
end
|
91
|
+
|
92
|
+
def setup_base(base)
|
93
|
+
base.extend(Loader)
|
94
|
+
|
95
|
+
require_root = self.root
|
96
|
+
base.define_singleton_method(:require_root) {require_root}
|
97
|
+
end
|
98
|
+
|
99
|
+
def load_registry
|
100
|
+
registry = Sus::Registry.new
|
101
|
+
|
102
|
+
self.setup_base(registry.base)
|
103
|
+
|
66
104
|
if @paths&.any?
|
105
|
+
registry = Sus::Filter.new(registry)
|
67
106
|
@paths.each do |path|
|
68
107
|
registry.load(path)
|
69
108
|
end
|
@@ -72,6 +111,8 @@ module Sus
|
|
72
111
|
registry.load(path)
|
73
112
|
end
|
74
113
|
end
|
114
|
+
|
115
|
+
return registry
|
75
116
|
end
|
76
117
|
|
77
118
|
def before_tests(assertions)
|
@@ -81,6 +122,12 @@ module Sus
|
|
81
122
|
def after_tests(assertions)
|
82
123
|
@clock.stop!
|
83
124
|
|
125
|
+
self.print_summary(assertions)
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
def print_summary(assertions)
|
84
131
|
output = self.output
|
85
132
|
|
86
133
|
assertions.print(output)
|
@@ -96,12 +143,6 @@ module Sus
|
|
96
143
|
print_failed_assertions(assertions)
|
97
144
|
end
|
98
145
|
|
99
|
-
protected
|
100
|
-
|
101
|
-
def partial?
|
102
|
-
@paths.any?
|
103
|
-
end
|
104
|
-
|
105
146
|
def print_finished_statistics(assertions)
|
106
147
|
duration = @clock.duration
|
107
148
|
rate = assertions.count / duration
|
data/lib/sus/expect.rb
CHANGED
@@ -42,9 +42,9 @@ module Sus
|
|
42
42
|
class Base
|
43
43
|
def expect(subject = nil, **options, &block)
|
44
44
|
if block_given?
|
45
|
-
Expect.new(@
|
45
|
+
Expect.new(@__assertions__, block, **options)
|
46
46
|
else
|
47
|
-
Expect.new(@
|
47
|
+
Expect.new(@__assertions__, subject, **options)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
data/lib/sus/file.rb
CHANGED
@@ -7,9 +7,7 @@ Sus::TOPLEVEL_CLASS_EVAL = ->(klass, path){klass.class_eval(::File.read(path), p
|
|
7
7
|
module Sus
|
8
8
|
module File
|
9
9
|
extend Context
|
10
|
-
|
11
|
-
attr_accessor :path
|
12
|
-
|
10
|
+
|
13
11
|
def self.extended(base)
|
14
12
|
base.children = Hash.new
|
15
13
|
end
|
@@ -24,6 +22,10 @@ module Sus
|
|
24
22
|
|
25
23
|
return base
|
26
24
|
end
|
25
|
+
|
26
|
+
def print(output)
|
27
|
+
output.write("file ", :path, self.identity)
|
28
|
+
end
|
27
29
|
end
|
28
30
|
|
29
31
|
module Context
|
data/lib/sus/filter.rb
CHANGED
data/lib/sus/it.rb
CHANGED
@@ -22,11 +22,11 @@ module Sus
|
|
22
22
|
|
23
23
|
def print(output)
|
24
24
|
self.superclass.print(output)
|
25
|
-
output.write(" it ", :it, self.description, :reset, " ", self.identity.to_s)
|
25
|
+
output.write(" it ", :it, self.description, :reset, " ", :identity, self.identity.to_s, :reset)
|
26
26
|
end
|
27
27
|
|
28
28
|
def call(assertions)
|
29
|
-
assertions.nested(self, isolated: true, measure: true) do |assertions|
|
29
|
+
assertions.nested(self, identity: self.identity, isolated: true, measure: true) do |assertions|
|
30
30
|
instance = self.new(assertions)
|
31
31
|
|
32
32
|
instance.around do
|
data/lib/sus/let.rb
CHANGED
@@ -4,13 +4,13 @@ require_relative 'context'
|
|
4
4
|
module Sus
|
5
5
|
module Context
|
6
6
|
def let(name, &block)
|
7
|
-
|
7
|
+
instance_variable = :"@#{name}"
|
8
8
|
|
9
9
|
self.define_method(name) do
|
10
|
-
if
|
11
|
-
return
|
10
|
+
if self.instance_variable_defined?(instance_variable)
|
11
|
+
return self.instance_variable_get(instance_variable)
|
12
12
|
else
|
13
|
-
self.instance_variable_set(
|
13
|
+
self.instance_variable_set(instance_variable, self.instance_exec(&block))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/sus/loader.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
module Sus
|
24
|
+
module Output
|
25
|
+
# Print out a backtrace relevant to the given test identity if provided.
|
26
|
+
class Backtrace
|
27
|
+
def self.first(identity = nil)
|
28
|
+
# This implementation could be a little more efficient.
|
29
|
+
self.new(caller_locations(1), identity&.path, 1)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.for(exception, identity = nil)
|
33
|
+
self.new(exception.backtrace_locations, identity&.path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(stack, root = nil, limit = nil)
|
37
|
+
@stack = stack
|
38
|
+
@root = root
|
39
|
+
@limit = limit
|
40
|
+
end
|
41
|
+
|
42
|
+
def filter(root = @root)
|
43
|
+
if @root
|
44
|
+
stack = @stack.select do |frame|
|
45
|
+
frame.path.start_with?(@root)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
stack = @stack
|
49
|
+
end
|
50
|
+
|
51
|
+
if @limit
|
52
|
+
stack = stack.take(@limit)
|
53
|
+
end
|
54
|
+
|
55
|
+
return stack
|
56
|
+
end
|
57
|
+
|
58
|
+
def print(output)
|
59
|
+
if @limit == 1
|
60
|
+
filter.each do |frame|
|
61
|
+
output.write " ", :path, frame.path, :line, ":", frame.lineno
|
62
|
+
end
|
63
|
+
else
|
64
|
+
output.indented do
|
65
|
+
filter.each do |frame|
|
66
|
+
output.puts :indent, :path, frame.path, :line, ":", frame.lineno, :reset, " ", frame.label
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/sus/output/buffered.rb
CHANGED
@@ -27,32 +27,55 @@ module Sus
|
|
27
27
|
# Styled output output.
|
28
28
|
module Output
|
29
29
|
class Buffered
|
30
|
-
def initialize
|
31
|
-
@
|
30
|
+
def initialize(tee = nil)
|
31
|
+
@chunks = Array.new
|
32
|
+
@tee = tee
|
33
|
+
end
|
34
|
+
|
35
|
+
attr :chunks
|
36
|
+
attr :tee
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
if @tee
|
40
|
+
"\#<#{self.class.name} #{@chunks.size} chunks -> #{@tee.class}>"
|
41
|
+
else
|
42
|
+
"\#<#{self.class.name} #{@chunks.size} chunks>"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def buffered
|
47
|
+
self.class.new(self)
|
32
48
|
end
|
33
49
|
|
34
50
|
attr :output
|
35
51
|
|
36
52
|
def each(&block)
|
37
|
-
@
|
53
|
+
@chunks.each(&block)
|
38
54
|
end
|
39
55
|
|
40
56
|
def append(buffer)
|
41
|
-
@
|
57
|
+
@chunks.concat(buffer.output)
|
58
|
+
@tee&.append(buffer)
|
42
59
|
end
|
43
60
|
|
44
61
|
def string
|
45
62
|
io = StringIO.new
|
46
|
-
Text.new(io).append(@
|
63
|
+
Text.new(io).append(@chunks)
|
47
64
|
return io.string
|
48
65
|
end
|
49
66
|
|
67
|
+
INDENT = [:indent].freeze
|
68
|
+
|
50
69
|
def indent
|
51
|
-
@
|
70
|
+
@chunks << INDENT
|
71
|
+
@tee&.indent
|
52
72
|
end
|
53
73
|
|
74
|
+
OUTDENT = [:outdent].freeze
|
75
|
+
|
54
76
|
def outdent
|
55
|
-
@
|
77
|
+
@chunks << OUTDENT
|
78
|
+
@tee&.outdent
|
56
79
|
end
|
57
80
|
|
58
81
|
def indented
|
@@ -63,11 +86,13 @@ module Sus
|
|
63
86
|
end
|
64
87
|
|
65
88
|
def write(*arguments)
|
66
|
-
@
|
89
|
+
@chunks << [:write, *arguments]
|
90
|
+
@tee&.write(*arguments)
|
67
91
|
end
|
68
92
|
|
69
93
|
def puts(*arguments)
|
70
|
-
@
|
94
|
+
@chunks << [:puts, *arguments]
|
95
|
+
@tee&.puts(*arguments)
|
71
96
|
end
|
72
97
|
end
|
73
98
|
end
|
data/lib/sus/output/null.rb
CHANGED
data/lib/sus/output/text.rb
CHANGED
@@ -29,12 +29,17 @@ module Sus
|
|
29
29
|
class Text
|
30
30
|
def initialize(io)
|
31
31
|
@io = io
|
32
|
+
|
32
33
|
@styles = {reset: self.reset}
|
33
34
|
|
34
35
|
@indent = String.new
|
35
36
|
@styles[:indent] = @indent
|
36
37
|
end
|
37
38
|
|
39
|
+
def buffered
|
40
|
+
Buffered.new(self)
|
41
|
+
end
|
42
|
+
|
38
43
|
def append(buffer)
|
39
44
|
buffer.each do |operation|
|
40
45
|
self.public_send(*operation)
|
data/lib/sus/output.rb
CHANGED
@@ -23,14 +23,19 @@ module Sus
|
|
23
23
|
|
24
24
|
output[:context] = output.style(nil, nil, :bold)
|
25
25
|
|
26
|
-
output[:describe] = output.style(:cyan
|
26
|
+
output[:describe] = output.style(:cyan)
|
27
27
|
output[:it] = output.style(:cyan)
|
28
28
|
output[:with] = output.style(:cyan)
|
29
29
|
|
30
30
|
output[:variable] = output.style(:blue, nil, :bold)
|
31
31
|
|
32
|
-
output[:
|
33
|
-
output[:
|
32
|
+
output[:path] = output.style(:yellow)
|
33
|
+
output[:line] = output.style(:yellow)
|
34
|
+
output[:identity] = output.style(:yellow)
|
35
|
+
|
36
|
+
output[:passed] = output.style(:green)
|
37
|
+
output[:failed] = output.style(:red)
|
38
|
+
output[:error] = output.style(:red)
|
34
39
|
|
35
40
|
return output
|
36
41
|
end
|
data/lib/sus/receive.rb
CHANGED
@@ -9,7 +9,7 @@ module Sus
|
|
9
9
|
@base = base
|
10
10
|
@method = method
|
11
11
|
|
12
|
-
@times =
|
12
|
+
@times = Times.new
|
13
13
|
@arguments = nil
|
14
14
|
@options = nil
|
15
15
|
@block = nil
|
@@ -35,6 +35,18 @@ module Sus
|
|
35
35
|
return self
|
36
36
|
end
|
37
37
|
|
38
|
+
def once
|
39
|
+
@times = Times.new(Be.new(:==, 1))
|
40
|
+
end
|
41
|
+
|
42
|
+
def twice
|
43
|
+
@times = Times.new(Be.new(:==, 2))
|
44
|
+
end
|
45
|
+
|
46
|
+
def with_call_count(predicate)
|
47
|
+
@times = Times.new(predicate)
|
48
|
+
end
|
49
|
+
|
38
50
|
def and_return(*returning)
|
39
51
|
if returning.size == 1
|
40
52
|
@returning = returning.first
|
@@ -72,8 +84,10 @@ module Sus
|
|
72
84
|
end
|
73
85
|
end
|
74
86
|
|
75
|
-
|
76
|
-
|
87
|
+
if @times
|
88
|
+
assertions.defer do
|
89
|
+
@times.call(assertions, called)
|
90
|
+
end
|
77
91
|
end
|
78
92
|
end
|
79
93
|
end
|
@@ -126,14 +140,20 @@ module Sus
|
|
126
140
|
end
|
127
141
|
end
|
128
142
|
|
129
|
-
class
|
143
|
+
class Times
|
144
|
+
ONCE = Be.new(:==, 1)
|
145
|
+
|
146
|
+
def initialize(condition = ONCE)
|
147
|
+
@condition = condition
|
148
|
+
end
|
149
|
+
|
130
150
|
def print(output)
|
131
|
-
output.write("
|
151
|
+
output.write("with call count ", @condition)
|
132
152
|
end
|
133
153
|
|
134
154
|
def call(assertions, subject)
|
135
155
|
assertions.nested(self) do |assertions|
|
136
|
-
Expect.new(assertions, subject).to(
|
156
|
+
Expect.new(assertions, subject).to(@condition)
|
137
157
|
end
|
138
158
|
end
|
139
159
|
end
|
data/lib/sus/registry.rb
CHANGED
@@ -16,12 +16,16 @@ require_relative 'let'
|
|
16
16
|
module Sus
|
17
17
|
class Registry
|
18
18
|
# Create a top level scope with self as the instance:
|
19
|
-
def initialize(base = Sus.base(
|
19
|
+
def initialize(base = Sus.base(self))
|
20
20
|
@base = base
|
21
21
|
end
|
22
22
|
|
23
23
|
attr :base
|
24
24
|
|
25
|
+
def print(output)
|
26
|
+
output.write("Test Registry")
|
27
|
+
end
|
28
|
+
|
25
29
|
def load(path)
|
26
30
|
@base.file(path)
|
27
31
|
end
|
data/lib/sus/respond_to.rb
CHANGED
@@ -64,16 +64,19 @@ module Sus
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def print(output)
|
67
|
-
output.write("respond to ", :variable, @method.to_s)
|
67
|
+
output.write("respond to ", :variable, @method.to_s, :reset)
|
68
68
|
end
|
69
69
|
|
70
70
|
def call(assertions, subject)
|
71
71
|
assertions.nested(self) do |assertions|
|
72
|
-
|
72
|
+
condition = subject.respond_to?(@method)
|
73
|
+
assertions.assert(condition, self)
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
75
|
+
if condition and (@parameters or @options)
|
76
|
+
parameters = subject.method(@method).parameters
|
77
|
+
@parameters.call(assertions, parameters) if @parameters
|
78
|
+
@options.call(assertions, parameters) if @options
|
79
|
+
end
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
data/lib/sus/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -37,7 +37,7 @@ cert_chain:
|
|
37
37
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
38
38
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
39
39
|
-----END CERTIFICATE-----
|
40
|
-
date: 2022-08-
|
40
|
+
date: 2022-08-21 00:00:00.000000000 Z
|
41
41
|
dependencies: []
|
42
42
|
description:
|
43
43
|
email:
|
@@ -67,8 +67,10 @@ files:
|
|
67
67
|
- lib/sus/it.rb
|
68
68
|
- lib/sus/it_behaves_like.rb
|
69
69
|
- lib/sus/let.rb
|
70
|
+
- lib/sus/loader.rb
|
70
71
|
- lib/sus/mock.rb
|
71
72
|
- lib/sus/output.rb
|
73
|
+
- lib/sus/output/backtrace.rb
|
72
74
|
- lib/sus/output/bar.rb
|
73
75
|
- lib/sus/output/buffered.rb
|
74
76
|
- lib/sus/output/lines.rb
|
metadata.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
��g��X&+��dz/�){�L�dB�kj'bi��r�m�Nɿ�B�3�dRY��%��2(��ד����^�%�!e��Wy<�ăz���Q{Iu�M�A� �C�u��o'���d �p D�TG"�5
|
2
|
+
�n����N��ƥ1��K��Xި�ljW�&�XM웿c��q�Q�l{����X��(T��1�)�m������b�Ɠ�fxWS����I�\�9D�XJ��
|