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.
@@ -0,0 +1,97 @@
1
+
2
+ module Pork
3
+ # default to :auto while eliminating warnings for uninitialized ivar
4
+ def self.inspect_failure_mode inspect=nil
5
+ @inspect = inspect || @inspect ||= :auto
6
+ end
7
+
8
+ class Inspect < Struct.new(:flip)
9
+ def self.with *args
10
+ lambda{ public_send("with_#{Pork.inspect_failure_mode}", *args) }
11
+ end
12
+
13
+ def self.with_auto expect, msg, args, negate
14
+ if args.size > 1
15
+ with_inline(expect, msg, args, negate)
16
+
17
+ elsif expect.kind_of?(Hash) && args.first.kind_of?(Hash)
18
+ if expect.inspect.size > 78
19
+ for_diff_hash(msg, negate,
20
+ Inspect.new(true).diff_hash(expect, args.first).
21
+ merge(Inspect.new(false).diff_hash(args.first, expect)))
22
+ else
23
+ with_inline(Hash[expect.sort], msg, [Hash[args.first.sort]], negate)
24
+ end
25
+
26
+ elsif expect.kind_of?(String) && expect.size > 400 &&
27
+ expect.count("\n") > 4 && !`which diff`.empty?
28
+ with_diff(expect, msg, args, negate)
29
+
30
+ elsif expect.inspect.size > 78
31
+ with_newline(expect, msg, args, negate)
32
+
33
+ else
34
+ with_inline( expect, msg, args, negate)
35
+ end
36
+ end
37
+
38
+ def self.for_diff_hash msg, negate, result
39
+ "\n" + result.map do |key, (expect, actual)|
40
+ body = with_auto(expect, msg, [actual], negate)
41
+ "\tHash with key path: #{key.inspect}\n#{body.sub(/\A\n/, '')}"
42
+ end.join("\n\n")
43
+ end
44
+
45
+ def self.with_inline expect, msg, args, negate
46
+ a = args.map(&:inspect).join(', ')
47
+ "#{expect.inspect}.#{msg}(#{a}) to return #{!negate}"
48
+ end
49
+
50
+ def self.with_newline expect, msg, args, negate
51
+ a = args.map(&:inspect).join(",\n")
52
+ "\n#{expect.inspect}.#{msg}(\n#{a}) to return #{!negate}"
53
+ end
54
+
55
+ def self.with_diff expect, msg, args, negate
56
+ require 'tempfile'
57
+ Tempfile.open('pork-expect') do |its|
58
+ Tempfile.open('pork-was') do |was|
59
+ its.puts(expect.to_s)
60
+ its.close
61
+ was.puts(args.map(&:to_s).join(",\n"))
62
+ was.close
63
+ name = "#{expect.class}##{msg}(\n"
64
+ "#{name}#{`diff #{its.path} #{was.path}`}) to return #{!negate}"
65
+ end
66
+ end
67
+ end
68
+
69
+ def diff_hash expect, actual, result={}, prefix=''
70
+ expect.inject(result) do |r, (key, e)|
71
+ diff_object(e, actual[key], r, "#{prefix}#{key}")
72
+ end
73
+ end
74
+
75
+ def diff_array expect, actual, result={}, prefix=''
76
+ expect.each.with_index.inject(result) do |r, (e, idx)|
77
+ diff_object(e, actual[idx], r, "#{prefix}#{idx}")
78
+ end
79
+ end
80
+
81
+ def diff_object expect, actual, result, prefix
82
+ return result if expect == actual
83
+
84
+ if expect.kind_of?(Hash) && actual.kind_of?(Hash)
85
+ diff_hash(expect, actual, result, "#{prefix}:")
86
+ elsif expect.kind_of?(Array) && actual.kind_of?(Array)
87
+ diff_array(expect, actual, result, "#{prefix}:")
88
+ elsif flip
89
+ result[prefix] = [actual, expect]
90
+ else
91
+ result[prefix] = [expect, actual]
92
+ end
93
+
94
+ result
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,55 @@
1
+
2
+ require 'pork'
3
+ require 'pork/executor'
4
+
5
+ module Pork
6
+ module Isolate
7
+ def all_tests
8
+ @all_tests ||= Hash[build_all_tests]
9
+ end
10
+
11
+ def isolate name, stat=Stat.new
12
+ execute(stat) do |s|
13
+ execute_with_isolation(all_tests[name], s)
14
+ end
15
+ end
16
+
17
+ protected
18
+ def build_all_tests paths=[]
19
+ @tests.flat_map.with_index do |(type, arg, _), index|
20
+ current = paths + [index]
21
+ case type
22
+ when :describe
23
+ arg.build_all_tests(current)
24
+ when :would
25
+ [["#{desc.chomp(': ')} #{arg} ##{current}", current]]
26
+ else
27
+ []
28
+ end
29
+ end
30
+ end
31
+
32
+ def execute_with_isolation paths, stat, super_env=nil
33
+ env = Env.new(super_env)
34
+ idx = paths.first
35
+
36
+ @tests.first(idx).each do |(type, arg, _)|
37
+ case type
38
+ when :before
39
+ env.before << arg
40
+ when :after
41
+ env.after << arg
42
+ end
43
+ end
44
+
45
+ if paths.size == 1
46
+ _, desc, test = @tests[idx]
47
+ run(desc, test, stat, env)
48
+ else
49
+ @tests[idx][1].execute_with_isolation(paths.drop(1), stat, env)
50
+ end
51
+ end
52
+ end
53
+
54
+ Executor.extend(Isolate)
55
+ end
@@ -0,0 +1,16 @@
1
+
2
+ require 'pork'
3
+ require 'pork/isolate'
4
+
5
+ module Pork
6
+ module Parallel
7
+ def parallel cores=8, stat=Stat.new
8
+ all_tests.keys.shuffle.each_slice(cores).map do |names|
9
+ Thread.new{ names.each{ |n| isolate(n, stat) } }
10
+ end.each(&:join)
11
+ stat
12
+ end
13
+ end
14
+
15
+ Executor.extend(Parallel)
16
+ end
@@ -0,0 +1,16 @@
1
+
2
+ require 'pork'
3
+ require 'pork/isolate'
4
+
5
+ module Pork
6
+ module Shuffle
7
+ def shuffle stat=Stat.new
8
+ all_tests.keys.shuffle.each do |name|
9
+ isolate(name, stat)
10
+ end
11
+ stat
12
+ end
13
+ end
14
+
15
+ Executor.extend(Shuffle)
16
+ end
@@ -0,0 +1,25 @@
1
+
2
+ require 'pork/executor'
3
+
4
+ module Kernel
5
+ def should *args, &block
6
+ Pork::Expect.new(
7
+ Thread.current.group.list.first[:pork_stat], self, *args, &block)
8
+ end
9
+ end
10
+
11
+ module Pork
12
+ module Should
13
+ def execute stat=Stat.new
14
+ thread = Thread.current
15
+ original_group, group = thread.group, ThreadGroup.new
16
+ group.add(thread)
17
+ thread[:pork_stat] = stat
18
+ super
19
+ ensure
20
+ original_group.add(thread)
21
+ end
22
+ end
23
+
24
+ Executor.extend(Should)
25
+ end
data/lib/pork/stat.rb ADDED
@@ -0,0 +1,38 @@
1
+
2
+ require 'thread'
3
+
4
+ module Pork
5
+ class Stat < Struct.new(:tests, :assertions, :skips,
6
+ :failures, :errors, :start, :io)
7
+ def initialize io=$stdout
8
+ @mutex = Mutex.new
9
+ super(0, 0, 0, [], [], Time.now, io)
10
+ end
11
+
12
+ def incr_assertions; @mutex.synchronize{ self.assertions += 1 }; end
13
+ def incr_tests ; @mutex.synchronize{ self.tests += 1 }; end
14
+ def incr_skips ; @mutex.synchronize{ self.skips += 1 }; end
15
+ def add_failure *e ; @mutex.synchronize{ failures << e }; end
16
+ def add_error *e ; @mutex.synchronize{ errors << e }; end
17
+ def passed?; failures.size + errors.size == 0 ; end
18
+ def numbers; [tests, assertions, failures.size, errors.size, skips]; end
19
+ def report
20
+ io.puts
21
+ io.puts (failures + errors).map{ |(e, m)|
22
+ "\n#{m}\n#{e.class}: #{e.message}\n #{backtrace(e)}"
23
+ }
24
+ io.printf("\nFinished in %f seconds.\n", Time.now - start)
25
+ io.printf("%d tests, %d assertions, %d failures, %d errors, %d skips\n",
26
+ *numbers)
27
+ end
28
+
29
+ private
30
+ def backtrace e
31
+ if $VERBOSE
32
+ e.backtrace
33
+ else
34
+ e.backtrace.reject{ |line| line =~ %r{/pork(/\w+)?\.rb:\d+} }
35
+ end.join("\n ")
36
+ end
37
+ end
38
+ end
data/lib/pork/test.rb ADDED
@@ -0,0 +1,17 @@
1
+
2
+ require 'pork/auto'
3
+ require 'muack'
4
+
5
+ copy do
6
+ before do
7
+ Muack::API.stub(Pork::Executor.all_tests).keys.peek_return do |names|
8
+ names.reject do |n|
9
+ n =~ /^Pork::(Isolate|Shuffle|Parallel) /
10
+ end
11
+ end
12
+ end
13
+
14
+ after do
15
+ Muack.reset
16
+ end
17
+ end
data/lib/pork/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Pork
3
- VERSION = '0.9.2'
3
+ VERSION = '1.0.0'
4
4
  end
