calabash-android 0.4.0.pre11 → 0.4.0.pre15
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/Rakefile +27 -2
- data/lib/calabash-android/helpers.rb +5 -3
- data/lib/calabash-android/lib/TestServer.apk +0 -0
- data/lib/calabash-android/operations.rb +47 -17
- data/lib/calabash-android/version.rb +2 -1
- data/lib/calabash-android/wait_helpers.rb +93 -0
- data/test-server/instrumentation-backend/antlr/UIQuery.g +2 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/FinishOpenedActivities.java +19 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GetOpenedActivities.java +31 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/activity/GoBackToActivity.java +67 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/DragCoordinates.java +28 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/Swipe.java +11 -5
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/version/Version.java +37 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryLexer.java +90 -71
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryParser.java +75 -93
- metadata +8 -2
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ load 'lib/calabash-android/helpers.rb'
|
|
3
3
|
|
4
4
|
def build
|
5
5
|
test_server_template_dir = File.join(File.dirname(__FILE__), 'test-server')
|
6
|
-
|
6
|
+
|
7
7
|
Dir.mktmpdir do |workspace_dir|
|
8
8
|
|
9
9
|
@test_server_dir = File.join(workspace_dir, 'test-server')
|
@@ -34,8 +34,32 @@ def build
|
|
34
34
|
end
|
35
35
|
|
36
36
|
|
37
|
+
desc "Update server version to expected server version"
|
38
|
+
task :match_versions do
|
39
|
+
test_server_template_dir = File.join(File.dirname(__FILE__), 'test-server')
|
40
|
+
|
41
|
+
version_command = File.join(test_server_template_dir,
|
42
|
+
"instrumentation-backend",
|
43
|
+
"src",
|
44
|
+
"sh",
|
45
|
+
"calaba",
|
46
|
+
"instrumentationbackend",
|
47
|
+
"actions",
|
48
|
+
"version",
|
49
|
+
"Version.java" )
|
50
|
+
|
51
|
+
unless File.exist?(version_command)
|
52
|
+
raise "Unable to find version file at: #{version_command}"
|
53
|
+
end
|
54
|
+
|
55
|
+
text = File.read(version_command)
|
56
|
+
text.gsub!(/public static final String VERSION=".*";/,%Q[public static final String VERSION="#{Calabash::Android::SERVER_VERSION}";])
|
57
|
+
File.open(version_command, 'w') { |f| f.write(text) }
|
58
|
+
|
59
|
+
end
|
37
60
|
|
38
|
-
|
61
|
+
|
62
|
+
task :build => :match_versions do
|
39
63
|
unless File.exists? "test-server/calabash-js/src"
|
40
64
|
puts "calabash-js not found!"
|
41
65
|
puts "For instuctions see: https://github.com/calabash/calabash-android/wiki/Building-calabash-android"
|
@@ -48,4 +72,5 @@ task :build do
|
|
48
72
|
|
49
73
|
end
|
50
74
|
|
75
|
+
|
51
76
|
Bundler::GemHelper.install_tasks
|
@@ -59,7 +59,7 @@ def sign_apk(app_path, dest_path)
|
|
59
59
|
jarsigner_path = "jarsigner"
|
60
60
|
end
|
61
61
|
|
62
|
-
cmd = "#{jarsigner_path} -sigalg MD5withRSA -digestalg SHA1 -signedjar #{dest_path} -storepass #{keystore["keystore_password"]} -keystore
|
62
|
+
cmd = "#{jarsigner_path} -sigalg MD5withRSA -digestalg SHA1 -signedjar #{dest_path} -storepass #{keystore["keystore_password"]} -keystore #{keystore["keystore_location"]} #{app_path} #{keystore["keystore_alias"]}"
|
63
63
|
log cmd
|
64
64
|
unless system(cmd)
|
65
65
|
puts "jarsigner command: #{cmd}"
|
@@ -69,10 +69,12 @@ end
|
|
69
69
|
|
70
70
|
def read_keystore_info
|
71
71
|
if File.exist? ".calabash_settings"
|
72
|
-
JSON.parse(IO.read(".calabash_settings"))
|
72
|
+
keystore = JSON.parse(IO.read(".calabash_settings"))
|
73
|
+
keystore["keystore_location"] = '"' + File.expand_path(keystore["keystore_location"]) + '"' if keystore["keystore_location"]
|
74
|
+
keystore
|
73
75
|
else
|
74
76
|
{
|
75
|
-
"keystore_location" => "#{ENV["HOME"]
|
77
|
+
"keystore_location" => %Q("#{File.expand_path(File.join(ENV["HOME"], "/.android/debug.keystore"))}\"),
|
76
78
|
"keystore_password" => "android",
|
77
79
|
"keystore_alias" => "androiddebugkey",
|
78
80
|
}
|
Binary file
|
@@ -6,12 +6,16 @@ require 'json'
|
|
6
6
|
require 'socket'
|
7
7
|
require 'timeout'
|
8
8
|
require 'calabash-android/helpers'
|
9
|
+
require 'calabash-android/wait_helpers'
|
10
|
+
require 'calabash-android/version'
|
9
11
|
require 'retriable'
|
12
|
+
require 'cucumber'
|
10
13
|
|
11
14
|
|
12
15
|
module Calabash module Android
|
13
16
|
|
14
17
|
module Operations
|
18
|
+
include Calabash::Android::WaitHelpers
|
15
19
|
|
16
20
|
def log(message)
|
17
21
|
$stdout.puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - #{message}" if (ARGV.include? "-v" or ARGV.include? "--verbose")
|
@@ -86,19 +90,19 @@ module Operations
|
|
86
90
|
default_device.set_gps_coordinates(latitude, longitude)
|
87
91
|
end
|
88
92
|
|
89
|
-
def wait_for(timeout, &block)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
93
|
+
#def wait_for(timeout, &block)
|
94
|
+
# value = nil
|
95
|
+
# begin
|
96
|
+
# Timeout::timeout(timeout) do
|
97
|
+
# until (value = block.call)
|
98
|
+
# sleep 0.3
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
# rescue Exception => e
|
102
|
+
# raise e
|
103
|
+
# end
|
104
|
+
# value
|
105
|
+
#end
|
102
106
|
|
103
107
|
def query(uiquery, *args)
|
104
108
|
converted_args = []
|
@@ -177,7 +181,7 @@ module Operations
|
|
177
181
|
succeeded = `#{adb_command} shell pm list packages`.include?("package:#{pn}")
|
178
182
|
|
179
183
|
unless succeeded
|
180
|
-
Cucumber.wants_to_quit = true
|
184
|
+
::Cucumber.wants_to_quit = true
|
181
185
|
raise "#{pn} did not get installed. Aborting!"
|
182
186
|
end
|
183
187
|
end
|
@@ -325,7 +329,7 @@ module Operations
|
|
325
329
|
end
|
326
330
|
|
327
331
|
def start_test_server_in_background(options={})
|
328
|
-
raise "Will not start test server because of previous failures." if Cucumber.wants_to_quit
|
332
|
+
raise "Will not start test server because of previous failures." if ::Cucumber.wants_to_quit
|
329
333
|
|
330
334
|
if keyguard_enabled?
|
331
335
|
wake_up
|
@@ -369,12 +373,39 @@ module Operations
|
|
369
373
|
log "Instrumentation backend is ready!"
|
370
374
|
end
|
371
375
|
end
|
376
|
+
rescue Exception => e
|
372
377
|
|
373
|
-
rescue
|
374
378
|
msg = "Unable to make connection to Calabash Test Server at http://127.0.0.1:#{@server_port}/\n"
|
375
379
|
msg << "Please check the logcat output for more info about what happened\n"
|
376
380
|
raise msg
|
377
381
|
end
|
382
|
+
|
383
|
+
log "Checking client-server version match..."
|
384
|
+
response = perform_action('version')
|
385
|
+
unless response['success']
|
386
|
+
msg = ["Unable to obtain Test Server version. "]
|
387
|
+
msg << "Please delete your test_servers"
|
388
|
+
msg << "and re-run calabash-android run..."
|
389
|
+
msg_s = msg.join("\n")
|
390
|
+
log(msg_s)
|
391
|
+
raise msg_s
|
392
|
+
end
|
393
|
+
unless response['message'] == Calabash::Android::SERVER_VERSION
|
394
|
+
|
395
|
+
msg = ["Calabash Client and Test-server version mismatch."]
|
396
|
+
msg << "Client version #{Calabash::Android::VERSION}"
|
397
|
+
msg << "Test-server version #{response['message']}"
|
398
|
+
msg << "Expected Test-server version #{Calabash::Android::SERVER_VERSION}"
|
399
|
+
msg << "\n\nSolution:\n\n"
|
400
|
+
msg << "Please delete your test_servers"
|
401
|
+
msg << "and re-run calabash-android run..."
|
402
|
+
msg_s = msg.join("\n")
|
403
|
+
log(msg_s)
|
404
|
+
raise msg_s
|
405
|
+
end
|
406
|
+
log("Client and server versions match. Proceeding...")
|
407
|
+
|
408
|
+
|
378
409
|
end
|
379
410
|
|
380
411
|
def shutdown_test_server
|
@@ -408,7 +439,6 @@ module Operations
|
|
408
439
|
|
409
440
|
def screenshot_and_raise(msg)
|
410
441
|
screenshot_embed
|
411
|
-
sleep 5
|
412
442
|
raise(msg)
|
413
443
|
end
|
414
444
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Calabash
|
2
|
+
module Android
|
3
|
+
|
4
|
+
module WaitHelpers
|
5
|
+
|
6
|
+
|
7
|
+
class WaitError < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def wait_for(options_or_timeout=
|
12
|
+
{:timeout => 10,
|
13
|
+
:retry_frequency => 0.2,
|
14
|
+
:post_timeout => 0.1,
|
15
|
+
:timeout_message => "Timed out waiting...",
|
16
|
+
:screenshot_on_error => true}, &block)
|
17
|
+
#note Hash is preferred, number acceptable for backwards compat
|
18
|
+
timeout=options_or_timeout
|
19
|
+
post_timeout=0.1
|
20
|
+
retry_frequency=0.2
|
21
|
+
timeout_message = nil
|
22
|
+
screenshot_on_error = true
|
23
|
+
|
24
|
+
if options_or_timeout.is_a?(Hash)
|
25
|
+
timeout = options_or_timeout[:timeout] || 10
|
26
|
+
retry_frequency = options_or_timeout[:retry_frequency] || 0.2
|
27
|
+
post_timeout = options_or_timeout[:post_timeout] || 0.1
|
28
|
+
timeout_message = options_or_timeout[:timeout_message]
|
29
|
+
screenshot_on_error = options_or_timeout[:screenshot_on_error] || true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
Timeout::timeout(timeout, WaitError) do
|
34
|
+
sleep(retry_frequency) until yield
|
35
|
+
end
|
36
|
+
sleep(post_timeout) if post_timeout > 0
|
37
|
+
rescue WaitError => e
|
38
|
+
handle_error_with_options(e, timeout_message, screenshot_on_error)
|
39
|
+
rescue Exception => e
|
40
|
+
handle_error_with_options(e, nil, screenshot_on_error)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def wait_poll(opts, &block)
|
45
|
+
test = opts[:until]
|
46
|
+
if test.nil?
|
47
|
+
cond = opts[:until_exists]
|
48
|
+
raise "Must provide :until or :until_exists" unless cond
|
49
|
+
test = lambda { element_exists(cond) }
|
50
|
+
end
|
51
|
+
wait_for(opts) do
|
52
|
+
if test.call()
|
53
|
+
true
|
54
|
+
else
|
55
|
+
yield
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
#options for wait_for apply
|
62
|
+
def wait_for_elements_exist(elements_arr, options={})
|
63
|
+
options[:timeout_message] = options[:timeout_message] || "Timeout waiting for elements: #{elements_arr.join(",")}"
|
64
|
+
wait_for(options) do
|
65
|
+
elements_arr.all? { |q| element_exists(q) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#options for wait_for apply
|
70
|
+
def wait_for_elements_do_not_exist(elements_arr, options={})
|
71
|
+
options[:timeout_message] = options[:timeout_message] || "Timeout waiting for no elements matching: #{elements_arr.join(",")}"
|
72
|
+
wait_for(options) do
|
73
|
+
elements_arr.none? { |q| element_exists(q) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_error_with_options(ex, timeout_message, screenshot_on_error)
|
78
|
+
msg = (timeout_message || ex)
|
79
|
+
if ex
|
80
|
+
msg = "#{msg} (#{ex.class})"
|
81
|
+
end
|
82
|
+
if screenshot_on_error
|
83
|
+
screenshot_and_raise msg
|
84
|
+
else
|
85
|
+
raise msg
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
@@ -12,7 +12,7 @@ options {
|
|
12
12
|
package sh.calaba.instrumentationbackend.query.antlr;
|
13
13
|
}
|
14
14
|
|
15
|
-
@members {
|
15
|
+
@lexer::members {
|
16
16
|
public String getErrorMessage(RecognitionException e, String[] tokenNames)
|
17
17
|
{
|
18
18
|
List stack = getRuleInvocationStack(e, this.getClass().getName());
|
@@ -20,6 +20,7 @@ options {
|
|
20
20
|
if ( e instanceof NoViableAltException ) {
|
21
21
|
NoViableAltException nvae = (NoViableAltException)e;
|
22
22
|
msg = " no viable alt; token="+e.token+" (decision="+nvae.decisionNumber+" state "+nvae.stateNumber+")"+" decision=<<"+nvae.grammarDecisionDescription+">>";
|
23
|
+
throw new RuntimeException(msg, e);
|
23
24
|
}
|
24
25
|
else {
|
25
26
|
msg = super.getErrorMessage(e, tokenNames);
|
@@ -0,0 +1,19 @@
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.activity;
|
2
|
+
|
3
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
4
|
+
import sh.calaba.instrumentationbackend.Result;
|
5
|
+
import sh.calaba.instrumentationbackend.actions.Action;
|
6
|
+
|
7
|
+
public class FinishOpenedActivities implements Action {
|
8
|
+
|
9
|
+
@Override
|
10
|
+
public Result execute(String... args) {
|
11
|
+
InstrumentationBackend.solo.finishOpenedActivities();
|
12
|
+
return Result.successResult();
|
13
|
+
}
|
14
|
+
|
15
|
+
@Override
|
16
|
+
public String key() {
|
17
|
+
return "finish_opened_activities";
|
18
|
+
}
|
19
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.activity;
|
2
|
+
|
3
|
+
import java.util.ArrayList;
|
4
|
+
|
5
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
6
|
+
import sh.calaba.instrumentationbackend.Result;
|
7
|
+
import sh.calaba.instrumentationbackend.actions.Action;
|
8
|
+
import android.app.Activity;
|
9
|
+
|
10
|
+
public class GetOpenedActivities implements Action {
|
11
|
+
|
12
|
+
@Override
|
13
|
+
public Result execute(String... args) {
|
14
|
+
final ArrayList<Activity> activities = InstrumentationBackend.solo
|
15
|
+
.getAllOpenedActivities();
|
16
|
+
final ArrayList<String> opened = new ArrayList<String>(
|
17
|
+
activities.size());
|
18
|
+
|
19
|
+
for (final Activity activity : activities) {
|
20
|
+
opened.add(activity.getClass().getSimpleName());
|
21
|
+
}
|
22
|
+
|
23
|
+
// opened is attached to bonusInformation
|
24
|
+
return new Result(true, opened);
|
25
|
+
}
|
26
|
+
|
27
|
+
@Override
|
28
|
+
public String key() {
|
29
|
+
return "get_opened_activities";
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.activity;
|
2
|
+
|
3
|
+
import java.util.ArrayList;
|
4
|
+
|
5
|
+
import android.app.Activity;
|
6
|
+
import android.view.KeyEvent;
|
7
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
8
|
+
import sh.calaba.instrumentationbackend.Result;
|
9
|
+
import sh.calaba.instrumentationbackend.actions.Action;
|
10
|
+
import sh.calaba.instrumentationbackend.actions.Actions;
|
11
|
+
|
12
|
+
public class GoBackToActivity implements Action {
|
13
|
+
|
14
|
+
@Override
|
15
|
+
public Result execute(String... args) {
|
16
|
+
|
17
|
+
if (args == null) {
|
18
|
+
return Result
|
19
|
+
.failedResult("Target activity name must not be null.");
|
20
|
+
}
|
21
|
+
|
22
|
+
if (args.length != 1) {
|
23
|
+
return Result.failedResult("Must pass exactly one argument.");
|
24
|
+
}
|
25
|
+
|
26
|
+
if (args[0].trim().length() == 0) {
|
27
|
+
return Result.failedResult("Argument must not be whitespace.");
|
28
|
+
}
|
29
|
+
|
30
|
+
final String targetActivityName = args[0];
|
31
|
+
|
32
|
+
final ArrayList<Activity> activities = InstrumentationBackend.solo
|
33
|
+
.getAllOpenedActivities();
|
34
|
+
boolean success = false;
|
35
|
+
|
36
|
+
final ArrayList<String> opened = new ArrayList<String>(
|
37
|
+
activities.size());
|
38
|
+
|
39
|
+
for (final Activity activity : activities) {
|
40
|
+
final String name = activity.getClass().getSimpleName();
|
41
|
+
if (name.contentEquals(targetActivityName)) {
|
42
|
+
success = true;
|
43
|
+
}
|
44
|
+
opened.add(name);
|
45
|
+
}
|
46
|
+
|
47
|
+
if (!success) {
|
48
|
+
return new Result(false, opened);
|
49
|
+
}
|
50
|
+
|
51
|
+
while (!InstrumentationBackend.solo.getCurrentActivity().getClass()
|
52
|
+
.getSimpleName().contentEquals(targetActivityName)) {
|
53
|
+
try {
|
54
|
+
Actions.parentInstrumentation
|
55
|
+
.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
|
56
|
+
} catch (Exception exception) {
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
return new Result(true, opened);
|
61
|
+
}
|
62
|
+
|
63
|
+
@Override
|
64
|
+
public String key() {
|
65
|
+
return "go_back_to_activity";
|
66
|
+
}
|
67
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
package sh.calaba.instrumentationbackend.actions.gestures;
|
2
|
+
|
3
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
4
|
+
import sh.calaba.instrumentationbackend.Result;
|
5
|
+
import sh.calaba.instrumentationbackend.actions.Action;
|
6
|
+
|
7
|
+
public class DragCoordinates implements Action {
|
8
|
+
|
9
|
+
@Override
|
10
|
+
public Result execute(String... args) {
|
11
|
+
|
12
|
+
Float fromX = new Float(args[0]);
|
13
|
+
Float fromY = new Float(args[1]);
|
14
|
+
Float toX = new Float(args[2]);
|
15
|
+
Float toY = new Float(args[3]);
|
16
|
+
Integer stepCount = 40;
|
17
|
+
|
18
|
+
InstrumentationBackend.solo.drag(fromX, toX, fromY, toY, stepCount);
|
19
|
+
|
20
|
+
return Result.successResult();
|
21
|
+
}
|
22
|
+
|
23
|
+
@Override
|
24
|
+
public String key() {
|
25
|
+
return "drag_coordinates";
|
26
|
+
}
|
27
|
+
|
28
|
+
}
|