pork 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b60799f58ae3ada9e2e21aac7abd1976b58fc4a5
4
- data.tar.gz: d164b9472b6172aa706d5b0277e76973f72d442c
3
+ metadata.gz: 2a1e1b97195a2ba57307d273567e63ba63984492
4
+ data.tar.gz: 1fa9b61acb0d8fa4face3e6175d0cff655d4f4a6
5
5
  SHA512:
6
- metadata.gz: 976ed47455385545294100c395a7eae919bf05817abe3d28ece08c123160fe6cc76a41e2d6ee41b40cd53520ef163b0c9f91355a474ad75cfd98dd4b00afc2fe
7
- data.tar.gz: e5829c79dad56585a2fd33e74dab2692d6ece7261de31d53e6428da3fdad1cac47287f361205221f3840ab7edd79129e17738e57793d10e5981bf89fc4deaf50
6
+ metadata.gz: 733207de6f02953d3bd378047b756e35495d59ccd8620dd39d169c5d90b25de788a2592613eb3bc3d8cd9d713195247f7423cd16cf8217f8f249e5c718e97a15
7
+ data.tar.gz: 63dfbffaf0a50ed00e0c79ba022a180816de4279667dd318c268fe1b5ab3fe412a9e4ae1f769f0c42132181197cdad761a6e5e2254dcb3103dd6101c2fd561b7
data/.travis.yml CHANGED
@@ -5,7 +5,6 @@ rvm:
5
5
  - 2.1
6
6
  - 2.2
7
7
  - rbx-2
8
- - jruby
9
8
  - jruby-head
10
9
 
11
10
  install: 'bundle install --retry=3'
data/CHANGES.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGES
2
2
 
3
+ ## Pork 1.4.0 -- 2015-07-18
4
+
5
+ ### Enhancement
6
+
7
+ * Introduced `Pork.report_mode` and `ENV['PORK_REPORT']`.
8
+ * The replicating command for `ENV['PORK_SEED']` is more accurate now.
9
+ * Now you can still run test sequentially when ENV['PORK_TEST'] is set.
10
+
3
11
  ## Pork 1.3.1 -- 2015-06-06
4
12
 
5
13
  ### Enhancement
data/README.md CHANGED
@@ -835,6 +835,21 @@ Summary by examples:
835
835
  * `env PORK_TEST='group0' rake test`
836
836
  * `env PORK_TEST='group0,group1' rake test`
837
837
 
838
+ ### `env PORK_SEED=`
839
+
840
+ By default, before running a test case, Pork would try to generate a
841
+ random seed for each test case. This way, we could easily replicate
842
+ each test case by setting the same random seed.
843
+
844
+ However, this could hurt performance and randomness. This is a trade off
845
+ before Ruby can restore arbitrary random state. If you don't want this
846
+ behaviour, you could set `PORK_SEED=random` to force Pork only set the
847
+ seed before running the entire test suite, saving you some performance
848
+ and randomness.
849
+
850
+ Otherwise, you don't have to care about this option. Just copy and
851
+ paste the replicating command when one of your test cases failed.
852
+
838
853
  ### Pork.execute_mode
839
854
 
840
855
  By default, `Pork.execute_mode` is set to `:shuffled` which would execute
@@ -869,6 +884,42 @@ Or:
869
884
  env PORK_MODE=parallel ruby -Ilib test/test_pork.rb
