parallelized_specs 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), "/../parallelized_specs/tasks")
@@ -5,13 +5,13 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "parallelized_specs"
8
- s.version = "0.0.5"
8
+ s.version = "0.0.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jake Sorce, Bryan Madsen"]
12
- s.date = "2012-04-18"
12
+ s.date = "2012-04-19"
13
13
  s.email = "jake@instructure.com"
14
- s.executables = ["parallelized_spec", "parallelized_test"]
14
+ s.executables = ["parallelized_spec"]
15
15
  s.files = [
16
16
  "Gemfile",
17
17
  "Gemfile.lock",
@@ -19,8 +19,10 @@ Gem::Specification.new do |s|
19
19
  "Readme.md",
20
20
  "VERSION",
21
21
  "bin/parallelized_spec",
22
- "bin/parallelized_test",
23
22
  "lib/parallelized_specs.rb",
23
+ "lib/parallelized_specs/grouper.rb",
24
+ "lib/parallelized_specs/railtie.rb",
25
+ "lib/parallelized_specs/runtime_logger.rb",
24
26
  "lib/parallelized_specs/spec_error_count_logger.rb",
25
27
  "lib/parallelized_specs/spec_error_logger.rb",
26
28
  "lib/parallelized_specs/spec_failures_logger.rb",
@@ -28,20 +30,13 @@ Gem::Specification.new do |s|
28
30
  "lib/parallelized_specs/spec_runtime_logger.rb",
29
31
  "lib/parallelized_specs/spec_start_finish_logger.rb",
30
32
  "lib/parallelized_specs/spec_summary_logger.rb",
31
- "lib/parallelized_tests.rb",
32
- "lib/parallelized_tests/grouper.rb",
33
- "lib/parallelized_tests/railtie.rb",
34
- "lib/parallelized_tests/runtime_logger.rb",
35
- "lib/parallelized_tests/tasks.rb",
36
- "lib/tasks/parallelized_tests.rake",
33
+ "lib/parallelized_specs/tasks.rb",
34
+ "lib/tasks/parallelized_specs.rake",
37
35
  "parallelized_specs.gemspec",
38
- "spec/integration_spec.rb",
39
36
  "spec/parallelized_specs/spec_failure_logger_spec.rb",
40
37
  "spec/parallelized_specs/spec_runtime_logger_spec.rb",
41
38
  "spec/parallelized_specs/spec_summary_logger_spec.rb",
42
39
  "spec/parallelized_specs_spec.rb",
43
- "spec/parallelized_tests/runtime_logger_spec.rb",
44
- "spec/parallelized_tests_spec.rb",
45
40
  "spec/spec_helper.rb"
46
41
  ]
47
42
  s.homepage = "http://github.com/jakesorce/parallelized_specs"
@@ -53,12 +48,9 @@ Gem::Specification.new do |s|
53
48
  s.specification_version = 3
54
49
 
55
50
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
56
- s.add_runtime_dependency(%q<parallel>, [">= 0"])
57
51
  else
58
- s.add_dependency(%q<parallel>, [">= 0"])
59
52
  end
60
53
  else
61
- s.add_dependency(%q<parallel>, [">= 0"])
62
54
  end
63
55
  end
64
56
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallelized_specs
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 6
10
+ version: 0.0.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jake Sorce, Bryan Madsen
@@ -15,27 +15,13 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-18 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- name: parallel
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
24
- none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 3
29
- segments:
30
- - 0
31
- version: "0"
32
- type: :runtime
33
- version_requirements: *id001
18
+ date: 2012-04-19 00:00:00 Z
19
+ dependencies: []
20
+
34
21
  description:
35
22
  email: jake@instructure.com
36
23
  executables:
37
24
  - parallelized_spec
38
- - parallelized_test
39
25
  extensions: []
40
26
 
41
27
  extra_rdoc_files: []
@@ -47,8 +33,10 @@ files:
47
33
  - Readme.md
