calabash 1.2.1 → 1.9.9.pre1
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.
- 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
|