ruboto 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Gemfile.lock +9 -9
  2. data/README.md +2 -0
  3. data/Rakefile +71 -7
  4. data/assets/Rakefile +2 -407
  5. data/assets/libs/dexmaker20120305.jar +0 -0
  6. data/assets/rakelib/ruboto.rake +428 -0
  7. data/assets/samples/sample_broadcast_receiver.rb +7 -3
  8. data/assets/samples/sample_broadcast_receiver_test.rb +47 -1
  9. data/assets/src/RubotoActivity.java +6 -2
  10. data/assets/src/org/ruboto/EntryPointActivity.java +2 -2
  11. data/assets/src/org/ruboto/Script.java +91 -24
  12. data/assets/src/org/ruboto/test/ActivityTest.java +27 -24
  13. data/assets/src/org/ruboto/test/InstrumentationTestRunner.java +42 -8
  14. data/assets/src/ruboto/activity.rb +1 -1
  15. data/assets/src/ruboto/base.rb +17 -6
  16. data/assets/src/ruboto/generate.rb +458 -0
  17. data/assets/src/ruboto/legacy.rb +9 -12
  18. data/assets/src/ruboto/widget.rb +9 -1
  19. data/lib/java_class_gen/android_api.xml +1 -1
  20. data/lib/ruboto.rb +1 -2
  21. data/lib/ruboto/commands/base.rb +19 -4
  22. data/lib/ruboto/sdk_versions.rb +12 -0
  23. data/lib/ruboto/util/build.rb +10 -11
  24. data/lib/ruboto/util/update.rb +150 -51
  25. data/lib/ruboto/util/verify.rb +6 -4
  26. data/lib/ruboto/util/xml_element.rb +2 -2
  27. data/lib/ruboto/version.rb +1 -1
  28. data/test/activity/option_menu_activity.rb +5 -1
  29. data/test/activity/psych_activity.rb +11 -6
  30. data/test/activity/stack_activity_test.rb +13 -5
  31. data/test/app_test_methods.rb +4 -3
  32. data/test/broadcast_receiver_test.rb +86 -0
  33. data/test/minimal_app_test.rb +27 -19
  34. data/test/rake_test.rb +13 -2
  35. data/test/ruboto_gen_test.rb +17 -3
  36. data/test/ruboto_update_test.rb +24 -2
  37. data/test/service_test.rb +1 -1
  38. data/test/test_helper.rb +134 -62
  39. data/test/update_test_methods.rb +40 -14
  40. data/test/updated_example_test_methods.rb +41 -0
  41. metadata +12 -6
@@ -17,7 +17,7 @@ public class THE_RUBOTO_CLASS THE_ACTION THE_ANDROID_CLASS {
17
17
  private String scriptName;
18
18
  private String remoteVariable = null;
19
19
  private Object[] args;
20
- private Bundle configBundle;
20
+ private Bundle configBundle = null;
21
21
 
22
22
  THE_CONSTANTS
23
23
 
@@ -85,7 +85,7 @@ THE_CONSTANTS
85
85
  if (scriptName != null) {
86
86
  Script.setScriptFilename(getClass().getClassLoader().getResource(scriptName).getPath());
87
87
  Script.execute(new Script(scriptName).getContents());
88
- } else {
88
+ } else if (configBundle != null) {
89
89
  // TODO: Why doesn't this work?
90
90
  // Script.callMethod(this, "initialize_ruboto");
91
91
  Script.execute("$activity.initialize_ruboto");
@@ -99,6 +99,10 @@ THE_CONSTANTS
99
99
  }
100
100
  }
101
101
 
