rubotium 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +1 -0
- data/bin/rubotium +26 -0
- data/lib/rubotium/adb/command.rb +21 -0
- data/lib/rubotium/adb/devices.rb +25 -0
- data/lib/rubotium/adb/install_command.rb +17 -0
- data/lib/rubotium/adb/instrumentation.rb +36 -0
- data/lib/rubotium/adb/shell.rb +30 -0
- data/lib/rubotium/adb/test_result_parser.rb +87 -0
- data/lib/rubotium/adb/uninstall_command.rb +17 -0
- data/lib/rubotium/adb.rb +13 -0
- data/lib/rubotium/apk/converter.rb +22 -0
- data/lib/rubotium/apk.rb +7 -0
- data/lib/rubotium/cmd.rb +17 -0
- data/lib/rubotium/device.rb +49 -0
- data/lib/rubotium/devices.rb +25 -0
- data/lib/rubotium/formatters/junit_formatter.rb +82 -0
- data/lib/rubotium/grouper.rb +40 -0
- data/lib/rubotium/jar_reader.rb +70 -0
- data/lib/rubotium/package.rb +36 -0
- data/lib/rubotium/runable_test.rb +11 -0
- data/lib/rubotium/test_case.rb +6 -0
- data/lib/rubotium/test_suite.rb +12 -0
- data/lib/rubotium/version.rb +3 -0
- data/lib/rubotium.rb +85 -0
- data/rubotium.gemspec +33 -0
- data/spec/fixtures/adb_devices_results.rb +23 -0
- data/spec/fixtures/adb_results.rb +60 -0
- data/spec/fixtures/jar_contents.rb +28 -0
- data/spec/fixtures/javap_classes.rb +52 -0
- data/spec/lib/rubotium/adb/adb_devices_spec.rb +25 -0
- data/spec/lib/rubotium/adb/adb_instrumentation_spec.rb +32 -0
- data/spec/lib/rubotium/adb/adb_result_parser_spec.rb +132 -0
- data/spec/lib/rubotium/adb/adb_shell_spec.rb +23 -0
- data/spec/lib/rubotium/devices_spec.rb +45 -0
- data/spec/lib/rubotium/formatters/junit_formatter_spec.rb +7 -0
- data/spec/lib/rubotium/grouper_spec.rb +56 -0
- data/spec/lib/rubotium/jar_reader_spec.rb +58 -0
- data/spec/lib/rubotium_spec.rb +13 -0
- data/spec/spec_helper.rb +20 -0
- metadata +256 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a668e6a758f73295484b33a8bb12d7787ace3669
|
4
|
+
data.tar.gz: 32bb1aedd3650eadb296d6cbe3c8030d85cbe013
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e803e2b146f3654ef5b790f6e4c989b66afaf5aefd172c37c5c03b68ba43014d56ce1b7bd68af8123d84431d5fb953c2b895b4bd1c2f4f4a4c6294a99dd5987f
|
7
|
+
data.tar.gz: 999a97ea8be422f305648034fc05eab2dac5a12210bac25f1f7e0bb67c887d812773df18b75b75d00a890625cbf068a1847a54633877076aa9185fea4c268ae9
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Slawomir Smiechura
|
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,24 @@
|
|
1
|
+
# Rubotium
|
2
|
+
|
3
|
+
This is an Android's Instrumentation test runner. It's in quite early phase but already solves couple of issues with instrumentation tests:
|
4
|
+
|
5
|
+
* runs tests in parallel
|
6
|
+
* retries failed tests
|
7
|
+
* does execute all the package tests even if the app dies during the execution
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
gem install rubotium
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
$ rubotium -t <path_to_tests.apk> -a <path_to_application.apk> -r <instrumentation_test_runner>
|
16
|
+
$ rubotium -h for help
|
17
|
+
|
18
|
+
## Contributing
|
19
|
+
|
20
|
+
1. Fork it ( http://github.com/<my-github-username>/rubotium/fork )
|
21
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
22
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
23
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
24
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/rubotium
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubotium'
|
4
|
+
|
5
|
+
require 'trollop'
|
6
|
+
opts = Trollop::options do
|
7
|
+
opt :test_jar_path, 'Path to the tests .jar file', :type => :string, :short => '-j'
|
8
|
+
opt :test_apk_path, 'Path to the tests .apk file', :type => :string, :short => '-t'
|
9
|
+
opt :app_apk_path, 'Path to the app .apk file', :type => :string, :short => '-a'
|
10
|
+
opt :rerun, 'Retries count', :default => 0, :short => '-R'
|
11
|
+
opt :out, 'Report file', :default => 'report.xml', :short => '-o'
|
12
|
+
opt :device, 'Match devices', :type => :string, :short => '-d'
|
13
|
+
opt :runner, 'Test runner', :type => :string, :short => 'r'
|
14
|
+
end
|
15
|
+
|
16
|
+
params = {
|
17
|
+
:tests_jar_path => opts[:test_jar_path],
|
18
|
+
:tests_apk_path => opts[:test_apk_path],
|
19
|
+
:app_apk_path => opts[:app_apk_path],
|
20
|
+
:rerun_count => opts[:rerun],
|
21
|
+
:report => opts[:out],
|
22
|
+
:device_matcher => opts[:device],
|
23
|
+
:runner => opts[:runner]
|
24
|
+
}
|
25
|
+
|
26
|
+
Rubotium.new(params)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class Command
|
4
|
+
def initialize(device_serial)
|
5
|
+
@device_serial = device_serial
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute(command_to_run)
|
9
|
+
puts "EXECUTING_COMMAND: #{adb_command} #{command_to_run.executable_command}"
|
10
|
+
CMD.run_command(adb_command + " " + command_to_run.executable_command)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
attr_reader :device_serial
|
15
|
+
|
16
|
+
def adb_command
|
17
|
+
"adb -s #{device_serial} "
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class Devices
|
4
|
+
attr_reader :list
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@list = parse(CMD.run_command('adb devices',{ :timeout => 5 } ))
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def parse result
|
12
|
+
list = result.split("\n")
|
13
|
+
list.shift
|
14
|
+
attached_devices list
|
15
|
+
end
|
16
|
+
|
17
|
+
def attached_devices(list)
|
18
|
+
list.collect { |device|
|
19
|
+
parts = device.split("\t")
|
20
|
+
parts.last == 'device' ? parts.first : nil
|
21
|
+
}.compact
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class InstallCommand
|
4
|
+
COMMAND = 'install'
|
5
|
+
def initialize(apk_path)
|
6
|
+
@apk_path = apk_path
|
7
|
+
end
|
8
|
+
|
9
|
+
def executable_command
|
10
|
+
"#{COMMAND} #{apk_path}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
attr_reader :apk_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class NoTestRunnerError < StandardError; end
|
4
|
+
class NoTestPackageError < StandardError; end
|
5
|
+
|
6
|
+
class Instrumentation
|
7
|
+
attr_accessor :test_package_name, :test_runner
|
8
|
+
attr_reader :adb_shell
|
9
|
+
|
10
|
+
def initialize(device)
|
11
|
+
@adb_shell = Rubotium::Adb::Shell.new(device)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_test(runable_test)
|
15
|
+
check_packages
|
16
|
+
result = adb_shell.run_command(instrument_command(runable_test.package_name, runable_test.test_name))
|
17
|
+
TestResultParser.new(result, runable_test.package_name, runable_test.test_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def instrument_command package_name, test_name
|
22
|
+
"am instrument -w -e class #{class_test(package_name, test_name)} #{test_package_name}/#{test_runner}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def class_test package_name, test_name
|
26
|
+
"#{package_name}##{test_name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def check_packages
|
30
|
+
raise NoTestRunnerError if !test_runner
|
31
|
+
raise NoTestPackageError if !test_package_name
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class Shell
|
4
|
+
ADB = 'adb'
|
5
|
+
attr_reader :device_serial
|
6
|
+
|
7
|
+
def initialize(device_serial)
|
8
|
+
@device_serial = device_serial
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_command command_to_run
|
12
|
+
CMD.run_command(command + " " + command_to_run)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def command
|
17
|
+
[ADB, device, 'shell'].compact.join(" ")
|
18
|
+
end
|
19
|
+
|
20
|
+
def device
|
21
|
+
if device_serial && !device_serial.empty?
|
22
|
+
['-s', device_serial]
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class TestResultParser
|
4
|
+
ERROR_STATUS = 'ERROR'
|
5
|
+
FAIL_STATUS = 'FAIL'
|
6
|
+
OK_STATUS = 'OK'
|
7
|
+
|
8
|
+
attr_reader :result, :stack_trace, :time, :error_message, :status, :package_name, :test_name
|
9
|
+
|
10
|
+
def initialize(result, package_name, test_name)
|
11
|
+
@result = result
|
12
|
+
@package_name = package_name
|
13
|
+
@test_name = test_name
|
14
|
+
parse
|
15
|
+
end
|
16
|
+
|
17
|
+
def failed?
|
18
|
+
@status == FAIL_STATUS
|
19
|
+
end
|
20
|
+
|
21
|
+
def passed?
|
22
|
+
@status == OK_STATUS
|
23
|
+
end
|
24
|
+
|
25
|
+
def errored?
|
26
|
+
@status == ERROR_STATUS
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def parse
|
31
|
+
if ENV['DEBUG']
|
32
|
+
p result
|
33
|
+
end
|
34
|
+
@stack_trace = has_failed? ? get_stack_trace : ""
|
35
|
+
@status = get_status
|
36
|
+
@time = has_errored? ? 0 : get_time
|
37
|
+
@error_message = has_errored? ? get_error : ""
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_status
|
41
|
+
if has_errored?
|
42
|
+
ERROR_STATUS
|
43
|
+
elsif has_failed?
|
44
|
+
FAIL_STATUS
|
45
|
+
else
|
46
|
+
OK_STATUS
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_errored?
|
51
|
+
['INSTRUMENTATION_ABORTED', 'INSTRUMENTATION_RESULT','INSTRUMENTATION_STATUS'].any? {|error_message|
|
52
|
+
result.include? error_message
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_failed?
|
57
|
+
result.include? 'FAILURES'
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_time
|
61
|
+
result.match(/Time: (-?\d+.\d+)/)[1].to_f.abs
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_stack_trace
|
65
|
+
result.split("\r\n\r\n")[0].split(/^/)[3..-1].join
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_error
|
69
|
+
if instrumentation_error
|
70
|
+
result.match(/INSTRUMENTATION_RESULT: longMsg=(.*)\r/)[1]
|
71
|
+
elsif application_error
|
72
|
+
result.match(/INSTRUMENTATION_ABORTED: (.*)/)[1]
|
73
|
+
else
|
74
|
+
result.match(/INSTRUMENTATION_STATUS: Error=(.*)\r/)[1]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def application_error
|
79
|
+
result.match(/INSTRUMENTATION_ABORTED/) != nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def instrumentation_error
|
83
|
+
result.match(/INSTRUMENTATION_RESULT: longMsg/) != nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rubotium
|
2
|
+
module Adb
|
3
|
+
class UninstallCommand
|
4
|
+
COMMAND = 'uninstall'
|
5
|
+
def initialize(package_name)
|
6
|
+
@package_name = package_name
|
7
|
+
end
|
8
|
+
|
9
|
+
def executable_command
|
10
|
+
"#{COMMAND} #{package_name}"
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
attr_reader :package_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/rubotium/adb.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'adb/command'
|
2
|
+
require_relative 'adb/install_command'
|
3
|
+
require_relative 'adb/uninstall_command'
|
4
|
+
require_relative 'adb/devices'
|
5
|
+
require_relative 'adb/test_result_parser'
|
6
|
+
require_relative 'adb/shell'
|
7
|
+
require_relative 'adb/instrumentation'
|
8
|
+
|
9
|
+
module Rubotium
|
10
|
+
module Adb
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'dex2jar'
|
2
|
+
module Rubotium
|
3
|
+
module Apk
|
4
|
+
class Converter
|
5
|
+
def initialize(apk_path, output_path)
|
6
|
+
@apk_path = apk_path
|
7
|
+
@output_path = output_path
|
8
|
+
end
|
9
|
+
|
10
|
+
def convert_to_jar
|
11
|
+
Dex2jar.execute("-f -o #{output_path}", [apk_path])
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_reader :apk_path, :output_path
|
16
|
+
|
17
|
+
def file_exists?
|
18
|
+
File.exist? apk_path
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/rubotium/apk.rb
ADDED
data/lib/rubotium/cmd.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
module Rubotium
|
3
|
+
class CMD
|
4
|
+
class << self
|
5
|
+
def run_command(command_to_run, opts = {})
|
6
|
+
begin
|
7
|
+
Timeout::timeout(opts[:timeout] || 10 * 60) {
|
8
|
+
puts "[EXECUTING]: #{command_to_run}" if ENV['DEBUG']
|
9
|
+
`#{command_to_run}`
|
10
|
+
}
|
11
|
+
rescue Timeout::Error
|
12
|
+
""
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Rubotium
|
2
|
+
class Device
|
3
|
+
attr_accessor :testsuite
|
4
|
+
attr_reader :serial, :results
|
5
|
+
def initialize(serial, test_runner)
|
6
|
+
@runner = test_runner
|
7
|
+
@retry = 1
|
8
|
+
@serial = serial
|
9
|
+
@results = {}
|
10
|
+
@command = Rubotium::Adb::Command.new(serial)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_runner_name= name
|
14
|
+
runner.test_runner = name
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_package_name= name
|
18
|
+
runner.test_package_name = name
|
19
|
+
end
|
20
|
+
|
21
|
+
def install(apk_path)
|
22
|
+
command.execute(Rubotium::Adb::InstallCommand.new(apk_path))
|
23
|
+
end
|
24
|
+
|
25
|
+
def uninstall(package_name)
|
26
|
+
command.execute(Rubotium::Adb::UninstallCommand.new(package_name))
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_tests
|
30
|
+
raise(NoTestSuiteError, "Please setup test suite before running tests") if testsuite.nil?
|
31
|
+
puts "Running tests"
|
32
|
+
testsuite.each{|runable_test|
|
33
|
+
@results[runable_test.package_name] = []
|
34
|
+
puts runable_test.name
|
35
|
+
run_count = 0
|
36
|
+
puts "TEST: #{runable_test.name}"
|
37
|
+
while ((result = runner.run_test(runable_test)) && (result.failed? || result.errored?) && run_count < @retry ) do
|
38
|
+
puts "RERUNNING TEST: #{runable_test.name}, STATUS: #{result.status}"
|
39
|
+
run_count += 1
|
40
|
+
end
|
41
|
+
puts "FINISHED with status: #{result.status}"
|
42
|
+
@results[runable_test.package_name].push(result)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
attr_reader :runner, :command
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rubotium
|
2
|
+
class Devices
|
3
|
+
def initialize(device_matcher = '')
|
4
|
+
@attached_devices = Adb::Devices.new.list
|
5
|
+
@matched = matched_devices device_matcher
|
6
|
+
end
|
7
|
+
|
8
|
+
def all
|
9
|
+
raise NoDevicesError if attached_devices.empty?
|
10
|
+
raise NoMatchedDevicesError if matched.empty?
|
11
|
+
matched.map{|device|
|
12
|
+
Device.new(device, Adb::Instrumentation.new(device))
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
attr_reader :matched, :attached_devices
|
18
|
+
|
19
|
+
def matched_devices(device_matcher)
|
20
|
+
device_matcher ? attached_devices.select{|device|
|
21
|
+
device.include? device_matcher
|
22
|
+
} : attached_devices
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module Rubotium
|
4
|
+
module Formatters
|
5
|
+
class JunitFormatter
|
6
|
+
attr_reader :xml
|
7
|
+
def initialize(results, path_to_file)
|
8
|
+
@xml = Builder::XmlMarkup.new :target => ensure_io(path_to_file), :indent => 2
|
9
|
+
|
10
|
+
xml.testsuites do
|
11
|
+
results.each{|package_name, tests|
|
12
|
+
start_test_suite(package_name, tests)
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
private
|
17
|
+
def start_test_suite(package_name, tests)
|
18
|
+
failures = get_failures(tests)
|
19
|
+
errors = get_errors(tests)
|
20
|
+
tests_time = get_tests_time(tests)
|
21
|
+
tests_count = tests.count
|
22
|
+
params = {
|
23
|
+
:errors => errors,
|
24
|
+
:failures => failures,
|
25
|
+
:name => package_name,
|
26
|
+
:tests => tests_count,
|
27
|
+
:time => tests_time,
|
28
|
+
:timestamp => Time.now
|
29
|
+
}
|
30
|
+
|
31
|
+
xml.testsuite(params) do
|
32
|
+
tests.each { |test|
|
33
|
+
print_testcase(test)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def print_testcase(test)
|
39
|
+
xml.testcase :classname=>test.package_name, :name=>test.test_name, :time=>test.time do
|
40
|
+
has_failures(test)
|
41
|
+
has_errors(test)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def has_failures(test)
|
46
|
+
if test.failed?
|
47
|
+
xml.failure :message=>"", :type=>"" do
|
48
|
+
xml.cdata! test.stack_trace
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def has_errors(test)
|
54
|
+
if test.errored?
|
55
|
+
xml.error :message=>"", :type=>"" do
|
56
|
+
xml.cdata! test.error_message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_tests_time(tests)
|
62
|
+
tests.inject(0){|time_sum, test| time_sum += test.time.to_f }
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_errors(tests)
|
66
|
+
tests.select{|test|
|
67
|
+
test.errored?
|
68
|
+
}.count
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_failures(tests)
|
72
|
+
tests.select{|test|
|
73
|
+
test.failed?
|
74
|
+
}.count
|
75
|
+
end
|
76
|
+
|
77
|
+
def ensure_io(path_to_file)
|
78
|
+
File.open(path_to_file, 'w')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Rubotium
|
2
|
+
class Grouper
|
3
|
+
def initialize(test_suites, num_of_groups)
|
4
|
+
@test_suites = test_suites
|
5
|
+
@num_of_groups = num_of_groups
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_groups
|
9
|
+
runnable_tests.each{|runnable|
|
10
|
+
next_bucket.push(runnable)
|
11
|
+
}
|
12
|
+
buckets
|
13
|
+
end
|
14
|
+
|
15
|
+
def runnable_tests
|
16
|
+
test_suites.map {|test_suite|
|
17
|
+
test_suite.test_cases.map{|test|
|
18
|
+
RunableTest.new(test_suite.name, test.name)
|
19
|
+
}
|
20
|
+
}.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
def next_bucket
|
24
|
+
buckets_enum.next
|
25
|
+
end
|
26
|
+
|
27
|
+
def buckets_enum
|
28
|
+
@buckets_enum ||= buckets.cycle
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def buckets
|
33
|
+
@buckets ||= Array.new(num_of_groups) { [] }
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
attr_reader :test_suites, :num_of_groups
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'set'
|
2
|
+
module Rubotium
|
3
|
+
class JarReader
|
4
|
+
TEST_PATTERN = Regexp.new('public void (test.*)\(')
|
5
|
+
attr_reader :path_to_jar
|
6
|
+
|
7
|
+
def initialize(jar_path)
|
8
|
+
@path_to_jar = jar_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_suites
|
12
|
+
Converter.new(classes_in_jar).convert
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_tests
|
16
|
+
parse
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def classes_in_jar
|
21
|
+
CMD.run_command("jar -tf #{path_to_jar} | grep '.class'")
|
22
|
+
end
|
23
|
+
|
24
|
+
def tests_in_class(suite_name)
|
25
|
+
CMD.run_command "javap -classpath #{path_to_jar} #{suite_name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse
|
29
|
+
test_suites.map{|test_suite|
|
30
|
+
tests_in_class(test_suite.name).scan(TEST_PATTERN).flatten.each{|test|
|
31
|
+
test_suite.add_test_case(TestCase.new(test))
|
32
|
+
}
|
33
|
+
test_suite
|
34
|
+
}.delete_if{|test_suite| test_suite.test_cases.empty?}
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
class Converter
|
39
|
+
attr_reader :list
|
40
|
+
def initialize(list_of_classes)
|
41
|
+
@list = list_of_classes.split
|
42
|
+
end
|
43
|
+
|
44
|
+
def convert
|
45
|
+
deduplicated_test_suites
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def test_suites
|
50
|
+
converted_class_names.map{|class_name|
|
51
|
+
TestSuite.new(class_name)
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def converted_class_names
|
56
|
+
list.map{|class_name|
|
57
|
+
class_name.gsub("/", ".")
|
58
|
+
.gsub(".class", "")
|
59
|
+
.gsub(/\$.*/, "")
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def deduplicated_test_suites
|
64
|
+
test_suites.uniq{|test_suite|
|
65
|
+
test_suite.name
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|