Skip to content

Commit df165a9

Browse files
mfglushchenkoVladislav Sumin
andauthored
add adbserver plugin and setup publication (#611)
* remove now stable feature VERSION_CATALOGS definition * move samples as separate project * rename alure && compose modules with gradle module naming convention * fix .cirrus to run tests form samples * add empty kaspresso plugin && add it to samples * fix cirrus gradlew paths resolution * setup empty start/stop adb server tasks * update desktop to run async * add working directory support for adb server * move test artifacts to sample directory * remove manual start adb server from cirrus * add specify adb server path option for desktop server * fix detekt * add gradle logger * add documentation to Desktop.kt && fix pattern compile at every getAttachedDevicesByAdb function call * add adb path resolution && logging * setup gradle plugin portal publishing * fix build error * fix detekt * move allure support files back where they should be * fix build * temporarily remove unknown props for api 30 tests * try fix api 30 tests * fix gradle sync for samples * more test fixes --------- Co-authored-by: Vladislav Sumin <[email protected]>
1 parent 69ddba1 commit df165a9

File tree

71 files changed

+715
-46
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+715
-46
lines changed

.cirrus.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,17 @@ check_android_30_task:
1010
kvm: true
1111
cpu: 8
1212
memory: 24G
13-
start_adb_server_background_script:
14-
java -jar artifacts/adbserver-desktop.jar || true
13+
# xfce4 somehow helps to pass "Geolocation" test
14+
install_de_script: |
15+
export DEBIAN_FRONTEND=noninteractive
16+
apt-get update
17+
apt-get install xfce4 -y
18+
apt-get purge -y pm-utils xscreensaver*
19+
apt-get install xvfb -y
20+
start_de_background_script: |
21+
Xvfb :99 -screen 0 1000x1000x16 &
22+
sleep 5
23+
startxfce4
1524
accept_licenses_script:
1625
echo yes | sdkmanager --licenses
1726
install_emulator_script:
@@ -33,7 +42,7 @@ check_android_30_task:
3342
-no-snapshot
3443
-no-window
3544
assemble_instrumented_tests_script:
36-
./gradlew -PCI=true assembleDebugAndroidTest
45+
cd samples && ./gradlew assembleDebugAndroidTest
3746
wait_for_avd_script:
3847
adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 3; done; input keyevent 82'
3948
configure_avd_script: |
@@ -45,7 +54,9 @@ check_android_30_task:
4554
start_logcat_background_script:
4655
adb logcat > log.log
4756
run_tests_script:
48-
./gradlew -PCI=true connectedDebugAndroidTest
57+
cd samples && ./gradlew connectedDebugAndroidTest
58+
# After we do "adb root" connection is closed for a moment. So first attempt to pull screenshots usually fails
59+
# That's why we make 5 attempts to pull folders
4960
always:
5061
stop_logcat_script: |
5162
if [[ $(adb devices | awk 'NR>1 {print $1}') =~ "emulator.*" ]]; then

adb-server/adb-server-common/src/main/java/com/kaspersky/adbserver/common/log/LoggerFactory.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.kaspersky.adbserver.common.log
22

33
import com.kaspersky.adbserver.common.log.filterlog.FullLoggerOptimiser
4+
import com.kaspersky.adbserver.common.log.fulllogger.FullLogger
45
import com.kaspersky.adbserver.common.log.fulllogger.FullLoggerSystemImpl
56
import com.kaspersky.adbserver.common.log.logger.DesktopLogger
67
import com.kaspersky.adbserver.common.log.logger.LogLevel
@@ -12,8 +13,12 @@ import com.kaspersky.adbserver.common.log.logger.LoggerImpl
1213
*/
1314
object LoggerFactory {
1415

15-
fun getDesktopLogger(logLevel: LogLevel, desktopName: String): DesktopLogger {
16-
val logger = getCommonLogger(logLevel, desktopName)
16+
fun getDesktopLogger(
17+
logLevel: LogLevel,
18+
desktopName: String,
19+
fullLogger: FullLogger = FullLoggerSystemImpl(logLevel, desktopName, null)
20+
): DesktopLogger {
21+
val logger = getCommonLogger(logLevel, desktopName, fullLogger = fullLogger)
1722
return DesktopLogger(logger, logLevel, desktopName)
1823
}
1924

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

29-
private fun getCommonLogger(logLevel: LogLevel, desktopName: String? = null, deviceName: String? = null): Logger {
30-
val fullLogger = FullLoggerSystemImpl(logLevel, desktopName, deviceName)
34+
private fun getCommonLogger(
35+
logLevel: LogLevel,
36+
desktopName: String? = null,
37+
deviceName: String? = null,
38+
fullLogger: FullLogger = FullLoggerSystemImpl(logLevel, desktopName, deviceName)
39+
): Logger {
3140
val fullLoggerWrapper =
3241
if (logLevel == LogLevel.DEBUG) FullLoggerOptimiser(originalFullLogger = fullLogger, generateLogs = true) else fullLogger
3342
return LoggerImpl(fullLoggerWrapper)

adb-server/adb-server-common/src/main/java/com/kaspersky/adbserver/common/log/fulllogger/FullLogger.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package com.kaspersky.adbserver.common.log.fulllogger
22

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

5-
internal interface FullLogger {
5+
interface FullLogger {
66

77
fun log(
88
logLevel: LogLevel? = null,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.kaspersky.adbserver.desktop
2+
3+
import com.kaspersky.adbserver.common.api.CommandResult
4+
import java.nio.file.Path
5+
6+
/**
7+
* @param adbPath - path to adb binary
8+
*/
9+
class AdbCommandPerformer(
10+
private val adbPath: Path,
11+
private val cmdCommandPerformer: CmdCommandPerformer,
12+
) {
13+
14+
/**
15+
* Be aware it's a synchronous method
16+
* @param command - adb command without path to adb binaries
17+
*/
18+
fun perform(command: String): CommandResult {
19+
return cmdCommandPerformer.perform("$adbPath $command")
20+
}
21+
}

adb-server/adbserver-desktop/src/main/java/com/kaspersky/adbserver/desktop/CmdCommandPerformer.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ package com.kaspersky.adbserver.desktop
22

33
import com.kaspersky.adbserver.common.api.CommandResult
44
import com.kaspersky.adbserver.common.api.ExecutorResultStatus
5+
import java.io.File
6+
import java.nio.file.Path
57
import java.util.concurrent.TimeUnit
68

7-
internal class CmdCommandPerformer(
8-
private val desktopName: String
9+
/**
10+
* @param workingDir - working directory used to execute any cmd command if null when use default process working directory
11+
*/
12+
class CmdCommandPerformer(
13+
private val desktopName: String,
14+
private val workingDir: Path? = null
915
) {
1016

1117
companion object {
@@ -17,7 +23,8 @@ internal class CmdCommandPerformer(
1723
*/
1824
fun perform(command: String): CommandResult {
1925
val serviceInfo = "The command was executed on desktop=$desktopName"
20-
val process = Runtime.getRuntime().exec(command)
26+
val workingDir = workingDir?.toFile() ?: File(".")
27+
val process = Runtime.getRuntime().exec(command, emptyArray(), workingDir)
2128
try {
2229
if (process.waitFor(EXECUTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
2330
val exitCode = process.exitValue()

adb-server/adbserver-desktop/src/main/java/com/kaspersky/adbserver/desktop/CommandExecutorImpl.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import java.lang.UnsupportedOperationException
1010

1111
internal class CommandExecutorImpl(
1212
private val cmdCommandPerformer: CmdCommandPerformer,
13+
private val adbCommandPerformer: AdbCommandPerformer,
1314
private val deviceName: String,
1415
private val adbServerPort: String?,
1516
private val logger: Logger,
@@ -20,9 +21,9 @@ internal class CommandExecutorImpl(
2021
return when (command) {
2122
is CmdCommand -> cmdCommandPerformer.perform(command.body)
2223
is AdbCommand -> {
23-
val adbCommand = "$adbPath ${ adbServerPort?.let { "-P $adbServerPort " } ?: "" }-s $deviceName ${command.body}"
24-
logger.d("The created adbCommand=$adbCommand")
25-
cmdCommandPerformer.perform(adbCommand)
24+
val adbCommand = "${ adbServerPort?.let { "-P $adbServerPort " } ?: "" }-s $deviceName ${command.body}"
25+
logger.d("The created adbCommand=adb $adbCommand")
26+
adbCommandPerformer.perform(adbCommand)
2627
}
2728
else -> throw UnsupportedOperationException("The command=$command is unsupported command")
2829
}

adb-server/adbserver-desktop/src/main/java/com/kaspersky/adbserver/desktop/Desktop.kt

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ package com.kaspersky.adbserver.desktop
33
import com.kaspersky.adbserver.common.api.ExecutorResultStatus
44
import com.kaspersky.adbserver.common.log.LoggerFactory
55
import com.kaspersky.adbserver.common.log.logger.DesktopLogger
6+
import java.util.concurrent.atomic.AtomicBoolean
67
import java.util.regex.Pattern
8+
import kotlin.concurrent.thread
79

8-
internal class Desktop(
10+
class Desktop(
911
private val cmdCommandPerformer: CmdCommandPerformer,
12+
private val adbCommandPerformer: AdbCommandPerformer,
1013
private val presetEmulators: List<String>,
1114
private val adbServerPort: String?,
1215
private val logger: DesktopLogger,
@@ -15,20 +18,52 @@ internal class Desktop(
1518

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

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

22-
fun startDevicesObserving() {
27+
/**
28+
* Start Desktop server.
29+
* Blocking current thread while server working
30+
* @throws IllegalStateException - if server already running
31+
*/
32+
fun startDevicesObservingSync() {
33+
if (!isRunning.compareAndSet(false, true)) error("Desktop already running")
34+
startDevicesObservingInternal()
35+
}
36+
37+
/**
38+
* Start Desktop server asynchronously
39+
* @throws IllegalStateException - if server already running
40+
*/
41+
fun startDevicesObservingAsync() {
42+
if (!isRunning.compareAndSet(false, true)) error("Desktop already running")
43+
thread {
44+
startDevicesObservingInternal()
45+
}
46+
}
47+
48+
/**
49+
* Stop Desktop server
50+
* @throws IllegalStateException - if server already stopped
51+
*/
52+
fun stopDevicesObserving() {
53+
if (!isRunning.compareAndSet(true, false)) error("Desktop already stopped")
54+
}
55+
56+
private fun startDevicesObservingInternal() {
2357
logger.d("start")
24-
while (true) {
58+
while (isRunning.get()) {
2559
val namesOfAttachedDevicesByAdb = getAttachedDevicesByAdb()
2660
namesOfAttachedDevicesByAdb.forEach { deviceName ->
2761
if (devices.find { client -> client.deviceName == deviceName } == null) {
2862
logger.i("New device has been found: $deviceName. Initialize connection to the device...")
2963
val deviceMirror =
3064
DeviceMirror.create(
3165
cmdCommandPerformer,
66+
adbCommandPerformer,
3267
deviceName,
3368
adbServerPort,
3469
LoggerFactory.getDesktopLoggerReflectingDevice(logger, deviceName),
@@ -49,18 +84,22 @@ internal class Desktop(
4984
}
5085
Thread.sleep(PAUSE_MS)
5186
}
87+
88+
devices.forEach { client ->
89+
client.stopConnectionToDevice()
90+
}
91+
devices.clear()
5292
}
5393

5494
private fun getAttachedDevicesByAdb(): List<String> {
55-
val pattern = Pattern.compile("^([a-zA-Z0-9\\-:.]+)(\\s+)(device)")
56-
val commandResult = cmdCommandPerformer.perform("$adbPath devices")
95+
val commandResult = adbCommandPerformer.perform("devices")
5796
if (commandResult.status != ExecutorResultStatus.SUCCESS) {
5897
return emptyList()
5998
}
6099
val adbDevicesCommandResult: String = commandResult.description
61100
return adbDevicesCommandResult.lines()
62101
.asSequence()
63-
.map { pattern.matcher(it) }
102+
.map { DEVICE_PATTERN.matcher(it) }
64103
.filter { matcher -> matcher.find() }
65104
.map { matcher -> matcher.group(1) }
66105
.filter { foundEmulator -> presetEmulators.isEmpty() || presetEmulators.contains(foundEmulator) }

adb-server/adbserver-desktop/src/main/java/com/kaspersky/adbserver/desktop/DeviceMirror.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ internal class DeviceMirror private constructor(
2222

2323
fun create(
2424
cmdCommandPerformer: CmdCommandPerformer,
25+
adbCommandPerformer: AdbCommandPerformer,
2526
deviceName: String,
2627
adbServerPort: String?,
2728
logger: Logger,
@@ -31,6 +32,7 @@ internal class DeviceMirror private constructor(
3132
DesktopDeviceSocketConnectionFactory.getSockets(DesktopDeviceSocketConnectionType.FORWARD)
3233
val commandExecutor = CommandExecutorImpl(
3334
cmdCommandPerformer,
35+
adbCommandPerformer,
3436
deviceName,
3537
adbServerPort,
3638
logger,

adb-server/adbserver-desktop/src/main/java/com/kaspersky/adbserver/desktop/Main.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import kotlinx.cli.ArgType
77
import kotlinx.cli.default
88
import kotlinx.cli.delimiter
99
import java.lang.management.ManagementFactory
10+
import java.nio.file.Path
1011

1112
private const val DESKTOP = "Desktop-"
13+
private const val ERROR_EXIT_CODE = -1
14+
1215
// It is assumed that adb is preinstall and available by "adb" keyword
1316
private const val DEFAULT_ADB_PATH = "adb"
1417

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

5356
val cmdCommandPerformer = CmdCommandPerformer(desktopName)
57+
val adbCommandPerformer = AdbCommandPerformer(Path.of(adbPath), cmdCommandPerformer)
5458
val desktop = Desktop(
5559
cmdCommandPerformer = cmdCommandPerformer,
60+
adbCommandPerformer = adbCommandPerformer,
5661
presetEmulators = emulators,
5762
adbServerPort = port,
5863
logger = desktopLogger,
5964
adbPath = adbPath
6065
)
61-
desktop.startDevicesObserving()
66+
desktop.startDevicesObservingSync()
6267
}
6368

6469
private fun getDesktopName(): String {

build-logic/settings.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
enableFeaturePreview("VERSION_CATALOGS")
2-
31
rootProject.name = "build-logic"
42

53
include("android")

0 commit comments

Comments
 (0)