102
+ public boolean rubotoAttachable() {
103
+ return true;
104
+ }
105
+
102
106
  /****************************************************************************************
103
107
  *
104
108
  * Generated Methods
@@ -25,7 +25,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
25
25
  private ProgressDialog loadingDialog;
26
26
  private boolean dialogCancelled = false;
27
27
  private BroadcastReceiver receiver;
28
- private boolean appStarted = false;
28
+ protected boolean appStarted = false;
29
29
 
30
30
  public void onCreate(Bundle bundle) {
31
31
  Log.d("RUBOTO", "onCreate: ");
@@ -154,7 +154,7 @@ public class EntryPointActivity extends org.ruboto.RubotoActivity {
154
154
  }
155
155
  }
156
156
 
157
- private void fireRubotoActivity() {
157
+ protected void fireRubotoActivity() {
158
158
  if(appStarted) return;
159
159
  appStarted = true;
160
160
  Log.i("RUBOTO", "Starting activity");
@@ -33,6 +33,8 @@ public class Script {
33
33
 
34
34
  private String name = null;
35
35
  private static Object ruby;
36
+ private static boolean isDebugBuild = false;
37
+ private static PrintStream output = null;
36
38
  private static boolean initialized = false;
37
39
 
38
40
  private static String localContextScope = "SINGLETON";
@@ -40,6 +42,7 @@ public class Script {
40
42
 
41
43
  public static final String TAG = "RUBOTO"; // for logging
42
44
  private static String JRUBY_VERSION;
45
+ private static String RUBOTO_CORE_VERSION_NAME;
43
46
 
44
47
  /*************************************************************************************************
45
48
  *
@@ -69,19 +72,29 @@ public class Script {
69
72
  return initialized;
70
73
  }
71
74
 
75
+ public static boolean usesPlatformApk() {
76
+ return RUBOTO_CORE_VERSION_NAME != null;
77
+ }
78
+
79
+ public static String getPlatformVersionName() {
80
+ return RUBOTO_CORE_VERSION_NAME;
81
+ }
82
+
72
83
  public static synchronized boolean setUpJRuby(Context appContext) {
73
- return setUpJRuby(appContext, System.out);
84
+ return setUpJRuby(appContext, output == null ? System.out : output);
74
85
  }
75
86
 
76
87
  public static synchronized boolean setUpJRuby(Context appContext, PrintStream out) {
77
88
  if (!initialized) {
78
- Log.d(TAG, "Setting up JRuby runtime");
79
- System.setProperty("jruby.bytecode.version", "1.5");
89
+ setDebugBuild(appContext);
90
+ Log.d(TAG, "Setting up JRuby runtime (" + (isDebugBuild ? "DEBUG" : "RELEASE") + ")");
91
+ System.setProperty("jruby.bytecode.version", "1.6");
80
92
  System.setProperty("jruby.interfaces.useProxy", "true");
81
93
  System.setProperty("jruby.management.enabled", "false");
82
94
  System.setProperty("jruby.objectspace.enabled", "false");
83
95
  System.setProperty("jruby.thread.pooling", "true");
84
96
  System.setProperty("jruby.native.enabled", "false");
97
+ // System.setProperty("jruby.compat.version", "RUBY1_8"); // RUBY1_9 is the default
85
98
 
86
99
  // Uncomment these to debug Ruby source loading
87
100
  // System.setProperty("jruby.debug.loadService", "true");
@@ -102,9 +115,14 @@ public class Script {
102
115
  } catch (ClassNotFoundException e1) {
103
116
  String packageName = "org.ruboto.core";
104
117
  try {
105
- apkName = appContext.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
106
- } catch (PackageManager.NameNotFoundException e) {
107
- System.out.println("JRuby not found");
118
+ PackageInfo pkgInfo = appContext.getPackageManager().getPackageInfo(packageName, 0);
119
+ apkName = pkgInfo.applicationInfo.sourceDir;
120
+ RUBOTO_CORE_VERSION_NAME = pkgInfo.versionName;
121
+ } catch (PackageManager.NameNotFoundException e2) {
122
+ out.println("JRuby not found in local APK:");
123
+ e1.printStackTrace(out);
124
+ out.println("JRuby not found in platform APK:");
125
+ e2.printStackTrace(out);
108
126
  return false;
109
127
  }
110
128
 
@@ -169,10 +187,10 @@ public class Script {
169
187
  callScriptingContainerMethod(Void.class, "setCurrentDirectory", defaultCurrentDir);
170
188
 
171
189
  if (out != null) {
172
- Method setOutputMethod = ruby.getClass().getMethod("setOutput", PrintStream.class);
173
- setOutputMethod.invoke(ruby, out);
174
- Method setErrorMethod = ruby.getClass().getMethod("setError", PrintStream.class);
175
- setErrorMethod.invoke(ruby, out);
190
+ output = out;
191
+ setOutputStream(out);
192
+ } else if (output != null) {
193
+ setOutputStream(output);
176
194
  }
177
195
 
178
196
  String jrubyHome = "file:" + apkName + "!";
@@ -204,6 +222,29 @@ public class Script {
204
222
  return initialized;
205
223
  }
206
224
 
225
+ public static void setOutputStream(PrintStream out) {
226
+ if (ruby == null) {
227
+ output = out;
228
+ } else {
229
+ try {
230
+ Method setOutputMethod = ruby.getClass().getMethod("setOutput", PrintStream.class);
231
+ setOutputMethod.invoke(ruby, out);
232
+ Method setErrorMethod = ruby.getClass().getMethod("setError", PrintStream.class);
233
+ setErrorMethod.invoke(ruby, out);
234
+ } catch (IllegalArgumentException e) {
235
+ handleInitException(e);
236
+ } catch (SecurityException e) {
237
+ handleInitException(e);
238
+ } catch (IllegalAccessException e) {
239
+ handleInitException(e);
240
+ } catch (InvocationTargetException e) {
241
+ handleInitException(e);
242
+ } catch (NoSuchMethodException e) {
243
+ handleInitException(e);
244
+ }
245
+ }
246
+ }
247
+
207
248
  private static void handleInitException(Exception e) {
208
249
  Log.e(TAG, "Exception starting JRuby");
209
250
  Log.e(TAG, e.getMessage() != null ? e.getMessage() : e.getClass().getName());
@@ -252,7 +293,11 @@ public class Script {
252
293
  } catch (IllegalAccessException iae) {
253
294
  throw new RuntimeException(iae);
254
295
  } catch (java.lang.reflect.InvocationTargetException ite) {
255
- throw ((RuntimeException) ite.getCause());
296
+ if (isDebugBuild) {
297
+ throw ((RuntimeException) ite.getCause());
298
+ } else {
299
+ return null;
300
+ }
256
301
  }
257
302
  }
258
303
 
@@ -315,7 +360,7 @@ public class Script {
315
360
  }
316
361
 
317
362
  private static List<String> getLoadPath() {
318
- return callScriptingContainerMethod(List.class, "getLoadPaths");
363
+ return (List<String>)callScriptingContainerMethod(List.class, "getLoadPaths");
319
364
  }
320
365
 
321
366
  public static Boolean configDir(String scriptsDir) {
@@ -371,21 +416,20 @@ public class Script {
371
416
  }
372
417
  }
373
418
 
374
- private static boolean isDebugBuild(Context context) {
419
+ private static void setDebugBuild(Context context) {
375
420
  PackageManager pm = context.getPackageManager();
376
421
  PackageInfo pi;
377
422
  try {
378
423
  pi = pm.getPackageInfo(context.getPackageName(), 0);
379
- return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
424
+ isDebugBuild = ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
380
425
  } catch (NameNotFoundException e) {
381
- return false;
426
+ isDebugBuild = false;
382
427
  }
383
-
384
428
  }
385
429
 
386
430
  private static String scriptsDirName(Context context) {
387
431
  File storageDir = null;
388
- if (isDebugBuild(context)) {
432
+ if (isDebugBuild) {
389
433
 
390
434
  // FIXME(uwe): Simplify this as soon as we drop support for android-7 or JRuby 1.5.6 or JRuby 1.6.2
391
435
  Log.i(TAG, "JRuby VERSION: " + JRUBY_VERSION);
@@ -474,7 +518,7 @@ public class Script {
474
518
  */
