calabash-android 0.4.0.pre10 → 0.4.0.pre11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. data/lib/calabash-android/lib/TestServer.apk +0 -0
  2. data/lib/calabash-android/operations.rb +19 -2
  3. data/lib/calabash-android/steps/list_steps.rb +1 -1
  4. data/lib/calabash-android/steps/time_picker_steps.rb +1 -1
  5. data/lib/calabash-android/version.rb +1 -1
  6. data/test-server/AndroidManifest.xml +2 -0
  7. data/test-server/instrumentation-backend/antlr/UIQuery.g +13 -1
  8. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/CalabashChromeClient.java +1 -6
  9. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/ExecuteJavascript.java +10 -11
  10. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/InvocationOperation.java +26 -5
  11. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/Query.java +6 -2
  12. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/UIQuery.tokens +17 -14
  13. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryLexer.java +727 -236
  14. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/antlr/UIQueryParser.java +212 -78
  15. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/BeginsWithRelation.java +45 -0
  16. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +54 -0
  17. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ContainsRelation.java +41 -0
  18. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/EndsWithRelation.java +42 -0
  19. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/LikeRelation.java +79 -0
  20. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +147 -0
  21. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicateRelation.java +5 -0
  22. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +3 -26
  23. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +50 -9
  24. metadata +9 -2
