Skip to content
This repository has been archived by the owner on Dec 7, 2019. It is now read-only.

Modernization and adb deletion helpers #28

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Modules:
Commander is [available on jcenter](https://jcenter.bintray.com/com/gojuno/commander).

```groovy
compile 'com.gojuno.commander:os:some-version'
compile 'com.gojuno.commander:android:some-version'
compile 'com.gojuno.commander:os:${latestVersion}'
compile 'com.gojuno.commander:android:${latestVersion}'
```

All the releases and changelogs can be found on [Releases Page](https://github.com/gojuno/commander/releases).
Expand Down Expand Up @@ -43,6 +43,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```

[spoon]: https://github.com/square/spoon
[test sharding]: https://developer.android.com/topic/libraries/testing-support-library/index.html#ajur-sharding
27 changes: 3 additions & 24 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,28 +1,7 @@
apply plugin: 'kotlin'
apply plugin: 'org.junit.platform.gradle.plugin'
apply from: '../gradle/publishing.gradle'
description = "Functions to work with Android SDK Tools like adb, avdmanager, sdkmanager."

dependencies {
compile libraries.os
compile libraries.kotlinStd
compile libraries.kotlinRuntime
compile libraries.kotlinReflect
compile libraries.rxJava
api project(':os')
}

dependencies {
testCompile libraries.spek
testCompile libraries.spekSubjectExtension
testCompile libraries.spekJunitPlatformEngine
testCompile libraries.assertJ
}

junitPlatform {
platformVersion = versions.junitPlatform

filters {
engines {
include 'spek'
}
}
}
apply from: '../gradle/publishing.gradle'
2 changes: 0 additions & 2 deletions android/gradle.properties

This file was deleted.

85 changes: 59 additions & 26 deletions android/src/main/kotlin/com/gojuno/commander/android/Adb.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
@file:Suppress("unused")

package com.gojuno.commander.android

import com.gojuno.commander.os.Notification
import com.gojuno.commander.os.log
import com.gojuno.commander.os.nanosToHumanReadableTime
import com.gojuno.commander.os.process
import rx.Observable
import rx.Single
import io.reactivex.Observable
import io.reactivex.Single
import java.io.File
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeUnit.MINUTES
import java.util.concurrent.TimeUnit.SECONDS
import kotlin.system.exitProcess

val androidHome: String by lazy { requireNotNull(System.getenv("ANDROID_HOME")) { "Please specify ANDROID_HOME env variable" } }
val adb: String by lazy { "$androidHome${File.pathSeparator}platform-tools${File.pathSeparator}adb" }
val adb: String by lazy { "$androidHome${File.separator}platform-tools${File.separator}adb" }
private val buildTools: String? by lazy {
File(androidHome, "build-tools")
requireNotNull(File(androidHome, "build-tools")
.listFiles()
.sortedArray()
.lastOrNull()
?.absolutePath
?.sortedArray()
?.lastOrNull()
?.absolutePath) { "" }
}
val aapt: String by lazy { buildTools?.let { "$buildTools${File.pathSeparator}aapt" } ?: "" }
val aapt: String by lazy { buildTools?.let { "$buildTools${File.separator}aapt" } ?: "" }

internal fun Observable<Notification>.trimmedOutput() = this
.ofType(Notification.Exit::class.java)
.toSingle()
.singleOrError()
.map { it.output.readText().trim() }

fun AdbDevice.externalStorage(): Single<String> = process(listOf(adb, "-s", id, "shell", "printenv", "EXTERNAL_STORAGE"))
Expand All @@ -35,7 +38,7 @@ fun AdbDevice.deviceModel(): Single<String> = process(listOf(adb, "-s", id, "she
.trimmedOutput()
.doOnError { log("Could not get model name of device $id, error = $it") }

fun connectedAdbDevices(): Observable<Set<AdbDevice>> = process(listOf(adb, "devices"), unbufferedOutput = true)
fun connectedAdbDevices(): Single<Set<AdbDevice>> = process(listOf(adb, "devices"), unbufferedOutput = true)
.ofType(Notification.Exit::class.java)
.map { it.output.readText() }
.map {
Expand All @@ -52,8 +55,8 @@ fun connectedAdbDevices(): Observable<Set<AdbDevice>> = process(listOf(adb, "dev

shouldRetry
}
.flatMapIterable {
it
.flatMapIterable { adbStdOut ->
adbStdOut
.substringAfter("List of devices attached")
.split(System.lineSeparator())
.map { it.trim() }
Expand Down Expand Up @@ -81,9 +84,8 @@ fun connectedAdbDevices(): Observable<Set<AdbDevice>> = process(listOf(adb, "dev
fun AdbDevice.log(message: String) = com.gojuno.commander.os.log("[$id] $message")

fun AdbDevice.installApk(pathToApk: String, timeout: Pair<Int, TimeUnit> = 2 to MINUTES): Observable<Unit> {
val adbDevice = this
val installApk = process(
commandAndArgs = listOf(adb, "-s", adbDevice.id, "install", "-r", pathToApk),
commandAndArgs = listOf(adb, "-s", id, "install", "-r", pathToApk),
unbufferedOutput = true,
timeout = timeout
)
Expand All @@ -104,39 +106,70 @@ fun AdbDevice.installApk(pathToApk: String, timeout: Pair<Int, TimeUnit> = 2 to

when (success) {
true -> {
adbDevice.log("Successfully installed apk in ${duration.nanosToHumanReadableTime()}, pathToApk = $pathToApk")
log("Successfully installed apk in ${duration.nanosToHumanReadableTime()}, pathToApk = $pathToApk")
}

false -> {
adbDevice.log("Failed to install apk $pathToApk")
System.exit(1)
log("Failed to install apk $pathToApk")
exitProcess(1)
}
}
}
.doOnSubscribe { adbDevice.log("Installing apk... pathToApk = $pathToApk") }
.doOnError { adbDevice.log("Error during installing apk: $it, pathToApk = $pathToApk") }
.doOnSubscribe { log("Installing apk... pathToApk = $pathToApk") }
.doOnError { log("Error during installing apk: $it, pathToApk = $pathToApk") }
}

fun AdbDevice.pullFolder(folderOnDevice: String, folderOnHostMachine: File, logErrors: Boolean, timeout: Pair<Int, TimeUnit> = 60 to SECONDS): Single<Boolean> {
val adbDevice = this
val pullFiles = process(
commandAndArgs = listOf(adb, "-s", adbDevice.id, "pull", folderOnDevice, folderOnHostMachine.absolutePath),
commandAndArgs = listOf(adb, "-s", id, "pull", folderOnDevice, folderOnHostMachine.absolutePath),
timeout = timeout,
unbufferedOutput = true
)

return pullFiles
.ofType(Notification.Exit::class.java)
.retry(3)
.doOnError { error -> if (logErrors) log("Failed to pull files from $folderOnDevice to $folderOnHostMachine failed: $error") }
.doOnError { error -> if (logErrors) log("Failed to pull files from $folderOnDevice to $folderOnHostMachine: $error") }
.map { true }
.onErrorReturn { false }
.toSingle()
.singleOrError()
}

fun AdbDevice.deleteFolder(folderOnDevice: String, logErrors: Boolean, timeout: Pair<Int, TimeUnit> = 60 to SECONDS): Single<Boolean> {
val deleteFolder = process(
commandAndArgs = listOf(adb, "-s", id, "shell", "rm", "-r", folderOnDevice),
timeout = timeout,
unbufferedOutput = true
)

return deleteFolder
.ofType(Notification.Exit::class.java)
.retry(3)
.doOnError { error -> if (logErrors) log("Failed to delete directory $folderOnDevice: $error") }
.map { true }
.onErrorReturn { false }
.singleOrError()
}

fun AdbDevice.deleteFile(fileOnDevice: String, logErrors: Boolean, timeout: Pair<Int, TimeUnit> = 60 to SECONDS): Single<Boolean> {
val deleteFile = process(
commandAndArgs = listOf(adb, "-s", id, "shell", "rm", fileOnDevice),
timeout = timeout,
unbufferedOutput = true
)

return deleteFile
.ofType(Notification.Exit::class.java)
.retry(3)
.doOnError { error -> if (logErrors) log("Failed to delete file $fileOnDevice: $error") }
.map { true }
.onErrorReturn { false }
.singleOrError()
}

fun AdbDevice.redirectLogcatToFile(file: File): Single<Process> = Observable
fun AdbDevice.redirectLogcatToFile(file: File): Single<Process> = Single
.fromCallable { file.parentFile.mkdirs() }
.flatMap { process(listOf(adb, "-s", this.id, "logcat"), redirectOutputTo = file, timeout = null) }
.flatMapObservable { process(listOf(adb, "-s", id, "logcat"), redirectOutputTo = file, timeout = null) }
.ofType(Notification.Start::class.java)
.doOnError {
when (it) {
Expand All @@ -146,4 +179,4 @@ fun AdbDevice.redirectLogcatToFile(file: File): Single<Process> = Observable
}
.map { it.process }
.take(1)
.toSingle()
.singleOrError()
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@file:Suppress("unused")
package com.gojuno.commander.android

data class AdbDevice(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.gojuno.commander.android

import com.gojuno.commander.os.Notification
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.dsl.describe
import org.jetbrains.spek.api.dsl.it
import rx.Observable
import io.reactivex.Observable
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe

class AdbSpec : Spek({

Expand Down
30 changes: 19 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
apply from: 'dependencies.gradle'

buildscript {
// Gradle will not find vars defined in an external file when referring to them
// in the buildscript block, unless you link it from the buildscript block, too.
apply from: 'dependencies.gradle'

repositories {
jcenter()
}

dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.13.0'
classpath "org.junit.platform:junit-platform-gradle-plugin:$versions.junitPlatform"
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.40"
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
}
}

allprojects {
apply plugin: 'kotlin'

repositories {
jcenter()
}

apply plugin: 'com.github.ben-manes.versions'
dependencies {
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8"

testImplementation "org.assertj:assertj-core:3.11.1"

testImplementation "org.spekframework.spek2:spek-dsl-jvm:2.0.5"
testRuntimeOnly "org.spekframework.spek2:spek-runner-junit5:2.0.5"
testRuntimeOnly "org.jetbrains.kotlin:kotlin-reflect"
}

test {
useJUnitPlatform {
includeEngines 'spek2'
}
}
}
33 changes: 0 additions & 33 deletions dependencies.gradle

This file was deleted.

2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
group=com.gojuno.commander
version=0.2.0
Loading