summaryrefslogtreecommitdiff
path: root/logcat
diff options
context:
space:
mode:
authorAlon Albert <aalbert@google.com>2022-07-21 07:59:12 -0700
committerTreeHugger Robot <treehugger-gerrit@google.com>2022-07-21 21:24:28 +0000
commitfb94495ec89fcce5f11f82d2de0f2930f4fbad4a (patch)
tree7836b5c2bfa3e33d18f46da2e2d5ec376c96d291 /logcat
parentb5dd8b48cb5bb22c81aea3e00fa1ba81feca7533 (diff)
downloadidea-fb94495ec89fcce5f11f82d2de0f2930f4fbad4a.tar.gz
Change DeviceComboBoxDeviceTrackerTest to Use a Fake Adb Server
Fixes: n/a Test: Self Change-Id: I1eaa56943f204b99dacaaf5e0c1517b1490adb1e
Diffstat (limited to 'logcat')
-rw-r--r--logcat/BUILD1
-rw-r--r--logcat/intellij.android.logcat.tests.iml1
-rw-r--r--logcat/src/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTracker.kt12
-rw-r--r--logcat/src/com/android/tools/idea/logcat/devices/IDeviceComboBoxDeviceTracker.kt2
-rw-r--r--logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTrackerTest.kt451
-rw-r--r--logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxTest.kt2
-rw-r--r--logcat/testSrc/com/android/tools/idea/logcat/testing/TestDevice.kt6
7 files changed, 270 insertions, 205 deletions
diff --git a/logcat/BUILD b/logcat/BUILD
index ba8b07f1615..22f5fba18fe 100644
--- a/logcat/BUILD
+++ b/logcat/BUILD
@@ -59,5 +59,6 @@ iml_module(
"//tools/adt/idea/artwork:intellij.android.artwork[module, test]",
"//tools/base/adblib:studio.android.sdktools.adblib[module, test]",
"//tools/adt/idea/android-adb:intellij.android.adb[module, test]",
+ "//tools/base/fakeadbserver:studio.android.sdktools.fakeadbserver[module, test]",
],
)
diff --git a/logcat/intellij.android.logcat.tests.iml b/logcat/intellij.android.logcat.tests.iml
index 88cd097bde4..7b2301aae12 100644
--- a/logcat/intellij.android.logcat.tests.iml
+++ b/logcat/intellij.android.logcat.tests.iml
@@ -26,6 +26,7 @@
<orderEntry type="module" module-name="intellij.android.artwork" scope="TEST" />
<orderEntry type="module" module-name="android.sdktools.adblib" scope="TEST" />
<orderEntry type="module" module-name="intellij.android.adb" scope="TEST" />
+ <orderEntry type="module" module-name="android.sdktools.fakeadbserver" scope="TEST" />
</component>
<component name="TestModuleProperties" production-module="intellij.android.logcat" />
</module> \ No newline at end of file
diff --git a/logcat/src/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTracker.kt b/logcat/src/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTracker.kt
index e2eda0b68a2..f38ebab8e27 100644
--- a/logcat/src/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTracker.kt
+++ b/logcat/src/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTracker.kt
@@ -46,7 +46,9 @@ internal class DeviceComboBoxDeviceTracker(
private val coroutineContext: CoroutineContext = Dispatchers.IO,
) : IDeviceComboBoxDeviceTracker {
- override suspend fun trackDevices(): Flow<DeviceEvent> {
+ override suspend fun trackDevices(
+ /* For test only. Temporary until we switch to the new trackDevices API */ retryOnException: Boolean
+ ): Flow<DeviceEvent> {
return flow {
while (true) {
// TODO(b/228224334): This should be handled internally by AdbLib
@@ -54,9 +56,11 @@ internal class DeviceComboBoxDeviceTracker(
trackDevicesInternal()
}
catch (e: IOException) {
- LOGGER.info("Device tracker exception, restarting it...", e)
emit(TrackingReset(e))
- continue
+ if (retryOnException) {
+ LOGGER.info("Device tracker exception, restarting it...", e)
+ continue
+ }
}
break
}
@@ -88,7 +92,7 @@ internal class DeviceComboBoxDeviceTracker(
// We only care about devices that are online.
// If a previously unknown device comes online, we emit Added
// If a previously known device comes online, we emit StateChanged
- // If previously online device is missing from the kist, we emit a StateChanged.
+ // If previously online device is missing from the list, we emit a StateChanged.
adbSession.hostServices.trackDevices().collect { deviceList ->
val devices = deviceList.entries.filter { it.isOnline() }.associateBy { it.serialNumber }
devices.values.forEach {
diff --git a/logcat/src/com/android/tools/idea/logcat/devices/IDeviceComboBoxDeviceTracker.kt b/logcat/src/com/android/tools/idea/logcat/devices/IDeviceComboBoxDeviceTracker.kt
index 2dd564032c7..bdb0be4afa3 100644
--- a/logcat/src/com/android/tools/idea/logcat/devices/IDeviceComboBoxDeviceTracker.kt
+++ b/logcat/src/com/android/tools/idea/logcat/devices/IDeviceComboBoxDeviceTracker.kt
@@ -25,6 +25,6 @@ import kotlinx.coroutines.flow.Flow
* Devices are not removed when they go offline.
*/
internal interface IDeviceComboBoxDeviceTracker {
- suspend fun trackDevices(): Flow<DeviceEvent>
+ suspend fun trackDevices(retryOnException: Boolean = true): Flow<DeviceEvent>
}
diff --git a/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTrackerTest.kt b/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTrackerTest.kt
index b8650c20382..86cb5e9cc22 100644
--- a/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTrackerTest.kt
+++ b/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxDeviceTrackerTest.kt
@@ -15,301 +15,360 @@
*/
package com.android.tools.idea.logcat.devices
+import com.android.adblib.AdbChannelProviderFactory
import com.android.adblib.AdbSession
-import com.android.adblib.DeviceState.AUTHORIZING
import com.android.adblib.DeviceState.OFFLINE
import com.android.adblib.DeviceState.ONLINE
-import com.android.adblib.testing.FakeAdbSession
+import com.android.adblib.testingutils.CloseablesRule
+import com.android.adblib.testingutils.TestingAdbSessionHost
+import com.android.ddmlib.testing.FakeAdbRule
+import com.android.fakeadbserver.DeviceState
+import com.android.fakeadbserver.FakeAdbServer
+import com.android.fakeadbserver.devicecommandhandlers.DeviceCommandHandler
import com.android.tools.idea.logcat.devices.DeviceEvent.Added
import com.android.tools.idea.logcat.devices.DeviceEvent.StateChanged
import com.android.tools.idea.logcat.devices.DeviceEvent.TrackingReset
import com.android.tools.idea.logcat.testing.TestDevice
-import com.android.tools.idea.logcat.testing.sendDevices
-import com.android.tools.idea.logcat.testing.setDevices
-import com.android.tools.idea.logcat.testing.setupCommandsForDevice
import com.google.common.truth.Truth.assertThat
import com.intellij.testFramework.ProjectRule
-import kotlinx.coroutines.async
+import com.intellij.testFramework.RuleChain
+import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.flow.toList
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
+import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Rule
import org.junit.Test
-import java.io.IOException
+import java.net.Socket
+import java.time.Duration
+import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
+private const val GET_PROPS_DEVICE =
+ "getprop ro.build.version.release ; getprop ro.build.version.sdk ; getprop ro.product.manufacturer ; getprop ro.product.model"
+
+private const val GET_PROPS_EMULATOR =
+ "getprop ro.build.version.release ; getprop ro.build.version.sdk ; getprop ro.boot.qemu.avd_name ; getprop ro.kernel.qemu.avd_name"
+
/**
* Tests for [DeviceComboBoxDeviceTracker]
*/
@Suppress("OPT_IN_USAGE") // runBlockingTest is experimental
class DeviceComboBoxDeviceTrackerTest {
+ private val projectRule = ProjectRule()
+ private val fakeAdb = FakeAdbRule()
+ private val closeables = CloseablesRule()
+
+ private val deviceCommandHandler = MyDeviceCommandHandler()
+
@get:Rule
- val projectRule = ProjectRule()
+ val rule = RuleChain(projectRule, fakeAdb.withDeviceCommandHandler(deviceCommandHandler), closeables)
- private val adbSession = FakeAdbSession()
- private val hostServices = adbSession.hostServices
- private val deviceServices = adbSession.deviceServices
+ private val device1 = TestDevice("device-1", ONLINE, 11, 30, "manufacturer1", "model1")
+ private val device2 = TestDevice("device-2", ONLINE, 12, 31, "manufacturer2", "model2")
+ private val emulator1 = TestDevice("emulator-1", ONLINE, 11, 30, avdName = "avd1")
+ private val emulator2 = TestDevice("emulator-2", ONLINE, 11, 30, avdNamePre31 = "avd2")
+ private val emulator3 = TestDevice("emulator-3", ONLINE, 11, 30, avdName = "", avdNamePre31 = "")
- private val device1 = TestDevice("device-1", ONLINE, 11, 30, "manufacturer1", "model1", avdName = "")
- private val device2 = TestDevice("device-2", ONLINE, 12, 31, "manufacturer2", "model2", avdName = "")
- private val emulator1 = TestDevice("emulator-1", ONLINE, 11, 30, manufacturer = "", model = "", avdName = "avd1")
+ private val events = mutableListOf<DeviceEvent>()
@Before
fun setUp() {
- deviceServices.setupCommandsForDevice(device1)
- deviceServices.setupCommandsForDevice(device2)
- deviceServices.setupCommandsForDevice(emulator1)
- deviceServices.setupCommandsForDevice(emulator1.withSerialNumber("emulator-2"))
+ deviceCommandHandler.setupDevice(device1)
+ deviceCommandHandler.setupDevice(device2)
+ deviceCommandHandler.setupDevice(emulator1)
+ deviceCommandHandler.setupDevice(emulator2)
+ deviceCommandHandler.setupDevice(emulator3)
}
@Test
- fun initialDevices(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
- hostServices.setDevices(device1, emulator1)
-
- val events = async { deviceTracker.trackDevices().toList() }
- hostServices.closeTrackDevicesFlow()
-
- assertThat(events.await()).containsExactly(
- Added(device1.device),
- Added(emulator1.device),
- ).inOrder()
- }
+ fun name(): Unit = runBlocking {
+ fakeAdb.attachDevices(device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
- @Test
- fun emulatorWithLegacyAvdName(): Unit = runBlockingTest {
- val emulator =
- TestDevice("emulator-3", ONLINE, 10, 29, manufacturer = "", model = "", avdName = "", avdNamePre31 = "avd3")
- adbSession.deviceServices.setupCommandsForDevice(emulator)
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
-
- hostServices.use {
- it.sendDevices(emulator)
- it.sendDevices(emulator.withState(OFFLINE))
- it.sendDevices()
+ launch {
+ deviceTracker.trackDevices(retryOnException = false).toList(events)
}
- assertThat(events.await()).containsExactly(
- Added(Device.createEmulator(emulator.serialNumber, true, emulator.release, emulator.sdk, emulator.avdNamePre31)),
- StateChanged(Device.createEmulator(emulator.serialNumber, false, emulator.release, emulator.sdk, emulator.avdNamePre31)),
- ).inOrder()
+ yieldUntil { events.size == 1 }
+ //waitForCondition(5, SECONDS) { events.size == 1 }
+ println(events)
+ fakeAdb.stop()
}
@Test
- fun emulatorWithoutAvdProperty(): Unit = runBlockingTest {
- val emulator =
- TestDevice("emulator-3", ONLINE, 10, 29, manufacturer = "", model = "", avdName = "", avdNamePre31 = "")
- adbSession.deviceServices.setupCommandsForDevice(emulator)
-
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
+ fun initialDevices(): Unit = runBlocking {
+ val initialDevices = arrayOf(device1, emulator1, emulator2, emulator3)
+ fakeAdb.attachDevices(*initialDevices)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
- val events = async { deviceTracker.trackDevices().toList() }
-
- hostServices.use {
- it.sendDevices(emulator)
- it.sendDevices(emulator.withState(OFFLINE))
- it.sendDevices()
- }
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
- assertThat(events.await()).containsExactly(
- Added(Device.createEmulator(emulator.serialNumber, true, emulator.release, emulator.sdk, emulator.serialNumber)),
- StateChanged(Device.createEmulator(emulator.serialNumber, false, emulator.release, emulator.sdk, emulator.serialNumber)),
- ).inOrder()
+ yieldUntil { events.size == initialDevices.size }
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
+ Added(device1.device),
+ Added(emulator1.device),
+ Added(emulator2.device),
+ Added(emulator3.device.copy(deviceId = emulator3.serialNumber, name = emulator3.serialNumber)),
+ )
}
@Test
- fun initialDevices_ignoresOffline(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
- hostServices.setDevices(device1, emulator1.withState(AUTHORIZING))
+ fun initialDevices_ignoresOffline(): Unit = runBlocking {
+ fakeAdb.attachDevices(device1, device2.withState(OFFLINE))
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
- val events = async { deviceTracker.trackDevices().toList() }
- hostServices.closeTrackDevicesFlow()
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
- assertThat(events.await()).containsExactly(
+ yieldUntil { events.size == 1 }
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
Added(device1.device),
- ).inOrder()
+ )
}
@Test
- fun initialDevices_withPreexistingDevice(): Unit = runBlockingTest {
- val emulator1Offline = emulator1.withState(OFFLINE)
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession, preexistingDevice = emulator1Offline.device)
- hostServices.setDevices(device1)
+ fun initialDevices_withInitialPreexistingDevice(): Unit = runBlocking {
+ val preexistingEmulator = emulator1.withState(OFFLINE).withSerialNumber("")
+ fakeAdb.attachDevices(device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession(), preexistingDevice = preexistingEmulator.device)
+
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
- val events = async { deviceTracker.trackDevices().toList() }
- hostServices.closeTrackDevicesFlow()
+ yieldUntil { events.size == 2 }
+ fakeAdb.stop()
+ job.join()
- assertThat(events.await()).containsExactly(
+ assertThat(events.dropTrackingReset()).containsExactly(
Added(device1.device),
- Added(emulator1Offline.device),
- ).inOrder()
+ Added(preexistingEmulator.device),
+ )
}
@Test
- fun initialDevices_withInitialPreexistingDevice(): Unit = runBlockingTest {
+ fun initialDevices_withInitialPreexistingDeviceMatchingOnlineDevice(): Unit = runBlocking {
val preexistingEmulator = emulator1.withState(OFFLINE).withSerialNumber("")
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession, preexistingDevice = preexistingEmulator.device)
- hostServices.setDevices(emulator1, device1)
+ fakeAdb.attachDevices(emulator1, device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession(), preexistingDevice = preexistingEmulator.device)
- val events = async { deviceTracker.trackDevices().toList() }
- hostServices.closeTrackDevicesFlow()
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
- assertThat(events.await()).containsExactly(
+ yieldUntil { events.size == 2 }
+ fakeAdb.stop()
+ job.join()
+
+ assertThat(events.dropTrackingReset()).containsExactly(
Added(emulator1.device),
Added(device1.device),
- ).inOrder()
+ )
}
@Test
- fun deviceAdded(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
-
- hostServices.use {
- it.sendDevices(device1, device2)
- it.sendDevices(device1, device2, emulator1)
- }
-
- assertThat(events.await()).containsExactly(
- Added(device1.device),
+ fun deviceAdded(): Unit = runBlocking {
+ val preexistingDevice = device1.withState(OFFLINE).device
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession(), preexistingDevice = preexistingDevice)
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
+ yieldUntil { events.size == 1 }
+
+ fakeAdb.attachDevices(device2)
+ fakeAdb.attachDevices(emulator1)
+
+ yieldUntil { events.size == 3 }
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
+ Added(preexistingDevice),
Added(device2.device),
Added(emulator1.device),
- ).inOrder()
+ )
}
@Test
- fun deviceAdded_ignoreOffline(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
+ fun changeState_goesOffline(): Unit = runBlocking {
+ val deviceState = fakeAdb.attachDevice(device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
+ yieldUntil { events.size == 1 }
- val events = async { deviceTracker.trackDevices().toList() }
+ deviceState.deviceStatus = DeviceState.DeviceStatus.OFFLINE
- hostServices.use {
- it.sendDevices(device1, device2.withState(OFFLINE))
- }
+ yieldUntil { events.size == 2 }
- assertThat(events.await()).containsExactly(
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
Added(device1.device),
+ StateChanged(device1.withState(OFFLINE).device),
).inOrder()
}
@Test
- fun deviceAdded_ignoreIfAlreadyAdded(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
-
- hostServices.use {
- it.sendDevices(device1)
- it.sendDevices(device1)
- }
-
- assertThat(events.await()).containsExactly(
+ fun changeState_goesOfflineComesOnline(): Unit = runBlocking {
+ val deviceState = fakeAdb.attachDevice(device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
+ yieldUntil { events.size == 1 }
+
+ deviceState.deviceStatus = DeviceState.DeviceStatus.OFFLINE
+ yieldUntil { events.size == 2 }
+ deviceState.deviceStatus = DeviceState.DeviceStatus.ONLINE
+ yieldUntil { events.size == 3 }
+
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
Added(device1.device),
+ StateChanged(device1.withState(OFFLINE).device),
+ StateChanged(device1.device),
).inOrder()
}
@Test
- fun changeState_goesOffline(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
-
- hostServices.use {
- it.sendDevices(device1)
- it.sendDevices(device1.withState(OFFLINE))
- }
-
- assertThat(events.await()).containsExactly(
- Added(device1.device.copy(isOnline = true)),
- StateChanged(device1.device.copy(isOnline = false)),
+ fun changeState_disconnects(): Unit = runBlocking {
+ fakeAdb.attachDevice(device1)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
+ yieldUntil { events.size == 1 }
+
+ fakeAdb.disconnectDevice(device1.serialNumber)
+ yieldUntil { events.size == 2 }
+
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
+ Added(device1.device),
+ StateChanged(device1.withState(OFFLINE).device),
).inOrder()
}
- @Test
- fun changeState_ignoreSameState(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
- hostServices.use {
- it.sendDevices(device1.withState(ONLINE))
- it.sendDevices(device1.withState(ONLINE))
- it.sendDevices(device1.withState(OFFLINE))
- it.sendDevices(device1.withState(OFFLINE))
- }
-
- assertThat(events.await()).containsExactly(
- Added(device1.device),
- StateChanged(device1.device.copy(isOnline = false)),
+ @Test
+ fun changeState_emulatorComesOnlineWithDifferentSerialNumber(): Unit = runBlocking {
+ val emulator = emulator1.withSerialNumber("emulator-1")
+ fakeAdb.attachDevice(emulator)
+ val deviceTracker = deviceComboBoxDeviceTracker(adbSession = fakeAdb.createAdbSession())
+ val job = launch { deviceTracker.trackDevices(retryOnException = false).toList(events) }
+ yieldUntil { events.size == 1 }
+
+ fakeAdb.disconnectDevice(emulator1.serialNumber)
+ yieldUntil { events.size == 2 }
+ val emulatorReconnectedOnDifferentPort = emulator1.withSerialNumber("emulator-2")
+ deviceCommandHandler.setupDevice(emulatorReconnectedOnDifferentPort)
+ fakeAdb.attachDevice(emulatorReconnectedOnDifferentPort)
+ yieldUntil { events.size == 3 }
+
+ fakeAdb.stop()
+ job.join()
+ assertThat(events.dropTrackingReset()).containsExactly(
+ Added(emulator.device),
+ StateChanged(emulator1.withState(OFFLINE).device),
+ StateChanged(emulatorReconnectedOnDifferentPort.device),
).inOrder()
}
- @Test
- fun changeState_goesOfflineComesOnline(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
+ private fun deviceComboBoxDeviceTracker(
+ preexistingDevice: Device? = null,
+ adbSession: AdbSession = fakeAdb.createAdbSession(),
+ coroutineContext: CoroutineContext = EmptyCoroutineContext,
+ ) = DeviceComboBoxDeviceTracker(projectRule.project, preexistingDevice, adbSession, coroutineContext)
- val events = async { deviceTracker.trackDevices().toList() }
+ private class MyDeviceCommandHandler : DeviceCommandHandler("") {
- hostServices.use {
- it.sendDevices(device1.withState(ONLINE))
- it.sendDevices(device1.withState(OFFLINE))
- it.sendDevices()
- it.sendDevices(device1.withState(ONLINE))
- }
+ private val devices = mutableMapOf<String, TestDevice>()
- assertThat(events.await()).containsExactly(
- Added(device1.device.copy(isOnline = true)),
- StateChanged(device1.device.copy(isOnline = false)),
- StateChanged(device1.device.copy(isOnline = true)),
- ).inOrder()
- }
+ fun setupDevice(device: TestDevice) {
+ devices[device.serialNumber] = device
+ }
- @Test
- fun changeState_emulatorComesOnlineWithDifferentSerialNumber(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
+ override fun accept(server: FakeAdbServer, socket: Socket, deviceState: DeviceState, command: String, args: String): Boolean {
+ if (command != "shell") {
+ return false
+ }
+ val result = when (args) {
+ GET_PROPS_DEVICE -> getDeviceProps(deviceState.deviceId)
+ GET_PROPS_EMULATOR -> getEmulatorProps(deviceState.deviceId)
+ else -> return false
+ }
+
+ val stream = socket.getOutputStream()
+ writeOkay(stream)
+ writeString(stream, result)
+
+ return true
+ }
- val events = async { deviceTracker.trackDevices().toList() }
+ private fun getDeviceProps(serialNumber: String): String {
+ val device = getDevice(serialNumber)
+ return """
+ ${device.release}
+ ${device.sdk}
+ ${device.manufacturer}
+ ${device.model}
+ """.trimIndent() + "\n"
+ }
- hostServices.use {
- it.sendDevices(emulator1.withState(ONLINE).withSerialNumber("emulator-1"))
- it.sendDevices(emulator1.withState(OFFLINE))
- it.sendDevices()
- it.sendDevices(emulator1.withState(ONLINE).withSerialNumber("emulator-2"))
+ private fun getEmulatorProps(serialNumber: String): String {
+ val device = getDevice(serialNumber)
+ return """
+ ${device.release}
+ ${device.sdk}
+ ${device.avdName}
+ ${device.avdNamePre31}
+ """.trimIndent() + "\n"
}
- assertThat(events.await()).containsExactly(
- Added(emulator1.device.copy(serialNumber = "emulator-1")),
- StateChanged(emulator1.device.copy(isOnline = false)),
- StateChanged(emulator1.device.copy(isOnline = true, serialNumber = "emulator-2")),
- ).inOrder()
+ private fun getDevice(serialNumber: String) = devices[serialNumber] ?: throw AssertionError("Device $serialNumber not set up")
}
+}
- @Test
- fun trackDevicesThrows(): Unit = runBlockingTest {
- val deviceTracker = deviceComboBoxDeviceTracker(adbSession = adbSession)
-
- val events = async { deviceTracker.trackDevices().toList() }
+/**
+ * Remove the last [DeviceEvent] from the list asserting it is a [TrackingReset] event
+ *
+ * Since we stop the ADB server, we expect the flow to emit a TrackingReset event, but we don't want to have to specify it every time.
+ */
+private fun <E> MutableList<E>.dropTrackingReset(): List<E> {
+ assertThat(last()).isInstanceOf(TrackingReset::class.java)
+ return dropLast(1)
+}
- hostServices.sendDevices(device1)
- val ioException = IOException("error while tracking")
- hostServices.closeTrackDevicesFlow(-1, ioException)
- hostServices.sendDevices(device1)
+private fun FakeAdbRule.attachDevices(vararg devices: TestDevice): List<DeviceState> = devices.map(::attachDevice)
- hostServices.close()
- assertThat(events.await()).containsExactly(
- Added(device1.device),
- TrackingReset(ioException),
- Added(device1.device),
- ).inOrder()
+private fun FakeAdbRule.attachDevice(device: TestDevice): DeviceState {
+ // Since we handle the response to the getprop commands ourselves, we don't really need to provide them to attachDevice
+ val deviceState = attachDevice(device.serialNumber, manufacturer = "", model = "", release = "", sdk = "")
+ if (!device.device.isOnline) {
+ deviceState.deviceStatus = DeviceState.DeviceStatus.OFFLINE
}
+ return deviceState
+}
- private fun deviceComboBoxDeviceTracker(
- preexistingDevice: Device? = null,
- adbSession: AdbSession = this@DeviceComboBoxDeviceTrackerTest.adbSession,
- ) = DeviceComboBoxDeviceTracker(projectRule.project, preexistingDevice, adbSession, EmptyCoroutineContext)
+private fun FakeAdbRule.createAdbSession(): AdbSession {
+ val host = TestingAdbSessionHost()
+ val channelProvider = AdbChannelProviderFactory.createOpenLocalHost(host) { fakeAdbServerPort }
+ return AdbSession.create(host, channelProvider)
}
+suspend fun yieldUntil(
+ timeout: Duration = Duration.ofSeconds(5),
+ predicate: suspend () -> Boolean
+) {
+ try {
+ withTimeout(timeout.toMillis()) {
+ while (!predicate()) {
+ yield()
+ }
+ }
+ }
+ catch (e: TimeoutCancellationException) {
+ throw AssertionError(
+ "A yieldUntil condition was not satisfied within " +
+ "5 seconds, there is a bug somewhere (in the test or in the tested code)", e
+ )
+ }
+}
diff --git a/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxTest.kt b/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxTest.kt
index 074d471e16f..8c3fcf73f48 100644
--- a/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxTest.kt
+++ b/logcat/testSrc/com/android/tools/idea/logcat/devices/DeviceComboBoxTest.kt
@@ -252,7 +252,7 @@ private class FakeDeviceComboBoxDeviceTracker : IDeviceComboBoxDeviceTracker, Cl
events.forEach { eventChannel.send(it) }
}
- override suspend fun trackDevices(): Flow<DeviceEvent> = eventChannel.consumeAsFlow()
+ override suspend fun trackDevices(retryOnException: Boolean): Flow<DeviceEvent> = eventChannel.consumeAsFlow()
override fun close() {
eventChannel.close()
diff --git a/logcat/testSrc/com/android/tools/idea/logcat/testing/TestDevice.kt b/logcat/testSrc/com/android/tools/idea/logcat/testing/TestDevice.kt
index 81d37129e54..52e40d7954e 100644
--- a/logcat/testSrc/com/android/tools/idea/logcat/testing/TestDevice.kt
+++ b/logcat/testSrc/com/android/tools/idea/logcat/testing/TestDevice.kt
@@ -35,9 +35,9 @@ internal class TestDevice(
state: DeviceState,
val release: Int,
val sdk: Int,
- val manufacturer: String,
- val model: String,
- val avdName: String,
+ val manufacturer: String = "",
+ val model: String = "",
+ val avdName: String = "",
val avdNamePre31: String = "",
) {