Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add adbserver plugin and setup publication (with CI fix) #631

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ee5755d
remove now stable feature VERSION_CATALOGS definition
VladislavSumin Nov 11, 2022
863ba3f
move samples as separate project
VladislavSumin Nov 11, 2022
9b6cd4a
rename alure && compose modules with gradle module naming convention
VladislavSumin Nov 11, 2022
29fe1cf
fix .cirrus to run tests form samples
VladislavSumin Nov 11, 2022
d4e627a
add empty kaspresso plugin && add it to samples
VladislavSumin Nov 11, 2022
537f89f
fix cirrus gradlew paths resolution
VladislavSumin Nov 11, 2022
3be8743
setup empty start/stop adb server tasks
VladislavSumin Nov 11, 2022
c4def6c
update desktop to run async
VladislavSumin Nov 14, 2022
2c22a44
add working directory support for adb server
VladislavSumin Nov 14, 2022
af29ff2
move test artifacts to sample directory
VladislavSumin Nov 14, 2022
5ea1b01
remove manual start adb server from cirrus
VladislavSumin Nov 14, 2022
1a02316
add specify adb server path option for desktop server
VladislavSumin Nov 14, 2022
6a0c52d
fix detekt
VladislavSumin Nov 14, 2022
1467593
add gradle logger
VladislavSumin Nov 14, 2022
8ed449d
add documentation to Desktop.kt && fix pattern compile at every getAt…
VladislavSumin Nov 15, 2022
2a86a0c
add adb path resolution && logging
VladislavSumin Nov 15, 2022
069fe7e
setup gradle plugin portal publishing
mfglushchenko Dec 4, 2023
b1e120e
merge master
mfglushchenko Dec 18, 2023
46f361a
fix build error
mfglushchenko Dec 18, 2023
599cd25
fix detekt
mfglushchenko Dec 18, 2023
30f6c17
move allure support files back where they should be
mfglushchenko Dec 18, 2023
53799fd
fix build
mfglushchenko Dec 18, 2023
e420b9d
temporarily remove unknown props for api 30 tests
mfglushchenko Dec 19, 2023
cfe131b
try fix api 30 tests
mfglushchenko Dec 20, 2023
bfe72f7
fix gradle sync for samples
mfglushchenko Dec 20, 2023
b8f0944
more test fixes
mfglushchenko Dec 20, 2023
d2049d1
update cirrus config file
mfglushchenko Feb 6, 2024
edd2301
merge master
mfglushchenko Feb 6, 2024
4098f3a
fix errors
mfglushchenko Feb 6, 2024
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
27 changes: 19 additions & 8 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@ check_android_30_task:
kvm: true
cpu: 8
memory: 24G
start_adb_server_background_script:
java -jar artifacts/adbserver-desktop.jar || true
# xfce4 somehow helps to pass "Geolocation" test
install_de_script: |
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install xfce4 -y
apt-get purge -y pm-utils xscreensaver*
apt-get install xvfb -y
start_de_background_script: |
Xvfb :99 -screen 0 1000x1000x16 &
sleep 5
startxfce4
accept_licenses_script:
echo yes | sdkmanager --licenses
install_emulator_script:
Expand All @@ -33,7 +42,7 @@ check_android_30_task:
-no-snapshot
-no-window
assemble_instrumented_tests_script:
./gradlew -PCI=true assembleDebugAndroidTest
cd samples && ./gradlew assembleDebugAndroidTest
wait_for_avd_script:
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 3; done; input keyevent 82'
configure_avd_script: |
Expand All @@ -45,7 +54,9 @@ check_android_30_task:
start_logcat_background_script:
adb logcat > log.log
run_tests_script:
./gradlew -PCI=true connectedDebugAndroidTest
cd samples && ./gradlew connectedDebugAndroidTest
# After we do "adb root" connection is closed for a moment. So first attempt to pull screenshots usually fails
# That's why we make 5 attempts to pull folders
always:
stop_logcat_script: |
if [[ $(adb devices | awk 'NR>1 {print $1}') =~ "emulator.*" ]]; then
Expand Down Expand Up @@ -96,7 +107,7 @@ check_android_33_task:
-no-snapshot
-no-window
assemble_instrumented_tests_script:
./gradlew -PCI=true assembleDebugAndroidTest
cd samples && ./gradlew -PCI=true assembleDebugAndroidTest
wait_for_avd_script:
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 3; done; input keyevent 82'
configure_avd_script: |
Expand All @@ -109,7 +120,7 @@ check_android_33_task:
start_logcat_background_script:
adb logcat > log.log
run_tests_script:
./gradlew -PCI=true connectedDebugAndroidTest
cd samples && ./gradlew -PCI=true connectedDebugAndroidTest
always:
stop_logcat_script: |
if [[ $(adb devices | awk 'NR>1 {print $1}') =~ "emulator.*" ]]; then
Expand Down Expand Up @@ -164,7 +175,7 @@ check_android_21_task:
-no-snapshot
-no-window
assemble_instrumented_tests_script:
./gradlew -PCI=true assembleDebugAndroidTest
cd samples && ./gradlew -PCI=true assembleDebugAndroidTest
wait_for_avd_script:
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 3; done; input keyevent 82'
configure_avd_script: |
Expand All @@ -176,7 +187,7 @@ check_android_21_task:
start_logcat_background_script:
adb logcat > log.log
run_tests_script:
./gradlew -PCI=true connectedDebugAndroidTest
cd samples && ./gradlew -PCI=true connectedDebugAndroidTest
always:
stop_logcat_script: |
if [[ $(adb devices | awk 'NR>1 {print $1}') =~ "emulator.*" ]]; then
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.kaspersky.adbserver.common.log

