Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android support. Phase I. #148

Merged
merged 2 commits into from
Jun 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions detox/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ ios/EarlGrey/Tests/UnitTests/TestRig/Resources
ios/EarlGrey/Tests/FunctionalTests/TestRig/Resources
Detox.framework
Detox.framework.tar
detox-debug.aar
detox-release.aar

lib

Expand Down
20 changes: 12 additions & 8 deletions detox/android/detox/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
apply plugin: 'com.android.library'

android {
compileSdkVersion 25
buildToolsVersion "25.0.1"
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand All @@ -16,16 +16,20 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
publishNonDefault true
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':invoke')
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.squareup.okhttp3:okhttp:3.5.0'
//compile project(':invoke')
compile "com.android.support:appcompat-v7:23.0.1"
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okhttp3:okhttp-ws:3.4.1'
testCompile 'junit:junit:4.12'
compile('com.android.support.test.espresso:espresso-core:2.2.2')
compile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.google.code.findbugs'
})
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}
}
108 changes: 108 additions & 0 deletions detox/android/detox/src/main/java/com/wix/detox/Delegator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.wix.detox;

import org.joor.Reflect;
import org.joor.ReflectException;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* Created by simonracz on 29/05/2017.
*/

/**
* <p>
* Helper class for InvocationHandlers, which delegates equals, hashCode and toString
* calls to Object.
* </p>
*
* <p>
* Copied from here
* <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html">Delegator</a>
* </p>
*/
public class Delegator implements InvocationHandler {

private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
static {
try {
hashCodeMethod = Object.class.getMethod("hashCode", null);
equalsMethod =
Object.class.getMethod("equals", new Class[] { Object.class });
toStringMethod = Object.class.getMethod("toString", null);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}

private Class[] interfaces;
private Object[] delegates;

public Delegator(Class[] interfaces, Object[] delegates) {
this.interfaces = (Class[]) interfaces.clone();
this.delegates = (Object[]) delegates.clone();
}

public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Class declaringClass = m.getDeclaringClass();

if (declaringClass == Object.class) {
if (m.equals(hashCodeMethod)) {
return proxyHashCode(proxy);
} else if (m.equals(equalsMethod)) {
return proxyEquals(proxy, args[0]);
} else if (m.equals(toStringMethod)) {
return proxyToString(proxy);
} else {
throw new InternalError(
"unexpected Object method dispatched: " + m);
}
} else {
for (int i = 0; i < interfaces.length; i++) {
if (declaringClass.isAssignableFrom(interfaces[i])) {
try {
return Reflect.on(delegates[i]).call(m.getName(), args).get();
} catch (ReflectException e) {
throw e.getCause();
}
}
}

return invokeNotDelegated(proxy, m, args);
}
}

// Simple workaround for a deeply rooted issue regarding Proxy classes
public Object invokeAsString(String methodName) throws ReflectException {
return Reflect.on(delegates[0]).call(methodName).get();
}

// Simple workaround for a deeply rooted issue regarding Proxy classes
public Object invokeAsString(String methodName, Object[] args) throws ReflectException {
return Reflect.on(delegates[0]).call(methodName, args).get();
}

protected Object invokeNotDelegated(Object proxy, Method m,
Object[] args)
throws Throwable
{
throw new InternalError("unexpected method dispatched: " + m);
}

protected Integer proxyHashCode(Object proxy) {
return new Integer(System.identityHashCode(proxy));
}

protected Boolean proxyEquals(Object proxy, Object other) {
return (proxy == other ? Boolean.TRUE : Boolean.FALSE);
}

protected String proxyToString(Object proxy) {
return proxy.getClass().getName() + '@' +
Integer.toHexString(proxy.hashCode());
}
}
124 changes: 124 additions & 0 deletions detox/android/detox/src/main/java/com/wix/detox/Detox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.wix.detox;

import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;

/**
* <p>Static class.</p>
*
* <p>To start Detox tests, call runTests() from a JUnit test.
* This test must use AndroidJUnitTestRunner or a subclass of it, as Detox uses Espresso internally.
* All non-standard async code must be wrapped in an Espresso
* <a href="https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/">IdlingResource</a>.</p>
*
* Example usage
* <pre>{@code
*@literal @runWith(AndroidJUnit4.class)
*@literal @LargeTest
* public class DetoxTest {
* @literal @Rule
* //The Activity that controls React Native.
* public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule(MainActivity.class);
*
* @literal @Before
* public void setUpCustomEspressoIdlingResources() {
* // set up your own custom Espresso resources here
* }
*
* @literal @Test
* public void runDetoxTests() {
* Detox.runTests();
* }
* }}</pre>
*
* <p>Two required parameters are detoxServer and detoxSessionId. These
* must be provided either by Gradle.
* <br>
*
* <pre>{@code
* android {
* defaultConfig {
* testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
* testInstrumentationRunnerArguments = [
* 'detoxServer': 'ws://10.0.2.2:8001',
* 'detoxSessionId': '1'
* ]
* }
* }}
* </pre>
*
* Or through command line, e.g <br>
* <blockquote>{@code adb shell am instrument -w -e detoxServer ws://localhost:8001 -e detoxSessionId
* 1 com.example/android.support.test.runner.AndroidJUnitRunner}</blockquote></p>
*
* <p>These are automatically set using,
* <blockquote>{@code detox test}</blockquote></p>
*
* <p>If not set, then Detox tests are no ops. So it's safe to mix it with other tests.</p>
*/
public final class Detox {
private Detox() {
// static class
}

/**
* <p>
* Call this method from a JUnit test to invoke detox tests.
* </p>
*
* <p>
* In case you have a non-standard React Native application, consider using
* {@link Detox#runTests(Object)}.
* </p>
*/
public static void runTests() {
Object appContext = InstrumentationRegistry.getTargetContext().getApplicationContext();
runTests(appContext);
}

/**
* <p>
* Call this method only if you have a React Native application and it
* doesn't implement ReactApplication.
* </p>
*
* Call {@link Detox#runTests()} in every other case.
*
* <p>
* The only requirement is that the passed in object must have
* a method with the signature
* <blockquote>{@code ReactNativeHost getReactNativeHost();}</blockquote>
* </p>
*
* @param reactActivityDelegate an object that has a {@code getReactNativeHost()} method
*/
public static void runTests(@NonNull final Object reactActivityDelegate) {
// Kicks off another thread and attaches a Looper to that.
// The goal is to keep the test thread intact,
// as Loopers can't run on a thread twice.
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
DetoxManager detoxManager = new DetoxManager(reactActivityDelegate);
detoxManager.start();
}
});
Looper.loop();
}
}, "com.wix.detox.manager");
t.start();
try {
t.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Got interrupted", e);
}
}
}
Loading