data/pork.gemspec CHANGED
@@ -1,39 +1,74 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: pork 0.9.2 ruby lib
2
+ # stub: pork 1.0.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "pork"
6
- s.version = "0.9.2"
6
+ s.version = "1.0.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-11-07"
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"
11
+ s.date = "2014-11-20"
12
+ s.description = "Pork -- Simple and clean and modular testing library.\n\nInspired by [Bacon][].\n\n[Bacon]: https://github.com/chneukirchen/bacon"
13
13
  s.email = ["godfat (XD) godfat.org"]
14
14
  s.files = [
15
15
  ".gitignore",
16
16
  ".gitmodules",
17
17
  ".travis.yml",
18
18
  "CHANGES.md",
19
+ "Gemfile",
19
20
  "LICENSE",
20
21
  "README.md",
21
22
  "Rakefile",
23
+ "lib/mutant/integration/pork.rb",
22
24
  "lib/pork.rb",
23
25
  "lib/pork/auto.rb",
26
+ "lib/pork/context.rb",
27
+ "lib/pork/env.rb",
28
+ "lib/pork/error.rb",
29
+ "lib/pork/executor.rb",
30
+ "lib/pork/expect.rb",
31
+ "lib/pork/imp.rb",
32
+ "lib/pork/inspect.rb",
33
+ "lib/pork/isolate.rb",
34
+ "lib/pork/mode/parallel.rb",
35
+ "lib/pork/mode/shuffle.rb",
36
+ "lib/pork/should.rb",
37
+ "lib/pork/stat.rb",
38
+ "lib/pork/test.rb",
24
39
  "lib/pork/version.rb",
25
40
  "pork.gemspec",
26
41
  "task/README.md",
27
42
  "task/gemgem.rb",
28
43
  "test/test_bacon.rb",
44
+ "test/test_inspect.rb",
45
+ "test/test_isolate.rb",
29
46
  "test/test_nested.rb",
30
- "test/test_readme.rb"]
47
+ "test/test_parallel.rb",
48
+ "test/test_readme.rb",
49
+ "test/test_shuffle.rb"]
31
50
  s.homepage = "https://github.com/godfat/pork"
