diff --git a/app/src/test/java/com/droiddeck/launcher/agent/AgentLogExportTest.kt b/app/src/test/java/com/droiddeck/launcher/agent/AgentLogExportTest.kt new file mode 100644 index 000000000..babdec83b --- /dev/null +++ b/app/src/test/java/com/droiddeck/launcher/agent/AgentLogExportTest.kt @@ -0,0 +1,65 @@ +package com.droiddeck.launcher.agent + +import android.os.Bundle +import com.droiddeck.launcher.session.SessionState +import com.droiddeck.launcher.session.installJvmSecureSourceOpener +import com.droiddeck.launcher.session.resetSecureSourceOpener +import org.json.JSONObject +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config +import java.io.File + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [33], manifest = Config.NONE) +class AgentLogExportTest { + private val context get() = RuntimeEnvironment.getApplication() + private var previousLogDirectory: File? = null + + @Before fun prepare() { + previousLogDirectory = SessionState.logDirectory + installJvmSecureSourceOpener() + } + + @After fun cleanup() { + SessionState.logDirectory = previousLogDirectory + resetSecureSourceOpener() + } + + @Test fun independentLogRequestsKeepBothExportDirectories() { + val session = File(context.cacheDir, "agent-session").apply { mkdirs() } + File(session, "session.log").writeText("booted\n") + SessionState.logDirectory = session + val provider = Robolectric.buildContentProvider(AgentBridgeProvider::class.java).create().get() + var firstDir: File? = null + var secondDir: File? = null + + try { + fun requestLogs() = JSONObject(provider.call("logs", null, Bundle()).getString("json")!!) + val first = requestLogs() + val second = requestLogs() + firstDir = File(first.getString("cleanupDir")) + secondDir = File(second.getString("cleanupDir")) + val firstZip = File(first.getString("zip")) + val secondZip = File(second.getString("zip")) + + assertNotEquals(firstDir, secondDir) + assertEquals(firstDir, firstZip.parentFile) + assertEquals(secondDir, secondZip.parentFile) + assertTrue(firstZip.isFile) + assertTrue(secondZip.isFile) + } finally { + firstDir?.deleteRecursively() + secondDir?.deleteRecursively() + session.deleteRecursively() + } + } +} diff --git a/docs/agent-control.md b/docs/agent-control.md index eba6ca773..7a1fed1d0 100644 --- a/docs/agent-control.md +++ b/docs/agent-control.md @@ -92,6 +92,10 @@ Every command writes one JSON object to stdout and its resolved serial to stderr success, 2 invalid or rejected command, 3 ADB or device error, 4 session error, 5 timeout, 6 artifact or file error, 7 debugging tools disabled. +When upgrading the CLI, use a new destination for the first pull if the old destination contains +archives created by an older CLI without a `.droiddeckctl-managed-logs.json` manifest and their +filenames overlap. The upgraded CLI refuses to overwrite those files to preserve unrelated data. + ## Commands ```sh