pork 0.9.2 → 1.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -0
- data/CHANGES.md +35 -0
- data/Gemfile +13 -0
- data/README.md +186 -39
- data/Rakefile +1 -1
- data/lib/mutant/integration/pork.rb +76 -0
- data/lib/pork.rb +14 -231
- data/lib/pork/auto.rb +2 -1
- data/lib/pork/context.rb +24 -0
- data/lib/pork/env.rb +18 -0
- data/lib/pork/error.rb +6 -0
- data/lib/pork/executor.rb +11 -0
- data/lib/pork/expect.rb +69 -0
- data/lib/pork/imp.rb +103 -0
- data/lib/pork/inspect.rb +97 -0
- data/lib/pork/isolate.rb +55 -0
- data/lib/pork/mode/parallel.rb +16 -0
- data/lib/pork/mode/shuffle.rb +16 -0
- data/lib/pork/should.rb +25 -0
- data/lib/pork/stat.rb +38 -0
- data/lib/pork/test.rb +17 -0
- data/lib/pork/version.rb +1 -1
- data/pork.gemspec +42 -7
- data/task/gemgem.rb +100 -51
- data/test/test_inspect.rb +41 -0
- data/test/test_isolate.rb +13 -0
- data/test/test_nested.rb +0 -21
- data/test/test_parallel.rb +12 -0
- data/test/test_shuffle.rb +12 -0
- metadata +42 -5
data/lib/pork.rb
CHANGED
@@ -1,247 +1,30 @@
|
|
1
1
|
|
2
|
-
require '
|
3
|
-
|
4
|
-
module Kernel
|
5
|
-
def should message=nil, message_lazy=nil, &checker
|
6
|
-
Pork::Should.new(self, message, message_lazy, &checker)
|
7
|
-
end
|
8
|
-
end
|
2
|
+
require 'pork/executor'
|
9
3
|
|
10
4
|
module Pork
|
11
|
-
Error = Class.new(StandardError)
|
12
|
-
Failure = Class.new(Error)
|
13
|
-
Skip = Class.new(Error)
|
14
|
-
|
15
|
-
def self.stats ; @stats ||= Stats.new; end
|
16
|
-
def self.reset ; @stats = nil ; end
|
17
|
-
def self.report; stats.report; reset ; end
|
18
|
-
def self.report_at_exit
|
19
|
-
Pork.stats.start
|
20
|
-
@report_at_exit ||= at_exit do
|
21
|
-
stats.report
|
22
|
-
exit stats.failures.size + stats.errors.size + ($! && 1).to_i
|
23
|
-
end
|
24
|
-
end
|
25
|
-
# default to :auto while eliminating warnings for uninitialized ivar
|
26
|
-
def self.inspect_failure_mode mode=nil; @mode = mode || @mode ||= :auto; end
|
27
|
-
def self.inspect_failure *args
|
28
|
-
lambda{ public_send("inspect_failure_#{inspect_failure_mode}", *args) }
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.inspect_failure_auto object, msg, args, negate
|
32
|
-
if args.size > 1
|
33
|
-
inspect_failure_inline(object, msg, args, negate)
|
34
|
-
elsif object.kind_of?(Hash) && args.first.kind_of?(Hash)
|
35
|
-
inspect_failure_inline(Hash[object.sort], msg,
|
36
|
-
[Hash[args.first.sort]], negate)
|
37
|
-
elsif object.kind_of?(String) && object.size > 400 &&
|
38
|
-
object.count("\n") > 4 && !`which diff`.empty?
|
39
|
-
inspect_failure_diff(object, msg, args, negate)
|
40
|
-
else
|
41
|
-
ins = object.inspect
|
42
|
-
if ins.size > 78
|
43
|
-
inspect_failure_newline(object, msg, args, negate)
|
44
|
-
else
|
45
|
-
inspect_failure_inline( object, msg, args, negate)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.inspect_failure_inline object, msg, args, negate
|
51
|
-
a = args.map(&:inspect).join(', ')
|
52
|
-
"#{object.inspect}.#{msg}(#{a}) to return #{!negate}"
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.inspect_failure_newline object, msg, args, negate
|
56
|
-
a = args.map(&:inspect).join(",\n")
|
57
|
-
"\n#{object.inspect}.#{msg}(\n#{a}) to return #{!negate}"
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.inspect_failure_diff object, msg, args, negate
|
61
|
-
require 'tempfile'
|
62
|
-
Tempfile.open('pork-expect') do |expect|
|
63
|
-
Tempfile.open('pork-was') do |was|
|
64
|
-
expect.puts(object.to_s)
|
65
|
-
expect.close
|
66
|
-
was.puts(args.map(&:to_s).join(",\n"))
|
67
|
-
was.close
|
68
|
-
name = "#{object.class}##{msg}(\n"
|
69
|
-
"#{name}#{`diff #{expect.path} #{was.path}`}) to return #{!negate}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
5
|
module API
|
75
6
|
module_function
|
76
7
|
def before █ Executor.before(&block); end
|
77
8
|
def after █ Executor.after( &block); end
|
78
|
-
def
|
79
|
-
def copy desc=:default, &suite; Executor.copy( desc, &suite); end
|
9
|
+
def copy desc=:default, █ Executor.copy( desc, &block); end
|
80
10
|
def paste desc=:default, *args ; Executor.paste( desc, *args ); end
|
11
|
+
def describe desc=:default, &suite; Executor.describe(desc, &suite); end
|
81
12
|
def would desc=:default, &test ; Executor.would( desc, &test ); end
|
82
13
|
end
|
83
14
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
if block_given? then @before << block else @before end
|
88
|
-
end
|
89
|
-
def after &block
|
90
|
-
if block_given? then @after << block else @after end
|
91
|
-
end
|
92
|
-
def describe desc=:default, &suite
|
93
|
-
Class.new(self){ init("#{desc}: ") }.module_eval(&suite)
|
94
|
-
end
|
95
|
-
def copy desc=:default, &suite; stash[desc] = suite; end
|
96
|
-
def paste desc=:default, *args
|
97
|
-
module_exec(*args, &search_stash(desc))
|
98
|
-
end
|
99
|
-
def would desc=:default, &test
|
100
|
-
assertions = Pork.stats.assertions
|
101
|
-
context = new(desc)
|
102
|
-
run_before(context)
|
103
|
-
context.instance_eval(&test)
|
104
|
-
if assertions == Pork.stats.assertions
|
105
|
-
raise Error.new('Missing assertions')
|
106
|
-
end
|
107
|
-
rescue Error, StandardError => e
|
108
|
-
case e
|
109
|
-
when Skip
|
110
|
-
Pork.stats.incr_skips
|
111
|
-
when Failure
|
112
|
-
Pork.stats.add_failure(e, description_for("would #{desc}"))
|
113
|
-
when Error, StandardError
|
114
|
-
Pork.stats.add_error( e, description_for("would #{desc}"))
|
115
|
-
end
|
116
|
-
else
|
117
|
-
print '.'
|
118
|
-
ensure
|
119
|
-
Pork.stats.incr_tests
|
120
|
-
run_after(context)
|
121
|
-
end
|
122
|
-
|
123
|
-
protected
|
124
|
-
def init desc=''; @desc, @before, @after, @stash = desc, [], [], {}; end
|
125
|
-
def super_executor
|
126
|
-
@super_executor ||= ancestors[1..-1].find{ |a| a <= Executor }
|
127
|
-
end
|
128
|
-
def search_stash desc
|
129
|
-
stash[desc] or super_executor && super_executor.search_stash(desc)
|
130
|
-
end
|
131
|
-
def description_for name=''
|
132
|
-
"#{desc}#{super_executor && super_executor.description_for}#{name}"
|
133
|
-
end
|
134
|
-
def run_before context
|
135
|
-
super_executor && super_executor.run_before(context)
|
136
|
-
before.each{ |b| context.instance_eval(&b) }
|
137
|
-
end
|
138
|
-
def run_after context
|
139
|
-
super_executor && super_executor.run_after(context)
|
140
|
-
after.each{ |b| context.instance_eval(&b) }
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
class Executor < Struct.new(:desc)
|
145
|
-
extend Pork::Imp, Pork::API
|
146
|
-
init
|
147
|
-
def skip ; raise Skip.new("Skipping #{desc}"); end
|
148
|
-
def flunk reason='Flunked'; raise Error.new(reason) ; end
|
149
|
-
def ok ; Pork.stats.incr_assertions ; end
|
15
|
+
# default to :execute while eliminating warnings for uninitialized ivar
|
16
|
+
def self.execute_mode execute=nil
|
17
|
+
@execute = execute || @execute ||= :execute
|
150
18
|
end
|
151
19
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
def method_missing msg, *args, &block
|
161
|
-
satisfy(nil, ::Pork.inspect_failure(@object, msg, args, @negate)) do
|
162
|
-
@object.public_send(msg, *args, &block)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def satisfy desc=@object, desc_lazy=nil
|
167
|
-
result = yield(@object)
|
168
|
-
if !!result == @negate
|
169
|
-
d = desc_lazy && desc_lazy.call || desc
|
170
|
-
m = @message_lazy && @message_lazy.call || @message
|
171
|
-
::Kernel.raise Failure.new("Expect #{d}\n#{m}".chomp)
|
172
|
-
else
|
173
|
-
::Pork.stats.incr_assertions
|
174
|
-
end
|
175
|
-
result
|
176
|
-
end
|
177
|
-
|
178
|
-
def not &checker
|
179
|
-
@negate = !@negate
|
180
|
-
satisfy(&checker) if checker
|
181
|
-
self
|
182
|
-
end
|
183
|
-
|
184
|
-
def eq rhs; self == rhs; end
|
185
|
-
def lt rhs; self < rhs; end
|
186
|
-
def gt rhs; self > rhs; end
|
187
|
-
def lte rhs; self <= rhs; end
|
188
|
-
def gte rhs; self >= rhs; end
|
189
|
-
|
190
|
-
def raise exception=::RuntimeError
|
191
|
-
satisfy("#{__not__}raising #{exception}") do
|
192
|
-
begin
|
193
|
-
if ::Kernel.block_given? then yield else @object.call end
|
194
|
-
rescue exception => e
|
195
|
-
e
|
196
|
-
else
|
197
|
-
false
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def throw msg
|
203
|
-
satisfy("#{__not__}throwing #{msg}") do
|
204
|
-
flag = true
|
205
|
-
data = ::Kernel.catch(msg) do
|
206
|
-
if ::Kernel.block_given? then yield else @object.call end
|
207
|
-
flag = false
|
208
|
-
end
|
209
|
-
flag && [msg, data]
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
private
|
214
|
-
def __not__; if @negate == true then 'not ' else '' end; end
|
215
|
-
end
|
216
|
-
|
217
|
-
class Stats < Struct.new(:tests, :assertions, :skips, :failures, :errors)
|
218
|
-
def initialize
|
219
|
-
@mutex = Mutex.new
|
220
|
-
super(0, 0, 0, [], [])
|
221
|
-
end
|
222
|
-
def incr_assertions; @mutex.synchronize{ self.assertions += 1 }; end
|
223
|
-
def incr_tests ; @mutex.synchronize{ self.tests += 1 }; end
|
224
|
-
def incr_skips ; @mutex.synchronize{ self.skips += 1; print('s')}; end
|
225
|
-
def add_failure *e ; @mutex.synchronize{ failures << e; print('F')}; end
|
226
|
-
def add_error *e ; @mutex.synchronize{ errors << e; print('E')}; end
|
227
|
-
def numbers; [tests, assertions, failures.size, errors.size, skips]; end
|
228
|
-
def start ; @start ||= Time.now ; end
|
229
|
-
def report
|
230
|
-
puts
|
231
|
-
puts (failures + errors).map{ |(e, m)|
|
232
|
-
"\n#{m}\n#{e.class}: #{e.message}\n #{backtrace(e)}"
|
233
|
-
}
|
234
|
-
printf("\nFinished in %f seconds.\n", Time.now - @start)
|
235
|
-
printf("%d tests, %d assertions, %d failures, %d errors, %d skips\n",
|
236
|
-
*numbers)
|
237
|
-
end
|
238
|
-
private
|
239
|
-
def backtrace e
|
240
|
-
if $VERBOSE
|
241
|
-
e.backtrace
|
242
|
-
else
|
243
|
-
e.backtrace.reject{ |line| line =~ %r{/pork\.rb:\d+} }
|
244
|
-
end.join("\n ")
|
20
|
+
def self.autorun auto=true
|
21
|
+
@auto = auto
|
22
|
+
@autorun ||= at_exit do
|
23
|
+
next unless @auto
|
24
|
+
require "pork/mode/#{execute_mode}" unless execute_mode == :execute
|
25
|
+
stat = Executor.public_send(execute_mode)
|
26
|
+
stat.report
|
27
|
+
exit stat.failures.size + stat.errors.size + ($! && 1).to_i
|
245
28
|
end
|
246
29
|
end
|
247
30
|
end
|
data/lib/pork/auto.rb
CHANGED
data/lib/pork/context.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
require 'pork/expect'
|
3
|
+
require 'pork/error'
|
4
|
+
|
5
|
+
module Pork
|
6
|
+
module Context
|
7
|
+
private
|
8
|
+
def expect *args, &block
|
9
|
+
Expect.new(pork_stat, *args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def skip
|
13
|
+
raise Skip.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def flunk reason='Flunked'
|
17
|
+
raise Error.new(reason)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ok
|
21
|
+
pork_stat.incr_assertions
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/pork/env.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Pork
|
3
|
+
class Env < Struct.new(:super_env, :before, :after)
|
4
|
+
def initialize se=nil
|
5
|
+
super(se, [], [])
|
6
|
+
end
|
7
|
+
|
8
|
+
def run_before context
|
9
|
+
super_env && super_env.run_before(context)
|
10
|
+
before.each{ |b| context.instance_eval(&b) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_after context
|
14
|
+
super_env && super_env.run_after(context)
|
15
|
+
after.each{ |b| context.instance_eval(&b) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/pork/error.rb
ADDED
data/lib/pork/expect.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
require 'pork/inspect'
|
3
|
+
|
4
|
+
module Pork
|
5
|
+
class Expect < BasicObject
|
6
|
+
instance_methods.each{ |m| undef_method(m) unless m =~ /^__|^object_id$/ }
|
7
|
+
def initialize stat, object=nil, message=nil, message_lazy=nil, &checker
|
8
|
+
@stat, @object, @negate = stat, object, false
|
9
|
+
@message, @message_lazy = message, message_lazy
|
10
|
+
satisfy(&checker) if checker
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing msg, *args, &block
|
14
|
+
satisfy(nil, Inspect.with(@object, msg, args, @negate)) do
|
15
|
+
@object.public_send(msg, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def satisfy desc=@object, desc_lazy=nil
|
20
|
+
result = yield(@object)
|
21
|
+
if !!result == @negate
|
22
|
+
d = desc_lazy && desc_lazy.call || desc
|
23
|
+
m = @message_lazy && @message_lazy.call || @message
|
24
|
+
::Kernel.raise Failure.new("Expect #{d}\n#{m}".chomp)
|
25
|
+
else
|
26
|
+
@stat.incr_assertions
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
def not &checker
|
32
|
+
@negate = !@negate
|
33
|
+
satisfy(&checker) if checker
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def eq rhs; self == rhs; end
|
38
|
+
def lt rhs; self < rhs; end
|
39
|
+
def gt rhs; self > rhs; end
|
40
|
+
def lte rhs; self <= rhs; end
|
41
|
+
def gte rhs; self >= rhs; end
|
42
|
+
|
43
|
+
def raise exception=::RuntimeError
|
44
|
+
satisfy("#{__not__}raising #{exception}") do
|
45
|
+
begin
|
46
|
+
if ::Kernel.block_given? then yield else @object.call end
|
47
|
+
rescue exception => e
|
48
|
+
e
|
49
|
+
else
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def throw msg
|
56
|
+
satisfy("#{__not__}throwing #{msg}") do
|
57
|
+
flag = true
|
58
|
+
data = ::Kernel.catch(msg) do
|
59
|
+
if ::Kernel.block_given? then yield else @object.call end
|
60
|
+
flag = false
|
61
|
+
end
|
62
|
+
flag && [msg, data]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def __not__; if @negate == true then 'not ' else '' end; end
|
68
|
+
end
|
69
|
+
end
|
data/lib/pork/imp.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
require 'pork/env'
|
3
|
+
require 'pork/stat'
|
4
|
+
require 'pork/error'
|
5
|
+
require 'pork/expect'
|
6
|
+
|
7
|
+
module Pork
|
8
|
+
module Imp
|
9
|
+
attr_reader :desc, :tests
|
10
|
+
|
11
|
+
def before █ @tests << [:before, block]; end
|
12
|
+
def after █ @tests << [:after , block]; end
|
13
|
+
|
14
|
+
def copy desc=:default, &suite
|
15
|
+
@stash[desc] = suite
|
16
|
+
end
|
17
|
+
def paste desc=:default, *args
|
18
|
+
module_exec(*args, &search_stash(desc))
|
19
|
+
end
|
20
|
+
|
21
|
+
def describe desc=:default, &suite
|
22
|
+
executor = Class.new(self){ init("#{desc}: ") }
|
23
|
+
executor.module_eval(&suite)
|
24
|
+
@tests << [:describe, executor]
|
25
|
+
end
|
26
|
+
|
27
|
+
def would desc=:default, &test
|
28
|
+
@tests << [:would, desc, test]
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute stat=Stat.new
|
32
|
+
if block_given?
|
33
|
+
yield(stat)
|
34
|
+
else
|
35
|
+
execute_with_parent(stat)
|
36
|
+
end
|
37
|
+
stat
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def init desc=''
|
42
|
+
@desc, @tests, @stash = desc, [], {}
|
43
|
+
@super_executor = ancestors[1..-1].find{ |a| a <= Executor }
|
44
|
+
end
|
45
|
+
|
46
|
+
def run desc, test, stat, env
|
47
|
+
assertions = stat.assertions
|
48
|
+
context = new(stat)
|
49
|
+
run_protected(desc, stat) do
|
50
|
+
env.run_before(context)
|
51
|
+
context.instance_eval(&test)
|
52
|
+
if assertions == stat.assertions
|
53
|
+
raise Error.new('Missing assertions')
|
54
|
+
end
|
55
|
+
stat.io.print '.'
|
56
|
+
end
|
57
|
+
ensure
|
58
|
+
stat.incr_tests
|
59
|
+
run_protected(desc, stat){ env.run_after(context) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_protected desc, stat
|
63
|
+
yield
|
64
|
+
rescue Error, StandardError => e
|
65
|
+
case e
|
66
|
+
when Skip
|
67
|
+
stat.incr_skips
|
68
|
+
stat.io.print 's'
|
69
|
+
when Failure
|
70
|
+
stat.add_failure(e, description_for("would #{desc}"))
|
71
|
+
stat.io.print 'F'
|
72
|
+
when Error, StandardError
|
73
|
+
stat.add_error( e, description_for("would #{desc}"))
|
74
|
+
stat.io.print 'E'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
def execute_with_parent stat, super_env=nil
|
80
|
+
env = Env.new(super_env)
|
81
|
+
@tests.each do |(type, arg, test)|
|
82
|
+
case type
|
83
|
+
when :before
|
84
|
+
env.before << arg
|
85
|
+
when :after
|
86
|
+
env.after << arg
|
87
|
+
when :describe
|
88
|
+
arg.execute_with_parent(stat, env)
|
89
|
+
when :would
|
90
|
+
run(arg, test, stat, env)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def search_stash desc
|
96
|
+
@stash[desc] or @super_executor && @super_executor.search_stash(desc)
|
97
|
+
end
|
98
|
+
|
99
|
+
def description_for name=''
|
100
|
+
"#{@desc}#{@super_executor && @super_executor.description_for}#{name}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|