475
519
 
476
520
  public static String getScriptFilename() {
477
- return callScriptingContainerMethod(String.class, "getScriptFilename");
521
+ return (String)callScriptingContainerMethod(String.class, "getScriptFilename");
478
522
  }
479
523
 
480
524
  public static void setScriptFilename(String name) {
@@ -496,6 +540,9 @@ public class Script {
496
540
  throw new RuntimeException(iae);
497
541
  } catch (java.lang.reflect.InvocationTargetException ite) {
498
542
  printStackTrace(ite);
543
+ if (isDebugBuild) {
544
+ throw new RuntimeException(ite);
545
+ }
499
546
  }
500
547
  }
501
548
 
@@ -522,14 +569,34 @@ public class Script {
522
569
  return null;
523
570
  }
524
571
 
525
- public static <T> T callMethod(Object receiver, String methodName,
526
- Object arg, Class<T> returnType) {
527
- return callMethod(receiver, methodName, new Object[]{arg}, returnType);
572
+ @SuppressWarnings("unchecked")
573
+ public static <T> T callMethod(Object receiver, String methodName, Object arg, Class<T> returnType) {
574
+ try {
575
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Object.class, Class.class);
576
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, arg, returnType);
577
+ } catch (NoSuchMethodException nsme) {
578
+ throw new RuntimeException(nsme);
579
+ } catch (IllegalAccessException iae) {
580
+ throw new RuntimeException(iae);
581
+ } catch (java.lang.reflect.InvocationTargetException ite) {
582
+ printStackTrace(ite);
583
+ }
584
+ return null;
528
585
  }
