rubotium 0.0.7 → 0.0.17

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.
@@ -13,6 +13,113 @@ module Rubotium
13
13
  json
14
14
  end
15
15
 
16
+ def group_by_failures
17
+ TestSuites.new(
18
+ group_by_package.map{|name, test_results|
19
+ TestSuite.new(name, test_results)
20
+ }.sort_by{|test_suite|
21
+ test_suite.state
22
+ })
23
+ end
24
+
16
25
  attr_reader :results
17
26
  end
27
+
28
+ class TestSuites
29
+ def initialize(test_suites)
30
+ @test_suites = test_suites
31
+ end
32
+
33
+ def time
34
+ test_suites.map(&:time).inject(&:+).to_s
35
+ end
36
+
37
+ def each
38
+ test_suites.each{|suite|
39
+ yield(suite)
40
+ }
41
+ end
42
+
43
+ def total
44
+ sum_of(:tests_count)
45
+ end
46
+
47
+ def passed
48
+ sum_of(:passed_count)
49
+
50
+ end
51
+
52
+ def errored
53
+ sum_of(:error_count)
54
+ end
55
+
56
+ def failed
57
+ sum_of(:failures_count)
58
+ end
59
+
60
+ private
61
+ attr_reader :test_suites
62
+
63
+ def sum_of(value)
64
+ select_suites_with(value).inject(&:+)
65
+ end
66
+
67
+ def select_suites_with(value)
68
+ test_suites.map(&value).compact
69
+ end
70
+ end
71
+
72
+ class TestSuite
73
+ attr_reader :name
74
+
75
+ def initialize(name, test_cases)
76
+ @name = name
77
+ @test_cases = test_cases
78
+ end
79
+
80
+ def failures_count
81
+ @failures ||= count_by(:failed?).to_i
82
+ end
83
+
84
+ def error_count
85
+ @errors ||= count_by(:errored?).to_i
86
+ end
87
+
88
+ def passed_count
89
+ @passed_count ||= count_by(:passed?).to_i
90
+ end
91
+
92
+ def state
93
+ return (failures_count + error_count > 0) ? 'failed' : 'passed'
94
+ end
95
+
96
+ def time
97
+ @time ||= sum_of(:time)
98
+ end
99
+
100
+ def tests_count
101
+ @tests_count ||= test_cases.count
102
+ end
103
+
104
+ def each_test
105
+ test_cases.each{|test_case|
106
+ yield(test_case)
107
+ }
108
+ end
109
+
110
+ private
111
+ attr_reader :test_cases
112
+
113
+ def count_by(value)
114
+ test_cases
115
+ .select(&value)
116
+ .count
117
+ end
118
+
119
+ def sum_of(value_to_sum)
120
+ test_cases
121
+ .map(&value_to_sum)
122
+ .inject(&:+)
123
+ end
124
+ end
18
125
  end
@@ -8,14 +8,24 @@ module Rubotium
8
8
  end
9
9
 
10
10
  def run_test(runnable_test)
11
- result = device.shell(instrument_command(runnable_test))
12
- Rubotium::TestResult.new(Rubotium::Adb::Parsers::TestResultsParser.new(result), runnable_test)
11
+ device.clean_logcat
12
+ result = execute(instrument_command(runnable_test))
13
+ File.open("results/logs/#{runnable_test.name}.log", 'w+') do |file|
14
+ file.write device.logcat
15
+ end
16
+ Rubotium::TestResult.new(result , runnable_test)
13
17
  end
14
18
 
15
19
  private
16
20
 
17
21
  attr_reader :device, :test_package
18
22
 
23
+ def execute(command)
24
+ Rubotium::Adb::Parsers::TestResultsParser.new(device.shell(command))
25
+ end
26
+
27
+
28
+
19
29
  def instrument_command (runnable_test)
20
30
  "am instrument -w -r -e class #{runnable_test.name} #{test_package.name}/#{test_package.test_runner}"
21
31
  end
@@ -7,6 +7,7 @@ module Rubotium
7
7
  @tests_package = tests_package
8
8
  @tests_runner = tests_package.test_runner
9
9
  @options = options
10
+ @clear = options[:clear]
10
11
  @test_number = 0
11
12
  end
12
13
 
@@ -15,7 +16,7 @@ module Rubotium
15
16
  end
16
17
 
17
18
  def tests_to_execute
18
- tests_queue.count
19
+ tests_queue.count
19
20
  end
