From 33a01d43b5888b204f0d302ef42aebdea5c2cbc9 Mon Sep 17 00:00:00 2001 From: Michael Wright <slooxied@gmail.com> Date: Mon, 2 Nov 2020 09:53:07 +0100 Subject: [PATCH 1/3] feat: ios scenario numbers (#1287) * added missing documentation * added scenario-numbers to common flank config * Added and fixed tests * Fix PR comments and check if scenario numbers is less than 1024 --- docs/index.md | 14 +++++ test_runner/flank.ios.yml | 7 +++ .../src/main/kotlin/ftl/args/AndroidArgs.kt | 1 - .../src/main/kotlin/ftl/args/CommonArgs.kt | 1 + .../main/kotlin/ftl/args/CreateAndroidArgs.kt | 1 - .../main/kotlin/ftl/args/CreateCommonArgs.kt | 1 + test_runner/src/main/kotlin/ftl/args/IArgs.kt | 1 + .../src/main/kotlin/ftl/args/IosArgs.kt | 1 + .../kotlin/ftl/args/ValidateAndroidArgs.kt | 3 +- .../main/kotlin/ftl/args/ValidateIosArgs.kt | 8 +++ .../ftl/config/android/AndroidGcloudConfig.kt | 11 ---- .../ftl/config/common/CommonGcloudConfig.kt | 11 ++++ .../src/test/kotlin/ftl/args/IosArgsTest.kt | 58 +++++++++++++++++++ 13 files changed, 104 insertions(+), 14 deletions(-) diff --git a/docs/index.md b/docs/index.md index b9477409b5..4366640083 100644 --- a/docs/index.md +++ b/docs/index.md @@ -160,6 +160,13 @@ gcloud: # - gs://bucket/additional.ipa # - path/to/local/ipa/file.ipa + ## A list of game-loop scenario numbers which will be run as part of the test (default: all scenarios). + ## A maximum of 1024 scenarios may be specified in one test matrix, but the maximum number may also be limited by the overall test --timeout setting. + # scenario-numbers: + # - 1 + # - 2 + # - 3 + ## The type of iOS test to run. TYPE must be one of: xctest, game-loop. Default: xctest # type: xctest @@ -361,6 +368,13 @@ gcloud: # - local/file/path/test1.obb # - local/file/path/test2.obb + ## A list of game-loop scenario numbers which will be run as part of the test (default: all scenarios). + ## A maximum of 1024 scenarios may be specified in one test matrix, but the maximum number may also be limited by the overall test --timeout setting. + # scenario-numbers: + # - 1 + # - 2 + # - 3 + ## A list of OBB required filenames. OBB file name must conform to the format as specified by Android e.g. ## [main|patch].0300110.com.example.android.obb which will be installed into <shared-storage>/Android/obb/<package-name>/ on the device. # obb-names: diff --git a/test_runner/flank.ios.yml b/test_runner/flank.ios.yml index 6c3a2cfb25..5c50560f56 100644 --- a/test_runner/flank.ios.yml +++ b/test_runner/flank.ios.yml @@ -91,6 +91,13 @@ gcloud: # - gs://bucket/additional.ipa # - path/to/local/ipa/file.ipa + ## A list of game-loop scenario numbers which will be run as part of the test (default: all scenarios). + ## A maximum of 1024 scenarios may be specified in one test matrix, but the maximum number may also be limited by the overall test --timeout setting. + # scenario-numbers: + # - 1 + # - 2 + # - 3 + ## The type of iOS test to run. TYPE must be one of: xctest, game-loop. Default: xctest # type: xctest diff --git a/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt b/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt index 0476fd8395..42c76f6c70 100644 --- a/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt @@ -15,7 +15,6 @@ data class AndroidArgs( val environmentVariables: Map<String, String>, // should not be printed, becuase could contains sensitive informations val directoriesToPull: List<String>, val grantPermissions: String?, - val scenarioNumbers: List<String>, val scenarioLabels: List<String>, val obbFiles: List<String>, val obbNames: List<String>, diff --git a/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt b/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt index 94f5110cbe..7cdb512aa4 100644 --- a/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt @@ -20,6 +20,7 @@ data class CommonArgs( override val networkProfile: String?, override val otherFiles: Map<String, String>, override val type: Type?, + override val scenarioNumbers: List<String>, // flank override val project: String, diff --git a/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt b/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt index 0b9d3de164..c8520a936c 100644 --- a/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt @@ -41,6 +41,5 @@ fun createAndroidArgs( obfuscateDumpShards = obfuscate, obbFiles = gcloud.obbfiles!!, obbNames = gcloud.obbnames!!, - scenarioNumbers = gcloud.scenarioNumbers!!, grantPermissions = gcloud.grantPermissions ) diff --git a/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt b/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt index 5c3665e909..41c0dcb3c1 100644 --- a/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt @@ -26,6 +26,7 @@ fun CommonConfig.createCommonArgs( networkProfile = gcloud.networkProfile, clientDetails = gcloud.clientDetails, otherFiles = gcloud.otherFiles!!.mapValues { (_, path) -> path.normalizeFilePath() }, + scenarioNumbers = gcloud.scenarioNumbers!!, type = gcloud.type?.toType(), // flank diff --git a/test_runner/src/main/kotlin/ftl/args/IArgs.kt b/test_runner/src/main/kotlin/ftl/args/IArgs.kt index e98c5fa2fe..a046d1bc21 100644 --- a/test_runner/src/main/kotlin/ftl/args/IArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/IArgs.kt @@ -24,6 +24,7 @@ interface IArgs { val resultsHistoryName: String? val flakyTestAttempts: Int val otherFiles: Map<String, String> + val scenarioNumbers: List<String> val type: Type? get() = null // FlankYml diff --git a/test_runner/src/main/kotlin/ftl/args/IosArgs.kt b/test_runner/src/main/kotlin/ftl/args/IosArgs.kt index c85b505887..017dee948f 100644 --- a/test_runner/src/main/kotlin/ftl/args/IosArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/IosArgs.kt @@ -42,6 +42,7 @@ IosArgs num-flaky-test-attempts: $flakyTestAttempts other-files: ${ArgsToString.mapToString(otherFiles)} additional-ipas: ${ArgsToString.listToString(additionalIpas)} + scenario-numbers: ${ArgsToString.listToString(scenarioNumbers)} type: ${type?.ymlName} flank: diff --git a/test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt b/test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt index 15e4f05d4a..ca974c8b22 100644 --- a/test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt @@ -56,9 +56,10 @@ private fun AndroidArgs.assertLabelContent() { else -> obbFiles.forEach { ArgsHelper.assertFileExists(it, " (obb file)") } } - if (scenarioNumbers.isNotEmpty() && (type == null || type != Type.GAMELOOP)) + if (scenarioNumbers.isNotEmpty() && (type != Type.GAMELOOP)) throw FlankConfigurationError("Scenario numbers defined but Type is not Game-loop.") scenarioNumbers.forEach { it.toIntOrNull() ?: throw FlankConfigurationError("Invalid scenario number provided - $it") } + if (scenarioNumbers.size > 1024) throw FlankConfigurationError("There cannot be more than 1024 Scenario numbers") } private const val MAX_OBB_FILES = 2 diff --git a/test_runner/src/main/kotlin/ftl/args/ValidateIosArgs.kt b/test_runner/src/main/kotlin/ftl/args/ValidateIosArgs.kt index 0f3191f54d..bc0bb848a0 100644 --- a/test_runner/src/main/kotlin/ftl/args/ValidateIosArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/ValidateIosArgs.kt @@ -15,6 +15,14 @@ fun IosArgs.validate() = apply { checkResultsDirUnique() assertAdditionalIpas() validType() + assertGameloop() +} + +fun IosArgs.assertGameloop() { + if (scenarioNumbers.isNotEmpty() && (type != Type.GAMELOOP)) + throw FlankConfigurationError("Scenario numbers defined but Type is not Game-loop.") + scenarioNumbers.forEach { it.toIntOrNull() ?: throw FlankConfigurationError("Invalid scenario number provided - $it") } + if (scenarioNumbers.size > 1024) throw FlankConfigurationError("There cannot be more than 1024 Scenario numbers") } fun IosArgs.validateRefresh() = apply { diff --git a/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt b/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt index e5aa19c973..11c6e32da5 100644 --- a/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt +++ b/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt @@ -115,16 +115,6 @@ data class AndroidGcloudConfig @JsonIgnore constructor( @set:JsonProperty("directories-to-pull") var directoriesToPull: List<String>? by data - @set:CommandLine.Option( - names = ["--scenario-numbers"], - split = ",", - description = ["A list of game-loop scenario numbers which will be run as part of the test (default: all scenarios). " + - "A maximum of 1024 scenarios may be specified in one test matrix, " + - "but the maximum number may also be limited by the overall test --timeout setting."] - ) - @set:JsonProperty("scenario-numbers") - var scenarioNumbers: List<String>? by data - @set:CommandLine.Option( names = ["--scenario-labels"], split = ",", @@ -250,7 +240,6 @@ data class AndroidGcloudConfig @JsonIgnore constructor( environmentVariables = emptyMap() grantPermissions = FlankDefaults.GRANT_PERMISSIONS_ALL directoriesToPull = emptyList() - scenarioNumbers = emptyList() scenarioLabels = emptyList() obbfiles = emptyList() obbnames = emptyList() diff --git a/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt b/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt index 3afb361ce5..341b8d4a03 100644 --- a/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt +++ b/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt @@ -130,6 +130,16 @@ data class CommonGcloudConfig @JsonIgnore constructor( @set:JsonProperty("other-files") var otherFiles: Map<String, String>? by data + @set:CommandLine.Option( + names = ["--scenario-numbers"], + split = ",", + description = ["A list of game-loop scenario numbers which will be run as part of the test (default: all scenarios). " + + "A maximum of 1024 scenarios may be specified in one test matrix, " + + "but the maximum number may also be limited by the overall test --timeout setting."] + ) + @set:JsonProperty("scenario-numbers") + var scenarioNumbers: List<String>? by data + @set:CommandLine.Option( names = ["--type"], description = ["The type of test to run. TYPE must be one of: instrumentation, robo, xctest, game-loop."] @@ -162,6 +172,7 @@ data class CommonGcloudConfig @JsonIgnore constructor( devices = listOf(defaultDevice(android)) otherFiles = emptyMap() type = null + scenarioNumbers = emptyList() } } } diff --git a/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt b/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt index dd94ebad87..e9460a3ec3 100644 --- a/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt +++ b/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt @@ -241,6 +241,7 @@ IosArgs additional-ipas: - $testIpa1 - $testIpa2 + scenario-numbers: type: xctest flank: @@ -301,6 +302,7 @@ IosArgs num-flaky-test-attempts: 0 other-files: additional-ipas: + scenario-numbers: type: xctest flank: @@ -1127,6 +1129,62 @@ IosArgs assertFalse(systemOutRule.log.contains("WARNING: Google cloud storage result directory should be unique, otherwise results from multiple test matrices will be overwritten or intermingled")) } } + + @Test + fun `should not throw exception if game-loop is provided and nothing else`() { + val yaml = """ + gcloud: + test: $testPath + xctestrun-file: $testPath + results-dir: test + type: game-loop + """.trimIndent() + IosArgs.load(yaml).validate() + } + + @Test(expected = FlankConfigurationError::class) + fun `should throw exception if game-loop is not provided and scenario numbers are`() { + val yaml = """ + gcloud: + test: $testPath + xctestrun-file: $testPath + results-dir: test + scenario-numbers: + - 1 + - 2 + """.trimIndent() + IosArgs.load(yaml).validate() + } + + @Test + fun `should not throw exception if game-loop is provided and scenario numbers are`() { + val yaml = """ + gcloud: + test: $testPath + xctestrun-file: $testPath + results-dir: test + type: game-loop + scenario-numbers: + - 1 + - 2 + """.trimIndent() + IosArgs.load(yaml).validate() + } + + @Test(expected = FlankConfigurationError::class) + fun `should throw exception if invalid scenario numbers are provided`() { + val yaml = """ + gcloud: + test: $testPath + xctestrun-file: $testPath + results-dir: test + type: game-loop + scenario-numbers: + - error1 + - error2 + """.trimIndent() + IosArgs.load(yaml).validate() + } } private fun IosArgs.Companion.load(yamlData: String, cli: IosRunCommand? = null): IosArgs = From eba5ffecafd3bd26aa61ba784f655d7051f1bccb Mon Sep 17 00:00:00 2001 From: piotradamczyk5 <65554637+piotradamczyk5@users.noreply.github.com> Date: Mon, 2 Nov 2020 13:31:04 +0100 Subject: [PATCH 2/3] refactor: Rewrite scripts to Kotlin (#1246) * feat: Change updateFlank script to Kotlin * create PathHelper * added buildGo script * GradleCommand, updateFlank - windows compability * fix GradleCommand.kt * added buildFlankScripts.main.kts * fixed buildGo script * added testFilters helpers * Add android paths, initial work of android build command * Add build of base android apk and tests with copy to directory * added update library kotlin script * WIP added first iteration of windows file downloads * Add duplicated apk names build and doc * Update android.ops.main.kts * Added build of multiModule app and cucumber App * Update android.ops.main.kts * adding updatingJson and generatingJavaClient * initial ios * Update ios.ops.main.kts * Update ios.ops.main.kts * Update ios.ops.main.kts * run pod install working * Build and copy ios artifacts * added ios scripts * added iosScripts * directories reorganization * refactor utils * Update ios.ops.main.kts * Ios Command in separated file * ios example change to createCommand * Move some functions of ios to downloadSoftware main * refactor utils * Update ios.ops.main.kts * Add go build execution to ops * added shell and bat script for firebase api * added shell for generate java client * refactor gradle helper * Update buildGo.main.kts * Update buildGo.main.kts * add scripts for update flank and build flank scripts * refractor * added scripts for go * added directory to scripts * added script fo unversal framework * clean up * Gradle compability with windows * fix execution * Update android script * Update updateFlank.main.kts * Windows compability for build/update flank * Update formating * fast fail on mac only scripts * Added section to web page * fix windows scripts * Add build option for standalone run * remove unused environment declaration * Update flank-bash/scripts/android.ops.main.kts Co-authored-by: piotradamczyk5 <65554637+piotradamczyk5@users.noreply.github.com> * code formating * code style changes * Add check and install command * Add download software * rewrite updateBinaries to flankScripts * rewrite updateBinaries to flankScripts * rewrite updateBinaries to flankScripts * Move firebase to shell * Add custom error on generate java client * rewrite ios to flankScripts * Add UpdateApiJsonCommand * Ops commands initial * Add android build * rewrite buildFlank, testFilter and goops to flankScripts * Change classes to objects * added ios ops and make some code and documentation changes * added shell command * fixed scripts for building flankScripts * remove standalon flank bash * fixed native scripts * fix native scripts and descriptions * update documentation * fix scripts * Add --copy --generate --artifacts options * update documentation for ops * Update ops.sh * Add ios commands * Add windows batch script * Add batch script to build android * Fix build on windows * Update EarlGrey ops * Update simple-ios-flank.yml * fix ops.sh script * fixed pipe * Add support for FlankExample build * Add commands for biuld flankExample and earlGreyExample * Update README.md * Update buildFlankScripts.bat Co-authored-by: Adam <adam.filipowicz92@gmail.com> Co-authored-by: Michael Wright <Slooxied@gmail.com> Co-authored-by: adamfilipow92 <64852261+adamfilipow92@users.noreply.github.com> --- buildSrc/src/main/kotlin/Dependencies.kt | 2 + buildSrc/src/main/kotlin/Versions.kt | 2 + firebase_apis/generate_java_client.bat | 2 + firebase_apis/generate_java_client.sh | 34 +---- firebase_apis/update_api_json.bat | 2 + firebase_apis/update_api_json.sh | 20 +-- flank-scripts/README.md | 81 ++++++++++++ flank-scripts/bash/buildFlankScripts.bat | 1 - flank-scripts/bash/flankScripts.bat | 4 + flank-scripts/build.gradle.kts | 2 + .../src/main/kotlin/flank/scripts/Main.kt | 4 +- .../exceptions/FlankScriptsExceptions.kt | 6 + .../kotlin/flank/scripts/shell/BuildFlank.kt | 35 +++++ .../flank/scripts/shell/ShellCommand.kt | 33 +++++ .../scripts/shell/firebase/FirebaseCommand.kt | 18 +++ .../firebase/GenerateJavaClientCommand.kt | 36 ++++++ .../shell/firebase/UpdateApiJsonCommand.kt | 31 +++++ .../flank/scripts/shell/ios/BuildExample.kt | 57 ++++++++ .../flank/scripts/shell/ios/BuildFtl.kt | 47 +++++++ .../shell/ios/InstallXcPrettyCommand.kt | 12 ++ .../scripts/shell/ios/IosBuildCommand.kt | 10 ++ .../flank/scripts/shell/ios/LipoHelper.kt | 6 + .../flank/scripts/shell/ios/RunFtlLocal.kt | 32 +++++ .../scripts/shell/ios/SetupIosEnvCommand.kt | 17 +++ .../scripts/shell/ios/UniversalFramework.kt | 61 +++++++++ .../scripts/shell/ops/AndroidOpsCommand.kt | 26 ++++ .../flank/scripts/shell/ops/BuildAndroid.kt | 118 +++++++++++++++++ .../shell/ops/BuildEarlGreyExampleCommand.kt | 35 +++++ .../shell/ops/BuildFlankExampleCommand.kt | 32 +++++ .../flank/scripts/shell/ops/BuildIos.kt | 99 ++++++++++++++ .../kotlin/flank/scripts/shell/ops/GoOS.kt | 11 ++ .../flank/scripts/shell/ops/GoOpsCommand.kt | 36 ++++++ .../flank/scripts/shell/ops/OpsCommand.kt | 18 +++ .../shell/updatebinaries/UpdateAtomic.kt | 52 ++++++++ .../updatebinaries/UpdateBinariesCommand.kt | 21 +++ .../shell/updatebinaries/UpdateLlvm.kt | 78 +++++++++++ .../shell/updatebinaries/UpdateSwift.kt | 79 ++++++++++++ .../scripts/shell/utils/FastFailForWindows.kt | 11 ++ .../scripts/shell/utils/GradleCommand.kt | 17 +++ .../flank/scripts/shell/utils/PathHelper.kt | 22 ++++ .../flank/scripts/shell/utils/ShellHelper.kt | 12 ++ .../flank/scripts/shell/utils/TestFilters.kt | 28 ++++ .../testartifacts/core/DownloadFixtures.kt | 4 +- .../kotlin/flank/scripts/utils/Archive.kt | 56 ++++++++ .../kotlin/flank/scripts/utils/Download.kt | 6 +- .../flank/scripts/utils/DownloadSoftware.kt | 30 +++++ .../main/kotlin/flank/scripts/utils/Env.kt | 2 + .../flank/scripts/utils/ShellExecute.kt | 25 +++- test_projects/android/build.bat | 2 + test_projects/android/ops.sh | 122 +----------------- test_projects/gohello/build.bat | 2 + test_projects/gohello/build.sh | 12 +- .../ios/EarlGreyExample/build_example.sh | 35 +---- .../ios/EarlGreyExample/build_ftl.sh | 28 +--- test_projects/ios/EarlGreyExample/ops.sh | 67 +--------- .../ios/EarlGreyExample/run_ftl_local.sh | 17 +-- .../EarlGreyExample/universal_framework.sh | 28 +--- test_projects/ops.bat | 18 +++ test_projects/ops.sh | 20 +-- test_runner/bash/update_flank.bat | 9 +- test_runner/bash/update_flank.sh | 8 +- 61 files changed, 1364 insertions(+), 377 deletions(-) create mode 100755 firebase_apis/generate_java_client.bat create mode 100755 firebase_apis/update_api_json.bat create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/BuildFlank.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ShellCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/FirebaseCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/GenerateJavaClientCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/UpdateApiJsonCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildExample.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildFtl.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/InstallXcPrettyCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/IosBuildCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/LipoHelper.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/RunFtlLocal.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/SetupIosEnvCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ios/UniversalFramework.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/AndroidOpsCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildAndroid.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildEarlGreyExampleCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildFlankExampleCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildIos.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOS.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOpsCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/ops/OpsCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateAtomic.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateBinariesCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateLlvm.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateSwift.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/utils/FastFailForWindows.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/utils/GradleCommand.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/utils/PathHelper.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/utils/ShellHelper.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/shell/utils/TestFilters.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/utils/Archive.kt create mode 100644 flank-scripts/src/main/kotlin/flank/scripts/utils/DownloadSoftware.kt create mode 100644 test_projects/android/build.bat create mode 100644 test_projects/gohello/build.bat create mode 100644 test_projects/ops.bat diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 38a627e763..0b5dea3838 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -54,6 +54,8 @@ object Dependencies { const val KOTLIN_SERIALIZATION = "org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.KOTLIN_SERIALIZATION}" //region flank-scripts + const val ARCHIVE_LIB = "org.rauschig:jarchivelib:${Versions.ARCHIVE_LIB}" + const val TUKAANI_XZ = "org.tukaani:xz:${Versions.TUKAANI_XZ}" const val CLIKT = "com.github.ajalt:clikt:${Versions.CLIKT}" const val JCABI_GITHUB = "com.jcabi:jcabi-github:${Versions.JCABI_GITHUB}" const val SLF4J_NOP = "org.slf4j:slf4j-nop:${Versions.SLF4J_NOP}" diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 837a6222c6..b2dae80325 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -88,6 +88,8 @@ object Versions { const val PROGUARD = "7.0.0" // ============== flank-scripts ============== + const val ARCHIVE_LIB = "1.1.0" + const val TUKAANI_XZ = "1.0" const val KOTLIN_SERIALIZATION = "1.0.0" const val FUEL = "2.3.0" const val CLIKT = "2.8.0" diff --git a/firebase_apis/generate_java_client.bat b/firebase_apis/generate_java_client.bat new file mode 100755 index 0000000000..7b27596f9d --- /dev/null +++ b/firebase_apis/generate_java_client.bat @@ -0,0 +1,2 @@ +SET DIR=%~dp0 +%DIR%\..\..\flank-scripts\bash\flankScripts.bat shell firebase generateJavaClient diff --git a/firebase_apis/generate_java_client.sh b/firebase_apis/generate_java_client.sh index 08cc4b5f61..1ed7797d25 100755 --- a/firebase_apis/generate_java_client.sh +++ b/firebase_apis/generate_java_client.sh @@ -1,32 +1,2 @@ -#!/bin/bash - -# Note: Must have already installed google-apis-client-generator from the master branch. PIP release will not work! - -# git clone https://github.com/google/apis-client-generator.git -# xcode-select --install -# brew install python@2 -# export PATH="/usr/local/opt/python@2/libexec/bin:$PATH" -# pip install --upgrade pip setuptools -# pip install . - -# Generate only the testing library since the others are published officially already. - -# generate_library \ -# --input=./storage_v1.json \ -# --language=java \ -# --output_dir=./storage - -rm -rf "./test_api/src/" - - generate_library \ - --input=./json/testing_v1.json \ - --language=java \ - --package_path=api/services \ - --output_dir=./test_api/src/main/java - -mv ./test_api/src/main/java/pom.xml ./test_api/pom.xml - -# generate_library \ -# --input=./toolresults_v1beta3.json \ -# --language=java \ -# --output_dir=./apis/toolresults +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell firebase generateJavaClient diff --git a/firebase_apis/update_api_json.bat b/firebase_apis/update_api_json.bat new file mode 100755 index 0000000000..1de7925542 --- /dev/null +++ b/firebase_apis/update_api_json.bat @@ -0,0 +1,2 @@ +SET DIR=%~dp0 +%DIR%\..\..\flank-scripts\bash\flankScripts.bat shell firebase updateApiJson diff --git a/firebase_apis/update_api_json.sh b/firebase_apis/update_api_json.sh index f1635105cc..a07105b5b1 100755 --- a/firebase_apis/update_api_json.sh +++ b/firebase_apis/update_api_json.sh @@ -1,18 +1,2 @@ -#!/usr/bin/env bash - -# npm -g install sort-json - -# Note: API discovery JSON is out of date. Check the gcloud CLI repo for most recent JSON. -# https://github.com/bootstraponline/gcloud_cli/blob/master/google-cloud-sdk/lib/googlecloudsdk/third_party/apis/testing_v1.json - -cd json - -TOOL_RESULTS=toolresults_v1beta3.json -rm "$TOOL_RESULTS" -curl -o "$TOOL_RESULTS" https://www.googleapis.com/discovery/v1/apis/toolresults/v1beta3/rest -sort-json "$TOOL_RESULTS" - -TESTING=testing_v1.json -rm "$TESTING" -curl -o "$TESTING" https://www.googleapis.com/discovery/v1/apis/testing/v1/rest -sort-json "$TESTING" +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell firebase updateApiJson diff --git a/flank-scripts/README.md b/flank-scripts/README.md index 79c736387b..4eaaa3791a 100644 --- a/flank-scripts/README.md +++ b/flank-scripts/README.md @@ -163,3 +163,84 @@ All [testArtifacts](../flank-scripts/src/main/kotlin/flank/scripts/testartifacts #### `resolve` Automatically prepare local artifacts if needed. +### Shell + +To show all available commands for shell use: `flankScripts shell` + +Available commands are: + - `firebase` Contains all firebase commands + - `iosBuildExample` Build example ios app + - `iosBuildFtl` Build ftl ios app + - `iosRunFtlLocal` Run ftl locally ios app + - `iosUniversalFramework` Create Universal Framework + - `ops` Contains all ops command: android, ios, gp + - `updateBinaries` Update binaries used by Flank + - `buildFlank` Build Flank + +#### `firebase` + +Contains tasks related to firebase client generation. +These tasks are : + - `updateApiJson` Download file for generating client + - `generateJavaClient` Generates Java client + +##### `updateApiJson` +Download file for generating client + +##### `generateJavaClient` +Generate Java Client from json schema + +#### `iosBuildExample` +Build example ios app + +#### `iosBuildFtl` +Build ftl ios app + +#### `iosRunFtlLocal` +Run ftl locally ios app + +| Option | Description | +|-------------|--------------------------------------------------------------------------| +| --device-id | Device id. Please take it from Xcode -> Window -> Devices and Simulators | + +#### `iosUniversalFramework` + +#### `ops` +Contains tasks related to building sample apps with tests. +These tasks are : + - `go` Build go app with tests + - `ios` Build ios app with tests + - `android` Build android apks with tests + +##### `go` +Build go app with tests + +##### `build_earl_grey_example` +Build ios earl grey example app with tests + +| Option | Short option | Description | +|------------|--------------|--------------------------| +| --generate | -g | Make build | +| --copy | -c | Copy output files to tmp | + +##### `build_flank_example` +Build ios flank example app with tests + +| Option | Short option | Description | +|------------|--------------|--------------------------| +| --generate | -g | Make build | +| --copy | -c | Copy output files to tmp | + +##### `android` +Build android apks with tests + +| Option | Short option | Description | +|------------|--------------|--------------------------| +| --generate | -g | Make build | +| --copy | -c | Copy output files to tmp | + +#### `updateBinaries` +Update binaries used by Flank + +#### `buildFlank` +Build Flank test runner diff --git a/flank-scripts/bash/buildFlankScripts.bat b/flank-scripts/bash/buildFlankScripts.bat index 7c701773a2..aeeb65e9c6 100755 --- a/flank-scripts/bash/buildFlankScripts.bat +++ b/flank-scripts/bash/buildFlankScripts.bat @@ -1,4 +1,3 @@ -Rem REPLACE with #1246 SET DIR=%~dp0 SET FLANK_SCRIPTS=%DIR%\.. diff --git a/flank-scripts/bash/flankScripts.bat b/flank-scripts/bash/flankScripts.bat index 8158f96d59..bed24e6664 100755 --- a/flank-scripts/bash/flankScripts.bat +++ b/flank-scripts/bash/flankScripts.bat @@ -1,4 +1,8 @@ SET DIR=%~dp0 SET scriptsJar=%DIR%\flankScripts.jar +if not exist "%scriptsJar%" { + %DIR%\buildFlankScripts.bat +} + java -jar "%scriptsJar%" %* diff --git a/flank-scripts/build.gradle.kts b/flank-scripts/build.gradle.kts index b4f2c6c6fc..e89b908efe 100644 --- a/flank-scripts/build.gradle.kts +++ b/flank-scripts/build.gradle.kts @@ -74,6 +74,8 @@ dependencies { implementation(Dependencies.JCABI_GITHUB) implementation(Dependencies.SLF4J_NOP) implementation(Dependencies.GLASSFISH_JSON) + implementation(Dependencies.ARCHIVE_LIB) + implementation(Dependencies.TUKAANI_XZ) detektPlugins(Dependencies.DETEKT_FORMATTING) diff --git a/flank-scripts/src/main/kotlin/flank/scripts/Main.kt b/flank-scripts/src/main/kotlin/flank/scripts/Main.kt index 082ff2dabd..81dfc4fe8f 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/Main.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/Main.kt @@ -5,6 +5,7 @@ import com.github.ajalt.clikt.core.subcommands import flank.scripts.ci.CiCommand import flank.scripts.dependencies.DependenciesCommand import flank.scripts.release.ReleaseCommand +import flank.scripts.shell.ShellCommand import flank.scripts.testartifacts.TestArtifactsCommand class Main : CliktCommand(name = "flankScripts") { @@ -17,6 +18,7 @@ fun main(args: Array<String>) { ReleaseCommand(), CiCommand(), DependenciesCommand, - TestArtifactsCommand() + TestArtifactsCommand(), + ShellCommand ).main(args) } diff --git a/flank-scripts/src/main/kotlin/flank/scripts/exceptions/FlankScriptsExceptions.kt b/flank-scripts/src/main/kotlin/flank/scripts/exceptions/FlankScriptsExceptions.kt index 3299901d03..817f6e2c1d 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/exceptions/FlankScriptsExceptions.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/exceptions/FlankScriptsExceptions.kt @@ -16,3 +16,9 @@ class BugsnagException(val body: BugSnagResponse) : FlankScriptsExceptions() { return "Error while doing Bugnsag request, because of ${body.errors.joinToString()}" } } + +class ShellCommandException(private val errorMessage: String) : FlankScriptsExceptions() { + override fun toString(): String { + return "Error while executing shell command, details: $errorMessage" + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/BuildFlank.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/BuildFlank.kt new file mode 100644 index 0000000000..c8fe356b00 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/BuildFlank.kt @@ -0,0 +1,35 @@ +package flank.scripts.shell + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.createGradleCommand +import flank.scripts.shell.utils.rootDirectoryPathString +import flank.scripts.utils.runCommand +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +object BuildFlankCommand : CliktCommand(name = "buildFlank", help = "Build Flank") { + override fun run() { + buildFlank() + } +} + +private fun buildFlank() { + createGradleCommand( + workingDir = rootDirectoryPathString, + "-p", rootDirectoryPathString, ":test_runner:clean", ":test_runner:assemble", ":test_runner:shadowJar" + ) + .runCommand() + + copyFlankOutputFile() +} + +private fun copyFlankOutputFile() { + val flankDirectory = Paths.get(rootDirectoryPathString, "test_runner").toString() + + Files.copy( + Paths.get(flankDirectory, "build", "libs", "flank.jar"), + Paths.get(flankDirectory, "bash", "flank.jar"), + StandardCopyOption.REPLACE_EXISTING + ) +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ShellCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ShellCommand.kt new file mode 100644 index 0000000000..69bcaf5ca2 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ShellCommand.kt @@ -0,0 +1,33 @@ +package flank.scripts.shell + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands +import flank.scripts.shell.firebase.FirebaseCommand +import flank.scripts.shell.ios.BuildExampleCommand +import flank.scripts.shell.ios.BuildFtlCommand +import flank.scripts.shell.ios.InstallXcPrettyCommand +import flank.scripts.shell.ios.RunFtlLocalCommand +import flank.scripts.shell.ios.SetupIosEnvCommand +import flank.scripts.shell.ios.UniversalFrameworkCommand +import flank.scripts.shell.ops.OpsCommand +import flank.scripts.shell.updatebinaries.UpdateBinariesCommand + +object ShellCommand : CliktCommand(name = "shell", help = "Task for shell commands") { + init { + subcommands( + FirebaseCommand, + BuildExampleCommand, + BuildFtlCommand, + RunFtlLocalCommand, + UniversalFrameworkCommand, + OpsCommand, + UpdateBinariesCommand, + BuildFlankCommand, + InstallXcPrettyCommand, + SetupIosEnvCommand + ) + } + + @Suppress("EmptyFunctionBlock") + override fun run() {} +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/FirebaseCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/FirebaseCommand.kt new file mode 100644 index 0000000000..80d68d6377 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/FirebaseCommand.kt @@ -0,0 +1,18 @@ +package flank.scripts.shell.firebase + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands + +object FirebaseCommand : CliktCommand(name = "firebase", help = "Contains all firebase commands") { + + init { + subcommands( + UpdateApiJsonCommand, + GenerateJavaClientCommand + ) + } + + @Suppress("EmptyFunctionBlock") + override fun run() { + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/GenerateJavaClientCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/GenerateJavaClientCommand.kt new file mode 100644 index 0000000000..ff7f1ddee5 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/GenerateJavaClientCommand.kt @@ -0,0 +1,36 @@ +package flank.scripts.shell.firebase + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.exceptions.ShellCommandException +import flank.scripts.utils.checkIfPipInstalled +import flank.scripts.utils.installClientGeneratorIfNeeded +import flank.scripts.utils.runCommand +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +object GenerateJavaClientCommand : CliktCommand(name = "generateJavaClient", help = "Generate Java Client") { + + override fun run() { + checkIfPipInstalled() + installClientGeneratorIfNeeded() + val apiPath = Paths.get("test_api").toString() + val outputDirectory = Paths.get(apiPath, "src", "main", "java").toString() + val testingJsonInput = Paths.get("json", "testing_v1.json").toString() + Paths.get(apiPath, "src").toFile().deleteRecursively() + + val generateLibraryCommand = "generate_library " + + "--input=$testingJsonInput " + + "--language=java " + + "--output_dir=$outputDirectory" + + val result = generateLibraryCommand.runCommand() + if (result != 0) throw ShellCommandException("Error when execute generate_library command") + + Files.move( + Paths.get(outputDirectory, "pom.xml"), + Paths.get(apiPath, "pom.xml"), + StandardCopyOption.REPLACE_EXISTING + ) + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/UpdateApiJsonCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/UpdateApiJsonCommand.kt new file mode 100644 index 0000000000..831bfa5a79 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/firebase/UpdateApiJsonCommand.kt @@ -0,0 +1,31 @@ +package flank.scripts.shell.firebase + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.currentPath +import flank.scripts.utils.downloadFile +import flank.scripts.utils.downloadSortJsonIfNeeded +import flank.scripts.utils.runCommand +import java.nio.file.Paths + +object UpdateApiJsonCommand : CliktCommand(name = "updateApiJson", help = "Download file for generating client") { + override fun run() { + val jsonDirectoryPath = Paths.get(currentPath.toString(), "json") + val testingV1Path = Paths.get(jsonDirectoryPath.toString(), "testing_v1.json").toString() + val testingV1Beta3Path = Paths.get(jsonDirectoryPath.toString(), "toolresults_v1beta3.json").toString() + + downloadFile( + "https://raw.githubusercontent.com/Flank/gcloud_cli/master/google-cloud-sdk/lib/googlecloudsdk/third_party/apis/testing_v1.json", + testingV1Path + ) + + downloadFile( + "https://raw.githubusercontent.com/Flank/gcloud_cli/master/google-cloud-sdk/lib/googlecloudsdk/third_party/apis/toolresults_v1beta3.json", + testingV1Beta3Path + ) + + downloadSortJsonIfNeeded() + + "sort-json $testingV1Path".runCommand() + "sort-json $testingV1Beta3Path".runCommand() + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildExample.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildExample.kt new file mode 100644 index 0000000000..bdbf611155 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildExample.kt @@ -0,0 +1,57 @@ + +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.currentPath +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.shell.utils.pipe +import flank.scripts.utils.archive +import flank.scripts.utils.downloadXcPrettyIfNeeded +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardCopyOption +import java.util.stream.Collectors + +object BuildExampleCommand : CliktCommand(name = "iosBuildExample", help = "Build example ios app") { + override fun run() { + failIfWindows() + downloadXcPrettyIfNeeded() + buildExample() + } +} + +private fun buildExample() { + val dataPath: Path = Paths.get(currentPath.toString(), "dd_tmp").apply { + toFile().deleteRecursively() + } + + val xcodeCommandSwiftTests = createIosBuildCommand( + dataPath.toString(), + "./EarlGreyExample.xcworkspace", + "EarlGreyExampleSwiftTests" + ) + xcodeCommandSwiftTests pipe "xcpretty" + + val xcodeCommandTests = createIosBuildCommand(dataPath.toString(), "./EarlGreyExample.xcworkspace", "EarlGreyExampleTests") + xcodeCommandTests pipe "xcpretty" + + copyExampleOutputFiles(dataPath.toString()) +} + +private fun copyExampleOutputFiles(dataPath: String) { + val archiveFileName = "earlgrey_example.zip" + val buildProductPath = Paths.get(dataPath, "Build", "Products") + + Files.walk(Paths.get("")) + .filter { it.fileName.toString().endsWith("-iphoneos") || it.fileName.toString().endsWith(".xctestrun") } + .map { it.toFile() } + .collect(Collectors.toList()) + .archive(archiveFileName, currentPath.toFile()) + + Files.move( + Paths.get("", archiveFileName), + Paths.get(buildProductPath.toString(), archiveFileName), + StandardCopyOption.REPLACE_EXISTING + ) +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildFtl.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildFtl.kt new file mode 100644 index 0000000000..8b355490e0 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/BuildFtl.kt @@ -0,0 +1,47 @@ +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.shell.utils.pipe +import flank.scripts.utils.archive +import flank.scripts.utils.downloadXcPrettyIfNeeded +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption +import java.util.stream.Collectors + +object BuildFtlCommand : CliktCommand(name = "iosBuildFtl", help = "Build ftl ios app") { + override fun run() { + failIfWindows() + downloadXcPrettyIfNeeded() + buildFtl() + } +} + +private fun buildFtl() { + val dataPath = Paths.get("", "dd_tmp").apply { + toFile().deleteRecursively() + }.toString() + val xcodeCommand = createIosBuildCommand(dataPath, "./EarlGreyExample.xcworkspace", "\"EarlGreyExampleSwiftTests\"") + + xcodeCommand pipe "xcpretty" + copyFtlOutputFiles(dataPath) +} + +private fun copyFtlOutputFiles(dataPath: String) { + val archiveFileName = "earlgrey_example.zip" + val buildProductPath = Paths.get(dataPath, "Build", "Products") + val currentPath = Paths.get("") + + Files.walk(currentPath) + .filter { it.fileName.toString().endsWith("-iphoneos") || it.fileName.toString().endsWith(".xctestrun") } + .map { it.toFile() } + .collect(Collectors.toList()) + .archive(archiveFileName, currentPath.toFile()) + + Files.move( + Paths.get("", archiveFileName), + Paths.get(buildProductPath.toString(), archiveFileName), + StandardCopyOption.REPLACE_EXISTING + ) +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/InstallXcPrettyCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/InstallXcPrettyCommand.kt new file mode 100644 index 0000000000..86247fc3ab --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/InstallXcPrettyCommand.kt @@ -0,0 +1,12 @@ +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.utils.downloadXcPrettyIfNeeded + +object InstallXcPrettyCommand : CliktCommand(name = "install_xcpretty", help = "Build ios app with tests") { + override fun run() { + failIfWindows() + downloadXcPrettyIfNeeded() + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/IosBuildCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/IosBuildCommand.kt new file mode 100644 index 0000000000..947fe99784 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/IosBuildCommand.kt @@ -0,0 +1,10 @@ +package flank.scripts.shell.ios + +fun createIosBuildCommand(buildDir: String, workspace: String, scheme: String, project: String = "") = + "xcodebuild build-for-testing" + + " -allowProvisioningUpdates" + + (if (workspace.isBlank()) "" else " -workspace $workspace") + + (if (project.isBlank()) "" else " -project $project") + + " -scheme $scheme" + + " -derivedDataPath $buildDir" + + " -sdk iphoneos" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/LipoHelper.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/LipoHelper.kt new file mode 100644 index 0000000000..d442ed3176 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/LipoHelper.kt @@ -0,0 +1,6 @@ +package flank.scripts.shell.ios + +fun createLipoCommand( + outputPath: String, + vararg files: String +) = "lipo -create ${files.joinToString(" ")} -output $outputPath" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/RunFtlLocal.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/RunFtlLocal.kt new file mode 100644 index 0000000000..ff20f624ab --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/RunFtlLocal.kt @@ -0,0 +1,32 @@ +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.option +import com.github.ajalt.clikt.parameters.options.required +import flank.scripts.shell.utils.currentPath +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.utils.runCommand +import java.nio.file.Path +import java.nio.file.Paths + +object RunFtlLocalCommand : CliktCommand(name = "iosRunFtlLocal", help = "Run ftl locally ios app") { + + private val deviceId by option(help = "Device id. Please take it from Xcode -> Window -> Devices and Simulators") + .required() + + override fun run() { + failIfWindows() + runFtlLocal(deviceId) + } +} + +private fun runFtlLocal(deviceId: String) { + val dataPath: Path = Paths.get(currentPath.toString(), "dd_tmp", "Build", "Products") + + val xcodeCommand = "xcodebuild test-without-building " + + " -xctestrun $dataPath/*.xctestrun " + + "-derivedDataPath $dataPath " + + "-destination 'id=$deviceId'" + + xcodeCommand.runCommand() +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/SetupIosEnvCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/SetupIosEnvCommand.kt new file mode 100644 index 0000000000..47eac9ee68 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/SetupIosEnvCommand.kt @@ -0,0 +1,17 @@ +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.ops.EARL_GREY_EXAMPLE +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.shell.utils.iOSTestProjectsPath +import flank.scripts.utils.downloadCocoaPodsIfNeeded +import flank.scripts.utils.installPods +import java.nio.file.Paths + +object SetupIosEnvCommand : CliktCommand(name = "setup_ios_env", help = "Build ios app with tests") { + override fun run() { + failIfWindows() + downloadCocoaPodsIfNeeded() + installPods(Paths.get(iOSTestProjectsPath, EARL_GREY_EXAMPLE)) + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/UniversalFramework.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/UniversalFramework.kt new file mode 100644 index 0000000000..d96be8649e --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ios/UniversalFramework.kt @@ -0,0 +1,61 @@ +package flank.scripts.shell.ios + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.currentPath +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.utils.runCommand +import java.nio.file.Paths + +object UniversalFrameworkCommand : CliktCommand(name = "iosUniversalFramework", help = "Create Universal Framework") { + override fun run() { + failIfWindows() + createUniversalFiles() + } +} + +private const val APP_FRAMEWORK_FRAMEWORK = "AppFramework.framework" +private const val APP_FRAMEWORK = "AppFramework" + +private fun createUniversalFiles() { + val comboPath = Paths.get(currentPath.toString(), "ios-frameworks").toString() + val devicePath = Paths.get(comboPath, "Debug-iphoneos").toString() + val simPath = Paths.get(comboPath, "Debug-iphonesimulator").toString() + + listOf( + "libChannelLib.a", + "libCommonLib.a", + "libeDistantObject.a", + "libTestLib.a", + "libUILib.a" + ).map { fileName -> + createLipoCommand( + outputPath = Paths.get(comboPath, fileName).toString(), + Paths.get(devicePath, fileName).toString(), Paths.get(simPath, fileName).toString() + ) + }.forEach { command -> command.runCommand() } + + copyAppFrameworkFiles(devicePath, comboPath) + + runDsym( + universalFileOutput = Paths.get(comboPath, APP_FRAMEWORK_FRAMEWORK, APP_FRAMEWORK).toString(), + comboPath = comboPath, + files = arrayOf( + Paths.get(devicePath, APP_FRAMEWORK_FRAMEWORK, APP_FRAMEWORK).toString(), + Paths.get(simPath, APP_FRAMEWORK_FRAMEWORK, APP_FRAMEWORK).toString() + ) + ) +} + +private fun copyAppFrameworkFiles(fromPath: String, toPath: String) { + Paths.get(fromPath, APP_FRAMEWORK_FRAMEWORK).toFile() + .copyRecursively(Paths.get(toPath, APP_FRAMEWORK_FRAMEWORK).toFile(), overwrite = true) +} + +private fun runDsym( + universalFileOutput: String, + comboPath: String, + files: Array<String> +) { + createLipoCommand(universalFileOutput, *files).runCommand() + "dsymutil $universalFileOutput --out ${Paths.get(comboPath, "AppFramework.framework.dSYM")}".runCommand() +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/AndroidOpsCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/AndroidOpsCommand.kt new file mode 100644 index 0000000000..d837f45723 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/AndroidOpsCommand.kt @@ -0,0 +1,26 @@ +package flank.scripts.shell.ops + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.multiple +import com.github.ajalt.clikt.parameters.options.option + +object AndroidOpsCommand : CliktCommand(name = "android", help = "Build android apks with tests") { + + private val generate: Boolean by option(help = "Make build").flag("-g", default = true) + + private val copy: Boolean by option(help = "Copy output files to tmp").flag("-c", default = true) + + private val artifacts: List<String> by option().multiple() + + override fun run() { + if (generate.not()) return + AndroidBuildConfiguration(artifacts, generate, copy).run { + buildBaseApk() + buildBaseTestApk() + buildDuplicatedNamesApks() + buildMultiModulesApks() + buildCucumberSampleApp() + } + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildAndroid.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildAndroid.kt new file mode 100644 index 0000000000..3bb9c033c5 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildAndroid.kt @@ -0,0 +1,118 @@ +package flank.scripts.shell.ops + +import flank.scripts.shell.utils.androidTestProjectsPath +import flank.scripts.shell.utils.createGradleCommand +import flank.scripts.shell.utils.flankFixturesTmpPath +import flank.scripts.utils.runCommand +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +fun AndroidBuildConfiguration.buildBaseApk() { + if (artifacts.canExecute("buildBaseApk").not()) return + + createGradleCommand( + workingDir = androidTestProjectsPath, + options = listOf("-p", androidTestProjectsPath, "app:assemble") + ).runCommand() + + if (copy) copyBaseApk() +} + +private fun copyBaseApk() { + val outputDir = Paths.get(flankFixturesTmpPath, "apk", "app-debug.apk") + + if (!outputDir.parent.toFile().exists()) Files.createDirectories(outputDir.parent) + + val assembleDirectory = Paths.get(androidTestProjectsPath, "app", "build", "outputs", "apk", "singleSuccess", "debug", "app-single-success-debug.apk") + Files.copy(assembleDirectory, outputDir, StandardCopyOption.REPLACE_EXISTING) +} + +fun AndroidBuildConfiguration.buildBaseTestApk() { + if (artifacts.canExecute("buildBaseTestApk").not()) return + createGradleCommand( + workingDir = androidTestProjectsPath, + options = listOf("-p", androidTestProjectsPath, "app:assembleAndroidTest") + ).runCommand() + + if (copy) copyBaseTestApk() +} + +private fun copyBaseTestApk() { + val assembleDirectory = Paths.get(androidTestProjectsPath, "app", "build", "outputs", "apk", "androidTest") + assembleDirectory.toFile().findApks().forEach { + Files.copy(it.toPath(), Paths.get(flankFixturesTmpPath, "apk", it.name), StandardCopyOption.REPLACE_EXISTING) + } +} + +fun AndroidBuildConfiguration.buildDuplicatedNamesApks() { + if (artifacts.canExecute("buildDuplicatedNamesApks").not()) return + val modules = (0..3).map { "dir$it" } + + createGradleCommand( + workingDir = androidTestProjectsPath, + options = listOf("-p", androidTestProjectsPath) + modules.map { "$it:testModule:assembleAndroidTest" }.toList() + ).runCommand() + + if (copy) copyDuplicatedNamesApks() +} + +private fun copyDuplicatedNamesApks() { + val modules = (0..3).map { "dir$it" } + val outputDir = Paths.get(flankFixturesTmpPath, "apk", "duplicated_names") + if (!outputDir.toFile().exists()) Files.createDirectories(outputDir) + + modules.map { Paths.get(androidTestProjectsPath, it, "testModule", "build", "outputs", "apk").toFile() } + .flatMap { it.findApks().toList() } + .forEachIndexed { index, file -> + file.copyApkToDirectory(Paths.get(outputDir.toString(), modules[index], file.name)) + } +} + +private fun File.copyApkToDirectory(output: Path): Path = toPath().let { sourceFile -> + if (!output.parent.toFile().exists()) Files.createDirectories(output.parent) + Files.copy(sourceFile, output, StandardCopyOption.REPLACE_EXISTING) +} + +fun AndroidBuildConfiguration.buildMultiModulesApks() { + if (artifacts.canExecute("buildMultiModulesApks").not()) return + createGradleCommand( + workingDir = androidTestProjectsPath, + options = listOf("-p", androidTestProjectsPath, + ":multi-modules:multiapp:assemble") + (1..20).map { ":multi-modules:testModule$it:assembleAndroidTest" }).runCommand() + + if (copy) copyMultiModulesApks() +} + +private fun copyMultiModulesApks() { + val outputDir = Paths.get(flankFixturesTmpPath, "apk", "multi-modules").toString() + Paths.get(androidTestProjectsPath, "multi-modules").toFile().findApks() + .forEach { it.copyApkToDirectory(Paths.get(outputDir, it.name)) } +} + +fun AndroidBuildConfiguration.buildCucumberSampleApp() { + if (artifacts.canExecute("buildMultiModulesApks").not()) return + createGradleCommand( + workingDir = androidTestProjectsPath, + options = listOf("-p", androidTestProjectsPath, "cucumber_sample_app:cukeulator:assembleDebug", ":cucumber_sample_app:cukeulator:assembleAndroidTest") + ).runCommand() + + if (copy) copyCucumberSampleApp() +} + +private fun copyCucumberSampleApp() { + val outputDir = Paths.get(flankFixturesTmpPath, "apk", "cucumber_sample_app").toString() + Paths.get(androidTestProjectsPath, "cucumber_sample_app").toFile().findApks().copyApksToPath(outputDir) +} + +private fun File.findApks() = walk().filter { it.extension == "apk" } + +private fun Sequence<File>.copyApksToPath(outputDirectory: String) = forEach { + it.copyApkToDirectory(Paths.get(outputDirectory, it.name)) +} + +private fun List<String>.canExecute(actionName: String) = isEmpty() || any { it.toLowerCase() == actionName.toLowerCase() } + +data class AndroidBuildConfiguration(val artifacts: List<String>, val generate: Boolean, val copy: Boolean) diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildEarlGreyExampleCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildEarlGreyExampleCommand.kt new file mode 100644 index 0000000000..8f92a88303 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildEarlGreyExampleCommand.kt @@ -0,0 +1,35 @@ +package flank.scripts.shell.ops + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.shell.utils.iOSTestProjectsPath +import java.nio.file.Paths + +object BuildEarlGreyExampleCommand : CliktCommand(name = "build_earl_grey_example", help = "Build ios earl grey example app with tests") { + + private val generate: Boolean? by option(help = "Make build").flag("-g", default = true) + + private val copy: Boolean? by option(help = "Copy output files to tmp").flag("-c", default = true) + + override fun run() { + failIfWindows() + + IosBuildConfiguration( + projectPath = Paths.get(iOSTestProjectsPath, EARL_GREY_EXAMPLE).toString(), + projectName = EARL_GREY_EXAMPLE, + buildConfigurations = listOf( + IosTestBuildConfiguration(EARL_GREY_EXAMPLE_SWIFT_TESTS, "swift"), + IosTestBuildConfiguration(EARL_GREY_EXAMPLE_TESTS, "objective_c") + ), + useWorkspace = true, + generate = generate ?: true, + copy = copy ?: true + ).generateIos() + } +} + +const val EARL_GREY_EXAMPLE = "EarlGreyExample" +private const val EARL_GREY_EXAMPLE_TESTS = "EarlGreyExampleTests" +private const val EARL_GREY_EXAMPLE_SWIFT_TESTS = "EarlGreyExampleSwiftTests" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildFlankExampleCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildFlankExampleCommand.kt new file mode 100644 index 0000000000..5cd940f831 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildFlankExampleCommand.kt @@ -0,0 +1,32 @@ +package flank.scripts.shell.ops + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import flank.scripts.shell.utils.failIfWindows +import flank.scripts.shell.utils.iOSTestProjectsPath +import java.nio.file.Paths + +object BuildFlankExampleCommand : CliktCommand(name = "build_flank_example", help = "Build ios flank example app with tests") { + + private val generate: Boolean? by option(help = "Make build").flag("-g", default = true) + + private val copy: Boolean? by option(help = "Copy output files to tmp").flag("-c", default = true) + + override fun run() { + failIfWindows() + + IosBuildConfiguration( + projectPath = Paths.get(iOSTestProjectsPath, FLANK_EXAMPLE).toString(), + projectName = FLANK_EXAMPLE, + buildConfigurations = listOf( + IosTestBuildConfiguration(FLANK_EXAMPLE, "tests"), + ), + useWorkspace = false, + generate = generate ?: true, + copy = copy ?: true + ).generateIos() + } +} + +private const val FLANK_EXAMPLE = "FlankExample" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildIos.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildIos.kt new file mode 100644 index 0000000000..d10e9ccc33 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/BuildIos.kt @@ -0,0 +1,99 @@ +package flank.scripts.shell.ops + +import flank.scripts.shell.ios.createIosBuildCommand +import flank.scripts.shell.utils.flankFixturesIosTmpPath +import flank.scripts.shell.utils.pipe +import flank.scripts.utils.archive +import flank.scripts.utils.downloadCocoaPodsIfNeeded +import flank.scripts.utils.downloadXcPrettyIfNeeded +import flank.scripts.utils.installPods +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths + +fun IosBuildConfiguration.generateIos() { + downloadCocoaPodsIfNeeded() + installPods(Paths.get(projectPath)) + downloadXcPrettyIfNeeded() + if (generate) buildEarlGreyExample() +} + +private fun IosBuildConfiguration.buildEarlGreyExample() = Paths.get(projectPath, "Build") + .runBuilds(this) + .resolve("Products") + .apply { renameXctestFiles().filterFilesToCopy().archiveProject(projectName).copyIosProductFiles(projectName) } + .copyTestFiles(this) + +private fun Path.runBuilds(configuration: IosBuildConfiguration) = apply { + toFile().deleteRecursively() + val parent = toFile().parent + val workspace = + if (configuration.useWorkspace) Paths.get(parent, configuration.workspaceName).toString() + else "" + + val project = if (configuration.useWorkspace) "" + else Paths.get(parent, "${configuration.projectName}.xcodeproj").toString() + configuration.buildConfigurations.forEach { + val buildCommand = createIosBuildCommand( + parent, + workspace, + scheme = it.scheme, + project, + ) + buildCommand pipe "xcpretty" + } +} + +private fun Path.renameXctestFiles() = apply { + toFile().walk().filter { it.extension == "xctestrun" }.forEach { + it.reduceTestFileName() + } +} + +private fun Sequence<File>.archiveProject(projectName: String) = also { + it.toList().archive("$projectName.zip", File(flankFixturesIosTmpPath, projectName)) +} + +private fun File.reduceTestFileName() = + renameTo(toPath().parent.resolve(name.reduceTestFileName()).toFile()) + +private fun String.reduceTestFileName() = + "_.*xctestrun".toRegex().replace(this, ".xctestrun") + +private fun Path.filterFilesToCopy() = + toFile().walk().filter { it.nameWithoutExtension.endsWith("-iphoneos") || it.extension == "xctestrun" } + +private fun Sequence<File>.copyIosProductFiles(projectName: String) = forEach { + if (it.isDirectory) it.copyRecursively(Paths.get(flankFixturesIosTmpPath, projectName, it.name).toFile(), overwrite = true) + else it.copyTo(Paths.get(flankFixturesIosTmpPath, projectName, it.name).toFile(), overwrite = true) +} + +private fun Path.copyTestFiles(configuration: IosBuildConfiguration) = toString().takeIf { configuration.copy }?.let { productsDirectory -> + val appDirectory = Paths.get(productsDirectory, "Debug-iphoneos").toFile().findTestDirectories() + appDirectory.forEach { + it.walk().filter { it.isFile && it.extension == "" }.forEach { testFile -> + configuration.copyTestFile(testFile) + } + } +} + +private fun IosBuildConfiguration.copyTestFile( + fileToCopy: File, +) = + fileToCopy.copyTo(Paths.get(flankFixturesIosTmpPath, projectName, fileToCopy.name).toFile(), true) + +private fun File.findTestDirectories() = walk().filter { it.isDirectory && it.name.endsWith(".xctest") } + +data class IosBuildConfiguration( + val projectPath: String, + val projectName: String, + val buildConfigurations: List<IosTestBuildConfiguration>, + val useWorkspace: Boolean = false, + val generate: Boolean = true, + val copy: Boolean = true +) + +data class IosTestBuildConfiguration(val scheme: String, val outputDirectoryName: String) + +private val IosBuildConfiguration.workspaceName + get() = "$projectName.xcworkspace" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOS.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOS.kt new file mode 100644 index 0000000000..a68d806cfa --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOS.kt @@ -0,0 +1,11 @@ +package flank.scripts.shell.ops + +enum class GoOS( + val goName: String, + val directory: String, + val extension: String = "" +) { + LINUX("linux", "bin/linux"), + MAC("darwin", "bin/mac"), + WINDOWS("windows", "bin/win", ".exe"), +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOpsCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOpsCommand.kt new file mode 100644 index 0000000000..761ca0703e --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/GoOpsCommand.kt @@ -0,0 +1,36 @@ +package flank.scripts.shell.ops + +import com.github.ajalt.clikt.core.CliktCommand +import flank.scripts.shell.utils.flankFixturesTmpPath +import flank.scripts.shell.utils.testProjectsPath +import flank.scripts.utils.runCommand +import java.nio.file.Path +import java.nio.file.Paths + +object GoOpsCommand : CliktCommand(name = "go", help = "Build go app with tests") { + override fun run() { + generateGoArtifacts() + } + + private fun generateGoArtifacts() { + val goHelloBinDirectoryPath = Paths.get(testProjectsPath, "gohello", "bin").apply { + toFile().deleteRecursively() + } + GoOS.values().forEach { createExecutable(it, goHelloBinDirectoryPath) } + } + + private fun createExecutable(os: GoOS, goHelloBinDirectoryPath: Path) { + Paths.get(goHelloBinDirectoryPath.toString(), *os.directory.split('/').toTypedArray()) + .toFile() + .mkdirs() + "go build -o ${os.createOutputPathForBinary()}".runCommand( + environmentVariables = mapOf( + "GOOS" to os.goName, + "GOARCH" to "amd64" + ) + ) + } + + private fun GoOS.createOutputPathForBinary() = + Paths.get(flankFixturesTmpPath, "gohello", directory, "gohello$extension") +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/OpsCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/OpsCommand.kt new file mode 100644 index 0000000000..c13d24ab81 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/ops/OpsCommand.kt @@ -0,0 +1,18 @@ +package flank.scripts.shell.ops + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands + +object OpsCommand : CliktCommand(name = "ops", help = "Contains all ops command: android, ios, gp") { + init { + subcommands( + AndroidOpsCommand, + BuildEarlGreyExampleCommand, + BuildFlankExampleCommand, + GoOpsCommand + ) + } + + @Suppress("EmptyFunctionBlock") + override fun run() {} +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateAtomic.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateAtomic.kt new file mode 100644 index 0000000000..e4ef86440b --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateAtomic.kt @@ -0,0 +1,52 @@ +package flank.scripts.shell.updatebinaries + +import flank.scripts.utils.downloadFile +import flank.scripts.utils.extract +import java.nio.file.Files +import java.nio.file.Paths +import java.util.stream.Collectors + +private val currentPath = Paths.get("") +private val atomicPath = Paths.get(currentPath.toString(), "libatomic") + +fun updateAtomic() { + val atomicDeb = Paths.get(atomicPath.toString(), "libatomic.deb").toFile() + val atomicDataTarXz = Paths.get(atomicPath.toString(), "data.tar.xz").toFile() + + if (atomicDeb.exists()) { + println("Atomic exists") + } else { + println("Downloading Atomic...") + atomicPath.toFile().mkdirs() + downloadFile( + srcUrl = "http://mirrors.kernel.org/ubuntu/pool/main/g/gcc-8/libatomic1_8-20180414-1ubuntu2_amd64.deb", + destinationPath = atomicDeb.toString() + ) + } + + atomicDeb.extract(atomicPath.toFile(), "ar") + atomicDataTarXz.extract(atomicPath.toFile(), "tar", "xz") + findAndCopyAtomicLicense() + findAndCopyAtomicFiles() + atomicPath.toFile().deleteRecursively() +} + +private fun findAndCopyAtomicLicense() { + val licenseOutputFile = Paths.get(currentPath.toString(), "libatomic.txt").toFile() + + downloadFile( + "http://changelogs.ubuntu.com/changelogs/pool/main/g/gcc-8/gcc-8_8-20180414-1ubuntu2/copyright", + licenseOutputFile.toString() + ) +} + +private fun findAndCopyAtomicFiles() { + println("Copying atomic files ...") + val list = Files.walk(atomicPath) + .filter { it.toString().endsWith("libatomic.so.1") || it.toString().endsWith("libatomic.so.1.2.0") } + .collect(Collectors.toList()) + + list.forEach { + it.toFile().copyTo(Paths.get(currentPath.toString(), it.fileName.toString()).toFile(), true) + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateBinariesCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateBinariesCommand.kt new file mode 100644 index 0000000000..b42fe638ad --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateBinariesCommand.kt @@ -0,0 +1,21 @@ +package flank.scripts.shell.updatebinaries + +import com.github.ajalt.clikt.core.CliktCommand +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +object UpdateBinariesCommand : CliktCommand(name = "updateBinaries", help = "Update binaries used by Flank") { + + override fun run() { + runBlocking { + listOf( + launch { updateAtomic() }, + launch { updateLlvm() }, + launch { updateSwift() } + ).joinAll() + + println("Binaries updated") + } + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateLlvm.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateLlvm.kt new file mode 100644 index 0000000000..4e652d8312 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateLlvm.kt @@ -0,0 +1,78 @@ +package flank.scripts.shell.updatebinaries + +import flank.scripts.utils.downloadFile +import flank.scripts.utils.extract +import flank.scripts.utils.isWindows +import java.nio.file.Files +import java.nio.file.Paths + +private val currentPath = Paths.get("") +private val llvmPath = Paths.get(currentPath.toString(), "llvm") + +fun updateLlvm() = if (isWindows) updateLlvmWindows() else updateLlvmNonWindows() + +private fun updateLlvmWindows() { + println(" Will be available after #1134") + /* + TODO finish this in #1134 + val llvmExe = Paths.get(llvmPath.toString(), "LLVM-win64.exe") + if (llvmExe.toFile().exists()) { + println("LLVM exists") + } else { + println("Downloading Windows LLVM...") + llvmPath.toFile().mkdirs() + downloadFile( + srcUrl = "https://releases.llvm.org/8.0.0/LLVM-8.0.0-win64.exe", + destinationPath = llvmExe.toString() + ) + } + + llvmExe.toFile().extract(llvmPath.toFile(), "zip", "xz") + findAndCopyLlvmLicense() + findAndCopyLlvmNmFile() + llvmPath.toFile().deleteRecursively()*/ +} + +private fun updateLlvmNonWindows() { + val llvmTarXz = Paths.get(llvmPath.toString(), "llvm.tar.xz") + + if (llvmTarXz.toFile().exists()) { + println("LLVM exists") + } else { + println("Downloading LLVM...") + llvmPath.toFile().mkdirs() + downloadFile( + srcUrl = "http://releases.llvm.org/8.0.0/clang+llvm-8.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz", + destinationPath = llvmTarXz.toString() + ) + } + + llvmTarXz.toFile().extract(llvmPath.toFile(), "tar", "xz") + findAndCopyLlvmLicense() + findAndCopyLlvmNmFile() + llvmPath.toFile().deleteRecursively() +} + +private fun findAndCopyLlvmLicense() { + val licensePathSuffix = Paths.get("include", "llvm", "Support", "LICENSE.TXT").toString() + val licenseOutputFile = Paths.get(currentPath.toString(), "llvm.txt").toFile() + + println("Copying license ...") + Files.walk(llvmPath) + .filter { it.toString().endsWith(licensePathSuffix) } + .findFirst() + .takeIf { it.isPresent } + ?.run { get().toFile().copyTo(licenseOutputFile, overwrite = true) } +} + +private fun findAndCopyLlvmNmFile() { + val llvmNmSuffix = Paths.get("bin", "llvm-nm").toString() + val llvmNmOutputFile = Paths.get(currentPath.toString(), "nm").toFile() + + println("Copying llvm nm ...") + Files.walk(llvmPath) + .filter { it.toString().endsWith(llvmNmSuffix) } + .findFirst() + .takeIf { it.isPresent } + ?.run { get().toFile().copyTo(llvmNmOutputFile, overwrite = true) } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateSwift.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateSwift.kt new file mode 100644 index 0000000000..fe109b2734 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/updatebinaries/UpdateSwift.kt @@ -0,0 +1,79 @@ +package flank.scripts.shell.updatebinaries + +import flank.scripts.utils.downloadFile +import flank.scripts.utils.extract +import flank.scripts.utils.isWindows +import java.nio.file.Files +import java.nio.file.Paths + +private val currentPath = Paths.get("") +private val swiftPath = Paths.get(currentPath.toString(), "swift") + +fun updateSwift() = if (isWindows) updateSwiftWindows() else updateSwiftOther() + +private fun updateSwiftWindows() { + println(" Will be available after #1134") + /* + TODO finish this in #1134 + val swiftExe = Paths.get(swiftPath.toString(), "swift.exe") + + if (swiftExe.toFile().exists()) { + println("Swift exists") + } else { + println("Downloading swift for Windows") + swiftPath.toFile().mkdirs() + downloadFile( + srcUrl = "https://swift.org/builds/swift-5.3-release/windows10/swift-5.3-RELEASE/swift-5.3-RELEASE-windows10.exe", + destinationPath = swiftExe.toString() + ) + } + + swiftExe.toFile().extract(swiftPath.toFile(), "zip", "gz") + findAndCopySwiftLicense() + findAndCopySwiftDemangleFile() + swiftPath.toFile().deleteRecursively()*/ +} + +private fun updateSwiftOther() { + val swiftTarGz = Paths.get(swiftPath.toString(), "swift.tar.gz") + + if (swiftTarGz.toFile().exists()) { + println("Swift exists") + } else { + println("Downloading swift") + swiftPath.toFile().mkdirs() + downloadFile( + srcUrl = "https://swift.org/builds/swift-5.0.1-release/ubuntu1604/swift-5.0.1-RELEASE/swift-5.0.1-RELEASE-ubuntu16.04.tar.gz", + destinationPath = swiftTarGz.toString() + ) + } + + swiftTarGz.toFile().extract(swiftPath.toFile(), "tar", "gz") + findAndCopySwiftLicense() + findAndCopySwiftDemangleFile() + swiftPath.toFile().deleteRecursively() +} + +private fun findAndCopySwiftLicense() { + val licenseFileSuffix = Paths.get("usr", "share", "swift", "LICENSE.txt").toString() + val licenseOutputFile = Paths.get(currentPath.toString(), "swift.txt").toFile() + + println("Copying license ...") + Files.walk(swiftPath) + .filter { it.toString().endsWith(licenseFileSuffix) } + .findFirst() + .takeIf { it.isPresent } + ?.run { get().toFile().copyTo(licenseOutputFile, overwrite = true) } +} + +private fun findAndCopySwiftDemangleFile() { + val switftDemangleFileSuffix = Paths.get("usr", "bin", "swift-demangle").toString() + val switftDemangleOutputFile = Paths.get(currentPath.toString(), "swift-demangle").toFile() + + println("Copying swift-demangle ...") + Files.walk(swiftPath) + .filter { it.toString().endsWith(switftDemangleFileSuffix) } + .findFirst() + .takeIf { it.isPresent } + ?.run { get().toFile().copyTo(switftDemangleOutputFile, overwrite = true) } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/FastFailForWindows.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/FastFailForWindows.kt new file mode 100644 index 0000000000..2aec5874d0 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/FastFailForWindows.kt @@ -0,0 +1,11 @@ +package flank.scripts.shell.utils + +import flank.scripts.utils.isWindows +import kotlin.system.exitProcess + +fun failIfWindows() { + if (isWindows) { + println("This script does not work on Windows") + exitProcess(1) + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/GradleCommand.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/GradleCommand.kt new file mode 100644 index 0000000000..c4021fe718 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/GradleCommand.kt @@ -0,0 +1,17 @@ +package flank.scripts.shell.utils + +import flank.scripts.utils.isWindows +import java.nio.file.Paths + +fun createGradleCommand( + workingDir: String, + vararg options: String +) = createGradleCommand(workingDir, options.asList()) + +fun createGradleCommand( + workingDir: String, + options: List<String> +) = "${Paths.get(workingDir, gradleExecutable)} ${options.joinToString(" ")}" + +private val gradleExecutable: String + get() = if (isWindows) "gradlew.bat" else "./gradlew" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/PathHelper.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/PathHelper.kt new file mode 100644 index 0000000000..3fe4748e95 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/PathHelper.kt @@ -0,0 +1,22 @@ +package flank.scripts.shell.utils + +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.Path + +val currentPath = Paths.get("") +val rootDirectoryPath = goToRoot(currentPath) +val rootDirectoryPathString = rootDirectoryPath.toString() + +fun goToRoot(startPath: Path): Path = + if (startPath.isRoot()) startPath.toAbsolutePath() else goToRoot(startPath.toAbsolutePath().parent) + +fun Path.isRoot() = Files.exists(Paths.get(toString(), "settings.gradle.kts")) + +val testProjectsPath = Paths.get(rootDirectoryPathString, "test_projects").toString() +val androidTestProjectsPath = Paths.get(testProjectsPath, "android").toString() +val iOSTestProjectsPath = Paths.get(testProjectsPath, "ios").toString() +val flankFixturesTmpPath = + Paths.get(rootDirectoryPathString, "test_runner", "src", "test", "kotlin", "ftl", "fixtures", "tmp").toString() +val flankFixturesIosTmpPath = + Paths.get(flankFixturesTmpPath, "ios").toString() diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/ShellHelper.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/ShellHelper.kt new file mode 100644 index 0000000000..f086d9c36a --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/ShellHelper.kt @@ -0,0 +1,12 @@ +package flank.scripts.shell.utils + +import flank.scripts.utils.isWindows +import flank.scripts.utils.runCommand + +infix fun String.pipe(command: String) { + if (isWindows) { + listOf("cmd", "/C", "$this | $command").runCommand() + } else { + listOf("/bin/bash", "-c", "$this | $command").runCommand() + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/TestFilters.kt b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/TestFilters.kt new file mode 100644 index 0000000000..7a18faeeea --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/shell/utils/TestFilters.kt @@ -0,0 +1,28 @@ +package flank.scripts.shell.utils + +private const val PACKAGE = "com.example.test_app" +private const val PACKAGE_FOO = "com.example.test_app.foo" +private const val PACKAGE_BAR = "com.example.test_app.bar" + +private const val ANNOTATION = "$PACKAGE.Annotation" + +private const val CLASS = "$PACKAGE.InstrumentedTest" +private const val CLASS_FOO = "$PACKAGE_FOO.FooInstrumentedTest" +private const val CLASS_BAR = "$PACKAGE_BAR.BarInstrumentedTest" + +private const val METHOD_1 = "$CLASS#test1" +private const val METHOD_FOO = "$CLASS_FOO#testFoo" + +private const val RUNNER = "com.example.test_app.test/androidx.test.runner.AndroidJUnitRunner" + +val runAllTestsShellCommand: String + get() = "adb shell am instrument -r -w \$@ $RUNNER" + +val filterPackageBarClassFooMethod1Command: String + get() = "$runAllTestsShellCommand -e package $PACKAGE_BAR -e class $CLASS_FOO -e class $METHOD_1" + +val filterAnnotationMethodFooCommand: String + get() = "$runAllTestsShellCommand -e annotation $ANNOTATION -e class $METHOD_FOO" + +val filterNotPackageFooNotClassBarCommand: String + get() = "$runAllTestsShellCommand -e notPackage $PACKAGE_FOO -e notClass $CLASS_BAR" diff --git a/flank-scripts/src/main/kotlin/flank/scripts/testartifacts/core/DownloadFixtures.kt b/flank-scripts/src/main/kotlin/flank/scripts/testartifacts/core/DownloadFixtures.kt index 1291654cf6..3186f43c75 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/testartifacts/core/DownloadFixtures.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/testartifacts/core/DownloadFixtures.kt @@ -2,7 +2,7 @@ package flank.scripts.testartifacts.core import com.jcabi.github.Release import flank.scripts.github.getRelease -import flank.scripts.utils.download +import flank.scripts.utils.downloadFile import java.io.File fun Context.downloadFixtures( @@ -28,7 +28,7 @@ private fun Context.downloadFixtures( ).fullName ).run { if (exists() && overwrite) delete() - if (!exists()) download(url, absolutePath).also { println("OK") } + if (!exists()) downloadFile(url, absolutePath).also { println("OK") } else println("ABORTED (already exists)") } } diff --git a/flank-scripts/src/main/kotlin/flank/scripts/utils/Archive.kt b/flank-scripts/src/main/kotlin/flank/scripts/utils/Archive.kt new file mode 100644 index 0000000000..ab71d3db41 --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/utils/Archive.kt @@ -0,0 +1,56 @@ +package flank.scripts.utils + +import org.rauschig.jarchivelib.ArchiveFormat +import org.rauschig.jarchivelib.ArchiverFactory +import org.rauschig.jarchivelib.CompressionType +import java.io.File + +fun File.extract( + destination: File, + archiveFormat: ArchiveFormat = ArchiveFormat.AR, + compressFormat: CompressionType? = null +) { + println("Unpacking...$name") + val archiver = if (compressFormat != null) { + ArchiverFactory.createArchiver(archiveFormat, compressFormat) + } else { + ArchiverFactory.createArchiver(archiveFormat) + } + runCatching { + archiver.extract(this, destination) + }.onFailure { + println("There was an error when unpacking $name - $it") + } +} + +fun File.extract( + destination: File, + archiveFormat: String, + compressFormat: String? = null +) { + println("Unpacking...$name") + val archiver = if (compressFormat != null) { + ArchiverFactory.createArchiver(archiveFormat, compressFormat) + } else { + ArchiverFactory.createArchiver(archiveFormat) + } + runCatching { + archiver.extract(this, destination) + }.onFailure { + println("There was an error when unpacking $name - $it") + } +} + +fun List<File>.archive( + destinationFileName: String, + destinationDirectory: File, + archiveFormat: ArchiveFormat = ArchiveFormat.ZIP +) { + println("Packing...$destinationFileName") + val archiver = ArchiverFactory.createArchiver(archiveFormat) + runCatching { + archiver.create(destinationFileName, destinationDirectory, *toTypedArray()) + }.onFailure { + println("There was an error when packing ${destinationDirectory.absolutePath}${File.separator}$destinationFileName - $it") + } +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/utils/Download.kt b/flank-scripts/src/main/kotlin/flank/scripts/utils/Download.kt index e21c666720..ddc4686644 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/utils/Download.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/utils/Download.kt @@ -3,11 +3,11 @@ package flank.scripts.utils import com.github.kittinunf.fuel.Fuel import java.io.File -fun download( +fun downloadFile( srcUrl: String, - dstFile: String + destinationPath: String ) { Fuel.download(srcUrl) - .fileDestination { _, _ -> File(dstFile) } + .fileDestination { _, _ -> File(destinationPath) } .responseString() } diff --git a/flank-scripts/src/main/kotlin/flank/scripts/utils/DownloadSoftware.kt b/flank-scripts/src/main/kotlin/flank/scripts/utils/DownloadSoftware.kt new file mode 100644 index 0000000000..ca67f9a98d --- /dev/null +++ b/flank-scripts/src/main/kotlin/flank/scripts/utils/DownloadSoftware.kt @@ -0,0 +1,30 @@ +package flank.scripts.utils + +import java.nio.file.Path + +fun downloadXcPrettyIfNeeded() { + "xcpretty".checkAndInstallIfNeed("gem install xcpretty") +} + +fun downloadCocoaPodsIfNeeded() { + "xcpretty".checkAndInstallIfNeed("gem install cocoapods -v 1.9.3") +} + +fun installPods(path: Path) { + "pod install --project-directory=$path --verbose".runCommand() +} + +fun checkIfPipInstalled() { + "pip".commandInstalledOr { + println("You need pip fot this script. To install it follow https://pip.pypa.io/en/stable/installing/") + } +} + +fun downloadSortJsonIfNeeded() { + "sort-json".checkAndInstallIfNeed("npm -g install sort-json") +} + +fun installClientGeneratorIfNeeded() { + val generateLibraryCheckCommand = (if (isWindows) "where " else "command -v ") + "generate_library" + generateLibraryCheckCommand.checkAndInstallIfNeed("pip install google-apis-client-generator") +} diff --git a/flank-scripts/src/main/kotlin/flank/scripts/utils/Env.kt b/flank-scripts/src/main/kotlin/flank/scripts/utils/Env.kt index f865ca96dc..68ec960e1d 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/utils/Env.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/utils/Env.kt @@ -2,3 +2,5 @@ package flank.scripts.utils fun getEnv(key: String): String = System.getenv(key) ?: throw RuntimeException("Environment variable '$key' not found!") + +val isWindows: Boolean = System.getProperty("os.name").startsWith("Windows") diff --git a/flank-scripts/src/main/kotlin/flank/scripts/utils/ShellExecute.kt b/flank-scripts/src/main/kotlin/flank/scripts/utils/ShellExecute.kt index b8e1403308..f9e6f953ef 100644 --- a/flank-scripts/src/main/kotlin/flank/scripts/utils/ShellExecute.kt +++ b/flank-scripts/src/main/kotlin/flank/scripts/utils/ShellExecute.kt @@ -2,8 +2,12 @@ package flank.scripts.utils import java.io.File -fun List<String>.runCommand(retryCount: Int = 0, fileForOutput: File? = null) = - ProcessBuilder(this).apply { +fun List<String>.runCommand( + retryCount: Int = 0, + fileForOutput: File? = null, + environmentVariables: Map<String, String> = mapOf() +) = ProcessBuilder(this).apply { + environment().putAll(environmentVariables) if (fileForOutput != null) { redirectOutput(fileForOutput) redirectError(fileForOutput) @@ -14,8 +18,11 @@ fun List<String>.runCommand(retryCount: Int = 0, fileForOutput: File? = null) = } .startWithRetry(retryCount) -fun String.runCommand(retryCount: Int = 0, fileForOutput: File? = null) = - split(" ").toList().runCommand(retryCount, fileForOutput) +fun String.runCommand( + retryCount: Int = 0, + fileForOutput: File? = null, + environmentVariables: Map<String, String> = mapOf() +) = split(" ").toList().runCommand(retryCount, fileForOutput, environmentVariables) fun String.runForOutput(retryCount: Int = 0): String = File .createTempFile(hashCode().toString(), "") @@ -24,6 +31,16 @@ fun String.runForOutput(retryCount: Int = 0): String = File file.readText() } +fun String.checkCommandExists() = (if (isWindows) "where " else "command -v ").plus(this).runCommand() == 0 + +fun String.checkAndInstallIfNeed(installCommand: String) = checkCommandExists().takeUnless { it }?.let { + installCommand.runCommand() +} + +fun String.commandInstalledOr(orAction: () -> Unit) = checkCommandExists().takeUnless { it }?.let { + orAction() +} + internal fun ProcessBuilder.startWithRetry(retryCount: Int): Int { var retryTries = 0 var processResponse: Int diff --git a/test_projects/android/build.bat b/test_projects/android/build.bat new file mode 100644 index 0000000000..15e97ba4d8 --- /dev/null +++ b/test_projects/android/build.bat @@ -0,0 +1,2 @@ +SET DIR=%~dp0 +%DIR%\..\..\flank-scripts\bash\flankScripts.bat shell ops android --copy --generate diff --git a/test_projects/android/ops.sh b/test_projects/android/ops.sh index 00636bef56..ce68785836 100755 --- a/test_projects/android/ops.sh +++ b/test_projects/android/ops.sh @@ -1,136 +1,26 @@ #!/usr/bin/env bash +DIR=`dirname "$BASH_SOURCE"` function base_app_apk() { - local dir=$TEST_PROJECTS_ANDROID - local outputDir="$FLANK_FIXTURES_TMP/apk" - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - "$dir/gradlew" -p "$dir" app:assemble - ;; - - '--copy' | '-c') - local apkIn="$dir/app/build/outputs/apk/singleSuccess/debug/app-single-success-debug.apk" - local apkOut="$outputDir/app-debug.apk" - - mkdir -p "$outputDir" - cp "$apkIn" "$apkOut" - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops android --copy --generate --artifacts=buildBaseApk } # depends on base_app_apk function base_test_apks() { - local dir=$TEST_PROJECTS_ANDROID - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - "$dir/gradlew" -p "$dir" app:assembleAndroidTest - ;; - - '--copy' | '-c') - local outputDir="$FLANK_FIXTURES_TMP/apk" - - mkdir -p "$outputDir" - cp "$dir"/app/build/outputs/apk/androidTest/**/debug/*.apk "$outputDir/" - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops android --copy --generate --artifacts=buildBaseTestApk } # depends on base_app_apk function duplicated_names_apks() { - local dir=$TEST_PROJECTS_ANDROID - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - "$dir/gradlew" -p "$dir" \ - dir0:testModule:assembleAndroidTest \ - dir1:testModule:assembleAndroidTest \ - dir2:testModule:assembleAndroidTest \ - dir3:testModule:assembleAndroidTest - ;; - - '--copy' | '-c') - local outputDir="$FLANK_FIXTURES_TMP/apk" - local testIn="$dir/app/build/outputs/apk/androidTest/**/debug/*.apk" - - mkdir -p "$outputDir" - local dir=$(dirname "${BASH_SOURCE[0]-$0}") - local outputDir="$FLANK_FIXTURES_TMP/apk/duplicated_names/" - - for index in 0 1 2 3; do - moduleName="dir$index" - apkDir="$outputDir/$moduleName/" - mkdir -p "$apkDir" - cp "$dir/$moduleName"/testModule/build/outputs/apk/**/debug/*.apk "$apkDir" - done - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops android --copy --generate --artifacts=buildDuplicatedNamesApks } function multi_module_apks() { - local dir=$TEST_PROJECTS_ANDROID - local outputDir="$FLANK_FIXTURES_TMP/apk/multi-modules/" - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - "$dir/gradlew" -p "$dir" \ - :multi-modules:multiapp:assemble \ - :multi-modules:testModule1:assembleAndroidTest \ - :multi-modules:testModule2:assembleAndroidTest \ - :multi-modules:testModule3:assembleAndroidTest \ - :multi-modules:testModule4:assembleAndroidTest \ - :multi-modules:testModule5:assembleAndroidTest \ - :multi-modules:testModule6:assembleAndroidTest \ - :multi-modules:testModule7:assembleAndroidTest \ - :multi-modules:testModule8:assembleAndroidTest \ - :multi-modules:testModule9:assembleAndroidTest \ - :multi-modules:testModule10:assembleAndroidTest \ - :multi-modules:testModule11:assembleAndroidTest \ - :multi-modules:testModule12:assembleAndroidTest \ - :multi-modules:testModule13:assembleAndroidTest \ - :multi-modules:testModule14:assembleAndroidTest \ - :multi-modules:testModule15:assembleAndroidTest \ - :multi-modules:testModule16:assembleAndroidTest \ - :multi-modules:testModule17:assembleAndroidTest \ - :multi-modules:testModule18:assembleAndroidTest \ - :multi-modules:testModule19:assembleAndroidTest \ - :multi-modules:testModule20:assembleAndroidTest - ;; - - '--copy' | '-c') - mkdir -p "$outputDir" - find "$dir/multi-modules" -type f -name "*.apk" -exec cp {} "$outputDir" \; - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops android --copy --generate --artifacts=buildMultiModulesApks } function cucumber_sample_app() { - local dir=$TEST_PROJECTS_ANDROID - local outputDir="$FLANK_FIXTURES_TMP/apk/cucumber_sample_app/" - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - "$dir/gradlew" -p "$dir" \ - :cucumber_sample_app:cukeulator:assemble \ - :cucumber_sample_app:cukeulator:assembleAndroidTest - ;; - - '--copy' | '-c') - mkdir -p "$outputDir" - find "$dir/cucumber_sample_app" -type f -name "*.apk" -exec cp {} "$outputDir" \; - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops android --copy --generate --artifacts=buildCucumberSampleApp } echo "Android test projects ops loaded" diff --git a/test_projects/gohello/build.bat b/test_projects/gohello/build.bat new file mode 100644 index 0000000000..2955f93675 --- /dev/null +++ b/test_projects/gohello/build.bat @@ -0,0 +1,2 @@ +SET DIR=%~dp0 +%DIR%\..\..\flank-scripts\bash\flankScripts.bat shell ops go diff --git a/test_projects/gohello/build.sh b/test_projects/gohello/build.sh index a7e383a006..33510f858f 100755 --- a/test_projects/gohello/build.sh +++ b/test_projects/gohello/build.sh @@ -1,10 +1,2 @@ -#!/bin/bash - -rm -rf "./bin/" -mkdir -p bin/win -mkdir -p bin/linux -mkdir -p bin/mac - -GOOS=windows GOARCH=amd64 go build -o ./bin/win/gohello.exe -GOOS=linux GOARCH=amd64 go build -o ./bin/linux/gohello -GOOS=darwin GOARCH=amd64 go build -o ./bin/mac/gohello +DIR=`dirname "$BASH_SOURCE"` +$DIR/../../flank-scripts/bash/flankScripts shell ops go diff --git a/test_projects/ios/EarlGreyExample/build_example.sh b/test_projects/ios/EarlGreyExample/build_example.sh index df03cd26e0..54ce63a89e 100755 --- a/test_projects/ios/EarlGreyExample/build_example.sh +++ b/test_projects/ios/EarlGreyExample/build_example.sh @@ -1,33 +1,2 @@ -#!/bin/bash - -set -euxo pipefail - -if ! [ -x "$(command -v xcpretty)" ]; then - gem install xcpretty -fi - -DD="dd_tmp" -ZIP="earlgrey_example.zip" - -rm -rf "$DD" - -xcodebuild build-for-testing \ - -allowProvisioningUpdates \ - -workspace ./EarlGreyExample.xcworkspace \ - -scheme "EarlGreyExampleSwiftTests" \ - -derivedDataPath "$DD" \ - -sdk iphoneos \ - | xcpretty - -xcodebuild build-for-testing \ - -allowProvisioningUpdates \ - -workspace ./EarlGreyExample.xcworkspace \ - -scheme "EarlGreyExampleTests" \ - -derivedDataPath "$DD" \ - -sdk iphoneos \ - | xcpretty - -pushd "$DD/Build/Products" -zip -r "$ZIP" *-iphoneos *.xctestrun -popd -mv "$DD/Build/Products/$ZIP" . +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell iosBuildExample diff --git a/test_projects/ios/EarlGreyExample/build_ftl.sh b/test_projects/ios/EarlGreyExample/build_ftl.sh index 36c2dd7a32..f2cdb7f3c3 100755 --- a/test_projects/ios/EarlGreyExample/build_ftl.sh +++ b/test_projects/ios/EarlGreyExample/build_ftl.sh @@ -1,26 +1,2 @@ -#!/bin/bash - -set -euxo pipefail - -if ! [ -x "$(command -v xcpretty)" ]; then - gem install xcpretty -fi - -DD="dd_tmp" -SCHEME="EarlGreyExampleSwiftTests" -ZIP="ios_earlgrey2.zip" - -rm -rf "$DD" - -xcodebuild build-for-testing \ - -allowProvisioningUpdates \ - -workspace ./EarlGreyExample.xcworkspace \ - -scheme "$SCHEME" \ - -derivedDataPath "$DD" \ - -sdk iphoneos \ - | xcpretty - -pushd "$DD/Build/Products" -zip -r "$ZIP" *-iphoneos *.xctestrun -popd -mv "$DD/Build/Products/$ZIP" . +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell iosBuildFtl diff --git a/test_projects/ios/EarlGreyExample/ops.sh b/test_projects/ios/EarlGreyExample/ops.sh index 9ea3d2d552..77f108e8bf 100644 --- a/test_projects/ios/EarlGreyExample/ops.sh +++ b/test_projects/ios/EarlGreyExample/ops.sh @@ -1,78 +1,19 @@ #!/usr/bin/env bash -EARL_GREY_EXAMPLE="$TEST_PROJECTS_IOS/EarlGreyExample" - function setup_ios_env() { - if ! [ -x "$(command -v xcpretty)" ]; then - gem install cocoapods -v 1.9.3 - fi - (cd "$EARL_GREY_EXAMPLE" && pod install) + $DIR/../../flank-scripts/bash/flankScripts shell setup_ios_env } function install_xcpretty() { - if ! [ -x "$(command -v xcpretty)" ]; then - gem install xcpretty - fi + $DIR/../../flank-scripts/bash/flankScripts shell install_xcpretty } function universal_framework() { - "$EARL_GREY_EXAMPLE/universal_framework.sh" + $DIR/../../flank-scripts/bash/flankScripts shell iosUniversalFramework } function earl_grey_example() { - local dir=$EARL_GREY_EXAMPLE - local buildDir="$dir/build" - - for arg in "$@"; do case "$arg" in - - '--generate' | '-g') - - install_xcpretty - - rm -rf "$buildDir" - - xcodebuild build-for-testing \ - -allowProvisioningUpdates \ - -workspace "$dir/EarlGreyExample.xcworkspace" \ - -scheme "EarlGreyExampleSwiftTests" \ - -derivedDataPath "$buildDir" \ - -sdk iphoneos | - xcpretty - - xcodebuild build-for-testing \ - -allowProvisioningUpdates \ - -workspace "$dir/EarlGreyExample.xcworkspace" \ - -scheme "EarlGreyExampleTests" \ - -derivedDataPath "$buildDir" \ - -sdk iphoneos | - xcpretty - ;; - - '--copy' | '-c') - - local productsDir="$dir/build/Build/Products" - - # xcodebuild generates .xctestrun files names in format: PROJECTNAME_platform_version_architecture.xctestrun, code below removes "_platform_version_architecture" part - mv -f "$productsDir/EarlGreyExampleSwiftTests"*.xctestrun "$productsDir/EarlGreyExampleSwiftTests.xctestrun" - mv -f "$productsDir/EarlGreyExampleTests"*.xctestrun "$productsDir/EarlGreyExampleTests.xctestrun" - - mkdir -p "$FLANK_FIXTURES_TMP/ios/earl_grey_example/objc/" - mkdir -p "$FLANK_FIXTURES_TMP/ios/earl_grey_example/swift/" - - cp -Rf "$productsDir"/*-iphoneos "$FLANK_FIXTURES_TMP/ios/earl_grey_example/" - - cp "$productsDir"/*.xctestrun "$FLANK_FIXTURES_TMP/ios/earl_grey_example/" - - cp \ - "$productsDir/Debug-iphoneos/EarlGreyExampleSwift.app/PlugIns/EarlGreyExampleTests.xctest/EarlGreyExampleTests" \ - "$FLANK_FIXTURES_TMP/ios/earl_grey_example/objc/" - - cp \ - "$productsDir/Debug-iphoneos/EarlGreyExampleSwift.app/PlugIns/EarlGreyExampleSwiftTests.xctest/EarlGreyExampleSwiftTests" \ - "$FLANK_FIXTURES_TMP/ios/earl_grey_example/swift/" - ;; - - esac done + $DIR/../../flank-scripts/bash/flankScripts shell ops ios --copy --generate } echo "iOS EarlGreyExample test projects ops loaded" diff --git a/test_projects/ios/EarlGreyExample/run_ftl_local.sh b/test_projects/ios/EarlGreyExample/run_ftl_local.sh index 600117bfb9..c81d9a56e5 100755 --- a/test_projects/ios/EarlGreyExample/run_ftl_local.sh +++ b/test_projects/ios/EarlGreyExample/run_ftl_local.sh @@ -1,15 +1,2 @@ -#!/bin/bash - -set -euxo pipefail - -DD="dd_tmp" -SCHEME="appUITests" -ZIP="ios_earlgrey2.zip" - -# Firebase test lab runs using -xctestrun -xcodebuild test-without-building \ - -xctestrun $DD/Build/Products/*.xctestrun \ - -derivedDataPath "$DD" \ - -destination 'id=ADD_YOUR_ID_HERE' - -# get device identifier in Xcode -> Window -> Devices and Simulators +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell iosRunFtlLocal diff --git a/test_projects/ios/EarlGreyExample/universal_framework.sh b/test_projects/ios/EarlGreyExample/universal_framework.sh index f703753799..cec365d10a 100755 --- a/test_projects/ios/EarlGreyExample/universal_framework.sh +++ b/test_projects/ios/EarlGreyExample/universal_framework.sh @@ -1,26 +1,2 @@ -#!/bin/bash - -set -euxo pipefail - -COMBO="./ios-frameworks" -DEVICE="$COMBO/Debug-iphoneos" -SIM="$COMBO/Debug-iphonesimulator" - -lipo -create $DEVICE/libChannelLib.a $SIM/libChannelLib.a -output $COMBO/libChannelLib.a -lipo -create $DEVICE/libCommonLib.a $SIM/libCommonLib.a -output $COMBO/libCommonLib.a -lipo -create $DEVICE/libeDistantObject.a $SIM/libeDistantObject.a -output $COMBO/libeDistantObject.a -lipo -create $DEVICE/libTestLib.a $SIM/libTestLib.a -output $COMBO/libTestLib.a -lipo -create $DEVICE/libUILib.a $SIM/libUILib.a -output $COMBO/libUILib.a - -cp -RL $DEVICE/AppFramework.framework $COMBO/AppFramework.framework -DEVICE_FRAMEWORK="$DEVICE/AppFramework.framework/AppFramework" -SIM_FRAMEWORK="$SIM/AppFramework.framework/AppFramework" -UNI_FRAMEWORK="$COMBO/AppFramework.framework/AppFramework" - -lipo -create \ - "$DEVICE_FRAMEWORK" \ - "$SIM_FRAMEWORK" \ - -output "$UNI_FRAMEWORK" - -dsymutil "$UNI_FRAMEWORK" \ - --out "$COMBO/AppFramework.framework.dSYM" +DIR=`dirname "$BASH_SOURCE"` +$DIR/../flank-scripts/bash/flankScripts shell iosUniversalFramework diff --git a/test_projects/ops.bat b/test_projects/ops.bat new file mode 100644 index 0000000000..f1c35124ac --- /dev/null +++ b/test_projects/ops.bat @@ -0,0 +1,18 @@ +@ECHO OFF +SET ARG=%1 +if "%ARG%" == "android" ( + CALL ../flank-scripts/bash/flankScripts.bat shell ops android --copy --generate +) + +if "%ARG%" == "ios" ( + echo iOS Build on windows not supported +) + +if "%ARG%" == "go" ( + CALL ../flank-scripts/bash/flankScripts.bat shell ops go +) + +if "%ARG%" == "all" ( + CALL ../flank-scripts/bash/flankScripts.bat shell ops android --copy --generate + CALL ../flank-scripts/bash/flankScripts.bat shell ops go +) diff --git a/test_projects/ops.sh b/test_projects/ops.sh index 9a1e7914e0..f9ab388618 100755 --- a/test_projects/ops.sh +++ b/test_projects/ops.sh @@ -1,33 +1,27 @@ #!/usr/bin/env bash -TEST_PROJECTS_ANDROID="$TEST_PROJECTS/android" -TEST_PROJECTS_IOS="$TEST_PROJECTS/ios" - -. "$TEST_PROJECTS_ANDROID/ops.sh" -. "$TEST_PROJECTS_IOS/EarlGreyExample/ops.sh" -. "$TEST_PROJECTS_IOS/FlankExample/ops.sh" +DIR=`dirname "$BASH_SOURCE"` function update_test_artifacts() { for arg in "$@"; do case "$arg" in android) - base_app_apk --generate --copy - base_test_apks --generate --copy + $DIR/../flank-scripts/bash/flankScripts shell ops android --copy --generate ;; ios) - setup_ios_env - earl_grey_example --generate --copy - flank_ios_example --generate --copy + $DIR/../flank-scripts/bash/flankScripts shell ops ios --copy --generate ;; go) - cp -R "$FLANK_ROOT/test_projects/gohello" "$FLANK_FIXTURES_TMP/" + $DIR/../flank-scripts/bash/flankScripts shell ops go --copy --generate ;; all) - update_test_artifacts android ios go + $DIR/../flank-scripts/bash/flankScripts shell ops android --copy --generate + $DIR/../flank-scripts/bash/flankScripts shell ops ios --copy --generate + $DIR/../flank-scripts/bash/flankScripts shell ops go --copy --generate ;; esac done diff --git a/test_runner/bash/update_flank.bat b/test_runner/bash/update_flank.bat index 6f4d73a8c5..6a5e4d0489 100644 --- a/test_runner/bash/update_flank.bat +++ b/test_runner/bash/update_flank.bat @@ -1,7 +1,2 @@ - -for %%F in (%filename%) do set dirname=%%~dpF -echo "%dirname%" -cd .. -cd .. -call gradlew.bat clean assemble shadowjar -cd test_runner\bash +SET DIR=%~dp0 +%DIR%\..\..\flank-scripts\bash\flankScripts.bat shell buildFlank diff --git a/test_runner/bash/update_flank.sh b/test_runner/bash/update_flank.sh index 471ab34ed6..9090fbd33c 100755 --- a/test_runner/bash/update_flank.sh +++ b/test_runner/bash/update_flank.sh @@ -1,9 +1,3 @@ -#!/usr/bin/env bash DIR=`dirname "$BASH_SOURCE"` - -FLANK="$DIR/.." - -"$FLANK/../gradlew" -p "$FLANK" clean assemble shadowJar - -cp "$FLANK"/build/libs/flank.jar "$DIR/flank.jar" +$DIR/../../flank-scripts/bash/flankScripts shell buildFlank From 22da7bf441ae880d3a6901f5d2bfad49699dc81b Mon Sep 17 00:00:00 2001 From: adamfilipow92 <64852261+adamfilipow92@users.noreply.github.com> Date: Mon, 2 Nov 2020 14:17:40 +0100 Subject: [PATCH 3/3] feat: iOS add directories-to-pull (#1266) * Moved directories to pull to CommonArgs, added tests * Fix tests * Udpate docs * Update GcIosTestMatrixTest.kt * fix build issues after conflicting rebase * Fix pull directory from ios * update view controller for flank example * Update IArgs.kt * Update AndroidGcloudConfig.kt Co-authored-by: Piotr Adamczyk <piotr.adamczyk@gogoapps.io> Co-authored-by: piotradamczyk5 <65554637+piotradamczyk5@users.noreply.github.com> --- docs/index.md | 6 ++++ .../EarlGreyExample.xcodeproj/project.pbxproj | 12 ++++---- .../EarlGreyExampleSwift/ViewController.swift | 20 ++++++++++++- .../FlankExample/ViewController.swift | 19 ++++++++++++ test_runner/flank.ios.yml | 6 ++++ .../src/main/kotlin/ftl/args/AndroidArgs.kt | 1 - .../src/main/kotlin/ftl/args/CommonArgs.kt | 1 + .../main/kotlin/ftl/args/CreateAndroidArgs.kt | 1 - .../main/kotlin/ftl/args/CreateCommonArgs.kt | 1 + test_runner/src/main/kotlin/ftl/args/IArgs.kt | 1 + .../src/main/kotlin/ftl/args/IosArgs.kt | 1 + .../ftl/config/android/AndroidGcloudConfig.kt | 14 --------- .../ftl/config/common/CommonGcloudConfig.kt | 16 ++++++++++ .../src/main/kotlin/ftl/gc/GcIosTestMatrix.kt | 4 +++ .../src/main/kotlin/ftl/gc/android/Utils.kt | 23 +++++++------- .../src/test/kotlin/ftl/args/IosArgsTest.kt | 2 ++ .../test/android/AndroidRunCommandTest.kt | 4 +-- .../firebase/test/ios/IosRunCommandTest.kt | 1 + .../test/kotlin/ftl/gc/GcIosTestMatrixTest.kt | 30 ++++++++++++++++++- 19 files changed, 125 insertions(+), 38 deletions(-) diff --git a/docs/index.md b/docs/index.md index 4366640083..2fef138aba 100644 --- a/docs/index.md +++ b/docs/index.md @@ -145,6 +145,12 @@ gcloud: # version: 12.0 # locale: es_ES # orientation: landscape + + ## A list of paths that will be copied from the device's storage to the designated results bucket after the test + ## is complete. These must be absolute paths under /private/var/mobile/Media or /Documents + ## of the app under test. If the path is under an app's /Documents, it must be prefixed with the app's bundle id and a colon + # directories-to-pull: + # - /private/var/mobile/Media ## A list of device-path=file-path pairs that specify the paths of the test device and the files you want pushed to the device prior to testing. ## Device paths should either be under the Media shared folder (e.g. prefixed with /private/var/mobile/Media) or diff --git a/test_projects/ios/EarlGreyExample/EarlGreyExample.xcodeproj/project.pbxproj b/test_projects/ios/EarlGreyExample/EarlGreyExample.xcodeproj/project.pbxproj index 07123bd199..f43ad0c808 100644 --- a/test_projects/ios/EarlGreyExample/EarlGreyExample.xcodeproj/project.pbxproj +++ b/test_projects/ios/EarlGreyExample/EarlGreyExample.xcodeproj/project.pbxproj @@ -283,18 +283,18 @@ TargetAttributes = { 2CB7314C1C29E54A00CF35C1 = { CreatedOnToolsVersion = 7.2; - DevelopmentTeam = AD2V26JBWL; + DevelopmentTeam = L2UF9MLSM6; LastSwiftMigration = 0800; TestTargetID = 5F5A53781ADE67D500F81DF0; }; 5F5A53781ADE67D500F81DF0 = { CreatedOnToolsVersion = 6.3; - DevelopmentTeam = AD2V26JBWL; + DevelopmentTeam = L2UF9MLSM6; LastSwiftMigration = 0800; }; 5FDE05571B0DAA090037B82F = { CreatedOnToolsVersion = 6.3.2; - DevelopmentTeam = AD2V26JBWL; + DevelopmentTeam = L2UF9MLSM6; TestTargetID = 5F5A53781ADE67D500F81DF0; }; }; @@ -485,7 +485,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = AD2V26JBWL; + DEVELOPMENT_TEAM = L2UF9MLSM6; ENABLE_TESTABILITY = YES; INFOPLIST_FILE = EarlGreyExampleSwiftTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 9.0; @@ -619,7 +619,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = AD2V26JBWL; + DEVELOPMENT_TEAM = L2UF9MLSM6; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; INFOPLIST_FILE = "$(SRCROOT)/EarlGreyExample/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; @@ -666,7 +666,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = NO; - DEVELOPMENT_TEAM = AD2V26JBWL; + DEVELOPMENT_TEAM = L2UF9MLSM6; EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( diff --git a/test_projects/ios/EarlGreyExample/EarlGreyExampleSwift/ViewController.swift b/test_projects/ios/EarlGreyExample/EarlGreyExampleSwift/ViewController.swift index 1c7e517b7a..51da069885 100644 --- a/test_projects/ios/EarlGreyExample/EarlGreyExampleSwift/ViewController.swift +++ b/test_projects/ios/EarlGreyExample/EarlGreyExampleSwift/ViewController.swift @@ -20,9 +20,27 @@ class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSour var tableItems = (1...50).map { $0 } + func getDocumentsDirectory() -> URL { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return paths[0] + } + override func viewDidLoad() { super.viewDidLoad() - + + let str = "viewLoaded" + let outputDirectory = getDocumentsDirectory().appendingPathComponent("output") + + do { + try FileManager.default.createDirectory(atPath: outputDirectory.path, withIntermediateDirectories: true, attributes: nil) + + let filename = outputDirectory.appendingPathComponent("test.txt") + + try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8) + + } catch { + print("error on create test file") + } // Create the send message view to contain one of the two send buttons let sendMessageView = SendMessageView(frame: CGRect(x: 0, y: 0, width: 200, height: 200)) sendMessageView.translatesAutoresizingMaskIntoConstraints = false diff --git a/test_projects/ios/FlankExample/FlankExample/ViewController.swift b/test_projects/ios/FlankExample/FlankExample/ViewController.swift index 46087b0f6b..c2e96cf14a 100644 --- a/test_projects/ios/FlankExample/FlankExample/ViewController.swift +++ b/test_projects/ios/FlankExample/FlankExample/ViewController.swift @@ -10,9 +10,28 @@ import UIKit class ViewController: UIViewController { + func getDocumentsDirectory() -> URL { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return paths[0] + } + override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. + + + let str = "viewLoaded" + let outputDirectory = getDocumentsDirectory().appendingPathComponent("output") + + do { + try FileManager.default.createDirectory(atPath: outputDirectory.path, withIntermediateDirectories: true, attributes: nil) + let filename = outputDirectory.appendingPathComponent("test.txt") + + try str.write(to: filename, atomically: true, encoding: String.Encoding.utf8) + + } catch { + print("error on create test file") + } } } diff --git a/test_runner/flank.ios.yml b/test_runner/flank.ios.yml index 5c50560f56..9b760a40bc 100644 --- a/test_runner/flank.ios.yml +++ b/test_runner/flank.ios.yml @@ -77,6 +77,12 @@ gcloud: # locale: es_ES # orientation: landscape + ## A list of paths that will be copied from the device's storage to the designated results bucket after the test + ## is complete. These must be absolute paths under /private/var/mobile/Media or /Documents + ## of the app under test. If the path is under an app's /Documents, it must be prefixed with the app's bundle id and a colon + # directories-to-pull: + # - /private/var/mobile/Media + ## A list of device-path=file-path pairs that specify the paths of the test device and the files you want pushed to the device prior to testing. ## Device paths should either be under the Media shared folder (e.g. prefixed with /private/var/mobile/Media) or ## within the documents directory of the filesystem of an app under test (e.g. /Documents). Device paths to app diff --git a/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt b/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt index 42c76f6c70..7a0f60eda4 100644 --- a/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt @@ -13,7 +13,6 @@ data class AndroidArgs( val roboDirectives: List<FlankRoboDirective>, val roboScript: String?, val environmentVariables: Map<String, String>, // should not be printed, becuase could contains sensitive informations - val directoriesToPull: List<String>, val grantPermissions: String?, val scenarioLabels: List<String>, val obbFiles: List<String>, diff --git a/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt b/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt index 7cdb512aa4..ee87d6b555 100644 --- a/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CommonArgs.kt @@ -20,6 +20,7 @@ data class CommonArgs( override val networkProfile: String?, override val otherFiles: Map<String, String>, override val type: Type?, + override val directoriesToPull: List<String>, override val scenarioNumbers: List<String>, // flank diff --git a/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt b/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt index c8520a936c..cd5b4b4b8b 100644 --- a/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CreateAndroidArgs.kt @@ -23,7 +23,6 @@ fun createAndroidArgs( performanceMetrics = gcloud.performanceMetrics!!, numUniformShards = gcloud.numUniformShards, environmentVariables = gcloud.environmentVariables!!, - directoriesToPull = gcloud.directoriesToPull!!, autoGoogleLogin = gcloud.autoGoogleLogin!!, additionalApks = gcloud.additionalApks!!.map { it.normalizeFilePath() }, roboScript = gcloud.roboScript?.normalizeFilePath(), diff --git a/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt b/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt index 41c0dcb3c1..2393846831 100644 --- a/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/CreateCommonArgs.kt @@ -25,6 +25,7 @@ fun CommonConfig.createCommonArgs( flakyTestAttempts = gcloud.flakyTestAttempts!!, networkProfile = gcloud.networkProfile, clientDetails = gcloud.clientDetails, + directoriesToPull = gcloud.directoriesToPull!!, otherFiles = gcloud.otherFiles!!.mapValues { (_, path) -> path.normalizeFilePath() }, scenarioNumbers = gcloud.scenarioNumbers!!, type = gcloud.type?.toType(), diff --git a/test_runner/src/main/kotlin/ftl/args/IArgs.kt b/test_runner/src/main/kotlin/ftl/args/IArgs.kt index a046d1bc21..da03e39e2c 100644 --- a/test_runner/src/main/kotlin/ftl/args/IArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/IArgs.kt @@ -26,6 +26,7 @@ interface IArgs { val otherFiles: Map<String, String> val scenarioNumbers: List<String> val type: Type? get() = null + val directoriesToPull: List<String> // FlankYml val maxTestShards: Int diff --git a/test_runner/src/main/kotlin/ftl/args/IosArgs.kt b/test_runner/src/main/kotlin/ftl/args/IosArgs.kt index 017dee948f..afc09e2bce 100644 --- a/test_runner/src/main/kotlin/ftl/args/IosArgs.kt +++ b/test_runner/src/main/kotlin/ftl/args/IosArgs.kt @@ -40,6 +40,7 @@ IosArgs xcode-version: $xcodeVersion device:${ArgsToString.objectsToString(devices)} num-flaky-test-attempts: $flakyTestAttempts + directories-to-pull: ${ArgsToString.listToString(directoriesToPull)} other-files: ${ArgsToString.mapToString(otherFiles)} additional-ipas: ${ArgsToString.listToString(additionalIpas)} scenario-numbers: ${ArgsToString.listToString(scenarioNumbers)} diff --git a/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt b/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt index 11c6e32da5..6de48aad1f 100644 --- a/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt +++ b/test_runner/src/main/kotlin/ftl/config/android/AndroidGcloudConfig.kt @@ -102,19 +102,6 @@ data class AndroidGcloudConfig @JsonIgnore constructor( @set:JsonProperty("grant-permissions") var grantPermissions: String? by data - @set:CommandLine.Option( - names = ["--directories-to-pull"], - split = ",", - description = ["A list of paths that will be copied from the device's " + - "storage to the designated results bucket after the test is complete. These must be absolute paths under " + - "/sdcard or /data/local/tmp (for example, --directories-to-pull /sdcard/tempDir1,/data/local/tmp/tempDir2). " + - "Path names are restricted to the characters a-zA-Z0-9_-./+. The paths /sdcard and /data will be made available " + - "and treated as implicit path substitutions. E.g. if /sdcard on a particular device does not map to external " + - "storage, the system will replace it with the external storage path prefix for that device."] - ) - @set:JsonProperty("directories-to-pull") - var directoriesToPull: List<String>? by data - @set:CommandLine.Option( names = ["--scenario-labels"], split = ",", @@ -239,7 +226,6 @@ data class AndroidGcloudConfig @JsonIgnore constructor( useOrchestrator = true environmentVariables = emptyMap() grantPermissions = FlankDefaults.GRANT_PERMISSIONS_ALL - directoriesToPull = emptyList() scenarioLabels = emptyList() obbfiles = emptyList() obbnames = emptyList() diff --git a/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt b/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt index 341b8d4a03..acce84442e 100644 --- a/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt +++ b/test_runner/src/main/kotlin/ftl/config/common/CommonGcloudConfig.kt @@ -120,6 +120,21 @@ data class CommonGcloudConfig @JsonIgnore constructor( @set:JsonProperty("num-flaky-test-attempts") var flakyTestAttempts: Int? by data + @set:CommandLine.Option( + names = ["--directories-to-pull"], + split = ",", + description = ["A list of paths that will be copied from the device's " + + "storage to the designated results bucket after the test is complete. For Android devices these must be absolute paths under " + + "/sdcard or /data/local/tmp (for example, --directories-to-pull /sdcard/tempDir1,/data/local/tmp/tempDir2). " + + "Path names are restricted to the characters a-zA-Z0-9_-./+. The paths /sdcard and /data will be made available " + + "and treated as implicit path substitutions. E.g. if /sdcard on a particular device does not map to external " + + "storage, the system will replace it with the external storage path prefix for that device. " + + "For iOS devices these must be absolute paths under /private/var/mobile/Media or /Documents " + + "of the app under test. If the path is under an app's /Documents, it must be prefixed with the app's bundle id and a colon"] + ) + @set:JsonProperty("directories-to-pull") + var directoriesToPull: List<String>? by data + @set:CommandLine.Option( names = ["--other-files"], split = ",", @@ -170,6 +185,7 @@ data class CommonGcloudConfig @JsonIgnore constructor( clientDetails = null networkProfile = null devices = listOf(defaultDevice(android)) + directoriesToPull = emptyList() otherFiles = emptyMap() type = null scenarioNumbers = emptyList() diff --git a/test_runner/src/main/kotlin/ftl/gc/GcIosTestMatrix.kt b/test_runner/src/main/kotlin/ftl/gc/GcIosTestMatrix.kt index 0ae83f4a52..c2a571aa2e 100644 --- a/test_runner/src/main/kotlin/ftl/gc/GcIosTestMatrix.kt +++ b/test_runner/src/main/kotlin/ftl/gc/GcIosTestMatrix.kt @@ -16,6 +16,7 @@ import com.google.api.services.testing.model.ToolResultsHistory import ftl.args.IosArgs import ftl.gc.android.mapGcsPathsToFileReference import ftl.gc.android.mapToIosDeviceFiles +import ftl.gc.android.toIosDeviceFile import ftl.ios.Xctestrun import ftl.ios.Xctestrun.toByteArray import ftl.run.exception.FlankGeneralError @@ -69,6 +70,7 @@ object GcIosTestMatrix { .setNetworkProfile(args.networkProfile) .setPushFiles(otherFiles.mapToIosDeviceFiles()) .setAdditionalIpas(additionalIpasGcsPaths.mapGcsPathsToFileReference()) + .setPullDirectories(args.directoriesToPull.toIosDeviceFiles()) val testTimeoutSeconds = timeoutToSeconds(args.testTimeout) @@ -98,3 +100,5 @@ object GcIosTestMatrix { } } } + +private fun List<String>.toIosDeviceFiles() = map { path -> toIosDeviceFile(path) } diff --git a/test_runner/src/main/kotlin/ftl/gc/android/Utils.kt b/test_runner/src/main/kotlin/ftl/gc/android/Utils.kt index 827d3f549f..61e4e59de2 100644 --- a/test_runner/src/main/kotlin/ftl/gc/android/Utils.kt +++ b/test_runner/src/main/kotlin/ftl/gc/android/Utils.kt @@ -32,16 +32,15 @@ internal fun Map<String, String>.mapToDeviceObbFiles(obbnames: List<String>): Li } } -internal fun Map<String, String>.mapToIosDeviceFiles(): List<IosDeviceFile> = - map { (testDevicePath, gcsFilePath) -> - IosDeviceFile().apply { - if (testDevicePath.contains(":")) { - val (bundleIdSeparated, pathSeparated) = testDevicePath.split(":") - bundleId = bundleIdSeparated - devicePath = pathSeparated - } else { - devicePath = testDevicePath - } - content = FileReference().setGcsPath(gcsFilePath) - } +internal fun Map<String, String>.mapToIosDeviceFiles(): List<IosDeviceFile> = map { (testDevicePath, gcsFilePath) -> toIosDeviceFile(testDevicePath, gcsFilePath) } + +internal fun toIosDeviceFile(testDevicePath: String, gcsFilePath: String = "") = IosDeviceFile().apply { + if (testDevicePath.contains(":")) { + val (bundleIdSeparated, pathSeparated) = testDevicePath.split(":") + bundleId = bundleIdSeparated + devicePath = pathSeparated + } else { + devicePath = testDevicePath } + content = FileReference().setGcsPath(gcsFilePath) +} diff --git a/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt b/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt index e9460a3ec3..641b97aa81 100644 --- a/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt +++ b/test_runner/src/test/kotlin/ftl/args/IosArgsTest.kt @@ -235,6 +235,7 @@ IosArgs locale: c orientation: default num-flaky-test-attempts: 4 + directories-to-pull: other-files: com.my.app:/Documents/file.txt: local/file.txt /private/var/mobile/Media/file.jpg: gs://bucket/file.jpg @@ -300,6 +301,7 @@ IosArgs locale: en orientation: portrait num-flaky-test-attempts: 0 + directories-to-pull: other-files: additional-ipas: scenario-numbers: diff --git a/test_runner/src/test/kotlin/ftl/cli/firebase/test/android/AndroidRunCommandTest.kt b/test_runner/src/test/kotlin/ftl/cli/firebase/test/android/AndroidRunCommandTest.kt index a23f0b56d1..14e23f7405 100644 --- a/test_runner/src/test/kotlin/ftl/cli/firebase/test/android/AndroidRunCommandTest.kt +++ b/test_runner/src/test/kotlin/ftl/cli/firebase/test/android/AndroidRunCommandTest.kt @@ -80,7 +80,6 @@ class AndroidRunCommandTest { assertThat(cmd.config.platform.gcloud.numUniformShards).isNull() assertThat(cmd.config.platform.gcloud.testRunnerClass).isNull() assertThat(cmd.config.platform.gcloud.environmentVariables).isNull() - assertThat(cmd.config.platform.gcloud.directoriesToPull).isNull() assertThat(cmd.config.common.gcloud.otherFiles).isNull() assertThat(cmd.config.common.gcloud.devices).isNull() assertThat(cmd.config.common.gcloud.resultsBucket).isNull() @@ -98,6 +97,7 @@ class AndroidRunCommandTest { assertThat(cmd.config.common.flank.filesToDownload).isNull() assertThat(cmd.config.common.gcloud.resultsDir).isNull() assertThat(cmd.config.common.gcloud.flakyTestAttempts).isNull() + assertThat(cmd.config.common.gcloud.directoriesToPull).isNull() assertThat(cmd.config.common.flank.disableSharding).isNull() assertThat(cmd.config.common.flank.localResultsDir).isNull() assertThat(cmd.config.common.flank.smartFlankDisableUpload).isNull() @@ -219,7 +219,7 @@ class AndroidRunCommandTest { val cmd = AndroidRunCommand() CommandLine(cmd).parseArgs("--directories-to-pull=a,b") - assertThat(cmd.config.platform.gcloud.directoriesToPull).hasSize(2) + assertThat(cmd.config.common.gcloud.directoriesToPull).hasSize(2) } @Test diff --git a/test_runner/src/test/kotlin/ftl/cli/firebase/test/ios/IosRunCommandTest.kt b/test_runner/src/test/kotlin/ftl/cli/firebase/test/ios/IosRunCommandTest.kt index 264cb75667..f86b076a92 100644 --- a/test_runner/src/test/kotlin/ftl/cli/firebase/test/ios/IosRunCommandTest.kt +++ b/test_runner/src/test/kotlin/ftl/cli/firebase/test/ios/IosRunCommandTest.kt @@ -90,6 +90,7 @@ class IosRunCommandTest { assertThat(cmd.config.common.gcloud.devices).isNull() assertThat(cmd.config.common.gcloud.resultsDir).isNull() assertThat(cmd.config.common.gcloud.flakyTestAttempts).isNull() + assertThat(cmd.config.common.gcloud.directoriesToPull).isNull() assertThat(cmd.config.common.flank.localResultsDir).isNull() assertThat(cmd.config.common.flank.smartFlankDisableUpload).isNull() assertThat(cmd.config.common.flank.smartFlankGcsPath).isNull() diff --git a/test_runner/src/test/kotlin/ftl/gc/GcIosTestMatrixTest.kt b/test_runner/src/test/kotlin/ftl/gc/GcIosTestMatrixTest.kt index 6c52717f9b..e8da56641c 100644 --- a/test_runner/src/test/kotlin/ftl/gc/GcIosTestMatrixTest.kt +++ b/test_runner/src/test/kotlin/ftl/gc/GcIosTestMatrixTest.kt @@ -2,10 +2,10 @@ package ftl.gc import com.dd.plist.NSDictionary import com.google.api.services.testing.model.IosDeviceList -import ftl.shard.Chunk import ftl.args.IosArgs import ftl.config.FtlConstants.isWindows import ftl.ios.FIXTURES_PATH +import ftl.shard.Chunk import ftl.shard.TestMethod import ftl.test.util.FlankTestRunner import ftl.util.ShardCounter @@ -165,4 +165,32 @@ class GcIosTestMatrixTest { val expected = emptyList<String>() assertEquals(expected, iosArgs.additionalIpas) } + + @Test + fun `should fill directoriesToPull`() { + val iosArgs = IosArgs.load(StringReader(""" + gcloud: + test: ./test_runner/src/test/kotlin/ftl/fixtures/tmp/earlgrey_example.zip + xctestrun-file: ./test_runner/src/test/kotlin/ftl/fixtures/tmp/EarlGreyExampleSwiftTests_iphoneos13.4-arm64e.xctestrun + results-dir: test_dir + directories-to-pull: + - test/test/test + """.trimIndent())) + + val expected = listOf("test/test/test") + assertEquals(expected, iosArgs.directoriesToPull) + } + + @Test + fun `should not fill directoriesToPull`() { + val iosArgs = IosArgs.load(StringReader(""" + gcloud: + test: ./test_runner/src/test/kotlin/ftl/fixtures/tmp/earlgrey_example.zip + xctestrun-file: ./test_runner/src/test/kotlin/ftl/fixtures/tmp/EarlGreyExampleSwiftTests_iphoneos13.4-arm64e.xctestrun + results-dir: test_dir + """.trimIndent())) + + val expected = emptyList<String>() + assertEquals(expected, iosArgs.directoriesToPull) + } }