summaryrefslogtreecommitdiff
path: root/android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java')
-rw-r--r--android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java547
1 files changed, 547 insertions, 0 deletions
diff --git a/android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java b/android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java
new file mode 100644
index 00000000000..1a3790b45ff
--- /dev/null
+++ b/android/controller/src/main/java/com/zeroc/testcontroller/ControllerApp.java
@@ -0,0 +1,547 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2017 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+package com.zeroc.testcontroller;
+
+import java.io.*;
+import java.util.Optional;
+import java.util.OptionalInt;
+
+import com.zeroc.Ice.Logger;
+import com.zeroc.Ice.Communicator;
+import com.zeroc.IceInternal.Time;
+
+import dalvik.system.DexClassLoader;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.app.Application;
+
+import Test.Common.ProcessControllerRegistryPrx;
+import Test.Common.ProcessControllerPrx;
+import test.Util.Application.CommunicatorListener;
+
+public class ControllerApp extends Application
+{
+ private final String TAG = "ControllerApp";
+ private ControllerHelper _helper;
+ private ControllerActivity _controller;
+ private java.util.Map<String, ClassLoader> _classLoaders = new java.util.HashMap<String, ClassLoader>();
+
+ private ClassLoader getDEXClassLoader(String classDir, ClassLoader parent) throws IOException
+ {
+ ClassLoader classLoader = _classLoaders.get(classDir);
+ if(classLoader == null)
+ {
+ if(parent == null)
+ {
+ parent = getClassLoader();
+ }
+
+ File dexInternalStoragePath = new java.io.File(getDir("dex", Context.MODE_PRIVATE), classDir);
+ BufferedInputStream bis = new BufferedInputStream(getAssets().open(classDir));
+ OutputStream dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath));
+ final int sz = 8 * 1024;
+ byte[] buf = new byte[sz];
+ int len;
+ while((len = bis.read(buf, 0, sz)) > 0)
+ {
+ dexWriter.write(buf, 0, len);
+ }
+ dexWriter.close();
+ bis.close();
+
+ // Internal storage where the DexClassLoader writes the optimized dex file to
+ final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
+
+ classLoader = new DexClassLoader(
+ dexInternalStoragePath.getAbsolutePath(),
+ optimizedDexOutputPath.getAbsolutePath(),
+ null,
+ parent);
+ _classLoaders.put(classDir, classLoader);
+ }
+ return classLoader;
+ }
+
+ static private class TestSuiteBundle
+ {
+ @SuppressWarnings("unchecked")
+ TestSuiteBundle(String name, ClassLoader loader)
+ {
+ _loader = loader;
+ try
+ {
+ _class = (Class<? extends test.Util.Application>)_loader.loadClass(name);
+ }
+ catch(ClassNotFoundException e)
+ {
+ }
+ }
+
+ test.Util.Application newInstance()
+ throws IllegalAccessException, InstantiationException
+ {
+ if(_class == null)
+ {
+ return null;
+ }
+ return _class.newInstance();
+ }
+
+ ClassLoader getClassLoader()
+ {
+ return _loader;
+ }
+
+ private String _name;
+ private ClassLoader _loader;
+ private Class<? extends test.Util.Application> _class;
+ }
+
+ class AndroidLogger implements Logger
+ {
+ private final String _prefix;
+
+ AndroidLogger(String prefix)
+ {
+ _prefix = prefix;
+ }
+
+ @Override
+ public void print(String message)
+ {
+ Log.d(TAG, message);
+ }
+
+ @Override
+ public void trace(String category, String message)
+ {
+ Log.v(category, message);
+ }
+
+ @Override
+ public void warning(String message)
+ {
+ Log.w(TAG, message);
+ }
+
+ @Override
+ public void error(String message)
+ {
+ Log.e(TAG, message);
+ }
+
+ @Override
+ public String getPrefix()
+ {
+ return _prefix;
+ }
+
+ @Override
+ public Logger cloneWithPrefix(String s)
+ {
+ return new AndroidLogger(s);
+ }
+ }
+
+ @Override
+ public void onCreate()
+ {
+ super.onCreate();
+ com.zeroc.Ice.Util.setProcessLogger(new AndroidLogger(""));
+ }
+
+ public synchronized void startController(ControllerActivity controller)
+ {
+ if(_helper == null)
+ {
+ _controller = controller;
+ _helper = new ControllerHelper();
+ }
+ else
+ {
+ _controller = controller;
+ }
+ }
+
+ public synchronized void println(final String data)
+ {
+ _controller.runOnUiThread(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ synchronized(ControllerApp.this)
+ {
+ _controller.println(data);
+ }
+ }
+ });
+ }
+
+ public static boolean isEmulator()
+ {
+ return Build.FINGERPRINT.startsWith("generic") ||
+ Build.FINGERPRINT.startsWith("unknown") ||
+ Build.MODEL.contains("google_sdk") ||
+ Build.MODEL.contains("Emulator") ||
+ Build.MODEL.contains("Android SDK built for x86") ||
+ Build.MANUFACTURER.contains("Genymotion") ||
+ (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
+ Build.PRODUCT.equals("google_sdk");
+ }
+
+ class ControllerHelper
+ {
+ public ControllerHelper()
+ {
+ com.zeroc.Ice.InitializationData initData = new com.zeroc.Ice.InitializationData();
+ initData.properties = com.zeroc.Ice.Util.createProperties();
+ initData.properties.setProperty("Ice.ThreadPool.Server.SizeMax", "10");
+ initData.properties.setProperty("ControllerAdapter.Endpoints", "tcp");
+ initData.properties.setProperty("Ice.Trace.Network", "3");
+ initData.properties.setProperty("Ice.Trace.Protocol", "1");
+
+ initData.properties.setProperty("ControllerAdapter.AdapterId", java.util.UUID.randomUUID().toString());
+ if(!isEmulator())
+ {
+ initData.properties.setProperty("Ice.Plugin.IceDiscovery", "IceDiscovery.PluginFactory");
+ initData.properties.setProperty("IceDiscovery.DomainId", "TestController");
+ }
+ _communicator = com.zeroc.Ice.Util.initialize(initData);
+ com.zeroc.Ice.ObjectAdapter adapter = _communicator.createObjectAdapter("ControllerAdapter");
+ ProcessControllerPrx processController = ProcessControllerPrx.uncheckedCast(
+ adapter.add(new ProcessControllerI("localhost"),
+ com.zeroc.Ice.Util.stringToIdentity("Android/ProcessController")));
+ adapter.activate();
+ if(isEmulator())
+ {
+ ProcessControllerRegistryPrx registry = ProcessControllerRegistryPrx.uncheckedCast(
+ _communicator.stringToProxy("Util/ProcessControllerRegistry:tcp -h 10.0.2.2 -p 15001").ice_invocationTimeout(300));
+ registerProcessController(adapter, registry, processController);
+ }
+ println("Android/ProcessController");
+ }
+
+ public void
+ registerProcessController(final com.zeroc.Ice.ObjectAdapter adapter,
+ final ProcessControllerRegistryPrx registry,
+ final ProcessControllerPrx processController)
+ {
+ registry.ice_pingAsync().whenCompleteAsync(
+ (r1, e1) ->
+ {
+ if(e1 != null)
+ {
+ handleException(e1, adapter, registry, processController);
+ }
+ else
+ {
+ com.zeroc.Ice.Connection connection = registry.ice_getConnection();
+ connection.setAdapter(adapter);
+ connection.setACM(OptionalInt.of(5),
+ Optional.of(com.zeroc.Ice.ACMClose.CloseOff),
+ Optional.of(com.zeroc.Ice.ACMHeartbeat.HeartbeatAlways));
+ connection.setCloseCallback(
+ con ->
+ {
+ println("connection with process controller registry closed");
+ while (true) {
+ try
+ {
+ Thread.sleep(500);
+ break;
+ }
+ catch(InterruptedException e)
+ {
+ }
+ }
+ registerProcessController(adapter, registry, processController);
+ });
+
+ registry.setProcessControllerAsync(processController).whenCompleteAsync(
+ (r2, e2) ->
+ {
+ if(e2 != null)
+ {
+ handleException(e2, adapter, registry, processController);
+ }
+ });
+ }
+ });
+ }
+
+ public void handleException(Throwable ex,
+ final com.zeroc.Ice.ObjectAdapter adapter,
+ final ProcessControllerRegistryPrx registry,
+ final ProcessControllerPrx processController)
+ {
+ if(ex instanceof com.zeroc.Ice.ConnectFailedException || ex instanceof com.zeroc.Ice.TimeoutException)
+ {
+ while(true)
+ {
+ try
+ {
+ Thread.sleep(500);
+ break;
+ }
+ catch(InterruptedException e)
+ {
+ }
+ }
+ registerProcessController(adapter, registry, processController);
+ }
+ else
+ {
+ println(ex.toString());
+ }
+ }
+
+ public void destroy()
+ {
+ _communicator.destroy();
+ }
+
+ private ProcessControllerRegistryPrx _registry;
+ private com.zeroc.Ice.Communicator _communicator;
+ }
+
+ class MainHelperI extends Thread implements test.Util.Application.ServerReadyListener
+ {
+ public MainHelperI(TestSuiteBundle bundle, String[] args, String exe)
+ {
+ _bundle = bundle;
+ _args = args;
+ }
+
+ public void run()
+ {
+ try
+ {
+ _app = _bundle.newInstance();
+ _app.setClassLoader(_bundle.getClassLoader());
+ _app.setCommunicatorListener(new CommunicatorListener()
+ {
+ public void communicatorInitialized(Communicator communicator)
+ {
+ if(communicator.getProperties().getProperty("Ice.Plugin.IceSSL").equals("com.zeroc.IceSSL.PluginFactory"))
+ {
+ com.zeroc.IceSSL.Plugin plugin = (com.zeroc.IceSSL.Plugin)communicator.getPluginManager().getPlugin("IceSSL");
+ String keystore = communicator.getProperties().getProperty("IceSSL.Keystore");
+ communicator.getProperties().setProperty("IceSSL.Keystore", "");
+ java.io.InputStream certs = getResources().openRawResource(
+ keystore.equals("client.bks") ? R.raw.client : R.raw.server);
+ plugin.setKeystoreStream(certs);
+ plugin.setTruststoreStream(certs);
+ communicator.getPluginManager().initializePlugins();
+ }
+ }
+ });
+ _app.setWriter(new Writer()
+ {
+ @Override
+ public void close() throws IOException
+ {
+ }
+
+ @Override
+ public void flush() throws IOException
+ {
+ }
+
+ @Override
+ public void write(char[] buf, int offset, int count)
+ throws IOException
+ {
+ _out.append(buf, offset, count);
+ }
+ });
+ _app.setServerReadyListener(this);
+
+ int status = _app.main(_exe, _args);
+ synchronized(this)
+ {
+ _status = status;
+ _completed = true;
+ notifyAll();
+ }
+ }
+ catch(Exception ex)
+ {
+ _out.append(ex.toString());
+ synchronized(this)
+ {
+ _status = -1;
+ _completed = true;
+ notifyAll();
+ }
+ }
+ }
+
+ public void shutdown()
+ {
+ if(_app != null)
+ {
+ _app.stop();
+ }
+ }
+
+ public String getOutput()
+ {
+ return _out.toString();
+ }
+
+ synchronized private void completed(int status)
+ {
+ _completed = true;
+ _status = status;
+ notifyAll();
+ }
+
+ synchronized public void serverReady()
+ {
+ _ready = true;
+ notifyAll();
+ }
+
+ synchronized private void waitReady(int timeout)
+ throws Test.Common.ProcessFailedException
+ {
+ long now = Time.currentMonotonicTimeMillis();
+ while(!_ready && !_completed)
+ {
+ try
+ {
+ wait(timeout * 1000);
+ if(Time.currentMonotonicTimeMillis() - now > timeout * 1000)
+ {
+ throw new Test.Common.ProcessFailedException("timed out waiting for the process to be ready");
+ }
+ }
+ catch(java.lang.InterruptedException ex)
+ {
+ }
+ }
+
+ if(_completed && _status != 0)
+ {
+ throw new Test.Common.ProcessFailedException(_out.toString());
+ }
+ }
+
+ synchronized private int waitSuccess(int timeout)
+ throws Test.Common.ProcessFailedException
+ {
+ long now = Time.currentMonotonicTimeMillis();
+ while(!_completed)
+ {
+ try
+ {
+ wait(timeout * 1000);
+ if(Time.currentMonotonicTimeMillis() - now > timeout * 1000)
+ {
+ throw new Test.Common.ProcessFailedException("timed out waiting for the process to be ready");
+ }
+ }
+ catch(java.lang.InterruptedException ex)
+ {
+ }
+ }
+ return _status;
+ }
+
+ private TestSuiteBundle _bundle;
+ private String[] _args;
+ private String _exe;
+ private test.Util.Application _app;
+ private boolean _ready = false;
+ private boolean _completed = false;
+ private int _status = 0;
+ private final StringBuffer _out = new StringBuffer();
+ }
+
+ class ProcessControllerI implements Test.Common.ProcessController
+ {
+ public ProcessControllerI(String hostname)
+ {
+ _hostname = hostname;
+ }
+
+ public Test.Common.ProcessPrx start(final String testsuite, final String exe, String[] args,
+ com.zeroc.Ice.Current current)
+ throws Test.Common.ProcessFailedException
+ {
+ println("starting " + testsuite + " " + exe + "... ");
+ String className = "test." + testsuite.replace("/", ".") + "." + exe.substring(0, 1).toUpperCase() + exe.substring(1);
+ String dexFile = "test_" + testsuite.replace("/", "_") + ".dex";
+ try
+ {
+ TestSuiteBundle bundle = new TestSuiteBundle(className, getDEXClassLoader(dexFile, null));
+ MainHelperI mainHelper = new MainHelperI(bundle, args, exe);
+ mainHelper.start();
+ return Test.Common.ProcessPrx.uncheckedCast(current.adapter.addWithUUID(new ProcessI(mainHelper)));
+ }
+ catch(IOException ex)
+ {
+ throw new Test.Common.ProcessFailedException(
+ "testsuite `" + testsuite + "' exe ` " + exe + "' start failed", ex);
+ }
+ }
+
+ public String getHost(String protocol, boolean ipv6,
+ com.zeroc.Ice.Current current)
+ {
+ return _hostname;
+ }
+
+ private String _hostname;
+ }
+
+ class ProcessI implements Test.Common.Process
+ {
+ public ProcessI(MainHelperI helper)
+ {
+ _helper = helper;
+ }
+
+ public void waitReady(int timeout, com.zeroc.Ice.Current current)
+ throws Test.Common.ProcessFailedException
+ {
+ _helper.waitReady(timeout);
+ }
+
+ public int waitSuccess(int timeout, com.zeroc.Ice.Current current)
+ throws Test.Common.ProcessFailedException
+ {
+ return _helper.waitSuccess(timeout);
+ }
+
+ public String terminate(com.zeroc.Ice.Current current)
+ {
+ _helper.shutdown();
+ current.adapter.remove(current.id);
+ while(true)
+ {
+ try
+ {
+ _helper.join();
+ break;
+ }
+ catch(InterruptedException ex)
+ {
+ }
+ }
+ return _helper.getOutput();
+ }
+
+ private MainHelperI _helper;
+ }
+}