20
21
 
21
22
  def tests_results
@@ -25,16 +26,19 @@ module Rubotium
25
26
  def run_tests
26
27
  fill_tests_queue
27
28
  Parallel.each(devices, :in_threads => devices.count) { |device|
28
- until tests_queue.empty?
29
- test = next_test
29
+ while(test = next_test)
30
30
  memory_monitor = Rubotium::Memory::Monitor.new(device, { :interval => 1 })
31
31
  memory_monitor.start
32
32
  display_test_progress
33
33
  result = test_runner(device).run_test(test)
34
34
  if(result.failed? || result.errored?)
35
+ screencast = Rubotium::Recorder.new(device.serial)
36
+ screencast.start(test.name)
35
37
  result = test_runner(device).run_test(test)
38
+ screencast.stop
36
39
  end
37
40
  results.push(result)
41
+ clear_if_required(device)
38
42
  memory_monitor.stop_and_save(memory_results_file(test))
39
43
  end
40
44
  }
@@ -47,7 +51,7 @@ module Rubotium
47
51
 
48
52
  private
49
53
 
50
- attr_reader :devices, :tests_package, :tests
54
+ attr_reader :devices, :tests_package, :tests, :clear
51
55
 
52
56
  def memory_results_file(test)
53
57
  File.open("results/memory_logs/#{test.name}.json", 'w+')
@@ -58,7 +62,7 @@ module Rubotium
58
62
  end
59
63
 
60
64
  def next_test
61
- tests_queue.pop
65
+ tests_queue.pop(true) rescue nil
62
66
  end
63
67
 
64
68
  def fill_tests_queue
@@ -69,6 +73,10 @@ module Rubotium
69
73
  @queue ||= Queue.new
70
74
  end
71
75
 
76
+ def clear_if_required(device)
77
+ device.shell("pm clear #{clear}") if clear
78
+ end
79
+
72
80
  def test_runner(device)
73
81
  Rubotium::TestRunners::InstrumentationTestRunner.new(device, tests_package)
74
82
  end
@@ -1,3 +1,3 @@
1
1
  module Rubotium
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.17"
3
3
  end
data/lib/rubotium.rb CHANGED
@@ -6,6 +6,8 @@ require 'rubotium/device'
6
6
  require 'rubotium/devices'
7
7
  require 'rubotium/tests_runner'
8
8
  require 'rubotium/formatters/junit_formatter'
9
+ require 'rubotium/screencast'
10
+ require 'rubotium/formatters/html_formatter'
9
11
  require 'rubotium/runnable_test'
10
12
  require 'rubotium/package'
11
13
  require 'rubotium/memory'
@@ -38,32 +40,43 @@ module Rubotium
38
40
  raise RuntimeError, "Empty configuration" if opts.empty?
39
41
  raise Errno::ENOENT, "Tests apk does not exist" if !File.exist?(opts[:tests_apk_path])
40
42
  raise Errno::ENOENT, "App apk does not exist" if !File.exist?(opts[:app_apk_path])
43
+ if !opts[:helper_apk_path].nil?
44
+ raise Errno::ENOENT, "Helper apk does not exist" if !File.exist?(opts[:helper_apk_path])
45
+ end
41
46
 
42
47
  logger.level = Logger::INFO
43
48
 
44
49
  startTime = Time.now
45
- FileUtils.mkdir_p('results')
50
+ FileUtils.mkdir_p('results/logs')
46
51
  FileUtils.mkdir_p('results/memory_logs')
52
+ FileUtils.mkdir_p('results/screencasts')
47
53
  FileUtils.mkdir_p('screens')
48
54
  FileUtils.mkdir_p('logs')
49
55
 
50
56
  application_package = Rubotium::Package.new(opts[:app_apk_path])
51
57
  tests_package = Rubotium::Package.new(opts[:tests_apk_path], opts[:runner])
58
+ helper_package = Rubotium::Package.new(opts[:helper_apk_path])
52
59
 
53
- devices = Devices.new(:name => opts[:device_matcher]).all
60
+ devices = Devices.new(:name => opts[:device_matcher], :sdk => opts[:device_sdk], :serial=> opts[:serial]).all
54
61
 
