sus 0.17.2 → 0.18.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-host +104 -0
- data/bin/sus-tree +12 -0
- data/lib/sus/assertions.rb +79 -7
- data/lib/sus/base.rb +3 -2
- data/lib/sus/clock.rb +8 -0
- data/lib/sus/config.rb +20 -21
- data/lib/sus/expect.rb +11 -6
- data/lib/sus/file.rb +20 -5
- data/lib/sus/identity.rb +36 -0
- data/lib/sus/output/buffered.rb +1 -3
- data/lib/sus/registry.rb +9 -4
- data/lib/sus/tree.rb +27 -0
- data/lib/sus/version.rb +1 -1
- data/lib/sus.rb +1 -0
- data.tar.gz.sig +2 -4
- metadata +7 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e39bdf28a3135bf28cf66714d3ac2998c1c863e979c172573a3e5e509de2246e
|
4
|
+
data.tar.gz: 88273ca646abe719cb562693f233d6210d8e505456ae8dd663d74971086d33b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eca3ca7daddbc30591698658f6d87cc11a56c9dee2456b1060339fe1442aa5d8479a11ccdf7d9f4e7d617478ff033b0c24482db8d8145b7046e69a106a75409c
|
7
|
+
data.tar.gz: 2df536e6206a37f475bde130d8a6f8f4f8c8eab49a3ea2f7d29f48b4a64b53d6145cc77c27f2105043385756cc8d44d34983334a6de633c5c8d78275777b1ee7
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/bin/sus-host
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative '../lib/sus/config'
|
6
|
+
config = Sus::Config.load
|
7
|
+
|
8
|
+
require_relative '../lib/sus'
|
9
|
+
|
10
|
+
verbose = false
|
11
|
+
guard = Thread::Mutex.new
|
12
|
+
|
13
|
+
require 'etc'
|
14
|
+
count = Etc.nprocessors
|
15
|
+
|
16
|
+
$stdout.sync = true
|
17
|
+
|
18
|
+
input = $stdin.dup
|
19
|
+
$stdin.reopen(File::NULL)
|
20
|
+
output = $stdout.dup
|
21
|
+
$stdout.reopen($stderr)
|
22
|
+
|
23
|
+
def failure_messages_for(assertions)
|
24
|
+
messages = []
|
25
|
+
|
26
|
+
assertions.each_failure do |failure|
|
27
|
+
messages << failure.message
|
28
|
+
end
|
29
|
+
|
30
|
+
return messages
|
31
|
+
end
|
32
|
+
|
33
|
+
while line = input.gets
|
34
|
+
message = JSON.parse(line)
|
35
|
+
|
36
|
+
if tests = message['run']
|
37
|
+
jobs = Thread::Queue.new
|
38
|
+
results = Thread::Queue.new
|
39
|
+
|
40
|
+
top = Sus::Assertions.new(measure: true)
|
41
|
+
config.before_tests(top)
|
42
|
+
|
43
|
+
aggregate = Thread.new do
|
44
|
+
while result = results.pop
|
45
|
+
top.add(result)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
loader = Thread.new do
|
50
|
+
registry = config.load_registry(tests)
|
51
|
+
|
52
|
+
registry.each do |child|
|
53
|
+
jobs << child
|
54
|
+
end
|
55
|
+
|
56
|
+
jobs.close
|
57
|
+
end
|
58
|
+
|
59
|
+
workers = count.times.map do |index|
|
60
|
+
Thread.new do
|
61
|
+
while job = jobs.pop
|
62
|
+
guard.synchronize do
|
63
|
+
output.puts JSON.generate({started: job.identity})
|
64
|
+
end
|
65
|
+
|
66
|
+
assertions = Sus::Assertions.new(measure: true)
|
67
|
+
job.call(assertions)
|
68
|
+
results.push(assertions)
|
69
|
+
|
70
|
+
guard.synchronize do
|
71
|
+
if assertions.passed?
|
72
|
+
output.puts JSON.generate({passed: job.identity, duration: assertions.clock.ms})
|
73
|
+
elsif assertions.errored?
|
74
|
+
output.puts JSON.generate({errored: job.identity, messages: failure_messages_for(assertions), duration: assertions.clock.ms})
|
75
|
+
else
|
76
|
+
output.puts JSON.generate({failed: job.identity, messages: failure_messages_for(assertions), duration: assertions.clock.ms})
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
loader.join
|
84
|
+
workers.each(&:join)
|
85
|
+
results.close
|
86
|
+
|
87
|
+
aggregate.join
|
88
|
+
config.after_tests(top)
|
89
|
+
|
90
|
+
workers.each(&:join)
|
91
|
+
|
92
|
+
if config.respond_to?(:covered)
|
93
|
+
if covered = config.covered and covered.record?
|
94
|
+
covered.policy.each do |coverage|
|
95
|
+
output.puts JSON.generate({coverage: coverage.path, counts: coverage.counts})
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
output.puts JSON.generate({finished: true, message: top.output.string, duration: top.clock.ms})
|
101
|
+
else
|
102
|
+
$stderr.puts "Unknown message: #{message}"
|
103
|
+
end
|
104
|
+
end
|
data/bin/sus-tree
ADDED
data/lib/sus/assertions.rb
CHANGED
@@ -41,6 +41,7 @@ module Sus
|
|
41
41
|
@count = 0
|
42
42
|
end
|
43
43
|
|
44
|
+
attr :identity
|
44
45
|
attr :target
|
45
46
|
attr :output
|
46
47
|
attr :level
|
@@ -75,6 +76,13 @@ module Sus
|
|
75
76
|
"\#<#{self.class} #{@passed.size} passed #{@failed.size} failed #{@deferred.size} deferred #{@skipped.size} skipped #{@errored.size} errored>"
|
76
77
|
end
|
77
78
|
|
79
|
+
def message
|
80
|
+
{
|
81
|
+
text: @output.string,
|
82
|
+
location: @identity&.to_location
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
78
86
|
def total
|
79
87
|
@passed.size + @failed.size + @deferred.size + @skipped.size + @errored.size
|
80
88
|
end
|
@@ -138,24 +146,65 @@ module Sus
|
|
138
146
|
@errored.any?
|
139
147
|
end
|
140
148
|
|
149
|
+
class Assert
|
150
|
+
def initialize(identity, message)
|
151
|
+
@identity = identity
|
152
|
+
@message = message
|
153
|
+
end
|
154
|
+
|
155
|
+
attr :identity
|
156
|
+
attr :message
|
157
|
+
|
158
|
+
def each_failure(&block)
|
159
|
+
yield self
|
160
|
+
end
|
161
|
+
|
162
|
+
def message
|
163
|
+
{
|
164
|
+
text: @message,
|
165
|
+
location: @identity&.to_location
|
166
|
+
}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
141
170
|
def assert(condition, message = nil)
|
142
171
|
@count += 1
|
143
172
|
|
173
|
+
message ||= self.output.string
|
174
|
+
backtrace = Output::Backtrace.first(@identity)
|
175
|
+
identity = @identity&.scoped
|
176
|
+
|
144
177
|
if condition
|
145
|
-
@passed <<
|
178
|
+
@passed << Assert.new(identity, message)
|
146
179
|
|
147
180
|
if !@orientation || @verbose
|
148
|
-
@output.puts(:indent, *pass_prefix, message
|
181
|
+
@output.puts(:indent, *pass_prefix, message, backtrace)
|
149
182
|
end
|
150
183
|
else
|
151
|
-
@failed <<
|
184
|
+
@failed << Assert.new(identity, message)
|
152
185
|
|
153
186
|
if @orientation || @verbose
|
154
|
-
@output.puts(:indent, *fail_prefix, message
|
187
|
+
@output.puts(:indent, *fail_prefix, message, backtrace)
|
155
188
|
end
|
156
189
|
end
|
157
190
|
end
|
158
191
|
|
192
|
+
def each_failure(&block)
|
193
|
+
return to_enum(__method__) unless block_given?
|
194
|
+
|
195
|
+
# if self.failed? and @identity
|
196
|
+
# yield self
|
197
|
+
# end
|
198
|
+
|
199
|
+
@failed.each do |assertions|
|
200
|
+
assertions.each_failure(&block)
|
201
|
+
end
|
202
|
+
|
203
|
+
@errored.each do |assertions|
|
204
|
+
assertions.each_failure(&block)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
159
208
|
def skip(reason)
|
160
209
|
@output.puts(:indent, :skipped, skip_prefix, reason)
|
161
210
|
@skipped << self
|
@@ -184,8 +233,31 @@ module Sus
|
|
184
233
|
end
|
185
234
|
end
|
186
235
|
|
236
|
+
class Error
|
237
|
+
def initialize(identity, error)
|
238
|
+
@identity = identity
|
239
|
+
@error = error
|
240
|
+
end
|
241
|
+
|
242
|
+
attr :identity
|
243
|
+
attr :error
|
244
|
+
|
245
|
+
def each_failure(&block)
|
246
|
+
yield self
|
247
|
+
end
|
248
|
+
|
249
|
+
def message
|
250
|
+
{
|
251
|
+
text: @error.message,
|
252
|
+
location: @identity&.to_location
|
253
|
+
}
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
187
257
|
def error!(error)
|
188
|
-
|
258
|
+
identity = @identity.scoped(error.backtrace_locations)
|
259
|
+
|
260
|
+
@errored << Error.new(identity, error)
|
189
261
|
|
190
262
|
lines = error.message.split(/\r?\n/)
|
191
263
|
|
@@ -198,11 +270,11 @@ module Sus
|
|
198
270
|
@output.write(Output::Backtrace.for(error, @identity))
|
199
271
|
end
|
200
272
|
|
201
|
-
def nested(target, identity: @identity, isolated: false, inverted: false, **options)
|
273
|
+
def nested(target, identity: @identity, isolated: false, buffered: false, inverted: false, **options)
|
202
274
|
result = nil
|
203
275
|
|
204
276
|
# Isolated assertions need to have buffered output so they can be replayed if they fail:
|
205
|
-
if isolated
|
277
|
+
if isolated or buffered
|
206
278
|
output = @output.buffered
|
207
279
|
else
|
208
280
|
output = @output
|
data/lib/sus/base.rb
CHANGED
@@ -39,12 +39,13 @@ module Sus
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def self.base(description = nil)
|
42
|
+
def self.base(description = nil, root: nil)
|
43
43
|
base = Class.new(Base)
|
44
44
|
|
45
45
|
base.extend(Context)
|
46
|
+
base.identity = Identity.new(root) if root
|
46
47
|
base.description = description
|
47
|
-
|
48
|
+
|
48
49
|
return base
|
49
50
|
end
|
50
51
|
end
|
data/lib/sus/clock.rb
CHANGED
@@ -33,6 +33,10 @@ module Sus
|
|
33
33
|
duration
|
34
34
|
end
|
35
35
|
|
36
|
+
def ms
|
37
|
+
duration * 1000.0
|
38
|
+
end
|
39
|
+
|
36
40
|
def to_s
|
37
41
|
duration = self.duration
|
38
42
|
|
@@ -45,6 +49,10 @@ module Sus
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
52
|
+
def reset!(duration = 0.0)
|
53
|
+
@duration = duration
|
54
|
+
end
|
55
|
+
|
48
56
|
def start!
|
49
57
|
@start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
50
58
|
end
|
data/lib/sus/config.rb
CHANGED
@@ -82,12 +82,12 @@ module Sus
|
|
82
82
|
@registry ||= self.load_registry
|
83
83
|
end
|
84
84
|
|
85
|
-
def load_registry
|
86
|
-
registry = Sus::Registry.new
|
85
|
+
def load_registry(paths = @paths)
|
86
|
+
registry = Sus::Registry.new(root: @root)
|
87
87
|
|
88
|
-
if
|
88
|
+
if paths&.any?
|
89
89
|
registry = Sus::Filter.new(registry)
|
90
|
-
|
90
|
+
paths.each do |path|
|
91
91
|
registry.load(path)
|
92
92
|
end
|
93
93
|
else
|
@@ -99,42 +99,41 @@ module Sus
|
|
99
99
|
return registry
|
100
100
|
end
|
101
101
|
|
102
|
-
def before_tests(assertions)
|
102
|
+
def before_tests(assertions, output: self.output)
|
103
|
+
@clock.reset!
|
103
104
|
@clock.start!
|
104
105
|
end
|
105
106
|
|
106
|
-
def after_tests(assertions)
|
107
|
+
def after_tests(assertions, output: self.output)
|
107
108
|
@clock.stop!
|
108
109
|
|
109
|
-
self.print_summary(assertions)
|
110
|
+
self.print_summary(output, assertions)
|
110
111
|
end
|
111
112
|
|
112
113
|
protected
|
113
114
|
|
114
|
-
def print_summary(assertions)
|
115
|
-
output = self.output
|
116
|
-
|
115
|
+
def print_summary(output, assertions)
|
117
116
|
assertions.print(output)
|
118
117
|
output.puts
|
119
118
|
|
120
|
-
print_finished_statistics(assertions)
|
119
|
+
print_finished_statistics(output, assertions)
|
121
120
|
|
122
121
|
if !partial? and assertions.passed?
|
123
|
-
print_test_feedback(assertions)
|
122
|
+
print_test_feedback(output, assertions)
|
124
123
|
end
|
125
124
|
|
126
|
-
print_slow_tests(assertions)
|
127
|
-
print_failed_assertions(assertions)
|
125
|
+
print_slow_tests(output, assertions)
|
126
|
+
print_failed_assertions(output, assertions)
|
128
127
|
end
|
129
128
|
|
130
|
-
def print_finished_statistics(assertions)
|
129
|
+
def print_finished_statistics(output, assertions)
|
131
130
|
duration = @clock.duration
|
132
131
|
rate = assertions.count / duration
|
133
132
|
|
134
133
|
output.puts "🏁 Finished in ", @clock, "; #{rate.round(3)} assertions per second."
|
135
134
|
end
|
136
135
|
|
137
|
-
def print_test_feedback(assertions)
|
136
|
+
def print_test_feedback(output, assertions)
|
138
137
|
duration = @clock.duration
|
139
138
|
rate = assertions.count / duration
|
140
139
|
|
@@ -179,7 +178,7 @@ module Sus
|
|
179
178
|
end
|
180
179
|
end
|
181
180
|
|
182
|
-
def print_slow_tests(assertions, threshold = 0.1)
|
181
|
+
def print_slow_tests(output, assertions, threshold = 0.1)
|
183
182
|
slowest_tests = assertions.passed.select{|test| test.clock > threshold}.sort_by(&:clock).reverse!
|
184
183
|
|
185
184
|
if slowest_tests.empty?
|
@@ -193,7 +192,7 @@ module Sus
|
|
193
192
|
end
|
194
193
|
end
|
195
194
|
|
196
|
-
def print_assertions(title, assertions)
|
195
|
+
def print_assertions(output, title, assertions)
|
197
196
|
if assertions.any?
|
198
197
|
output.puts
|
199
198
|
output.puts title
|
@@ -206,9 +205,9 @@ module Sus
|
|
206
205
|
end
|
207
206
|
end
|
208
207
|
|
209
|
-
def print_failed_assertions(assertions)
|
210
|
-
print_assertions("🤔 Failed assertions:", assertions.failed)
|
211
|
-
print_assertions("🔥 Errored assertions:", assertions.errored)
|
208
|
+
def print_failed_assertions(output, assertions)
|
209
|
+
print_assertions(output, "🤔 Failed assertions:", assertions.failed)
|
210
|
+
print_assertions(output, "🔥 Errored assertions:", assertions.errored)
|
212
211
|
end
|
213
212
|
end
|
214
213
|
end
|
data/lib/sus/expect.rb
CHANGED
@@ -5,14 +5,16 @@
|
|
5
5
|
|
6
6
|
module Sus
|
7
7
|
class Expect
|
8
|
-
def initialize(assertions, subject, inverted: false)
|
8
|
+
def initialize(assertions, subject, inverted: false, buffered: false)
|
9
9
|
@assertions = assertions
|
10
10
|
@subject = subject
|
11
11
|
@inverted = inverted
|
12
|
+
@buffered = buffered
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
attr :subject
|
15
16
|
attr :inverted
|
17
|
+
attr :assertions
|
16
18
|
|
17
19
|
def not
|
18
20
|
self.dup.tap do |expect|
|
@@ -31,13 +33,16 @@ module Sus
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def to(predicate)
|
34
|
-
|
36
|
+
# This gets the identity scoped to the current call stack, which ensures that any failures are logged at this point in the code.
|
37
|
+
identity = @assertions.identity&.scoped
|
38
|
+
|
39
|
+
@assertions.nested(self, inverted: @inverted, buffered: @buffered, identity: identity) do |assertions|
|
35
40
|
predicate.call(assertions, @subject)
|
36
41
|
end
|
37
42
|
|
38
43
|
return self
|
39
44
|
end
|
40
|
-
|
45
|
+
|
41
46
|
def and(predicate)
|
42
47
|
return to(predicate)
|
43
48
|
end
|
@@ -46,9 +51,9 @@ module Sus
|
|
46
51
|
class Base
|
47
52
|
def expect(subject = nil, &block)
|
48
53
|
if block_given?
|
49
|
-
Expect.new(@__assertions__, block)
|
54
|
+
Expect.new(@__assertions__, block, buffered: true)
|
50
55
|
else
|
51
|
-
Expect.new(@__assertions__, subject)
|
56
|
+
Expect.new(@__assertions__, subject, buffered: true)
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
data/lib/sus/file.rb
CHANGED
@@ -19,11 +19,17 @@ module Sus
|
|
19
19
|
|
20
20
|
def self.build(parent, path)
|
21
21
|
base = Class.new(parent)
|
22
|
+
|
22
23
|
base.extend(File)
|
23
24
|
base.description = path
|
24
|
-
base.identity = Identity.
|
25
|
+
base.identity = Identity.file(parent.identity, path)
|
25
26
|
|
26
|
-
|
27
|
+
begin
|
28
|
+
TOPLEVEL_CLASS_EVAL.call(base, path)
|
29
|
+
rescue StandardError, LoadError, SyntaxError => error
|
30
|
+
# We add this as a child of the base class so that it is included in the tree under the file rather than completely replacing it, which can be confusing:
|
31
|
+
base.add FileLoadError.build(self, path, error)
|
32
|
+
end
|
27
33
|
|
28
34
|
return base
|
29
35
|
end
|
@@ -35,7 +41,8 @@ module Sus
|
|
35
41
|
|
36
42
|
class FileLoadError
|
37
43
|
def self.build(parent, path, error)
|
38
|
-
|
44
|
+
identity = Identity.file(parent.identity, path).scoped(error.backtrace_locations)
|
45
|
+
self.new(identity, path, error)
|
39
46
|
end
|
40
47
|
|
41
48
|
def initialize(identity, path, error)
|
@@ -50,6 +57,16 @@ module Sus
|
|
50
57
|
true
|
51
58
|
end
|
52
59
|
|
60
|
+
EMPTY = Hash.new.freeze
|
61
|
+
|
62
|
+
def children
|
63
|
+
EMPTY
|
64
|
+
end
|
65
|
+
|
66
|
+
def description
|
67
|
+
@path
|
68
|
+
end
|
69
|
+
|
53
70
|
def print(output)
|
54
71
|
output.write("file ", :path, @identity)
|
55
72
|
end
|
@@ -66,8 +83,6 @@ module Sus
|
|
66
83
|
module Context
|
67
84
|
def file(path)
|
68
85
|
add File.build(self, path)
|
69
|
-
rescue StandardError, LoadError, SyntaxError => error
|
70
|
-
add FileLoadError.build(self, path, error)
|
71
86
|
end
|
72
87
|
end
|
73
88
|
end
|
data/lib/sus/identity.rb
CHANGED
@@ -5,6 +5,10 @@
|
|
5
5
|
|
6
6
|
module Sus
|
7
7
|
class Identity
|
8
|
+
def self.file(parent, path, name = path, **options)
|
9
|
+
self.new(path, name, nil, nil, **options)
|
10
|
+
end
|
11
|
+
|
8
12
|
def self.nested(parent, name, location = nil, **options)
|
9
13
|
location ||= caller_locations(3...4).first
|
10
14
|
|
@@ -32,6 +36,13 @@ module Sus
|
|
32
36
|
self.key
|
33
37
|
end
|
34
38
|
|
39
|
+
def to_location
|
40
|
+
{
|
41
|
+
path: ::File.expand_path(@path),
|
42
|
+
line: @line,
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
35
46
|
def inspect
|
36
47
|
"\#<#{self.class} #{self.to_s}>"
|
37
48
|
end
|
@@ -69,8 +80,33 @@ module Sus
|
|
69
80
|
return @key
|
70
81
|
end
|
71
82
|
|
83
|
+
# Given a set of locations, find the first one which matches this identity and return a new identity with the updated line number. This can be used to extract a location from a backtrace.
|
84
|
+
def scoped(locations = nil)
|
85
|
+
if locations
|
86
|
+
# This code path is normally taken if we've got an exception with a backtrace:
|
87
|
+
locations.each do |location|
|
88
|
+
if location.path == @path
|
89
|
+
return self.with_line(location.lineno)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
# In theory this should be a bit faster:
|
94
|
+
Thread.each_caller_location do |location|
|
95
|
+
if location.path == @path
|
96
|
+
return self.with_line(location.lineno)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
|
72
104
|
protected
|
73
105
|
|
106
|
+
def with_line(line)
|
107
|
+
Identity.new(@path, @name, line, @parent, unique: @unique)
|
108
|
+
end
|
109
|
+
|
74
110
|
def append_unique_key(key, unique = @unique)
|
75
111
|
if @parent
|
76
112
|
@parent.append_unique_key(key)
|
data/lib/sus/output/buffered.rb
CHANGED
@@ -30,14 +30,12 @@ module Sus
|
|
30
30
|
self.class.new(self)
|
31
31
|
end
|
32
32
|
|
33
|
-
attr :output
|
34
|
-
|
35
33
|
def each(&block)
|
36
34
|
@chunks.each(&block)
|
37
35
|
end
|
38
36
|
|
39
37
|
def append(buffer)
|
40
|
-
@chunks.concat(buffer.
|
38
|
+
@chunks.concat(buffer.chunks)
|
41
39
|
@tee&.append(buffer)
|
42
40
|
end
|
43
41
|
|
data/lib/sus/registry.rb
CHANGED
@@ -23,16 +23,21 @@ module Sus
|
|
23
23
|
DIRECTORY_GLOB = "**/*.rb"
|
24
24
|
|
25
25
|
# Create a top level scope with self as the instance:
|
26
|
-
def initialize(
|
27
|
-
@base = base
|
26
|
+
def initialize(**options)
|
27
|
+
@base = Sus.base(self, **options)
|
28
|
+
@loaded = {}
|
28
29
|
end
|
29
|
-
|
30
|
+
|
30
31
|
attr :base
|
31
32
|
|
32
33
|
def print(output)
|
33
34
|
output.write("Test Registry")
|
34
35
|
end
|
35
36
|
|
37
|
+
def to_s
|
38
|
+
@base.identity.to_s
|
39
|
+
end
|
40
|
+
|
36
41
|
def load(path)
|
37
42
|
if ::File.directory?(path)
|
38
43
|
load_directory(path)
|
@@ -42,7 +47,7 @@ module Sus
|
|
42
47
|
end
|
43
48
|
|
44
49
|
private def load_file(path)
|
45
|
-
@base.file(path)
|
50
|
+
@loaded[path] ||= @base.file(path)
|
46
51
|
end
|
47
52
|
|
48
53
|
private def load_directory(path)
|
data/lib/sus/tree.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Sus
|
2
|
+
class Tree
|
3
|
+
def initialize(context)
|
4
|
+
@context = context
|
5
|
+
end
|
6
|
+
|
7
|
+
def traverse(current = @context, &block)
|
8
|
+
node = {}
|
9
|
+
|
10
|
+
node[:self] = yield(current)
|
11
|
+
|
12
|
+
if children = current.children # and children.any?
|
13
|
+
node[:children] = children.values.map do |context|
|
14
|
+
self.traverse(context, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
return node
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json(options = nil)
|
22
|
+
traverse do |context|
|
23
|
+
[context.identity, context.description, context.leaf?]
|
24
|
+
end.to_json(options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/sus/version.rb
CHANGED
data/lib/sus.rb
CHANGED
data.tar.gz.sig
CHANGED
@@ -1,4 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
ǜ���Դ���2w�D1�}�yOy`��b��.yG��qJc����M
|
4
|
-
X*�e�
|
1
|
+
am�����tJ��)�p����Bn�ɠϝռ:����*����-TIV�3#AFH�pg�a�WY��*k5�:�4^�>z�I�=UD,ϑk*�8�t�۳nb"��k��~�k(x�&}���M-�c�H��̚���kΑ�
|
2
|
+
��P߾��D;q�����!��� �L7V�����d���^�@s��?kF� ��:-�a�3ag��=\{�<����#!ȔDI����p����kvM�$��K`�zP��jOם¡�)bѽ/��y�#���թ��
|
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.18.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -38,7 +38,7 @@ cert_chain:
|
|
38
38
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
39
39
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
40
40
|
-----END CERTIFICATE-----
|
41
|
-
date: 2023-02-
|
41
|
+
date: 2023-02-20 00:00:00.000000000 Z
|
42
42
|
dependencies:
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: bake-test
|
@@ -87,11 +87,15 @@ email:
|
|
87
87
|
executables:
|
88
88
|
- sus
|
89
89
|
- sus-parallel
|
90
|
+
- sus-tree
|
91
|
+
- sus-host
|
90
92
|
extensions: []
|
91
93
|
extra_rdoc_files: []
|
92
94
|
files:
|
93
95
|
- bin/sus
|
96
|
+
- bin/sus-host
|
94
97
|
- bin/sus-parallel
|
98
|
+
- bin/sus-tree
|
95
99
|
- lib/sus.rb
|
96
100
|
- lib/sus/assertions.rb
|
97
101
|
- lib/sus/base.rb
|
@@ -131,6 +135,7 @@ files:
|
|
131
135
|
- lib/sus/registry.rb
|
132
136
|
- lib/sus/respond_to.rb
|
133
137
|
- lib/sus/shared.rb
|
138
|
+
- lib/sus/tree.rb
|
134
139
|
- lib/sus/version.rb
|
135
140
|
- lib/sus/with.rb
|
136
141
|
- license.md
|
metadata.gz.sig
CHANGED
Binary file
|