Skip to content

Commit 28a34fe

Browse files
authored
Refactor & reimpl DetoxManager (Android) to make debug-sync work properly (wix#2703)
1 parent 9d4e60f commit 28a34fe

File tree

17 files changed

+370
-302
lines changed

17 files changed

+370
-302
lines changed

detox/android/detox/src/full/java/com/wix/detox/Detox.java

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import android.content.Context;
66
import android.content.Intent;
77
import android.os.Bundle;
8-
import android.os.Looper;
9-
import android.util.Log;
108

119
import com.wix.detox.config.DetoxConfig;
1210
import com.wix.detox.espresso.UiControllerSpy;
@@ -18,8 +16,6 @@
1816
import androidx.test.platform.app.InstrumentationRegistry;
1917
import androidx.test.rule.ActivityTestRule;
2018

21-
import static com.wix.detox.common.DetoxLog.LOG_TAG;
22-
2319
/**
2420
* <p>Static class.</p>
2521
*
@@ -200,25 +196,9 @@ public static void runTests(ActivityTestRule activityTestRule, @NonNull final Co
200196
Intent intent = extractInitialIntent();
201197
sActivityTestRule.launchActivity(intent);
202198

203-
// Kicks off another thread and attaches a Looper to that.
204-
// The goal is to keep the test thread intact,
205-
// as Loopers can't run on a thread twice.
206-
final Thread t = new Thread(new Runnable() {
207-
@Override
208-
public void run() {
209-
Thread thread = Thread.currentThread();
210-
Log.i(LOG_TAG, "Detox thread starting (" + thread.getName() + ")");
211-
212-
Looper.prepare();
213-
new DetoxManager(context).start();
214-
Looper.loop();
215-
}
216-
}, "com.wix.detox.manager");
217-
t.start();
218-
219199
try {
220-
t.join();
221-
} catch (InterruptedException e) {
200+
DetoxMain.run(context);
201+
} catch (Exception e) {
222202
Thread.currentThread().interrupt();
223203
throw new RuntimeException("Detox got interrupted prematurely", e);
224204
}

detox/android/detox/src/full/java/com/wix/detox/DetoxANRHandler.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ package com.wix.detox
22

33
import android.util.Log
44
import com.github.anrwatchdog.ANRWatchDog
5+
import com.wix.detox.adapters.server.OutboundServerAdapter
56

6-
class DetoxANRHandler(private val wsClient: WebSocketClient) {
7+
class DetoxANRHandler(private val outboundServerAdapter: OutboundServerAdapter) {
78
fun attach() {
89
ANRWatchDog().setReportMainThreadOnly().setANRListener {
910
val info = mapOf("threadDump" to Log.getStackTraceString(it))
10-
wsClient.sendAction(ACTION_NAME, info, MESSAGE_ID)
11+
outboundServerAdapter.sendMessage(ACTION_NAME, info, MESSAGE_ID)
1112
}.start()
1213

1314
ANRWatchDog().setANRListener {

detox/android/detox/src/full/java/com/wix/detox/DetoxCrashHandler.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.wix.detox
22

33
import android.util.Log
4+
import com.wix.detox.adapters.server.OutboundServerAdapter
45

5-
class DetoxCrashHandler(private val wsClient: WebSocketClient) {
6+
class DetoxCrashHandler(private val outboundServerAdapter: OutboundServerAdapter) {
67
fun attach() {
78
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
89
Log.e(LOG_TAG, "Crash detected!!! thread=${thread.name} (${thread.id})")
910

1011
val crashInfo = mapOf("errorDetails" to "@Thread ${thread.name}(${thread.id}):\n${Log.getStackTraceString(exception)}\nCheck device logs for full details!")
11-
wsClient.sendAction(ACTION_NAME, crashInfo, MESSAGE_ID)
12+
outboundServerAdapter.sendMessage(ACTION_NAME, crashInfo, MESSAGE_ID)
1213
}
1314
}
1415

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.wix.detox
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import com.wix.detox.adapters.server.*
6+
import com.wix.detox.common.DetoxLog.Companion.LOG_TAG
7+
import com.wix.detox.instruments.DetoxInstrumentsManager
8+
import com.wix.detox.reactnative.ReactNativeExtension
9+
import com.wix.invoke.MethodInvocation
10+
11+
private const val INIT_ACTION = "_init"
12+
private const val IS_READY_ACTION = "isReady"
13+
private const val TERMINATION_ACTION = "_terminate"
14+
15+
object DetoxMain {
16+
@JvmStatic
17+
fun run(rnHostHolder: Context) {
18+
val detoxServerInfo = DetoxServerInfo()
19+
Log.i(LOG_TAG, "Detox server connection details: $detoxServerInfo")
20+
21+
val testEngineFacade = TestEngineFacade()
22+
val actionsDispatcher = DetoxActionsDispatcher()
23+
val externalAdapter = DetoxServerAdapter(actionsDispatcher, detoxServerInfo, IS_READY_ACTION, TERMINATION_ACTION)
24+
initActionHandlers(actionsDispatcher, externalAdapter, testEngineFacade, rnHostHolder)
25+
actionsDispatcher.dispatchAction(INIT_ACTION, "", 0)
26+
actionsDispatcher.join()
27+
}
28+
29+
private fun doInit(externalAdapter: DetoxServerAdapter, rnHostHolder: Context) {
30+
externalAdapter.connect()
31+
32+
initCrashHandler(externalAdapter)
33+
initANRListener(externalAdapter)
34+
initReactNativeIfNeeded(rnHostHolder)
35+
}
36+
37+
private fun doTeardown(serverAdapter: DetoxServerAdapter, actionsDispatcher: DetoxActionsDispatcher, testEngineFacade: TestEngineFacade) {
38+
testEngineFacade.resetReactNative()
39+
40+
serverAdapter.teardown()
41+
actionsDispatcher.teardown()
42+
}
43+
44+
private fun initActionHandlers(actionsDispatcher: DetoxActionsDispatcher, serverAdapter: DetoxServerAdapter, testEngineFacade: TestEngineFacade, rnHostHolder: Context) {
45+
// Primary actions
46+
with(actionsDispatcher) {
47+
val rnReloadHandler = ReactNativeReloadActionHandler(rnHostHolder, serverAdapter, testEngineFacade)
48+
49+
associateActionHandler(INIT_ACTION, object : DetoxActionHandler {
50+
override fun handle(params: String, messageId: Long) =
51+
synchronized(this@DetoxMain) {
52+
this@DetoxMain.doInit(serverAdapter, rnHostHolder)
53+
}
54+
})
55+
associateActionHandler(IS_READY_ACTION, ReadyActionHandler(serverAdapter, testEngineFacade))
56+
57+
associateActionHandler("loginSuccess", ScarceActionHandler())
58+
associateActionHandler("reactNativeReload", object: DetoxActionHandler {
59+
override fun handle(params: String, messageId: Long) =
60+
synchronized(this@DetoxMain) {
61+
rnReloadHandler.handle(params, messageId)
62+
}
63+
})
64+
associateActionHandler("invoke", InvokeActionHandler(MethodInvocation(), serverAdapter))
65+
associateActionHandler("cleanup", CleanupActionHandler(serverAdapter, testEngineFacade) {
66+
dispatchAction(TERMINATION_ACTION, "", 0)
67+
})
68+
associateActionHandler(TERMINATION_ACTION, object: DetoxActionHandler {
69+
override fun handle(params: String, messageId: Long) {
70+
this@DetoxMain.doTeardown(serverAdapter, actionsDispatcher, testEngineFacade)
71+
}
72+
})
73+
74+
if (DetoxInstrumentsManager.supports()) {
75+
val instrumentsManager = DetoxInstrumentsManager(rnHostHolder)
76+
associateActionHandler("setRecordingState", InstrumentsRecordingStateActionHandler(instrumentsManager, serverAdapter))
77+
associateActionHandler("event", InstrumentsEventsActionsHandler(instrumentsManager, serverAdapter))
78+
}
79+
}
80+
81+
// Secondary actions
82+
with(actionsDispatcher) {
83+
val queryStatusHandler = QueryStatusActionHandler(serverAdapter, testEngineFacade)
84+
associateActionHandler("currentStatus", object: DetoxActionHandler {
85+
override fun handle(params: String, messageId: Long) =
86+
synchronized(this@DetoxMain) {
87+
queryStatusHandler.handle(params, messageId)
88+
}
89+
}, false)
90+
}
91+
}
92+
93+
private fun initCrashHandler(outboundServerAdapter: OutboundServerAdapter) {
94+
DetoxCrashHandler(outboundServerAdapter).attach()
95+
}
96+
97+
private fun initANRListener(outboundServerAdapter: OutboundServerAdapter) {
98+
DetoxANRHandler(outboundServerAdapter).attach()
99+
}
100+
101+
private fun initReactNativeIfNeeded(rnHostHolder: Context) {
102+
ReactNativeExtension.waitForRNBootstrap(rnHostHolder)
103+
}
104+
}

detox/android/detox/src/full/java/com/wix/detox/DetoxManager.java

Lines changed: 0 additions & 170 deletions
This file was deleted.

0 commit comments

Comments
 (0)