probatio 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/README.md +301 -0
- data/exe/proba +245 -1
- data/lib/probatio/assertions.rb +311 -0
- data/lib/probatio/debug.rb +35 -0
- data/lib/probatio/examples/a_plugin.rb +14 -0
- data/lib/probatio/examples/a_test.rb +126 -0
- data/lib/probatio/mangle.rb +42 -0
- data/lib/probatio/more.rb +151 -0
- data/lib/probatio/plug.rb +72 -0
- data/lib/probatio/plugins.rb +253 -0
- data/lib/probatio/waiters.rb +44 -0
- data/lib/probatio.rb +773 -157
- data/probatio.gemspec +2 -7
- metadata +17 -8
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# probatio/plug.rb
|
4
|
+
|
5
|
+
|
6
|
+
module Probatio; class << self
|
7
|
+
|
8
|
+
def plugins; @plugins; end
|
9
|
+
|
10
|
+
def plug(x, position=:last)
|
11
|
+
|
12
|
+
@plugins.insert(determine_plugin_pos(position), x)
|
13
|
+
@plugouts = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def unplug(old)
|
17
|
+
|
18
|
+
i =
|
19
|
+
plug_index(old) ||
|
20
|
+
fail(ArgumentError.new("Cannot locate plugin to remove"))
|
21
|
+
|
22
|
+
@plugins.delete_at(i)
|
23
|
+
@plugouts = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def replug(old, new)
|
27
|
+
|
28
|
+
i =
|
29
|
+
plug_index(old) ||
|
30
|
+
fail(ArgumentError.new("Cannot locate plugin to replace"))
|
31
|
+
|
32
|
+
@plugins[i] = new
|
33
|
+
@plugouts = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def plugin_index(x)
|
39
|
+
|
40
|
+
return x \
|
41
|
+
if x.is_a?(Integer)
|
42
|
+
return @plugins.index { |pl| pl.respond_to?(x) } \
|
43
|
+
if x.is_a?(Symbol) || x.is_a?(String)
|
44
|
+
|
45
|
+
i = @plugins.index(x); return i if i
|
46
|
+
|
47
|
+
return @plugins.index { |pl| pl.is_a?(x) } if x.is_a?(Module)
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def determine_plugin_pos(pos)
|
53
|
+
|
54
|
+
return pos if pos.is_a?(Integer)
|
55
|
+
|
56
|
+
return 0 if pos == :first
|
57
|
+
#return @plugins.length if pos == :last
|
58
|
+
|
59
|
+
h = pos.is_a?(Hash) ? pos : {}
|
60
|
+
|
61
|
+
l = @plugins.length
|
62
|
+
|
63
|
+
if af = h[:after]
|
64
|
+
(@plugins.index { |pl| pl == af || (pl.is_a?(af) rescue nil) } || l) + 1
|
65
|
+
elsif bf = h[:before]
|
66
|
+
(@plugins.index { |pl| pl == bf || (pl.is_a?(bf) rescue nil) } || l)
|
67
|
+
else
|
68
|
+
l # last resort, put at the end...
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end; end
|
72
|
+
|
@@ -0,0 +1,253 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# probatio/plugins.rb
|
4
|
+
|
5
|
+
module Probatio::Colours
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def c; Probatio.c; end # colours or not...
|
10
|
+
end
|
11
|
+
|
12
|
+
class Probatio::Recorder
|
13
|
+
|
14
|
+
attr_reader :events
|
15
|
+
|
16
|
+
def record(ev)
|
17
|
+
|
18
|
+
(@events ||= []) << ev
|
19
|
+
end
|
20
|
+
|
21
|
+
def failures; @events.select { |ev| ev.name == 'test_fail' }; end
|
22
|
+
def successes; @events.select { |ev| ev.name == 'test_succeed' }; end
|
23
|
+
|
24
|
+
def test_leave_event(test_node)
|
25
|
+
|
26
|
+
@events.find { |e|
|
27
|
+
e.name == 'test_leave' &&
|
28
|
+
e.node_full_name == test_node.full_name }
|
29
|
+
end
|
30
|
+
|
31
|
+
def total_duration
|
32
|
+
|
33
|
+
@events.last.tstamp - @events.first.tstamp
|
34
|
+
end
|
35
|
+
|
36
|
+
def failed_tests; @events.select { |e| e.name == 'test_fail' }; end
|
37
|
+
|
38
|
+
def test_count; @events.count { |e| e.name == 'test_leave' }; end
|
39
|
+
def assertion_count; @events.count { |e| e.name == 'assertion_leave' }; end
|
40
|
+
def failure_count; @events.count { |e| e.name == 'test_fail' }; end
|
41
|
+
def pending_count; @events.count { |e| e.name == 'test_pending' }; end
|
42
|
+
|
43
|
+
def file_count
|
44
|
+
|
45
|
+
@events.map(&:path).compact.uniq.count
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Probatio
|
50
|
+
|
51
|
+
class << self
|
52
|
+
|
53
|
+
def recorder_plugin
|
54
|
+
|
55
|
+
@plugins.find { |pl| pl.respond_to?(:events) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Probatio::Chronometer
|
61
|
+
|
62
|
+
def record(ev)
|
63
|
+
|
64
|
+
# compute ev.leave_delta if ev is a "leave"
|
65
|
+
|
66
|
+
if ev.enter?
|
67
|
+
|
68
|
+
(@enters ||= []) << ev
|
69
|
+
|
70
|
+
elsif ev.leave?
|
71
|
+
|
72
|
+
e = @enters.pop
|
73
|
+
|
74
|
+
fail "ev mismatch #{ev.name} vs #{e.name}" \
|
75
|
+
if ( ! e) || (ev.type != e.type)
|
76
|
+
|
77
|
+
ev.leave_delta = ev.tstamp - e.tstamp
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
module Probatio::SeedReporter
|
83
|
+
|
84
|
+
def on_start(ev)
|
85
|
+
|
86
|
+
puts
|
87
|
+
puts "Run options: --seed #{Probatio.seed}"
|
88
|
+
puts
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Probatio::DotReporter
|
93
|
+
|
94
|
+
include Probatio::Colours
|
95
|
+
include Probatio::SeedReporter
|
96
|
+
|
97
|
+
def on_test_succeed(ev)
|
98
|
+
|
99
|
+
print c.dark_grey + '·' + c.reset
|
100
|
+
end
|
101
|
+
|
102
|
+
def on_test_fail(ev)
|
103
|
+
|
104
|
+
print c.red + 'x' + c.reset
|
105
|
+
end
|
106
|
+
|
107
|
+
def on_test_pending(ev)
|
108
|
+
|
109
|
+
print c.yellow + '.' + c.reset
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class Probatio::VanillaSummarizer
|
114
|
+
|
115
|
+
def on_over(ev)
|
116
|
+
|
117
|
+
c = Probatio.c # colours or not
|
118
|
+
|
119
|
+
recorder = Probatio.plugins.find { |pl| pl.respond_to?(:failures) }
|
120
|
+
return unless recorder
|
121
|
+
|
122
|
+
if recorder.test_count == 0
|
123
|
+
puts c.dark_grey(" ¯\\_(ツ)_/¯")
|
124
|
+
else
|
125
|
+
puts
|
126
|
+
end
|
127
|
+
|
128
|
+
recorder.failures.each do |ev|
|
129
|
+
|
130
|
+
#puts
|
131
|
+
puts '-' * [ Probatio.term_width, 80 ].min
|
132
|
+
#puts ev.leaf.parent.to_s
|
133
|
+
#puts ev.leaf.head
|
134
|
+
#puts ev.leaf.trail
|
135
|
+
puts ev.error.trail
|
136
|
+
puts
|
137
|
+
#puts "%4d %s" % [ ev.error.line, c.dark_grey(ev.error.source_line) ]
|
138
|
+
#puts c.dark_grey(ev.error.path)
|
139
|
+
ev.error.source_lines.each do |i, l|
|
140
|
+
puts "%4s %s" % [
|
141
|
+
i == ev.error.line ? c.underlined(i.to_s) :
|
142
|
+
i % 5 == 0 ? c.dark_grey(i.to_s) :
|
143
|
+
c.white(i.to_s),
|
144
|
+
i == ev.error.line ? c.yellow(l) : c.dark_grey(l) ]
|
145
|
+
end
|
146
|
+
puts; puts c.dark_grey(ev.error.summary(' '))
|
147
|
+
#puts ev.error.inspect
|
148
|
+
#puts '.'
|
149
|
+
#puts ev.to_s
|
150
|
+
#puts
|
151
|
+
end
|
152
|
+
#puts '-' * 80 if recorder.failures.any?
|
153
|
+
|
154
|
+
r = Probatio.recorder_plugin
|
155
|
+
|
156
|
+
d = r.total_duration
|
157
|
+
|
158
|
+
tc = r.test_count
|
159
|
+
ac = r.assertion_count
|
160
|
+
|
161
|
+
fc = r.failure_count; fc = Probatio.c.red(fc.to_s) if fc > 0
|
162
|
+
pc = r.pending_count; pc = Probatio.c.yellow(pc.to_s) if pc > 0
|
163
|
+
|
164
|
+
tpc = tc / d
|
165
|
+
apc = ac / d
|
166
|
+
|
167
|
+
fic = r.file_count
|
168
|
+
|
169
|
+
puts
|
170
|
+
puts
|
171
|
+
print "Finished in #{Probatio.to_time_s(d)}, "
|
172
|
+
print "%0.3f tests/s, %0.3f assertions/s." % [ tpc, apc ]
|
173
|
+
puts
|
174
|
+
puts
|
175
|
+
print "#{tc} test#{s(tc)}, #{ac} assertion#{s(ac)}, "
|
176
|
+
print "#{fc} failure#{s(fc)}, #{pc} pending, "
|
177
|
+
print "#{fic} file#{s(fic)}."
|
178
|
+
puts
|
179
|
+
puts
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
|
184
|
+
def s(count); count == 1 ? '' : 's'; end
|
185
|
+
end
|
186
|
+
|
187
|
+
class Probatio::ProbaOutputter
|
188
|
+
|
189
|
+
require 'rbconfig'
|
190
|
+
|
191
|
+
def on_over(ev)
|
192
|
+
|
193
|
+
# TODO unplug if --mute or some switch like that...
|
194
|
+
r = Probatio.recorder_plugin
|
195
|
+
|
196
|
+
flh = r.failed_tests.collect(&:to_h).each { |h| h.delete(:n) }
|
197
|
+
fls = Cerata.table_to_s(flh, ' ')
|
198
|
+
|
199
|
+
rb = {}
|
200
|
+
#
|
201
|
+
rv =
|
202
|
+
File.exist?('.ruby-version') &&
|
203
|
+
File.readlines('.ruby-version').find { |l| ! l.strip.start_with?('#') }
|
204
|
+
#
|
205
|
+
rb[:v] = ".ruby-version:#{rv.strip}" if rv
|
206
|
+
rb[:p] = File.join(
|
207
|
+
RbConfig::CONFIG['bindir'],
|
208
|
+
RbConfig::CONFIG['ruby_install_name'])
|
209
|
+
rb[:d] = RUBY_DESCRIPTION
|
210
|
+
rb[:l] = RUBY_PATCHLEVEL
|
211
|
+
#
|
212
|
+
#rb = Cerata.horizontal_h_to_s(rb)
|
213
|
+
rb = Cerata.vertical_h_to_s(rb, ' ')
|
214
|
+
|
215
|
+
env = Cerata.vertical_h_to_s(
|
216
|
+
ENV.filter { |k, _|
|
217
|
+
k.match?(/^(RUBY_|GEM_|(HOME|PATH|USER|SHELL|PWD)$)/) },
|
218
|
+
' ')
|
219
|
+
|
220
|
+
File.open(Probatio.opath, 'wb') do |o|
|
221
|
+
o << '# ' << Probatio.opath << "\n"
|
222
|
+
o << "{\n"
|
223
|
+
o << "argv: " << Cerata.horizontal_a_to_s(ARGV) << ",\n"
|
224
|
+
o << "failures:\n"
|
225
|
+
#o << " [\n"
|
226
|
+
#fls.each { |fl| o << ' ' << fl << ",\n" }
|
227
|
+
#o << " ],\n"
|
228
|
+
o << fls << ",\n"
|
229
|
+
o << "duration: #{Probatio.to_time_s(r.total_duration).inspect},\n"
|
230
|
+
o << "probatio: { v: #{Probatio::VERSION.inspect} },\n"
|
231
|
+
o << "ruby:\n#{rb},\n"
|
232
|
+
o << "some_env:\n#{env},\n"
|
233
|
+
o << "}\n"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
class Probatio::Exitter
|
239
|
+
|
240
|
+
def on_exit(ev)
|
241
|
+
|
242
|
+
exit 1 if Probatio.recorder_plugin.failure_count > 0
|
243
|
+
exit 0
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
Probatio.plug(Probatio::Recorder.new)
|
248
|
+
Probatio.plug(Probatio::Chronometer.new)
|
249
|
+
Probatio.plug(Probatio::DotReporter.new)
|
250
|
+
Probatio.plug(Probatio::VanillaSummarizer.new)
|
251
|
+
Probatio.plug(Probatio::ProbaOutputter.new)
|
252
|
+
Probatio.plug(Probatio::Exitter.new)
|
253
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# probatio/waiters.rb
|
4
|
+
|
5
|
+
module Probatio::Waiters
|
6
|
+
|
7
|
+
def wait_until(opts={}, &block)
|
8
|
+
|
9
|
+
timeout = opts[:timeout] || 14
|
10
|
+
frequency = opts[:frequency] || 0.1
|
11
|
+
|
12
|
+
start = Probatio.monow
|
13
|
+
|
14
|
+
loop do
|
15
|
+
|
16
|
+
sleep(frequency)
|
17
|
+
|
18
|
+
#return if block.call == true
|
19
|
+
r = block.call
|
20
|
+
return r if r
|
21
|
+
|
22
|
+
break if Probatio.monow - start > timeout
|
23
|
+
end
|
24
|
+
|
25
|
+
fail "timeout after #{timeout}s"
|
26
|
+
end
|
27
|
+
alias wait_for wait_until
|
28
|
+
|
29
|
+
def monow
|
30
|
+
|
31
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Probatio::Section
|
36
|
+
|
37
|
+
include Probatio::Waiters
|
38
|
+
end
|
39
|
+
|
40
|
+
class Probatio::Context
|
41
|
+
|
42
|
+
include Probatio::Waiters
|
43
|
+
end
|
44
|
+
|