brazenhead 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.
- 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'
|