calabash-android 0.4.0.pre10 → 0.4.0.pre11

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.
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
  }