pork 0.9.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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