870
885
  ```
871
886
 
887
+ ### Pork.report_mode
888
+
889
+ By default, `Pork.report_mode` is set to `:dot` which would print a dot
890
+ for each test case. This is the same as test/unit bundled in Ruby. We
891
+ provide another option: `:description` which would print the description
892
+ for each test case. This might be useful if you are not running a bunch
893
+ of test cases. All the options are:
894
+
895
+ * `:dot` (default)
896
+ * `:description`
897
+
898
+ Pass the symbol to it to use the mode:
899
+
900
+ ``` ruby
901
+ Pork.report_mode :description
902
+ ```
903
+
904
+ On the other hand, you could also set `ENV['PORK_REPORT']` for picking an
905
+ reporting mode. This would be convenient if you just want to switch to a
906
+ particular mode temporary via command line. For example:
907
+
908
+ ``` shell
909
+ env PORK_REPORT=description rake test
910
+ ```
911
+
912
+ Or:
913
+
914
+ ``` shell
915
+ env PORK_REPORT=description ruby -Ilib test/test_pork.rb
916
+ ```
917
+
918
+ Caveat: You might see interleaving description output if you're running with
919
+ `Pork.execute_mode :shuffled` because... it's shuffled. You might want to run
920
+ in `Pork.execute_mode :sequential` along with `Pork.report_mode :description`
921
+ if you don't want to see interleaving descriptions.
922
+
872
923
  ### Pork.inspect_failure_mode
873
924
 
874
925
  By default, `Pork.inspect_failure_mode` is set to `:auto`, which would
data/lib/pork.rb CHANGED
@@ -13,18 +13,32 @@ module Pork
13
13
  end
14
14
 
15
15
  # default to :shuffled while eliminating warnings for uninitialized ivar
16
- def self.execute_mode execute=nil
17
- @execute = execute || @execute ||= :shuffled
16
+ def self.execute_mode mode=nil
17
+ @execute_mode = mode || @execute_mode ||= :shuffled
18
+ end
19
+
20
+ def self.report_mode mode=nil
21
+ @report_mode = mode || @report_mode ||= :dot
22
+ require "pork/report/#{@report_mode}"
23
+ @report_mode
24
+ end
25
+
26
+ def self.report_class
27
+ const_get(report_mode.to_s.capitalize)
28
+ end
29
+
30
+ def self.report_extensions
31
+ @report_extensions ||= []
18
32
  end
19
33
 
20
34
  def self.Rainbows!
21
35
  require 'pork/extra/rainbows'
22
- Pork::Stat.__send__(:include, Pork::Rainbows)
36
+ report_extensions << Rainbows
23
37
  end
24
38
 
25
39
  def self.show_source
26
40
  require 'pork/extra/show_source'
27
- Pork::Stat.__send__(:include, Pork::ShowSource)
41
+ report_extensions << ShowSource
28
42
  end
29
43
 
30
44
  def self.stat
@@ -39,6 +53,25 @@ module Pork
39
53
  end
40
54
  end
41
55
 
56
+ def self.reseed
57
+ if ENV['PORK_SEED']
58
+ seed
59
+ else
60
+ new_seed = Random.new_seed
61
+ Random.srand(new_seed)
62
+ new_seed
63
+ end
64
+ end
65
+
66
+ def self.srand
67
+ case ENV['PORK_SEED']
68
+ when nil, 'random'
69
+ Random.srand(seed)
70
+ else
71
+ Random.srand(Integer(ENV['PORK_SEED']))
72
+ end
73
+ end
74
+
42
75
  def self.trap sig='SIGINT'
43
76
  Signal.trap(sig) do
44
77
  stat.report
@@ -48,15 +81,10 @@ module Pork
48
81
  end
49
82
 
50
83
  def self.execute
51
- Random.srand(ENV['PORK_SEED'].to_i) if ENV['PORK_SEED']
52
- seed
53
84
  if ENV['PORK_TEST']
54
- require 'pork/mode/shuffled'
85
+ require 'pork/isolate'
55
86
  if tests = Executor[ENV['PORK_TEST']]
56
- paths, imps =
57
- tests.group_by{ |p| p.kind_of?(Array) }.values_at(true, false)
58
- @stat = Executor.execute(execute_mode, stat, paths) if paths
59
- @stat = imps.inject(stat){ |s, i| i.execute(execute_mode, s) } if imps
87
+ @stat = Executor.execute(execute_mode, stat, tests)
60
88
  else
61
89
  puts "Cannot find test: #{ENV['PORK_TEST']}"
62
90
  exit 254
@@ -67,7 +95,9 @@ module Pork
67
95
  end
68
96
 
69
97
  def self.run
98
+ srand
70
99
  execute_mode(ENV['PORK_MODE'])
100
+ report_mode(ENV['PORK_REPORT'])
71
101
  trap
72
102
  execute
73
103
  stat.report
data/lib/pork/executor.rb CHANGED
@@ -4,6 +4,10 @@ require 'pork/context'
4
4
 
5
5
  module Pork
6
6
  class Executor < Struct.new(:pork_stat, :pork_description)
7
+ # we don't want this method from Struct.new, it's confusing when
8
+ # pork/isolate was not loaded. (i.e. isolate would override it anyway)
9
+ singleton_class.superclass.send(:remove_method, :[])
10
+
7
11
  extend Imp
8
12
  include Context
9
13
  init
@@ -1,15 +1,19 @@
1
1
 
2
2
  module Pork
3
3
  module Rainbows
4
- def case_pass msg='.'
5
- @rainbows ||= 0
6
- io.print( color256(rainbows(@rainbows)){msg} )
7
- @rainbows += 1
4
+ def msg_pass
5
+ @rainbows ||= -1
6
+ @rainbows += +1
7
+ color256(rainbows(@rainbows), strip_color(super))
8
8
  end
9
9
 
10
10
  private
11
- def color256 rgb
12
- "\e[38;5;#{rgb}m#{yield}\e[0m"
11
+ def strip_color text
12
+ text.gsub(/\e\[\d+m/, '')
13
+ end
14
+
15
+ def color256 rgb, text
16
+ "\e[38;5;#{rgb}m#{text}\e[0m"
13
17
  end
14
18
 
15
19
  def rainbows i
data/lib/pork/imp.rb CHANGED
@@ -30,11 +30,7 @@ module Pork
30
30
 
31
31
  def execute mode, *args
32
32
  require "pork/mode/#{mode}"
33
- if args.size == 1 || mode.to_s != 'sequential'
34
- send(mode, *args)
35
- else # cannot run sequential tests for specific paths
36
- shuffled(*args)
37
- end
33
+ public_send(mode, *args)
38
34
  end
39
35
 
40
36
  private
@@ -46,35 +42,38 @@ module Pork
46
42
  def run stat, desc, test, env
47
43
  assertions = stat.assertions
48
44
  context = new(stat, desc)
49
- run_protected(stat, desc, test) do
45
+ seed = Pork.reseed
46
+ stat.reporter.case_start(context)
47
+ run_protected(stat, desc, test, seed) do
50
48
  env.run_before(context)
51
49
  context.instance_eval(&test)
52
50
  if assertions == stat.assertions
53
51
  raise Error.new('Missing assertions')
54
52
  end
55
- stat.case_pass
53
+ stat.reporter.case_pass
56
54
  end
57
55
  ensure
58
56
  stat.incr_tests
59
- run_protected(stat, desc, test){ env.run_after(context) }
57
+ run_protected(stat, desc, test, seed){ env.run_after(context) }
58
+ stat.reporter.case_end
60
59
  end
61
60
 
62
- def run_protected stat, desc, test
61
+ def run_protected stat, desc, test, seed
63
62
  yield
64
63
  rescue Error, StandardError => e
65
64
  case e
66
65
  when Skip
67
66
  stat.incr_skips
68
- stat.case_skip
67
+ stat.reporter.case_skip
69
68
  else
70
- err = [e, description_for("would #{desc}"), test]
69
+ err = [e, description_for("would #{desc}"), test, seed]
71
70
  case e
72
71
  when Failure
73
72
  stat.add_failure(err)
74
- stat.case_failed
73
+ stat.reporter.case_failed
75
74
  when Error, StandardError
76
75
  stat.add_error(err)
77
- stat.case_errored
76
+ stat.reporter.case_errored
78
77
  end
79
78
  end
80
79
  end
@@ -0,0 +1,102 @@
1
+
2
+ require 'pork'
3
+
4
+ module Pork
5
+ module Isolate
6
+ def all_tests
7
+ @all_tests ||= build_all_tests
8
+ end
9
+
10
+ def all_paths
11
+ (all_tests[:files] || {}).values.flat_map(&:values).flatten(1).uniq
12
+ end
13
+
14
+ def [] index
15
+ by_groups(index) || by_source(index)
16
+ end
17
+
18
+ def by_groups groups
19
+ return unless tests = all_tests[:groups]
20
+ paths = groups.split(',').flat_map do |g|
21
+ tests[g.strip] || []
22
+ end.uniq
23
+ paths unless paths.empty?
24
+ end
25
+
26
+ def by_source source
27
+ return unless tests = all_tests[:files]
28
+ file_str, line_str = source.split(':')
29
+ file, line = File.expand_path(file_str), line_str.to_i
30
+ return unless cases = tests[file]
31
+ if line.zero?
32
+ cases.values.flatten(1).uniq
33
+ else
34
+ _, paths = cases.reverse_each.find{ |(l, _)| l <= line }
35
+ paths
36
+ end
37
+ end
38
+
39
+ protected
40
+ def isolate stat, path, super_env=nil
41
+ env = Env.new(super_env)
42
+ idx = path.first
43
+
44
+ @tests.first(idx).each do |(type, arg, _)|
45
+ case type
46
+ when :before
47
+ env.before << arg
48
+ when :after
49
+ env.after << arg
50
+ end
51
+ end
52
+
53
+ if path.size == 1
54
+ _, desc, test = @tests[idx]
55
+ run(stat, desc, test, env)
56
+ else
57
+ @tests[idx][1].isolate(stat, path.drop(1), env)
58
+ end
59
+
60
+ stat
61
+ end
62
+
63
+ def build_all_tests result={}, path=[]
64
+ @tests.each_with_index.inject(result) do |r,
65
+ ((type, imp, test, opts),
66
+ index)|
67
+ current = path + [index]
68
+
69
+ case type
70
+ when :describe
71
+ imp.build_all_tests(r, current) do |nested|
72
+ store_path(r, nested, test, opts[:groups])
73
+ end
74
+ when :would
75
+ yield(current) if block_given?
76
+ store_path(r, current, test, opts[:groups])
77
+ end
78
+
79
+ r
80
+ end
81
+ end
82
+
83
+ def store_path tests, path, test, groups
84
+ store_for_groups(tests, path, groups) if groups
85
+ store_for_source(tests, path, *test.source_location)
86
+ end
87
+
88
+ def store_for_groups tests, path, groups
89
+ r = tests[:groups] ||= {}
90
+ groups.each do |g|
91
+ (r[g.to_s] ||= []) << path
92
+ end
93
+ end
94
+
95
+ def store_for_source tests, path, file, line
96
+ r = tests[:files] ||= {}
97
+ ((r[File.expand_path(file)] ||= {})[line] ||= []) << path
98
+ end
99
+ end
100
+
101
+ Executor.extend(Isolate)
102
+ end
@@ -10,7 +10,7 @@ module Pork
10
10
  def parallel stat=Stat.new, paths=all_paths
11
11
  paths.shuffle.each_slice(cores).map do |paths_slice|
12
12
  Thread.new do
13
- execute(:shuffled, Stat.new(stat.io), paths_slice)
13
+ execute(:shuffled, Stat.new(stat.reporter), paths_slice)
14
14
  end
15
15
  end.map(&:value).inject(stat, &:merge)
16
16
  end
@@ -3,7 +3,17 @@ require 'pork'
3
3
 
4
4
  module Pork
5
5
  module Sequential
6
- def sequential stat=Stat.new, super_env=nil
6
+ def sequential stat=Stat.new, paths=nil
7
+ if paths
8
+ require 'pork/isolate'
9
+ paths.inject(stat, &method(:isolate))
10
+ else # maybe we could remove this mode if it's not faster and lighter
11
+ sequential_with_env(stat)
12
+ end
13
+ end
14
+
15
+ protected
16
+ def sequential_with_env stat, super_env=nil
7
17
  env = Env.new(super_env)
8
18
  @tests.each do |(type, arg, test)|
9
19
  case type
@@ -12,7 +22,7 @@ module Pork
12
22
  when :after
13
23
  env.after << arg
14
24
  when :describe
15
- arg.sequential(stat, env)
25
+ arg.sequential_with_env(stat, env)
16
26
  when :would
17
27
  run(stat, arg, test, env)
18
28
  end
@@ -1,102 +1,11 @@
1
1
 
2
- require 'pork'
2
+ require 'pork/isolate'
3
3
 
4
4
  module Pork
5
5
  module Shuffled
6
- def all_tests
7
- @all_tests ||= build_all_tests
8
- end
9
-
10
- def all_paths
11
- (all_tests[:files] || {}).values.flat_map(&:values).flatten(1).
12
- select{ |path| path.kind_of?(Array) }
13
- end
14
-
15
- def [] index
16
- by_groups(index) || by_source(index)
17
- end
18
-
19
- def by_groups groups
20
- return unless tests = all_tests[:groups]
21
- paths = groups.split(',').flat_map do |g|
22
- tests[g.strip] || []
23
- end.uniq
24
- paths unless paths.empty?
25
- end
26
-
27
- def by_source source
28
- return unless tests = all_tests[:files]
29
- file_str, line_str = source.split(':')
30
- file, line = File.expand_path(file_str), line_str.to_i
31
- return unless cases = tests[file]
32
- if line.zero?
33
- cases.values.flatten(1)
34
- else
35
- _, paths = cases.reverse_each.find{ |(l, _)| l <= line }
36
- paths
37
- end
38
- end
39
-
40
6
  def shuffled stat=Stat.new, paths=all_paths
41
7
  paths.shuffle.inject(stat, &method(:isolate))
42
8
  end
43
-
44
- protected
45
- def isolate stat, path, super_env=nil
46
- env = Env.new(super_env)
47
- idx = path.first
48
-
49
- @tests.first(idx).each do |(type, arg, _)|
50
- case type
51
- when :before
52
- env.before << arg
53
- when :after
54
- env.after << arg
55
- end
56
- end
57
-
58
- if path.size == 1
59
- _, desc, test = @tests[idx]
60
- run(stat, desc, test, env)
61
- else
62
- @tests[idx][1].isolate(stat, path.drop(1), env)
63
- end
64
-
65
- stat
66
- end
67
-
68
- def build_all_tests result={}, path=[]
69
- @tests.each_with_index.inject(result) do |r,
70
- ((type, imp, test, opts),
71
- index)|
72
- current = path + [index]
73
- path_or_imp = case type
74
- when :describe
75
- imp
76
- when :would
77
- current
78
- else
79
- next r
80
- end
81
- groups = opts[:groups]
82
- store_for_groups(r, path_or_imp, groups) if groups
83
- store_for_source(r, path_or_imp, *test.source_location)
84
- imp.build_all_tests(r, current) if type == :describe
85
- r
86
- end
87
- end
88
-
89
- def store_for_groups tests, path_or_imp, groups
90
- r = tests[:groups] ||= {}
91
- groups.each do |g|
92
- (r[g.to_s] ||= []) << path_or_imp
93
- end
94
- end
95
-
96
- def store_for_source tests, path_or_imp, file, line
97
- r = tests[:files] ||= {}
98
- ((r[File.expand_path(file)] ||= {})[line] ||= []) << path_or_imp
99
- end
100
9
  end
101
10
 
102
11
  Executor.extend(Shuffled)
@@ -1,5 +1,5 @@
1
1
 
2
- require 'pork/stat'
2
+ require 'pork'
3
3
 
4
4
  module Pork
5
5
  module BottomupBacktrace
@@ -9,5 +9,5 @@ module Pork
9
9
  end
10
10
  end
11
11
 
12
- Pork::Stat.__send__(:include, Pork::BottomupBacktrace)
12
+ report_extensions << BottomupBacktrace
13
13
  end
@@ -1,14 +1,14 @@
1
1
 
2
- require 'pork/stat'
2
+ require 'pork'
3
3
 
4
4
  module Pork
5
5
  module Color
6
- def case_skip msg='s'; super(yellow( msg)); end
7
- def case_failed msg='F'; super(magenta(msg)); end
8
- def case_errored msg='E'; super(red( msg)); end
6
+ def msg_skip ; yellow(super); end
7
+ def msg_failed ; magenta(super); end
8
+ def msg_errored; red(super); end
9
9
 
10
10
  private
11
- def command name
11
+ def command name, seed
12
12
  gray(super)
13
13
  end
14
14
 
@@ -28,8 +28,8 @@ module Pork
28
28
  cyan(super)
29
29
  end
30
30
 
31
- def numbers
32
- super.zip(%w[green green magenta red yellow]).map do |(num, col)|
31
+ def numbers stat
32
+ stat.numbers.zip(%w[green green magenta red yellow]).map do |(num, col)|
33
33
  if num == 0
34
34
  num
35
35
  else
@@ -38,8 +38,8 @@ module Pork
38
38
  end
39
39
  end
40
40
 
41
- def velocity
42
- super.zip(%w[cyan blue blue]).map do |(str, col)|
41
+ def velocity stat
42
+ stat.velocity.zip(%w[cyan blue blue]).map do |(str, col)|
43
43
  send(col, str)
44
44
  end
45
45
  end
@@ -69,5 +69,5 @@ module Pork
69
69
  end
70
70
  end
71
71
 
72
- Pork::Stat.__send__(:include, Pork::Color)
72
+ report_extensions << Color
73
73
  end
@@ -0,0 +1,128 @@
1
+
2
+ module Pork
3
+ Report = Struct.new(:io)
4
+
5
+ module Report::Imp
6
+ def initialize o=$stdout
7
+ super
8
+ extend(*Pork.report_extensions.reverse)
9
+ end
10
+
11
+ def case_start _; end
12
+ def case_end ; end
13
+ def case_pass ; io.print msg_pass ; end
14
+ def case_skip ; io.print msg_skip ; end
15
+ def case_failed ; io.print msg_failed ; end
16
+ def case_errored; io.print msg_errored; end
17
+
18
+ def report stat
19
+ io.puts
20
+ io.puts messages(stat)
21
+ io.printf("\nFinished in %s seconds, %s tests/s, %s assertions/s \n",
22
+ *velocity(stat))
23
+ io.printf("%s tests, %s assertions, %s failures, %s errors, %s skips\n",
24
+ *numbers(stat))
25
+ end
26
+
27
+ private
28
+ def velocity stat
29
+ stat.velocity
30
+ end
31
+
32
+ def numbers stat
33
+ stat.numbers
34
+ end
35
+
36
+ def messages stat
37
+ stat.exceptions.reverse_each.map do |(err, msg, test, seed)|
38
+ "\n #{show_command(test.source_location, seed)}" \
39
+ "\n #{show_backtrace(test, err)}" \
40
+ "#{show_source(test, err)}" \
41
+ "\n#{show_message(msg)}" \
42
+ "\n#{show_exception(err)}"
43
+ end
44
+ end
45
+
46
+ def show_command source_location, seed
47
+ "Replicate this test with:\n#{command(source_location, seed)}"
48
+ end
49
+
50
+ def command source_location, seed
51
+ "env#{pork_test(source_location)} #{pork_mode} #{pork_seed(seed)}" \
52
+ " #{Gem.ruby} -S #{$0} #{ARGV.join(' ')}"
53
+ end
54
+
55
+ def show_backtrace test, err
56
+ backtrace(test, err).join("\n ")
57
+ end
58
+
59
+ def backtrace test, err
60
+ if $VERBOSE
61
+ err.backtrace
62
+ else
63
+ strip(reject_pork(test, err))
64
+ end
65
+ end
66
+
67
+ def show_source _, _
68
+ ''
69
+ end
70
+
71
+ def highlight_line line
72
+ line
73
+ end
74
+
75
+ def backlight_line line
76
+ line
77
+ end
78
+
79
+ def show_message msg
80
+ msg
81
+ end
82
+
83
+ def show_exception err
84
+ "#{err.class}: #{err.message}"
85
+ end
86
+
87
+ def reject_pork test, err
88
+ bt = err.backtrace.reject{ |l| l =~ %r{/lib/pork(/\w+)*\.rb:\d+} }
89
+ if bt.empty?
90
+ ["#{test.source_location.join(':')}:in `block in would'"]
91
+ else
92
+ bt
93
+ end
94
+ end
95
+
96
+ def strip bt
97
+ strip_home(strip_cwd(bt))
98
+ end
99
+
100
+ def strip_home bt
101
+ bt.map{ |path| path.sub(ENV['HOME'], '~') }
102
+ end
103
+
104
+ def strip_cwd bt
105
+ bt.map{ |path| path.sub("#{Dir.pwd}/", '') }
106
+ end
107
+
108
+ def pork_test source_location
109
+ if !!ENV['PORK_SEED'] == !!ENV['PORK_TEST'] || ENV['PORK_TEST']
110
+ file, line = source_location
111
+ " PORK_TEST='#{strip([file]).join}:#{line}'"
112
+ else
113
+ # cannot replicate a test case with PORK_SEED set and PORK_TEST unset
114
+ # unless we could restore random's state (srand didn't work for that)
115
+ end
116
+ end
117
+
118
+ def pork_mode
119
+ "PORK_MODE=#{Pork.execute_mode}"
120
+ end
121
+
122
+ def pork_seed seed
123
+ "PORK_SEED=#{seed}"
124
+ end
125
+ end
126
+
127
+ Report.__send__(:include, Report::Imp)
128
+ end
@@ -0,0 +1,39 @@
1
+
2
+ require 'pork/report/dot'
3
+
4
+ module Pork
5
+ class Description < Dot
6
+ attr_accessor :last_executor
7
+
8
+ def msg_pass
9
+ msg = "\e[1Go"
10
+ if respond_to?(:green, true)
11
+ green(msg)
12
+ else
13
+ msg
14
+ end
15
+ end
16
+
17
+ def msg_skip ; "\e[1G#{super}"; end
18
+ def msg_failed ; "\e[1G#{super}"; end
19
+ def msg_errored; "\e[1G#{super}"; end
20
+
21
+ def case_start context
22
+ self.last_executor ||= Executor
23
+ executor = context.class
24
+ levels = executor.ancestors.drop(1).count{ |a| a <= Executor }
25
+
26
+ if executor != Executor && last_executor != executor
27
+ io.puts "#{' ' * (levels - 1)}#{executor.desc}"
28
+ end
29
+
30
+ io.print "#{' ' * levels}#{context.pork_description}: "
31
+
32
+ self.last_executor = executor
33
+ end
34
+
35
+ def case_end
36
+ io.puts
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+
2
+ require 'pork/report'
3
+
4
+ module Pork
5
+ class Dot < Report
6
+ def msg_pass ; '.'; end
7
+ def msg_skip ; 's'; end
8
+ def msg_failed ; 'F'; end
9
+ def msg_errored; 'E'; end
10
+ end
11
+ end
data/lib/pork/stat.rb CHANGED
@@ -1,146 +1,57 @@
1
1
 
