pork 1.3.1 → 1.4.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 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.