48
34
  - VERSION
49
35
  - bin/parallelized_spec
50
- - bin/parallelized_test
51
36
  - lib/parallelized_specs.rb
37
+ - lib/parallelized_specs/grouper.rb
38
+ - lib/parallelized_specs/railtie.rb
39
+ - lib/parallelized_specs/runtime_logger.rb
52
40
  - lib/parallelized_specs/spec_error_count_logger.rb
53
41
  - lib/parallelized_specs/spec_error_logger.rb
54
42
  - lib/parallelized_specs/spec_failures_logger.rb
@@ -56,20 +44,13 @@ files:
56
44
  - lib/parallelized_specs/spec_runtime_logger.rb
57
45
  - lib/parallelized_specs/spec_start_finish_logger.rb
58
46
  - lib/parallelized_specs/spec_summary_logger.rb
59
- - lib/parallelized_tests.rb
60
- - lib/parallelized_tests/grouper.rb
61
- - lib/parallelized_tests/railtie.rb
62
- - lib/parallelized_tests/runtime_logger.rb
63
- - lib/parallelized_tests/tasks.rb
64
- - lib/tasks/parallelized_tests.rake
47
+ - lib/parallelized_specs/tasks.rb
48
+ - lib/tasks/parallelized_specs.rake
65
49
  - parallelized_specs.gemspec
66
- - spec/integration_spec.rb
67
50
  - spec/parallelized_specs/spec_failure_logger_spec.rb
68
51
  - spec/parallelized_specs/spec_runtime_logger_spec.rb
69
52
  - spec/parallelized_specs/spec_summary_logger_spec.rb
70
53
  - spec/parallelized_specs_spec.rb
71
- - spec/parallelized_tests/runtime_logger_spec.rb
72
- - spec/parallelized_tests_spec.rb
73
54
  - spec/spec_helper.rb
74
55
  homepage: http://github.com/jakesorce/parallelized_specs
