pork 0.1.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,71 +1,57 @@
1
1
 
2
2
  require 'thread'
3
3
 
4
- module Pork
5
- class Stats < Struct.new(:tests, :assertions, :skips, :failures, :errors)
6
- def initialize
7
- @mutex = Mutex.new
8
- super(0, 0, 0, [], [])
9
- end
10
- def assertions= num; @mutex.synchronize{ super }; end
11
- def tests= num; @mutex.synchronize{ super }; end
12
- def skips= num; @mutex.synchronize{ super ; print('s')}; end
13
- def add_failure *e ; @mutex.synchronize{ failures << e; print('F')}; end
14
- def add_error *e ; @mutex.synchronize{ errors << e; print('E')}; end
15
- def numbers
16
- [tests, assertions, failures.size, errors.size, skips]
17
- end
18
- def start
19
- @start ||= Time.now
20
- end
21
- def report
22
- puts
23
- puts (failures + errors).map{ |(e, m)|
24
- "\n#{m}\n#{e.class}: #{e.message}\n #{backtrace(e)}"
25
- }
26
- printf("\nFinished in %f seconds.\n", Time.now - @start)
27
- printf("%d tests, %d assertions, %d failures, %d errors, %d skips\n",
28
- *numbers)
29
- end
30
- private
31
- def backtrace e
32
- if $VERBOSE
33
- e.backtrace
34
- else
35
- e.backtrace.reject{ |line| line =~ %r{/pork\.rb:\d+} }
36
- end.join("\n ")
37
- end
4
+ module Kernel
5
+ def should message=nil, &checker
6
+ Pork::Should.new(self, message, &checker)
38
7
  end
8
+ end
9
+
10
+ module Pork
11
+ Error = Class.new(Exception)
12
+ Failure = Class.new(Error)
13
+ Skip = Class.new(Error)
39
14
 
40
15
  def self.stats ; @stats ||= Stats.new; end
41
16
  def self.reset ; @stats = nil ; end
42
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
23
+ end
24
+ end
43
25
 
44
26
  module API
45
27
  module_function
46
- def describe desc, &suite
47
- Pork.stats.start
48
- Pork::Executor.execute(self, desc, &suite)
49
- Pork.stats.tests += 1
50
- end
28
+ def before &block; Executor.before(&block); end
29
+ def after &block; Executor.after( &block); end
30
+ def describe desc=:default, &suite; Executor.describe(desc, &suite); end
31
+ def copy desc=:default, &suite; Executor.copy( desc, &suite); end
32
+ def paste desc=:default, *args ; Executor.paste( desc, *args ); end
33
+ def would desc=:default, &test ; Executor.would( desc, &test ); end
51
34
  end
52
35
 
53
- Error = Class.new(Exception)
54
- Failure = Class.new(Error)
55
- Skip = Class.new(Error)
56
-
57
- class Executor < Struct.new(:name)
58
- extend Pork::API
59
- def self.execute caller, desc, &suite
60
- parent = if caller.kind_of?(Class) then caller else self end
61
- Class.new(parent){
62
- @desc, @before, @after = "#{desc}:", [], []
63
- }.module_eval(&suite)
36
+ module Imp
37
+ attr_reader :stash, :desc
38
+ def before &block
39
+ if block_given? then @before << block else @before end
64
40
  end
65
-
66
- def self.would name, &test
41
+ def after &block
42
+ if block_given? then @after << block else @after end
43
+ end
44
+ def describe desc=:default, &suite
45
+ Class.new(self){ init("#{desc}: ") }.module_eval(&suite)
46
+ end
47
+ def copy desc=:default, &suite; stash[desc] = suite; end
48
+ def paste desc=:default, *args
49
+ stashes = [self, super_executor].compact.map(&:stash)
50
+ module_exec(*args, &stashes.find{ |s| s[desc] }[desc])
51
+ end
52
+ def would desc=:default, &test
67
53
  assertions = Pork.stats.assertions
68
- context = new(name)
54
+ context = new(desc)
69
55
  run_before(context)
70
56
  context.instance_eval(&test)
71
57
  if assertions == Pork.stats.assertions
@@ -74,55 +60,81 @@ module Pork
74
60
  rescue Error, StandardError => e
75
61
  case e
76
62
  when Skip
77
- Pork.stats.skips += 1
63
+ Pork.stats.incr_skips
78
64
  when Failure
