parallel_calabash 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a66007ad0d753bdc9664b5f4583fc80a47536566
4
+ data.tar.gz: b8196f697abf267a9deba0170ad02daf366b5793
5
+ SHA512:
6
+ metadata.gz: 7a76ae1d4616bcccae2622fcd618d9df0fb3993a2d3998d3ea5acd708acab088c8f34c935c7a12db8bafbcadb861414a25dbda90d85621f45a7ba24f4a5222a6
7
+ data.tar.gz: eeeddfae731c1e4fe2920b72bfbe0d52617a8b85e3eb6a570413faae2523e39a29de07243e0ae49b6617ba93cae4f1465ba7c7ab8526b91ccb3810885b6d0b38
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.idea
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use ruby-1.9.3-p0@parallel_calabash
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parallel_calabash.gemspec
4
+ gemspec
5
+ gem 'rake'
6
+ gem 'calabash-android'
7
+ gem 'pry'
8
+ gem 'rspec'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Rajdeep
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # ParallelCalabash
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'parallel_calabash'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install parallel_calabash
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/parallel_calabash/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path("../../lib", __FILE__)
4
+ require "parallel_calabash"
5
+ require 'parallel_calabash/version'
6
+ require 'optparse'
7
+
8
+ def parse_arguments(arguments)
9
+ options = {}
10
+ opt_parser = OptionParser.new do |opts|
11
+ opts.banner = "Usage: parallel_calabash [options]\n" \
12
+ "Example: parallel_calabash -a my.apk -o '<cucumber opts>' features/"
13
+
14
+ opts.on("-h", "--help", "Show this message") do
15
+ puts opts
16
+ exit
17
+ end
18
+
19
+ opts.on("-v", "--version", "Show version") do
20
+ puts ParallelCalabash::VERSION
21
+ exit
22
+ end
23
+
24
+ opts.on("-a", "--apk apk_path", "apk file path") do |apk_path|
25
+ options[:apk_path] = apk_path
26
+ end
27
+
28
+ opts.on("-o", "--cucumber_opts '[OPTIONS]'", "execute with those cucumber options") do |cucumber_opts|
29
+ options[:cucumber_options] = cucumber_opts
30
+ end
31
+
32
+ opts.on("--serialize-stdout", "Serialize stdout output, nothing will be written until everything is done") do |mute_output|
33
+ options[:mute_output] = mute_output
34
+ end
35
+
36
+ end
37
+
38
+ opt_parser.parse!(arguments)
39
+ options[:feature_folder] = arguments
40
+ options
41
+ rescue OptionParser::InvalidOption
42
+ puts "Invalid arguments "
43
+ puts opt_parser.help
44
+ end
45
+
46
+ options = parse_arguments(ARGV)
47
+
48
+ ParallelCalabash.run_tests_in_parallel options
49
+
50
+
@@ -0,0 +1,51 @@
1
+ require 'tempfile'
2
+ require 'parallel'
3
+ require 'parallel_calabash/version'
4
+ require 'parallel_calabash/adb_helper'
5
+ require 'parallel_calabash/runner'
6
+ require 'parallel_calabash/feature_grouper'
7
+ require 'parallel_calabash/result_formatter'
8
+ require 'rbconfig'
9
+
10
+ module ParallelCalabash
11
+
12
+ WINDOWS = (RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
13
+ class << self
14
+
15
+ def number_of_processes_to_start
16
+ number_of_processes = AdbHelper.number_of_connected_devices
17
+ raise "\n**** NO DEVICE FOUND ****\n" if number_of_processes==0
18
+ puts "*******************************"
19
+ puts " #{number_of_processes} DEVICES FOUND"
20
+ puts "*******************************"
21
+ number_of_processes
22
+ end
23
+
24
+ def run_tests_in_parallel(options)
25
+ number_of_processes = number_of_processes_to_start
26
+
27
+ test_results = nil
28
+ report_time_taken do
29
+ groups = FeatureGrouper.feature_groups(options[:feature_folder], number_of_processes)
30
+ puts "#{number_of_processes} processes for #{groups.flatten.size} features"
31
+ test_results = Parallel.map(groups, :in_threads => groups.size) do |group|
32
+ Runner.run_tests(group, groups.index(group), options)
33
+ end
34
+ ResultFormatter.report_results(test_results)
35
+ end
36
+
37
+ Kernel.exit(1) if any_test_failed?(test_results)
38
+ end
39
+
40
+ def any_test_failed?(test_results)
41
+ test_results.any? { |result| result[:exit_status] != 0 }
42
+ end
43
+
44
+ def report_time_taken
45
+ start = Time.now
46
+ yield
47
+ puts "\nTook #{Time.now - start} seconds"
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,24 @@
1
+ module ParallelCalabash
2
+ module AdbHelper
3
+ class << self
4
+
5
+ def connected_devices
6
+ begin
7
+ `adb devices`.scan(/\n(.*)\t/).flatten
8
+ rescue
9
+ []
10
+ end
11
+ end
12
+
13
+ def device_for_process process_num
14
+ connected_devices[process_num]
15
+ end
16
+
17
+ def number_of_connected_devices
18
+ connected_devices.size
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module ParallelCalabash
2
+ class FeatureGrouper
3
+
4
+ class << self
5
+
6
+ def feature_groups(feature_folder, group_size)
7
+ files = feature_files_in_folder feature_folder
8
+ min_number_files_per_group = files.size/group_size
9
+ remaining_number_of_files = files.size % group_size
10
+ groups = Array.new(group_size) { [] }
11
+ groups.each do |group|
12
+ min_number_files_per_group.times { group << files.delete_at(0) }
13
+ end
14
+ unless remaining_number_of_files==0
15
+ groups[0..(remaining_number_of_files-1)].each do |group|
16
+ group << files.delete_at(0)
17
+ end
18
+ end
19
+ groups.reject(&:empty?)
20
+ end
21
+
22
+ def feature_files_in_folder feature_dir
23
+ if File.directory?(feature_dir.first)
24
+ files = Dir[File.join(feature_dir, "**{,/*/**}/*")].uniq
25
+ files.grep(/\.feature$/)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,77 @@
1
+ module ParallelCalabash
2
+ class ResultFormatter
3
+ class << self
4
+
5
+
6
+ def report_results(test_results)
7
+ results = find_results(test_results.map { |result| result[:stdout] }.join(''))
8
+ puts ""
9
+ puts summarize_results(results)
10
+ end
11
+
12
+ def find_results(test_output)
13
+ test_output.split("\n").map { |line|
14
+ line.gsub!(/\e\[\d+m/, '')
15
+ next unless line_is_result?(line)
16
+ line
17
+ }.compact
18
+ end
19
+
20
+ def line_is_result?(line)
21
+ line =~scenario_or_step_result_regex or line =~ failing_scenario_regex
22
+ end
23
+
24
+ def summarize_results(results)
25
+ output = []
26
+
27
+ failing_scenarios = results.grep(failing_scenario_regex)
28
+ if failing_scenarios.any?
29
+ failing_scenarios.unshift("Failing Scenarios:")
30
+ output << failing_scenarios.join("\n")
31
+ end
32
+
33
+ output << summary(results)
34
+
35
+ output.join("\n\n")
36
+ end
37
+
38
+
39
+ def summary(results)
40
+ sort_order = %w[scenario step failed undefined skipped pending passed]
41
+
42
+ %w[scenario step].map do |group|
43
+ group_results = results.grep(/^\d+ #{group}/)
44
+ next if group_results.empty?
45
+
46
+ sums = sum_up_results(group_results)
47
+ sums = sums.sort_by { |word, _| sort_order.index(word) || 999 }
48
+ sums.map! do |word, number|
49
+ plural = "s" if word == group and number != 1
50
+ "#{number} #{word}#{plural}"
51
+ end
52
+ "#{sums[0]} (#{sums[1..-1].join(", ")})"
53
+ end.compact.join("\n")
54
+ end
55
+
56
+ def sum_up_results(results)
57
+ results = results.join(' ').gsub(/s\b/, '') # combine and singularize results
58
+ counts = results.scan(/(\d+) (\w+)/)
59
+ counts.inject(Hash.new(0)) do |sum, (number, word)|
60
+ sum[word] += number.to_i
61
+ sum
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def scenario_or_step_result_regex
68
+ /^\d+ (steps?|scenarios?)/
69
+ end
70
+
71
+ def failing_scenario_regex
72
+ /^cucumber features\/.+:\d+/
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,52 @@
1
+ module ParallelCalabash
2
+ class Runner
3
+ class << self
4
+ def base_command
5
+ 'calabash-android run'
6
+ end
7
+
8
+ def run_tests(test_files, process_number, options)
9
+ cmd = [base_command, options[:apk_path], options[:cucumber_options], *test_files].compact*' '
10
+ execute_command_for_process(process_number, cmd, options[:mute_output])
11
+ end
12
+
13
+ def execute_command_for_process(process_number, cmd, silence)
14
+ command_for_current_process = command_for_process(process_number, cmd)
15
+ output = open("|#{command_for_current_process}", "r") { |output| show_output(output, silence) }
16
+ exitstatus = $?.exitstatus
17
+
18
+ if silence
19
+ $stdout.print output
20
+ $stdout.flush
21
+ end
22
+ puts "\n****** PROCESS #{process_number} COMPLETED ******\n\n"
23
+ {:stdout => output, :exit_status => exitstatus}
24
+ end
25
+
26
+ def command_for_process process_number, cmd
27
+ env = {}
28
+ device_for_current_process = ParallelCalabash::AdbHelper.device_for_process process_number
29
+ env = env.merge({'AUTOTEST' => '1', 'ADB_DEVICE_ARG' => device_for_current_process, "TEST_PROCESS_NUMBER" => (process_number+1).to_s})
30
+ separator = (WINDOWS ? ' & ' : ';')
31
+ exports = env.map { |k, v| WINDOWS ? "(SET \"#{k}=#{v}\")" : "#{k}=#{v};export #{k}" }.join(' ')
32
+ exports + separator + cmd
33
+ end
34
+
35
+ def show_output(output, silence)
36
+ result = ""
37
+ loop do
38
+ begin
39
+ read = output.readpartial(1000000) # read whatever chunk we can get
40
+ result << read
41
+ unless silence
42
+ $stdout.print read
43
+ $stdout.flush
44
+ end
45
+ end
46
+ end rescue EOFError
47
+ result
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module ParallelCalabash
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parallel_calabash/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "parallel_calabash"
8
+ spec.version = ParallelCalabash::VERSION
9
+ spec.authors = ["Rajdeep"]
10
+ spec.email = ["mail.rajvarma@gmail.com"]
11
+ spec.summary = %q{calabash android tests in parallel}
12
+ spec.description = %q{Run different calabash android tests in parallel on different devices}
13
+ spec.homepage = "https://github.com/rajdeepv/parallel_calabash"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_runtime_dependency 'parallel'
24
+ end
@@ -0,0 +1,89 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ # rspec-expectations config goes here. You can use an alternate
19
+ # assertion/expectation library such as wrong or the stdlib/minitest
20
+ # assertions if you prefer.
21
+ config.expect_with :rspec do |expectations|
22
+ # This option will default to `true` in RSpec 4. It makes the `description`
23
+ # and `failure_message` of custom matchers include text for helper methods
24
+ # defined using `chain`, e.g.:
25
+ # be_bigger_than(2).and_smaller_than(4).description
26
+ # # => "be bigger than 2 and smaller than 4"
27
+ # ...rather than:
28
+ # # => "be bigger than 2"
29
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
30
+ end
31
+
32
+ # rspec-mocks config goes here. You can use an alternate test double
33
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
34
+ config.mock_with :rspec do |mocks|
35
+ # Prevents you from mocking or stubbing a method that does not exist on
36
+ # a real object. This is generally recommended, and will default to
37
+ # `true` in RSpec 4.
38
+ mocks.verify_partial_doubles = true
39
+ end
40
+
41
+ # The settings below are suggested to provide a good initial experience
42
+ # with RSpec, but feel free to customize to your heart's content.
43
+ =begin
44
+ # These two settings work together to allow you to limit a spec run
45
+ # to individual examples or groups you care about by tagging them with
46
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
47
+ # get run.
48
+ config.filter_run :focus
49
+ config.run_all_when_everything_filtered = true
50
+
51
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
52
+ # For more details, see:
53
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
54
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
55
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
56
+ config.disable_monkey_patching!
57
+
58
+ # This setting enables warnings. It's recommended, but in some cases may
59
+ # be too noisy due to issues in dependencies.
60
+ config.warnings = true
61
+
62
+ # Many RSpec users commonly either run the entire suite or an individual
63
+ # file, and it's useful to allow more verbose output when running an
64
+ # individual spec file.
65
+ if config.files_to_run.one?
66
+ # Use the documentation formatter for detailed output,
67
+ # unless a formatter has already been configured
68
+ # (e.g. via a command-line flag).
69
+ config.default_formatter = 'doc'
70
+ end
71
+
72
+ # Print the 10 slowest examples and example groups at the
73
+ # end of the spec run, to help surface which specs are running
74
+ # particularly slow.
75
+ config.profile_examples = 10
76
+
77
+ # Run specs in random order to surface order dependencies. If you find an
78
+ # order dependency and want to debug it, you can fix the order by providing
79
+ # the seed, which is printed after each run.
80
+ # --seed 1234
81
+ config.order = :random
82
+
83
+ # Seed global randomization in this process using the `--seed` CLI option.
84
+ # Setting this allows you to use `--seed` to deterministically reproduce
85
+ # test failures related to randomization by passing the same `--seed` value
86
+ # as the one that triggered the failure.
87
+ Kernel.srand config.seed
88
+ =end
89
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parallel_calabash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Rajdeep
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: parallel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Run different calabash android tests in parallel on different devices
56
+ email:
57
+ - mail.rajvarma@gmail.com
58
+ executables:
59
+ - parallel_calabash
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - .gitignore
64
+ - .rspec
65
+ - .rvmrc
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/parallel_calabash
71
+ - lib/parallel_calabash.rb
72
+ - lib/parallel_calabash/adb_helper.rb
73
+ - lib/parallel_calabash/feature_grouper.rb
74
+ - lib/parallel_calabash/result_formatter.rb
75
+ - lib/parallel_calabash/runner.rb
76
+ - lib/parallel_calabash/version.rb
77
+ - parallel_calabash.gemspec
78
+ - spec/spec_helper.rb
79
+ homepage: https://github.com/rajdeepv/parallel_calabash
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.4.2
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: calabash android tests in parallel
103
+ test_files:
104
+ - spec/spec_helper.rb