75
56
  licenses: []
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'optparse'
4
- require 'parallel'
5
- raise "please ' gem install parallel '" if Gem::Version.new(Parallel::VERSION) < Gem::Version.new('0.4.2')
6
- $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
7
- require "parallelized_tests"
8
-
9
- options = {}
10
- OptionParser.new do |opts|
11
- opts.banner = <<BANNER
12
- Run all tests in parallel, giving each process ENV['TEST_ENV_NUMBER'] ('', '2', '3', ...)
13
-
14
- [optional] Only run selected files & folders:
15
- parallelized_test test/bar test/baz/xxx_text.rb
16
-
17
- Options are:
18
- BANNER
19
- opts.on("-n [PROCESSES]", Integer, "How many processes to use, default: available CPUs"){|n| options[:count] = n }
20
- opts.on("-p", '--pattern [PATTERN]', "run tests matching this pattern"){|pattern| options[:pattern] = pattern }
21
- opts.on("--no-sort", "do not sort files before running them"){ |no_sort| options[:no_sort] = no_sort }
22
- opts.on("-m [FLOAT]", "--multiply-processes [FLOAT]", Float, "use given number as a multiplier of processes to run"){ |multiply| options[:multiply] = multiply }
23
- opts.on("-r", '--root [PATH]', "execute test commands from this path"){|path| options[:root] = path }
24
- opts.on("-s [PATTERN]", "--single [PATTERN]", "Run all matching files in only one process") do |pattern|
25
- options[:single_process] ||= []
26
- options[:single_process] << /#{pattern}/
27
- end
28
- opts.on("-e", '--exec [COMMAND]', "execute this code parallel and with ENV['TEST_ENV_NUM']"){|path| options[:execute] = path }
29
- opts.on("-o", "--test-options '[OPTIONS]'", "execute test commands with those options"){|arg| options[:test_options] = arg }
30
- opts.on("-t", "--type [TYPE]", "which type of tests to run? test, spec or features"){|type| options[:type] = type }
31
- opts.on("--non-parallel", "execute same commands but do not in parallel, needs --exec"){ options[:non_parallel] = true }
32
- opts.on("--chunk-timeout [TIMEOUT]", "timeout before re-printing the output of a child-process"){|timeout| options[:chunk_timeout] = timeout.to_f }
33
- opts.on('-v', '--version', 'Show Version'){ puts ParallelizedTests::VERSION; exit}
34
- opts.on("-h", "--help", "Show this.") { puts opts; exit }
35
- end.parse!
36
-
37
- raise "--no-sort and --single-process are not supported" if options[:no_sort] and options[:single_process]
38
-
39
- # get files to run from arguments
40
- options[:files] = ARGV if ARGV.size > 0
41
-
42
- num_processes = options[:count] || Parallel.processor_count
43
- num_processes = num_processes * (options[:multiply] || 1)
44
-
45
- if options[:execute]
46
- runs = (0...num_processes).to_a
47
- results = if options[:non_parallel]
48
- runs.map do |i|
49
- ParallelizedTests.execute_command(options[:execute], i, options)
50
- end
51
- else
52
- Parallel.map(runs, :in_processes => num_processes) do |i|
53
- ParallelizedTests.execute_command(options[:execute], i, options)
54
- end
55
- end.flatten
56
- abort if results.any?{|r| r[:exit_status] != 0 }
57
- else
58
- lib, name, task = {
59
- 'test' => ["tests", "test", "test"],
60
- 'spec' => ["specs", "spec", "spec"],
61
- }[options[:type]||'test']
62
-
63
- require "parallelized_#{lib}"
64
- klass = eval("Parallelized#{lib.capitalize}")
65
-
66
- start = Time.now
67
-
68
- tests_folder = task
69
- tests_folder = File.join(options[:root], tests_folder) unless options[:root].to_s.empty?
70
-
71
- groups = klass.tests_in_groups(options[:files] || tests_folder, num_processes, options)
72
- num_processes = groups.size
73
-
74
- #adjust processes to groups
75
- abort "no #{name}s found!" if groups.size == 0
76
-
77
- num_tests = groups.inject(0){|sum,item| sum + item.size }
78
- puts "#{num_processes} processes for #{num_tests} #{name}s, ~ #{num_tests / groups.size} #{name}s per process"
79
-
80
- test_results = Parallel.map(groups, :in_processes => num_processes) do |group|
81
- klass.run_tests(group, groups.index(group), options)
82
- end
83
-
84
- #parse and print results
85
- results = klass.find_results(test_results.map{|result| result[:stdout] }*"")
86
- puts ""
87
- puts klass.summarize_results(results)
88
-
89
- #report total time taken
90
- puts ""
91
- puts "Took #{Time.now - start} seconds"
92
-
93
- #exit with correct status code so rake parallel:test && echo 123 works
94
- failed = test_results.any?{|result| result[:exit_status] != 0 }
95
- abort "#{name.capitalize}s Failed" if failed
96
- end
@@ -1,163 +0,0 @@
1
- require 'parallel'
2
- require 'parallelized_tests/grouper'
3
- require 'parallelized_tests/railtie'
4
-
5
- class ParallelizedTests
6
- VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
7
-
8
- # parallel:spec[:count, :pattern, :options]
9
- def self.parse_rake_args(args)
10
- # order as given by user
11
- args = [args[:count], args[:pattern], args[:options]]
12
-
13
- # count given or empty ?
14
- # parallel:spec[2,models,options]
15
- # parallel:spec[,models,options]
16
- count = args.shift if args.first.to_s =~ /^\d*$/
17
- num_processes = count.to_i unless count.to_s.empty?
18
- num_processes ||= ENV['PARALLEL_TEST_PROCESSORS'].to_i if ENV['PARALLEL_TEST_PROCESSORS']
19
- num_processes ||= Parallel.processor_count
20
-
21
- pattern = args.shift
22
- options = args.shift
23
-
24
- [num_processes.to_i, pattern.to_s, options.to_s]
25
- end
26
-
27
- # finds all tests and partitions them into groups
28
- def self.tests_in_groups(root, num_groups, options={})
29
- tests = find_tests(root, options)
30
- if options[:no_sort] == true
31
- Grouper.in_groups(tests, num_groups)
32
- else
33
- tests = with_runtime_info(tests)
34
- Grouper.in_even_groups_by_size(tests, num_groups, options)
35
- end
36
- end
37
-
38
- def self.run_tests(test_files, process_number, options)
39
- require_list = test_files.map { |filename| %{"#{File.expand_path filename}"} }.join(",")
40
- cmd = "ruby -Itest -e '[#{require_list}].each {|f| require f }' -- #{options[:test_options]}"
41
- execute_command(cmd, process_number, options)
42
- end
43
-
44
- def self.execute_command(cmd, process_number, options)
45
- cmd = "TEST_ENV_NUMBER=#{test_env_number(process_number)} ; export TEST_ENV_NUMBER; #{cmd}"
46
- f = open("|#{cmd}", 'r')
47
- output = fetch_output(f, options)
48
- f.close
49
- {:stdout => output, :exit_status => $?.exitstatus}
50
- end
51
-
52
- def self.find_results(test_output)
53
- test_output.split("\n").map {|line|
54
- line = line.gsub(/\.|F|\*/,'')
55
- next unless line_is_result?(line)
56
- line
57
- }.compact
58
- end
59
-
60
- def self.test_env_number(process_number)
61
- process_number == 0 ? '' : process_number + 1
62
- end
63
-
64
- def self.runtime_log
65
- 'tmp/parallelized_runtime_test.log'
66
- end
67
-
68
- def self.summarize_results(results)
69
- results = results.join(' ').gsub(/s\b/,'') # combine and singularize results
70
- counts = results.scan(/(\d+) (\w+)/)
71
- sums = counts.inject(Hash.new(0)) do |sum, (number, word)|
72
- sum[word] += number.to_i
73
- sum
74
- end
75
- sums.sort.map{|word, number| "#{number} #{word}#{'s' if number != 1}" }.join(', ')
76
- end
77
-
78
- protected
79
-
80
- # read output of the process and print in in chucks
81
- def self.fetch_output(process, options)
82
- all = ''
83
- buffer = ''
84
- timeout = options[:chunk_timeout] || 0.2
85
- flushed = Time.now.to_f
86
-
87
- while char = process.getc
88
- char = (char.is_a?(Fixnum) ? char.chr : char) # 1.8 <-> 1.9
89
- all << char
90
-
91
- # print in chunks so large blocks stay together
92
- now = Time.now.to_f
93
- buffer << char
94
- if flushed + timeout < now
95
- print buffer
96
- STDOUT.flush
97
- buffer = ''
98
- flushed = now
99
- end
100
- end
101
-
102
- # print the remainder
103
- print buffer
104
- STDOUT.flush
105
-
106
- all
107
- end
108
-
109
- # copied from http://github.com/carlhuda/bundler Bundler::SharedHelpers#find_gemfile
110
- def self.bundler_enabled?
111
- return true if Object.const_defined?(:Bundler)
112
-
113
- previous = nil
114
- current = File.expand_path(Dir.pwd)
115
-
116
- until !File.directory?(current) || current == previous
117
- filename = File.join(current, "Gemfile")
118
- return true if File.exists?(filename)
119
- current, previous = File.expand_path("..", current), current
120
- end
121
-
122
- false
123
- end
124
-
125
- def self.line_is_result?(line)
126
- line =~ /\d+ failure/
127
- end
128
-
129
- def self.test_suffix
130
- "_test.rb"
131
- end
132
-
133
- def self.with_runtime_info(tests)
134
- lines = File.read(runtime_log).split("\n") rescue []
135
-
136
- # use recorded test runtime if we got enough data
137
- if lines.size * 1.5 > tests.size
138
- puts "Using recorded test runtime"
139
- times = Hash.new(1)
140
- lines.each do |line|
141
- test, time = line.split(":")
142
- next unless test and time
143
- times[File.expand_path(test)] = time.to_f
144
- end
145
- tests.sort.map{|test| [test, times[test]] }
146
- else # use file sizes
147
- tests.sort.map{|test| [test, File.stat(test).size] }
148
- end
149
- end
150
-
151
- def self.find_tests(root, options={})
152
- if root.is_a?(Array)
153
- root
154
- else
155
- # follow one symlink and direct children
156
- # http://stackoverflow.com/questions/357754/can-i-traverse-symlinked-directories-in-ruby-with-a-glob
157
- files = Dir["#{root}/**{,/*/**}/*#{test_suffix}"].uniq
158
- files = files.map{|f| f.sub(root+'/','') }
159
- files = files.grep(/#{options[:pattern]}/)
160
- files.map{|f| "#{root}/#{f}" }
161
- end
162
- end
163
- end
@@ -1,78 +0,0 @@
1
- class ParallelizedTests::RuntimeLogger
2
- @@has_started = false
3
-
4
- def self.log(test, start_time, end_time)
5
- return if test.is_a? Test::Unit::TestSuite # don't log for suites-of-suites
6
-
7
- if !@@has_started # make empty log file
8
- File.open(ParallelizedTests.runtime_log, 'w') do end
9
- @@has_started = true
10
- end
11
-
12
- File.open(ParallelizedTests.runtime_log, 'a') do |output|
13
- begin
14
- output.flock File::LOCK_EX
15
- output.puts(self.message(test, start_time, end_time))
16
- ensure
17
- output.flock File::LOCK_UN
18
- end
19
- end
20
- end
21
-
22
- def self.message(test, start_time, end_time)
23
- delta="%.2f" % (end_time.to_f-start_time.to_f)
24
- filename=class_directory(test.class) + class_to_filename(test.class) + ".rb"
25
- message="#{filename}:#{delta}"
26
- end
27
-
28
- # Note: this is a best guess at conventional test directory structure, and may need
29
- # tweaking / post-processing to match correctly for any given project
30
- def self.class_directory(suspect)
31
- result = "test/"
32
-
33
- if defined?(Rails)
34
- result += case suspect.superclass.name
35
- when "ActionDispatch::IntegrationTest"
36
- "integration/"
37
- when "ActionDispatch::PerformanceTest"
38
- "performance/"
39
- when "ActionController::TestCase"
40
- "functional/"
41
- when "ActionView::TestCase"
42
- "unit/helpers/"
43
- else
44
- "unit/"
45
- end
46
- end
47
- result
48
- end
49
-
50
- # based on https://github.com/grosser/single_test/blob/master/lib/single_test.rb#L117
51
- def self.class_to_filename(suspect)
52
- word = suspect.to_s.dup
53
- return word unless word.match /^[A-Z]/ and not word.match %r{/[a-z]}
54
-
55
- word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
56
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
57
- word.gsub!(/\:\:/,'/')
58
- word.tr!("-", "_")
59
- word.downcase!
60
- word
61
- end
62
-
63
- end
64
-
65
- require 'test/unit/testsuite'
66
- class Test::Unit::TestSuite
67
-
68
- alias :run_without_timing :run unless defined? @@timing_installed
69
-
70
- def run(result, &progress_block)
71
- start_time=Time.now
72
- run_without_timing(result, &progress_block)
73
- end_time=Time.now
74
- ParallelizedTests::RuntimeLogger.log(self.tests.first, start_time, end_time)
75
- end
76
- @@timing_installed = true
77
-
78
- end