79
- Pork.stats.add_failure(e, description_for("would #{name}"))
65
+ Pork.stats.add_failure(e, description_for("would #{desc}"))
80
66
  when Error, StandardError
81
- Pork.stats.add_error( e, description_for("would #{name}"))
67
+ Pork.stats.add_error( e, description_for("would #{desc}"))
82
68
  end
83
69
  else
84
70
  print '.'
85
71
  ensure
72
+ Pork.stats.incr_tests
86
73
  run_after(context)
87
74
  end
88
75
 
89
- def self.before &block
90
- if block_given? then @before << block else @before end
76
+ protected
77
+ def init desc=''
78
+ @desc, @before, @after, @stash = desc, [], [], {}
91
79
  end
92
- def self.after &block
93
- if block_given? then @after << block else @after end
94
- end
95
-
96
- def self.super_executor
97
- @super_executor ||= ancestors[1..-1].find{ |a| a < Executor }
80
+ def super_executor
81
+ @super_executor ||= ancestors[1..-1].find{ |a| a <= Executor }
98
82
  end
99
-
100
- def self.description_for name=''
101
- supername = if super_executor
102
- " #{super_executor.description_for}"
103
- else
104
- ' '
105
- end
106
- "#{@desc}#{supername}#{name}"
83
+ def description_for name=''
84
+ "#{desc}#{super_executor && super_executor.description_for}#{name}"
107
85
  end
108
-
109
- def self.run_before context
86
+ def run_before context
110
87
  super_executor.run_before(context) if super_executor
111
88
  before.each{ |b| context.instance_eval(&b) }
112
89
  end
113
-
114
- def self.run_after context
90
+ def run_after context
115
91
  super_executor.run_after(context) if super_executor
116
92
  after.each{ |b| context.instance_eval(&b) }
117
93
  end
94
+ end
95
+
96
+ class Executor < Struct.new(:desc)
97
+ extend Pork::Imp, Pork::API
98
+ init
99
+ def skip ; raise Skip.new("Skipping #{desc}"); end
100
+ def flunk reason='Flunked'; raise Error.new(reason) ; end
101
+ def ok ; Pork.stats.incr_assertions ; end
102
+ end
103
+
104
+ module InspectInlineError
105
+ def inspect_error object, msg, args, negate
106
+ a = args.map(&:inspect).join(', ')
107
+ "#{object.inspect}.#{msg}(#{a}) to return #{!negate}"
108
+ end
109
+ end
118
110
 
