calabash 1.2.1 → 1.9.9.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +39 -0
- data/LICENSE +204 -21
- data/README.md +36 -6
- data/VERSIONING.md +16 -0
- data/bin/calabash +95 -0
- data/lib/calabash.rb +185 -1
- data/lib/calabash/android.rb +64 -0
- data/lib/calabash/android/adb.rb +277 -0
- data/lib/calabash/android/application.rb +110 -0
- data/lib/calabash/android/build.rb +12 -0
- data/lib/calabash/android/build/application.rb +13 -0
- data/lib/calabash/android/build/build_error.rb +11 -0
- data/lib/calabash/android/build/builder.rb +119 -0
- data/lib/calabash/android/build/java_keystore.rb +177 -0
- data/lib/calabash/android/build/resigner.rb +56 -0
- data/lib/calabash/android/build/test_server.rb +27 -0
- data/lib/calabash/android/console_helpers.rb +44 -0
- data/lib/calabash/android/cucumber.rb +3 -0
- data/lib/calabash/android/device.rb +965 -0
- data/lib/calabash/android/environment.rb +470 -0
- data/lib/calabash/android/gestures.rb +369 -0
- data/lib/calabash/android/interactions.rb +45 -0
- data/lib/calabash/android/lib/.irbrc +55 -0
- data/lib/calabash/android/lib/AndroidManifest.xml +51 -0
- data/lib/calabash/android/lib/TestServer.apk +0 -0
- data/lib/calabash/android/lib/calmd5/arm64-v8a/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/arm64-v8a/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi-v7a/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi-v7a/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/armeabi/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/mips/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/mips/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/mips64/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/mips64/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/x86/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/x86/calmd5-pie +0 -0
- data/lib/calabash/android/lib/calmd5/x86_64/calmd5 +0 -0
- data/lib/calabash/android/lib/calmd5/x86_64/calmd5-pie +0 -0
- data/lib/calabash/android/lib/screenshot_taker.jar +0 -0
- data/lib/calabash/android/life_cycle.rb +37 -0
- data/lib/calabash/android/orientation.rb +30 -0
- data/lib/calabash/android/physical_buttons.rb +39 -0
- data/lib/calabash/android/screenshot.rb +9 -0
- data/lib/calabash/android/scroll.rb +5 -0
- data/lib/calabash/android/server.rb +10 -0
- data/lib/calabash/android/text.rb +54 -0
- data/lib/calabash/application.rb +74 -0
- data/lib/calabash/cli.rb +12 -0
- data/lib/calabash/cli/build.rb +33 -0
- data/lib/calabash/cli/console.rb +90 -0
- data/lib/calabash/cli/generate.rb +110 -0
- data/lib/calabash/cli/helpers.rb +130 -0
- data/lib/calabash/cli/resign.rb +33 -0
- data/lib/calabash/cli/run.rb +99 -0
- data/lib/calabash/cli/setup_keystore.rb +39 -0
- data/lib/calabash/color.rb +32 -0
- data/lib/calabash/console_helpers.rb +90 -0
- data/lib/calabash/defaults.rb +56 -0
- data/lib/calabash/device.rb +401 -0
- data/lib/calabash/environment.rb +75 -0
- data/lib/calabash/gestures.rb +384 -0
- data/lib/calabash/http.rb +8 -0
- data/lib/calabash/http/error.rb +15 -0
- data/lib/calabash/http/request.rb +42 -0
- data/lib/calabash/http/retriable_client.rb +156 -0
- data/lib/calabash/interactions.rb +105 -0
- data/lib/calabash/ios.rb +37 -0
- data/lib/calabash/ios/application.rb +119 -0
- data/lib/calabash/ios/conditions.rb +79 -0
- data/lib/calabash/ios/console_helpers.rb +72 -0
- data/lib/calabash/ios/device.rb +24 -0
- data/lib/calabash/ios/device/device_implementation.rb +779 -0
- data/lib/calabash/ios/device/gestures_mixin.rb +167 -0
- data/lib/calabash/ios/device/keyboard_mixin.rb +133 -0
- data/lib/calabash/ios/device/physical_device_mixin.rb +266 -0
- data/lib/calabash/ios/device/rotation_mixin.rb +124 -0
- data/lib/calabash/ios/device/routes/backdoor_route_mixin.rb +86 -0
- data/lib/calabash/ios/device/routes/condition_route_mixin.rb +62 -0
- data/lib/calabash/ios/device/routes/error.rb +8 -0
- data/lib/calabash/ios/device/routes/handle_route_mixin.rb +102 -0
- data/lib/calabash/ios/device/routes/map_route_mixin.rb +38 -0
- data/lib/calabash/ios/device/routes/playback_route_mixin.rb +70 -0
- data/lib/calabash/ios/device/routes/response_parser.rb +48 -0
- data/lib/calabash/ios/device/routes/uia_route_mixin.rb +238 -0
- data/lib/calabash/ios/device/runtime_attributes.rb +184 -0
- data/lib/calabash/ios/device/status_bar_mixin.rb +17 -0
- data/lib/calabash/ios/device/text_mixin.rb +19 -0
- data/lib/calabash/ios/device/uia_keyboard_mixin.rb +188 -0
- data/lib/calabash/ios/device/uia_mixin.rb +12 -0
- data/lib/calabash/ios/environment.rb +41 -0
- data/lib/calabash/ios/interactions.rb +10 -0
- data/lib/calabash/ios/lib/.irbrc +55 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_down_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_down_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_left_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_left_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_right_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_right_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_up_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_left_home_up_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_down_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_down_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_left_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_left_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_right_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_right_iphone.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_up_ipad.base64 +2 -0
- data/lib/calabash/ios/lib/recordings/rotate_right_home_up_iphone.base64 +2 -0
- data/lib/calabash/ios/orientation.rb +117 -0
- data/lib/calabash/ios/scroll.rb +504 -0
- data/lib/calabash/ios/server.rb +73 -0
- data/lib/calabash/ios/text.rb +248 -0
- data/lib/calabash/ios/uia.rb +24 -0
- data/lib/calabash/lib/skeleton/config/cucumber.yml +6 -0
- data/lib/calabash/lib/skeleton/features/sample.feature +5 -0
- data/lib/calabash/lib/skeleton/features/step_definitions/calabash_steps.rb +29 -0
- data/lib/calabash/lib/skeleton/features/support/env.rb +54 -0
- data/lib/calabash/lib/skeleton/features/support/hooks.rb +83 -0
- data/lib/calabash/life_cycle.rb +111 -0
- data/lib/calabash/location.rb +51 -0
- data/lib/calabash/logger.rb +87 -0
- data/lib/calabash/orientation.rb +84 -0
- data/lib/calabash/page.rb +35 -0
- data/lib/calabash/patch.rb +14 -0
- data/lib/calabash/patch/array.rb +16 -0
- data/lib/calabash/patch/run_loop.rb +90 -0
- data/lib/calabash/query.rb +160 -0
- data/lib/calabash/query_result.rb +85 -0
- data/lib/calabash/screenshot.rb +89 -0
- data/lib/calabash/server.rb +16 -0
- data/lib/calabash/text.rb +76 -0
- data/lib/calabash/utility.rb +58 -0
- data/lib/calabash/version.rb +3 -1
- data/lib/calabash/wait.rb +474 -0
- metadata +462 -24
@@ -0,0 +1,64 @@
|
|
1
|
+
module Calabash
|
2
|
+
# Contains the Android implementations of the Calabash APIs.
|
3
|
+
module Android
|
4
|
+
TEST_SERVER_CODE_PATH = File.join(File.dirname(__FILE__), '..', '..', 'android', 'test-server')
|
5
|
+
UNSIGNED_TEST_SERVER_APK = File.join(File.dirname(__FILE__), 'android', 'lib', 'TestServer.apk')
|
6
|
+
ANDROID_MANIFEST_PATH = File.join(File.dirname(__FILE__), 'android', 'lib', 'AndroidManifest.xml')
|
7
|
+
|
8
|
+
require 'calabash'
|
9
|
+
include Calabash
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
def self.extended(base)
|
13
|
+
Calabash.send(:extended, base)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @!visibility private
|
17
|
+
def self.included(base)
|
18
|
+
Calabash.send(:included, base)
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'calabash/android/environment'
|
22
|
+
|
23
|
+
require 'calabash/android/application'
|
24
|
+
require 'calabash/android/build'
|
25
|
+
require 'calabash/android/device'
|
26
|
+
require 'calabash/android/screenshot'
|
27
|
+
require 'calabash/android/server'
|
28
|
+
require 'calabash/android/adb'
|
29
|
+
require 'calabash/android/gestures'
|
30
|
+
require 'calabash/android/interactions'
|
31
|
+
require 'calabash/android/orientation'
|
32
|
+
require 'calabash/android/physical_buttons'
|
33
|
+
require 'calabash/android/text'
|
34
|
+
require 'calabash/android/console_helpers'
|
35
|
+
require 'calabash/android/life_cycle'
|
36
|
+
|
37
|
+
include Calabash::Android::Gestures
|
38
|
+
include Calabash::Android::Interactions
|
39
|
+
include Calabash::Android::LifeCycle
|
40
|
+
include Calabash::Android::Orientation
|
41
|
+
include Calabash::Android::PhysicalButtons
|
42
|
+
include Calabash::Android::Text
|
43
|
+
|
44
|
+
# @!visibility private
|
45
|
+
def self.binary_location(name, abi, using_pie)
|
46
|
+
binary_name = if using_pie
|
47
|
+
"#{name}-pie"
|
48
|
+
else
|
49
|
+
name
|
50
|
+
end
|
51
|
+
|
52
|
+
file = File.join(File.dirname(__FILE__), 'android', 'lib', name, abi, binary_name)
|
53
|
+
|
54
|
+
unless File.exist?(file)
|
55
|
+
raise "No such file '#{file}'"
|
56
|
+
end
|
57
|
+
|
58
|
+
file
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Setup environment on load
|
64
|
+
Calabash::Android::Environment.setup
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module Calabash
|
5
|
+
module Android
|
6
|
+
# @!visibility private
|
7
|
+
class ADB
|
8
|
+
# @!visibility private
|
9
|
+
class ADBCallError < StandardError
|
10
|
+
attr_reader :stderr, :stdout
|
11
|
+
|
12
|
+
def initialize(message, stderr=nil, stdout=nil)
|
13
|
+
super(message)
|
14
|
+
|
15
|
+
@stderr = stderr
|
16
|
+
@stdout = stdout
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
PROCESS_WAIT_TIME = 10
|
21
|
+
|
22
|
+
def self.open_pipe_with_timeout(timeout, *cmd, &block)
|
23
|
+
begin
|
24
|
+
i, o, e, t, pid = nil
|
25
|
+
|
26
|
+
Timeout.timeout(timeout, ProcessDidNotExitError) do
|
27
|
+
i, o, e, t = Open3.popen3(*cmd)
|
28
|
+
i.sync = true
|
29
|
+
o.sync = true
|
30
|
+
e.sync = true
|
31
|
+
pid = t.pid
|
32
|
+
block.call(i, o, e)
|
33
|
+
|
34
|
+
t.value.exitstatus
|
35
|
+
end
|
36
|
+
rescue ProcessDidNotExitError => _
|
37
|
+
raise ADBCallError, 'ADB process did not exit'
|
38
|
+
ensure
|
39
|
+
i.close unless i.nil? || i.closed?
|
40
|
+
o.close unless o.nil? || o.closed?
|
41
|
+
e.close unless e.nil? || e.closed?
|
42
|
+
|
43
|
+
if pid
|
44
|
+
begin
|
45
|
+
Process.kill(9, pid)
|
46
|
+
rescue Errno::ESRCH => _
|
47
|
+
# do nothing
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
t.join if t
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.open_adb_pipe(*cmd, **options, &block)
|
56
|
+
timeout = options.fetch(:timeout, PROCESS_WAIT_TIME)
|
57
|
+
|
58
|
+
open_pipe_with_timeout(timeout, Environment.adb_path, *cmd) do |i, o, e|
|
59
|
+
block.call(i, o, e) if block
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
DAEMON_STARTED_MESSAGE = "* daemon not running. starting it now on port 5037 *\n* daemon started successfully *\n"
|
64
|
+
|
65
|
+
def self.command(*cmd, **args)
|
66
|
+
Logger.debug("ADB Command: #{cmd.join(', ')}")
|
67
|
+
Logger.debug("ADB input: #{args[:input]}")
|
68
|
+
stderr = nil
|
69
|
+
stdout = nil
|
70
|
+
exit_code = nil
|
71
|
+
|
72
|
+
input = args[:input]
|
73
|
+
|
74
|
+
begin
|
75
|
+
exit_code = open_adb_pipe(*cmd, args) do |i, o, e|
|
76
|
+
if input
|
77
|
+
input.each do |p_cmd|
|
78
|
+
begin
|
79
|
+
i.puts p_cmd
|
80
|
+
rescue Errno::EPIPE => err
|
81
|
+
i.close
|
82
|
+
stderr = e.readlines.join
|
83
|
+
stdout = o.readlines.join
|
84
|
+
raise ADBCallError.new(err, stderr, stdout)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
i.close
|
89
|
+
end
|
90
|
+
|
91
|
+
unless args.fetch(:no_read, false)
|
92
|
+
stdout = o.readlines.join
|
93
|
+
stderr = e.readlines.join
|
94
|
+
end
|
95
|
+
|
96
|
+
if stdout && stdout.start_with?(DAEMON_STARTED_MESSAGE)
|
97
|
+
stdout = stdout[DAEMON_STARTED_MESSAGE.length..-1]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
rescue IOError => e
|
101
|
+
raise ADBCallError, e
|
102
|
+
end
|
103
|
+
|
104
|
+
if exit_code != 0
|
105
|
+
Logger.debug("Adb process exited with #{exit_code}")
|
106
|
+
Logger.debug("Error message from ADB: ")
|
107
|
+
Logger.debug(stderr)
|
108
|
+
|
109
|
+
stderr_output = if stderr && !stderr.empty?
|
110
|
+
": #{dot_string(stderr.lines.first, 100)}"
|
111
|
+
else
|
112
|
+
''
|
113
|
+
end
|
114
|
+
|
115
|
+
raise ADBCallError.new(
|
116
|
+
"Adb process exited with #{exit_code}#{stderr_output}", stderr, stdout)
|
117
|
+
end
|
118
|
+
|
119
|
+
stdout
|
120
|
+
end
|
121
|
+
|
122
|
+
attr_reader :serial
|
123
|
+
|
124
|
+
def initialize(serial)
|
125
|
+
@serial = serial
|
126
|
+
end
|
127
|
+
|
128
|
+
def command(*argv, **args)
|
129
|
+
cmd = argv.dup
|
130
|
+
|
131
|
+
if serial
|
132
|
+
cmd.unshift('-s', serial)
|
133
|
+
end
|
134
|
+
|
135
|
+
ADB.command(*cmd, args)
|
136
|
+
end
|
137
|
+
|
138
|
+
END_STRING = '__CAL_END__'
|
139
|
+
|
140
|
+
def shell(shell_cmd, options={})
|
141
|
+
if shell_cmd.nil? || shell_cmd.empty?
|
142
|
+
raise ArgumentError, "Invalid shell command '#{shell_cmd}'"
|
143
|
+
end
|
144
|
+
|
145
|
+
input =
|
146
|
+
[
|
147
|
+
"#{shell_cmd}; echo \"#{END_STRING}$?\"; exit 0"
|
148
|
+
]
|
149
|
+
|
150
|
+
args = options.merge(input: input)
|
151
|
+
|
152
|
+
result = command('shell', args)
|
153
|
+
|
154
|
+
# We get a result like this:
|
155
|
+
#
|
156
|
+
# [0] "getprop ro.build.version.release; echo \"\r\n",
|
157
|
+
# [1] "$?\"; exit 0\r\n",
|
158
|
+
# [2] "shell@hammerhead:/ $ getprop ro.build.version.release; echo \"\r\r\n",
|
159
|
+
# [3] "> $?\"; exit 0\r\r\n",
|
160
|
+
# [4] "4.4\r\n",
|
161
|
+
# [5] "\r\n",
|
162
|
+
# [6] "0\r\n"
|
163
|
+
#
|
164
|
+
# out =
|
165
|
+
# [4] "4.4\r\n"
|
166
|
+
# [5] "\r\n",
|
167
|
+
# [6] "0\r\n"
|
168
|
+
#
|
169
|
+
# command_result =
|
170
|
+
# [4] "4.4\r\n"
|
171
|
+
# [5] "\r\n",
|
172
|
+
#
|
173
|
+
# exit_code_s =
|
174
|
+
# [6] "0\r\n"
|
175
|
+
|
176
|
+
index = result.lines.index {|line| line.start_with?(shell_name)}
|
177
|
+
|
178
|
+
if index.nil?
|
179
|
+
raise ADBCallError.new("Could not parse output #{ADB.dot_string(result, 100)}", result)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Remove the commands
|
183
|
+
out = result.lines[index+1..-1]
|
184
|
+
|
185
|
+
last_line = out.last
|
186
|
+
end_index = nil
|
187
|
+
|
188
|
+
15.times do |i|
|
189
|
+
if last_line[-(END_STRING.length+i-1)..-i] == END_STRING
|
190
|
+
end_index = -i
|
191
|
+
break
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
if end_index.nil?
|
196
|
+
raise ADBCallError.new("Could not parse output #{ADB.dot_string(result, 100)}", result)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Get the result from the command
|
200
|
+
command_result = out[0..-2].join + last_line[0..(end_index - END_STRING.length)]
|
201
|
+
|
202
|
+
# Get the exit code
|
203
|
+
exit_code_s = out[-1][end_index+1..-1]
|
204
|
+
|
205
|
+
unless options[:no_exit_code_check]
|
206
|
+
unless exit_code_s.to_i.to_s == exit_code_s.chomp
|
207
|
+
raise ADBCallError,
|
208
|
+
"Unable to obtain exit code. Result: '#{exit_code_s}'"
|
209
|
+
end
|
210
|
+
|
211
|
+
exit_code = exit_code_s.to_i
|
212
|
+
|
213
|
+
if exit_code != 0
|
214
|
+
Logger.debug("Adb shell command exited with #{exit_code}")
|
215
|
+
Logger.debug("Error message from ADB: ")
|
216
|
+
Logger.debug(command_result)
|
217
|
+
|
218
|
+
stderr_output = if command_result && !command_result.empty?
|
219
|
+
": #{ADB.dot_string(command_result.lines.first, 100)}"
|
220
|
+
else
|
221
|
+
''
|
222
|
+
end
|
223
|
+
|
224
|
+
raise ADBCallError.new(
|
225
|
+
"Adb shell command exited with #{exit_code}#{stderr_output}", command_result)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
command_result
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def self.dot_string(string, length)
|
235
|
+
if string.length > length
|
236
|
+
"#{string[0, length-3]}..."
|
237
|
+
else
|
238
|
+
string
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def shell_name
|
243
|
+
if @shell_name
|
244
|
+
@shell_name
|
245
|
+
else
|
246
|
+
result = command('shell', input: ['echo "test"; exit 0'])
|
247
|
+
|
248
|
+
# result.lines =
|
249
|
+
# [
|
250
|
+
# [0] "echo \"foo\"; exit 0\r\n",
|
251
|
+
# [1] "shell@hammerhead:/ $ echo \"foo\"; exit 0\r\r\n",
|
252
|
+
# [2] "foo\r\n"
|
253
|
+
# ]
|
254
|
+
#
|
255
|
+
# OR
|
256
|
+
#
|
257
|
+
# result.lines =
|
258
|
+
# [
|
259
|
+
# [1] "shell@hammerhead:/ $ echo \"foo\"; exit 0\r\r\n",
|
260
|
+
# [2] "foo\r\n"
|
261
|
+
# ]
|
262
|
+
|
263
|
+
#result.lines.index {|line| !line.start_with?('echo')}
|
264
|
+
|
265
|
+
# "shell@hammerhead:/ $ echo \"foo\"; exit 0\r\r\n"
|
266
|
+
shell_name_line = result.lines[-2]
|
267
|
+
|
268
|
+
# "shell@hammerhead:/ $ "
|
269
|
+
@shell_name = shell_name_line.split('echo').first
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# @!visibility private
|
274
|
+
class ProcessDidNotExitError < RuntimeError; end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Calabash
|
2
|
+
module Android
|
3
|
+
# A class to represent an Android application (.apk).
|
4
|
+
class Application < Calabash::Application
|
5
|
+
attr_reader :test_server
|
6
|
+
|
7
|
+
def self.default_from_environment
|
8
|
+
application_path = Environment::APP_PATH
|
9
|
+
|
10
|
+
if application_path.nil?
|
11
|
+
raise 'No application path is set'
|
12
|
+
end
|
13
|
+
|
14
|
+
unless File.exist?(application_path)
|
15
|
+
raise "Application '#{application_path}' does not exist"
|
16
|
+
end
|
17
|
+
|
18
|
+
if File.directory?(application_path)
|
19
|
+
raise "Application '#{application_path}' is not a file"
|
20
|
+
end
|
21
|
+
|
22
|
+
build_test_server = Build::TestServer.new(application_path)
|
23
|
+
test_server_path = Environment::TEST_SERVER_PATH ||
|
24
|
+
build_test_server.path
|
25
|
+
|
26
|
+
unless File.exist?(test_server_path)
|
27
|
+
Logger.error "Test-server '#{test_server_path}' does not exist."
|
28
|
+
Logger.error "Build it using: 'calabash build \"#{application_path}\"'"
|
29
|
+
Logger.error ''
|
30
|
+
raise "Test-server '#{test_server_path}' does not exist."
|
31
|
+
end
|
32
|
+
|
33
|
+
Application.new(application_path, test_server_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(application_path, test_server_path, options = {})
|
37
|
+
super(application_path, options)
|
38
|
+
@test_server = Application.new(test_server_path, nil, options) if test_server_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def extract_identifier
|
42
|
+
package_line = aapt_dump('package').first
|
43
|
+
raise "'package' not found in aapt output" unless package_line
|
44
|
+
m = package_line.match(/name='([^']+)'/)
|
45
|
+
raise "Unexpected output from aapt: #{package_line}" unless m
|
46
|
+
m[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
def main_activity
|
50
|
+
begin
|
51
|
+
@logger.log("Trying to find launchable activity")
|
52
|
+
launchable_activity_line = aapt_dump('launchable-activity').first
|
53
|
+
raise "'launchable-activity' not found in aapt output" unless launchable_activity_line
|
54
|
+
m = launchable_activity_line.match(/name='([^']+)'/)
|
55
|
+
raise "Unexpected output from aapt: #{launchable_activity_line}" unless m
|
56
|
+
@logger.log("Found launchable activity '#{m[1]}'")
|
57
|
+
m[1]
|
58
|
+
rescue => e
|
59
|
+
@logger.log("Could not find launchable activity, trying to parse raw AndroidManifest. #{e.message}")
|
60
|
+
|
61
|
+
manifest_data = `"#{Environment.aapt_path}/aapt" dump xmltree "#{@path}" AndroidManifest.xml`
|
62
|
+
regex = /^\s*A:[\s*]android:name\(\w+\)\=\"android.intent.category.LAUNCHER\"/
|
63
|
+
lines = manifest_data.lines.collect(&:strip)
|
64
|
+
indicator_line = nil
|
65
|
+
|
66
|
+
lines.each_with_index do |line, index|
|
67
|
+
match = line.match(regex)
|
68
|
+
|
69
|
+
unless match.nil?
|
70
|
+
raise 'More than one launchable activity in AndroidManifest' unless indicator_line.nil?
|
71
|
+
indicator_line = index
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
raise 'No launchable activity found in AndroidManifest' unless indicator_line
|
76
|
+
|
77
|
+
intent_filter_found = false
|
78
|
+
|
79
|
+
(0..indicator_line).reverse_each do |index|
|
80
|
+
if intent_filter_found
|
81
|
+
match = lines[index].match(/\s*E:\s*activity-alias/)
|
82
|
+
|
83
|
+
raise 'Could not find target activity in activity alias' if match
|
84
|
+
|
85
|
+
match = lines[index].match(/^\s*A:\s*android:targetActivity\(\w*\)\=\"([^\"]+)/){$1}
|
86
|
+
|
87
|
+
if match
|
88
|
+
@logger.log("Found launchable activity '#{match}'")
|
89
|
+
|
90
|
+
return match
|
91
|
+
end
|
92
|
+
else
|
93
|
+
unless lines[index].match(/\s*E: intent-filter/).nil?
|
94
|
+
@logger.log("Read intent filter")
|
95
|
+
intent_filter_found = true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
raise 'Could not find launchable activity'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def aapt_dump(key)
|
105
|
+
lines = `"#{Environment.aapt_path}" dump badging "#{path}"`.lines.collect(&:strip)
|
106
|
+
lines.select { |l| l.start_with?("#{key}:") }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|