55
62
  devices = Parallel.map(devices, :in_threads => devices.count) {|device|
56
63
  device.uninstall application_package.name
57
64
  device.install application_package.path
58
65
  device.uninstall tests_package.name
59
66
  device.install tests_package.path
67
+ if !opts[:helper_apk_path].nil?
68
+ device.uninstall helper_package.name
69
+ device.install helper_package.path
70
+ end
71
+ device.shell('mkdir /sdcard/screencasts')
60
72
  device
61
73
  }
62
74
 
63
- test_suites = Rubotium::TestCasesReader.new(devices.first, tests_package).read_tests
75
+ test_suites = Rubotium::TestCasesReader.new(devices.first, tests_package, { :annotation=>opts[:annotation]}).read_tests
76
+
64
77
  puts "There are #{test_suites.count} tests to run"
65
78
 
66
- runner = Rubotium::TestsRunner.new(devices, test_suites, tests_package, {:annotation=>opts[:annotation]})
79
+ runner = Rubotium::TestsRunner.new(devices, test_suites, tests_package, {annotation: opts[:annotation], clear: application_package.name})
67
80
  runner.run_tests
68
81
 
69
82
  FileUtils.mkdir_p(['screens', 'logs'])
@@ -71,16 +84,20 @@ module Rubotium
71
84
  devices.each{|device|
72
85
  device.pull('/sdcard/Robotium-Screenshots')
73
86
  device.pull('/sdcard/RobotiumLogs')
74
- device.shell('rm -R /sdcard/Robotium-Screenshots ')
75
- device.shell('rm -R /sdcard/RobotiumLogs ')
87
+ device.pull('/sdcard/screencasts')
88
+ device.shell('rm -R /sdcard/Robotium-Screenshots')
89
+ device.shell('rm -R /sdcard/RobotiumLogs')
90
+ device.shell('rm -R /sdcard/screencasts')
91
+ device.shell('reboot')
76
92
  }
77
93
  FileUtils.mv(Dir.glob('*.jpg'), 'screens')
78
94
  FileUtils.mv(Dir.glob('*.log'), 'logs')
95
+ FileUtils.mv(Dir.glob('*.mp4'), 'results/screencasts')
79
96
 
80
97
  puts "Tests took: #{Time.at(Time.now-startTime).utc.strftime("%H:%M:%S")}"
81
98
 
82
99
  Formatters::JunitFormatter.new(runner.tests_results.group_by_package, opts[:report])
83
-
100
+ Formatters::HtmlFormatter.new(runner.tests_results.group_by_failures, 'results/index.html')
84
101
  end
85
102
 
86
103
  def logger
data/rubotium.gemspec CHANGED
@@ -21,12 +21,11 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.5"
22
22
  spec.add_development_dependency "simplecov", "0.8.2"
23
23
  spec.add_development_dependency "coveralls", "0.7.0"
24
- spec.add_development_dependency "rspec", "2.14.1"
25
- spec.add_development_dependency "rspec-mocks"
24
+ spec.add_development_dependency "rspec", "3.2.0"
25
+
26
26
  spec.add_development_dependency "rake"
27
27
 
28
28
  spec.add_dependency 'builder', '3.2.2'
29
29
  spec.add_dependency 'trollop', '2.0'
30
30
  spec.add_dependency 'parallel', '0.9.2'
31
- spec.add_dependency 'dex2jar', '0.0.6'
32
31
  end
@@ -135,13 +135,13 @@ describe Rubotium::Adb::TestResultParser do
135
135
  let(:parsed_result) { parser.new(Fixtures::Adb.gingerbread_exception, package, "") }
136
136
 
137
137
  it 'should behave as error case' do
138
- pending
139
- parsed_result.should be_errored
138
+
139
+
140
140
  end
141
141
 
142
142
  it 'should know the error reason' do
143
- pending
144
- parsed_result.error_message.should eql('')
143
+
144
+
145
145
  end
146
146
  end