import com.kaspersky.adbserver.common.log.filterlog.FullLoggerOptimiser
import com.kaspersky.adbserver.common.log.fulllogger.FullLogger
import com.kaspersky.adbserver.common.log.fulllogger.FullLoggerSystemImpl
import com.kaspersky.adbserver.common.log.logger.DesktopLogger
import com.kaspersky.adbserver.common.log.logger.LogLevel
Expand All @@ -12,8 +13,12 @@ import com.kaspersky.adbserver.common.log.logger.LoggerImpl
*/
object LoggerFactory {

fun getDesktopLogger(logLevel: LogLevel, desktopName: String): DesktopLogger {
val logger = getCommonLogger(logLevel, desktopName)
fun getDesktopLogger(
logLevel: LogLevel,
desktopName: String,
fullLogger: FullLogger = FullLoggerSystemImpl(logLevel, desktopName, null)
): DesktopLogger {
val logger = getCommonLogger(logLevel, desktopName, fullLogger = fullLogger)
return DesktopLogger(logger, logLevel, desktopName)
}

Expand All @@ -26,8 +31,12 @@ object LoggerFactory {
fun getDeviceLogger(logLevel: LogLevel): Logger =
getCommonLogger(logLevel)

private fun getCommonLogger(logLevel: LogLevel, desktopName: String? = null, deviceName: String? = null): Logger {
val fullLogger = FullLoggerSystemImpl(logLevel, desktopName, deviceName)
private fun getCommonLogger(
logLevel: LogLevel,
desktopName: String? = null,
deviceName: String? = null,
fullLogger: FullLogger = FullLoggerSystemImpl(logLevel, desktopName, deviceName)
): Logger {
val fullLoggerWrapper =
if (logLevel == LogLevel.DEBUG) FullLoggerOptimiser(originalFullLogger = fullLogger, generateLogs = true) else fullLogger
return LoggerImpl(fullLoggerWrapper)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.kaspersky.adbserver.common.log.fulllogger

import com.kaspersky.adbserver.common.log.logger.LogLevel

internal interface FullLogger {
interface FullLogger {

fun log(
logLevel: LogLevel? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.kaspersky.adbserver.desktop

import com.kaspersky.adbserver.common.api.CommandResult
import java.nio.file.Path

/**
* @param adbPath - path to adb binary
*/
class AdbCommandPerformer(
private val adbPath: Path,
private val cmdCommandPerformer: CmdCommandPerformer,
) {

/**
* Be aware it's a synchronous method
* @param command - adb command without path to adb binaries
*/
fun perform(command: String): CommandResult {
return cmdCommandPerformer.perform("$adbPath $command")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ package com.kaspersky.adbserver.desktop
import com.kaspersky.adbserver.common.api.CommandResult
import com.kaspersky.adbserver.common.api.ExecutorResultStatus
import com.kaspersky.adbserver.common.log.logger.Logger
import java.io.File
import java.nio.file.Path
import java.util.concurrent.TimeUnit

internal class CmdCommandPerformer(
/**
* @param workingDir - working directory used to execute any cmd command if null when use default process working directory
*/
class CmdCommandPerformer(
private val desktopName: String,
private val logger: Logger,
private val workingDir: Path? = null
) {

companion object {
Expand All @@ -19,16 +25,17 @@ internal class CmdCommandPerformer(
*/
fun perform(command: String, arguments: List<String> = emptyList()): CommandResult {
val serviceInfo = "The command was executed on desktop=$desktopName"
val workingDir = workingDir?.toFile() ?: File(".")

val process = try {
if (arguments.isEmpty()) {
Runtime.getRuntime().exec(command)
Runtime.getRuntime().exec(command, emptyArray(), workingDir)
} else {
val fullCommand = buildList {
add(command)
addAll(arguments)
}.toTypedArray()
Runtime.getRuntime().exec(fullCommand)
Runtime.getRuntime().exec(fullCommand, emptyArray(), workingDir)
}
} catch (ex: Throwable) {
logger.e(ex.stackTraceToString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import java.lang.UnsupportedOperationException

internal class CommandExecutorImpl(
private val cmdCommandPerformer: CmdCommandPerformer,
private val adbCommandPerformer: AdbCommandPerformer,
private val deviceName: String,
private val adbServerPort: String?,
private val logger: Logger,
Expand All @@ -25,7 +26,7 @@ internal class CommandExecutorImpl(
val adbArguments: List<String>

if (command.arguments.isEmpty()) {
adbCommand = "$adbPath ${adbServerPort?.let { "-P $adbServerPort " } ?: ""}-s $deviceName ${command.command}"
adbCommand = "${adbServerPort?.let { "-P $adbServerPort " } ?: ""}-s $deviceName ${command.command}"
adbArguments = emptyList()
} else {
adbCommand = adbPath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package com.kaspersky.adbserver.desktop
import com.kaspersky.adbserver.common.api.ExecutorResultStatus
import com.kaspersky.adbserver.common.log.LoggerFactory
import com.kaspersky.adbserver.common.log.logger.DesktopLogger
import java.util.concurrent.atomic.AtomicBoolean
import java.util.regex.Pattern
import kotlin.concurrent.thread

internal class Desktop(
class Desktop(
private val cmdCommandPerformer: CmdCommandPerformer,
private val adbCommandPerformer: AdbCommandPerformer,
private val presetEmulators: List<String>,
private val adbServerPort: String?,
private val logger: DesktopLogger,
Expand All @@ -15,20 +18,52 @@ internal class Desktop(

companion object {
private const val PAUSE_MS = 500L
private val DEVICE_PATTERN = Pattern.compile("^([a-zA-Z0-9\\-:.]+)(\\s+)(device)")
}

private val devices: MutableCollection<DeviceMirror> = mutableListOf()
private var isRunning = AtomicBoolean(false)

fun startDevicesObserving() {
/**
* Start Desktop server.
* Blocking current thread while server working
* @throws IllegalStateException - if server already running
*/
fun startDevicesObservingSync() {
if (!isRunning.compareAndSet(false, true)) error("Desktop already running")
startDevicesObservingInternal()
}

/**
* Start Desktop server asynchronously
* @throws IllegalStateException - if server already running
*/
fun startDevicesObservingAsync() {
if (!isRunning.compareAndSet(false, true)) error("Desktop already running")
thread {
startDevicesObservingInternal()
}
}

/**
* Stop Desktop server
* @throws IllegalStateException - if server already stopped
*/
fun stopDevicesObserving() {
if (!isRunning.compareAndSet(true, false)) error("Desktop already stopped")
}

private fun startDevicesObservingInternal() {
logger.d("start")
while (true) {
while (isRunning.get()) {
val namesOfAttachedDevicesByAdb = getAttachedDevicesByAdb()
namesOfAttachedDevicesByAdb.forEach { deviceName ->
if (devices.find { client -> client.deviceName == deviceName } == null) {
logger.i("New device has been found: $deviceName. Initialize connection to the device...")
val deviceMirror =
DeviceMirror.create(
cmdCommandPerformer,
adbCommandPerformer,
deviceName,
adbServerPort,
LoggerFactory.getDesktopLoggerReflectingDevice(logger, deviceName),
Expand All @@ -49,18 +84,22 @@ internal class Desktop(
}
Thread.sleep(PAUSE_MS)
}

devices.forEach { client ->
client.stopConnectionToDevice()
}
devices.clear()
}

private fun getAttachedDevicesByAdb(): List<String> {
val pattern = Pattern.compile("^([a-zA-Z0-9\\-:.]+)(\\s+)(device)")
val commandResult = cmdCommandPerformer.perform("$adbPath devices")
val commandResult = adbCommandPerformer.perform("devices")
if (commandResult.status != ExecutorResultStatus.SUCCESS) {
return emptyList()
}
val adbDevicesCommandResult: String = commandResult.description
return adbDevicesCommandResult.lines()
.asSequence()
.map { pattern.matcher(it) }
.map { DEVICE_PATTERN.matcher(it) }
.filter { matcher -> matcher.find() }
.map { matcher -> matcher.group(1) }
.filter { foundEmulator -> presetEmulators.isEmpty() || presetEmulators.contains(foundEmulator) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal class DeviceMirror private constructor(

fun create(
cmdCommandPerformer: CmdCommandPerformer,
adbCommandPerformer: AdbCommandPerformer,
deviceName: String,
adbServerPort: String?,
logger: Logger,
Expand All @@ -31,6 +32,7 @@ internal class DeviceMirror private constructor(
DesktopDeviceSocketConnectionFactory.getSockets(DesktopDeviceSocketConnectionType.FORWARD)
val commandExecutor = CommandExecutorImpl(
cmdCommandPerformer,
adbCommandPerformer,
deviceName,
adbServerPort,
logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import kotlinx.cli.ArgType
import kotlinx.cli.default
import kotlinx.cli.delimiter
import java.lang.management.ManagementFactory
import java.nio.file.Path

private const val DESKTOP = "Desktop-"
private const val ERROR_EXIT_CODE = -1

// It is assumed that adb is preinstall and available by "adb" keyword
private const val DEFAULT_ADB_PATH = "adb"

Expand Down Expand Up @@ -51,14 +54,17 @@ internal fun main(args: Array<String>) {
desktopLogger.i("Desktop started with arguments: emulators=$emulators, adbServerPort=$port, adbPath=$adbPath")

val cmdCommandPerformer = CmdCommandPerformer(desktopName, desktopLogger)
val adbCommandPerformer = AdbCommandPerformer(Path.of(adbPath), cmdCommandPerformer)

val desktop = Desktop(
cmdCommandPerformer = cmdCommandPerformer,
adbCommandPerformer = adbCommandPerformer,
presetEmulators = emulators,
adbServerPort = port,
logger = desktopLogger,
adbPath = adbPath
)
desktop.startDevicesObserving()
desktop.startDevicesObservingSync()
}

private fun getDesktopName(): String {
Expand Down
File renamed without changes.
File renamed without changes.
38 changes: 38 additions & 0 deletions kaspresso-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
`kotlin-dsl`
`java-gradle-plugin`
id("com.gradle.plugin-publish") version "1.2.1"
}

dependencies {
implementation(libs.androidPlugin)
implementation(projects.adbServer.adbServerCommon)
implementation(projects.adbServer.adbserverDesktop)
}

group = "com.kaspersky.kaspresso"
version = "1.0"
gradlePlugin {
// TODO: fix for tests on API 30 and uncomment
// website.set("https://kasperskylab.github.io/Kaspresso/en/")
// vcsUrl.set("https://github.com/KasperskyLab/Kaspresso/")

plugins {
create("AdbServerPlugin") {
id = "com.kaspersky.kaspresso-adb-server-plugin"
displayName = "Kaspresso ADB-server plugin"
description = "Run Kaspresso ADB server for Android UI tests"
// tags.set(listOf("testing", "UI tests", "test automation", "android", "kasresso", "adb server"))
implementationClass = "com.kaspersky.kaspresso.plugin.KaspressoPlugin"
}
}
}

publishing {
repositories {
maven {
name = "localPluginRepository"
url = uri("../local-plugin-repository")
}
}
}
Loading
Loading