@@ -0,0 +1,54 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ public enum ComparisonOperator implements UIQueryASTPredicateRelation {
4
+
5
+ LESSTHAN {
6
+ public boolean areRelated(Object firstValue, Object secondValue) {
7
+ if (areNumbers(firstValue, secondValue)) {
8
+ Number firstNum = (Number) firstValue;
9
+ Number secondNum = (Number) secondValue;
10
+ return firstNum.doubleValue() < secondNum.doubleValue();
11
+ } else {
12
+ return false;
13
+ }
14
+ }
15
+ },
16
+
17
+ LESSTHANOREQUAL {
18
+ public boolean areRelated(Object firstValue, Object secondValue) {
19
+ return EQUAL.areRelated(firstValue, secondValue) || LESSTHAN.areRelated(firstValue, secondValue);
20
+ }
21
+ },
22
+
23
+ EQUAL {
24
+ public boolean areRelated(Object firstValue, Object secondValue) {
25
+ if (firstValue == secondValue) {
26
+ return true;
27
+ }
28
+ return (firstValue != null) && firstValue.equals(secondValue);
29
+ }
30
+ },
31
+
32
+ GREATERTHAN {
33
+ public boolean areRelated(Object firstValue, Object secondValue) {
34
+ if (areNumbers(firstValue, secondValue)) {
35
+ Number firstNum = (Number) firstValue;
36
+ Number secondNum = (Number) secondValue;
37
+ return firstNum.doubleValue() > secondNum.doubleValue();
38
+ } else {
39
+ return false;
40
+ }
41
+ }
42
+ },
43
+
44
+ GREATERTHANOREQUAL {
45
+ public boolean areRelated(Object firstValue, Object secondValue) {
46
+ return EQUAL.areRelated(firstValue, secondValue) || GREATERTHAN.areRelated(firstValue, secondValue);
47
+ }
48
+ };
49
+
50
+ protected boolean areNumbers(Object firstValue, Object secondValue) {
51
+ return (firstValue instanceof Number && secondValue instanceof Number);
52
+ }
53
+
54
+ }
@@ -0,0 +1,41 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ public class ContainsRelation implements UIQueryASTPredicateRelation {
4
+
5
+ private final boolean caseSensitive;
6
+
7
+ public ContainsRelation(boolean isCaseSensitive) {
8
+ super();
9
+ this.caseSensitive = isCaseSensitive;
10
+ }
11
+
12
+ public boolean isCaseSensitive() {
13
+ return caseSensitive;
14
+ }
15
+
16
+ /**
17
+ * Does firstValue CONTAIN secondValue?
18
+ */
19
+ @Override
20
+ public boolean areRelated(Object firstValue, Object secondValue) {
21
+ if (firstValue == secondValue) {
22
+ return true;
23
+ }
24
+ if (firstValue == null || secondValue == null) {
25
+ return false;
26
+ }
27
+ if (firstValue instanceof CharSequence && secondValue instanceof CharSequence) {
28
+ String firstStr = firstValue.toString();
29
+ String secondStr = secondValue.toString();
30
+ if (!isCaseSensitive()) {
31
+ firstStr = firstStr.toLowerCase();
32
+ secondStr = secondStr.toLowerCase();
33
+ }
34
+ return firstStr.indexOf(secondStr) != -1;
35
+ }
36
+ else {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ }
@@ -0,0 +1,42 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ public class EndsWithRelation implements UIQueryASTPredicateRelation {
4
+
5
+ private final boolean caseSensitive;
6
+
7
+ public EndsWithRelation(boolean isCaseSensitive) {
8
+ super();
9
+ this.caseSensitive = isCaseSensitive;
10
+ }
11
+
12
+ public boolean isCaseSensitive() {
13
+ return caseSensitive;
14
+ }
15
+
16
+
17
+ /**
18
+ * Does firstValue END WITH secondValue?
19
+ */
20
+ @Override
21
+ public boolean areRelated(Object firstValue, Object secondValue) {
22
+ if (firstValue == secondValue) {
23
+ return true;
24
+ }
25
+ if (firstValue == null || secondValue == null) {
26
+ return false;
27
+ }
28
+ if (firstValue instanceof CharSequence && secondValue instanceof CharSequence) {
29
+ String firstStr = firstValue.toString();
30
+ String secondStr = secondValue.toString();
31
+ if (!isCaseSensitive()) {
32
+ firstStr = firstStr.toLowerCase();
33
+ secondStr = secondStr.toLowerCase();
34
+ }
35
+ return firstStr.endsWith(secondStr);
36
+ }
37
+ else {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ }
@@ -0,0 +1,79 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ import java.util.regex.Matcher;
4
+ import java.util.regex.Pattern;
5
+
6
+ public class LikeRelation implements UIQueryASTPredicateRelation {
7
+
8
+ private final boolean caseSensitive;
9
+
10
+ public LikeRelation(boolean isCaseSensitive) {
11
+ super();
12
+ this.caseSensitive = isCaseSensitive;
13
+ }
14
+
15
+ public boolean isCaseSensitive() {
16
+ return caseSensitive;
17
+ }
18
+
19
+ /**
20
+ * Is firstValue LIKE secondValue? In this case secondValue is a pattern ala
21
+ * 'Cou* Down' which will match a string 'Count Down'.
22
+ */
23
+ @Override
24
+ public boolean areRelated(Object firstValue, Object secondValue) {
25
+ if (firstValue == secondValue) {
26
+ return true;
27
+ }
28
+ if (firstValue == null || secondValue == null) {
29
+ return false;
30
+ }
31
+ if (firstValue instanceof CharSequence
32
+ && secondValue instanceof CharSequence) {
33
+ String firstStr = firstValue.toString();
34
+ String patternStr = secondValue.toString();
35
+ Matcher matcher = matcherFrom(patternStr, firstStr);
36
+ return matcher.matches();
37
+ } else {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ private Matcher matcherFrom(String patternStr, String stringToMatch) {
43
+ Pattern compiledPat;
44
+ if (isCaseSensitive()) {
45
+ compiledPat = Pattern.compile(translatePatternString(patternStr));
46
+ } else {
47
+ compiledPat = Pattern.compile(translatePatternString(patternStr),
48
+ Pattern.CASE_INSENSITIVE);
49
+ }
50
+ return compiledPat.matcher(stringToMatch);
51
+ }
52
+
53
+ private String translatePatternString(String patternStr) {
54
+
55
+ StringBuilder converted = new StringBuilder();
56
+ int beginAt = 0;
57
+ for (int i = 0; i < patternStr.length(); i++) {
58
+ char charAti = patternStr.charAt(i);
59
+ if (charAti == '*' || charAti == '%') {
60
+ String subString = patternStr.substring(beginAt, i);
61
+ beginAt = i + 1;
62
+ converted.append(Pattern.quote(subString));
63
+ if (charAti == '*') {
64
+ converted.append(".*");
65
+ } else if (charAti == '%') {
66
+ converted.append(".");
67
+ }
68
+ }
69
+ }
70
+ if (beginAt < patternStr.length()) {
71
+ String subString = patternStr.substring(beginAt,
72
+ patternStr.length());
73
+ converted.append(Pattern.quote(subString));
74
+ }
75
+
76
+ return "^" + converted.toString() + "$";
77
+ }
78
+
79
+ }
@@ -0,0 +1,147 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ import java.lang.reflect.Method;
4
+ import java.util.ArrayList;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+ import java.util.concurrent.Callable;
8
+
9
+ import org.antlr.runtime.tree.CommonTree;
10
+
11
+ import android.view.View;
12
+
13
+ public class UIQueryASTPredicate implements UIQueryAST {
14
+
15
+ private final String propertyName;
16
+ private final UIQueryASTPredicateRelation relation;
17
+ private final Object valueToMatch;
18
+
19
+ public UIQueryASTPredicate(String text,
20
+ UIQueryASTPredicateRelation parsedRelation, Object parsedValue) {
21
+ this.propertyName = text;
22
+ this.relation = parsedRelation;
23
+ this.valueToMatch = parsedValue;
24
+ }
25
+
26
+ @SuppressWarnings("rawtypes")
27
+ @Override
28
+ public List evaluateWithViews(final List inputViews,
29
+ final UIQueryDirection direction, final UIQueryVisibility visibility) {
30
+ return (List) UIQueryUtils.evaluateSyncInMainThread(new Callable() {
31
+
32
+ @SuppressWarnings("unchecked")
33
+ @Override
34
+ public Object call() throws Exception {
35
+ List filteredResult = new ArrayList(16);
36
+ for (int i = 0; i < inputViews.size(); i++) {
37
+ Object o = inputViews.get(i);
38
+
39
+ if (o instanceof Map) {
40
+ Map result = evaluateForMap((Map) o);
41
+ if (result != null) {
42
+ filteredResult.add(result);
43
+ }
44
+
45
+ } else {
46
+ Object result = evaluateForObject(o, i);
47
+ if (result != null) {
48
+ filteredResult.add(result);
49
+ }
50
+ }
51
+
52
+ }
53
+
54
+ return filteredResult;
55
+ }
56
+ });
57
+ }
58
+
59
+ @SuppressWarnings("rawtypes")
60
+ private Map evaluateForMap(Map map) {
61
+ if (map.containsKey(this.propertyName)) {
62
+ Object value = map.get(this.propertyName);
63
+ if (this.relation.areRelated(value, this.valueToMatch)) {
64
+ return map;
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+
70
+ private Object evaluateForObject(Object o, int index) {
71
+ if (o instanceof View && this.propertyName.equals("id")) {
72
+ View view = (View) o;
73
+ String id = UIQueryUtils.getId(view);
74
+ if (this.relation.areRelated(id, this.valueToMatch)) {
75
+ return o;
76
+ } else {
77
+ // let it fall through and check via general property access
78
+ // in case the user actually wants to compre the real value of
79
+ // getId()
80
+ }
81
+ }
82
+
83
+ Method propertyAccessor = UIQueryUtils
84
+ .hasProperty(o, this.propertyName);
85
+ if (propertyAccessor == null) {
86
+ return null;
87
+ }
88
+ Object value = UIQueryUtils.getProperty(o, propertyAccessor);
89
+
90
+ if (this.relation.areRelated(value, this.valueToMatch)) {
91
+ return o;
92
+ } else if (this.valueToMatch instanceof String
93
+ && this.relation
94
+ .areRelated(value.toString(), this.valueToMatch)) {
95
+ return o;
96
+ } else {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ public static UIQueryASTPredicate newPredicateFromAST(CommonTree step) {
102
+ // TODO Auto-generated method stub
103
+ if (step.getChildCount() != 3) {
104
+ throw new IllegalStateException("Bad Predicate query: "+step+". Expected form {getter RELATION value}.");
105
+ }
106
+ CommonTree prop = (CommonTree) step.getChild(0);
107
+ CommonTree rel = (CommonTree) step.getChild(1);
108
+ CommonTree val = (CommonTree) step.getChild(2);
109
+ return new UIQueryASTPredicate(prop.getText(),
110
+ UIQueryASTPredicate.parseRelation(rel),
111
+ UIQueryUtils.parseValue(val));
112
+
113
+ }
114
+
115
+ private static UIQueryASTPredicateRelation parseRelation(CommonTree rel) {
116
+ String relText = rel.getText().toUpperCase();
117
+ boolean caseSensitive = true;
118
+ final String CASE_INSENSITIVE_SPEC = "[C]";
119
+ if (relText.endsWith(CASE_INSENSITIVE_SPEC)) {
120
+ caseSensitive = false;
121
+ relText = relText.substring(0,relText.length() - CASE_INSENSITIVE_SPEC.length());
122
+ }
123
+
124
+ if ("BEGINSWITH".equals(relText)) {
125
+ return new BeginsWithRelation(caseSensitive);
126
+ } else if ("ENDSWITH".equals(relText)) {
127
+ return new EndsWithRelation(caseSensitive);
128
+ } else if ("CONTAINS".equals(relText)) {
129
+ return new ContainsRelation(caseSensitive);
130
+ } else if ("LIKE".equals(relText)) {
131
+ return new LikeRelation(caseSensitive);
132
+ } else if ("<".equals(relText)) {
133
+ return ComparisonOperator.LESSTHAN;
134
+ } else if ("<=".equals(relText)) {
135
+ return ComparisonOperator.LESSTHANOREQUAL;
136
+ } else if ("=".equals(relText)) {
137
+ return ComparisonOperator.EQUAL;
138
+ } else if (">".equals(relText)) {
139
+ return ComparisonOperator.GREATERTHAN;
140
+ } else if (">=".equals(relText)) {
141
+ return ComparisonOperator.GREATERTHANOREQUAL;
142
+ } else {
143
+ throw new IllegalStateException("Unsupported Relation: " + relText);
144
+ }
145
+ }
146
+
147
+ }
@@ -0,0 +1,5 @@
1
+ package sh.calaba.instrumentationbackend.query.ast;
2
+
3
+ public interface UIQueryASTPredicateRelation {
4
+ public boolean areRelated(Object firstValue, Object secondValue);
5
+ }
@@ -10,7 +10,6 @@ import java.util.concurrent.Future;
10
10
  import org.antlr.runtime.tree.CommonTree;
11
11
 
12
12
  import sh.calaba.instrumentationbackend.actions.webview.QueryHelper;
13
- import sh.calaba.instrumentationbackend.query.antlr.UIQueryParser;
14
13
  import android.view.View;
15
14
  import android.webkit.WebView;
16
15
 
@@ -190,31 +189,9 @@ public class UIQueryASTWith implements UIQueryAST {
190
189
  public static UIQueryASTWith fromAST(CommonTree step) {
191
190
  CommonTree prop = (CommonTree) step.getChild(0);
192
191
  CommonTree val = (CommonTree) step.getChild(1);
193
-
194
- switch (val.getType()) {
195
- case UIQueryParser.STRING: {
196
- String textWithPings = val.getText();
197
- String text = textWithPings
198
- .substring(1, textWithPings.length() - 1);
199
- return new UIQueryASTWith(prop.getText(), text);
200
- }
201
- case UIQueryParser.INT:
202
- return new UIQueryASTWith(prop.getText(), Integer.parseInt(
203
- val.getText(), 10));
204
- case UIQueryParser.BOOL: {
205
- String text = val.getText();
206
- return new UIQueryASTWith(prop.getText(),
207
- Boolean.parseBoolean(text));
208
- }
209
- case UIQueryParser.NIL:
210
- return new UIQueryASTWith(prop.getText(), null);
211
-
212
- default:
213
- throw new IllegalArgumentException("Unable to parse value type:"
214
- + val.getType() + " text " + val.getText());
215
-
216
- }
217
-
192
+
193
+ Object parsedVal = UIQueryUtils.parseValue(val);
194
+ return new UIQueryASTWith(prop.getText(), parsedVal);
218
195
  }
219
196
 
220
197
  @Override
@@ -14,16 +14,20 @@ import java.util.concurrent.Future;
14
14
  import java.util.concurrent.TimeUnit;
15
15
  import java.util.concurrent.atomic.AtomicReference;
16
16
 
17
+ import org.antlr.runtime.tree.CommonTree;
18
+
17
19
  import sh.calaba.instrumentationbackend.InstrumentationBackend;
18
20
  import sh.calaba.instrumentationbackend.actions.webview.QueryHelper;
19
21
  import sh.calaba.instrumentationbackend.query.CompletedFuture;
22
+ import sh.calaba.instrumentationbackend.query.antlr.UIQueryParser;
20
23
  import sh.calaba.org.codehaus.jackson.map.ObjectMapper;
21
24
  import sh.calaba.org.codehaus.jackson.type.TypeReference;
22
25
  import android.content.res.Resources.NotFoundException;
23
26
  import android.view.View;
24
27
  import android.webkit.WebView;
25
28
 
26
- public class UIQueryUtils {
29
+ public class UIQueryUtils {
30
+
27
31
  @SuppressWarnings({ "unchecked", "rawtypes" })
28
32
  public static List subviews(Object o)
29
33
  {
@@ -206,11 +210,9 @@ public class UIQueryUtils {
206
210
  public Object call() throws Exception {
207
211
  List<Map<String, Object>> parsedResult;
208
212
  try {
209
- parsedResult = new ObjectMapper()
210
- .readValue(
211
- jsonResponse,
212
- new TypeReference<List<HashMap<String, Object>>>() {
213
- });
213
+ parsedResult = new ObjectMapper().readValue(
214
+ jsonResponse,
215
+ new TypeReference<List<HashMap<String, Object>>>() {});
214
216
  for (Map<String,Object> data : parsedResult) {
215
217
  Map<String,Object> rect = (Map<String, Object>) data.get("rect");
216
218
  Map <String,Object> updatedRect = QueryHelper.translateRectToScreenCoordinates(webView, rect);
@@ -218,13 +220,52 @@ public class UIQueryUtils {
218
220
  data.put("webView", webView);
219
221
  }
220
222
  return parsedResult;
221
- } catch (Exception e) {
222
- e.printStackTrace();
223
- throw new RuntimeException(e);
223
+ } catch (Exception igored) {
224
+ try {
225
+ Map resultAsMap = new ObjectMapper().readValue(
226
+ jsonResponse,
227
+ new TypeReference<HashMap>() {});
228
+ //This usually happens in case of error
229
+ //check this case
230
+ System.out.println(resultAsMap);
231
+ String errorMsg = (String) resultAsMap.get("error");
232
+ System.out.println(errorMsg);
233
+ return Collections.singletonList(resultAsMap);
234
+ } catch (Exception e) {
235
+ e.printStackTrace();
236
+ throw new RuntimeException(e);
237
+ }
238
+
239
+
224
240
  }
225
241
  }
226
242
  });
227
243
 
228
244
  }
229
245
 
246
+ public static Object parseValue(CommonTree val) {
247
+ switch (val.getType()) {
248
+ case UIQueryParser.STRING: {
249
+ String textWithPings = val.getText();
250
+ String text = textWithPings
251
+ .substring(1, textWithPings.length() - 1);
252
+ return text;
253
+ }
254
+ case UIQueryParser.INT:
255
+ return Integer.parseInt(val.getText(), 10);
256
+ case UIQueryParser.BOOL: {
257
+ String text = val.getText();
258
+ return Boolean.parseBoolean(text);
259
+ }
260
+ case UIQueryParser.NIL:
261
+ return null;
262
+
263
+ default:
264
+ throw new IllegalArgumentException("Unable to parse value type:"
265
+ + val.getType() + " text " + val.getText());
266
+
267
+ }
268
+
269
+ }
270
+
230
271
  }