147
147
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rubotium::Adb::Commands::Command do
4
+ let(:subject) { described_class.new(serial) }
5
+ let(:adb_command) { 'the command' }
6
+ let(:result) { 'result' }
7
+ let(:command_to_run) { double(Object) }
8
+
9
+ before do
10
+ expect(Rubotium::CMD).to receive(:run_command).with(shell_command) { result }
11
+ expect(command_to_run).to receive(:executable_command).at_least(:once) { adb_command }
12
+ end
13
+
14
+ context 'when nil serial is defined' do
15
+ let(:serial) { nil }
16
+ let(:shell_command) { "adb #{adb_command}" }
17
+
18
+ it 'executes the command without a serial' do
19
+ expect(subject.execute(command_to_run)).to eq result
20
+ end
21
+ end
22
+
23
+ context 'when "" serial is defined' do
24
+ let(:serial) { '' }
25
+ let(:shell_command) { "adb #{adb_command}" }
26
+
27
+ it 'executes the command without a serial' do
28
+ expect(subject.execute(command_to_run)).to eq result
29
+ end
30
+ end
31
+
32
+ context 'when a real serial is defined' do
33
+ let(:serial) { '01234567' }
34
+ let(:shell_command) { "adb -s #{serial} #{adb_command}" }
35
+
36
+ it 'executes the command with a serial' do
37
+ expect(subject.execute(command_to_run)).to eq result
38
+ end
39
+ end
40
+ end
@@ -13,7 +13,7 @@ describe Rubotium::Adb::Parsers::TestResultsParser do
13
13
  end
14
14
 
15
15
  it 'should return time' do
16
- expect(parser.time).to eql('0.142')
16
+ expect(parser.time).to eql(0.142)
17
17
  end
18
18
 
19
19
  it 'should not have error message' do
@@ -33,7 +33,7 @@ describe Rubotium::Adb::Parsers::TestResultsParser do
33
33
  end
34
34
 
35
35
  it 'should return time' do
36
- expect(parser.time).to eql('16.79')
36
+ expect(parser.time).to eql(16.79)
37
37
  end
38
38
 
39
39
  it 'should not have error message' do
@@ -53,7 +53,7 @@ describe Rubotium::Adb::Parsers::TestResultsParser do
53
53
  end
54
54
 
55
55
  it 'should return time = 0' do
56
- expect(parser.time).to eql('0')
56
+ expect(parser.time).to eql(0.0)
57
57
  end
58
58
 
59
59
  it 'should return error message' do
@@ -75,7 +75,7 @@ describe Rubotium::Adb::Parsers::TestResultsParser do
75
75
  end
76
76
 
77
77
  it 'should return time = 0' do
78
- expect(parser.time).to eql('0')
78
+ expect(parser.time).to eql(0.0)
79
79
  end
80
80
 
81
81
  it 'should return error message' do
@@ -97,7 +97,7 @@ describe Rubotium::Adb::Parsers::TestResultsParser do
97
97
  end
98
98
 
99
99
  it 'should return time = 0' do
100
- expect(parser.time).to eql('0')
100
+ expect(parser.time).to eql(0.0)
101
101
  end
102
102
 
103
103
  it 'should return error message' do
@@ -4,14 +4,16 @@ require 'spec_helper'
4
4
  require "pp"
5
5
 
6
6
  describe Rubotium::Apk::AndroidApk do
7
+ let(:aapt) {double(Rubotium::Apk::Aapt)}
7
8
 
8
9
  sample_file_path = File.dirname(__FILE__) + "/mock/sample.apk"
9
10
  sample2_file_path = File.dirname(__FILE__) + "/mock/BarcodeScanner4.2.apk"
10
11
  icon_not_set_file_path = File.dirname(__FILE__) + "/mock/UECExpress.apk"
11
12
  dummy_file_path = File.dirname(__FILE__) + "/mock/dummy.apk"
12
13
 
14
+
13
15
  context 'with non existing apk' do
14
- let(:apk) { described_class.new('dummy.apk') }
16
+ let(:apk) { described_class.new(aapt, 'dummy.apk') }
15
17
 
16
18
  it "should raise exception" do
17
19
  expect{apk}.to raise_exception(Errno::ENOENT)
@@ -19,17 +21,36 @@ describe Rubotium::Apk::AndroidApk do
19
21
  end
20
22
 
21
23
  context 'with not readable apk' do
22
- let(:apk) { described_class.new(dummy_file_path) }
24
+ let(:apk) { described_class.new(aapt, dummy_file_path) }
23
25
 
24
26
  it 'should raise exception' do
25
- expect{apk.package_name}.to raise_error(RuntimeError, /ERROR: dump failed because no AndroidManifest.xml found/)
27
+ expect(aapt).to receive(:dump).and_return "W/zipro (77199): Error opening archive spec/lib/rubotium/apk/mock/dummy.apk: Invalid file
28
+ ERROR: dump failed because assets could not be loaded"
29
+ expect{apk.package_name}.to raise_error(RuntimeError)
26
30
  end
