diff --git a/corellium/.gitignore b/corellium/.gitignore new file mode 100644 index 0000000000..cff141d5f2 --- /dev/null +++ b/corellium/.gitignore @@ -0,0 +1 @@ +**/*.properties diff --git a/corellium/adapter/build.gradle.kts b/corellium/adapter/build.gradle.kts index 36e6591a23..8002eb8954 100644 --- a/corellium/adapter/build.gradle.kts +++ b/corellium/adapter/build.gradle.kts @@ -5,6 +5,7 @@ plugins { } repositories { + jcenter() mavenCentral() maven(url = "https://kotlin.bintray.com/kotlinx") } diff --git a/corellium/adapter/src/main/kotlin/flank/corellium/Api.kt b/corellium/adapter/src/main/kotlin/flank/corellium/Api.kt index 502d61a394..17c0d2d20b 100644 --- a/corellium/adapter/src/main/kotlin/flank/corellium/Api.kt +++ b/corellium/adapter/src/main/kotlin/flank/corellium/Api.kt @@ -13,6 +13,8 @@ fun corelliumApi( installAndroidApps = InstallAndroidApps( projectName = projectName ), - invokeAndroidDevices = InvokeAndroidDevices, + invokeAndroidDevices = InvokeAndroidDevices( + projectName = projectName + ), executeTest = ExecuteAndroidTestPlan ) diff --git a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/ExecuteAndroidTestPlan.kt b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/ExecuteAndroidTestPlan.kt index 2a1c255950..a3e74c545d 100644 --- a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/ExecuteAndroidTestPlan.kt +++ b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/ExecuteAndroidTestPlan.kt @@ -29,11 +29,7 @@ object ExecuteAndroidTestPlan : AndroidTestPlan.Execute { sendCommand(command) } } - launch { - flowLogs().collect { - channel.send(it) - } - } + launch { flowLogs().collect(channel::send) } waitForIdle(10_000) } } @@ -43,15 +39,18 @@ object ExecuteAndroidTestPlan : AndroidTestPlan.Execute { } private fun AndroidTestPlan.Shard.prepareRunCommand(): String { - val base = "am instrument -r -w " - val testCases = testCases - // group test cases by filter type - [class, package] - .map { it.split(" ") }.groupBy({ it.first() }, { it.last() }).toList() - // build test cases string - .joinToString("") { (type, tests) -> "-e $type ${tests.joinToString(",")} " } + val testCases = testCases // example: listOf("class foo.Bar#baz") + .map { it.split(" ") } // example: listOf(listOf("class", "foo.Bar#baz")) + .groupBy({ it.first() }, { it.last() }) // example: first => "class", last => "foo.Bar#baz" + .toList().joinToString("") { (type, tests: List) -> + "-e $type ${tests.joinToString(",")} " // example: "-e class foo.Bar#baz" + } // example: "-e class foo.Bar#baz1,foo.Bar#baz2 -e package foo.test " val runner = "$packageName/$testRunner" - return base + testCases + runner + // example: "am instrument -r -w -e class foo.Bar#baz foo.test/androidx.test.runner.AndroidJUnitRunner" + return AM_INSTRUMENT + testCases + runner } + +private const val AM_INSTRUMENT = "am instrument -r -w " diff --git a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InstallAndroidApps.kt b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InstallAndroidApps.kt index 330d91241b..c2ef29ac62 100644 --- a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InstallAndroidApps.kt +++ b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InstallAndroidApps.kt @@ -1,13 +1,17 @@ package flank.corellium.adapter import flank.corellium.api.AndroidApps +import flank.corellium.client.agent.disconnect import flank.corellium.client.agent.uploadFile +import flank.corellium.client.console.close import flank.corellium.client.console.sendCommand import flank.corellium.client.core.connectAgent import flank.corellium.client.core.connectConsole import flank.corellium.client.core.getAllProjects import flank.corellium.client.core.getProjectInstancesList import java.io.File +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.flowOf private const val PATH_TO_UPLOAD = "/sdcard" @@ -15,23 +19,44 @@ class InstallAndroidApps( private val projectName: String ) : AndroidApps.Install { - override suspend fun List.invoke() { - val corellium = corellium - val projectId = corellium.getAllProjects().first { it.name == projectName }.id - val instances = corellium.getProjectInstancesList(projectId).associateBy { it.id } + override suspend fun List.invoke(): Unit = corellium.run { + val projectId = getAllProjects().first { it.name == projectName }.id + val instances = getProjectInstancesList(projectId).associateBy { it.id } forEach { apps -> val instance = instances.getValue(apps.instanceId) - val agent = corellium.connectAgent(instance.agent!!.info) - val console = corellium.connectConsole(instance.id) + + println("Connecting agent for ${apps.instanceId}") + val agentInfo = requireNotNull(instance.agent?.info) { + "Cannot connect to the agent, no agent info for instance ${instance.name} with id: ${instance.id}" + } + val agent = connectAgent(agentInfo) + + println("Connecting console for ${apps.instanceId}") + val console = connectConsole(instance.id) + + // Disable system logging + flowOf("su", "dmesg -n 1", "exit").collect(console::sendCommand) apps.paths.forEach { localPath -> val file = File(localPath) val remotePath = "$PATH_TO_UPLOAD/${file.name}" + println("Uploading apk $localPath") agent.uploadFile(remotePath, file.readBytes()) - console.sendCommand("pm install $remotePath") + + println("Installing apk $localPath") + console.sendCommand( + // Current solution is enough for the MVP. + // Fixme: Find better solution for recognizing test apk. + if (localPath.endsWith("androidTest.apk")) + "pm install -t $remotePath" else + "pm install $remotePath" + ) } + + console.close() + agent.disconnect() } } } diff --git a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt index d25956f9b2..dd157900bf 100644 --- a/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt +++ b/corellium/adapter/src/main/kotlin/flank/corellium/adapter/InvokeAndroidDevices.kt @@ -2,7 +2,136 @@ package flank.corellium.adapter import flank.corellium.api.AndroidInstance import flank.corellium.api.AndroidInstance.Config +import flank.corellium.client.core.createNewInstance +import flank.corellium.client.core.getAllProjects +import flank.corellium.client.core.getProjectInstancesList +import flank.corellium.client.core.startInstance +import flank.corellium.client.core.waitUntilInstanceIsReady +import flank.corellium.client.data.Instance +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch -object InvokeAndroidDevices : AndroidInstance.Invoke { - override suspend fun Config.invoke(): List = TODO() // https://github.com/flank/flank/issues/1837 +private const val FLANK_INSTANCE_NAME_PREFIX = "flank-android-" +private const val FLAVOUR = "ranchu" +private const val OS = "11.0.0" +private const val SCREEN = "720x1280:280" + +class InvokeAndroidDevices( + private val projectName: String, +) : AndroidInstance.Invoke { + override suspend fun Config.invoke(): List = coroutineScope { + val projectId = getProjectId(projectName) + val instances = getCreatedInstances(projectId, amount) + startNotRunningInstances(instances) + + val ids = instances.map(Instance::id) + let { + // When existing instances size match required amount + // there is not needs for creating more instances. + if (instances.size == amount) emptyList() + // Otherwise is required to create some additional instances + else createInstances( + projectId = projectId, + indexes = calculateAdditionalIndexes( + current = instances, + requiredAmount = amount + ) + ) + } + + waitForInstances(ids) + ids + } +} + +// Important!!! +// Try to keep the private methods not dependent on each other. +// Otherwise the implementation will go complicated. + +/** + * @return The project id for given project name. + */ +private suspend fun getProjectId(name: String) = + corellium.getAllProjects().first { it.name == name }.id + +/** + * Get all instances that was already created for flank. + * @return [List] of [Instance] where [List.size] <= [amount] + */ +private suspend fun getCreatedInstances( + projectId: String, + amount: Int +): List = corellium + .also { println("Getting instances already created by flank.") } + .getProjectInstancesList(projectId) + .filter { it.name.startsWith(FLANK_INSTANCE_NAME_PREFIX) } + .filter { it.state !in Instance.State.unavailable } + .take(amount) + .apply { println("Obtained $size already created devices") } + +/** + * Start all given instances with status different than "on". + */ +private suspend fun startNotRunningInstances( + instances: List +): Unit = instances + .filter { it.state != "on" } + .apply { if (isNotEmpty()) println("Starting not running $size instances.") } + .forEach { instance -> + corellium.startInstance(instance.id) + println(instance) + } + +/** + * Create new instances basing on given indexes. + */ +private suspend fun createInstances( + projectId: String, + indexes: List +) = indexes + .apply { println("Creating additional ${indexes.size} instances. Connecting to the agents may take longer.") } + .map { index -> + corellium.createNewInstance( + Instance( + project = projectId, + name = FLANK_INSTANCE_NAME_PREFIX + index, + flavor = FLAVOUR, + os = OS, + bootOptions = Instance.BootOptions( + screen = SCREEN + ) + ) + ) + } + +/** + * Calculate the list indexes for additional instances to create. + */ +private fun calculateAdditionalIndexes( + current: List, + requiredAmount: Int, +): List = + (0 until requiredAmount).toSet() // Create set of possible indexes starting from 0 + .minus(current.map(Instance::index)) // Subtract already created indexes + .sorted() + .take(requiredAmount - current.size) // Skip the excess indexes + +/** + * The index encoded in instance name. + * This will work only on devices created by flank. + */ +private val Instance.index get() = name.removePrefix(FLANK_INSTANCE_NAME_PREFIX).toInt() + +/** + * Block the execution and wait until each instance change the status to "on". + */ +private suspend fun waitForInstances(ids: List): Unit = coroutineScope { + println("Wait until all instances are ready...") + ids.map { id -> + launch { + corellium.waitUntilInstanceIsReady(id) + println("ready: $id") + } + }.joinAll() + println("All instances invoked and ready to use.") } diff --git a/corellium/adapter/src/test/kotlin/flank/corellium/adapter/AdaptersExample.kt b/corellium/adapter/src/test/kotlin/flank/corellium/adapter/AdaptersExample.kt deleted file mode 100644 index e9eff73c7b..0000000000 --- a/corellium/adapter/src/test/kotlin/flank/corellium/adapter/AdaptersExample.kt +++ /dev/null @@ -1,74 +0,0 @@ -package flank.corellium.adapter - -import flank.corellium.api.AndroidTestPlan -import flank.corellium.api.Authorization -import flank.corellium.api.invoke -import flank.corellium.client.core.getAllProjects -import flank.corellium.client.core.getProjectInstancesList -import flank.corellium.corelliumApi -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.runBlocking - -private const val PROJECT_NAME = "Default Project" - -// The credentials are not provided along with the code. -// If you need to execute example you have to deliver credentials on your own. -private val credentials = Authorization.Credentials( - host = TODO(), - username = TODO(), - password = TODO(), -) - -private val androidTestPlanConfig = AndroidTestPlan.Config( - instances = mapOf( - "d8ae09fe-a60a-480a-968f-1a30d77a0e11" to listOf( - AndroidTestPlan.Shard( - packageName = "com.example.test_app.test", - testRunner = "androidx.test.runner.AndroidJUnitRunner", - testCases = listOf( - "class com.example.test_app.InstrumentedTest#test0", - "class com.example.test_app.InstrumentedTest#test1", - ) - ), - AndroidTestPlan.Shard( - packageName = "com.example.test_app.test", - testRunner = "androidx.test.runner.AndroidJUnitRunner", - testCases = listOf( - "class com.example.test_app.InstrumentedTest#test2", - ) - ), - ), - "b7a305e5-b199-4ed6-9ba7-4c9b83c6762e" to listOf( - AndroidTestPlan.Shard( - packageName = "com.example.test_app.test", - testRunner = "androidx.test.runner.AndroidJUnitRunner", - testCases = listOf( - "class com.example.test_app.foo.FooInstrumentedTest#testFoo", - ) - ) - ) - ) -) - -fun main() { - val api = corelliumApi(PROJECT_NAME) - - runBlocking { - - println("* Authorizing") - api.authorize(credentials) - - corellium.run { - getProjectInstancesList(getAllProjects().first { it.name == PROJECT_NAME }.id) - }.forEach { - println(it) - } - - println("* Executing tests") - api.executeTest(androidTestPlanConfig).collect { line -> - print(line) - } - println() - println("* Finish") - } -} diff --git a/corellium/adapter/src/test/kotlin/flank/corellium/adapter/Example.kt b/corellium/adapter/src/test/kotlin/flank/corellium/adapter/Example.kt new file mode 100644 index 0000000000..5e2dd4872d --- /dev/null +++ b/corellium/adapter/src/test/kotlin/flank/corellium/adapter/Example.kt @@ -0,0 +1,95 @@ +package flank.corellium.adapter + +import flank.corellium.api.AndroidApps +import flank.corellium.api.AndroidInstance +import flank.corellium.api.AndroidTestPlan +import flank.corellium.api.Authorization +import flank.corellium.api.invoke +import flank.corellium.corelliumApi +import java.util.Properties +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.runBlocking + +private const val PROJECT_NAME = "Default Project" + +val credentials = Properties().run { + load(Unit.javaClass.classLoader.getResourceAsStream("corellium.properties")) + + Authorization.Credentials( + host = getProperty("api"), + username = getProperty("username"), + password = getProperty("password"), + ) +} + +private val shardsForTestPlan = listOf( + listOf( + AndroidTestPlan.Shard( + packageName = "com.example.test_app.test", + testRunner = "androidx.test.runner.AndroidJUnitRunner", + testCases = listOf( + "class com.example.test_app.InstrumentedTest#test0", + "class com.example.test_app.InstrumentedTest#test1", + ) + ), + AndroidTestPlan.Shard( + packageName = "com.example.test_app.test", + testRunner = "androidx.test.runner.AndroidJUnitRunner", + testCases = listOf( + "class com.example.test_app.InstrumentedTest#test2", + ) + ), + ), + listOf( + AndroidTestPlan.Shard( + packageName = "com.example.test_app.test", + testRunner = "androidx.test.runner.AndroidJUnitRunner", + testCases = listOf( + "class com.example.test_app.foo.FooInstrumentedTest#testFoo", + ) + ) + ), +) + +private val apks = listOf( + "./test_artifacts/master/apk/app-debug.apk", + "./test_artifacts/master/apk/app-multiple-success-debug-androidTest.apk", +) + +fun main() { + val api = corelliumApi(PROJECT_NAME) + + runBlocking { + + println("* Authorizing") + api.authorize(credentials) + + println("* Invoking devices") + val ids = api.invokeAndroidDevices(AndroidInstance.Config(2)).toMutableList() + + println("* Installing apks") + val apps = ids.map { id -> AndroidApps(id, apks) } + api.installAndroidApps(apps) + + // If tests will be executed to fast just after the + // app installed, the instrumentation will fail + // + delay(10_000) + + // Update test plan device ids + val shards = shardsForTestPlan.toMutableList() + val testPlan = AndroidTestPlan.Config( + instances = ids.associateWith { + shards.removeFirst() + } + ) + + println("* Executing tests") + api.executeTest(testPlan).collect { line -> + print(line) + } + println() + println("* Finish") + } +} diff --git a/corellium/adapter/src/test/resources/corellium.properties_template b/corellium/adapter/src/test/resources/corellium.properties_template new file mode 100644 index 0000000000..eab158b83d --- /dev/null +++ b/corellium/adapter/src/test/resources/corellium.properties_template @@ -0,0 +1,3 @@ +api= +username= +password= diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/agent/Connect.kt b/corellium/client/src/main/kotlin/flank/corellium/client/agent/Connect.kt index a97743bedc..ea3bd10578 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/agent/Connect.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/agent/Connect.kt @@ -2,7 +2,6 @@ package flank.corellium.client.agent import flank.corellium.client.data.AgentOperation import flank.corellium.client.data.CommandResult -import flank.corellium.client.util.withProgress import io.ktor.client.HttpClient import io.ktor.client.features.logging.LogLevel import io.ktor.client.features.logging.Logging @@ -23,7 +22,7 @@ suspend fun connectAgent( agentUrl: String, token: String, logLevel: LogLevel = LogLevel.NONE -): Agent = withProgress { +): Agent = run { val client = HttpClient { install(WebSockets) install(Logging) { @@ -44,7 +43,6 @@ suspend fun connectAgent( waitForReady() } } catch (ex: Exception) { - ex.printStackTrace() delay(20_000) } } while (connection == null) @@ -95,7 +93,7 @@ private fun Agent.handleIncomingFrames() = } private fun Agent.handleTestFrame(frame: Frame.Text) { - println("\nReceived: ${frame.readText()}") + println("Received: ${frame.readText()}") val result = format.decodeFromString(frame.readText()) tasks[result.id]?.invoke(result) } diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/agent/UploadFile.kt b/corellium/client/src/main/kotlin/flank/corellium/client/agent/UploadFile.kt index 7994f45474..09f661d1a0 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/agent/UploadFile.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/agent/UploadFile.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.withTimeoutOrNull import java.nio.ByteBuffer import java.nio.ByteOrder +import kotlinx.coroutines.withTimeout suspend fun Agent.uploadFile(path: String, bytes: ByteArray) { val id = counter.getAndIncrement() @@ -31,7 +32,7 @@ suspend fun Agent.uploadFile(path: String, bytes: ByteArray) { val task = Job() tasks[id] = defaultResultHandler(task) - withTimeoutOrNull(10_000) { + withTimeout(100_000) { task.join() } } diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/console/Functions.kt b/corellium/client/src/main/kotlin/flank/corellium/client/console/Functions.kt index 5b461dddaf..7073f4d8b8 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/console/Functions.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/console/Functions.kt @@ -12,12 +12,13 @@ suspend fun Console.sendCommand(command: String) = session.send(Frame.Binary(true, (command + "\n").encodeToByteArray())) suspend fun Console.waitForIdle(timeToWait: Long) { - delay(10_000) - while (System.currentTimeMillis() - lastResponseTime > timeToWait) delay(100) + delay(3_000) + while (System.currentTimeMillis() - lastResponseTime < timeToWait) delay(200) } suspend fun Console.close() = session.close() +@Deprecated("Use Console.flowLogs()") fun Console.launchOutputPrinter() = session.launch { // drop console bash history which is received as first frame session.incoming.receive() diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/core/Api.kt b/corellium/client/src/main/kotlin/flank/corellium/client/core/Api.kt index 8aa37ace5a..3bb2871194 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/core/Api.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/core/Api.kt @@ -8,7 +8,6 @@ import flank.corellium.client.data.ConsoleSocket import flank.corellium.client.data.Id import flank.corellium.client.data.Instance import flank.corellium.client.data.Project -import flank.corellium.client.util.withProgress import flank.corellium.client.util.withRetry import io.ktor.client.features.logging.LogLevel import io.ktor.client.request.delete @@ -21,11 +20,11 @@ import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.util.cio.writeChannel import io.ktor.utils.io.copyAndClose +import java.io.File +import java.util.UUID import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.delay -import java.io.File -import java.util.UUID suspend fun Corellium.getAllProjects(): List = getProjectIdList().map { @@ -33,7 +32,6 @@ suspend fun Corellium.getAllProjects(): List = async { client.get { url("$urlBase/projects/$it") - contentType(ContentType.Application.Json) header("Authorization", token) } } @@ -44,7 +42,6 @@ suspend fun Corellium.getProjectIdList(): List = withRetry { client.get> { url("$urlBase/projects?ids_only=1") - contentType(ContentType.Application.Json) header("Authorization", token) }.map { it.id } } @@ -55,7 +52,6 @@ suspend fun Corellium.getProjectInstancesList( withRetry { client.get { url("$urlBase/projects/$projectId/instances") - contentType(ContentType.Application.Json) header("Authorization", token) } } @@ -66,8 +62,8 @@ suspend fun Corellium.createNewInstance( withRetry { client.post { url("$urlBase/instances") - contentType(ContentType.Application.Json) header("Authorization", token) + contentType(ContentType.Application.Json) body = newInstance }.id } @@ -78,7 +74,46 @@ suspend fun Corellium.deleteInstance( withRetry { client.delete { url("$urlBase/instances/$instanceId") - contentType(ContentType.Application.Json) + header("Authorization", token) + } + } + +suspend fun Corellium.startInstance( + instanceId: String +): Unit = + withRetry { + client.post { + url("$urlBase/instances/$instanceId/start") + header("Authorization", token) + } + } + +suspend fun Corellium.stopInstance( + instanceId: String +): Unit = + withRetry { + client.post { + url("$urlBase/instances/$instanceId/stop") + header("Authorization", token) + } + } + +suspend fun Corellium.pauseInstance( + instanceId: String +): Unit = + withRetry { + client.post { + url("$urlBase/instances/$instanceId/stop") + header("Authorization", token) + } + } + +suspend fun Corellium.unpauseInstance( + instanceId: String +): Unit = + withRetry { + client.post { + url("$urlBase/instances/$instanceId/stop") header("Authorization", token) } } @@ -89,7 +124,6 @@ suspend fun Corellium.getInstanceInfo( withRetry { client.get { url("$urlBase/instances/$instanceId") - contentType(ContentType.Application.Json) header("Authorization", token) } } @@ -97,13 +131,11 @@ suspend fun Corellium.getInstanceInfo( suspend fun Corellium.waitUntilInstanceIsReady( instanceId: String ): Unit = - withProgress { - while (true) { - if (getInstanceInfo(instanceId).state == "on") { - println() - break - } - // it really takes loooong time + withRetry { + while (getInstanceInfo(instanceId).state != "on") { + // it could takes long time + // if instance was created just moment ago + // TODO: adjust the delay time depending on returned state delay(20_000) } } diff --git a/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt b/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt index 12c91f76bb..b3a4e7c8de 100644 --- a/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt +++ b/corellium/client/src/main/kotlin/flank/corellium/client/data/DTOs.kt @@ -21,6 +21,17 @@ data class Instance( @SerialName("port-adb") val portAdb: String = "" ) { + + object State { + const val ON = "on" + const val OFF = "off" + const val CREATING = "creating" + const val DELETING = "deleting" + const val DELETED = "deleted" + const val PAUSED = "paused" + val unavailable = setOf(DELETED, DELETING) + } + @Serializable data class Agent( val hash: String = "", diff --git a/corellium/sandbox/src/main/kotlin/flank/corellium/sandbox/android/AndroidExampleNoVPN.kt b/corellium/sandbox/src/main/kotlin/flank/corellium/sandbox/android/AndroidExampleNoVPN.kt index 321bc51fa5..1b080ee77f 100644 --- a/corellium/sandbox/src/main/kotlin/flank/corellium/sandbox/android/AndroidExampleNoVPN.kt +++ b/corellium/sandbox/src/main/kotlin/flank/corellium/sandbox/android/AndroidExampleNoVPN.kt @@ -21,15 +21,15 @@ import flank.corellium.client.data.Instance.BootOptions import flank.corellium.sandbox.config.Config import kotlinx.coroutines.runBlocking import java.io.File +import kotlinx.coroutines.delay -private const val instanceName = "corellium-android-2" +private const val instanceName = "flank-android-20" private const val flavor = "ranchu" private const val os = "11.0.0" private const val screen = "720x1280:280" private const val projectName = "Default Project" -private const val apkPath = "./corellium/sandbox/src/main/resources/android/app-debug.apk" -private const val testApkPath = - "./corellium/sandbox/src/main/resources/android/app-multiple-success-debug-androidTest.apk" +private const val apkPath = "./test_artifacts/master/apk/app-debug.apk" +private const val testApkPath = "./test_artifacts/master/apk/app-multiple-success-debug-androidTest.apk" private const val pathToUpload = "/sdcard" fun main(): Unit = runBlocking { @@ -97,9 +97,14 @@ fun main(): Unit = runBlocking { console.sendCommand("am instrument -r -w com.example.test_app.test/androidx.test.runner.AndroidJUnitRunner") console.launchOutputPrinter() - console.waitForIdle(10_000) + console.waitForIdle(5_000) println() println("Console idle up 10s") + + println("Removing apps...") + console.sendCommand("pm uninstall com.example.test_app") + delay(2000) + println("Disconnecting...") console.close()