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,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
|