32
51
  s.licenses = ["Apache License 2.0"]
33
- s.rubygems_version = "2.4.2"
52
+ s.rubygems_version = "2.4.4"
34
53
  s.summary = "Pork -- Simple and clean and modular testing library."
35
54
  s.test_files = [
36
55
  "test/test_bacon.rb",
56
+ "test/test_inspect.rb",
57
+ "test/test_isolate.rb",
37
58
  "test/test_nested.rb",
38
- "test/test_readme.rb"]
59
+ "test/test_parallel.rb",
60
+ "test/test_readme.rb",
61
+ "test/test_shuffle.rb"]
62
+
63
+ if s.respond_to? :specification_version then
64
+ s.specification_version = 4
65
+
66
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
67
+ s.add_development_dependency(%q<muack>, [">= 0"])
68
+ else
69
+ s.add_dependency(%q<muack>, [">= 0"])
70
+ end
71
+ else
72
+ s.add_dependency(%q<muack>, [">= 0"])
73
+ end
39
74
  end
data/task/gemgem.rb CHANGED
@@ -38,6 +38,99 @@ module Gemgem
38
38
  self.spec = spec
39
39
  end
40
40
 
41
+ def gem_install
42
+ require 'rubygems/commands/install_command'
43
+ # read ~/.gemrc
44
+ Gem.use_paths(Gem.configuration[:gemhome], Gem.configuration[:gempath])
45
+ Gem::Command.extra_args = Gem.configuration[:gem]
46
+
47
+ # setup install options
48
+ cmd = Gem::Commands::InstallCommand.new
49
+ cmd.handle_options([])
50
+
51
+ # install
52
+ install = Gem::Installer.new(gem_path, cmd.options)
53
+ install.install
54
+ puts "\e[35mGem installed: \e[33m#{strip_path(install.gem_dir)}\e[0m"
55
+ end
56
+
57
+ def gem_spec
58
+ create
59
+ write
60
+ end
61
+
62
+ def gem_build
63
+ require 'fileutils'
64
+ require 'rubygems/package'
65
+ gem = nil
66
+ Dir.chdir(dir) do
67
+ gem = Gem::Package.build(Gem::Specification.load(spec_path))
68
+ FileUtils.mkdir_p(pkg_dir)
69
+ FileUtils.mv(gem, pkg_dir) # gem is relative path, but might be ok
70
+ end
71
+ puts "\e[35mGem built: \e[33m#{strip_path("#{pkg_dir}/#{gem}")}\e[0m"
72
+ end
73
+
74
+ def gem_release
75
+ sh_git('tag', gem_tag)
76
+ sh_git('push')
77
+ sh_git('push', '--tags')
78
+ sh_gem('push', gem_path)
79
+ end
80
+
81
+ def gem_check
82
+ ver = spec.version.to_s
83
+
84
+ if ENV['VERSION'].nil?
85
+ puts("\e[35mExpected " \
86
+ "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m")
87
+ exit(1)
88
+
89
+ elsif ENV['VERSION'] != ver
90
+ puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \
91
+ "\e[35mbut got\n " \
92
+ "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m")
93
+ exit(2)
94
+ end
95
+ end
96
+
97
+ def test
98
+ return if test_files.empty?
99
+
100
+ if ENV['COV'] || ENV['CI']
101
+ require 'simplecov'
102
+ if ENV['CI']
103
+ require 'coveralls'
104
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
105
+ end
106
+ SimpleCov.start do
107
+ add_filter('test/')
108
+ add_filter('test.rb')
109
+ end
110
+ end
111
+
112
+ test_files.each{ |file| require "#{dir}/#{file[0..-4]}" }
113
+ end
114
+
115
+ def clean
116
+ return if ignored_files.empty?
117
+
118
+ require 'fileutils'
119
+ trash = File.expand_path("~/.Trash/#{spec.name}")
120
+ puts "Move the following files into: \e[35m#{strip_path(trash)}\e[33m"
121
+
122
+ ignored_files.each do |file|
123
+ from = "#{dir}/#{file}"
124
+ to = "#{trash}/#{File.dirname(file)}"
125
+ puts strip_path(from)
126
+
127
+ FileUtils.mkdir_p(to)
128
+ FileUtils.mv(from, to)
129
+ end
130
+
131
+ print "\e[0m"
132
+ end
133
+
41
134
  def write
