package org.ruboto.test; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; import java.util.Enumeration; import java.util.jar.JarFile; import java.util.jar.JarEntry; import java.util.List; import java.util.Map; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.ruboto.JRubyAdapter; import java.util.Set; import java.util.HashSet; public class InstrumentationTestRunner extends android.test.InstrumentationTestRunner { private Class activityClass; private Object setup; private Object teardown; private TestSuite suite; public TestSuite getAllTests() { Log.i(getClass().getName(), "Finding test scripts"); suite = new TestSuite("Sweet"); String loadStep = "Setup JRuby"; try { final AtomicBoolean JRubyLoadedOk = new AtomicBoolean(); // TODO(uwe): Running with large stack is currently only needed when running with JRuby 1.7.0 and android-10 // TODO(uwe): Simplify when we stop support for JRuby 1.7.0 or android-10 Thread t = new Thread(null, new Runnable() { public void run() { JRubyLoadedOk.set(JRubyAdapter.setUpJRuby(getTargetContext())); JRubyAdapter.runScriptlet("Java::OrgRubotoTest::InstrumentationTestRunner.__persistent__ = true"); } }, "Setup JRuby from instrumentation test runner", 64 * 1024); try { t.start(); t.join(); } catch(InterruptedException ie) { Thread.currentThread().interrupt(); throw new RuntimeException("Interrupted starting JRuby", ie); } // TODO end if (JRubyLoadedOk.get()) { loadStep = "Load test helper"; // TODO(uwe): Running with large stack is currently only needed when running with JRuby 1.7.0.dev and android-10 // TODO(uwe): Simplify when we stop support for JRuby 1.7.0.dev or android-10 final IOException[] ioex = new IOException[]{null}; Thread t2 = new Thread(null, new Runnable() { public void run() { try { loadScript("test_helper.rb"); } catch (IOException e) { ioex[0] = e; } } }, "Setup JRuby from instrumentation test runner", 64 * 1024); try { t2.start(); t2.join(); } catch(InterruptedException ie) { Thread.currentThread().interrupt(); throw new RuntimeException("Interrupted starting JRuby", ie); } if (ioex[0] != null) { throw ioex[0]; } // TODO end loadStep = "Get app test source dir"; String test_apk_path = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), 0).sourceDir; JarFile jar = new JarFile(test_apk_path); Enumeration<JarEntry> entries = jar.entries(); while(entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.indexOf("/") >= 0 || !name.endsWith(".rb")) { continue; } if (name.equals("test_helper.rb")) continue; loadStep = "Load " + name; loadScript(name); setup = teardown = null; } } else { addError(suite, loadStep, new RuntimeException("Ruboto Core platform is missing")); } } catch (android.content.pm.PackageManager.NameNotFoundException e) { addError(suite, loadStep, e); } catch (IOException e) { addError(suite, loadStep, e); } catch (RuntimeException e) { addError(suite, loadStep, e); } return suite; } public void activity(Class activityClass) { this.activityClass = activityClass; } public void setup(Object block) { this.setup = block; } public void teardown(Object block) { this.teardown = block; } public void test(String name, Object block) { test(name, null, block); } public void test(String name, Map options, Object block) { boolean runOnUiThread = options == null || options.get("ui") == "true"; Test test = new ActivityTest(activityClass, JRubyAdapter.getScriptFilename(), setup, teardown, name, runOnUiThread, block); suite.addTest(test); Log.d(getClass().getName(), "Made test instance: " + test); } private void addError(TestSuite suite, String loadStep, Throwable t) { Throwable cause = t; while(cause != null) { Log.e(getClass().getName(), "Exception loading tests (" + loadStep + "): " + cause); t = cause; cause = t.getCause(); } final Throwable rootCause = t; rootCause.printStackTrace(); suite.addTest(new TestCase(t.getMessage()) { public void runTest() throws java.lang.Throwable { throw rootCause; } }); } private void loadScript(String f) throws IOException { Log.d(getClass().getName(), "Loading test script: " + f); InputStream is = getClass().getClassLoader().getResourceAsStream(f); BufferedReader buffer = new BufferedReader(new InputStreamReader(is)); StringBuilder source = new StringBuilder(); while (true) { String line = buffer.readLine(); if (line == null) break; source.append(line).append("\n"); } buffer.close(); JRubyAdapter.setScriptFilename(f); if (f.equals("test_helper.rb")) { JRubyAdapter.runScriptlet(source.toString()); } else { JRubyAdapter.runRubyMethod(this, "instance_eval", source.toString(), f); } Log.d(getClass().getName(), "Test script " + f + " loaded"); } }