2
2
  require 'thread'
3
+ require 'pork/report'
3
4
 
4
5
  module Pork
5
- Stat = Struct.new(:io, :start, :mutex,
6
+ Stat = Struct.new(:reporter, :start, :mutex,
6
7
  :tests, :assertions, :skips, :failures, :errors,
7
8
  :exceptions)
8
9
 
9
- Stat::Imp = Module.new do
10
+ module Stat::Imp
10
11
  attr_accessor :stop
11
- def initialize io=$stdout, st=Time.now, mu=Mutex.new,
12
+ def initialize rt=Pork.report_class.new,
13
+ st=Time.now, mu=Mutex.new,
12
14
  t=0, a=0, s=0, f=0, e=0, x=[]
13
15
  super
14
16
  end
17
+
15
18
  def incr_assertions; mutex.synchronize{ self.assertions += 1 }; end
16
19
  def incr_tests ; mutex.synchronize{ self.tests += 1 }; end
17
20
  def incr_skips ; mutex.synchronize{ self.skips += 1 }; end
21
+
18
22
  def add_failure err
19
23
  mutex.synchronize do
20
24
  self.failures += 1
21
25
  exceptions << err
22
26
  end
23
27
  end
28
+
24
29
  def add_error err
25
30
  mutex.synchronize do
26
31
  self.errors += 1
27
32
  exceptions << err
28
33
  end
29
34
  end
30
- def case_pass msg='.'; io.print msg; end
31
- def case_skip msg='s'; io.print msg; end
32
- def case_failed msg='F'; io.print msg; end
33
- def case_errored msg='E'; io.print msg; end
35
+
34
36
  def passed?; exceptions.size == 0 ; end
35
37
  def numbers; [tests, assertions, failures, errors, skips]; end
38
+
36
39
  def velocity
37
40
  time_spent = stop - start
38
41
  [time_spent.round(6),
39
42
  (tests / time_spent).round(4),
40
43
  (assertions / time_spent).round(4)]
41
44
  end
45
+
42
46
  def report
43
47
  self.stop = Time.now
44
- io.puts
45
- io.puts report_exceptions
46
- io.printf("\nFinished in %s seconds, %s tests/s, %s assertions/s \n",
47
- *velocity)
48
- io.printf("%s tests, %s assertions, %s failures, %s errors, %s skips\n",
49
- *numbers)
48
+ reporter.report(self)
50
49
  end
50
+
51
51
  def merge stat
52
- self.class.new(io, start, mutex,
52
+ self.class.new(reporter, start, mutex,
53
53
  *to_a.drop(3).zip(stat.to_a.drop(3)).map{ |(a, b)| a + b })
54
54
  end
55
-
56
- private
57
- def report_exceptions
58
- exceptions.reverse_each.map do |(err, msg, test)|
59
- "\n #{show_command(test.source_location)}" \
60
- "\n #{show_backtrace(test, err)}" \
61
- "#{show_source(test, err)}" \
62
- "\n#{show_message(msg)}" \
63
- "\n#{show_exception(err)}"
64
- end
65
- end
66
-
67
- def show_command source_location
68
- "Replicate this test with:\n#{command(source_location)}"
69
- end
70
-
71
- def command source_location
72
- "#{env(source_location)} #{Gem.ruby} -S #{$0} #{ARGV.join(' ')}"
73
- end
74
-
75
- def show_backtrace test, err
76
- backtrace(test, err).join("\n ")
77
- end
78
-
79
- def backtrace test, err
80
- if $VERBOSE
81
- err.backtrace
82
- else
83
- strip(reject_pork(test, err))
84
- end
85
- end
86
-
87
- def show_source _, _
88
- ''
89
- end
90
-
91
- def highlight_line line
92
- line
93
- end
94
-
95
- def backlight_line line
96
- line
97
- end
98
-
99
- def show_message msg
100
- msg
101
- end
102
-
103
- def show_exception err
104
- "#{err.class}: #{err.message}"
105
- end
106
-
107
- def reject_pork test, err
108
- bt = err.backtrace.reject{ |l| l =~ %r{/lib/pork(/\w+)*\.rb:\d+} }
109
- if bt.empty?
110
- ["#{test.source_location.join(':')}:in `block in would'"]
111
- else
112
- bt
113
- end
114
- end
115
-
116
- def strip bt
117
- strip_home(strip_cwd(bt))
118
- end
119
-
120
- def strip_home bt
121
- bt.map{ |path| path.sub(ENV['HOME'], '~') }
122
- end
123
-
124
- def strip_cwd bt
125
- bt.map{ |path| path.sub("#{Dir.pwd}/", '') }
126
- end
127
-
128
- def env source_location
129
- "env #{pork(source_location)} #{pork_mode} #{pork_seed}"
130
- end
131
-
132
- def pork source_location
133
- file, line = source_location
134
- "PORK_TEST='#{strip([file]).join}:#{line}'"
135
- end
136
-
137
- def pork_mode
138
- "PORK_MODE=#{Pork.execute_mode}"
139
- end
140
-
141
- def pork_seed
142
- "PORK_SEED=#{Pork.seed}"
143
- end
144
55
  end
145
56
 
146
57
  Stat.__send__(:include, Stat::Imp)
data/lib/pork/test.rb CHANGED
@@ -1,19 +1,16 @@
1
1
 
2
2
  require 'pork/auto'
3
3
 
4
- Pork.autorun(false)
4
+ Pork.autorun
5
5
  Pork.show_source
6
+ Pork.Rainbows! if rand(10) == 0
6
7
 
7
- at_exit do
8
- Pork.module_eval do
9
- execute_mode(ENV['PORK_MODE'])
10
- trap
11
- execute
8
+ Pork.singleton_class.send(:prepend, Module.new{
9
+ def execute
10
+ super
12
11
  %w[sequential shuffled parallel].each do |mode|
13
12
  execute_mode(mode)
14
- execute
13
+ super
15
14
  end
16
- stat.report
17
- exit stat.failures + stat.errors + ($! && 1).to_i
18
15
  end
19
- end
16
+ })
data/lib/pork/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Pork
3
- VERSION = '1.3.1'
3
+ VERSION = '1.4.0'
4
4
  end
data/pork.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: pork 1.3.1 ruby lib
2
+ # stub: pork 1.4.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "pork"
6
- s.version = "1.3.1"
6
+ s.version = "1.4.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 = "2015-06-06"
11
+ s.date = "2015-07-18"
12
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 = [
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
32
32
  "lib/pork/extra/show_source.rb",
33
33
  "lib/pork/imp.rb",
34
34
  "lib/pork/inspect.rb",
35
+ "lib/pork/isolate.rb",
35
36
  "lib/pork/mode/parallel.rb",
36
37
  "lib/pork/mode/sequential.rb",
37
38
  "lib/pork/mode/shuffled.rb",
@@ -39,6 +40,9 @@ Gem::Specification.new do |s|
39
40
  "lib/pork/more/bottomup_backtrace.rb",
40
41
  "lib/pork/more/color.rb",
41
42
  "lib/pork/more/should.rb",
43
+ "lib/pork/report.rb",
44
+ "lib/pork/report/description.rb",
45
+ "lib/pork/report/dot.rb",
42
46
  "lib/pork/stat.rb",
43
47
  "lib/pork/test.rb",
44
48
  "lib/pork/version.rb",
@@ -54,7 +58,7 @@ Gem::Specification.new do |s|
54
58
  "test/test_stat.rb"]
55
59
  s.homepage = "https://github.com/godfat/pork"
56
60
  s.licenses = ["Apache License 2.0"]
57
- s.rubygems_version = "2.4.7"
61
+ s.rubygems_version = "2.4.8"
58
62
  s.summary = "Pork -- Simple and clean and modular testing library."
59
63
  s.test_files = [
60
64
  "test/test_bacon.rb",
@@ -2,8 +2,8 @@
2
2
  require 'pork/test'
3
3
 
4
4
  describe 'PORK_TEST=a' do
5
- def verify line, executor, index, offset=0
6
- path = executor[index][offset]
5
+ def verify line, executor, index
6
+ path = executor[index].first
7
7
  type, desc, block, opts = extract(path, executor)
8
8
  expect(type) .eq :would
9
9
  expect(desc) .eq 'find the corresponding test case'
@@ -23,17 +23,13 @@ describe 'PORK_TEST=a' do
23
23
  end
24
24
  end
25
25
 
26
- def woulds
27
- @woulds ||= Pork::Executor[__FILE__].select{ |p| p.kind_of?(Array) }
28
- end
29
-
30
26
  would 'find the corresponding test case', :groups => [:a, :b] do
31
27
  line = __LINE__ - 1
32
- [self.class, Pork::Executor].each.with_index do |executor, index|
28
+ [self.class, Pork::Executor].each do |executor|
33
29
  verify(line, executor, "#{__FILE__}:#{__LINE__}") # line
34
30
  verify(line, executor, 'a') # group
35
31
  verify(line, executor, "#{__FILE__}:#{__LINE__}") # diff line
36
- verify(line, executor, __FILE__, index) # file
32
+ verify(line, executor, __FILE__) # file
37
33
  # for self.class, would is the 1st, for Pork::Executor, would is 2nd
38
34
  end
39
35
  end
@@ -41,14 +37,14 @@ describe 'PORK_TEST=a' do
41
37
  describe 'PORK_TEST=b' do
42
38
  would 'find both', :groups => [:b] do
43
39
  line = __LINE__ - 1
44
- Pork::Executor[__FILE__].size.should.eq 6 # 3 describe
45
- woulds .size.should.eq 3
46
- Pork::Executor['a'] .size.should.eq 1
47
- Pork::Executor['b'] .size.should.eq 2
48
- Pork::Executor['b'] .should.eq woulds.first(2)
49
- Pork::Executor['a,b'] .should.eq woulds.first(2)
50
- self.class['a'] .should.nil?
51
- self.class['b'] .size.should.eq 1
40
+ woulds = Pork::Executor[__FILE__]
41
+ woulds .size.should.eq 4
42
+ Pork::Executor['a'] .size.should.eq 1
43
+ Pork::Executor['b'] .size.should.eq 2
44
+ Pork::Executor['b'] .should.eq woulds.first(2)
45
+ Pork::Executor['a,b'] .should.eq woulds.first(2)
46
+ self.class['a'] .should.nil?
47
+ self.class['b'] .size.should.eq 1
52
48
 
53
49
  a, b = Pork::Executor['b'].map{ |path| extract(path, Pork::Executor) }
54
50
  expect(a[0]) .eq :would
@@ -65,9 +61,15 @@ describe 'PORK_TEST=a' do
65
61
  would 'inherit groups from describe', :groups => [:d] do
66
62
  line = __LINE__ - 2
67
63
  c = Pork::Executor['c']
68
- expect(c.size ) .eq 1
69
- expect(c.first) .eq self.class
70
- expect(Pork::Executor["#{__FILE__}:#{line}"]).eq [self.class]
64
+ d = Pork::Executor['d']
65
+ expect(c.size) .eq 2
66
+ expect(d.size) .eq 1
67
+ expect(c.first) .eq d.first
68
+ expect(Pork::Executor["#{__FILE__}:#{line}"]).eq c
69
+ end
70
+
71
+ would 'dummy' do
72
+ ok
71
73
  end
72
74
  end
73
75
  end
data/test/test_stat.rb CHANGED
@@ -21,19 +21,21 @@ describe Pork::Stat do
21
21
  end
22
22
 
23
23
  def run check=:expect_one_error
24
- @stat = @executor.execute(Pork.execute_mode, Pork::Stat.new(StringIO.new))
24
+ @stat = @executor.execute(
25
+ Pork.execute_mode,
26
+ Pork::Stat.new(Pork.report_class.new(StringIO.new)))
25
27
  send(check)
26
28
  end
27
29
 
28
30
  def expect_one_error
29
- expect(@stat.io.string) .eq "\e[31mE\e[0m"
31
+ expect(@stat.reporter.io.string).eq "\e[31mE\e[0m"
30
32
  expect(@stat.tests) .eq 1
31
33
  expect(@stat.assertions).eq 0
32
34
  expect(@stat.errors) .eq 1
33
35
  end
34
36
 
35
37
  def expect_one_failure
36
- expect(@stat.io.string) .eq "\e[35mF\e[0m"
38
+ expect(@stat.reporter.io.string).eq "\e[35mF\e[0m"
37
39
  expect(@stat.tests) .eq 1
38
40
  expect(@stat.assertions).eq 0
39
41
  expect(@stat.failures) .eq 1
@@ -46,7 +48,7 @@ describe Pork::Stat do
46
48
  err, _, test = @stat.exceptions.first
47
49
  err.set_backtrace([])
48
50
 
49
- expect(@stat.send(:show_backtrace, test, err)).not.empty?
51
+ expect(@stat.reporter.send(:show_backtrace, test, err)).not.empty?
50
52
  end
51
53
 
52
54
  describe 'Pork::Stat#show_source' do
@@ -54,7 +56,7 @@ describe Pork::Stat do
54
56
  run(check)
55
57
  err, _, test = @stat.exceptions.first
56
58
  yield(err) if block_given?
57
- expect(@stat.send(:show_source, test, err)).include?(source)
59
+ expect(@stat.reporter.send(:show_source, test, err)).include?(source)
58
60
  end
59
61
 
60
62
  would 'one line' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pork
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lin Jen-Shin (godfat)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-06 00:00:00.000000000 Z
11
+ date: 2015-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: method_source
@@ -56,6 +56,7 @@ files:
56
56
  - lib/pork/extra/show_source.rb
57
57
  - lib/pork/imp.rb
58
58
  - lib/pork/inspect.rb
59
+ - lib/pork/isolate.rb
59
60
  - lib/pork/mode/parallel.rb
60
61
  - lib/pork/mode/sequential.rb
61
62
  - lib/pork/mode/shuffled.rb
@@ -63,6 +64,9 @@ files:
63
64
  - lib/pork/more/bottomup_backtrace.rb
64
65
  - lib/pork/more/color.rb
65
66
  - lib/pork/more/should.rb
67
+ - lib/pork/report.rb
68
+ - lib/pork/report/description.rb
69
+ - lib/pork/report/dot.rb
66
70
  - lib/pork/stat.rb
67
71
  - lib/pork/test.rb
68
72
  - lib/pork/version.rb
@@ -96,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
100
  version: '0'
97
101
  requirements: []
98
102
  rubyforge_project:
99
- rubygems_version: 2.4.7
103
+ rubygems_version: 2.4.8
100
104
  signing_key:
101
105
  specification_version: 4
102
106
  summary: Pork -- Simple and clean and modular testing library.