529
586
 
530
- public static <T> T callMethod(Object receiver, String methodName,
531
- Class<T> returnType) {
532
- return callMethod(receiver, methodName, new Object[]{}, returnType);
587
+ @SuppressWarnings("unchecked")
588
+ public static <T> T callMethod(Object receiver, String methodName, Class<T> returnType) {
589
+ try {
590
+ Method callMethodMethod = ruby.getClass().getMethod("callMethod", Object.class, String.class, Class.class);
591
+ return (T) callMethodMethod.invoke(ruby, receiver, methodName, returnType);
592
+ } catch (NoSuchMethodException nsme) {
593
+ throw new RuntimeException(nsme);
594
+ } catch (IllegalAccessException iae) {
595
+ throw new RuntimeException(iae);
596
+ } catch (java.lang.reflect.InvocationTargetException ite) {
597
+ printStackTrace(ite);
598
+ }
599
+ return null;
533
600
  }
534
601
 
535
602
  private static void printStackTrace(Throwable t) {
@@ -19,44 +19,47 @@ public class ActivityTest extends ActivityInstrumentationTestCase2 {
19
19
  private final Object setup;
20
20
  private final Object block;
21
21
  private final String filename;
22
+ private final boolean onUiThread;
22
23
 
23
- public ActivityTest(Class activityClass, String filename, Object setup, String name, Object block) {
24
+ public ActivityTest(Class activityClass, String filename, Object setup, String name, boolean onUiThread, Object block) {
24
25
  super(activityClass.getPackage().getName(), activityClass);
25
26
  this.filename = filename;
26
27
  this.setup = setup;
27
28
  setName(filename + "#" + name);
29
+ this.onUiThread = onUiThread;
28
30
  this.block = block;
29
31
  Log.i(getClass().getName(), "Instance: " + getName());
30
32
  }
31
33
 
32
34
  public void runTest() throws Exception {
33
- Log.i(getClass().getName(), "runTest");
34
- Log.i(getClass().getName(), "runTest: " + getName());
35
- if (Script.setUpJRuby(getActivity())) {
36
- Log.i(getClass().getName(), "ruby ok");
37
- try {
38
- final Activity activity = getActivity();
39
- Log.i(getClass().getName(), "activity ok");
40
- runTestOnUiThread(new Runnable() {
41
- public void run() {
42
- String oldFile = Script.getScriptFilename();
35
+ try {
36
+ Log.i(getClass().getName(), "runTest: " + getName());
37
+ final Activity activity = getActivity();
38
+ Log.i(getClass().getName(), "Activity OK");
39
+ Runnable testRunnable = new Runnable() {
40
+ public void run() {
41
+ String oldFile = Script.getScriptFilename();
43
42
 
44
- Log.i(getClass().getName(), "calling setup");
45
- Script.setScriptFilename(filename);
46
- Script.callMethod(setup, "call", activity);
47
- Log.i(getClass().getName(), "setup ok");
43
+ Log.i(getClass().getName(), "calling setup");
44
+ Script.setScriptFilename(filename);
45
+ Script.callMethod(setup, "call", activity);
46
+ Log.i(getClass().getName(), "setup ok");
48
47
 
49
- Script.setScriptFilename(filename);
50
- Script.callMethod(block, "call", activity);
51
- Script.setScriptFilename(oldFile);
52
- }
53
- });
54
- } catch (Throwable t) {
55
- throw new AssertionFailedError(t.getMessage() != null ? t.getMessage() : t.getClass().getName());
48
+ Script.setScriptFilename(filename);
49
+ Script.callMethod(block, "call", activity);
50
+ Script.setScriptFilename(oldFile);
51
+ }
52
+ };
53
+ if (onUiThread) {
54
+ runTestOnUiThread(testRunnable);
55
+ } else {
56
+ testRunnable.run();
56
57
  }
57
58
  Log.i(getClass().getName(), "runTest ok");
58
- } else {
59
- throw new AssertionFailedError("Ruboto Core platform is missing.");
59
+ } catch (Throwable t) {
60
+ AssertionFailedError afe = new AssertionFailedError("Exception running test.");
61
+ afe.initCause(t);
62
+ throw afe;
60
63
  }
61
64
  }
62
65
 
@@ -15,10 +15,12 @@ import java.net.URLDecoder;
15
15
  import java.util.ArrayList;
16
16
  import java.util.Arrays;
17
17
  import java.util.Collections;
18
+ import java.util.concurrent.atomic.AtomicBoolean;
18
19
  import java.util.Enumeration;
19
20
  import java.util.jar.JarFile;
20
21
  import java.util.jar.JarEntry;
21
22
  import java.util.List;
23
+ import java.util.Map;
22
24
  import junit.framework.Test;
23
25
  import junit.framework.TestCase;
24
26
  import junit.framework.TestSuite;
@@ -34,15 +36,37 @@ public class InstrumentationTestRunner extends android.test.InstrumentationTestR
34
36
  public TestSuite getAllTests() {
35
37
  Log.i(getClass().getName(), "Finding test scripts");
36
38
  suite = new TestSuite("Sweet");
39
+ String loadStep = "Setup JRuby";
37
40
 
38
41
  try {
39
- if (Script.setUpJRuby(getTargetContext())) {
42
+ final AtomicBoolean JRubyLoadedOk = new AtomicBoolean();
43
+
44
+ // TODO(uwe): Running with large stack is currently only needed when running with JRuby 1.7.0 and android-10
45
+ // TODO(uwe): Simplify when we stop support for JRuby 1.7.0 or android-10
46
+ Thread t = new Thread(null, new Runnable() {
47
+ public void run() {
48
+ JRubyLoadedOk.set(Script.setUpJRuby(getTargetContext()));
49
+ }
50
+ }, "Setup JRuby from instrumentation test runner", 64 * 1024);
51
+ try {
52
+ t.start();
53
+ t.join();
54
+ } catch(InterruptedException ie) {
55
+ Thread.currentThread().interrupt();
56
+ throw new RuntimeException("Interrupted starting JRuby", ie);
57
+ }
58
+ // TODO end
59
+
60
+ if (JRubyLoadedOk.get()) {
61
+ loadStep = "Setup global variables";
40
62
  Script.defineGlobalVariable("$runner", this);
41
63
  Script.defineGlobalVariable("$test", this);
42
64
  Script.defineGlobalVariable("$suite", suite);
43
65
 
66
+ loadStep = "Load test helper";
44
67
  loadScript("test_helper.rb");
45
68
 
69
+ loadStep = "Get app test source dir";
46
70
  String test_apk_path = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), 0).sourceDir;
47
71
  JarFile jar = new JarFile(test_apk_path);
48
72
  Enumeration<JarEntry> entries = jar.entries();
@@ -53,17 +77,18 @@ public class InstrumentationTestRunner extends android.test.InstrumentationTestR
53
77
  continue;
54
78
  }
55
79
  if (name.equals("test_helper.rb")) continue;
80
+ loadStep = "Load " + name;
56
81
  loadScript(name);
57
82
  }
58
83
  } else {
59
- addError(suite, new RuntimeException("Ruboto Core platform is missing"));
84
+ addError(suite, loadStep, new RuntimeException("Ruboto Core platform is missing"));
60
85
  }
61
86
  } catch (android.content.pm.PackageManager.NameNotFoundException e) {
62
- addError(suite, e);
87
+ addError(suite, loadStep, e);
63
88
  } catch (IOException e) {
64
- addError(suite, e);
89
+ addError(suite, loadStep, e);
65
90
  } catch (RuntimeException e) {
66
- addError(suite, e);
91
+ addError(suite, loadStep, e);
67
92
  }
68
93
  return suite;
69
94
  }
@@ -77,18 +102,27 @@ public class InstrumentationTestRunner extends android.test.InstrumentationTestR
77
102
  }
78
103
 
79
104
  public void test(String name, Object block) {
105
+ test(name, null, block);
106
+ }
107
+
108
+ public void test(String name, Map options, Object block) {
109
+ // FIXME(uwe): Remove when we stop supporting Android 2.2
80
110
  if (android.os.Build.VERSION.SDK_INT <= 8) {
81
111
  name ="runTest";
82
112
  }
83
- Test test = new ActivityTest(activityClass, Script.getScriptFilename(), setup, name, block);
113
+ // FIXME end
114
+
115
+ boolean runOnUiThread = options == null || options.get("ui") == "true";
116
+
117
+ Test test = new ActivityTest(activityClass, Script.getScriptFilename(), setup, name, runOnUiThread, block);
84
118
  suite.addTest(test);
85
119
  Log.d(getClass().getName(), "Made test instance: " + test);
86
120
  }
87
121
 
88
- private void addError(TestSuite suite, Throwable t) {
122
+ private void addError(TestSuite suite, String loadStep, Throwable t) {
89
123
  Throwable cause = t;
90
124
  while(cause != null) {
91
- Log.e(getClass().getName(), "Exception loading tests: " + cause);
125
+ Log.e(getClass().getName(), "Exception loading tests (" + loadStep + "): " + cause);
92
126
  t = cause;
93
127
  cause = t.getCause();
94
128
  }
@@ -35,7 +35,7 @@ module Ruboto
35
35
  $context_init_block = block
36
36
  $new_context_global = global_variable_name
37
37
 
38
- if @initialized or (self == $activity && !$activity.kind_of?(RubotoActivity))
38
+ if @initialized or (self == $activity && !$activity.rubotoAttachable)
39
39
  b = Java::android.os.Bundle.new
40
40
  b.putInt("Theme", theme) if theme
41
41