testautoa 0.4.0 → 0.4.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/Rakefile +7 -7
- data/bin/calabash-android +8 -1
- data/bin/calabash-android-build.rb +1 -1
- data/bin/calabash-android-console.rb +4 -4
- data/bin/calabash-android-run.rb +2 -10
- data/bin/testautoa +461 -0
- data/calabash-android.gemspec +3 -1
- data/features-skeleton/support/app_life_cycle_hooks.rb +0 -1
- data/irbrc +3 -1
- data/lib/calabash-android/helpers.rb +45 -17
- data/lib/calabash-android/lib/TestServer.apk +0 -0
- data/lib/calabash-android/lib/unsign.jar +0 -0
- data/lib/calabash-android/operations.rb +150 -66
- data/lib/calabash-android/steps/list_steps.rb +1 -1
- data/lib/calabash-android/steps/time_picker_steps.rb +1 -1
- data/lib/calabash-android/touch_helpers.rb +9 -0
- data/lib/calabash-android/version.rb +1 -1
- data/lib/calabash-android/wait_helpers.rb +93 -0
- data/test-server/AndroidManifest.xml +2 -0
- data/test-server/build.xml +1 -0
- data/test-server/instrumentation-backend/.classpath +0 -1
- data/test-server/instrumentation-backend/AndroidManifest.xml +1 -1
- data/test-server/instrumentation-backend/antlr/UIQuery.g +48 -5
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Command.java +4 -3
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/FranklyResult.java +95 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +7 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/HttpServer.java +14 -29
- 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/location/FakeGPSLocation.java +13 -10
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/LeftKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/RightKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/UpKey.java +24 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/version/Version.java +31 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/CalabashChromeClient.java +131 -36
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpBodyHtml.java +38 -18
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/DumpHtml.java +38 -16
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/EnterTextByCssSelector.java +94 -66
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteAsyncJavascript.java +55 -33
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteJavascript.java +53 -31
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +44 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +52 -27
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +39 -32
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ScrollTo.java +56 -41
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetPropertyByCssSelector.java +50 -25
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +19 -22
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/CompletedFuture.java +40 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java +222 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Operation.java +7 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/PropertyOperation.java +56 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java +151 -43
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQuery.tokens +19 -12
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQueryResultVoid.java +22 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +41 -11
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryLexer.java +1010 -242
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryParser.java +406 -98
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/BeginsWithRelation.java +45 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +54 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ContainsRelation.java +41 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/EndsWithRelation.java +42 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/LikeRelation.java +79 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/PartialFutureList.java +100 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryAST.java +1 -1
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTClassName.java +54 -25
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +147 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicateRelation.java +5 -0
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +153 -89
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryDirection.java +12 -2
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryEvaluator.java +58 -141
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +162 -7
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryVisibility.java +32 -0
- metadata +130 -97
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Query.java +0 -24
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/Touch.java +0 -44
- data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQuery.tokens +0 -10
- data/test-server/instrumentation-backend/tests/sh/calaba/instrumentationbackend/query/tests/UIQueryTest.java +0 -134
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
package sh.calaba.instrumentationbackend.query;
|
|
2
|
+
|
|
3
|
+
import java.lang.reflect.Method;
|
|
4
|
+
import java.util.List;
|
|
5
|
+
import java.util.concurrent.atomic.AtomicReference;
|
|
6
|
+
|
|
7
|
+
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
|
8
|
+
|
|
9
|
+
public class InvocationOperation implements Operation {
|
|
10
|
+
|
|
11
|
+
private final String methodName;
|
|
12
|
+
@SuppressWarnings("rawtypes")
|
|
13
|
+
private final List arguments;
|
|
14
|
+
private final Class<?>[] classes;
|
|
15
|
+
private final Class<?>[] classesWithCharseq;
|
|
16
|
+
|
|
17
|
+
@SuppressWarnings("rawtypes")
|
|
18
|
+
public InvocationOperation(String methodName, List arguments) {
|
|
19
|
+
this.methodName = methodName;
|
|
20
|
+
this.arguments = arguments;
|
|
21
|
+
this.classes = extractArgumentTypes(false);
|
|
22
|
+
this.classesWithCharseq = extractArgumentTypes(true);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@SuppressWarnings("rawtypes")
|
|
26
|
+
@Override
|
|
27
|
+
public Object apply(final Object o) throws Exception {
|
|
28
|
+
final AtomicReference ref = new AtomicReference();
|
|
29
|
+
final AtomicReference<Exception> refEx = new AtomicReference<Exception>();
|
|
30
|
+
|
|
31
|
+
InstrumentationBackend.instrumentation.runOnMainSync(new Runnable() {
|
|
32
|
+
|
|
33
|
+
@SuppressWarnings("unchecked")
|
|
34
|
+
@Override
|
|
35
|
+
public void run() {
|
|
36
|
+
try {
|
|
37
|
+
Method method = o.getClass().getMethod(InvocationOperation.this.methodName, InvocationOperation.this.classes);
|
|
38
|
+
method.setAccessible(true);
|
|
39
|
+
try {
|
|
40
|
+
Object result;
|
|
41
|
+
if( method.getReturnType().equals(Void.TYPE)){
|
|
42
|
+
invokeMethod(o, method);
|
|
43
|
+
result = "<VOID>";
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
result = invokeMethod(o, method);
|
|
47
|
+
}
|
|
48
|
+
ref.set(result);
|
|
49
|
+
return;
|
|
50
|
+
} catch (Exception e) {
|
|
51
|
+
refEx.set(e);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
} catch (NoSuchMethodException e) {
|
|
55
|
+
try {
|
|
56
|
+
Method method = o.getClass().getMethod(InvocationOperation.this.methodName, InvocationOperation.this.classesWithCharseq);
|
|
57
|
+
method.setAccessible(true);
|
|
58
|
+
try {
|
|
59
|
+
Object result;
|
|
60
|
+
if( method.getReturnType().equals(Void.TYPE)){
|
|
61
|
+
invokeMethod(o, method);
|
|
62
|
+
result = "<VOID>";
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
result = invokeMethod(o, method);
|
|
66
|
+
}
|
|
67
|
+
ref.set(result);
|
|
68
|
+
return;
|
|
69
|
+
} catch (Exception ee) {
|
|
70
|
+
refEx.set(e);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
} catch (NoSuchMethodException ee) {
|
|
74
|
+
System.out.println("Method not found with correct argument types. Trying to type convert.");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//Warning: Slow path
|
|
78
|
+
Method[] methods = o.getClass().getMethods();
|
|
79
|
+
for (Method m : methods) {
|
|
80
|
+
if (m.getName().equals(InvocationOperation.this.methodName)) {
|
|
81
|
+
Class<?>[] parameterTypes = m.getParameterTypes();
|
|
82
|
+
if (parameterTypes.length == InvocationOperation.this.classes.length && areArgumentsConvertibleTo(parameterTypes)) {
|
|
83
|
+
try {
|
|
84
|
+
Object result;
|
|
85
|
+
if( m.getReturnType().equals(Void.TYPE)){
|
|
86
|
+
invokeMethod(o, m);
|
|
87
|
+
result = "<VOID>";
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
result = invokeMethod(o, m);
|
|
91
|
+
}
|
|
92
|
+
ref.set(result);
|
|
93
|
+
return;
|
|
94
|
+
} catch (Exception e) {
|
|
95
|
+
e.printStackTrace();
|
|
96
|
+
refEx.set(e);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (refEx.get() != null) {
|
|
109
|
+
throw refEx.get();
|
|
110
|
+
}
|
|
111
|
+
return ref.get();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@SuppressWarnings("rawtypes")
|
|
115
|
+
private Class[] extractArgumentTypes(boolean convertStringCharSeq) {
|
|
116
|
+
Class[] types = new Class[arguments.size()];
|
|
117
|
+
for (int i=0;i<arguments.size(); i++) {
|
|
118
|
+
Object o = arguments.get(i);
|
|
119
|
+
if (o != null) {
|
|
120
|
+
Class<?> c = o.getClass();
|
|
121
|
+
if (convertStringCharSeq && c.equals(String.class)) {
|
|
122
|
+
c = CharSequence.class;//Android API specific optimization
|
|
123
|
+
}
|
|
124
|
+
types[i] = mapToPrimitiveClass(c);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
types[i] = null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return types;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private Class<?> mapToPrimitiveClass(Class<?> c) {
|
|
134
|
+
if (c.equals(Integer.class)) {
|
|
135
|
+
return int.class;
|
|
136
|
+
}
|
|
137
|
+
else if (c.equals(Float.class)) {
|
|
138
|
+
return float.class;
|
|
139
|
+
}
|
|
140
|
+
else if (c.equals(Double.class)) {
|
|
141
|
+
return double.class;
|
|
142
|
+
}
|
|
143
|
+
else if (c.equals(Boolean.class)) {
|
|
144
|
+
return boolean.class;
|
|
145
|
+
}
|
|
146
|
+
return c;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
//parameterType.length == this.classes.length
|
|
151
|
+
//Note: right now we don't do any clever mapping, we
|
|
152
|
+
//only use Java's sub type relation: isAssigableFrom
|
|
153
|
+
private boolean areArgumentsConvertibleTo(Class<?>[] parameterTypes) {
|
|
154
|
+
for (int i=0; i < parameterTypes.length; i++) {
|
|
155
|
+
Class<?> typei = parameterTypes[i];
|
|
156
|
+
if (this.arguments.get(i) == null) {
|
|
157
|
+
if (typei.isPrimitive()) {
|
|
158
|
+
//Can't pass null as primitive
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
continue; //can always pass null (unless primitive)
|
|
162
|
+
}
|
|
163
|
+
if (typei.isPrimitive() && typei.equals(mapToPrimitiveClass(this.classes[i]))) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!typei.isAssignableFrom(this.classes[i])) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private Object invokeMethod(Object o, Method m) throws Exception {
|
|
174
|
+
List<?> a = this.arguments;
|
|
175
|
+
int size = a.size();
|
|
176
|
+
switch(size) {
|
|
177
|
+
case 0:
|
|
178
|
+
return m.invoke(o);
|
|
179
|
+
case 1:
|
|
180
|
+
return m.invoke(o,a.get(0));
|
|
181
|
+
case 2:
|
|
182
|
+
return m.invoke(o,a.get(0),a.get(1));
|
|
183
|
+
case 3:
|
|
184
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2));
|
|
185
|
+
case 4:
|
|
186
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3));
|
|
187
|
+
case 5:
|
|
188
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4));
|
|
189
|
+
case 6:
|
|
190
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5));
|
|
191
|
+
case 7:
|
|
192
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6));
|
|
193
|
+
case 8:
|
|
194
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7));
|
|
195
|
+
case 9:
|
|
196
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8));
|
|
197
|
+
case 10:
|
|
198
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9));
|
|
199
|
+
case 11:
|
|
200
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10));
|
|
201
|
+
case 12:
|
|
202
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10),a.get(11));
|
|
203
|
+
case 13:
|
|
204
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10),a.get(11),a.get(12));
|
|
205
|
+
case 14:
|
|
206
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10),a.get(11),a.get(12),a.get(13));
|
|
207
|
+
case 15:
|
|
208
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10),a.get(11),a.get(12),a.get(13),a.get(14));
|
|
209
|
+
case 16:
|
|
210
|
+
return m.invoke(o,a.get(0),a.get(1),a.get(2),a.get(3),a.get(4),a.get(5),a.get(6),a.get(7),a.get(8),a.get(9),a.get(10),a.get(11),a.get(12),a.get(13),a.get(14),a.get(15));
|
|
211
|
+
}
|
|
212
|
+
throw new UnsupportedOperationException("Method with more than 16 arguments are not supported");
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@Override
|
|
218
|
+
public String getName() {
|
|
219
|
+
return "InvocationOp["+this.methodName+", arguments = " + this.arguments + "]";
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package sh.calaba.instrumentationbackend.query;
|
|
2
|
+
|
|
3
|
+
import java.lang.reflect.Method;
|
|
4
|
+
import java.util.Map;
|
|
5
|
+
|
|
6
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryUtils;
|
|
7
|
+
import android.view.View;
|
|
8
|
+
|
|
9
|
+
public class PropertyOperation implements Operation {
|
|
10
|
+
|
|
11
|
+
public final String propertyName;
|
|
12
|
+
|
|
13
|
+
public PropertyOperation(String propertyName) {
|
|
14
|
+
super();
|
|
15
|
+
this.propertyName = propertyName;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@SuppressWarnings({ "rawtypes" })
|
|
20
|
+
@Override
|
|
21
|
+
public Object apply(Object o) throws Exception {
|
|
22
|
+
if (o instanceof Map) {
|
|
23
|
+
Map objAsMap = (Map) o;
|
|
24
|
+
if (objAsMap.containsKey(propertyName)) {
|
|
25
|
+
return objAsMap.get(propertyName);
|
|
26
|
+
} else {
|
|
27
|
+
return UIQueryResultVoid.instance.asMap(
|
|
28
|
+
propertyName, o, "No key for "
|
|
29
|
+
+ propertyName + ". Keys: "
|
|
30
|
+
+ (objAsMap.keySet().toString()));
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
if (o instanceof View && "id".equals(propertyName)) {
|
|
34
|
+
return UIQueryUtils.getId((View) o);
|
|
35
|
+
} else {
|
|
36
|
+
Method m = UIQueryUtils
|
|
37
|
+
.hasProperty(o, propertyName);
|
|
38
|
+
if (m != null) {
|
|
39
|
+
return m.invoke(o);
|
|
40
|
+
} else {
|
|
41
|
+
return UIQueryResultVoid.instance
|
|
42
|
+
.asMap(propertyName, o,
|
|
43
|
+
"NO accessor for "
|
|
44
|
+
+ propertyName);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Override
|
|
52
|
+
public String getName() {
|
|
53
|
+
return "Property["+this.propertyName+"]";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java
CHANGED
|
@@ -6,64 +6,172 @@ import java.util.ArrayList;
|
|
|
6
6
|
import java.util.Collections;
|
|
7
7
|
import java.util.HashSet;
|
|
8
8
|
import java.util.List;
|
|
9
|
+
import java.util.Map;
|
|
9
10
|
import java.util.Set;
|
|
10
11
|
|
|
12
|
+
import org.antlr.runtime.ANTLRStringStream;
|
|
13
|
+
import org.antlr.runtime.CommonTokenStream;
|
|
14
|
+
import org.antlr.runtime.RecognitionException;
|
|
15
|
+
import org.antlr.runtime.tree.CommonTree;
|
|
16
|
+
|
|
17
|
+
import sh.calaba.instrumentationbackend.query.antlr.UIQueryLexer;
|
|
18
|
+
import sh.calaba.instrumentationbackend.query.antlr.UIQueryParser;
|
|
19
|
+
import sh.calaba.instrumentationbackend.query.ast.InvalidUIQueryException;
|
|
20
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryAST;
|
|
21
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryASTClassName;
|
|
22
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryASTPredicate;
|
|
23
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryASTWith;
|
|
24
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryDirection;
|
|
11
25
|
import sh.calaba.instrumentationbackend.query.ast.UIQueryEvaluator;
|
|
12
|
-
import sh.calaba.instrumentationbackend.query.ast.
|
|
26
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryVisibility;
|
|
13
27
|
import android.view.View;
|
|
14
28
|
|
|
15
29
|
public class Query {
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
private String queryString;
|
|
18
32
|
@SuppressWarnings("rawtypes")
|
|
19
|
-
private List
|
|
33
|
+
private List operations;
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
public Query(String queryString) {
|
|
36
|
+
this.queryString = queryString;
|
|
37
|
+
this.operations = Collections.EMPTY_LIST;
|
|
38
|
+
if (this.queryString == null || this.queryString.trim().equals("")) {
|
|
39
|
+
throw new IllegalArgumentException("Illegal query: "
|
|
40
|
+
+ this.queryString);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
public Query(String queryString,List args) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
@SuppressWarnings("rawtypes")
|
|
45
|
+
public Query(String queryString, List args) {
|
|
46
|
+
this(queryString);
|
|
47
|
+
this.operations = args;
|
|
48
|
+
}
|
|
35
49
|
|
|
50
|
+
@SuppressWarnings("rawtypes")
|
|
51
|
+
public List executeQuery() {
|
|
52
|
+
return UIQueryEvaluator.evaluateQueryWithOptions(parseQuery(this.queryString), rootViews(), parseOperations(this.operations));
|
|
53
|
+
}
|
|
36
54
|
|
|
37
|
-
|
|
38
|
-
public
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
@SuppressWarnings("rawtypes")
|
|
56
|
+
public static List<Operation> parseOperations(List ops) {
|
|
57
|
+
List<Operation> result = new ArrayList<Operation>(ops.size());
|
|
58
|
+
for (Object o : ops) {
|
|
59
|
+
Operation op = null;
|
|
60
|
+
if (o instanceof Operation) {
|
|
61
|
+
op = (Operation) o;
|
|
62
|
+
}
|
|
63
|
+
else if (o instanceof String) {
|
|
64
|
+
op = new PropertyOperation((String) o);
|
|
65
|
+
}
|
|
66
|
+
else if (o instanceof Map) {
|
|
67
|
+
Map mapOp = (Map) o;
|
|
68
|
+
String methodName = (String) mapOp.get("method_name");
|
|
69
|
+
if (methodName == null) {
|
|
70
|
+
throw new IllegalArgumentException("Trying to convert a Map without method_name to an operation. " + mapOp.toString());
|
|
71
|
+
}
|
|
72
|
+
List arguments = (List) mapOp.get("arguments");
|
|
73
|
+
if (arguments == null) {
|
|
74
|
+
throw new IllegalArgumentException("Trying to convert a Map without arguments to an operation. " + mapOp.toString());
|
|
75
|
+
}
|
|
76
|
+
op = new InvocationOperation(methodName, arguments);
|
|
77
|
+
}
|
|
78
|
+
result.add(op);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
53
82
|
|
|
83
|
+
@SuppressWarnings("unchecked")
|
|
84
|
+
public static List<UIQueryAST> parseQuery(String query) {
|
|
85
|
+
UIQueryLexer lexer = new UIQueryLexer(new ANTLRStringStream(query));
|
|
86
|
+
UIQueryParser parser = new UIQueryParser(new CommonTokenStream(lexer));
|
|
54
87
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
88
|
+
UIQueryParser.query_return q;
|
|
89
|
+
try {
|
|
90
|
+
q = parser.query();
|
|
91
|
+
} catch (RecognitionException e) {
|
|
92
|
+
throw new InvalidUIQueryException(e.getMessage());
|
|
93
|
+
}
|
|
94
|
+
if (q == null) {
|
|
95
|
+
throw new InvalidUIQueryException(query);
|
|
96
|
+
}
|
|
97
|
+
CommonTree rootNode = (CommonTree) q.getTree();
|
|
98
|
+
List<CommonTree> queryPath = rootNode.getChildren();
|
|
99
|
+
|
|
100
|
+
if (queryPath == null || queryPath.isEmpty()) {
|
|
101
|
+
queryPath = Collections.singletonList(rootNode);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return mapUIQueryFromAstNodes(queryPath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
public static List<UIQueryAST> mapUIQueryFromAstNodes(List<CommonTree> nodes) {
|
|
109
|
+
List<UIQueryAST> mapped = new ArrayList<UIQueryAST>(nodes.size());
|
|
110
|
+
for (CommonTree t : nodes) {
|
|
111
|
+
mapped.add(uiQueryFromAst(t));
|
|
112
|
+
}
|
|
113
|
+
return mapped;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public static UIQueryAST uiQueryFromAst(CommonTree step) {
|
|
117
|
+
String stepType = UIQueryParser.tokenNames[step.getType()];
|
|
118
|
+
switch (step.getType()) {
|
|
119
|
+
case UIQueryParser.QUALIFIED_NAME:
|
|
120
|
+
try {
|
|
121
|
+
return new UIQueryASTClassName(Class.forName(step.getText()));
|
|
122
|
+
} catch (ClassNotFoundException e) {
|
|
123
|
+
// TODO Auto-generated catch block
|
|
124
|
+
throw new InvalidUIQueryException("Qualified class name: "
|
|
125
|
+
+ step.getText() + " not found. (" + e.getMessage()
|
|
126
|
+
+ ")");
|
|
127
|
+
}
|
|
128
|
+
case UIQueryParser.NAME:
|
|
129
|
+
return new UIQueryASTClassName(step.getText());
|
|
130
|
+
|
|
131
|
+
case UIQueryParser.WILDCARD:
|
|
132
|
+
try {
|
|
133
|
+
return new UIQueryASTClassName(Class.forName("android.view.View"));
|
|
134
|
+
} catch (ClassNotFoundException e) {
|
|
135
|
+
//Cannot happen
|
|
136
|
+
throw new IllegalStateException(e);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
case UIQueryParser.FILTER_COLON:
|
|
142
|
+
return UIQueryASTWith.fromAST(step);
|
|
143
|
+
|
|
144
|
+
case UIQueryParser.ALL:
|
|
145
|
+
return UIQueryVisibility.ALL;
|
|
146
|
+
|
|
147
|
+
case UIQueryParser.VISIBLE:
|
|
148
|
+
return UIQueryVisibility.VISIBLE;
|
|
149
|
+
|
|
150
|
+
case UIQueryParser.BEGINPRED:
|
|
151
|
+
return UIQueryASTPredicate.newPredicateFromAST(step);
|
|
152
|
+
case UIQueryParser.DIRECTION:
|
|
153
|
+
return UIQueryDirection.valueOf(step.getText().toUpperCase());
|
|
154
|
+
|
|
155
|
+
default:
|
|
156
|
+
throw new InvalidUIQueryException("Unknown query: " + stepType
|
|
157
|
+
+ " with text: " + step.getText());
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
}
|
|
58
162
|
|
|
59
163
|
public List<View> rootViews() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
164
|
+
Set<View> parents = new HashSet<View>();
|
|
165
|
+
for (View v : viewFetcher.getAllViews(false))
|
|
166
|
+
{
|
|
167
|
+
View parent = viewFetcher.getTopParent(v);
|
|
168
|
+
System.out.println(parent);
|
|
169
|
+
parents.add(parent);
|
|
170
|
+
}
|
|
171
|
+
List<View> results = new ArrayList<View>();
|
|
172
|
+
results.addAll(parents);
|
|
173
|
+
return results;
|
|
68
174
|
}
|
|
175
|
+
|
|
176
|
+
|
|
69
177
|
}
|
data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQuery.tokens
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
ALL=4
|
|
2
|
+
BEGINPRED=5
|
|
3
|
+
BOOL=6
|
|
4
|
+
DIRECTION=7
|
|
5
|
+
ENDPRED=8
|
|
6
|
+
ESC_SEQ=9
|
|
7
|
+
FILTER_COLON=10
|
|
8
|
+
HEX_DIGIT=11
|
|
9
|
+
INT=12
|
|
10
|
+
NAME=13
|
|
11
|
+
NIL=14
|
|
12
|
+
OCTAL_ESC=15
|
|
13
|
+
QUALIFIED_NAME=16
|
|
14
|
+
RELATION=17
|
|
15
|
+
STRING=18
|
|
16
|
+
UNICODE_ESC=19
|
|
17
|
+
VISIBLE=20
|
|
18
|
+
WHITE=21
|
|
19
|
+
WILDCARD=22
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
package sh.calaba.instrumentationbackend.query;
|
|
2
|
+
|
|
3
|
+
import java.util.HashMap;
|
|
4
|
+
import java.util.Map;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
public class UIQueryResultVoid {
|
|
8
|
+
public static final UIQueryResultVoid instance = new UIQueryResultVoid();
|
|
9
|
+
|
|
10
|
+
private UIQueryResultVoid() {}
|
|
11
|
+
|
|
12
|
+
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
13
|
+
public Object asMap(String methodName, Object receiver,
|
|
14
|
+
String errorMessage) {
|
|
15
|
+
Map map = new HashMap();
|
|
16
|
+
map.put("error", errorMessage);
|
|
17
|
+
map.put("methodName", methodName);
|
|
18
|
+
map.put("receiverClass", receiver.getClass().getName());
|
|
19
|
+
map.put("receiverString", receiver.toString());
|
|
20
|
+
return map;
|
|
21
|
+
}
|
|
22
|
+
}
|
data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java
CHANGED
|
@@ -2,8 +2,10 @@ package sh.calaba.instrumentationbackend.query;
|
|
|
2
2
|
|
|
3
3
|
import java.util.HashMap;
|
|
4
4
|
import java.util.Map;
|
|
5
|
+
import java.util.Map.Entry;
|
|
5
6
|
|
|
6
7
|
import sh.calaba.instrumentationbackend.InstrumentationBackend;
|
|
8
|
+
import sh.calaba.instrumentationbackend.query.ast.UIQueryUtils;
|
|
7
9
|
import android.content.res.Resources;
|
|
8
10
|
import android.view.View;
|
|
9
11
|
import android.widget.Button;
|
|
@@ -13,12 +15,10 @@ import android.widget.TextView;
|
|
|
13
15
|
public class ViewMapper {
|
|
14
16
|
|
|
15
17
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
16
|
-
public static Object extractDataFromView(
|
|
17
|
-
|
|
18
|
-
View v = (View) obj;
|
|
18
|
+
public static Object extractDataFromView(View v) {
|
|
19
|
+
|
|
19
20
|
Map data = new HashMap();
|
|
20
|
-
data.put("class", v.getClass().
|
|
21
|
-
data.put("qualified_class", v.getClass().getName());
|
|
21
|
+
data.put("class", v.getClass().getName());
|
|
22
22
|
data.put("description", v.toString());
|
|
23
23
|
CharSequence description = v.getContentDescription();
|
|
24
24
|
data.put("contentDescription", description != null ? description.toString() : null);
|
|
@@ -33,16 +33,20 @@ public class ViewMapper {
|
|
|
33
33
|
}
|
|
34
34
|
data.put("id", id);
|
|
35
35
|
|
|
36
|
-
Map
|
|
36
|
+
Map rect = new HashMap();
|
|
37
37
|
int[] location = new int[2];
|
|
38
38
|
v.getLocationOnScreen(location);
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
rect.put("x", location[0]);
|
|
41
|
+
rect.put("y", location[1]);
|
|
42
|
+
|
|
43
|
+
rect.put("center_x", location[0] + v.getWidth()/2.0);
|
|
44
|
+
rect.put("center_y", location[1] + v.getHeight()/2.0);
|
|
45
|
+
|
|
46
|
+
rect.put("width", v.getWidth());
|
|
47
|
+
rect.put("height", v.getHeight());
|
|
44
48
|
|
|
45
|
-
data.put("
|
|
49
|
+
data.put("rect", rect);
|
|
46
50
|
|
|
47
51
|
if (v instanceof Button) {
|
|
48
52
|
Button b = (Button) v;
|
|
@@ -60,4 +64,30 @@ public class ViewMapper {
|
|
|
60
64
|
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
68
|
+
public static Object mapView(Object o) {
|
|
69
|
+
if (o instanceof View) {
|
|
70
|
+
return extractDataFromView((View) o);
|
|
71
|
+
}
|
|
72
|
+
else if (o instanceof Map) {
|
|
73
|
+
Map copy = new HashMap();
|
|
74
|
+
for (Object e : ((Map) o).entrySet()) {
|
|
75
|
+
Map.Entry entry = (Entry) e;
|
|
76
|
+
Object value = entry.getValue();
|
|
77
|
+
if (value instanceof View) {
|
|
78
|
+
copy.put(entry.getKey(), UIQueryUtils.getId((View) value));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
copy.put(entry.getKey(),entry.getValue());
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return copy;
|
|
86
|
+
}
|
|
87
|
+
else if (o instanceof CharSequence) {
|
|
88
|
+
return o.toString();
|
|
89
|
+
}
|
|
90
|
+
return o;
|
|
91
|
+
}
|
|
92
|
+
|
|
63
93
|
}
|