42
135
  File.open(spec_path, 'w'){ |f| f << split_lines(spec.to_ruby) }
43
136
  end
@@ -173,82 +266,38 @@ namespace :gem do
173
266
 
174
267
  desc 'Install gem'
175
268
  task :install => [:build] do
176
- Gemgem.sh_gem('install', Gemgem.gem_path)
269
+ Gemgem.gem_install
177
270
  end
178
271
 
179
272
  desc 'Build gem'
180
273
  task :build => [:spec] do
181
- require 'fileutils'
182
- require 'rubygems/package'
183
- gem = nil
184
- Dir.chdir(Gemgem.dir) do
185
- gem = Gem::Package.build(Gem::Specification.load(Gemgem.spec_path))
186
- FileUtils.mkdir_p(Gemgem.pkg_dir)
187
- FileUtils.mv(gem, Gemgem.pkg_dir) # gem is relative path, but might be ok
188
- end
189
- puts "\e[35mGem built: \e[33m" \
190
- "#{Gemgem.strip_path("#{Gemgem.pkg_dir}/#{gem}")}\e[0m"
274
+ Gemgem.gem_build
191
275
  end
192
276
 
193
277
  desc 'Generate gemspec'
194
278
  task :spec do
195
- Gemgem.create
196
- Gemgem.write
279
+ Gemgem.gem_spec
197
280
  end
