honeydew 0.20.0 → 0.21.0
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/lib/honeydew/device_matchers.rb +8 -0
- data/lib/honeydew/version.rb +1 -1
- data/server/pom.xml +7 -6
- data/server/src/main/java/com/amplify/honeydew_server/Action.java +6 -4
- data/server/src/main/java/com/amplify/honeydew_server/ActionsExecutor.java +14 -18
- data/server/src/main/java/com/amplify/honeydew_server/TestRunner.java +5 -4
- data/server/src/main/java/com/amplify/honeydew_server/actions/AbstractChildCountAction.java +33 -0
- data/server/src/main/java/com/amplify/honeydew_server/actions/IsChildCountEqualTo.java +5 -13
- data/server/src/main/java/com/amplify/honeydew_server/actions/IsChildCountGreaterThan.java +16 -0
- data/server/src/main/java/com/amplify/honeydew_server/httpd/RemoteCommandReceiver.java +34 -19
- data/server/target/honeydew-server-0.21.0.jar +0 -0
- metadata +5 -3
- data/server/target/honeydew-server-0.20.0.jar +0 -0
@@ -24,6 +24,7 @@ module Honeydew
|
|
24
24
|
perform_assertion :is_button_present, :text => button_text
|
25
25
|
end
|
26
26
|
|
27
|
+
# TODO: Look for a better way to express these similar looking matchers
|
27
28
|
def has_child_count?(parent_element_description, child_element_description, child_count)
|
28
29
|
perform_assertion :is_child_count_equal_to,
|
29
30
|
:parent_description => parent_element_description,
|
@@ -31,6 +32,13 @@ module Honeydew
|
|
31
32
|
:child_count => child_count
|
32
33
|
end
|
33
34
|
|
35
|
+
def has_child_count_greater_than?(parent_element_description, child_element_description, child_count)
|
36
|
+
perform_assertion :is_child_count_greater_than,
|
37
|
+
:parent_description => parent_element_description,
|
38
|
+
:child_description => child_element_description,
|
39
|
+
:child_count => child_count
|
40
|
+
end
|
41
|
+
|
34
42
|
def has_element_with_nested_text?(parent_description, child_text)
|
35
43
|
perform_assertion :is_element_with_nested_text_present,
|
36
44
|
:parent_description => parent_description,
|
data/lib/honeydew/version.rb
CHANGED
data/server/pom.xml
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
<groupId>amplify</groupId>
|
7
7
|
<artifactId>honeydew-server</artifactId>
|
8
|
-
<version>0.
|
8
|
+
<version>0.21.0</version>
|
9
9
|
<packaging>jar</packaging>
|
10
10
|
<name>Honeydew Android Server</name>
|
11
11
|
|
@@ -72,11 +72,12 @@
|
|
72
72
|
<groupId>fi.iki.elonen</groupId>
|
73
73
|
<artifactId>nanohttpd</artifactId>
|
74
74
|
<version>2.0.3</version>
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
75
|
+
<exclusions>
|
76
|
+
<exclusion>
|
77
|
+
<groupId>org.apache.httpcomponents</groupId>
|
78
|
+
<artifactId>httpclient</artifactId>
|
79
|
+
</exclusion>
|
80
|
+
</exclusions>
|
80
81
|
</dependency>
|
81
82
|
</dependencies>
|
82
83
|
|
@@ -9,6 +9,8 @@ import java.util.Map;
|
|
9
9
|
public abstract class Action {
|
10
10
|
protected final UiDevice uiDevice;
|
11
11
|
|
12
|
+
protected final String TAG = getClass().getSimpleName();
|
13
|
+
|
12
14
|
public Action(UiDevice uiDevice) {
|
13
15
|
this.uiDevice = uiDevice;
|
14
16
|
}
|
@@ -39,17 +41,17 @@ public abstract class Action {
|
|
39
41
|
protected ViewSelector getViewSelector(Map<String, Object> arguments) {
|
40
42
|
ViewSelector viewSelector = new ViewSelector();
|
41
43
|
|
42
|
-
Log.d(
|
44
|
+
Log.d(TAG, arguments.toString());
|
43
45
|
if (arguments.containsKey("type")) {
|
44
|
-
Log.d(
|
46
|
+
Log.d(TAG, String.format("Type %s", (String) arguments.get("type")));
|
45
47
|
viewSelector.withType((String) arguments.get("type"));
|
46
48
|
}
|
47
49
|
if (arguments.containsKey("text")) {
|
48
|
-
Log.d(
|
50
|
+
Log.d(TAG, String.format("Text %s", (String) arguments.get("text")));
|
49
51
|
viewSelector.withText((String) arguments.get("text"));
|
50
52
|
}
|
51
53
|
if (arguments.containsKey("description")) {
|
52
|
-
Log.d(
|
54
|
+
Log.d(TAG, String.format("Description %s", (String) arguments.get("description")));
|
53
55
|
viewSelector.withDescription((String) arguments.get("description"));
|
54
56
|
}
|
55
57
|
return viewSelector;
|
@@ -8,24 +8,26 @@ import com.google.common.base.Stopwatch;
|
|
8
8
|
|
9
9
|
import java.lang.reflect.Constructor;
|
10
10
|
import java.lang.reflect.InvocationTargetException;
|
11
|
-
import java.util.
|
11
|
+
import java.util.Map;
|
12
12
|
import java.util.Set;
|
13
|
-
import java.util.concurrent.TimeUnit;
|
14
13
|
|
14
|
+
import static com.google.common.collect.Maps.newHashMap;
|
15
15
|
import static com.google.common.collect.Sets.newHashSet;
|
16
|
+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
16
17
|
|
17
18
|
public class ActionsExecutor {
|
18
19
|
|
19
|
-
|
20
|
-
private final
|
20
|
+
protected final UiDevice uiDevice;
|
21
|
+
private final Map<String, Action> actions;
|
22
|
+
private static final String TAG = ActionsExecutor.class.getName();
|
21
23
|
|
22
24
|
public ActionsExecutor(UiDevice uiDevice) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
|
23
25
|
this.uiDevice = uiDevice;
|
24
|
-
actions =
|
26
|
+
actions = newHashMap();
|
25
27
|
for (Class<? extends Action> actionClass : allActionClasses()) {
|
26
28
|
Constructor<? extends Action> constructor = actionClass.getConstructor(UiDevice.class);
|
27
|
-
Action action = constructor.newInstance(
|
28
|
-
Log.d(
|
29
|
+
Action action = constructor.newInstance(this.uiDevice);
|
30
|
+
Log.d(TAG, String.format("Registering action: %s", action.name()));
|
29
31
|
actions.put(action.name(), action);
|
30
32
|
}
|
31
33
|
}
|
@@ -35,12 +37,9 @@ public class ActionsExecutor {
|
|
35
37
|
try {
|
36
38
|
Action action = actions.get(actionName);
|
37
39
|
if (action == null) {
|
38
|
-
return new Result("Action: " + actionName + " does not
|
40
|
+
return new Result("Action: " + actionName + " does not exist");
|
39
41
|
}
|
40
|
-
|
41
|
-
Result result = executeWithStopwatch(command, action);
|
42
|
-
|
43
|
-
return result;
|
42
|
+
return executeWithStopwatch(command, action);
|
44
43
|
} catch (Exception e) {
|
45
44
|
return new Result("Exception, on calling " + actionName, e);
|
46
45
|
}
|
@@ -52,8 +51,8 @@ public class ActionsExecutor {
|
|
52
51
|
Result result = action.execute(command.getArguments());
|
53
52
|
|
54
53
|
timer.stop();
|
55
|
-
Log.i(
|
56
|
-
command.getAction(), timer.elapsed(
|
54
|
+
Log.i(TAG, String.format("action '%s' took %d ms to execute on the tablet",
|
55
|
+
command.getAction(), timer.elapsed(MILLISECONDS)));
|
57
56
|
return result;
|
58
57
|
}
|
59
58
|
|
@@ -68,6 +67,7 @@ public class ActionsExecutor {
|
|
68
67
|
actionClasses.add(IsButtonPresent.class);
|
69
68
|
actionClasses.add(IsElementWithNestedTextPresent.class);
|
70
69
|
actionClasses.add(IsChildCountEqualTo.class);
|
70
|
+
actionClasses.add(IsChildCountGreaterThan.class);
|
71
71
|
actionClasses.add(Click.class);
|
72
72
|
actionClasses.add(ClickAndWaitForNewWindow.class);
|
73
73
|
actionClasses.add(LongClick.class);
|
@@ -84,8 +84,4 @@ public class ActionsExecutor {
|
|
84
84
|
actionClasses.add(ScrollToTextByIndex.class);
|
85
85
|
return actionClasses;
|
86
86
|
}
|
87
|
-
|
88
|
-
private UiDevice getUiDevice() {
|
89
|
-
return uiDevice;
|
90
|
-
}
|
91
87
|
}
|
@@ -5,21 +5,22 @@ import android.view.KeyEvent;
|
|
5
5
|
import com.amplify.honeydew_server.httpd.RemoteCommandReceiver;
|
6
6
|
import com.android.uiautomator.core.UiDevice;
|
7
7
|
import com.android.uiautomator.testrunner.UiAutomatorTestCase;
|
8
|
-
import fi.iki.elonen.ServerRunner;
|
9
8
|
|
10
9
|
public class TestRunner extends UiAutomatorTestCase {
|
11
10
|
|
11
|
+
private static final String TAG = TestRunner.class.getSimpleName();
|
12
|
+
|
12
13
|
public void testRemoteLoop() throws Exception {
|
13
|
-
Log.d(
|
14
|
+
Log.d(TAG, "Starting honeydew-server...");
|
14
15
|
|
15
16
|
UiDevice uiDevice = getUiDevice();
|
16
17
|
uiDevice.wakeUp();
|
17
18
|
unlockEmulator();
|
18
19
|
|
19
20
|
RemoteCommandReceiver remoteCommandReceiver = new RemoteCommandReceiver(new ActionsExecutor(uiDevice));
|
20
|
-
|
21
|
+
remoteCommandReceiver.start();
|
21
22
|
|
22
|
-
Log.d(
|
23
|
+
Log.d(TAG, "honeydew-server started, waiting for commands");
|
23
24
|
while(true) {
|
24
25
|
Thread.sleep(1000);
|
25
26
|
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
package com.amplify.honeydew_server.actions;
|
2
|
+
|
3
|
+
import android.util.Log;
|
4
|
+
import com.amplify.honeydew_server.Action;
|
5
|
+
import com.amplify.honeydew_server.Result;
|
6
|
+
import com.android.uiautomator.core.UiCollection;
|
7
|
+
import com.android.uiautomator.core.UiDevice;
|
8
|
+
import com.android.uiautomator.core.UiObjectNotFoundException;
|
9
|
+
import com.android.uiautomator.core.UiSelector;
|
10
|
+
|
11
|
+
import java.util.Map;
|
12
|
+
|
13
|
+
public abstract class AbstractChildCountAction extends Action {
|
14
|
+
public AbstractChildCountAction(UiDevice uiDevice) {
|
15
|
+
super(uiDevice);
|
16
|
+
}
|
17
|
+
|
18
|
+
protected abstract boolean isTrue(int childCount, int count);
|
19
|
+
|
20
|
+
@Override
|
21
|
+
public final Result execute(Map<String, Object> arguments) throws UiObjectNotFoundException {
|
22
|
+
Log.d(TAG, "Entering " + TAG);
|
23
|
+
String parentDescription = (String) arguments.get("parent_description");
|
24
|
+
String childDescription = (String) arguments.get("child_description");
|
25
|
+
int expectedCount = ((Double) arguments.get("child_count")).intValue();
|
26
|
+
|
27
|
+
UiCollection parentElement = new UiCollection(new UiSelector().description(parentDescription));
|
28
|
+
int actualCount = parentElement.getChildCount(new UiSelector().description(childDescription));
|
29
|
+
Log.d(TAG, "Actual count was: " + actualCount + " when " + expectedCount + " was expected");
|
30
|
+
return new Result(isTrue(expectedCount, actualCount), "Actual count was: " + actualCount + " when " + expectedCount + " was expected");
|
31
|
+
|
32
|
+
}
|
33
|
+
}
|
@@ -1,25 +1,17 @@
|
|
1
1
|
package com.amplify.honeydew_server.actions;
|
2
2
|
|
3
|
-
import com.
|
4
|
-
import com.android.uiautomator.core.*;
|
3
|
+
import com.android.uiautomator.core.UiDevice;
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
public class IsChildCountEqualTo extends Action {
|
5
|
+
public class IsChildCountEqualTo extends AbstractChildCountAction {
|
9
6
|
|
10
7
|
public IsChildCountEqualTo(UiDevice uiDevice) {
|
11
8
|
super(uiDevice);
|
12
9
|
}
|
13
10
|
|
14
11
|
@Override
|
15
|
-
|
16
|
-
|
17
|
-
String childDescription = (String) arguments.get("child_description");
|
18
|
-
int childCount = (Integer) arguments.get("child_count");
|
19
|
-
|
20
|
-
UiCollection parentElement = new UiCollection(new UiSelector().description(parentDescription));
|
21
|
-
int count = parentElement.getChildCount(new UiSelector().description(childDescription));
|
22
|
-
return new Result(childCount == count, "Actual count was: " + count + " when " + childCount + " was expected");
|
12
|
+
protected boolean isTrue(int childCount, int count) {
|
13
|
+
return childCount == count;
|
23
14
|
}
|
24
15
|
|
16
|
+
|
25
17
|
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
package com.amplify.honeydew_server.actions;
|
2
|
+
|
3
|
+
import com.android.uiautomator.core.UiDevice;
|
4
|
+
|
5
|
+
public class IsChildCountGreaterThan extends AbstractChildCountAction {
|
6
|
+
|
7
|
+
public IsChildCountGreaterThan(UiDevice uiDevice) {
|
8
|
+
super(uiDevice);
|
9
|
+
}
|
10
|
+
|
11
|
+
@Override
|
12
|
+
protected boolean isTrue(int expectedCount, int actualCount) {
|
13
|
+
return actualCount > expectedCount;
|
14
|
+
}
|
15
|
+
|
16
|
+
}
|
@@ -1,7 +1,9 @@
|
|
1
1
|
package com.amplify.honeydew_server.httpd;
|
2
2
|
|
3
3
|
import android.util.Log;
|
4
|
-
import com.amplify.honeydew_server
|
4
|
+
import com.amplify.honeydew_server.ActionsExecutor;
|
5
|
+
import com.amplify.honeydew_server.Command;
|
6
|
+
import com.amplify.honeydew_server.Result;
|
5
7
|
import com.google.gson.Gson;
|
6
8
|
import com.google.gson.reflect.TypeToken;
|
7
9
|
import fi.iki.elonen.NanoHTTPD;
|
@@ -10,15 +12,20 @@ import java.io.IOException;
|
|
10
12
|
import java.lang.reflect.Type;
|
11
13
|
import java.util.Map;
|
12
14
|
|
15
|
+
import static java.lang.String.format;
|
16
|
+
|
13
17
|
public class RemoteCommandReceiver extends NanoHTTPD {
|
14
18
|
|
15
19
|
private static final String PLAIN_TEXT = "plain/text";
|
20
|
+
public static final Type ARGUMENTS_COLLECTION_TYPE = new TypeToken<Map<String, Object>>() {
|
21
|
+
}.getType();
|
16
22
|
private final ActionsExecutor actionsExecutor;
|
17
23
|
|
18
|
-
private final Response
|
19
|
-
private final Response
|
20
|
-
private final Response
|
24
|
+
private static final Response OK = new Response("honeydew-server is awaiting commands");
|
25
|
+
private static final Response TERMINATED = new Response("honeydew-server is stopping");
|
26
|
+
private final Response BAD_REQUEST = new Response(Response.Status.BAD_REQUEST, PLAIN_TEXT, "Unsupported command");
|
21
27
|
|
28
|
+
private static final String TAG = RemoteCommandReceiver.class.getSimpleName();
|
22
29
|
private final Gson jsonParser = new Gson();
|
23
30
|
|
24
31
|
public RemoteCommandReceiver(ActionsExecutor actionsExecutor) throws IOException, InterruptedException {
|
@@ -29,35 +36,43 @@ public class RemoteCommandReceiver extends NanoHTTPD {
|
|
29
36
|
@Override
|
30
37
|
public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> params, Map<String, String> files) {
|
31
38
|
if (method == Method.POST && uri.equalsIgnoreCase("/terminate")) {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
} else if (method == Method.POST && uri.equalsIgnoreCase("/command")) {
|
39
|
+
return terminate();
|
40
|
+
}
|
41
|
+
if (method == Method.POST && uri.equalsIgnoreCase("/command")) {
|
36
42
|
return performCommand(params);
|
37
|
-
} else if (method == Method.GET && uri.equalsIgnoreCase("/status")) {
|
38
|
-
Log.d(getClass().getName(), "Got status request, responding OK");
|
39
|
-
return statusOkResponse;
|
40
43
|
}
|
44
|
+
if (method == Method.GET && uri.equalsIgnoreCase("/status")) {
|
45
|
+
return status();
|
46
|
+
}
|
47
|
+
|
48
|
+
return BAD_REQUEST;
|
49
|
+
}
|
50
|
+
|
51
|
+
private Response status() {
|
52
|
+
Log.d(TAG, "Got status request, responding OK");
|
53
|
+
return OK;
|
54
|
+
}
|
41
55
|
|
42
|
-
|
56
|
+
private Response terminate() {
|
57
|
+
Log.d(TAG, "Got terminate request, finishing up...");
|
58
|
+
stop();
|
59
|
+
return TERMINATED;
|
43
60
|
}
|
44
61
|
|
45
62
|
private Response performCommand(Map<String, String> params) {
|
46
63
|
String action = params.get("action");
|
47
64
|
String argumentJson = params.get("arguments");
|
48
|
-
|
49
|
-
Map<String, Object> arguments = jsonParser.fromJson(argumentJson, argumentCollectionType);
|
65
|
+
Map<String, Object> arguments = jsonParser.fromJson(argumentJson, ARGUMENTS_COLLECTION_TYPE);
|
50
66
|
|
51
|
-
Log.i(
|
67
|
+
Log.i(TAG, format("Performing action %s: %s for %s", action, argumentJson, params.toString()));
|
52
68
|
try {
|
53
69
|
Result result = actionsExecutor.execute(new Command(action, arguments));
|
54
70
|
if (result.success) {
|
55
71
|
return new Response(result.description);
|
56
|
-
} else {
|
57
|
-
return new Response(Response.Status.NO_CONTENT, null, (String) null);
|
58
72
|
}
|
59
|
-
|
60
|
-
|
73
|
+
return new Response(Response.Status.NO_CONTENT, null, (String) null);
|
74
|
+
} catch (Exception exception) {
|
75
|
+
Log.e(TAG, format("Server error while processing command %s: %s", action, exception.toString()));
|
61
76
|
return new Response(Response.Status.INTERNAL_ERROR, PLAIN_TEXT, exception.getMessage());
|
62
77
|
}
|
63
78
|
}
|
Binary file
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: honeydew
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.21.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Selvakumar Natesan
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-08-
|
15
|
+
date: 2013-08-22 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: activesupport
|
@@ -182,6 +182,7 @@ files:
|
|
182
182
|
- server/src/main/java/com/amplify/honeydew_server/Command.java
|
183
183
|
- server/src/main/java/com/amplify/honeydew_server/Result.java
|
184
184
|
- server/src/main/java/com/amplify/honeydew_server/TestRunner.java
|
185
|
+
- server/src/main/java/com/amplify/honeydew_server/actions/AbstractChildCountAction.java
|
185
186
|
- server/src/main/java/com/amplify/honeydew_server/actions/Click.java
|
186
187
|
- server/src/main/java/com/amplify/honeydew_server/actions/ClickAndWaitForNewWindow.java
|
187
188
|
- server/src/main/java/com/amplify/honeydew_server/actions/DumpWindowHierarchy.java
|
@@ -189,6 +190,7 @@ files:
|
|
189
190
|
- server/src/main/java/com/amplify/honeydew_server/actions/InspectOptionInSettingsMenu.java
|
190
191
|
- server/src/main/java/com/amplify/honeydew_server/actions/IsButtonPresent.java
|
191
192
|
- server/src/main/java/com/amplify/honeydew_server/actions/IsChildCountEqualTo.java
|
193
|
+
- server/src/main/java/com/amplify/honeydew_server/actions/IsChildCountGreaterThan.java
|
192
194
|
- server/src/main/java/com/amplify/honeydew_server/actions/IsElementWithNestedTextPresent.java
|
193
195
|
- server/src/main/java/com/amplify/honeydew_server/actions/IsOptionInSettingsMenuDisabled.java
|
194
196
|
- server/src/main/java/com/amplify/honeydew_server/actions/IsOptionInSettingsMenuEnabled.java
|
@@ -210,7 +212,7 @@ files:
|
|
210
212
|
- spec/honeydew/device_matchers_spec.rb
|
211
213
|
- spec/honeydew/device_spec.rb
|
212
214
|
- spec/spec_helper.rb
|
213
|
-
- server/target/honeydew-server-0.
|
215
|
+
- server/target/honeydew-server-0.21.0.jar
|
214
216
|
homepage:
|
215
217
|
licenses: []
|
216
218
|
post_install_message:
|
Binary file
|