-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Android support. Phase I.
- Loading branch information
Showing
24 changed files
with
2,096 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
detox/android/detox/src/main/java/com/wix/detox/Delegator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
124
detox/android/detox/src/main/java/com/wix/detox/Detox.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
Oops, something went wrong.