119
- def skip
120
- raise Skip.new("Skipping #{name}")
111
+ module InspectNewlineError
112
+ def inspect_error object, msg, args, negate
113
+ a = args.map(&:inspect).join(', ')
114
+ "\n#{object.inspect}.#{msg}(\n#{a}) to return #{!negate}"
115
+ end
116
+ end
117
+
118
+ module InspectDiffError
119
+ def inspect_error object, msg, args, negate
120
+ ::Kernel.require 'tempfile'
121
+ ::Tempfile.open('pork-expect') do |expect|
122
+ ::Tempfile.open('pork-was') do |was|
123
+ expect.puts(object.to_s)
124
+ expect.close
125
+ was.puts(args.map(&:to_s).join(",\n"))
126
+ was.close
127
+ name = "#{object.class}##{msg}(\n"
128
+ diff = ::Kernel.__send__(:`, "diff #{expect.path} #{was.path}")
129
+ "#{name}#{diff}) to return #{!negate}"
130
+ end
131
+ end
121
132
  end
122
133
  end
123
134
 
124
135
  class Should < BasicObject
125
136
  instance_methods.each{ |m| undef_method(m) unless m =~ /^__|^object_id$/ }
137
+ include ::Pork::InspectInlineError
126
138
 
127
139
  def initialize object, message, &checker
128
140
  @object = object
@@ -132,8 +144,7 @@ module Pork
132
144
  end
133
145
 
134
146
  def method_missing msg, *args, &block
135
- satisfy("#{@object.inspect}.#{msg}(#{args.join(', ')}) to" \
136
- " return #{!@negate}") do
147
+ satisfy(inspect_error(@object, msg, args, @negate)) do
137
148
  @object.public_send(msg, *args, &block)
138
149
  end
139
150
  end
@@ -143,7 +154,7 @@ module Pork
143
154
  if !!result == @negate
144
155
  ::Kernel.raise Failure.new("Expect #{desc}\n#{@message}".chomp)
145
156
  else
146
- ::Pork.stats.assertions += 1
157
+ ::Pork.stats.incr_assertions
147
158
  end
148
159
  result
149
160
  end
@@ -154,9 +165,11 @@ module Pork
154
165
  self
155
166
  end
156
167
 
157
- def eq rhs
158
- self == rhs
159
- end
168
+ def eq rhs; self == rhs; end
169
+ def lt rhs; self < rhs; end
170
+ def gt rhs; self > rhs; end
171
+ def lte rhs; self <= rhs; end
172
+ def gte rhs; self >= rhs; end
160
173
 
161
174
  def raise exception=::RuntimeError
162
175
  satisfy("#{__not__}raising #{exception}") do
@@ -173,18 +186,14 @@ module Pork
173
186
  def throw msg
174
187
  satisfy("#{__not__}throwing #{msg}") do
175
188
  flag = true
176
- ::Kernel.catch(msg) do
189
+ data = ::Kernel.catch(msg) do
177
190
  if ::Kernel.block_given? then yield else @object.call end
178
191
  flag = false
179
192
  end
180
- flag
193
+ flag && [msg, data]
181
194
  end
182
195
  end
183
196
 
184
- def flunk reason='Flunked'
185
- ::Kernel.raise Error.new(reason)
186
- end
187
-
188
197
  private
189
198
  def __not__
190
199
  if @negate == true
@@ -194,10 +203,39 @@ module Pork
194
203
  end
195
204
  end
196
205
  end
197
- end
198
206
 
199
- module Kernel
200
- def should message=nil, &checker
201
- Pork::Should.new(self, message, &checker)
207
+ class Stats < Struct.new(:tests, :assertions, :skips, :failures, :errors)
208
+ def initialize
209
+ @mutex = Mutex.new
210
+ super(0, 0, 0, [], [])
211
+ end
212
+ def incr_assertions; @mutex.synchronize{ self.assertions += 1 }; end
213
+ def incr_tests ; @mutex.synchronize{ self.tests += 1 }; end
214
+ def incr_skips ; @mutex.synchronize{ self.skips += 1; print('s')}; end
215
+ def add_failure *e ; @mutex.synchronize{ failures << e; print('F')}; end
216
+ def add_error *e ; @mutex.synchronize{ errors << e; print('E')}; end
217
+ def numbers
218
+ [tests, assertions, failures.size, errors.size, skips]
219
+ end
220
+ def start
221
+ @start ||= Time.now
222
+ end
223
+ def report
224
+ puts
225
+ puts (failures + errors).map{ |(e, m)|
226
+ "\n#{m}\n#{e.class}: #{e.message}\n #{backtrace(e)}"
227
+ }
228
+ printf("\nFinished in %f seconds.\n", Time.now - @start)
229
+ printf("%d tests, %d assertions, %d failures, %d errors, %d skips\n",
230
+ *numbers)
231
+ end
232
+ private
233
+ def backtrace e
234
+ if $VERBOSE
235
+ e.backtrace
236
+ else
237
+ e.backtrace.reject{ |line| line =~ %r{/pork\.rb:\d+} }
238
+ end.join("\n ")
239
+ end
202
240
  end
203
241
  end
@@ -0,0 +1,4 @@
1
+
2
+ require 'pork'
3
+ extend Pork::API
4
+ Pork.report_at_exit
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Pork
3
- VERSION = '0.1.0'
3
+ VERSION = '0.9.0'
4
4
  end
@@ -1,36 +1,39 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: pork 0.1.0 ruby lib
2
+ # stub: pork 0.9.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "pork"
6
- s.version = "0.1.0"
6
+ s.version = "0.9.0"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib"]
10
10
  s.authors = ["Lin Jen-Shin (godfat)"]
11
- s.date = "2014-07-09"
12
- s.description = "[Bacon][] reimplemented in an even more lightweight manner.\n\n[Bacon]: https://github.com/chneukirchen/bacon"
11
+ s.date = "2014-07-11"
12
+ s.description = "Pork -- Simple and clean and modular testing library.\n\n[Bacon][] reimplemented around 250 lines of code.\n\n[Bacon]: https://github.com/chneukirchen/bacon"
13
13
  s.email = ["godfat (XD) godfat.org"]
14
14
  s.files = [
15
+ ".gitignore",
15
16
  ".gitmodules",
16
17
  ".travis.yml",
18
+ "CHANGES.md",
17
19
  "LICENSE",
18
20
  "README.md",
19
21
  "Rakefile",
20
22
  "lib/pork.rb",
21
- "lib/pork/task.rb",
23
+ "lib/pork/auto.rb",
22
24
  "lib/pork/version.rb",
23
- "pkg/pork-0.1.0.gem",
24
25
  "pork.gemspec",
25
26
  "task/README.md",
26
27
  "task/gemgem.rb",
27
28
  "test/test_bacon.rb",
28
- "test/test_nested.rb"]
29
+ "test/test_nested.rb",
30
+ "test/test_readme.rb"]
29
31
  s.homepage = "https://github.com/godfat/pork"
30
32
  s.licenses = ["Apache License 2.0"]
31
33
  s.rubygems_version = "2.3.0"
32
- s.summary = "[Bacon][] reimplemented in an even more lightweight manner."
34
+ s.summary = "Pork -- Simple and clean and modular testing library."
33
35
  s.test_files = [
34
36
  "test/test_bacon.rb",
35
- "test/test_nested.rb"]
37
+ "test/test_nested.rb",
38
+ "test/test_readme.rb"]
36
39
  end
@@ -227,10 +227,6 @@ end # of gem namespace
227
227
  desc 'Run tests'
228
228
  task :test do
229
229
  next if Gemgem.test_files.empty?
230
-
231
- # require 'bacon'
232
- # Bacon.extend(Bacon::TestUnitOutput)
233
- # Bacon.summary_on_exit
234
230
  Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
235
231
  end
236
232
 
@@ -1,23 +1,21 @@
1
1
 
2
- require 'pork'
2
+ require 'pork/auto'
3
3
 
4
- # Hooray for meta-testing.
5
- module MetaTests
6
- def succeed block
7
- block.should.not.raise Pork::Error
8
- end
9
-
10
- def fail block
11
- block.should.raise Pork::Error
12
- end
4
+ describe Pork do
5
+ # Hooray for meta-testing.
6
+ include Module.new{
7
+ def succeed block
8
+ block.should.not.raise Pork::Error
9
+ end
13
10
 
14
- def equal_string x
15
- lambda{ |s| x == s.to_s }
16
- end
17
- end
11
+ def fail block
12
+ block.should.raise Pork::Error
13
+ end
18
14
 
19
- Pork::API.describe Pork do
20
- include MetaTests
15
+ def equal_string x
16
+ lambda{ |s| x == s.to_s }
17
+ end
18
+ }
21
19
 
22
20
  would "have should.satisfy" do
23
21
  succeed lambda { should.satisfy { 1 == 1 } }
@@ -221,13 +219,13 @@ Pork::API.describe Pork do
221
219
  succeed lambda { (1+2).should.not(&f) }
222
220
  end
223
221
 
224
- would "have should.flunk" do
225
- fail lambda { should.flunk }
226
- fail lambda { should.flunk "yikes" }
222
+ would "have flunk" do
223
+ fail lambda { flunk }
224
+ fail lambda { flunk "yikes" }
227
225
  end
228
226
  end
229
227
 
230
- Pork::API.describe "before/after" do
228
+ describe "before/after" do
231
229
  before do
232
230
  @a = 1
233
231
  @b = 2
@@ -287,89 +285,97 @@ Pork::API.describe "before/after" do
287
285
  end
288
286
  end
289
287
 
290
- # shared "a shared context" do
291
- # it "gets called where it is included" do
292
- # true.should.be.true
293
- # end
294
- # end
295
-
296
- # shared "another shared context" do
297
- # it "can access data" do
298
- # @magic.should.be.equal 42
299
- # end
300
- # end
301
-
302
- # describe "shared/behaves_like" do
303
- # behaves_like "a shared context"
304
-
305
- # ctx = self
306
- # it "raises NameError when the context is not found" do
307
- # lambda {
308
- # ctx.behaves_like "whoops"
309
- # }.should.raise NameError
310
- # end
311
-
312
- # behaves_like "a shared context"
313
-
314
- # before {
315
- # @magic = 42
316
- # }
317
- # behaves_like "another shared context"
318
- # end
319
-
320
- # describe "Methods" do
321
- # def the_meaning_of_life
322
- # 42
323
- # end
324
-
325
- # def the_towels
326
- # yield "DON'T PANIC"
327
- # end
328
-
329
- # it "should be accessible in a test" do
330
- # the_meaning_of_life.should == 42
331
- # end
332
-
333
- # describe "when in a sibling context" do
334
- # it "should be accessible in a test" do
335
- # the_meaning_of_life.should == 42
336
- # end
337
-
338
- # it "should pass the block" do
339
- # the_towels do |label|
340
- # label.should == "DON'T PANIC"
341
- # end.should == true
342
- # end
343
- # end
344
- # end
345
-
346
- # describe 'describe arguments' do
347
-
348
- # def check(ctx,name)
349
- # ctx.should.be.an.instance_of Bacon::Context
350
- # ctx.instance_variable_get('@name').should == name
351
- # end
352
-
353
- # it 'should work with string' do
354
- # check(describe('string') {},'string')
355
- # end
356
-
357
- # it 'should work with symbols' do
358
- # check(describe(:behaviour) {},'behaviour')
359
- # end
360
-
361
- # it 'should work with modules' do
362
- # check(describe(Bacon) {},'Bacon')
363
- # end
364
-
365
- # it 'should work with namespaced modules' do
366
- # check(describe(Bacon::Context) {},'Bacon::Context')
367
- # end
368
-
369
- # it 'should work with multiple arguments' do
370
- # check(describe(Bacon::Context, :empty) {},'Bacon::Context empty')
371
- # end
372
-
373
- # end
374
-
375
- Pork.report
288
+ copy "a shared context" do
289
+ would "get called where it is included" do
290
+ true.should.eq true
291
+ end
292
+ end
293
+
294
+ copy "another shared context" do
295
+ would "access data" do
296
+ @magic.should.eq 42
297
+ end
298
+ end
299
+
300
+ describe "shared/behaves_like" do
301
+ paste "a shared context"
302
+
303
+ ctx = self
304
+ would "raise NameError when the context is not found" do
305
+ lambda {
306
+ ctx.paste "whoops"
307
+ }.should.raise NameError
308
+ end
309
+
310
+ paste "a shared context"
311
+
312
+ before {
313
+ @magic = 42
314
+ }
315
+ paste "another shared context"
316
+ end
317
+
318
+ describe "Methods" do
319
+ def the_meaning_of_life
320
+ 42
321
+ end
322
+
323
+ def the_towels
324
+ yield "DON'T PANIC"
325
+ end
326
+
327
+ would "be accessible in a test" do
328
+ the_meaning_of_life.should.eq 42
329
+ end
330
+
331
+ describe "when in a sibling context" do
332
+ would "should be accessible in a test" do
333
+ the_meaning_of_life.should.eq 42
334
+ end
335
+
336
+ would "should pass the block" do
337
+ the_towels do |label|
338
+ label.should.eq "DON'T PANIC"
339
+ end.should.eq true
340
+ end
341
+ end
342
+ end
343
+
344
+ describe 'describe arguments' do
345
+ check = lambda do |ctx, desc, name=nil|
346
+ ctx.should.lt Pork::Executor
347
+ ctx.description_for(name).should.eq "#{desc}: #{name}"
348
+ end
349
+
350
+ would 'work with string' do
351
+ str = 'string'
352
+ Pork::API.describe(str) do
353
+ check[self, str]
354
+ would 'a' do check[self.class, str, 'a'] end
355
+ end
356
+ end
357
+
358
+ would 'work with symbols' do
359
+ str = 'behaviour'
360
+ Pork::API.describe(:behaviour) do
361
+ check[self, str]
362
+ would 'b' do check[self.class, str, 'b'] end
363
+ end
364
+ end
365
+
366
+ would 'work with modules' do
367
+ str = 'Pork'
368
+ Pork::API.describe(Pork) do
369
+ check[self, str]
370
+ would 'c' do check[self.class, str, 'c'] end
371
+ end
372
+ end
373
+
374
+ would 'work with namespaced modules' do
375
+ str = 'Pork::Executor'
376
+ Pork::API.describe(Pork::Executor) do
377
+ check[self, str]
378
+ would 'd' do check[self.class, str, 'd'] end
379
+ end
380
+ end
381
+ end