198
281
 
199
282
  desc 'Release gem'
200
283
  task :release => [:spec, :check, :build] do
201
- Gemgem.module_eval do
202
- sh_git('tag', Gemgem.gem_tag)
203
- sh_git('push')
204
- sh_git('push', '--tags')
205
- sh_gem('push', Gemgem.gem_path)
206
- end
284
+ Gemgem.gem_release
207
285
  end
208
286
 
209
287
  task :check do
210
- ver = Gemgem.spec.version.to_s
211
-
212
- if ENV['VERSION'].nil?
213
- puts("\e[35mExpected " \
214
- "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m")
215
- exit(1)
216
-
217
- elsif ENV['VERSION'] != ver
218
- puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \
219
- "\e[35mbut got\n " \
220
- "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m")
221
- exit(2)
222
- end
288
+ Gemgem.gem_check
223
289
  end
224
290
 
225
291
  end # of gem namespace
226
292
 
227
293
  desc 'Run tests'
228
294
  task :test do
229
- next if Gemgem.test_files.empty?
230
- Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
295
+ Gemgem.test
231
296
  end
232
297
 
233
298
  desc 'Trash ignored files'
234
299
  task :clean => ['gem:spec'] do
235
- next if Gemgem.ignored_files.empty?
236
-
237
- require 'fileutils'
238
- trash = File.expand_path("~/.Trash/#{Gemgem.spec.name}")
239
- puts "Move the following files into:" \
240
- " \e[35m#{Gemgem.strip_path(trash)}\e[33m"
241
-
242
- Gemgem.ignored_files.each do |file|
243
- from = "#{Gemgem.dir}/#{file}"
244
- to = "#{trash}/#{File.dirname(file)}"
245
- puts Gemgem.strip_path(from)
246
-
247
- FileUtils.mkdir_p(to)
248
- FileUtils.mv(from, to)
249
- end
250
-
251
- print "\e[0m"
300
+ Gemgem.clean
252
301
  end
253
302
 
254
303
  task :default do