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 AppleTV support #2067

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
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
4 changes: 1 addition & 3 deletions maestro-client/src/main/java/maestro/drivers/IOSDriver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,7 @@ class IOSDriver(
iosDevice.pressKey(name)
}

buttonNameMap[code]?.let { name ->
iosDevice.pressButton(name)
}
iosDevice.pressButton(code.description)
}
}

Expand Down
1 change: 1 addition & 0 deletions maestro-ios-driver/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
implementation(project(":maestro-utils"))

api(libs.square.okhttp)
api(libs.google.gson)
api(libs.square.okhttp.logs)
api(libs.jackson.module.kotlin)
api(libs.jarchivelib)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import okio.source
import org.apache.commons.io.FileUtils
import org.rauschig.jarchivelib.ArchiverFactory
import org.slf4j.LoggerFactory
import util.LocalSimulatorUtils
import util.XCRunnerCLIUtils
import xcuitest.XCTestClient
import java.io.File
Expand Down Expand Up @@ -159,16 +160,32 @@ class LocalXCTestInstaller(
return checkSuccessful
}

private fun isTV(deviceId: String): Boolean {
val list = LocalSimulatorUtils.list()
for (entry in list.devices.entries) {
for (device in entry.value) {
if (device.udid != deviceId) continue
if (device.deviceTypeIdentifier == null) continue
logger.trace("Device is an AppleTV")
return device.deviceTypeIdentifier.contains("Apple-TV")
}
}

return false
}

private fun startXCTestRunner() {
val processOutput = ProcessBuilder(listOf("xcrun", "simctl", "spawn", deviceId, "launchctl", "list"))
.start()
.inputStream.source().buffer().readUtf8()
.trim()

val isTV = isTV(deviceId)

logger.info("[Start] Writing xctest run file")
val tempDir = File(tempDir).apply { mkdir() }
val xctestRunFile = File("$tempDir/maestro-driver-ios-config.xctestrun")
writeFileToDestination(XCTEST_RUN_PATH, xctestRunFile)
val xctestRunFile = File("${tempDir}/maestro-driver-config.xctestrun")
writeFileToDestination(XCTEST_RUN_PATH, xctestRunFile, isTV)
logger.info("[Done] Writing xctest run file")

if (processOutput.contains(UI_TEST_RUNNER_APP_BUNDLE_ID)) {
Expand All @@ -177,13 +194,13 @@ class LocalXCTestInstaller(
} else {
logger.info("Not able to find ui test runner app running, going to install now")

logger.info("[Start] Writing maestro-driver-iosUITests-Runner app")
extractZipToApp("maestro-driver-iosUITests-Runner", UI_TEST_RUNNER_PATH)
logger.info("[Done] Writing maestro-driver-iosUITests-Runner app")
logger.info("[Start] Writing maestro-driverUITests-Runner app")
extractZipToApp("maestro-driverUITests-Runner", UI_TEST_RUNNER_PATH, isTV)
logger.info("[Done] Writing maestro-driverUITests-Runner app")

logger.info("[Start] Writing maestro-driver-ios app")
extractZipToApp("maestro-driver-ios", UI_TEST_HOST_PATH)
logger.info("[Done] Writing maestro-driver-ios app")
logger.info("[Start] Writing maestro-driver app")
extractZipToApp("maestro-driver", UI_TEST_HOST_PATH, isTV)
logger.info("[Done] Writing maestro-driver app")
}

logger.info("[Start] Running XcUITest with `xcodebuild test-without-building`")
Expand All @@ -207,28 +224,35 @@ class LocalXCTestInstaller(
logger.info("[Done] Cleaning up the ui test runner files")
}

private fun extractZipToApp(appFileName: String, srcAppPath: String) {
val appFile = File("$tempDir/Debug-iphonesimulator").apply { mkdir() }
private fun extractZipToApp(appFileName: String, srcAppPath: String, isTV: Boolean) {
val appFile = when(isTV) {
true -> File("$tempDir/Debug-appletvsimulator").apply { mkdir() }
false -> File("$tempDir/Debug-iphonesimulator").apply { mkdir() }
}
val appZip = File("$tempDir/$appFileName.zip")

writeFileToDestination(srcAppPath, appZip)
writeFileToDestination(srcAppPath, appZip, isTV)
ArchiverFactory.createArchiver(appZip).apply {
extract(appZip, appFile)
}
}

private fun writeFileToDestination(srcPath: String, destFile: File) {
LocalXCTestInstaller::class.java.getResourceAsStream(srcPath)?.let {
private fun writeFileToDestination(srcPath: String, destFile: File, isTV: Boolean) {
val prefix = when (isTV) {
true -> "/tvos/"
false -> "/ios/"
}
LocalXCTestInstaller::class.java.getResourceAsStream(prefix + srcPath)?.let {
val bufferedSink = destFile.sink().buffer()
bufferedSink.writeAll(it.source())
bufferedSink.flush()
}
}

companion object {
private const val UI_TEST_RUNNER_PATH = "/maestro-driver-iosUITests-Runner.zip"
private const val XCTEST_RUN_PATH = "/maestro-driver-ios-config.xctestrun"
private const val UI_TEST_HOST_PATH = "/maestro-driver-ios.zip"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID = "dev.mobile.maestro-driver-iosUITests.xctrunner"
private const val UI_TEST_RUNNER_PATH = "maestro-driverUITests-Runner.zip"
private const val XCTEST_RUN_PATH = "maestro-driver-config.xctestrun"
private const val UI_TEST_HOST_PATH = "maestro-driver.zip"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID = "dev.mobile.maestro-driverUITests.xctrunner"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CodeCoverageBuildableInfos</key>
<array>
<dict>
<key>Architectures</key>
<array>
<string>x86_64</string>
</array>
<key>BuildableIdentifier</key>
<string>44F995232C9D6F8800D15B50:primary</string>
<key>IncludeInReport</key>
<true/>
<key>IsStatic</key>
<false/>
<key>Name</key>
<string>maestro-driver.app</string>
<key>ProductPaths</key>
<array>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driver.app/maestro-driver</string>
</array>
<key>SourceFiles</key>
<array>
<string>ContentView.swift</string>
<string>maestro_driverApp.swift</string>
</array>
<key>SourceFilesCommonPathPrefix</key>
<string>/Users/max.phillips/dev/maestro-tvos/maestro-ios-xctest-runner/maestro-driver/</string>
<key>Toolchains</key>
<array>
<string>com.apple.dt.toolchain.XcodeDefault</string>
</array>
</dict>
<dict>
<key>Architectures</key>
<array>
<string>x86_64</string>
</array>
<key>BuildableIdentifier</key>
<string>52049BF2293503A200807AA3:primary</string>
<key>IncludeInReport</key>
<true/>
<key>IsStatic</key>
<false/>
<key>Name</key>
<string>maestro-driverUITests.xctest</string>
<key>ProductPaths</key>
<array>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driverUITests-Runner.app/PlugIns/maestro-driverUITests.xctest/maestro-driverUITests</string>
</array>
<key>SourceFiles</key>
<array>
<string>Routes/Extensions/Logger.swift</string>
<string>Routes/Extensions/StringExtensions.swift</string>
<string>Routes/Extensions/XCUIElement+Extensions.swift</string>
<string>Routes/Handlers/DeviceInfoHandler.swift</string>
<string>Routes/Handlers/EraseTextHandler.swift</string>
<string>Routes/Handlers/InputTextRouteHandler.swift</string>
<string>Routes/Handlers/KeyboardRouteHandler.swift</string>
<string>Routes/Handlers/PressButtonHandler.swift</string>
<string>Routes/Handlers/PressKeyHandler.swift</string>
<string>Routes/Handlers/RunningAppRouteHandler.swift</string>
<string>Routes/Handlers/ScreenDiffHandler.swift</string>
<string>Routes/Handlers/ScreenshotHandler.swift</string>
<string>Routes/Handlers/SetPermissionsHandler.swift</string>
<string>Routes/Handlers/StatusHandler.swift</string>
<string>Routes/Handlers/SwipeRouteHandler.swift</string>
<string>Routes/Handlers/SwipeRouteHandlerV2.swift</string>
<string>Routes/Handlers/TouchRouteHandler.swift</string>
<string>Routes/Handlers/ViewHierarchyHandler.swift</string>
<string>Routes/Helpers/AppError.swift</string>
<string>Routes/Helpers/ScreenSizeHelper.swift</string>
<string>Routes/Helpers/SystemPermissionHelper.swift</string>
<string>Routes/Helpers/TextInputHelper.swift</string>
<string>Routes/Helpers/TimeoutHelper.swift</string>
<string>Routes/Models/AXElement.swift</string>
<string>Routes/Models/DeviceInfoResponse.swift</string>
<string>Routes/Models/EraseTextRequest.swift</string>
<string>Routes/Models/GetRunningAppRequest.swift</string>
<string>Routes/Models/InputTextRequest.swift</string>
<string>Routes/Models/KeyboardHandlerRequest.swift</string>
<string>Routes/Models/KeyboardHandlerResponse.swift</string>
<string>Routes/Models/PressButtonRequest.swift</string>
<string>Routes/Models/PressKeyRequest.swift</string>
<string>Routes/Models/SetPermissionsRequest.swift</string>
<string>Routes/Models/StatusResponse.swift</string>
<string>Routes/Models/SwipeRequest.swift</string>
<string>Routes/Models/TouchRequest.swift</string>
<string>Routes/Models/ViewHierarchyRequest.swift</string>
<string>Routes/RouteHandlerFactory.swift</string>
<string>Routes/XCTest/AXClientSwizzler.swift</string>
<string>Routes/XCTest/EventRecord.swift</string>
<string>Routes/XCTest/EventTarget.swift</string>
<string>Routes/XCTest/KeyModifierFlags.swift</string>
<string>Routes/XCTest/PointerEventPath.swift</string>
<string>Routes/XCTest/RunnerDaemonProxy.swift</string>
<string>Routes/XCTest/RunningApp.swift</string>
<string>Routes/XCTestHTTPServer.swift</string>
<string>maestro_driver_iosUITests.swift</string>
</array>
<key>SourceFilesCommonPathPrefix</key>
<string>/Users/max.phillips/dev/maestro-tvos/maestro-ios-xctest-runner/maestro-driver-iosUITests/</string>
<key>Toolchains</key>
<array>
<string>com.apple.dt.toolchain.XcodeDefault</string>
</array>
</dict>
</array>
<key>ContainerInfo</key>
<dict>
<key>ContainerName</key>
<string>maestro-driver-ios</string>
<key>SchemeName</key>
<string>maestro-driver</string>
</dict>
<key>TestConfigurations</key>
<array>
<dict>
<key>Name</key>
<string>Test Scheme Action</string>
<key>TestTargets</key>
<array>
<dict>
<key>BlueprintName</key>
<string>maestro-driverUITests</string>
<key>BlueprintProviderName</key>
<string>maestro-driver-ios</string>
<key>BlueprintProviderRelativePath</key>
<string>maestro-driver-ios.xcodeproj</string>
<key>BundleIdentifiersForCrashReportEmphasis</key>
<array>
<string>dev.mobile.maestro-driver</string>
<string>dev.mobile.maestro-driverUITests</string>
</array>
<key>ClangProfileDataDirectoryPath</key>
<string>__DERIVEDDATA__/Build/Intermediates.noindex/CodeCoverage/ProfileData</string>
<key>CommandLineArguments</key>
<array/>
<key>DefaultTestExecutionTimeAllowance</key>
<integer>600</integer>
<key>DependentProductPaths</key>
<array>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driver.app</string>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driverUITests-Runner.app</string>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driverUITests-Runner.app/PlugIns/maestro-driverUITests.xctest</string>
</array>
<key>DiagnosticCollectionPolicy</key>
<integer>1</integer>
<key>EnvironmentVariables</key>
<dict>
<key>APP_DISTRIBUTOR_ID_OVERRIDE</key>
<string>com.apple.AppStore</string>
<key>OS_ACTIVITY_DT_MODE</key>
<string>YES</string>
<key>SQLITE_ENABLE_THREAD_ASSERTIONS</key>
<string>1</string>
<key>TERM</key>
<string>dumb</string>
</dict>
<key>IsUITestBundle</key>
<true/>
<key>IsXCTRunnerHostedTestBundle</key>
<true/>
<key>PreferredScreenCaptureFormat</key>
<string>screenRecording</string>
<key>ProductModuleName</key>
<string>maestro_driverUITests</string>
<key>SystemAttachmentLifetime</key>
<string>deleteOnSuccess</string>
<key>TestBundlePath</key>
<string>__TESTHOST__/PlugIns/maestro-driverUITests.xctest</string>
<key>TestHostBundleIdentifier</key>
<string>dev.mobile.maestro-driverUITests.xctrunner</string>
<key>TestHostPath</key>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driverUITests-Runner.app</string>
<key>TestLanguage</key>
<string></string>
<key>TestRegion</key>
<string></string>
<key>TestTimeoutsEnabled</key>
<false/>
<key>TestingEnvironmentVariables</key>
<dict>
<key>DYLD_FRAMEWORK_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator:__TESTROOT__/Debug-iphonesimulator/PackageFrameworks:__PLATFORMS__/iPhoneSimulator.platform/Developer/Library/Frameworks</string>
<key>DYLD_LIBRARY_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator:__PLATFORMS__/iPhoneSimulator.platform/Developer/usr/lib</string>
<key>__XCODE_BUILT_PRODUCTS_DIR_PATHS</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
<key>__XPC_DYLD_FRAMEWORK_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
<key>__XPC_DYLD_LIBRARY_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
</dict>
<key>ToolchainsSettingValue</key>
<array/>
<key>UITargetAppCommandLineArguments</key>
<array/>
<key>UITargetAppEnvironmentVariables</key>
<dict>
<key>APP_DISTRIBUTOR_ID_OVERRIDE</key>
<string>com.apple.AppStore</string>
<key>DYLD_FRAMEWORK_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator:__TESTROOT__/Debug-iphonesimulator/PackageFrameworks</string>
<key>DYLD_LIBRARY_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
<key>__XCODE_BUILT_PRODUCTS_DIR_PATHS</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
<key>__XPC_DYLD_FRAMEWORK_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
<key>__XPC_DYLD_LIBRARY_PATH</key>
<string>__TESTROOT__/Debug-iphonesimulator</string>
</dict>
<key>UITargetAppPath</key>
<string>__TESTROOT__/Debug-iphonesimulator/maestro-driver.app</string>
<key>UserAttachmentLifetime</key>
<string>deleteOnSuccess</string>
</dict>
</array>
</dict>
</array>
<key>TestPlan</key>
<dict>
<key>IsDefault</key>
<true/>
<key>Name</key>
<string>maestro-driver</string>
</dict>
<key>__xctestrun_metadata__</key>
<dict>
<key>FormatVersion</key>
<integer>2</integer>
</dict>
</dict>
</plist>
Binary file not shown.
Binary file not shown.
Loading
Loading