Skip to content

Commit 6a795d7

Browse files
fix: deserialization breaking for devicectl response (#2458)
1 parent 566f47c commit 6a795d7

File tree

7 files changed

+429
-90
lines changed

7 files changed

+429
-90
lines changed

maestro-cli/src/main/java/maestro/cli/session/MaestroSessionManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ object MaestroSessionManager {
368368

369369
val deviceController = when (deviceType) {
370370
Device.DeviceType.REAL -> {
371-
val device = util.LocalIOSDevice.listDeviceViaDeviceCtl(deviceId)
371+
val device = util.LocalIOSDevice().listDeviceViaDeviceCtl(deviceId)
372372
val deviceCtlDevice = DeviceControlIOSDevice(deviceId = device.identifier)
373373
deviceCtlDevice
374374
}

maestro-client/src/main/java/maestro/device/DeviceService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ object DeviceService {
259259
}
260260

261261
private fun listIOSConnectedDevices(): List<Device.Connected> {
262-
val connectedIphoneList = util.LocalIOSDevice.listDeviceViaDeviceCtl()
262+
val connectedIphoneList = util.LocalIOSDevice().listDeviceViaDeviceCtl()
263263

264264
return connectedIphoneList.map {
265265
val description = "${it.deviceProperties.name} - ${it.deviceProperties.osVersionNumber} - ${it.identifier}"

maestro-ios-driver/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mavenPublishing {
1313

1414
dependencies {
1515
implementation(project(":maestro-utils"))
16+
implementation(libs.commons.io)
1617

1718
api(libs.square.okhttp)
1819
api(libs.square.okhttp.logs)
@@ -25,7 +26,7 @@ dependencies {
2526
testImplementation(libs.junit.jupiter.api)
2627
testRuntimeOnly(libs.junit.jupiter.engine)
2728
testImplementation(libs.google.truth)
28-
implementation(libs.commons.io)
29+
testImplementation(libs.mockk)
2930
}
3031

3132
java {

maestro-ios-driver/src/main/kotlin/util/IOSDevice.kt

Lines changed: 1 addition & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,10 @@ enum class IOSDeviceType {
77
SIMULATOR
88
}
99

10-
@JsonIgnoreProperties(ignoreUnknown = true)
11-
data class XCDevice(
12-
val modelCode: String,
13-
val simulator: Boolean,
14-
val modelName: String,
15-
val identifier: String,
16-
val platform: String,
17-
val name: String,
18-
val operatingSystemVersion: String,
19-
val modelUTI: String,
20-
val architecture: String,
21-
) {
22-
companion object {
23-
const val IPHONE_PLATFORM = "com.apple.platform.iphoneos"
24-
}
25-
}
26-
2710
@JsonIgnoreProperties(ignoreUnknown = true)
2811
data class DeviceCtlResponse(
29-
val info: Info,
3012
val result: Result
3113
) {
32-
@JsonIgnoreProperties(ignoreUnknown = true)
33-
data class Info(
34-
val arguments: List<String>,
35-
val commandType: String,
36-
val environment: Map<String, String>,
37-
val jsonVersion: Int,
38-
val outcome: String,
39-
val version: String
40-
)
41-
4214
@JsonIgnoreProperties(ignoreUnknown = true)
4315
data class Result(
4416
val devices: List<Device>
@@ -47,68 +19,18 @@ data class DeviceCtlResponse(
4719
@JsonIgnoreProperties(ignoreUnknown = true)
4820
data class Device(
4921
val identifier: String,
50-
val capabilities: List<Capability>,
51-
val connectionProperties: ConnectionProperties,
5222
val deviceProperties: DeviceProperties,
53-
val hardwareProperties: HardwareProperties,
54-
val tags: List<String>,
55-
val visibilityClass: String
56-
)
57-
58-
@JsonIgnoreProperties(ignoreUnknown = true)
59-
data class Capability(
60-
val featureIdentifier: String,
61-
val name: String
62-
)
63-
64-
@JsonIgnoreProperties(ignoreUnknown = true)
65-
data class ConnectionProperties(
66-
val authenticationType: String,
67-
val isMobileDeviceOnly: Boolean,
68-
val lastConnectionDate: String,
69-
val pairingState: String,
70-
val potentialHostnames: List<String>,
71-
val transportType: String?,
72-
val tunnelState: String,
73-
val tunnelTransportProtocol: String?
23+
val hardwareProperties: HardwareProperties
7424
)
7525

7626
@JsonIgnoreProperties(ignoreUnknown = true)
7727
data class DeviceProperties(
78-
val bootedFromSnapshot: Boolean?,
79-
val bootedSnapshotName: String?,
80-
val ddiServicesAvailable: Boolean,
81-
val developerModeStatus: String,
82-
val hasInternalOSBuild: Boolean,
8328
val name: String,
84-
val osBuildUpdate: String,
8529
val osVersionNumber: String,
86-
val rootFileSystemIsWritable: Boolean
8730
)
8831

8932
@JsonIgnoreProperties(ignoreUnknown = true)
9033
data class HardwareProperties(
91-
val cpuType: CpuType,
92-
val deviceType: String,
93-
val ecid: String,
94-
val hardwareModel: String,
95-
val internalStorageCapacity: String?,
96-
val isProductionFused: Boolean,
97-
val marketingName: String,
98-
val platform: String,
99-
val productType: String,
100-
val reality: String,
101-
val serialNumber: String,
102-
val supportedCPUTypes: List<CpuType>?,
103-
val supportedDeviceFamilies: List<Int>?,
104-
val thinningProductType: String,
10534
val udid: String
10635
)
107-
108-
@JsonIgnoreProperties(ignoreUnknown = true)
109-
data class CpuType(
110-
val name: String,
111-
val subType: Int,
112-
val type: Int
113-
)
11436
}

maestro-ios-driver/src/main/kotlin/util/LocalIOSDevice.kt

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
package util
22

3+
import com.fasterxml.jackson.databind.DeserializationFeature
4+
import com.fasterxml.jackson.module.kotlin.KotlinFeature
5+
import com.fasterxml.jackson.module.kotlin.KotlinModule
36
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
47
import com.fasterxml.jackson.module.kotlin.readValue
58
import java.io.File
69

7-
object LocalIOSDevice {
10+
class DeviceCtlProcess {
11+
12+
fun devicectlDevicesOutput(): File {
13+
val tempOutput = File.createTempFile("devicectl_response", ".json")
14+
ProcessBuilder(listOf("xcrun", "devicectl", "--json-output", tempOutput.path, "list", "devices"))
15+
.redirectError(ProcessBuilder.Redirect.PIPE).start().apply {
16+
waitFor()
17+
}
18+
19+
return tempOutput
20+
}
21+
}
22+
23+
class LocalIOSDevice(private val deviceCtlProcess: DeviceCtlProcess = DeviceCtlProcess()) {
824

925
fun uninstall(deviceId: String, bundleIdentifier: String) {
1026
CommandLineUtils.runCommand(
@@ -31,7 +47,9 @@ object LocalIOSDevice {
3147
val bytes = tempOutput.readBytes()
3248
val response = String(bytes)
3349

34-
val deviceCtlResponse = jacksonObjectMapper().readValue<DeviceCtlResponse>(response)
50+
val jacksonObjectMapper = jacksonObjectMapper()
51+
jacksonObjectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false)
52+
val deviceCtlResponse = jacksonObjectMapper.readValue<DeviceCtlResponse>(response)
3553
return deviceCtlResponse.result.devices.find {
3654
it.hardwareProperties.udid == deviceId
3755
} ?: throw IllegalArgumentException("iOS device with identifier $deviceId not connected or available")
@@ -41,12 +59,8 @@ object LocalIOSDevice {
4159
}
4260

4361
fun listDeviceViaDeviceCtl(): List<DeviceCtlResponse.Device> {
44-
val tempOutput = File.createTempFile("devicectl_response", ".json")
62+
val tempOutput = deviceCtlProcess.devicectlDevicesOutput()
4563
try {
46-
ProcessBuilder(listOf("xcrun" , "devicectl", "--json-output", tempOutput.path, "list", "devices"))
47-
.redirectError(ProcessBuilder.Redirect.PIPE).start().apply {
48-
waitFor()
49-
}
5064
val bytes = tempOutput.readBytes()
5165
val response = String(bytes)
5266

0 commit comments

Comments
 (0)