Skip to content

Commit

Permalink
Merge branch 'master' into 1756-reactor-test-matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
adamfilipow92 authored May 8, 2021
2 parents 1b32f80 + 9a78fcd commit 75c5ecc
Show file tree
Hide file tree
Showing 31 changed files with 835 additions and 153 deletions.
22 changes: 14 additions & 8 deletions corellium/adapter/src/main/kotlin/flank/corellium/Api.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
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.adapter.executeAndroidTestPlan
import flank.corellium.adapter.installAndroidApps
import flank.corellium.adapter.invokeAndroidDevices
import flank.corellium.adapter.parseApkInfo
import flank.corellium.adapter.parseApkPackageName
import flank.corellium.adapter.parseApkTestCases
import flank.corellium.adapter.requestAuthorization
import flank.corellium.api.CorelliumApi

fun corelliumApi(
projectName: String
) = CorelliumApi(
authorize = RequestAuthorization,
installAndroidApps = InstallAndroidApps(
authorize = requestAuthorization,
installAndroidApps = installAndroidApps(
projectName = projectName
),
invokeAndroidDevices = InvokeAndroidDevices(
invokeAndroidDevices = invokeAndroidDevices(
projectName = projectName
),
executeTest = ExecuteAndroidTestPlan
executeTest = executeAndroidTestPlan,
parseTestCases = parseApkTestCases,
parseTestApkInfo = parseApkInfo,
parsePackageName = parseApkPackageName
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,13 @@ 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.Job
import kotlinx.coroutines.channels.SendChannel
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 { executeTests(instances, channel).joinAll() } }
}

private fun CoroutineScope.executeTests(
instances: Map<String, List<AndroidTestPlan.Shard>>,
channel: SendChannel<String>
): List<Job> =
instances.map { (instanceId, shards: List<AndroidTestPlan.Shard>) ->
launch {
val executeAndroidTestPlan = AndroidTestPlan.Execute { config ->
config.instances.map { (instanceId, shards: List<AndroidTestPlan.Shard>) ->
channelFlow {
println("Getting console $instanceId")
corellium.connectConsole(instanceId).apply {
clear()
Expand All @@ -40,6 +26,7 @@ private fun CoroutineScope.executeTests(
}
}
}
}

private fun AndroidTestPlan.Shard.prepareRunCommand(): String {
val testCases = testCases // example: listOf("class foo.Bar#baz")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,29 @@ import flank.corellium.client.core.getAllProjects
import flank.corellium.client.core.getProjectInstancesList
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import java.io.File

private const val PATH_TO_UPLOAD = "/sdcard"

class InstallAndroidApps(
private val projectName: String
) : AndroidApps.Install {
fun installAndroidApps(
projectName: String
) = AndroidApps.Install { apps ->
corellium.launch {
val projectId = corellium.getAllProjects().first { it.name == projectName }.id
val instances = corellium.getProjectInstancesList(projectId).associateBy { it.id }

override suspend fun List<AndroidApps>.invoke(): Unit = corellium.run {
val projectId = getAllProjects().first { it.name == projectName }.id
val instances = getProjectInstancesList(projectId).associateBy { it.id }

forEach { apps ->
apps.forEach { apps ->
val instance = instances.getValue(apps.instanceId)

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)
val agent = corellium.connectAgent(agentInfo)

println("Connecting console for ${apps.instanceId}")
val console = connectConsole(instance.id)
val console = corellium.connectConsole(instance.id)

// Disable system logging
flowOf("su", "dmesg -n 1", "exit").collect(console::sendCommand)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
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.channels.SendChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch

Expand All @@ -17,10 +18,10 @@ 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<String> = coroutineScope {
fun invokeAndroidDevices(
projectName: String,
) = AndroidInstance.Invoke { (amount) ->
channelFlow {
val projectId = getProjectId(projectName)
val instances = getCreatedInstances(projectId, amount)
startNotRunningInstances(instances)
Expand All @@ -39,8 +40,7 @@ class InvokeAndroidDevices(
)
}

waitForInstances(ids)
ids
waitForInstances(channel, ids)
}
}

Expand Down Expand Up @@ -125,12 +125,16 @@ private val Instance.index get() = name.removePrefix(FLANK_INSTANCE_NAME_PREFIX)
/**
* Block the execution and wait until each instance change the status to "on".
*/
private suspend fun waitForInstances(ids: List<String>): Unit = coroutineScope {
private suspend fun waitForInstances(
channel: SendChannel<String>,
ids: List<String>
) = coroutineScope {
println("Wait until all instances are ready...")
ids.map { id ->
launch {
corellium.waitUntilInstanceIsReady(id)
println("ready: $id")
channel.send(id)
}
}.joinAll()
println("All instances invoked and ready to use.")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package flank.corellium.adapter

import com.linkedin.dex.parser.DexParser
import com.linkedin.dex.parser.TestMethod
import flank.corellium.api.Apk
import net.dongliu.apk.parser.ApkFile
import org.xml.sax.InputSource
import java.io.StringReader
import javax.xml.parsers.DocumentBuilderFactory

val parseApkTestCases = Apk.ParseTestCases { path ->
DexParser.findTestMethods(path)
.map(TestMethod::testName)
}

val parseApkPackageName = Apk.ParsePackageName { path ->
ApkFile(path).apkMeta.packageName
}

val parseApkInfo = Apk.ParseInfo { path ->
Apk.Info(
packageName = ApkFile(path).apkMeta.packageName,
testRunner = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(InputSource(StringReader(ApkFile(path).manifestXml)))
.getElementsByTagName("instrumentation").item(0).attributes
.getNamedItem("android:name").nodeValue
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package flank.corellium.adapter

import flank.corellium.api.Authorization
import flank.corellium.client.core.connectCorellium
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

object RequestAuthorization : Authorization.Request {
override suspend fun Authorization.Credentials.invoke() {
val requestAuthorization = Authorization.Request { credentials ->
GlobalScope.launch {
corelliumRef = connectCorellium(
api = host,
username = username,
password = password
api = credentials.host,
username = credentials.username,
password = credentials.password
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ 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 kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.util.Properties

Expand Down Expand Up @@ -63,14 +64,14 @@ fun main() {
runBlocking {

println("* Authorizing")
api.authorize(credentials)
api.authorize(credentials).join()

println("* Invoking devices")
val ids = api.invokeAndroidDevices(AndroidInstance.Config(2)).toMutableList()
val ids = api.invokeAndroidDevices(AndroidInstance.Config(2)).toList().toMutableList()

println("* Installing apks")
val apps = ids.map { id -> AndroidApps(id, apks) }
api.installAndroidApps(apps)
api.installAndroidApps(apps).join()

// If tests will be executed to fast just after the
// app installed, the instrumentation will fail
Expand All @@ -85,8 +86,8 @@ fun main() {
)

println("* Executing tests")
api.executeTest(testPlan).collect { line ->
print(line)
api.executeTest(testPlan).forEach { flow ->
launch { flow.collect { line -> println(line) } }
}
println()
println("* Finish")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package flank.corellium.adapter

import flank.corellium.api.Apk
import org.junit.Assert.assertEquals
import org.junit.Test

private const val TEST_APK_PATH = "../../test_artifacts/master/apk/app-single-success-debug-androidTest.apk"

class ParseApkTest {

@Test
fun parseTestCases() {
assertEquals(
listOf(
"com.example.test_app.InstrumentedTest#ignoredTestWithIgnore",
"com.example.test_app.InstrumentedTest#ignoredTestWithSuppress",
"com.example.test_app.InstrumentedTest#test",
),
parseApkTestCases(TEST_APK_PATH)
)
}

@Test
fun parsePackageName() {
assertEquals(
"com.example.test_app.test",
parseApkPackageName(TEST_APK_PATH)
)
}

@Test
fun parseInfo() {
assertEquals(
Apk.Info(
packageName = "com.example.test_app.test",
testRunner = "androidx.test.runner.AndroidJUnitRunner"
),
parseApkInfo(TEST_APK_PATH)
)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package flank.corellium.api

import kotlinx.coroutines.Job

/**
* [AndroidApps] represents a bunch of apk files related to the testing instance.
*
Expand All @@ -14,5 +16,5 @@ data class AndroidApps(
/**
* Install android apps on the specified instances.
*/
interface Install : Request<List<AndroidApps>, Unit>
fun interface Install : (List<AndroidApps>) -> Job
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package flank.corellium.api

import kotlinx.coroutines.flow.Flow

/**
* The scope is representing API for managing the corellium android instances.
*
Expand All @@ -23,5 +25,5 @@ object AndroidInstance {
*
* @return list of invoked device ids.
*/
interface Invoke : Request<Config, List<String>>
fun interface Invoke : (Config) -> Flow<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object AndroidTestPlan {
/**
* Execute tests on android instances using specified configuration.
*/
interface Execute : Request<Config, Flow<String>>
fun interface Execute : (Config) -> List<Flow<String>>
}

private typealias InstanceId = String
Loading

0 comments on commit 75c5ecc

Please sign in to comment.