parallel_tests 1.1.1 → 1.2.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: d9936778844bea92756c550ca9ca6c224406ce07
4
- data.tar.gz: 2e410f1e0d4b8e76b5eb0558bfc5fa01117650ee
3
+ metadata.gz: c3784fb4e94413d435daab2e99def28d2293562b
4
+ data.tar.gz: 65b533d73a5bfa04bdba27161c0d6baea8a202f8
5
5
  SHA512:
6
- metadata.gz: eff8a7aa09428d7ac292d767b4ab79ff5a590c03e11d87e57764da3809bc933084d6ab34a23a848d519021e22fa220e33ff660f0f6eb681eb62c59c18153314a
7
- data.tar.gz: aaf9d2d50339745e6c5f6a80d70000fd2b6f26f4ed36196a8dbc1e97b8e781e373195669cf127377d7264054d4988797076720c13e18a75f67e5ef165c279240
6
+ metadata.gz: ab5162168063cfba147586acf5219c7065edfdf3bb110cda69c952165a3dad045464cac76c4e4c04839afa1dcaa9e23fbbbd0a5f650027788db4b6ed7aae150e
7
+ data.tar.gz: 0cc7aedcc2978f92ef01a9c81d2d0807665cc8db6b1134d40ecaa4d078b7ca8722a9b0a26c7ae4625b4f07d57bf153234eec55a6e4ed390f232ed04906f65266
data/Readme.md CHANGED
@@ -1,24 +1,19 @@
1
- Speedup Test::Unit + RSpec + Cucumber by running parallel on multiple CPUs (or cores).<br/>
2
- ParallelTests splits tests into even groups(by number of tests or runtime) and runs each group in a single process with its own database.
3
-
4
- [upgrading from 0.6 ?](https://github.com/grosser/parallel_tests/wiki/Upgrading-0.6.x-to-0.7.x)
1
+ Speedup Test::Unit + RSpec + Cucumber + Spinach by running parallel on multiple CPU cores.<br/>
2
+ ParallelTests splits tests into even groups (by number of lines or runtime) and runs each group in a single process with its own database.
5
3
 
6
4
  Setup for Rails
7
5
  ===============
8
6
  [RailsCasts episode #413 Fast Tests](http://railscasts.com/episodes/413-fast-tests)
9
- [still using Rails 2?](https://github.com/grosser/parallel_tests/blob/master/ReadmeRails2.md)
10
7
 
11
8
  ### Install
12
- If you use RSpec: ensure you have >= 2.4
13
-
14
- As gem
15
9
 
16
10
  ```ruby
17
- # add to Gemfile
11
+ # Gemfile
18
12
  gem "parallel_tests", :group => :development
19
13
  ```
20
14
 
21
15
  ### Add to `config/database.yml`
16
+
22
17
  ParallelTests uses 1 database per test-process.
23
18
  <table>
24
19
  <tr><td>Process number</td><td>1</td><td>2</td><td>3</td></tr>
@@ -93,6 +88,8 @@ Loggers
93
88
  Even test group run-times
94
89
  -------------------------
95
90
 
91
+ ### RSpec
92
+
96
93
  Add the `RuntimeLogger` to log how long each test takes to run.
97
94
  This log file will be loaded on the next test run, and the tests will be grouped
98
95
  so that each process should finish around the same time.
@@ -102,15 +99,19 @@ Rspec: Add to your `.rspec_parallel` (or `.rspec`) :
102
99
  --format progress
103
100
  --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log
104
101
 
105
- Test::Unit: Add to your `test_helper.rb`:
102
+ ### Test::Unit & Minitest 4
103
+
104
+ Add to your `test_helper.rb`:
106
105
  ```ruby
107
106
  require 'parallel_tests/test/runtime_logger'
108
107
  ```
109
108
 
109
+ results will be logged to tmp/parallel_runtime_test.log
110
+
110
111
  RSpec: SummaryLogger
111
112
  --------------------
112
113
 
113
- This logger logs the test output without the different processes overwriting each other.
114
+ Log the test output without the different processes overwriting each other.
114
115
 
115
116
  Add the following to your `.rspec_parallel` (or `.rspec`) :
116
117
 
@@ -120,7 +121,7 @@ Add the following to your `.rspec_parallel` (or `.rspec`) :
120
121
  RSpec: FailuresLogger
121
122
  -----------------------
122
123
 
123
- This logger produces pasteable command-line snippets for each failed example.
124
+ Produce pasteable command-line snippets for each failed example.
124
125
 
125
126
  E.g.
126
127
 
@@ -134,7 +135,7 @@ Add the following to your `.rspec_parallel` (or `.rspec`) :
134
135
  Cucumber: FailuresLogger
135
136
  -----------------------
136
137
 
137
- This logger logs failed cucumber scenarios to the specified file. The filename can be passed to cucumber, prefixed with '@' to rerun failures.
138
+ Log failed cucumber scenarios to the specified file. The filename can be passed to cucumber, prefixed with '@' to rerun failures.
138
139
 
139
140
  Usage:
140
141
 
@@ -148,10 +149,11 @@ Note if your `cucumber.yml` default profile uses `<%= std_opts %>` you may need
148
149
 
149
150
  To rerun failures:
150
151
 
151
- cucumber @tmp/cucumber_failures.log
152
+ cucumber @tmp/cucumber_failures.log
152
153
 
153
154
  Setup for non-rails
154
155
  ===================
156
+
155
157
  gem install parallel_tests
156
158
  # go to your project dir
157
159
  parallel_test test/
@@ -210,13 +212,10 @@ You can run any kind of code in parallel with -e / --execute
210
212
  TIPS
211
213
  ====
212
214
  - [RSpec] add a `.rspec_parallel` to use different options, e.g. **no --drb**
213
- - [RSpec] delete `script/spec`
214
- - [[Spork](https://github.com/sporkrb/spork)] does not work with parallel_tests except when using [sqlite in memory](https://github.com/grosser/parallel_tests/wiki#wiki-with-spork)
215
- - [RSpec] remove --loadby from you spec/*.opts
215
+ - Spring does not work with parallel_tests, use `DISABLE_SPRING=1 rake parallel:spec` if you have spring hardcoded in your binaries
216
+ - [RSpec] remove `--loadby` from `.rspec`
216
217
  - [RSpec] Instantly see failures (instead of just a red F) with [rspec-instafail](https://github.com/grosser/rspec-instafail)
217
- - [Bundler] if you have a `Gemfile` then `bundle exec` will be used to run tests
218
218
  - [Cucumber] add a `parallel: foo` profile to your `config/cucumber.yml` and it will be used to run parallel tests
219
- - [Cucumber] Pass in cucumber options by not giving the options an identifier ex: `rake parallel:features[,,'cucumber_opts']`
220
219
  - [Capybara setup](https://github.com/grosser/parallel_tests/wiki)
221
220
  - [Sphinx setup](https://github.com/grosser/parallel_tests/wiki)
222
221
  - [Capistrano setup](https://github.com/grosser/parallel_tests/wiki/Remotely-with-capistrano) let your tests run on a big box instead of your laptop
@@ -224,13 +223,13 @@ TIPS
224
223
  - `export PARALLEL_TEST_PROCESSORS=X` in your environment and parallel_tests will use this number of processors by default
225
224
  - [ZSH] use quotes to use rake arguments `rake "parallel:prepare[3]"`
226
225
  - [email_spec and/or action_mailer_cache_delivery](https://github.com/grosser/parallel_tests/wiki)
227
- - [Memcached] use different namespaces e.g. `config.cache_store = ..., :namespace => "test_#{ENV['TEST_ENV_NUMBER']}"`
226
+ - [Memcached] use different namespaces e.g. `config.cache_store = ..., namespace: "test_#{ENV['TEST_ENV_NUMBER']}"`
228
227
  - [zeus-parallel_tests](https://github.com/sevos/zeus-parallel_tests)
229
228
  - [Distributed parallel test (e.g. Travis Support)](https://github.com/grosser/parallel_tests/wiki/Distributed-Parallel-Tests-and-Travis-Support)
229
+ - Contribute your own gotaches to the [Wiki](https://github.com/grosser/parallel_tests/wiki) or even better open a PR :)
230
230
 
231
231
  TODO
232
232
  ====
233
- - make tests consistently pass with `--order random` in .rspec
234
233
  - fix tests vs cucumber >= 1.2 `unknown option --format`
235
234
  - add integration tests for the rake tasks, maybe generate a rails project ...
236
235
  - add unit tests for cucumber runtime formatter
@@ -240,7 +239,7 @@ Authors
240
239
  ====
241
240
  inspired by [pivotal labs](http://pivotallabs.com/users/miked/blog/articles/849-parallelize-your-rspec-suite)
242
241
 
243
- ### [Contributors](http://github.com/grosser/parallel_tests/contributors)
242
+ ### [Contributors](https://github.com/grosser/parallel_tests/contributors)
244
243
  - [Charles Finkel](http://charlesfinkel.com/)
245
244
  - [Indrek Juhkam](http://urgas.eu)
246
245
  - [Jason Morrison](http://jayunit.net)
@@ -33,7 +33,7 @@ module ParallelTests
33
33
 
34
34
  until !File.directory?(current) || current == previous
35
35
  filename = File.join(current, "Gemfile")
36
- return true if File.exists?(filename)
36
+ return true if File.exist?(filename)
37
37
  current, previous = File.expand_path("..", current), current
38
38
  end
39
39
 
@@ -64,5 +64,11 @@ module ParallelTests
64
64
  Time.now
65
65
  end
66
66
  end
67
+
68
+ def delta
69
+ before = now.to_f
70
+ yield
71
+ now.to_f - before
72
+ end
67
73
  end
68
74
  end
@@ -155,6 +155,8 @@ module ParallelTests
155
155
  options[:non_parallel] = true
156
156
  end
157
157
 
158
+ abort "Pass files or folders to run" if argv.empty? && !options[:execute]
159
+
158
160
  options[:files] = argv
159
161
 
160
162
  options[:group_by] ||= :filesize if options[:only_group]
@@ -24,7 +24,7 @@ module ParallelTests
24
24
  (runtime_logging if File.directory?(File.dirname(runtime_log))),
25
25
  cucumber_opts(options[:test_options]),
26
26
  *sanitized_test_files
27
- ].compact.join(' ')
27
+ ].compact.reject(&:empty?).join(' ')
28
28
  execute_command(cmd, process_number, num_processes, options)
29
29
  end
30
30
 
@@ -90,7 +90,7 @@ module ParallelTests
90
90
 
91
91
 
92
92
  def runtime_logging
93
- " --format ParallelTests::Gherkin::RuntimeLogger --out #{runtime_log}"
93
+ "--format ParallelTests::Gherkin::RuntimeLogger --out #{runtime_log}"
94
94
  end
95
95
 
96
96
  def runtime_log
@@ -99,7 +99,7 @@ module ParallelTests
99
99
 
100
100
  def determine_executable
101
101
  case
102
- when File.exists?("bin/#{name}")
102
+ when File.exist?("bin/#{name}")
103
103
  "bin/#{name}"
104
104
  when ParallelTests.bundler_enabled?
105
105
  "bundle exec #{name}"
@@ -17,7 +17,7 @@ module ParallelTests
17
17
 
18
18
  def determine_executable
19
19
  cmd = case
20
- when File.exists?("bin/rspec")
20
+ when File.exist?("bin/rspec")
21
21
  WINDOWS ? "ruby bin/rspec" : "bin/rspec"
22
22
  when File.file?("script/spec")
23
23
  "script/spec"
@@ -4,70 +4,59 @@ require 'parallel_tests/test/runner'
4
4
  module ParallelTests
5
5
  module Test
6
6
  class RuntimeLogger
7
- @@has_started = false
7
+ @@prepared = false
8
8
 
9
9
  class << self
10
- def log(test, start_time, end_time)
11
- return if test.is_a? ::Test::Unit::TestSuite # don't log for suites-of-suites
10
+ def log_test_run(test)
11
+ prepare
12
12
 
13
- if !@@has_started # make empty log file
14
- File.open(logfile, 'w'){}
15
- @@has_started = true
16
- end
13
+ result = nil
14
+ time = ParallelTests.delta { result = yield }
15
+ log(test, time)
17
16
 
18
- locked_appending_to(logfile) do |file|
19
- file.puts(message(test, start_time, end_time))
17
+ result
18
+ end
19
+
20
+ def unique_log
21
+ lock do
22
+ separator = "\n"
23
+ groups = File.read(logfile).split(separator).map { |line| line.split(":") }.group_by(&:first)
24
+ lines = groups.map { |file, times| "#{file}:#{times.map(&:last).map(&:to_f).inject(:+)}" }
25
+ File.write(logfile, lines.join(separator) + separator)
20
26
  end
21
27
  end
22
28
 
23
29
  private
24
30
 
25
- def message(test, start_time, end_time)
26
- delta = "%.2f" % (end_time.to_f-start_time.to_f)
27
- filename = class_directory(test.class) + class_to_filename(test.class) + ".rb"
28
- "#{filename}:#{delta}"
31
+ # ensure folder exists + clean out previous log
32
+ # this will happen in multiple processes, but should be roughly at the same time
33
+ # so there should be no log message lost
34
+ def prepare
35
+ return if @@prepared
36
+ @@prepared = true
37
+ FileUtils.mkdir_p(File.dirname(logfile))
38
+ File.write(logfile, '')
29
39
  end
30
40
 
31
- # Note: this is a best guess at conventional test directory structure, and may need
32
- # tweaking / post-processing to match correctly for any given project
33
- def class_directory(suspect)
34
- result = "test/"
35
-
36
- if defined?(Rails)
37
- result += case suspect.superclass.name
38
- when "ActionDispatch::IntegrationTest"
39
- "integration/"
40
- when "ActionDispatch::PerformanceTest"
41
- "performance/"
42
- when "ActionController::TestCase"
43
- "functional/"
44
- when "ActionView::TestCase"
45
- "unit/helpers/"
46
- else
47
- "unit/"
48
- end
41
+ def log(test, time)
42
+ return unless message = message(test, time)
43
+ lock do
44
+ File.open(logfile, 'a') { |f| f.puts message }
49
45
  end
50
- result
51
46
  end
52
47
 
53
- # based on https://github.com/grosser/single_test/blob/master/lib/single_test.rb#L117
54
- def class_to_filename(suspect)
55
- word = suspect.to_s.dup
56
- return word unless word.match /^[A-Z]/ and not word.match %r{/[a-z]}
57
-
58
- word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
59
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
60
- word.gsub!(/\:\:/, '/')
61
- word.tr!("-", "_")
62
- word.downcase!
63
- word
48
+ def message(test, time)
49
+ return unless method = test.public_instance_methods(true).detect { |method| method =~ /^test_/ }
50
+ delta = "%.2f" % time
51
+ filename = test.instance_method(method).source_location.first.sub("#{Dir.pwd}/", "")
52
+ "#{filename}:#{delta}"
64
53
  end
65
54
 
66
- def locked_appending_to(file)
67
- File.open(file, 'a') do |f|
55
+ def lock
56
+ File.open(logfile, 'r') do |f|
68
57
  begin
69
58
  f.flock File::LOCK_EX
70
- yield f
59
+ yield
71
60
  ensure
72
61
  f.flock File::LOCK_UN
73
62
  end
@@ -82,17 +71,38 @@ module ParallelTests
82
71
  end
83
72
  end
84
73
 
85
- require 'test/unit/testsuite'
86
- class ::Test::Unit::TestSuite
87
- alias :run_without_timing :run unless defined? @@timing_installed
74
+ if defined?(MiniTest::Unit)
75
+ MiniTest::Unit.class_eval do
76
+ alias_method :_run_suite_without_runtime_log, :_run_suite
77
+ def _run_suite(*args)
78
+ ParallelTests::Test::RuntimeLogger.log_test_run(args.first) do
79
+ _run_suite_without_runtime_log(*args)
80
+ end
81
+ end
88
82
 
89
- def run(result, &progress_block)
90
- first_test = self.tests.first
91
- start_time = ParallelTests.now
92
- run_without_timing(result, &progress_block)
93
- end_time = ParallelTests.now
94
- ParallelTests::Test::RuntimeLogger.log(first_test, start_time, end_time)
83
+ alias_method :_run_suites_without_runtime_log, :_run_suites
84
+ def _run_suites(*args)
85
+ result = _run_suites_without_runtime_log(*args)
86
+ ParallelTests::Test::RuntimeLogger.unique_log
87
+ result
88
+ end
95
89
  end
90
+ else
91
+ require 'test/unit/testsuite'
92
+ class ::Test::Unit::TestSuite
93
+ alias_method :run_without_timing, :run
94
+
95
+ def run(result, &block)
96
+ test = tests.first
96
97
 
97
- @@timing_installed = true
98
+ if test.is_a? ::Test::Unit::TestSuite # all tests ?
99
+ run_without_timing(result, &block)
100
+ ParallelTests::Test::RuntimeLogger.unique_log
101
+ else
102
+ ParallelTests::Test::RuntimeLogger.log_test_run(test.class) do
103
+ run_without_timing(result, &block)
104
+ end
105
+ end
106
+ end
107
+ end
98
108
  end
@@ -1,3 +1,3 @@
1
1
  module ParallelTests
2
- VERSION = Version = '1.1.1'
2
+ VERSION = Version = '1.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_tests
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-20 00:00:00.000000000 Z
11
+ date: 2015-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel