Skip to content

Commit

Permalink
Add adapters
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-goral committed Apr 29, 2021
1 parent 2f21a19 commit 20c2300
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 5 deletions.
7 changes: 3 additions & 4 deletions corellium/adapter/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin(Plugins.Kotlin.PLUGIN_JVM)
kotlin(Plugins.Kotlin.PLUGIN_SERIALIZATION) version Versions.KOTLIN
}

repositories {
jcenter()
mavenCentral()
maven(url = "https://kotlin.bintray.com/kotlinx")
}
Expand All @@ -15,12 +13,13 @@ tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "1.8" }

dependencies {
implementation(project(":corellium:api"))
implementation(project(":corellium:client"))

implementation(Dependencies.KOTLIN_COROUTINES_CORE)
implementation(Dependencies.DEX_TEST_PARSER)
implementation(Dependencies.APK_PARSER)

testImplementation(Dependencies.JUNIT)
testImplementation(Dependencies.MOCKK)
testImplementation(Dependencies.TRUTH)
}

tasks.test {
Expand Down
18 changes: 18 additions & 0 deletions corellium/adapter/src/main/kotlin/flank/corellium/Api.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package flank.corellium

import flank.corellium.adapter.ExecuteAndroidTestPlan
import flank.corellium.adapter.InstallAndroidApps
import flank.corellium.adapter.InvokeAndroidDevices
import flank.corellium.adapter.RequestAuthorization
import flank.corellium.api.CorelliumApi

fun corelliumApi(
projectName: String
) = CorelliumApi(
authorize = RequestAuthorization,
installAndroidApps = InstallAndroidApps(
projectName = projectName
),
invokeAndroidDevices = InvokeAndroidDevices,
executeTest = ExecuteAndroidTestPlan
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package flank.corellium.adapter

import flank.corellium.client.core.Corellium

internal val corellium: Corellium
get() = requireNotNull(corelliumRef) {
"Corellium is not initialized, try to call connectCorellium at first."
}

// It's totally ok to keep corellium as singleton since we don't need handle more than one connection for single run.
internal var corelliumRef: Corellium? = null
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package flank.corellium.adapter

import flank.corellium.api.AndroidTestPlan
import flank.corellium.client.console.clear
import flank.corellium.client.console.flowLogs
import flank.corellium.client.console.sendCommand
import flank.corellium.client.console.waitForIdle
import flank.corellium.client.core.connectConsole
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch

object ExecuteAndroidTestPlan : AndroidTestPlan.Execute {
override suspend fun AndroidTestPlan.Config.invoke(): Flow<String> =
coroutineScope {
channelFlow {
instances.map { (instanceId, shards: List<AndroidTestPlan.Shard>) ->
println("Getting console $instanceId")
launch {
corellium.connectConsole(instanceId).run {
clear()
launch {
shards.forEach { shard ->
val command = shard.prepareRunCommand()
println("Sending command: $command")
sendCommand(command)
}
}
launch {
flowLogs().collect {
channel.send(it)
}
}
waitForIdle(10_000)
}
}
}.joinAll()
}
}
}

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 runner = "$packageName/$testRunner"

return base + testCases + runner
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package flank.corellium.adapter

import flank.corellium.api.AndroidApps
import flank.corellium.client.agent.uploadFile
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

private const val PATH_TO_UPLOAD = "/sdcard"

class InstallAndroidApps(
private val projectName: String
) : AndroidApps.Install {

override suspend fun List<AndroidApps>.invoke() {
val corellium = corellium
val projectId = corellium.getAllProjects().first { it.name == projectName }.id
val instances = corellium.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)

apps.paths.forEach { localPath ->
val file = File(localPath)
val remotePath = "$PATH_TO_UPLOAD/${file.name}"

agent.uploadFile(remotePath, file.readBytes())
console.sendCommand("pm install $remotePath")
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package flank.corellium.adapter

import flank.corellium.api.AndroidInstance
import flank.corellium.api.AndroidInstance.Config

object InvokeAndroidDevices : AndroidInstance.Invoke {
override suspend fun Config.invoke(): List<String> = TODO() // https://github.com/flank/flank/issues/1837
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package flank.corellium.adapter

import flank.corellium.api.Authorization
import flank.corellium.client.core.connectCorellium

object RequestAuthorization : Authorization.Request {
override suspend fun Authorization.Credentials.invoke() {
corelliumRef = connectCorellium(
api = host,
username = username,
password = password
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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")
}
}
2 changes: 1 addition & 1 deletion corellium/shard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Depending on the provided test cases duration, the sharding algorithm will:

## Diagram

![sharding_class_diagram](http://www.plantuml.com/plantuml/proxy?cache=no&fmt=svg&src=https://raw.githubusercontent.com/Flank/flank/1801_Multi-module_sharding_algorithm/docs/corellium/sharding-class.puml)
![sharding_class_diagram](http://www.plantuml.com/plantuml/proxy?cache=no&fmt=svg&src=https://raw.githubusercontent.com/Flank/flank/master/docs/corellium/sharding-class.puml)

## Example

Expand Down

0 comments on commit 20c2300

Please sign in to comment.