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,21 @@
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
2
|
+
package="com.leandog.brazenhead"
|
3
|
+
android:versionCode="1"
|
4
|
+
android:versionName="1.0" >
|
5
|
+
|
6
|
+
<instrumentation
|
7
|
+
android:name="com.leandog.brazenhead.BrazenheadInstrumentation"
|
8
|
+
android:targetPackage="com.example.android.apis" />
|
9
|
+
|
10
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
11
|
+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
12
|
+
|
13
|
+
<uses-sdk
|
14
|
+
android:minSdkVersion="10"
|
15
|
+
android:targetSdkVersion="15" />
|
16
|
+
|
17
|
+
<application android:label="brazenhead">
|
18
|
+
<uses-library android:name="android.test.runner"/>
|
19
|
+
</application>
|
20
|
+
|
21
|
+
</manifest>
|
Binary file
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Feature: Functionality provided by the Brazenhead module
|
2
|
+
|
3
|
+
Scenario: Calling a method which takes no parameters
|
4
|
+
When I call the method "scroll_down" on the Brazenhead module
|
5
|
+
Then I should receive a successful result from the Brazenhead module
|
6
|
+
|
7
|
+
Scenario: Calling a method which takes a parameter
|
8
|
+
When I call the method "click_on_text" on the Brazenhead module passing "Content"
|
9
|
+
Then I should receive a successful result from the Brazenhead module
|
10
|
+
|
11
|
+
Scenario: Chaining two method calls together
|
12
|
+
When I chain together the methods "scroll_down" and "scroll_up" using the target "Robotium"
|
13
|
+
Then I should receive a successful result from the Brazenhead module
|
14
|
+
|
15
|
+
Scenario: Chaining two method calls together with second call on result of first
|
16
|
+
When I chain together the methods "getCurrentListViews" and "size" using the target "LastResultOrRobotium"
|
17
|
+
Then I should receive a successful result from the Brazenhead module
|
18
|
+
And the result from the chained calls should be "1"
|
19
|
+
|
20
|
+
Scenario: Chaining two method calls should use "LastResultOrRobotium" target by default
|
21
|
+
When I chain together the methods "getCurrentListViews" and "size" on the Brazenhead module
|
22
|
+
Then I should receive a successful result from the Brazenhead module
|
23
|
+
And the result from the chained calls should be "1"
|
24
|
+
|
25
|
+
Scenario: Chaining two method calls using a variable
|
26
|
+
When I call "get_text" passing the argument "Graphics" and saving it into the variable "@@graphics@@"
|
27
|
+
And then I call "click_on_view" using teh variable "@@graphics@@" using the target "Robotium"
|
28
|
+
Then I should see "AlphaBitmap" from teh Brazenhead module
|
29
|
+
|
30
|
+
Scenario: Chaining twice
|
31
|
+
When I chain together the methods "getCurrentListViews" and "size" on the Brazenhead module
|
32
|
+
And I call "get_text" passing the argument "Graphics" and saving it into the variable "@@graphics@@"
|
33
|
+
And then I call "click_on_view" using teh variable "@@graphics@@" using the target "Robotium" on the same driver
|
34
|
+
Then I should see "AlphaBitmap" from teh Brazenhead module
|
35
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Feature: Calling Robotium methods
|
2
|
+
|
3
|
+
Scenario: Getting a result from a parameterless method
|
4
|
+
When I do nothing but "scrollDown"
|
5
|
+
Then I should receive a successful result
|
6
|
+
|
7
|
+
Scenario: Calling a method with integers
|
8
|
+
When I call a method with an integer
|
9
|
+
Then I should receive a successful result
|
10
|
+
|
11
|
+
Scenario: Calling a method with strings
|
12
|
+
When I call a method with a string
|
13
|
+
Then I should receive a successful result
|
14
|
+
|
15
|
+
Scenario: Calling a method with floats
|
16
|
+
When I call a method with a float
|
17
|
+
Then I should receive a successful result
|
18
|
+
|
19
|
+
Scenario: Calling a method with booleans
|
20
|
+
When I call a method with a boolean
|
21
|
+
Then I should receive a successful result
|
22
|
+
|
23
|
+
Scenario: Getting useful View information
|
24
|
+
When I get the first "Text" View I find
|
25
|
+
Then I should receive a successful result
|
26
|
+
And the view should have some basic information
|
27
|
+
|
28
|
+
Scenario: Chaining method calls
|
29
|
+
When I call "getCurrentListViews" and then I call "size"
|
30
|
+
Then I should receive a successful result
|
31
|
+
And the result should be "1"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
When /^I call the method "(.*?)" on the Brazenhead module$/ do |method_name|
|
2
|
+
@driver.send method_name
|
3
|
+
end
|
4
|
+
|
5
|
+
When /^I call the method "(.*?)" on the Brazenhead module passing "(.*?)"$/ do |method_name, argument|
|
6
|
+
@driver.send method_name, argument
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I chain together the methods "(.*?)" and "(.*?)" using the target "(.*?)"$/ do |first_method, second_method, target|
|
10
|
+
@driver.chain_calls do |driver|
|
11
|
+
driver.send first_method, :target => target
|
12
|
+
driver.send second_method, :target => target
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^I should receive a successful result from the Brazenhead module$/ do
|
17
|
+
@driver.last_response.code.should == '200'
|
18
|
+
end
|
19
|
+
|
20
|
+
Then /^the result from the chained calls should be "(.*?)"$/ do |result|
|
21
|
+
@driver.last_response.body.should == result
|
22
|
+
end
|
23
|
+
|
24
|
+
When /^I chain together the methods "(.*?)" and "(.*?)" on the Brazenhead module$/ do |first_method, second_method|
|
25
|
+
@driver.chain_calls do |driver|
|
26
|
+
driver.send first_method
|
27
|
+
driver.send second_method
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
When /^I call "(.*?)" passing the argument "(.*?)" and saving it into the variable "(.*?)"$/ do |method, argument, variable|
|
32
|
+
@first_call = {:name => method, :arguments => argument, :variable => variable}
|
33
|
+
end
|
34
|
+
|
35
|
+
When /^then I call "(.*?)" using teh variable "(.*?)" using the target "(.*?)"$/ do |method, argument, target|
|
36
|
+
@driver.chain_calls do |driver|
|
37
|
+
driver.send @first_call[:name], @first_call[:arguments], {:variable => @first_call[:variable]}
|
38
|
+
driver.send method, argument, {:target => target}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
When /^then I call "(.*?)" using teh variable "(.*?)" using the target "(.*?)" on the same driver$/ do |method, argument, target|
|
43
|
+
@driver.chain_calls do |driver|
|
44
|
+
driver.send @first_call[:name], @first_call[:arguments], {:variable => @first_call[:variable]}
|
45
|
+
driver.send method, argument, {:target => target}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Then /^I should see "(.*?)" from teh Brazenhead module$/ do |value|
|
50
|
+
@driver.search_text value
|
51
|
+
@driver.last_response.body.should == 'true'
|
52
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
When /^I attempt to call a method that does not exist$/ do
|
2
|
+
@driver.should_not_exist_at_all
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^I should receive an internal server error$/ do
|
6
|
+
@driver.last_response.code.should eq '500'
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^the exception should have detailed information$/ do
|
10
|
+
json = JSON.parse(@driver.last_response.body)
|
11
|
+
json["exception"].should match ".*CommandNotFoundException"
|
12
|
+
json["errorMessage"].should match "The \\w+\\(.*\\) method was not found on .*Solo\\."
|
13
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
When /^I do nothing but "(.*?)"$/ do |method|
|
2
|
+
@driver.send method
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^I should receive "(.*?)"$/ do |json|
|
6
|
+
@driver.last_response.body.should match json
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I call a method with an integer$/ do
|
10
|
+
@driver.scroll_up_list(0)
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I call a method with a string$/ do
|
14
|
+
@driver.search_button('will not find')
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I call a method with a float$/ do
|
18
|
+
@driver.click_on_screen(100.0, 100.0)
|
19
|
+
end
|
20
|
+
|
21
|
+
When /^I call a method with a boolean$/ do
|
22
|
+
@driver.search_text('Views', true)
|
23
|
+
end
|
24
|
+
|
25
|
+
Then /^I should receive a successful result$/ do
|
26
|
+
@driver.last_response.code.should eq '200'
|
27
|
+
end
|
28
|
+
|
29
|
+
When /^I get the first "(.*?)" View I find$/ do |view_type|
|
30
|
+
@driver.send "get_#{view_type.downcase}", 0
|
31
|
+
end
|
32
|
+
|
33
|
+
Then /^the view should have some basic information$/ do
|
34
|
+
response = JSON.parse(@driver.last_response.body)
|
35
|
+
response.should have_key "id"
|
36
|
+
response.should have_key "classType"
|
37
|
+
response.should have_key "width"
|
38
|
+
response.should have_key "height"
|
39
|
+
response.should have_key "screenLocation"
|
40
|
+
response.should have_key "windowLocation"
|
41
|
+
response.should have_key "left"
|
42
|
+
response.should have_key "top"
|
43
|
+
response.should have_key "right"
|
44
|
+
response.should have_key "bottom"
|
45
|
+
end
|
46
|
+
|
47
|
+
When /^I call "(.*?)" and then I call "(.*?)"$/ do |first_method, next_method|
|
48
|
+
@driver.chain_calls do |driver|
|
49
|
+
driver.send first_method
|
50
|
+
driver.send next_method
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Then /^the result should be "(.*?)"$/ do |result|
|
55
|
+
@driver.last_response.body.should eq result
|
56
|
+
end
|
57
|
+
|
58
|
+
Then /^I should see "(.*?)"$/ do |text|
|
59
|
+
@driver.search_text text
|
60
|
+
@last_response.body.should eq 'true'
|
61
|
+
end
|
62
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,30 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../../', 'lib'))
|
2
|
+
|
3
|
+
require 'brazenhead'
|
4
|
+
require 'brazenhead/server'
|
5
|
+
require 'ADB'
|
6
|
+
require 'childprocess'
|
7
|
+
|
8
|
+
World(ADB)
|
9
|
+
|
10
|
+
keystore = {
|
11
|
+
:path => 'features/support/debug.keystore',
|
12
|
+
:alias => 'androiddebugkey',
|
13
|
+
:password => 'android',
|
14
|
+
:keystore_password => 'android'
|
15
|
+
}
|
16
|
+
|
17
|
+
server = Brazenhead::Server.new('features/support/ApiDemos.apk', keystore)
|
18
|
+
|
19
|
+
class Driver
|
20
|
+
include Brazenhead
|
21
|
+
end
|
22
|
+
|
23
|
+
Before do
|
24
|
+
@driver = Driver.new
|
25
|
+
server.start("ApiDemos")
|
26
|
+
end
|
27
|
+
|
28
|
+
After do
|
29
|
+
server.stop
|
30
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Brazenhead
|
2
|
+
module Android
|
3
|
+
def path_to(sdk)
|
4
|
+
jar = android_jar(sdk)
|
5
|
+
android_jar_missing(sdk) unless File.exists? jar
|
6
|
+
jar
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_keystore
|
10
|
+
{:path => default_key_path,
|
11
|
+
:alias => 'androiddebugkey',
|
12
|
+
:password => 'android',
|
13
|
+
:keystore_password => 'android'}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def android_home
|
18
|
+
ENV['ANDROID_HOME']
|
19
|
+
end
|
20
|
+
|
21
|
+
def platform(sdk)
|
22
|
+
"platforms/android-#{sdk}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_key_path
|
26
|
+
File.expand_path("~/.android/debug.keystore")
|
27
|
+
end
|
28
|
+
|
29
|
+
def android_jar(sdk)
|
30
|
+
File.join(android_home, platform(sdk), 'android.jar')
|
31
|
+
end
|
32
|
+
|
33
|
+
def android_jar_missing(sdk)
|
34
|
+
raise Exception, "the path to android-#{sdk} was not found"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'brazenhead/manifest_info'
|
3
|
+
require 'brazenhead/package'
|
4
|
+
require 'ADB'
|
5
|
+
|
6
|
+
module Brazenhead
|
7
|
+
class Builder
|
8
|
+
include Brazenhead::Package
|
9
|
+
include ADB
|
10
|
+
|
11
|
+
def build_for(apk, keystore)
|
12
|
+
@source_apk = apk
|
13
|
+
@keystore = keystore
|
14
|
+
invalid_package_err(apk) unless File.exists? @source_apk
|
15
|
+
install_server
|
16
|
+
manifest_info
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def install_server
|
21
|
+
Dir.mktmpdir do |dir|
|
22
|
+
copy_base_files_to(dir)
|
23
|
+
update_manifest_in(dir)
|
24
|
+
sign(test_apk_in(dir), @keystore)
|
25
|
+
reinstall test_apk_in(dir)
|
26
|
+
reinstall @source_apk
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reinstall(apk, timeout=90)
|
31
|
+
install apk, "-r", {}, timeout
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy_base_files_to(dir)
|
35
|
+
[test_apk, manifest].each do |file|
|
36
|
+
FileUtils.copy_file(driver_path_for(file), join(dir, file))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def driver_path_for(file)
|
41
|
+
join(File.expand_path("../../../", __FILE__), 'driver', file)
|
42
|
+
end
|
43
|
+
|
44
|
+
def join(*paths)
|
45
|
+
File.join(*paths)
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_manifest_in(dir)
|
49
|
+
manifest_path = join(dir, manifest)
|
50
|
+
|
51
|
+
replace(manifest_path, target_match, target_replace)
|
52
|
+
update_manifest(test_apk_in(dir), manifest_path, manifest_info.target_sdk)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_apk_in(dir)
|
56
|
+
join(dir, test_apk)
|
57
|
+
end
|
58
|
+
|
59
|
+
def replace(file, match, replacement)
|
60
|
+
File.write(file, File.read(file).gsub(match, replacement))
|
61
|
+
end
|
62
|
+
|
63
|
+
def target_match
|
64
|
+
/\btargetPackage="[^"]+"/
|
65
|
+
end
|
66
|
+
|
67
|
+
def target_replace
|
68
|
+
"targetPackage=\"#{the_target}\""
|
69
|
+
end
|
70
|
+
|
71
|
+
def the_target
|
72
|
+
@the_target ||= manifest_info.package
|
73
|
+
end
|
74
|
+
|
75
|
+
def manifest_info
|
76
|
+
@info ||= Brazenhead::ManifestInfo.new(@source_apk)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_apk
|
80
|
+
'brazenhead-release-unsigned.apk'
|
81
|
+
end
|
82
|
+
|
83
|
+
def manifest
|
84
|
+
'AndroidManifest.xml'
|
85
|
+
end
|
86
|
+
|
87
|
+
def invalid_package_err(apk)
|
88
|
+
raise Exception.new("Invalid package path: #{apk}")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '/', 'request')
|
2
|
+
|
3
|
+
module Brazenhead
|
4
|
+
class CallAccumulator
|
5
|
+
def method_missing(method, *args)
|
6
|
+
calls << request.call(method.to_s.to_java_call, args)
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"commands=#{calls.to_json}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
@calls = []
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def calls
|
20
|
+
@calls ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def request
|
24
|
+
@request ||= Brazenhead::Request.new
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Brazenhead
|
4
|
+
class Device
|
5
|
+
|
6
|
+
def send(message)
|
7
|
+
retries = 0
|
8
|
+
begin
|
9
|
+
@last_response = http.post '/', message
|
10
|
+
rescue
|
11
|
+
retries += 1
|
12
|
+
sleep 0.5
|
13
|
+
retry unless retries == 20
|
14
|
+
$stderr.puts "Failed to send the command #{message} #{retries} times..."
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
@last_response
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
http.post '/kill', ''
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_response
|
25
|
+
@last_response
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def http
|
31
|
+
@http ||= Net::HTTP.new '127.0.0.1', 7777
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'brazenhead/process'
|
2
|
+
|
3
|
+
module Brazenhead
|
4
|
+
class ManifestInfo
|
5
|
+
|
6
|
+
def package
|
7
|
+
first_capture /\bpackage="([^"]+)"/
|
8
|
+
end
|
9
|
+
|
10
|
+
def min_sdk
|
11
|
+
sdk(:min) || 1
|
12
|
+
end
|
13
|
+
|
14
|
+
def max_sdk
|
15
|
+
sdk(:max)
|
16
|
+
end
|
17
|
+
|
18
|
+
def target_sdk
|
19
|
+
sdk(:target)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(apk)
|
23
|
+
@apk = apk
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def manifest
|
28
|
+
@manifest ||= load_manifest
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_manifest
|
32
|
+
process.run('aapt', 'dump', 'xmltree', @apk, 'AndroidManifest.xml')
|
33
|
+
process.last_stdout
|
34
|
+
end
|
35
|
+
|
36
|
+
def first_capture(regex)
|
37
|
+
match = regex.match(manifest)
|
38
|
+
match.captures[0] unless match.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def sdk(which)
|
42
|
+
found = first_capture /android:#{which}SdkVersion.*=\(.*\)0x(\h+)/
|
43
|
+
found.hex unless found.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def process
|
48
|
+
@process ||= Brazenhead::Process.new
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'brazenhead/android'
|
2
|
+
require 'brazenhead/signer'
|
3
|
+
|
4
|
+
module Brazenhead
|
5
|
+
module Package
|
6
|
+
include Brazenhead::Android
|
7
|
+
include Brazenhead::Signer
|
8
|
+
|
9
|
+
def update_manifest(apk, manifest, min_sdk = 8)
|
10
|
+
process.run(*update, *package(apk), *with(manifest), *using(path_to(min_sdk)))
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def process
|
15
|
+
@process ||= Brazenhead::Process.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def update
|
19
|
+
"aapt p -u -f".split
|
20
|
+
end
|
21
|
+
|
22
|
+
def package(apk)
|
23
|
+
["-F", apk]
|
24
|
+
end
|
25
|
+
|
26
|
+
def with(manifest)
|
27
|
+
["-M", manifest]
|
28
|
+
end
|
29
|
+
|
30
|
+
def using(path)
|
31
|
+
["-I", path]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'childprocess'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Brazenhead
|
5
|
+
class Process
|
6
|
+
|
7
|
+
attr_accessor :last_stdout, :last_stderr
|
8
|
+
|
9
|
+
def run(process, *args)
|
10
|
+
process = ChildProcess.build(process, *args)
|
11
|
+
process.io.stdout, process.io.stderr = std_out_err
|
12
|
+
process.start
|
13
|
+
process.wait
|
14
|
+
@last_stdout = output(process.io.stdout)
|
15
|
+
@last_stderr = output(process.io.stderr)
|
16
|
+
end
|
17
|
+
|
18
|
+
def std_out_err
|
19
|
+
return ::Tempfile.new("brazenhead-proc-out-#{now}"), ::Tempfile.new("brazenhead-proc-err-#{now}")
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def output(file)
|
24
|
+
file.rewind
|
25
|
+
out = file.read
|
26
|
+
file.close
|
27
|
+
file.unlink
|
28
|
+
out
|
29
|
+
end
|
30
|
+
|
31
|
+
def now
|
32
|
+
"#{Time.now}".gsub(':', '_')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Brazenhead
|
4
|
+
class Request
|
5
|
+
def build(method, args)
|
6
|
+
"commands=#{[call(method, args)].to_json}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(method, args)
|
10
|
+
target = parse_target(args[-1])
|
11
|
+
variable = parse_variable(args[-1])
|
12
|
+
build_call(method, strip_hash_arg(args), variable, target)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_call(method, args, variable, target)
|
18
|
+
call = {:name => method} if args.empty?
|
19
|
+
call = {:name => method, :arguments => args} unless args.empty?
|
20
|
+
call[:variable] = variable if variable
|
21
|
+
call[:target] = target if target
|
22
|
+
call
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_target(hsh)
|
26
|
+
target = hsh.delete(:target) if hsh.is_a?(Hash)
|
27
|
+
return target.nil? ? nil : target
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_variable(hsh)
|
31
|
+
variable = hsh.delete(:variable) if hsh.is_a?(Hash)
|
32
|
+
return variable.nil? ? nil : variable
|
33
|
+
end
|
34
|
+
|
35
|
+
def strip_hash_arg(args)
|
36
|
+
args.delete_at(-1) if args[-1].is_a?(Hash) and args[-1].empty?
|
37
|
+
args
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'brazenhead/builder'
|
2
|
+
require 'brazenhead/device'
|
3
|
+
|
4
|
+
module Brazenhead
|
5
|
+
class Server
|
6
|
+
include Brazenhead::Package
|
7
|
+
include ADB
|
8
|
+
|
9
|
+
def initialize(apk, keystore = default_keystore)
|
10
|
+
@apk = apk
|
11
|
+
@keystore = keystore
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(activity)
|
15
|
+
build
|
16
|
+
instrument(runner, :packageName => their_package, :fullLauncherName => full(activity) , :class => the_test)
|
17
|
+
end
|
18
|
+
|
19
|
+
def stop
|
20
|
+
device.stop
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def build
|
25
|
+
forward "tcp:7777", "tcp:54767"
|
26
|
+
@manifest_info ||= Brazenhead::Builder.new.build_for(@apk, @keystore)
|
27
|
+
end
|
28
|
+
|
29
|
+
def device
|
30
|
+
@device ||= Brazenhead::Device.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def the_test
|
34
|
+
"#{leandog}.TheTest"
|
35
|
+
end
|
36
|
+
|
37
|
+
def full(activity)
|
38
|
+
"#{their_package}.#{activity}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def their_package
|
42
|
+
@manifest_info.package
|
43
|
+
end
|
44
|
+
|
45
|
+
def runner
|
46
|
+
"#{leandog}/#{leandog}.BrazenheadInstrumentation"
|
47
|
+
end
|
48
|
+
|
49
|
+
def leandog
|
50
|
+
'com.leandog.brazenhead'
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|