27
31
  end
28
32
 
29
33
  context 'with valid apk file' do
30
- let(:apk) { described_class.new(sample_file_path) }
34
+ let(:apk) { described_class.new(aapt, sample_file_path) }
31
35
 
32
36
  it 'should read package_name' do
37
+ expect(aapt).to receive(:dump).and_return "package: name='com.example.sample' versionCode='1' versionName='1.0' platformBuildVersionName=''
38
+ sdkVersion:'7'
39
+ targetSdkVersion:'15'
40
+ application-label:'sample'
41
+ application-label-ja:'g'
42
+ application-icon-160:'res/drawable-mdpi/ic_launcher.png'
43
+ application-icon-240:'res/drawable-hdpi/ic_launcher.png'
44
+ application-icon-320:'res/drawable-xhdpi/ic_launcher.png'
45
+ application: label='sample' icon='res/drawable-mdpi/ic_launcher.png'
46
+ application-debuggable
47
+ feature-group: label=''
48
+ uses-feature: name='android.hardware.touchscreen'
49
+ uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
50
+ supports-screens: 'small' 'normal' 'large' 'xlarge'
51
+ supports-any-density: 'true'
52
+ locales: '--_--' 'ja'
53
+ densities: '160' '240' '320'"
33
54
  expect(apk.package_name).to eql 'com.example.sample'
34
55
  end
35
56
 
@@ -1,12 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Rubotium::Device do
4
- let(:device) { described_class.new('12345') }
5
- let(:command) { double(Rubotium::Adb::Commands::Command)}
6
- before do
7
- device.stub(:adb_command).and_return(command)
8
- end
9
-
3
+ shared_examples 'a Rubotium::Device' do
10
4
  it 'installs package on device' do
11
5
  path_to_apk = 'path/to/apk'
12
6
  expect(command).to receive(:install).with(path_to_apk)
@@ -36,3 +30,24 @@ describe Rubotium::Device do
36
30
  device.shell(getprop)
37
31
  end
38
32
  end
33
+
34
+ describe Rubotium::Device do
35
+ let(:device) { described_class.new(serial) }
36
+ let(:command) { double(Rubotium::Adb::Commands::Command)}
37
+
38
+ before do
39
+ device.stub(:adb_command).and_return(command)
40
+ end
41
+
42
+ context 'when a serial is provided' do
43
+ let(:serial) { '12345' }
44
+
45
+ it_behaves_like 'a Rubotium::Device'
46
+ end
47
+
48
+ context 'when no serial is provided' do
49
+ let(:serial) { nil }
50
+
51
+ it_behaves_like 'a Rubotium::Device'
52
+ end
53
+ end
@@ -21,6 +21,7 @@ describe Rubotium::TestsRunner do
21
21
 
22
22
  context 'when running tests' do
23
23
  before do
24
+ runner.stub(:memory_results_file).and_return Tempfile.new('memory_monitor')
24
25
  device1.stub(:shell)
25
26
  device2.stub(:shell)
26
27
  runner.stub(:test_runner).and_return(test_runner)
data/test.rb CHANGED
@@ -1,11 +1,42 @@
1
- require 'parallel'
1
+ require "observer"
2
2
 
3
- q = Queue.new
3
+ class Ticker ### Periodically fetch a stock price.
4
+ include Observable
4
5
 
5
- (0..10).to_a.each{|e| q.push(e)}
6
- b = (1..4).to_a
7
- Parallel.map(b, :in_processes => 10){|executor|
8
- until(q.empty?)
9
- puts q.pop
6
+ def test_changed(msg)
7
+ changed
10
8
  end
11
- }
9
+
10
+ def test_notify(msg)
11
+ notify_observers(msg)
12
+ end
13
+ end
14
+
15
+ class Observer
16
+ def initialize(ticker, name)
17
+ @ticker = ticker
18
+ @name = name
19
+ ticker.add_observer(self)
20
+ end
21
+
22
+ def unsubscribe
23
+ @ticker.delete_observer(self)
24
+ end
25
+
26
+ def update(*args)
27
+ puts @name
28
+ p *args
29
+ end
30
+
31
+ def message_missing(*args)
32
+ puts @name
33
+ p args
34
+ end
35
+
36
+ end
37
+
38
+
39
+ ticker = Ticker.new()
40
+ observer = Observer.new(ticker, "observer1")
41
+
42
+