rubotium 0.0.7 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+