brazenhead 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/driver/AndroidManifest.xml +21 -0
- data/driver/brazenhead-release-unsigned.apk +0 -0
- data/features/brazenhead.feature +35 -0
- data/features/exception.feature +6 -0
- data/features/robotium.feature +31 -0
- data/features/step_definitions/brazenhead_steps.rb +52 -0
- data/features/step_definitions/exception_steps.rb +13 -0
- data/features/step_definitions/robotium_steps.rb +62 -0
- data/features/support/ApiDemos.apk +0 -0
- data/features/support/debug.keystore +0 -0
- data/features/support/env.rb +30 -0
- data/lib/brazenhead/android.rb +37 -0
- data/lib/brazenhead/builder.rb +92 -0
- data/lib/brazenhead/call_accumulator.rb +28 -0
- data/lib/brazenhead/core_ext/string.rb +7 -0
- data/lib/brazenhead/device.rb +35 -0
- data/lib/brazenhead/manifest_info.rb +51 -0
- data/lib/brazenhead/package.rb +36 -0
- data/lib/brazenhead/process.rb +35 -0
- data/lib/brazenhead/request.rb +40 -0
- data/lib/brazenhead/server.rb +54 -0
- data/lib/brazenhead/signer.rb +66 -0
- data/lib/brazenhead/version.rb +3 -0
- data/lib/brazenhead.rb +42 -0
- data/spec/lib/brazenhead/android_spec.rb +35 -0
- data/spec/lib/brazenhead/builder_spec.rb +108 -0
- data/spec/lib/brazenhead/call_accumulator_spec.rb +11 -0
- data/spec/lib/brazenhead/device_spec.rb +27 -0
- data/spec/lib/brazenhead/manifest_info_spec.rb +61 -0
- data/spec/lib/brazenhead/package_spec.rb +30 -0
- data/spec/lib/brazenhead/process_spec.rb +64 -0
- data/spec/lib/brazenhead/request_spec.rb +51 -0
- data/spec/lib/brazenhead/server_spec.rb +75 -0
- data/spec/lib/brazenhead/signer_spec.rb +35 -0
- data/spec/lib/brazenhead_spec.rb +34 -0
- data/spec/spec_helper.rb +18 -0
- metadata +176 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'brazenhead/android'
|
2
|
+
require 'brazenhead/process'
|
3
|
+
|
4
|
+
module Brazenhead
|
5
|
+
module Signer
|
6
|
+
include Brazenhead::Android
|
7
|
+
|
8
|
+
def sign(apk, keystore)
|
9
|
+
@keystore = keystore
|
10
|
+
jarsign(apk)
|
11
|
+
verify(apk)
|
12
|
+
process.run(*zipalign(apk), *signed(apk))
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def process
|
17
|
+
@process ||= Brazenhead::Process.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def jarsign(apk)
|
21
|
+
process.run(*jar_command, *keypasses, *the_key, apk, key_alias)
|
22
|
+
end
|
23
|
+
|
24
|
+
def jar_command
|
25
|
+
"jarsigner -sigalg MD5withRSA -digestalg SHA1".split
|
26
|
+
end
|
27
|
+
|
28
|
+
def verify(apk)
|
29
|
+
process.run(*"jarsigner -verify".split, apk)
|
30
|
+
error_signing(apk) unless process.last_stdout.include? "jar verified"
|
31
|
+
end
|
32
|
+
|
33
|
+
def zipalign(apk)
|
34
|
+
"zipalign -v 4".split << apk
|
35
|
+
end
|
36
|
+
|
37
|
+
def signed(apk)
|
38
|
+
File.join dir(apk), new_name(apk)
|
39
|
+
end
|
40
|
+
|
41
|
+
def dir(apk)
|
42
|
+
File.dirname(apk)
|
43
|
+
end
|
44
|
+
|
45
|
+
def new_name(apk)
|
46
|
+
File.basename(apk, '.apk') << '-signed.apk'
|
47
|
+
end
|
48
|
+
|
49
|
+
def keypasses
|
50
|
+
"-storepass #{@keystore[:password]} -keypass #{@keystore[:keystore_password]}".split
|
51
|
+
end
|
52
|
+
|
53
|
+
def the_key
|
54
|
+
["-keystore", File.expand_path(@keystore[:path])]
|
55
|
+
end
|
56
|
+
|
57
|
+
def key_alias
|
58
|
+
@keystore[:alias]
|
59
|
+
end
|
60
|
+
|
61
|
+
def error_signing(apk)
|
62
|
+
raise Exception, "error signing #{apk} (#{process.last_stdout})"
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
data/lib/brazenhead.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'brazenhead/version'
|
2
|
+
require 'brazenhead/device'
|
3
|
+
require 'brazenhead/request'
|
4
|
+
require 'brazenhead/call_accumulator'
|
5
|
+
require 'brazenhead/core_ext/string'
|
6
|
+
|
7
|
+
module Brazenhead
|
8
|
+
def method_missing(method, *args)
|
9
|
+
call_method_on_driver(method.to_s.to_java_call, args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def chain_calls(&block)
|
13
|
+
accumulator.clear
|
14
|
+
block.call accumulator
|
15
|
+
@last_response = device.send(accumulator.message)
|
16
|
+
@last_response
|
17
|
+
end
|
18
|
+
|
19
|
+
def last_response
|
20
|
+
@last_response
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def call_method_on_driver(method, args)
|
26
|
+
message = request.build(method, args)
|
27
|
+
@last_response = device.send(message)
|
28
|
+
@last_response
|
29
|
+
end
|
30
|
+
|
31
|
+
def device
|
32
|
+
@device ||= Brazenhead::Device.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def request
|
36
|
+
@request ||= Brazenhead::Request.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def accumulator
|
40
|
+
@accumulator ||= Brazenhead::CallAccumulator.new
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'brazenhead/android'
|
2
|
+
|
3
|
+
class AndroidTest
|
4
|
+
include Brazenhead::Android
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Brazenhead::Android do
|
8
|
+
let(:android) { AndroidTest.new }
|
9
|
+
|
10
|
+
context "resolving android paths" do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
ENV.stub(:[]).with('ANDROID_HOME').and_return('/path/to/android')
|
14
|
+
File.stub(:exists?).and_return(true)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be able to locate the android jar" do
|
18
|
+
ENV.stub(:[]).with('ANDROID_HOME').and_return('/path/to/android')
|
19
|
+
android.path_to(8).should eq '/path/to/android/platforms/android-8/android.jar'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise an error if the path is not found" do
|
23
|
+
File.should_receive(:exists?).and_return(false)
|
24
|
+
api = 8
|
25
|
+
lambda { android.path_to(api) }.should raise_error (message="the path to android-#{api} was not found")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "locating the keystore" do
|
30
|
+
it "should be able to locate the path to the default keystore" do
|
31
|
+
File.stub(:expand_path).with("~/.android/debug.keystore").and_return("/some/expanded/.android/debug.keystore")
|
32
|
+
android.default_keystore[:path].should eq "/some/expanded/.android/debug.keystore"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'brazenhead/builder'
|
2
|
+
|
3
|
+
describe Brazenhead::Builder do
|
4
|
+
let(:apk) { 'some_apk.apk' }
|
5
|
+
let(:activity) { 'SomeActivityToStart' }
|
6
|
+
let(:server) { Brazenhead::Builder.new }
|
7
|
+
let(:manifest_info) { double('manifest-info').as_null_object }
|
8
|
+
let(:tmpdir) { '/some/tmp/dir' }
|
9
|
+
let(:driver_apk) { 'brazenhead-release-unsigned.apk' }
|
10
|
+
let(:keystore) { {:path => 'default.keystore'} }
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
File.stub(:exists?).and_return(true)
|
14
|
+
Dir.stub(:mktmpdir).and_yield(tmpdir)
|
15
|
+
FileUtils.stub(:copy_file)
|
16
|
+
File.stub(:read).and_return('')
|
17
|
+
File.stub(:write)
|
18
|
+
server.stub(:update_manifest)
|
19
|
+
server.stub(:sign)
|
20
|
+
server.stub(:install)
|
21
|
+
Brazenhead::ManifestInfo.stub(:new).with(apk).and_return(manifest_info)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "building the test server" do
|
25
|
+
context "validating the arguments" do
|
26
|
+
it "should require that the package exists" do
|
27
|
+
File.should_receive(:exists?).and_return(false)
|
28
|
+
lambda { server.build_for('some_package.apk', keystore) }.should raise_error(message="Invalid package path: some_package.apk")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "setting up the test server sandbox" do
|
33
|
+
let(:base_gem_dir) { '/base/gem' }
|
34
|
+
let(:base_test_apk) { "#{base_gem_dir}/driver/#{driver_apk}" }
|
35
|
+
let(:manifest) { 'AndroidManifest.xml' }
|
36
|
+
let(:base_manifest) { "#{base_gem_dir}/driver/#{manifest}" }
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
File.stub(:expand_path).with("~/.android/debug.keystore").and_return("expanded/.android/debug.keystore")
|
40
|
+
File.stub(:expand_path).with("../../../", anything()).and_return(base_gem_dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should use a temporary directory" do
|
44
|
+
Dir.should_receive(:mktmpdir)
|
45
|
+
server.build_for(apk, keystore)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should copy the unsigned release package into the directory" do
|
49
|
+
FileUtils.should_receive(:copy_file).with(base_test_apk, File.join(tmpdir, driver_apk))
|
50
|
+
server.build_for(apk, keystore)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should copy the manifest into the directory" do
|
54
|
+
FileUtils.should_receive(:copy_file).with(base_manifest, File.join(tmpdir, manifest))
|
55
|
+
server.build_for(apk, keystore)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "updating the manifest" do
|
60
|
+
it "should load the contents of the existing manifest" do
|
61
|
+
File.should_receive(:read).with("#{tmpdir}/AndroidManifest.xml")
|
62
|
+
server.build_for(apk, keystore)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should replace the target package" do
|
66
|
+
the_target_package = "the.target.package"
|
67
|
+
File.should_receive(:read).and_return("android:targetPackage=\"it.does.not.matter\"")
|
68
|
+
manifest_info.should_receive(:package).and_return(the_target_package)
|
69
|
+
File.should_receive(:write).with("#{tmpdir}/AndroidManifest.xml", "android:targetPackage=\"#{the_target_package}\"")
|
70
|
+
|
71
|
+
server.build_for(apk, keystore)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should package the modified manifest back into the test package" do
|
75
|
+
manifest_info.should_receive(:target_sdk).and_return(10)
|
76
|
+
server.should_receive(:update_manifest).with("#{tmpdir}/#{driver_apk}", "#{tmpdir}/AndroidManifest.xml", 10)
|
77
|
+
server.build_for(apk, keystore)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
context "signing the test server" do
|
83
|
+
it "should use the provided keystore to sign the package" do
|
84
|
+
server.should_receive(:sign).with("#{tmpdir}/#{driver_apk}", {:path => 'another keystore'})
|
85
|
+
server.build_for(apk, :path => 'another keystore')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "installing the test server" do
|
90
|
+
it "should reinstall the test server to the device" do
|
91
|
+
server.should_receive(:install).with("#{tmpdir}/#{driver_apk}", "-r", {}, 90)
|
92
|
+
server.build_for(apk, keystore)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should reinstall the target package to the device" do
|
96
|
+
server.should_receive(:install).with(apk, "-r", {}, 90)
|
97
|
+
server.build_for(apk, keystore)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "sending back informationa about the server" do
|
104
|
+
it "should send back information about the target package" do
|
105
|
+
server.build_for(apk, keystore).should_be manifest_info
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brazenhead::CallAccumulator do
|
4
|
+
let(:accumulator) { Brazenhead::CallAccumulator.new }
|
5
|
+
|
6
|
+
it "should accumulate a series of calls and build the message" do
|
7
|
+
accumulator.first_call
|
8
|
+
accumulator.second_call
|
9
|
+
accumulator.message.should == "commands=[{\"name\":\"firstCall\"},{\"name\":\"secondCall\"}]"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brazenhead::Device do
|
4
|
+
let(:device) { Brazenhead::Device.new }
|
5
|
+
let(:http_mock) { double("http_mock") }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Net::HTTP.stub(:new).and_return(http_mock)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should retry the http call if it fails the first time" do
|
12
|
+
http_mock.should_receive(:post).and_raise("error")
|
13
|
+
http_mock.should_receive(:post).with('/', "blah")
|
14
|
+
device.send "blah"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should retry the http call a maximum of 20 times" do
|
18
|
+
device.should_receive(:sleep).exactly(20).times
|
19
|
+
http_mock.should_receive(:post).exactly(20).times.and_raise("error")
|
20
|
+
expect { device.send "blah" }.to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be able to stop the server" do
|
24
|
+
http_mock.should_receive(:post).with('/kill', '')
|
25
|
+
device.stop
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brazenhead::ManifestInfo do
|
4
|
+
let(:process) { double('brazenhead-process').as_null_object }
|
5
|
+
let(:manifest_info) { Brazenhead::ManifestInfo.new 'some_apk.apk' }
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Brazenhead::Process.stub(:new).and_return(process)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should grab the minimum sdk" do
|
12
|
+
process.should_receive(:last_stdout).and_return("
|
13
|
+
E: uses-sdk (line=39)
|
14
|
+
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x0f
|
15
|
+
A: android:targetSdkVersion(0x01010270)=(type 0x10)0xe")
|
16
|
+
|
17
|
+
manifest_info.min_sdk.should eq 15
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should load the manifest the first time it needs it" do
|
21
|
+
process.should_receive(:run).with('aapt', 'dump', 'xmltree', 'some_apk.apk', 'AndroidManifest.xml')
|
22
|
+
manifest_info.min_sdk
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should default the minimum sdk to 1" do
|
26
|
+
process.should_receive(:last_stdout).and_return("
|
27
|
+
E: uses-sdk (line=39)
|
28
|
+
A: android:notTheminSdkVersion(0x0101020c)=(type 0x10)0x0f
|
29
|
+
A: android:targetSdkVersion(0x01010270)=(type 0x10)0xe")
|
30
|
+
|
31
|
+
manifest_info.min_sdk.should eq 1
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should grab the maximum sdk" do
|
35
|
+
process.should_receive(:last_stdout).and_return("
|
36
|
+
E: uses-sdk (line=39)
|
37
|
+
A: android:minSdkVersion(0x0101020c)=(type 0x10)0x0f
|
38
|
+
A: android:maxSdkVersion(0x0101020c)=(type 0x10)0x0a")
|
39
|
+
|
40
|
+
manifest_info.max_sdk.should eq 10
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should grab the target sdk" do
|
44
|
+
process.should_receive(:last_stdout).and_return("
|
45
|
+
E: uses-sdk (line=39)
|
46
|
+
A: android:notTheminSdkVersion(0x0101020c)=(type 0x10)0x0f
|
47
|
+
A: android:targetSdkVersion(0x01010270)=(type 0x10)0xe")
|
48
|
+
|
49
|
+
manifest_info.target_sdk.should eq 14
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should grab the default package" do
|
53
|
+
process.should_receive(:last_stdout).and_return("
|
54
|
+
N: android=http://schemas.android.com/apk/res/android
|
55
|
+
E: manifest (line=22)
|
56
|
+
A: package=\"com.example.android.apis\" (Raw: \"com.example.android.apis\")")
|
57
|
+
|
58
|
+
manifest_info.package.should eq 'com.example.android.apis'
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'brazenhead/package'
|
2
|
+
|
3
|
+
class PackageTest
|
4
|
+
include Brazenhead::Package
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Brazenhead::Package do
|
8
|
+
let(:package) { PackageTest.new }
|
9
|
+
let(:process) { double('brazenhead-process').as_null_object }
|
10
|
+
let(:android) { double('brazenhead-android') }
|
11
|
+
let(:apk) { '/path/to/some_apk.apk' }
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
Brazenhead::Process.stub(:new).and_return(process)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "updating a manifest" do
|
18
|
+
let(:manifest) { '/path/to/AndroidManifest.xml' }
|
19
|
+
let(:android_jar) { '/path/to/android.jar' }
|
20
|
+
|
21
|
+
it "should update the manifest" do
|
22
|
+
package.stub(:path_to).and_return(android_jar)
|
23
|
+
process.should_receive(:run).with('aapt', 'p', '-u', '-f', '-F', apk, '-M', manifest, '-I', android_jar)
|
24
|
+
|
25
|
+
package.update_manifest(apk, manifest)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brazenhead::Process do
|
4
|
+
let(:stdout) { double(:name => 'stdout').as_null_object }
|
5
|
+
let(:stderr) { double(:name => 'stderr').as_null_object }
|
6
|
+
let(:running_process) { double('process').as_null_object }
|
7
|
+
let(:process) { Brazenhead::Process.new }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
Time.stub(:now) { 'this:time' }
|
11
|
+
Tempfile.stub(:new).and_return(stdout,stderr)
|
12
|
+
|
13
|
+
running_process.stub_chain(:io, :stdout).and_return(stdout)
|
14
|
+
running_process.stub_chain(:io, :stdout=)
|
15
|
+
running_process.stub_chain(:io, :stderr).and_return(stderr)
|
16
|
+
running_process.stub_chain(:io, :stderr=)
|
17
|
+
|
18
|
+
ChildProcess.stub(:build) { running_process }
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should run the command with arguments" do
|
22
|
+
ChildProcess.should_receive(:build).with('command', 'some', 'argument')
|
23
|
+
running_process.should_receive(:start)
|
24
|
+
process.run('command', 'some', 'argument')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should wait for the command to complete" do
|
28
|
+
running_process.should_receive(:wait)
|
29
|
+
process.run('anything')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should redirect stdout and stderr" do
|
33
|
+
Tempfile.should_receive(:new).with('brazenhead-proc-out-this_time')
|
34
|
+
Tempfile.should_receive(:new).with('brazenhead-proc-err-this_time')
|
35
|
+
process.run('anything')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should capture stdout and stderr" do
|
39
|
+
running_process.io.should_receive(:stdout=).with(stdout)
|
40
|
+
running_process.io.should_receive(:stderr=).with(stderr)
|
41
|
+
process.run('anything')
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should preserve the last stdout and stderr" do
|
45
|
+
[stdout, stderr].each do |iostream|
|
46
|
+
iostream.should_receive(:rewind)
|
47
|
+
iostream.should_receive(:read).and_return("last #{iostream.name}")
|
48
|
+
end
|
49
|
+
|
50
|
+
process.run('anything')
|
51
|
+
|
52
|
+
process.last_stdout.should eq 'last stdout'
|
53
|
+
process.last_stderr.should eq 'last stderr'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should clean up stdout and stderr temporary files" do
|
57
|
+
[stdout, stderr].each do |iostream|
|
58
|
+
iostream.should_receive(:close)
|
59
|
+
iostream.should_receive(:unlink)
|
60
|
+
end
|
61
|
+
process.run('anything')
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
describe Brazenhead::Request do
|
5
|
+
let(:request) { Brazenhead::Request.new }
|
6
|
+
|
7
|
+
def json_message(message)
|
8
|
+
JSON.parse(message[9..-1])[0]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should place the method name in the json file" do
|
12
|
+
message = request.build('call_me', [])
|
13
|
+
json_message(message)['name'].should == 'call_me'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should place an argument in the json file" do
|
17
|
+
message = request.build('call_me', ['the_argument'])
|
18
|
+
json_message(message)['arguments'].should == ['the_argument']
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should place multiple arguments in the json file" do
|
22
|
+
message = request.build('call_me', ['first', 'second'])
|
23
|
+
json_message(message)['arguments'].should == ['first', 'second']
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should use the target when one is provided" do
|
27
|
+
message = request.build('call_me', [{:target => 'Robotium'}])
|
28
|
+
json_message(message)['target'].should == 'Robotium'
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should grab the target from the final parameter" do
|
32
|
+
message = request.build('call_me', ['the_argument', {:target => 'Robotium'}])
|
33
|
+
json_message(message)['target'].should == 'Robotium'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use a variable when one is provided" do
|
37
|
+
message = request.build('call_me', [{:variable => '@@var@@'}])
|
38
|
+
json_message(message)['variable'].should == "@@var@@"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should grab the variable from the final parameter" do
|
42
|
+
message = request.build('call_me', ['the_argument', {:variable => '@@var@@'}])
|
43
|
+
json_message(message)['variable'].should == '@@var@@'
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should find both the target and variable in the final hash parameter" do
|
47
|
+
message = request.build('call_me', ['the_argument', {:target => 'Robotium', :variable => '@@var@@'}])
|
48
|
+
json_message(message)['variable'].should == '@@var@@'
|
49
|
+
json_message(message)['target'].should == 'Robotium'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brazenhead::Server do
|
4
|
+
let(:server) { Brazenhead::Server.new(apk) }
|
5
|
+
let(:builder) { double('brazenhead-builder').as_null_object }
|
6
|
+
let(:manifest) { double('manifest-info') }
|
7
|
+
let(:apk) { 'someapk.apk' }
|
8
|
+
let(:activity) { 'SomeActivity' }
|
9
|
+
let(:default_keystore) { server.default_keystore }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
Brazenhead::Builder.stub(:new).and_return(builder)
|
13
|
+
builder.stub(:build_for).with(apk, anything()).and_return(manifest)
|
14
|
+
server.stub(:shell)
|
15
|
+
server.stub(:forward)
|
16
|
+
server.stub(:instrument)
|
17
|
+
manifest.stub(:package).and_return('com.example')
|
18
|
+
end
|
19
|
+
|
20
|
+
context "installing the server for the first time" do
|
21
|
+
it "should install the server" do
|
22
|
+
server.start(activity)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should only install it the first time" do
|
26
|
+
builder.should_receive(:build_for).once.and_return(manifest)
|
27
|
+
server.start(activity)
|
28
|
+
server.start(activity)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should use the default keystore if none is provided" do
|
32
|
+
builder.should_receive(:build_for).with(apk, server.default_keystore)
|
33
|
+
server.start(activity)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use the provided keystore if it is given" do
|
37
|
+
other_keystore = {:path => 'other_keystore'}
|
38
|
+
builder.should_receive(:build_for).with(apk, other_keystore)
|
39
|
+
other_server = Brazenhead::Server.new(apk, other_keystore)
|
40
|
+
other_server.stub(:shell)
|
41
|
+
other_server.stub(:forward)
|
42
|
+
other_server.stub(:instrument)
|
43
|
+
other_server.start(activity)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should setup the proper port forwarding" do
|
47
|
+
server.should_receive(:forward).with("tcp:7777", "tcp:54767")
|
48
|
+
server.start(activity)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "instrumenting the application" do
|
53
|
+
let(:runner) { 'com.leandog.brazenhead/com.leandog.brazenhead.BrazenheadInstrumentation' }
|
54
|
+
|
55
|
+
it "should use the package from the target manifest" do
|
56
|
+
manifest.should_receive(:package)
|
57
|
+
server.start(activity)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should start instrumenting" do
|
61
|
+
expected = {:packageName => 'com.example', :fullLauncherName => 'com.example.SomeActivity', :class => 'com.leandog.brazenhead.TheTest'}
|
62
|
+
server.should_receive(:instrument).with(runner, expected)
|
63
|
+
server.start(activity)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be able to stop instrumenting" do
|
67
|
+
device = double('brazenhead-device')
|
68
|
+
Brazenhead::Device.should_receive(:new).and_return(device)
|
69
|
+
device.should_receive(:stop)
|
70
|
+
server.stop
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class SignerTest
|
2
|
+
include Brazenhead::Signer
|
3
|
+
end
|
4
|
+
|
5
|
+
describe Brazenhead::Signer do
|
6
|
+
let(:signer) { SignerTest.new }
|
7
|
+
let(:process) { double('brazenhead-process').as_null_object }
|
8
|
+
let(:keypath) { '/path/to/debug.keystore' }
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Brazenhead::Process.stub(:new).and_return(process)
|
12
|
+
process.stub(:last_stdout).and_return("jar verified")
|
13
|
+
signer.stub(:default_key_path).and_return(keypath)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should give back the default keystore information" do
|
17
|
+
expected = {:path => '/path/to/debug.keystore', :alias => 'androiddebugkey', :password => 'android', :keystore_password => 'android'}
|
18
|
+
signer.default_keystore.should eq expected
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be able to sign a package" do
|
22
|
+
expanded_keypath = "/expanded/#{keypath}"
|
23
|
+
File.should_receive(:expand_path).with(keypath).and_return(expanded_keypath)
|
24
|
+
process.should_receive(:run).with('jarsigner', '-sigalg', 'MD5withRSA', '-digestalg', 'SHA1', '-storepass', 'android', '-keypass', 'android', '-keystore', expanded_keypath, '/some_apk.apk', 'androiddebugkey')
|
25
|
+
process.should_receive(:run).with('zipalign', '-v', '4', '/some_apk.apk', '/some_apk-signed.apk')
|
26
|
+
signer.sign('/some_apk.apk', signer.default_keystore)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should raise if signing fails to verify" do
|
30
|
+
process.should_receive(:run).with(*"jarsigner -verify /some_apk.apk".split)
|
31
|
+
process.stub(:last_stdout).and_return("something went wrong")
|
32
|
+
lambda { signer.sign('/some_apk.apk', signer.default_keystore) }.should raise_error(message="error signing /some_apk.apk (something went wrong)")
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Driver
|
4
|
+
include Brazenhead
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Brazenhead do
|
8
|
+
|
9
|
+
let(:driver) { Driver.new }
|
10
|
+
let(:http_mock) { double("http_mock") }
|
11
|
+
|
12
|
+
context "when calling a method directly on the module" do
|
13
|
+
before(:each) do
|
14
|
+
Net::HTTP.stub(:new).and_return(http_mock)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should convert method name to camel case with first letter lowercase" do
|
18
|
+
driver.should_receive(:call_method_on_driver).with("fooBar", [])
|
19
|
+
driver.foo_bar
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should make an http call passing the method name as the name" do
|
23
|
+
Net::HTTP.should_receive(:new).and_return(http_mock)
|
24
|
+
http_mock.should_receive(:post).with('/', "commands=[{\"name\":\"fooBar\"}]")
|
25
|
+
driver.foo_bar
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should make the result of the call available for inspection" do
|
29
|
+
http_mock.should_receive(:post).with('/', "commands=[{\"name\":\"fooBar\"}]").and_return("Success")
|
30
|
+
result = driver.foo_bar
|
31
|
+
driver.last_response.should == result
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
|
5
|
+
if ENV['coverage']
|
6
|
+
raise "simplecov only works on Ruby 1.9" unless RUBY_VERSION =~ /^1\.9/
|
7
|
+
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start { add_filter "spec/" }
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rspec'
|
13
|
+
require 'brazenhead'
|
14
|
+
require 'brazenhead/manifest_info'
|
15
|
+
require 'brazenhead/server'
|
16
|
+
require 'net/http'
|
17
|
+
require 'childprocess'
|
18
|
+
require 'tempfile'
|