From 8fb4c3b19e5181b684053cd6a9bd545dd6e7e171 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Sun, 16 Oct 2022 21:59:29 +0200 Subject: [PATCH 01/15] start reworking CLI to use prebuilt .app on iOS Simulator --- .../lib/src/common/artifacts_repository.dart | 87 ++++++++++--------- .../features/drive/platform/ios_driver.dart | 74 +++++++++++----- 2 files changed, 99 insertions(+), 62 deletions(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 30ac3733d..256cde5e7 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -15,7 +15,7 @@ class ArtifactsRepository { _httpClient = httpClient ?? http.Client(), _zipDecoder = zipDecoder ?? ZipDecoder(), debug = false { - _paths = _Paths(artifactPath); + _paths = _ArtifactPaths(artifactPath); } static const artifactPathEnv = 'PATROL_CACHE'; @@ -26,7 +26,7 @@ class ArtifactsRepository { final ZipDecoder _zipDecoder; bool debug; - late final _Paths _paths; + late final _ArtifactPaths _paths; String get artifactPath { final env = platform.environment; @@ -72,7 +72,7 @@ class ArtifactsRepository { } String get iosArtifactDirPath { - return debug ? _paths.debugIOSArtifactDirPath : _paths.iosArtifactDirPath; + return debug ? _paths.iosDirDebugPath : _paths.iosDirPath; } /// Returns true if artifacts for the current patrol_cli version are present @@ -83,6 +83,8 @@ class ArtifactsRepository { if (platform.isMacOS) { final iosDir = _fs.directory(iosArtifactDirPath); + //final iosSimArmApp = _fs.directory(ios) + return serverApk.existsSync() && instrumentationApk.existsSync() && iosDir.existsSync(); @@ -94,22 +96,25 @@ class ArtifactsRepository { /// Downloads artifacts for the current patrol_cli version. Future downloadArtifacts() async { await Future.wait([ - _downloadArtifact(_paths.serverArtifactFile), - _downloadArtifact(_paths.instrumentationArtifactFile), - if (platform.isMacOS) _downloadArtifact(_paths.iosArtifactZip), + _downloadArtifact(_paths.androidServerFile), + _downloadArtifact(_paths.androidInstrumentationFile), + if (platform.isMacOS) ...[ + _downloadArtifact(_paths.iosProjectZip), + _downloadArtifact(_paths.iosAutomatorSimAmdZip), + _downloadArtifact(_paths.iosAutomatorSimArmZip), + ], ]); if (!platform.isMacOS) { return; } - final bytes = await _fs.file(_paths.iosArtifactZipPath).readAsBytes(); + final bytes = await _fs.file(_paths.iosZipPath).readAsBytes(); final archive = _zipDecoder.decodeBytes(bytes); for (final archiveFile in archive) { final filename = archiveFile.name; - final extractPath = - _paths.iosArtifactDirPath + platform.pathSeparator + filename; + final extractPath = _paths.iosDirPath + platform.pathSeparator + filename; if (archiveFile.isFile) { final data = archiveFile.content as List; final newFile = _fs.file(extractPath); @@ -143,66 +148,68 @@ class ArtifactsRepository { } } -class _Paths { - const _Paths(this._artifactPath); +class _ArtifactPaths { + const _ArtifactPaths(this._artifactPath); final String _artifactPath; - String get serverArtifact => 'server-$version'; + /// Returns a URI where [artifact] can be downloaded from. + /// + /// [artifact] must be in the form of `$artifact-$version.$extension`, for + /// example: `server-1.0.0.apk` or `ios-4.2.0.zip`. + Uri getUriForArtifact(String artifact) { + return Uri.parse( + 'https://github.com/leancodepl/patrol/releases/download/patrol_cli-v$version/$artifact', + ); + } - String get serverArtifactFile => '$serverArtifact.apk'; + String get androidServerFile => 'server-$version.apk'; - String get instrumentationArtifact => 'instrumentation-$version'; + String get androidInstrumentationFile => 'instrumentation-$version.apk'; - String get instrumentationArtifactFile => '$instrumentationArtifact.apk'; + String get androidServerDebugFile => 'server.apk'; - String get iosArtifactDir => 'ios-$version'; + String get androidInstrumentationDebugFile => 'instrumentation.apk'; - String get iosArtifactZip => 'ios-$version.zip'; + String get iosProjectDir => 'ios-$version'; - String get debugServerArtifactFile => 'server.apk'; + String get iosProjectZip => 'ios-$version.zip'; - String get debugInstrumentationArtifactFile => 'instrumentation.apk'; + String get iosProjectDebugDir => 'ios'; - String get debugIOSArtifactDir => 'ios'; + String get iosAutomatorSimAmdZip { + return 'AutomatorServer-iphonesimulator-x86_64-$version.zip'; + } - /// Returns a URI where [artifact] can be downloaded from. - /// - /// [artifact] must be in the form of `$artifact-$version.$extension`, for - /// example: `server-1.0.0.apk` or `ios-4.2.0.zip`. - Uri getUriForArtifact( - String artifact, - ) { - return Uri.parse( - 'https://github.com/leancodepl/patrol/releases/download/patrol_cli-v$version/$artifact', - ); + String get iosAutomatorSimArmZip { + return 'AutomatorServer-iphonesimulator-arm64-$version.zip'; } String get serverArtifactPath { - return join(_artifactPath, serverArtifactFile); + return join(_artifactPath, androidServerFile); } String get debugServerArtifactPath { - return join(_artifactPath, debugServerArtifactFile); + return join(_artifactPath, androidServerDebugFile); } String get instrumentationArtifactPath { - return join(_artifactPath, instrumentationArtifactFile); + return join(_artifactPath, androidInstrumentationFile); } String get debugInstrumentationArtifactPath { - return join(_artifactPath, debugInstrumentationArtifactFile); + return join(_artifactPath, androidInstrumentationDebugFile); } - String get iosArtifactZipPath { - return join(_artifactPath, iosArtifactZip); + String get iosZipPath { + return join(_artifactPath, iosProjectZip); } - String get iosArtifactDirPath { - return join(_artifactPath, iosArtifactDir); + String get iosDirPath { + return join(_artifactPath, iosProjectDir); } - String get debugIOSArtifactDirPath { - return join(_artifactPath, debugIOSArtifactDir); + String get iosDirDebugPath { + return join(_artifactPath, iosProjectDebugDir); } } diff --git a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart index 3d030a5c4..35d6627b0 100644 --- a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart +++ b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart @@ -31,12 +31,16 @@ class IOSDriver { if (device.real) { await _forwardPorts(port: port, deviceId: device.id); } - await _runServer( - deviceName: device.name, - deviceId: device.id, - simulator: !device.real, - port: port, - ); + + if (device.real) { + await _runServerPhysical( + deviceName: device.name, + deviceId: device.id, + port: port, + ); + } else { + await _runServerSimulator(deviceId: device.id, port: port); + } } /// Forwards ports using iproxy. @@ -91,17 +95,49 @@ class IOSDriver { progress.complete('Forwarded ports'); } + Future _runServerSimulator({ + required String port, + required String deviceId, + }) async { + // FIXME: artifact path to .app + _logger + .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); + + // FIXME: fix artifact path to .app + final installProcess = await Process.start( + 'xcrun', + [ + 'simctl', + 'install', + deviceId, + _artifactsRepository.iosArtifactDirPath, + ], + runInShell: true, + ); + + await installProcess.exitCode; + + final runProcess = await Process.start( + 'xcrun', + [ + 'simctl', + 'launch', + deviceId, + 'pl.leancode.AutomatorServerUITests.xctrunner', + ], + runInShell: true, + ); + } + /// Runs the server which is an infinite XCUITest. - Future _runServer({ + Future _runServerPhysical({ required String port, required String deviceName, required String deviceId, - required bool simulator, }) async { _logger .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); - // This xcodebuild fails when using Dart < 2.17. final process = await Process.start( 'xcodebuild', [ @@ -111,9 +147,9 @@ class IOSDriver { '-scheme', 'AutomatorServer', '-sdk', - if (simulator) 'iphonesimulator' else 'iphoneos', + 'iphoneos', '-destination', - 'platform=iOS${simulator ? " Simulator" : ""},name=$deviceName', + 'platform=iOS,name=$deviceName', ], runInShell: true, workingDirectory: _artifactsRepository.iosArtifactDirPath, @@ -128,17 +164,11 @@ class IOSDriver { // Uninstall AutomatorServer ..addDispose(() async { const bundleId = 'pl.leancode.AutomatorServerUITests.xctrunner'; - final process = simulator - ? await Process.run( - 'xcrun', - ['simctl', 'uninstall', deviceId, bundleId], - runInShell: true, - ) - : await Process.run( - 'ideviceinstaller', - ['--uninstall', bundleId, '--udid', deviceId], - runInShell: true, - ); + final process = await Process.run( + 'ideviceinstaller', + ['--uninstall', bundleId, '--udid', deviceId], + runInShell: true, + ); final exitCode = process.exitCode; final msg = exitCode == 0 From 6ec5367a4f00321a7923572285a49c537d2cfbaa Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 17 Oct 2022 01:11:01 +0200 Subject: [PATCH 02/15] use manual codesigning --- AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj b/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj index ca0323de2..aabe37223 100644 --- a/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj +++ b/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj @@ -292,7 +292,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U3EG6EALX7; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -321,7 +321,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U3EG6EALX7; + DEVELOPMENT_TEAM = ""; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", From 89ea1879d8be597d30ece975e6fb8863f79e90d9 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 17 Oct 2022 16:37:48 +0200 Subject: [PATCH 03/15] in progress --- .../lib/src/common/artifacts_repository.dart | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 256cde5e7..576b76f14 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -5,6 +5,59 @@ import 'package:path/path.dart' show join, dirname; import 'package:patrol_cli/src/common/constants.dart' show version; import 'package:platform/platform.dart'; +class _Artifact { + const _Artifact({ + required this.name, + required this.version, + required this.ext, + }); + + final String name; + final String? version; + final String ext; + + /// Returns a URI where this artifact is hosted. + Uri get uri { + final version = this.version; + if (version == null) { + throw StateError('cannot get uri for an unversioned artifact'); + } + + return Uri.parse( + 'https://github.com/leancodepl/patrol/releases/download/patrol_cli-v$version/$filename', + ); + } + + String get filename { + var result = name; + if (version != null) { + result += '-$version'; + } + result += '.$ext'; + + return result; + } +} + +const _androidArtifacts = [ + _Artifact(name: 'server', version: version, ext: '.apk'), + _Artifact(name: 'instrumentation', version: version, ext: '.apk'), +]; + +const _iosArtifacts = [ + _Artifact(name: 'ios', version: version, ext: '.zip'), + _Artifact( + name: 'AutomatorServer-iphonesimulator-arm64', + version: version, + ext: '.zip', + ), + _Artifact( + name: 'AutomatorServer-iphonesimulator-x86_64', + version: version, + ext: '.zip', + ), +]; + class ArtifactsRepository { ArtifactsRepository({ required FileSystem fs, From 519493eecd51361981b68105f8a941d521c97ced Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 17 Oct 2022 19:06:19 +0200 Subject: [PATCH 04/15] create `Artifact` freezed class and `Artifacts` static class --- .../AutomatorServer.xcodeproj/project.pbxproj | 4 +- .../lib/src/common/artifacts_repository.dart | 204 +++++--- .../common/artifacts_repository.freezed.dart | 434 ++++++++++++++++++ 3 files changed, 571 insertions(+), 71 deletions(-) create mode 100644 packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart diff --git a/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj b/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj index aabe37223..ca0323de2 100644 --- a/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj +++ b/AutomatorServer/ios/AutomatorServer.xcodeproj/project.pbxproj @@ -292,7 +292,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = U3EG6EALX7; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -321,7 +321,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = U3EG6EALX7; GENERATE_INFOPLIST_FILE = YES; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 576b76f14..7d634abe1 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -1,62 +1,102 @@ import 'package:archive/archive.dart'; import 'package:file/file.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' show join, dirname; import 'package:patrol_cli/src/common/constants.dart' show version; import 'package:platform/platform.dart'; -class _Artifact { - const _Artifact({ - required this.name, - required this.version, - required this.ext, - }); +part 'artifacts_repository.freezed.dart'; - final String name; - final String? version; - final String ext; +class Artifacts { + const Artifacts._(); - /// Returns a URI where this artifact is hosted. - Uri get uri { - final version = this.version; - if (version == null) { - throw StateError('cannot get uri for an unversioned artifact'); - } + // Android - return Uri.parse( - 'https://github.com/leancodepl/patrol/releases/download/patrol_cli-v$version/$filename', - ); - } + // The dummy app under test. + static const androidApp = Artifact.file( + name: 'server', + version: version, + ext: '.apk', + ); + + static const androidInstrumentation = Artifact.file( + name: 'instrumentation', + version: version, + ext: '.apk', + ); + + // iOS + + static const ios = Artifact.archive( + name: 'ios', + version: version, + ext: '.app', + ); + + static const iosSimulatorArm = Artifact.archive( + name: 'AutomatorServer-iphonesimulator-arm64', + version: version, + ext: '.app', + ); + + static const iosSimulatorAmd = Artifact.archive( + name: 'AutomatorServer-iphonesimulator-x86_64', + version: version, + ext: '.app', + ); +} + +@freezed +class Artifact with _$Artifact { + const factory Artifact.file({ + required String name, + String? version, + required String ext, + }) = _ArtifactFile; + + const factory Artifact.archive({ + required String name, + String? version, + required String ext, + }) = _ArtifactArchive; + + const Artifact._(); String get filename { var result = name; - if (version != null) { - result += '-$version'; - } + result += '-$version'; result += '.$ext'; return result; } -} -const _androidArtifacts = [ - _Artifact(name: 'server', version: version, ext: '.apk'), - _Artifact(name: 'instrumentation', version: version, ext: '.apk'), -]; + /// Returns an unversioned (i.e debug) variant of this artifact. + Artifact get debug => copyWith(version: null); -const _iosArtifacts = [ - _Artifact(name: 'ios', version: version, ext: '.zip'), - _Artifact( - name: 'AutomatorServer-iphonesimulator-arm64', - version: version, - ext: '.zip', - ), - _Artifact( - name: 'AutomatorServer-iphonesimulator-x86_64', - version: version, - ext: '.zip', - ), -]; + /// Returns a URI where this artifact is hosted. + Uri get uri { + final version = this.version; + if (version == null) { + throw StateError('cannot get uri for an unversioned artifact'); + } + + const repo = 'https://github.com/leancodepl/patrol'; + + return map( + file: (file) { + return Uri.parse( + '$repo/releases/download/patrol_cli-v$version/$filename', + ); + }, + archive: (archive) { + return Uri.parse( + '$repo/releases/download/patrol_cli-v$version/$name-$version.zip', + ); + }, + ); + } +} class ArtifactsRepository { ArtifactsRepository({ @@ -67,9 +107,7 @@ class ArtifactsRepository { }) : _fs = fs, _httpClient = httpClient ?? http.Client(), _zipDecoder = zipDecoder ?? ZipDecoder(), - debug = false { - _paths = _ArtifactPaths(artifactPath); - } + debug = false; static const artifactPathEnv = 'PATROL_CACHE'; @@ -79,8 +117,6 @@ class ArtifactsRepository { final ZipDecoder _zipDecoder; bool debug; - late final _ArtifactPaths _paths; - String get artifactPath { final env = platform.environment; String p; @@ -115,17 +151,37 @@ class ArtifactsRepository { } String get serverArtifactPath { - return debug ? _paths.debugServerArtifactPath : _paths.serverArtifactPath; + final artifact = debug ? Artifacts.androidApp.debug : Artifacts.androidApp; + + return join(artifactPath, artifact.filename); } String get instrumentationArtifactPath { - return debug - ? _paths.debugInstrumentationArtifactPath - : _paths.instrumentationArtifactPath; + final artifact = debug + ? Artifacts.androidInstrumentation.debug + : Artifacts.androidInstrumentation; + + return join(artifactPath, artifact.filename); } - String get iosArtifactDirPath { - return debug ? _paths.iosDirDebugPath : _paths.iosDirPath; + String get iosDevicePath { + final artifact = debug ? Artifacts.ios.debug : Artifacts.ios; + + return join(artifactPath, artifact.filename); + } + + String get iosSimulatorArmPath { + final artifact = + debug ? Artifacts.iosSimulatorArm.debug : Artifacts.iosSimulatorArm; + + return join(artifactPath, artifact.filename); + } + + String get iosSimulatorAmdPath { + final artifact = + debug ? Artifacts.iosSimulatorAmd.debug : Artifacts.iosSimulatorAmd; + + return join(artifactPath, artifact.filename); } /// Returns true if artifacts for the current patrol_cli version are present @@ -134,27 +190,37 @@ class ArtifactsRepository { final serverApk = _fs.file(serverArtifactPath); final instrumentationApk = _fs.file(instrumentationArtifactPath); + final androidArtifactsExist = + serverApk.existsSync() && instrumentationApk.existsSync(); + if (platform.isMacOS) { - final iosDir = _fs.directory(iosArtifactDirPath); - //final iosSimArmApp = _fs.directory(ios) + final iosDir = _fs.directory(iosDevicePath); + final iosSimArmDir = _fs.directory(iosSimulatorArmPath); + final iosSimAmdDir = _fs.directory(iosSimulatorAmdPath); + + final iosArtifactsExist = iosDir.existsSync() && + iosSimArmDir.existsSync() && + iosSimAmdDir.existsSync(); - return serverApk.existsSync() && - instrumentationApk.existsSync() && - iosDir.existsSync(); + return androidArtifactsExist && iosArtifactsExist; } else { - return serverApk.existsSync() && instrumentationApk.existsSync(); + return androidArtifactsExist; } } /// Downloads artifacts for the current patrol_cli version. - Future downloadArtifacts() async { + Future downloadArtifacts({String? ver}) async { + ver ??= version; + await Future.wait([ - _downloadArtifact(_paths.androidServerFile), - _downloadArtifact(_paths.androidInstrumentationFile), + _downloadArtifact(Artifacts.androidApp.copyWith(version: version)), + _downloadArtifact( + Artifacts.androidInstrumentation.copyWith(version: version), + ), if (platform.isMacOS) ...[ - _downloadArtifact(_paths.iosProjectZip), - _downloadArtifact(_paths.iosAutomatorSimAmdZip), - _downloadArtifact(_paths.iosAutomatorSimArmZip), + _downloadArtifact(Artifacts.ios), + _downloadArtifact(Artifacts.iosSimulatorArm), + _downloadArtifact(Artifacts.iosSimulatorAmd), ], ]); @@ -180,15 +246,14 @@ class ArtifactsRepository { } } - Future _downloadArtifact(String artifact) async { - final uri = _paths.getUriForArtifact(artifact); - final response = await _httpClient.get(uri); + Future _downloadArtifact(Artifact artifact) async { + final response = await _httpClient.get(artifact.uri); if (response.statusCode != 200) { - throw Exception('Failed to download $artifact from $uri'); + throw Exception('Failed to download $artifact from ${artifact.uri}'); } - final p = join(artifactPath, artifact); + final p = join(artifactPath, artifact.filename); _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); } @@ -201,7 +266,7 @@ class ArtifactsRepository { } } -class _ArtifactPaths { +/* class _ArtifactPaths { const _ArtifactPaths(this._artifactPath); final String _artifactPath; @@ -266,3 +331,4 @@ class _ArtifactPaths { return join(_artifactPath, iosProjectDebugDir); } } + */ diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart new file mode 100644 index 000000000..7a609068b --- /dev/null +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart @@ -0,0 +1,434 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target + +part of 'artifacts_repository.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +/// @nodoc +mixin _$Artifact { + String get name => throw _privateConstructorUsedError; + String? get version => throw _privateConstructorUsedError; + String get ext => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function(String name, String? version, String ext) file, + required TResult Function(String name, String? version, String ext) archive, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(_ArtifactFile value) file, + required TResult Function(_ArtifactArchive value) archive, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $ArtifactCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ArtifactCopyWith<$Res> { + factory $ArtifactCopyWith(Artifact value, $Res Function(Artifact) then) = + _$ArtifactCopyWithImpl<$Res>; + $Res call({String name, String? version, String ext}); +} + +/// @nodoc +class _$ArtifactCopyWithImpl<$Res> implements $ArtifactCopyWith<$Res> { + _$ArtifactCopyWithImpl(this._value, this._then); + + final Artifact _value; + // ignore: unused_field + final $Res Function(Artifact) _then; + + @override + $Res call({ + Object? name = freezed, + Object? version = freezed, + Object? ext = freezed, + }) { + return _then(_value.copyWith( + name: name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + version: version == freezed + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String?, + ext: ext == freezed + ? _value.ext + : ext // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +abstract class _$$_ArtifactFileCopyWith<$Res> + implements $ArtifactCopyWith<$Res> { + factory _$$_ArtifactFileCopyWith( + _$_ArtifactFile value, $Res Function(_$_ArtifactFile) then) = + __$$_ArtifactFileCopyWithImpl<$Res>; + @override + $Res call({String name, String? version, String ext}); +} + +/// @nodoc +class __$$_ArtifactFileCopyWithImpl<$Res> extends _$ArtifactCopyWithImpl<$Res> + implements _$$_ArtifactFileCopyWith<$Res> { + __$$_ArtifactFileCopyWithImpl( + _$_ArtifactFile _value, $Res Function(_$_ArtifactFile) _then) + : super(_value, (v) => _then(v as _$_ArtifactFile)); + + @override + _$_ArtifactFile get _value => super._value as _$_ArtifactFile; + + @override + $Res call({ + Object? name = freezed, + Object? version = freezed, + Object? ext = freezed, + }) { + return _then(_$_ArtifactFile( + name: name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + version: version == freezed + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String?, + ext: ext == freezed + ? _value.ext + : ext // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$_ArtifactFile extends _ArtifactFile { + const _$_ArtifactFile({required this.name, this.version, required this.ext}) + : super._(); + + @override + final String name; + @override + final String? version; + @override + final String ext; + + @override + String toString() { + return 'Artifact.file(name: $name, version: $version, ext: $ext)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_ArtifactFile && + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.version, version) && + const DeepCollectionEquality().equals(other.ext, ext)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(version), + const DeepCollectionEquality().hash(ext)); + + @JsonKey(ignore: true) + @override + _$$_ArtifactFileCopyWith<_$_ArtifactFile> get copyWith => + __$$_ArtifactFileCopyWithImpl<_$_ArtifactFile>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String name, String? version, String ext) file, + required TResult Function(String name, String? version, String ext) archive, + }) { + return file(name, version, ext); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + }) { + return file?.call(name, version, ext); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + required TResult orElse(), + }) { + if (file != null) { + return file(name, version, ext); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ArtifactFile value) file, + required TResult Function(_ArtifactArchive value) archive, + }) { + return file(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + }) { + return file?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + required TResult orElse(), + }) { + if (file != null) { + return file(this); + } + return orElse(); + } +} + +abstract class _ArtifactFile extends Artifact { + const factory _ArtifactFile( + {required final String name, + final String? version, + required final String ext}) = _$_ArtifactFile; + const _ArtifactFile._() : super._(); + + @override + String get name; + @override + String? get version; + @override + String get ext; + @override + @JsonKey(ignore: true) + _$$_ArtifactFileCopyWith<_$_ArtifactFile> get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class _$$_ArtifactArchiveCopyWith<$Res> + implements $ArtifactCopyWith<$Res> { + factory _$$_ArtifactArchiveCopyWith( + _$_ArtifactArchive value, $Res Function(_$_ArtifactArchive) then) = + __$$_ArtifactArchiveCopyWithImpl<$Res>; + @override + $Res call({String name, String? version, String ext}); +} + +/// @nodoc +class __$$_ArtifactArchiveCopyWithImpl<$Res> + extends _$ArtifactCopyWithImpl<$Res> + implements _$$_ArtifactArchiveCopyWith<$Res> { + __$$_ArtifactArchiveCopyWithImpl( + _$_ArtifactArchive _value, $Res Function(_$_ArtifactArchive) _then) + : super(_value, (v) => _then(v as _$_ArtifactArchive)); + + @override + _$_ArtifactArchive get _value => super._value as _$_ArtifactArchive; + + @override + $Res call({ + Object? name = freezed, + Object? version = freezed, + Object? ext = freezed, + }) { + return _then(_$_ArtifactArchive( + name: name == freezed + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + version: version == freezed + ? _value.version + : version // ignore: cast_nullable_to_non_nullable + as String?, + ext: ext == freezed + ? _value.ext + : ext // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$_ArtifactArchive extends _ArtifactArchive { + const _$_ArtifactArchive( + {required this.name, this.version, required this.ext}) + : super._(); + + @override + final String name; + @override + final String? version; + @override + final String ext; + + @override + String toString() { + return 'Artifact.archive(name: $name, version: $version, ext: $ext)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_ArtifactArchive && + const DeepCollectionEquality().equals(other.name, name) && + const DeepCollectionEquality().equals(other.version, version) && + const DeepCollectionEquality().equals(other.ext, ext)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(name), + const DeepCollectionEquality().hash(version), + const DeepCollectionEquality().hash(ext)); + + @JsonKey(ignore: true) + @override + _$$_ArtifactArchiveCopyWith<_$_ArtifactArchive> get copyWith => + __$$_ArtifactArchiveCopyWithImpl<_$_ArtifactArchive>(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function(String name, String? version, String ext) file, + required TResult Function(String name, String? version, String ext) archive, + }) { + return archive(name, version, ext); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + }) { + return archive?.call(name, version, ext); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function(String name, String? version, String ext)? file, + TResult Function(String name, String? version, String ext)? archive, + required TResult orElse(), + }) { + if (archive != null) { + return archive(name, version, ext); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(_ArtifactFile value) file, + required TResult Function(_ArtifactArchive value) archive, + }) { + return archive(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + }) { + return archive?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(_ArtifactFile value)? file, + TResult Function(_ArtifactArchive value)? archive, + required TResult orElse(), + }) { + if (archive != null) { + return archive(this); + } + return orElse(); + } +} + +abstract class _ArtifactArchive extends Artifact { + const factory _ArtifactArchive( + {required final String name, + final String? version, + required final String ext}) = _$_ArtifactArchive; + const _ArtifactArchive._() : super._(); + + @override + String get name; + @override + String? get version; + @override + String get ext; + @override + @JsonKey(ignore: true) + _$$_ArtifactArchiveCopyWith<_$_ArtifactArchive> get copyWith => + throw _privateConstructorUsedError; +} From 6280d332c44bfa5235a1ae35c412897ecb526ad8 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 17 Oct 2022 21:51:27 +0200 Subject: [PATCH 05/15] fix errors in ArtifactsRepository --- .../lib/src/common/artifacts_repository.dart | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 7d634abe1..e3390e2bd 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -71,6 +71,15 @@ class Artifact with _$Artifact { return result; } + String get archiveName { + return map( + file: (_) => throw StateError( + 'archiveName is not applicable for file artifacts', + ), + archive: (artifact) => '${artifact.name}-${artifact.version}.zip', + ); + } + /// Returns an unversioned (i.e debug) variant of this artifact. Artifact get debug => copyWith(version: null); @@ -98,6 +107,10 @@ class Artifact with _$Artifact { } } +extension _ArtifactArchiveX on _ArtifactArchive { + String get archiveName => '$name-$version.zip'; +} + class ArtifactsRepository { ArtifactsRepository({ required FileSystem fs, @@ -224,16 +237,32 @@ class ArtifactsRepository { ], ]); - if (!platform.isMacOS) { - return; + if (platform.isMacOS) { + await _extractArtifact(Artifacts.ios); + await _extractArtifact(Artifacts.iosSimulatorArm); + await _extractArtifact(Artifacts.iosSimulatorAmd); + } + } + + Future _downloadArtifact(Artifact artifact) async { + final response = await _httpClient.get(artifact.uri); + + if (response.statusCode != 200) { + throw Exception('Failed to download $artifact from ${artifact.uri}'); } - final bytes = await _fs.file(_paths.iosZipPath).readAsBytes(); + final p = join(artifactPath, artifact.filename); + _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); + } + + Future _extractArtifact(Artifact artifact) async { + final archievePath = join(artifactPath, artifact.archiveName); + final bytes = await _fs.file(archievePath).readAsBytes(); final archive = _zipDecoder.decodeBytes(bytes); for (final archiveFile in archive) { final filename = archiveFile.name; - final extractPath = _paths.iosDirPath + platform.pathSeparator + filename; + final extractPath = join(artifactPath, artifact.filename, filename); if (archiveFile.isFile) { final data = archiveFile.content as List; final newFile = _fs.file(extractPath); @@ -246,17 +275,6 @@ class ArtifactsRepository { } } - Future _downloadArtifact(Artifact artifact) async { - final response = await _httpClient.get(artifact.uri); - - if (response.statusCode != 200) { - throw Exception('Failed to download $artifact from ${artifact.uri}'); - } - - final p = join(artifactPath, artifact.filename); - _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); - } - /// Create a file at [fullPath], recursively creating non-existent /// directories. File _createFileRecursively(String fullPath) { From c3e3879ff49da342db5999555cd406907ec2b100 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Mon, 17 Oct 2022 23:32:20 +0200 Subject: [PATCH 06/15] nice progress --- .../patrol_cli/lib/src/command_runner.dart | 6 +- .../lib/src/common/artifacts_repository.dart | 77 ++++++++++++------- .../common/artifacts_repository.freezed.dart | 70 +++++++++-------- .../patrol_cli/lib/src/common/constants.dart | 2 +- .../features/drive/platform/ios_driver.dart | 30 +++++--- .../src/features/update/update_command.dart | 4 +- .../patrol_cli/test/command_runner_test.dart | 6 +- .../common/artifacts_repository_test.dart | 51 ++++++------ 8 files changed, 144 insertions(+), 102 deletions(-) diff --git a/packages/patrol_cli/lib/src/command_runner.dart b/packages/patrol_cli/lib/src/command_runner.dart index 0232a3fc4..6cfaa9829 100644 --- a/packages/patrol_cli/lib/src/command_runner.dart +++ b/packages/patrol_cli/lib/src/command_runner.dart @@ -220,7 +220,7 @@ class PatrolCommandRunner extends CommandRunner { final int? exitCode; if (topLevelResults['version'] == true) { - _logger.info('patrol_cli v$version'); + _logger.info('patrol_cli v$globalVersion'); exitCode = 0; } else { exitCode = await super.runCommand(topLevelResults); @@ -248,7 +248,7 @@ class PatrolCommandRunner extends CommandRunner { } final latestVersion = await _pubUpdater.getLatestVersion(patrolCliPackage); - final isUpToDate = version == latestVersion; + final isUpToDate = globalVersion == latestVersion; if (isUpToDate) { return; @@ -258,7 +258,7 @@ class PatrolCommandRunner extends CommandRunner { ..info('') ..info( ''' -${lightYellow.wrap('Update available!')} ${lightCyan.wrap(version)} \u2192 ${lightCyan.wrap(latestVersion)} +${lightYellow.wrap('Update available!')} ${lightCyan.wrap(globalVersion)} \u2192 ${lightCyan.wrap(latestVersion)} Run ${lightCyan.wrap('patrol update')} to update''', ) ..info(''); diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index e3390e2bd..52650f5d1 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -3,7 +3,7 @@ import 'package:file/file.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' show join, dirname; -import 'package:patrol_cli/src/common/constants.dart' show version; +import 'package:patrol_cli/src/common/constants.dart' show globalVersion; import 'package:platform/platform.dart'; part 'artifacts_repository.freezed.dart'; @@ -16,34 +16,39 @@ class Artifacts { // The dummy app under test. static const androidApp = Artifact.file( name: 'server', - version: version, - ext: '.apk', + version: globalVersion, + ext: 'apk', ); static const androidInstrumentation = Artifact.file( name: 'instrumentation', - version: version, - ext: '.apk', + version: globalVersion, + ext: 'apk', ); // iOS static const ios = Artifact.archive( name: 'ios', - version: version, - ext: '.app', + version: globalVersion, + ); + + static const iosDevice = Artifact.archive( + name: 'AutomatorServer-iphoneos-arm64', + version: globalVersion, + ext: 'app', ); static const iosSimulatorArm = Artifact.archive( name: 'AutomatorServer-iphonesimulator-arm64', - version: version, - ext: '.app', + version: globalVersion, + ext: 'app', ); static const iosSimulatorAmd = Artifact.archive( name: 'AutomatorServer-iphonesimulator-x86_64', - version: version, - ext: '.app', + version: globalVersion, + ext: 'app', ); } @@ -52,25 +57,39 @@ class Artifact with _$Artifact { const factory Artifact.file({ required String name, String? version, - required String ext, + String? ext, }) = _ArtifactFile; const factory Artifact.archive({ required String name, String? version, - required String ext, + String? ext, }) = _ArtifactArchive; const Artifact._(); String get filename { var result = name; - result += '-$version'; - result += '.$ext'; + + if (version != null) { + result += '-$version'; + } + + if (ext != null) { + result += '.$ext'; + } return result; } + /// Name of the file that was originally downloaded. + String get remoteName { + return map( + file: (artifact) => artifact.filename, + archive: (archive) => '$name-$version.zip', + ); + } + String get archiveName { return map( file: (_) => throw StateError( @@ -107,10 +126,6 @@ class Artifact with _$Artifact { } } -extension _ArtifactArchiveX on _ArtifactArchive { - String get archiveName => '$name-$version.zip'; -} - class ArtifactsRepository { ArtifactsRepository({ required FileSystem fs, @@ -177,12 +192,18 @@ class ArtifactsRepository { return join(artifactPath, artifact.filename); } - String get iosDevicePath { + String get iosPath { final artifact = debug ? Artifacts.ios.debug : Artifacts.ios; return join(artifactPath, artifact.filename); } + String get iosDevicePath { + final artifact = debug ? Artifacts.iosDevice.debug : Artifacts.iosDevice; + + return join(artifactPath, artifact.filename); + } + String get iosSimulatorArmPath { final artifact = debug ? Artifacts.iosSimulatorArm.debug : Artifacts.iosSimulatorArm; @@ -207,11 +228,13 @@ class ArtifactsRepository { serverApk.existsSync() && instrumentationApk.existsSync(); if (platform.isMacOS) { - final iosDir = _fs.directory(iosDevicePath); + final iosDir = _fs.directory(iosPath); + final iosDeviceDir = _fs.directory(iosDevicePath); final iosSimArmDir = _fs.directory(iosSimulatorArmPath); final iosSimAmdDir = _fs.directory(iosSimulatorAmdPath); final iosArtifactsExist = iosDir.existsSync() && + iosDeviceDir.existsSync() && iosSimArmDir.existsSync() && iosSimAmdDir.existsSync(); @@ -223,15 +246,16 @@ class ArtifactsRepository { /// Downloads artifacts for the current patrol_cli version. Future downloadArtifacts({String? ver}) async { - ver ??= version; + ver ??= globalVersion; await Future.wait([ - _downloadArtifact(Artifacts.androidApp.copyWith(version: version)), + _downloadArtifact(Artifacts.androidApp.copyWith(version: ver)), _downloadArtifact( - Artifacts.androidInstrumentation.copyWith(version: version), + Artifacts.androidInstrumentation.copyWith(version: ver), ), if (platform.isMacOS) ...[ _downloadArtifact(Artifacts.ios), + _downloadArtifact(Artifacts.iosDevice), _downloadArtifact(Artifacts.iosSimulatorArm), _downloadArtifact(Artifacts.iosSimulatorAmd), ], @@ -239,6 +263,7 @@ class ArtifactsRepository { if (platform.isMacOS) { await _extractArtifact(Artifacts.ios); + await _extractArtifact(Artifacts.iosDevice); await _extractArtifact(Artifacts.iosSimulatorArm); await _extractArtifact(Artifacts.iosSimulatorAmd); } @@ -251,7 +276,7 @@ class ArtifactsRepository { throw Exception('Failed to download $artifact from ${artifact.uri}'); } - final p = join(artifactPath, artifact.filename); + final p = join(artifactPath, artifact.remoteName); _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); } @@ -262,7 +287,7 @@ class ArtifactsRepository { for (final archiveFile in archive) { final filename = archiveFile.name; - final extractPath = join(artifactPath, artifact.filename, filename); + final extractPath = join(artifactPath, filename); if (archiveFile.isFile) { final data = archiveFile.content as List; final newFile = _fs.file(extractPath); diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart index 7a609068b..5a05337fb 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.freezed.dart @@ -18,23 +18,24 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$Artifact { String get name => throw _privateConstructorUsedError; String? get version => throw _privateConstructorUsedError; - String get ext => throw _privateConstructorUsedError; + String? get ext => throw _privateConstructorUsedError; @optionalTypeArgs TResult when({ - required TResult Function(String name, String? version, String ext) file, - required TResult Function(String name, String? version, String ext) archive, + required TResult Function(String name, String? version, String? ext) file, + required TResult Function(String name, String? version, String? ext) + archive, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult? whenOrNull({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -67,7 +68,7 @@ mixin _$Artifact { abstract class $ArtifactCopyWith<$Res> { factory $ArtifactCopyWith(Artifact value, $Res Function(Artifact) then) = _$ArtifactCopyWithImpl<$Res>; - $Res call({String name, String? version, String ext}); + $Res call({String name, String? version, String? ext}); } /// @nodoc @@ -96,7 +97,7 @@ class _$ArtifactCopyWithImpl<$Res> implements $ArtifactCopyWith<$Res> { ext: ext == freezed ? _value.ext : ext // ignore: cast_nullable_to_non_nullable - as String, + as String?, )); } } @@ -108,7 +109,7 @@ abstract class _$$_ArtifactFileCopyWith<$Res> _$_ArtifactFile value, $Res Function(_$_ArtifactFile) then) = __$$_ArtifactFileCopyWithImpl<$Res>; @override - $Res call({String name, String? version, String ext}); + $Res call({String name, String? version, String? ext}); } /// @nodoc @@ -139,7 +140,7 @@ class __$$_ArtifactFileCopyWithImpl<$Res> extends _$ArtifactCopyWithImpl<$Res> ext: ext == freezed ? _value.ext : ext // ignore: cast_nullable_to_non_nullable - as String, + as String?, )); } } @@ -147,7 +148,7 @@ class __$$_ArtifactFileCopyWithImpl<$Res> extends _$ArtifactCopyWithImpl<$Res> /// @nodoc class _$_ArtifactFile extends _ArtifactFile { - const _$_ArtifactFile({required this.name, this.version, required this.ext}) + const _$_ArtifactFile({required this.name, this.version, this.ext}) : super._(); @override @@ -155,7 +156,7 @@ class _$_ArtifactFile extends _ArtifactFile { @override final String? version; @override - final String ext; + final String? ext; @override String toString() { @@ -187,8 +188,9 @@ class _$_ArtifactFile extends _ArtifactFile { @override @optionalTypeArgs TResult when({ - required TResult Function(String name, String? version, String ext) file, - required TResult Function(String name, String? version, String ext) archive, + required TResult Function(String name, String? version, String? ext) file, + required TResult Function(String name, String? version, String? ext) + archive, }) { return file(name, version, ext); } @@ -196,8 +198,8 @@ class _$_ArtifactFile extends _ArtifactFile { @override @optionalTypeArgs TResult? whenOrNull({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, }) { return file?.call(name, version, ext); } @@ -205,8 +207,8 @@ class _$_ArtifactFile extends _ArtifactFile { @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, required TResult orElse(), }) { if (file != null) { @@ -251,7 +253,7 @@ abstract class _ArtifactFile extends Artifact { const factory _ArtifactFile( {required final String name, final String? version, - required final String ext}) = _$_ArtifactFile; + final String? ext}) = _$_ArtifactFile; const _ArtifactFile._() : super._(); @override @@ -259,7 +261,7 @@ abstract class _ArtifactFile extends Artifact { @override String? get version; @override - String get ext; + String? get ext; @override @JsonKey(ignore: true) _$$_ArtifactFileCopyWith<_$_ArtifactFile> get copyWith => @@ -273,7 +275,7 @@ abstract class _$$_ArtifactArchiveCopyWith<$Res> _$_ArtifactArchive value, $Res Function(_$_ArtifactArchive) then) = __$$_ArtifactArchiveCopyWithImpl<$Res>; @override - $Res call({String name, String? version, String ext}); + $Res call({String name, String? version, String? ext}); } /// @nodoc @@ -305,7 +307,7 @@ class __$$_ArtifactArchiveCopyWithImpl<$Res> ext: ext == freezed ? _value.ext : ext // ignore: cast_nullable_to_non_nullable - as String, + as String?, )); } } @@ -313,8 +315,7 @@ class __$$_ArtifactArchiveCopyWithImpl<$Res> /// @nodoc class _$_ArtifactArchive extends _ArtifactArchive { - const _$_ArtifactArchive( - {required this.name, this.version, required this.ext}) + const _$_ArtifactArchive({required this.name, this.version, this.ext}) : super._(); @override @@ -322,7 +323,7 @@ class _$_ArtifactArchive extends _ArtifactArchive { @override final String? version; @override - final String ext; + final String? ext; @override String toString() { @@ -354,8 +355,9 @@ class _$_ArtifactArchive extends _ArtifactArchive { @override @optionalTypeArgs TResult when({ - required TResult Function(String name, String? version, String ext) file, - required TResult Function(String name, String? version, String ext) archive, + required TResult Function(String name, String? version, String? ext) file, + required TResult Function(String name, String? version, String? ext) + archive, }) { return archive(name, version, ext); } @@ -363,8 +365,8 @@ class _$_ArtifactArchive extends _ArtifactArchive { @override @optionalTypeArgs TResult? whenOrNull({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, }) { return archive?.call(name, version, ext); } @@ -372,8 +374,8 @@ class _$_ArtifactArchive extends _ArtifactArchive { @override @optionalTypeArgs TResult maybeWhen({ - TResult Function(String name, String? version, String ext)? file, - TResult Function(String name, String? version, String ext)? archive, + TResult Function(String name, String? version, String? ext)? file, + TResult Function(String name, String? version, String? ext)? archive, required TResult orElse(), }) { if (archive != null) { @@ -418,7 +420,7 @@ abstract class _ArtifactArchive extends Artifact { const factory _ArtifactArchive( {required final String name, final String? version, - required final String ext}) = _$_ArtifactArchive; + final String? ext}) = _$_ArtifactArchive; const _ArtifactArchive._() : super._(); @override @@ -426,7 +428,7 @@ abstract class _ArtifactArchive extends Artifact { @override String? get version; @override - String get ext; + String? get ext; @override @JsonKey(ignore: true) _$$_ArtifactArchiveCopyWith<_$_ArtifactArchive> get copyWith => diff --git a/packages/patrol_cli/lib/src/common/constants.dart b/packages/patrol_cli/lib/src/common/constants.dart index 2e9b7ddc0..a0a662740 100644 --- a/packages/patrol_cli/lib/src/common/constants.dart +++ b/packages/patrol_cli/lib/src/common/constants.dart @@ -1,7 +1,7 @@ import 'package:path/path.dart' as path; /// Version of Patrol CLI. Must be kept in sync with pubspec.yaml. -const version = '0.7.1+3'; +const globalVersion = '0.7.1+3'; const patrolPackage = 'patrol'; const patrolCliPackage = 'patrol_cli'; diff --git a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart index 35d6627b0..af891037b 100644 --- a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart +++ b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart @@ -3,6 +3,7 @@ import 'dart:io' show Process, Platform; import 'package:dispose_scope/dispose_scope.dart'; import 'package:logging/logging.dart'; +import 'package:path/path.dart' show basename; import 'package:patrol_cli/src/common/artifacts_repository.dart'; import 'package:patrol_cli/src/common/common.dart'; import 'package:patrol_cli/src/features/drive/constants.dart'; @@ -32,6 +33,7 @@ class IOSDriver { await _forwardPorts(port: port, deviceId: device.id); } + _logger.info(device); if (device.real) { await _runServerPhysical( deviceName: device.name, @@ -39,7 +41,7 @@ class IOSDriver { port: port, ); } else { - await _runServerSimulator(deviceId: device.id, port: port); + await _runServerOnDevice(deviceId: device.id, port: port); } } @@ -95,26 +97,28 @@ class IOSDriver { progress.complete('Forwarded ports'); } - Future _runServerSimulator({ + Future _runServerOnDevice({ required String port, required String deviceId, }) async { - // FIXME: artifact path to .app - _logger - .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); + // TODO: Use artifact appropriate for the Simulator architecture + final artifactPath = _artifactsRepository.iosSimulatorArmPath; + _logger.fine('Using artifact ${basename(artifactPath)}'); - // FIXME: fix artifact path to .app final installProcess = await Process.start( 'xcrun', [ 'simctl', 'install', deviceId, - _artifactsRepository.iosArtifactDirPath, + artifactPath, ], runInShell: true, ); + installProcess.listenStdOut(_logger.info).disposedBy(_disposeScope); + installProcess.listenStdErr(_logger.fine).disposedBy(_disposeScope); + await installProcess.exitCode; final runProcess = await Process.start( @@ -127,6 +131,11 @@ class IOSDriver { ], runInShell: true, ); + + runProcess.listenStdOut(_logger.info).disposedBy(_disposeScope); + runProcess.listenStdErr(_logger.fine).disposedBy(_disposeScope); + + await runProcess.exitCode; } /// Runs the server which is an infinite XCUITest. @@ -135,8 +144,8 @@ class IOSDriver { required String deviceName, required String deviceId, }) async { - _logger - .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); + // _logger + // .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); final process = await Process.start( 'xcodebuild', @@ -152,7 +161,8 @@ class IOSDriver { 'platform=iOS,name=$deviceName', ], runInShell: true, - workingDirectory: _artifactsRepository.iosArtifactDirPath, + // FIXME: re-add workingDirectory + //workingDirectory: _artifactsRepository.iosArtifactDirPath, environment: { ...Platform.environment, // See https://stackoverflow.com/a/69237460/7009800 diff --git a/packages/patrol_cli/lib/src/features/update/update_command.dart b/packages/patrol_cli/lib/src/features/update/update_command.dart index 3be39c07a..605b2d068 100644 --- a/packages/patrol_cli/lib/src/features/update/update_command.dart +++ b/packages/patrol_cli/lib/src/features/update/update_command.dart @@ -23,7 +23,7 @@ class UpdateCommand extends Command { Future run() async { final isLatestVersion = await _pubUpdater.isUpToDate( packageName: patrolCliPackage, - currentVersion: version, + currentVersion: globalVersion, ); if (!isLatestVersion) { @@ -33,7 +33,7 @@ class UpdateCommand extends Command { await _update(latestVersion); } else { _logger.info( - 'You already have the newest version of $patrolCliPackage ($version)', + 'You already have the newest version of $patrolCliPackage ($globalVersion)', ); } diff --git a/packages/patrol_cli/test/command_runner_test.dart b/packages/patrol_cli/test/command_runner_test.dart index ed29b8824..bb1ba9878 100644 --- a/packages/patrol_cli/test/command_runner_test.dart +++ b/packages/patrol_cli/test/command_runner_test.dart @@ -19,7 +19,7 @@ class MockArtifactsRepository extends Mock implements ArtifactsRepository {} const latestVersion = '0.0.0'; final updatePrompt = ''' -${lightYellow.wrap('Update available!')} ${lightCyan.wrap(version)} \u2192 ${lightCyan.wrap(latestVersion)} +${lightYellow.wrap('Update available!')} ${lightCyan.wrap(globalVersion)} \u2192 ${lightCyan.wrap(latestVersion)} Run ${lightCyan.wrap('patrol update')} to update'''; void main() { @@ -38,7 +38,7 @@ void main() { when( () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => version); + ).thenAnswer((_) async => globalVersion); commandRunner = PatrolCommandRunner( logger: logger, @@ -126,7 +126,7 @@ void main() { test('prints current version', () async { final result = await commandRunner.run(['--version']); expect(result, equals(0)); - verify(() => logger.info('patrol_cli v$version')).called(1); + verify(() => logger.info('patrol_cli v$globalVersion')).called(1); }); }); diff --git a/packages/patrol_cli/test/common/artifacts_repository_test.dart b/packages/patrol_cli/test/common/artifacts_repository_test.dart index 948c5e255..6eb9f38ee 100644 --- a/packages/patrol_cli/test/common/artifacts_repository_test.dart +++ b/packages/patrol_cli/test/common/artifacts_repository_test.dart @@ -5,7 +5,7 @@ import 'package:http/http.dart' as http; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' show join; import 'package:patrol_cli/src/common/artifacts_repository.dart'; -import 'package:patrol_cli/src/common/constants.dart' show version; +import 'package:patrol_cli/src/common/constants.dart' show globalVersion; import 'package:platform/platform.dart'; import 'package:test/test.dart'; @@ -49,7 +49,7 @@ void main() { zipDecoder = MockZipDecoder(); when(() => zipDecoder.decodeBytes(any())).thenAnswer((invocation) { - final archiveFile = ArchiveFile.string('ios-$version', '') + final archiveFile = ArchiveFile.string('ios-$globalVersion', '') ..isFile = false; return Archive()..addFile(archiveFile); @@ -72,18 +72,19 @@ void main() { test('are correct for release artifacts', () { expect( artifactsRepository.serverArtifactPath, - equals('/home/johndoe/.cache/patrol/server-$version.apk'), + equals('/home/johndoe/.cache/patrol/server-$globalVersion.apk'), ); expect( artifactsRepository.instrumentationArtifactPath, - equals('/home/johndoe/.cache/patrol/instrumentation-$version.apk'), + equals( + '/home/johndoe/.cache/patrol/instrumentation-$globalVersion.apk'), ); - expect( - artifactsRepository.iosArtifactDirPath, - equals('/home/johndoe/.cache/patrol/ios-$version'), - ); + // expect( + // artifactsRepository.iosArtifactDirPath, + // equals('/home/johndoe/.cache/patrol/ios-$version'), + // ); }); test('are correct for debug artifacts', () { @@ -113,7 +114,7 @@ void main() { test('returns false when only server.apk exists', () { fs - .file(join(_artifactPath, 'server-$version.apk')) + .file(join(_artifactPath, 'server-$globalVersion.apk')) .createSync(recursive: true); expect(artifactsRepository.areArtifactsPresent(), equals(false)); @@ -121,7 +122,7 @@ void main() { test('returns false when only instrumentation.apk exists', () { fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .createSync(recursive: true); expect(artifactsRepository.areArtifactsPresent(), equals(false)); @@ -129,10 +130,10 @@ void main() { test('returns true when server.apk and instrumentation.apk exist', () { fs - .file(join(_artifactPath, 'server-$version.apk')) + .file(join(_artifactPath, 'server-$globalVersion.apk')) .createSync(recursive: true); fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .createSync(); expect(artifactsRepository.areArtifactsPresent(), equals(true)); @@ -142,10 +143,10 @@ void main() { artifactsRepository.platform = _macos; fs - .file(join(_artifactPath, 'server-$version.apk')) + .file(join(_artifactPath, 'server-$globalVersion.apk')) .createSync(recursive: true); fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .createSync(); expect(artifactsRepository.areArtifactsPresent(), equals(false)); @@ -154,12 +155,12 @@ void main() { test('returns true when Android and iOS artifacts exist on macOS', () { artifactsRepository.platform = _macos; fs - .file(join(_artifactPath, 'server-$version.apk')) + .file(join(_artifactPath, 'server-$globalVersion.apk')) .createSync(recursive: true); fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .createSync(); - fs.directory(join(_artifactPath, 'ios-$version')).createSync(); + fs.directory(join(_artifactPath, 'ios-$globalVersion')).createSync(); expect(artifactsRepository.areArtifactsPresent(), equals(true)); }); @@ -188,7 +189,7 @@ void main() { await artifactsRepository.downloadArtifacts(); expect( - fs.directory(join(_artifactPath, 'ios-$version')).existsSync(), + fs.directory(join(_artifactPath, 'ios-$globalVersion')).existsSync(), isFalse, ); verify(() => httpClient.get(any())).called(2); @@ -198,13 +199,15 @@ void main() { await artifactsRepository.downloadArtifacts(); expect( - fs.file(join(_artifactPath, 'server-$version.apk')).existsSync(), + fs + .file(join(_artifactPath, 'server-$globalVersion.apk')) + .existsSync(), isTrue, ); expect( fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .existsSync(), isTrue, ); @@ -217,19 +220,21 @@ void main() { await artifactsRepository.downloadArtifacts(); expect( - fs.file(join(_artifactPath, 'server-$version.apk')).existsSync(), + fs + .file(join(_artifactPath, 'server-$globalVersion.apk')) + .existsSync(), isTrue, ); expect( fs - .file(join(_artifactPath, 'instrumentation-$version.apk')) + .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) .existsSync(), isTrue, ); expect( - fs.directory(join(_artifactPath, 'ios-$version')).existsSync(), + fs.directory(join(_artifactPath, 'ios-$globalVersion')).existsSync(), isTrue, ); From 420fdaf4194ca109a1dbb832bf45ed0d3013865a Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 12:20:19 +0200 Subject: [PATCH 07/15] mark iOS Xcode project as deprecated --- packages/patrol_cli/lib/src/common/artifacts_repository.dart | 1 + packages/patrol_cli/test/common/artifacts_repository_test.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 52650f5d1..f70118355 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -28,6 +28,7 @@ class Artifacts { // iOS + @Deprecated('Remove once code signing for iosDevice works') static const ios = Artifact.archive( name: 'ios', version: globalVersion, diff --git a/packages/patrol_cli/test/common/artifacts_repository_test.dart b/packages/patrol_cli/test/common/artifacts_repository_test.dart index 6eb9f38ee..011c6aaca 100644 --- a/packages/patrol_cli/test/common/artifacts_repository_test.dart +++ b/packages/patrol_cli/test/common/artifacts_repository_test.dart @@ -101,7 +101,7 @@ void main() { ); expect( - artifactsRepository.iosArtifactDirPath, + artifactsRepository.iosPath, equals('/home/johndoe/.cache/patrol/ios'), ); }); From 9dc4408fc49049dc59d874da97aa5ddc62624918 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 12:57:15 +0200 Subject: [PATCH 08/15] work around ios project zip --- .../lib/src/common/artifacts_repository.dart | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index f70118355..3e6f06bd6 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -281,14 +281,23 @@ class ArtifactsRepository { _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); } + /// Extract the artifact and removes the archive upon completion. Future _extractArtifact(Artifact artifact) async { - final archievePath = join(artifactPath, artifact.archiveName); - final bytes = await _fs.file(archievePath).readAsBytes(); + final archivePath = join(artifactPath, artifact.archiveName); + final archiveFile = _fs.file(archivePath); + final bytes = await archiveFile.readAsBytes(); final archive = _zipDecoder.decodeBytes(bytes); + // TODO: Remove once iOS project is not needed + var newArtifactPath = artifactPath; + if (artifact.archiveName.startsWith('ios-')) { + newArtifactPath = join(artifactPath, artifact.filename); + print('newArtifactPath: $newArtifactPath'); + } + for (final archiveFile in archive) { final filename = archiveFile.name; - final extractPath = join(artifactPath, filename); + final extractPath = join(newArtifactPath, filename); if (archiveFile.isFile) { final data = archiveFile.content as List; final newFile = _fs.file(extractPath); @@ -299,6 +308,8 @@ class ArtifactsRepository { await directory.create(recursive: true); } } + + await archiveFile.delete(); } /// Create a file at [fullPath], recursively creating non-existent From 801f6fe1c68206d824f5457dec5a15b11cbb1015 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 12:58:52 +0200 Subject: [PATCH 09/15] remove commented-out code --- .../lib/src/common/artifacts_repository.dart | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index 3e6f06bd6..ae8a1873f 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -193,6 +193,7 @@ class ArtifactsRepository { return join(artifactPath, artifact.filename); } + @Deprecated('Migrate to iosDevicePath once it works') String get iosPath { final artifact = debug ? Artifacts.ios.debug : Artifacts.ios; @@ -292,7 +293,6 @@ class ArtifactsRepository { var newArtifactPath = artifactPath; if (artifact.archiveName.startsWith('ios-')) { newArtifactPath = join(artifactPath, artifact.filename); - print('newArtifactPath: $newArtifactPath'); } for (final archiveFile in archive) { @@ -320,70 +320,3 @@ class ArtifactsRepository { return _fs.file(fullPath)..createSync(); } } - -/* class _ArtifactPaths { - const _ArtifactPaths(this._artifactPath); - - final String _artifactPath; - - /// Returns a URI where [artifact] can be downloaded from. - /// - /// [artifact] must be in the form of `$artifact-$version.$extension`, for - /// example: `server-1.0.0.apk` or `ios-4.2.0.zip`. - Uri getUriForArtifact(String artifact) { - return Uri.parse( - 'https://github.com/leancodepl/patrol/releases/download/patrol_cli-v$version/$artifact', - ); - } - - String get androidServerFile => 'server-$version.apk'; - - String get androidInstrumentationFile => 'instrumentation-$version.apk'; - - String get androidServerDebugFile => 'server.apk'; - - String get androidInstrumentationDebugFile => 'instrumentation.apk'; - - String get iosProjectDir => 'ios-$version'; - - String get iosProjectZip => 'ios-$version.zip'; - - String get iosProjectDebugDir => 'ios'; - - String get iosAutomatorSimAmdZip { - return 'AutomatorServer-iphonesimulator-x86_64-$version.zip'; - } - - String get iosAutomatorSimArmZip { - return 'AutomatorServer-iphonesimulator-arm64-$version.zip'; - } - - String get serverArtifactPath { - return join(_artifactPath, androidServerFile); - } - - String get debugServerArtifactPath { - return join(_artifactPath, androidServerDebugFile); - } - - String get instrumentationArtifactPath { - return join(_artifactPath, androidInstrumentationFile); - } - - String get debugInstrumentationArtifactPath { - return join(_artifactPath, androidInstrumentationDebugFile); - } - - String get iosZipPath { - return join(_artifactPath, iosProjectZip); - } - - String get iosDirPath { - return join(_artifactPath, iosProjectDir); - } - - String get iosDirDebugPath { - return join(_artifactPath, iosProjectDebugDir); - } -} - */ From 5b8d4ed059c61806128a44fb07c28df8f1b8cba1 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 13:23:25 +0200 Subject: [PATCH 10/15] fix tests --- .../common/artifacts_repository_test.dart | 128 +++++++++++++++--- .../test/features/drive/test_finder_test.dart | 26 ++-- 2 files changed, 124 insertions(+), 30 deletions(-) diff --git a/packages/patrol_cli/test/common/artifacts_repository_test.dart b/packages/patrol_cli/test/common/artifacts_repository_test.dart index 011c6aaca..1109c897a 100644 --- a/packages/patrol_cli/test/common/artifacts_repository_test.dart +++ b/packages/patrol_cli/test/common/artifacts_repository_test.dart @@ -29,6 +29,43 @@ class MockHttpClient extends Mock implements http.Client {} class MockZipDecoder extends Mock implements ZipDecoder {} +extension _FileSystemX on FileSystem { + void _createAndroidArtifacts() { + file( + join(_artifactPath, 'server-$globalVersion.apk'), + ).createSync(recursive: true); + + file( + join(_artifactPath, 'instrumentation-$globalVersion.apk'), + ).createSync(); + } + + void _createIOSArtifacts() { + directory(join(_artifactPath, 'ios-$globalVersion')).createSync(); + + directory( + join( + _artifactPath, + 'AutomatorServer-iphonesimulator-arm64-$globalVersion.app', + ), + ).createSync(); + + directory( + join( + _artifactPath, + 'AutomatorServer-iphonesimulator-x86_64-$globalVersion.app', + ), + ).createSync(); + + directory( + join( + _artifactPath, + 'AutomatorServer-iphoneos-arm64-$globalVersion.app', + ), + ).createSync(); + } +} + void main() { setUpFakes(); @@ -48,11 +85,30 @@ void main() { ); zipDecoder = MockZipDecoder(); - when(() => zipDecoder.decodeBytes(any())).thenAnswer((invocation) { - final archiveFile = ArchiveFile.string('ios-$globalVersion', '') + when(() => zipDecoder.decodeBytes(any())).thenAnswer((_) { + final iosArchive = ArchiveFile.string('ios-$globalVersion', '') ..isFile = false; - return Archive()..addFile(archiveFile); + final iosDeviceArchive = ArchiveFile.string( + 'AutomatorServer-iphoneos-arm64-$globalVersion.app', + '', + )..isFile = false; + + final iosSimulatorArmArchive = ArchiveFile.string( + 'AutomatorServer-iphonesimulator-arm64-$globalVersion.app', + '', + )..isFile = false; + + final iosSimulatorAmdArchive = ArchiveFile.string( + 'AutomatorServer-iphonesimulator-x86_64-$globalVersion.app', + '', + )..isFile = false; + + return Archive() + ..addFile(iosArchive) + ..addFile(iosDeviceArchive) + ..addFile(iosSimulatorArmArchive) + ..addFile(iosSimulatorAmdArchive); }); fs = MemoryFileSystem.test(); @@ -78,13 +134,14 @@ void main() { expect( artifactsRepository.instrumentationArtifactPath, equals( - '/home/johndoe/.cache/patrol/instrumentation-$globalVersion.apk'), + '/home/johndoe/.cache/patrol/instrumentation-$globalVersion.apk', + ), ); - // expect( - // artifactsRepository.iosArtifactDirPath, - // equals('/home/johndoe/.cache/patrol/ios-$version'), - // ); + expect( + artifactsRepository.iosPath, + equals('/home/johndoe/.cache/patrol/ios-$globalVersion'), + ); }); test('are correct for debug artifacts', () { @@ -142,25 +199,17 @@ void main() { test('returns false when only Android artifacts exist on macOS', () { artifactsRepository.platform = _macos; - fs - .file(join(_artifactPath, 'server-$globalVersion.apk')) - .createSync(recursive: true); - fs - .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) - .createSync(); + fs._createAndroidArtifacts(); expect(artifactsRepository.areArtifactsPresent(), equals(false)); }); test('returns true when Android and iOS artifacts exist on macOS', () { artifactsRepository.platform = _macos; + fs - .file(join(_artifactPath, 'server-$globalVersion.apk')) - .createSync(recursive: true); - fs - .file(join(_artifactPath, 'instrumentation-$globalVersion.apk')) - .createSync(); - fs.directory(join(_artifactPath, 'ios-$globalVersion')).createSync(); + .._createAndroidArtifacts() + .._createIOSArtifacts(); expect(artifactsRepository.areArtifactsPresent(), equals(true)); }); @@ -238,7 +287,44 @@ void main() { isTrue, ); - verify(() => httpClient.get(any())).called(3); + expect( + fs + .directory( + join( + _artifactPath, + 'AutomatorServer-iphonesimulator-arm64-$globalVersion.app', + ), + ) + .existsSync(), + isTrue, + reason: "iOS Simulator arm64 artifact doesn't exist", + ); + + expect( + fs + .directory( + join( + _artifactPath, + 'AutomatorServer-iphonesimulator-x86_64-$globalVersion.app', + ), + ) + .existsSync(), + isTrue, + ); + + expect( + fs + .directory( + join( + _artifactPath, + 'AutomatorServer-iphoneos-arm64-$globalVersion.app', + ), + ) + .existsSync(), + isTrue, + ); + + verify(() => httpClient.get(any())).called(6); }); }); }); diff --git a/packages/patrol_cli/test/features/drive/test_finder_test.dart b/packages/patrol_cli/test/features/drive/test_finder_test.dart index 195846de1..7ad384b1d 100644 --- a/packages/patrol_cli/test/features/drive/test_finder_test.dart +++ b/packages/patrol_cli/test/features/drive/test_finder_test.dart @@ -30,23 +30,31 @@ void main() { fs.file('integration_test/app_test.dart').createSync(recursive: true); fs.file('integration_test/permission_test.dart').createSync(); - expect(testFinder.findAllTests(), [ - '${wd.path}/integration_test/app_test.dart', - '${wd.path}/integration_test/permission_test.dart' - ]); + expect( + testFinder.findAllTests(), + equals([ + '${wd.path}/integration_test/app_test.dart', + '${wd.path}/integration_test/permission_test.dart' + ]), + ); }); test('finds tests recursively', () { fs.file('integration_test/app_test.dart').createSync(recursive: true); fs.file('integration_test/permission_test.dart').createSync(); + fs.file('integration_test/webview_test.dart').createSync(); fs .file('integration_test/auth/sign_in_test.dart') .createSync(recursive: true); - expect(testFinder.findAllTests(), [ - '${wd.path}/integration_test/app_test.dart', - '${wd.path}/integration_test/permission_test.dart', - '${wd.path}/integration_test/auth/sign_in_test.dart', - ]); + expect( + testFinder.findAllTests(), + equals([ + '${wd.path}/integration_test/app_test.dart', + '${wd.path}/integration_test/permission_test.dart', + '${wd.path}/integration_test/webview_test.dart', + '${wd.path}/integration_test/auth/sign_in_test.dart', + ]), + ); }); } From 81e888627ecae98649a3d9d7ab662295cd2de4d9 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 17:40:48 +0200 Subject: [PATCH 11/15] uninstall artifacts after test --- .../features/drive/platform/ios_driver.dart | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart index af891037b..203a2bda8 100644 --- a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart +++ b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart @@ -35,13 +35,13 @@ class IOSDriver { _logger.info(device); if (device.real) { - await _runServerPhysical( + await _runServerOnDevice( deviceName: device.name, deviceId: device.id, port: port, ); } else { - await _runServerOnDevice(deviceId: device.id, port: port); + await _runServerOnSimulator(deviceId: device.id, port: port); } } @@ -97,7 +97,7 @@ class IOSDriver { progress.complete('Forwarded ports'); } - Future _runServerOnDevice({ + Future _runServerOnSimulator({ required String port, required String deviceId, }) async { @@ -116,6 +116,18 @@ class IOSDriver { runInShell: true, ); + _disposeScope.addDispose(() async { + await Process.run( + 'xcrun', + [ + 'simctl', + 'uninstall', + deviceId, + 'pl.leancode.AutomatorServerUITests.xctrunner', + ], + ); + }); + installProcess.listenStdOut(_logger.info).disposedBy(_disposeScope); installProcess.listenStdErr(_logger.fine).disposedBy(_disposeScope); @@ -139,13 +151,13 @@ class IOSDriver { } /// Runs the server which is an infinite XCUITest. - Future _runServerPhysical({ + Future _runServerOnDevice({ required String port, required String deviceName, required String deviceId, }) async { - // _logger - // .fine('Using artifact in ${_artifactsRepository.iosArtifactDirPath}'); + final artifactPath = _artifactsRepository.iosPath; + _logger.fine('Using artifact ${basename(artifactPath)}'); final process = await Process.start( 'xcodebuild', @@ -161,8 +173,7 @@ class IOSDriver { 'platform=iOS,name=$deviceName', ], runInShell: true, - // FIXME: re-add workingDirectory - //workingDirectory: _artifactsRepository.iosArtifactDirPath, + workingDirectory: artifactPath, environment: { ...Platform.environment, // See https://stackoverflow.com/a/69237460/7009800 From 66302735cfcce96ecae835aca5eb2db4a772ba9a Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 18:22:13 +0200 Subject: [PATCH 12/15] add `ArtifactRepository.iosSimulatorPath` --- .../lib/src/common/artifacts_repository.dart | 81 ++++++++++--------- .../features/drive/platform/ios_driver.dart | 55 ++++++------- 2 files changed, 69 insertions(+), 67 deletions(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index ae8a1873f..acbbf74f1 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -1,9 +1,12 @@ +import 'dart:io' show Process; + import 'package:archive/archive.dart'; import 'package:file/file.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:http/http.dart' as http; import 'package:path/path.dart' show join, dirname; import 'package:patrol_cli/src/common/constants.dart' show globalVersion; +import 'package:patrol_cli/src/common/extensions/process.dart'; import 'package:platform/platform.dart'; part 'artifacts_repository.freezed.dart'; @@ -28,6 +31,7 @@ class Artifacts { // iOS + // TODO(bartekpacia): Remove once code signing for iosDevice works @Deprecated('Remove once code signing for iosDevice works') static const ios = Artifact.archive( name: 'ios', @@ -69,7 +73,8 @@ class Artifact with _$Artifact { const Artifact._(); - String get filename { + /// Nmae of the file once downloaded and extracted. + String get localFileName { var result = name; if (version != null) { @@ -83,23 +88,14 @@ class Artifact with _$Artifact { return result; } - /// Name of the file that was originally downloaded. - String get remoteName { + /// Name of the file while hosted. + String get remoteFileName { return map( - file: (artifact) => artifact.filename, + file: (artifact) => artifact.localFileName, archive: (archive) => '$name-$version.zip', ); } - String get archiveName { - return map( - file: (_) => throw StateError( - 'archiveName is not applicable for file artifacts', - ), - archive: (artifact) => '${artifact.name}-${artifact.version}.zip', - ); - } - /// Returns an unversioned (i.e debug) variant of this artifact. Artifact get debug => copyWith(version: null); @@ -111,18 +107,8 @@ class Artifact with _$Artifact { } const repo = 'https://github.com/leancodepl/patrol'; - - return map( - file: (file) { - return Uri.parse( - '$repo/releases/download/patrol_cli-v$version/$filename', - ); - }, - archive: (archive) { - return Uri.parse( - '$repo/releases/download/patrol_cli-v$version/$name-$version.zip', - ); - }, + return Uri.parse( + '$repo/releases/download/patrol_cli-v$version/$remoteFileName', ); } } @@ -182,7 +168,7 @@ class ArtifactsRepository { String get serverArtifactPath { final artifact = debug ? Artifacts.androidApp.debug : Artifacts.androidApp; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); } String get instrumentationArtifactPath { @@ -190,34 +176,57 @@ class ArtifactsRepository { ? Artifacts.androidInstrumentation.debug : Artifacts.androidInstrumentation; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); } @Deprecated('Migrate to iosDevicePath once it works') String get iosPath { final artifact = debug ? Artifacts.ios.debug : Artifacts.ios; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); } String get iosDevicePath { final artifact = debug ? Artifacts.iosDevice.debug : Artifacts.iosDevice; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); + } + + /// Returns path to the iOS artifact appropriate for the architecture of the + /// machine. + /// + /// Known issues on Apple Silicon: + /// + /// * If Simulator.app is run with Rosetta 2, then artifact for x86_64 + /// architecture is used, and it won't work + /// + /// * If Terminal.app is run with Rosetta 2, then artifact for x86_64 + /// architecture is used even if the Simulator.app is running without + /// Rosetta 2, and it won't work + String get iosSimulatorPath { + final processResult = Process.runSync('uname', ['-m']); + final arch = processResult.stdOut.trim(); + if (arch == 'arm64') { + return iosSimulatorArmPath; + } else if (arch == 'x86_64') { + return iosSimulatorAmdPath; + } else { + throw StateError('architecture $arch is not supported'); + } } String get iosSimulatorArmPath { final artifact = debug ? Artifacts.iosSimulatorArm.debug : Artifacts.iosSimulatorArm; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); } String get iosSimulatorAmdPath { final artifact = debug ? Artifacts.iosSimulatorAmd.debug : Artifacts.iosSimulatorAmd; - return join(artifactPath, artifact.filename); + return join(artifactPath, artifact.localFileName); } /// Returns true if artifacts for the current patrol_cli version are present @@ -278,21 +287,21 @@ class ArtifactsRepository { throw Exception('Failed to download $artifact from ${artifact.uri}'); } - final p = join(artifactPath, artifact.remoteName); + final p = join(artifactPath, artifact.remoteFileName); _createFileRecursively(p).writeAsBytesSync(response.bodyBytes); } /// Extract the artifact and removes the archive upon completion. Future _extractArtifact(Artifact artifact) async { - final archivePath = join(artifactPath, artifact.archiveName); + final archivePath = join(artifactPath, artifact.remoteFileName); final archiveFile = _fs.file(archivePath); final bytes = await archiveFile.readAsBytes(); final archive = _zipDecoder.decodeBytes(bytes); - // TODO: Remove once iOS project is not needed + // TODO(bartekpacia): Remove once code signing for iosDevice works var newArtifactPath = artifactPath; - if (artifact.archiveName.startsWith('ios-')) { - newArtifactPath = join(artifactPath, artifact.filename); + if (artifact.remoteFileName.startsWith('ios-')) { + newArtifactPath = join(artifactPath, artifact.localFileName); } for (final archiveFile in archive) { diff --git a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart index 203a2bda8..dcaaba600 100644 --- a/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart +++ b/packages/patrol_cli/lib/src/features/drive/platform/ios_driver.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'dart:io' show Process, Platform; +import 'dart:io' show Platform, Process; import 'package:dispose_scope/dispose_scope.dart'; import 'package:logging/logging.dart'; @@ -24,6 +24,8 @@ class IOSDriver { final ArtifactsRepository _artifactsRepository; final Logger _logger; + static const _bundleId = 'pl.leancode.AutomatorServerUITests.xctrunner'; + Future run({ required String port, required Device device, @@ -31,17 +33,9 @@ class IOSDriver { }) async { if (device.real) { await _forwardPorts(port: port, deviceId: device.id); - } - - _logger.info(device); - if (device.real) { - await _runServerOnDevice( - deviceName: device.name, - deviceId: device.id, - port: port, - ); + await _runServerOnDevice(device: device, port: port); } else { - await _runServerOnSimulator(deviceId: device.id, port: port); + await _runServerOnSimulator(device: device, port: port); } } @@ -98,11 +92,10 @@ class IOSDriver { } Future _runServerOnSimulator({ + required Device device, required String port, - required String deviceId, }) async { - // TODO: Use artifact appropriate for the Simulator architecture - final artifactPath = _artifactsRepository.iosSimulatorArmPath; + final artifactPath = _artifactsRepository.iosSimulatorPath; _logger.fine('Using artifact ${basename(artifactPath)}'); final installProcess = await Process.start( @@ -110,11 +103,13 @@ class IOSDriver { [ 'simctl', 'install', - deviceId, + device.id, artifactPath, ], runInShell: true, - ); + ) + ..listenStdOut(_logger.info).disposedBy(_disposeScope) + ..listenStdErr(_logger.info).disposedBy(_disposeScope); _disposeScope.addDispose(() async { await Process.run( @@ -122,14 +117,14 @@ class IOSDriver { [ 'simctl', 'uninstall', - deviceId, - 'pl.leancode.AutomatorServerUITests.xctrunner', + device.id, + _bundleId, ], + runInShell: true, ); - }); - installProcess.listenStdOut(_logger.info).disposedBy(_disposeScope); - installProcess.listenStdErr(_logger.fine).disposedBy(_disposeScope); + _logger.fine('Uninstalled $_bundleId'); + }); await installProcess.exitCode; @@ -138,23 +133,21 @@ class IOSDriver { [ 'simctl', 'launch', - deviceId, - 'pl.leancode.AutomatorServerUITests.xctrunner', + device.id, + _bundleId, ], runInShell: true, - ); - - runProcess.listenStdOut(_logger.info).disposedBy(_disposeScope); - runProcess.listenStdErr(_logger.fine).disposedBy(_disposeScope); + ) + ..listenStdOut(_logger.info).disposedBy(_disposeScope) + ..listenStdErr(_logger.info).disposedBy(_disposeScope); await runProcess.exitCode; } /// Runs the server which is an infinite XCUITest. Future _runServerOnDevice({ + required Device device, required String port, - required String deviceName, - required String deviceId, }) async { final artifactPath = _artifactsRepository.iosPath; _logger.fine('Using artifact ${basename(artifactPath)}'); @@ -170,7 +163,7 @@ class IOSDriver { '-sdk', 'iphoneos', '-destination', - 'platform=iOS,name=$deviceName', + 'platform=iOS,name=${device.name}', ], runInShell: true, workingDirectory: artifactPath, @@ -187,7 +180,7 @@ class IOSDriver { const bundleId = 'pl.leancode.AutomatorServerUITests.xctrunner'; final process = await Process.run( 'ideviceinstaller', - ['--uninstall', bundleId, '--udid', deviceId], + ['--uninstall', bundleId, '--udid', device.id], runInShell: true, ); From f3321f01cc1c9908744c631e0a4eae6a43230873 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 18 Oct 2022 19:42:44 +0200 Subject: [PATCH 13/15] make update command also downloadn new artifacts --- .../patrol_cli/lib/src/command_runner.dart | 8 +++- .../lib/src/common/artifacts_repository.dart | 41 +++++++++++-------- .../src/features/update/update_command.dart | 33 ++++++++++----- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/packages/patrol_cli/lib/src/command_runner.dart b/packages/patrol_cli/lib/src/command_runner.dart index 6cfaa9829..ee9b41b93 100644 --- a/packages/patrol_cli/lib/src/command_runner.dart +++ b/packages/patrol_cli/lib/src/command_runner.dart @@ -116,7 +116,13 @@ class PatrolCommandRunner extends CommandRunner { logger: _logger, ), ); - addCommand(UpdateCommand(pubUpdater: _pubUpdater, logger: _logger)); + addCommand( + UpdateCommand( + pubUpdater: _pubUpdater, + artifactsRepository: _artifactsRepository, + logger: _logger, + ), + ); argParser ..addFlag( diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index acbbf74f1..eafa53891 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -16,7 +16,10 @@ class Artifacts { // Android - // The dummy app under test. + /// The dummy app under test. + /// + /// See also: + /// * https://github.com/leancodepl/patrol/pull/465 static const androidApp = Artifact.file( name: 'server', version: globalVersion, @@ -90,6 +93,10 @@ class Artifact with _$Artifact { /// Name of the file while hosted. String get remoteFileName { + if (version == null) { + throw StateError('unversioned artifacts do not have remoteFileName'); + } + return map( file: (artifact) => artifact.localFileName, archive: (archive) => '$name-$version.zip', @@ -255,33 +262,35 @@ class ArtifactsRepository { } } - /// Downloads artifacts for the current patrol_cli version. - Future downloadArtifacts({String? ver}) async { - ver ??= globalVersion; + /// Downloads and extracts artifacts for [version] of patrol_cli. + Future downloadArtifacts({String? version = globalVersion}) async { + final androidArtifacts = [ + Artifacts.androidApp, + Artifacts.androidInstrumentation, + ].map((artifact) => artifact.copyWith(version: version)); + + final iosArtifacts = [ + Artifacts.ios, + Artifacts.iosDevice, + Artifacts.iosSimulatorArm, + Artifacts.iosSimulatorAmd, + ].map((artifact) => artifact.copyWith(version: version)); await Future.wait([ - _downloadArtifact(Artifacts.androidApp.copyWith(version: ver)), - _downloadArtifact( - Artifacts.androidInstrumentation.copyWith(version: ver), - ), + ...androidArtifacts.map(_downloadArtifact), if (platform.isMacOS) ...[ - _downloadArtifact(Artifacts.ios), - _downloadArtifact(Artifacts.iosDevice), - _downloadArtifact(Artifacts.iosSimulatorArm), - _downloadArtifact(Artifacts.iosSimulatorAmd), + ...iosArtifacts.map(_downloadArtifact), ], ]); if (platform.isMacOS) { - await _extractArtifact(Artifacts.ios); - await _extractArtifact(Artifacts.iosDevice); - await _extractArtifact(Artifacts.iosSimulatorArm); - await _extractArtifact(Artifacts.iosSimulatorAmd); + await Future.wait(iosArtifacts.map(_extractArtifact)); } } Future _downloadArtifact(Artifact artifact) async { final response = await _httpClient.get(artifact.uri); + print('downloading ${artifact.uri.path}'); if (response.statusCode != 200) { throw Exception('Failed to download $artifact from ${artifact.uri}'); diff --git a/packages/patrol_cli/lib/src/features/update/update_command.dart b/packages/patrol_cli/lib/src/features/update/update_command.dart index 605b2d068..02ba15d08 100644 --- a/packages/patrol_cli/lib/src/features/update/update_command.dart +++ b/packages/patrol_cli/lib/src/features/update/update_command.dart @@ -1,16 +1,20 @@ import 'package:args/command_runner.dart'; import 'package:logging/logging.dart'; +import 'package:patrol_cli/src/common/artifacts_repository.dart'; import 'package:patrol_cli/src/common/common.dart'; import 'package:pub_updater/pub_updater.dart'; class UpdateCommand extends Command { UpdateCommand({ required PubUpdater pubUpdater, + required ArtifactsRepository artifactsRepository, required Logger logger, }) : _pubUpdater = pubUpdater, + _artifactsRepository = artifactsRepository, _logger = logger; final PubUpdater _pubUpdater; + final ArtifactsRepository _artifactsRepository; final Logger _logger; @override @@ -27,10 +31,10 @@ class UpdateCommand extends Command { ); if (!isLatestVersion) { - final latestVersion = await _pubUpdater.getLatestVersion( - patrolCliPackage, - ); - await _update(latestVersion); + final latestVersion = + await _pubUpdater.getLatestVersion(patrolCliPackage); + await _updateTool(latestVersion); + await _downloadArtifacts(latestVersion); } else { _logger.info( 'You already have the newest version of $patrolCliPackage ($globalVersion)', @@ -40,18 +44,27 @@ class UpdateCommand extends Command { return 0; } - Future _update(String latestVersion) async { + Future _updateTool(String ver) async { final progress = _logger.progress( - 'Updating $patrolCliPackage to version $latestVersion', + 'Updating $patrolCliPackage to version $ver', ); try { await _pubUpdater.update(packageName: patrolCliPackage); - progress.complete('Updated $patrolCliPackage to version $latestVersion'); + progress.complete('Updated $patrolCliPackage to version $ver'); } catch (err) { - progress.fail( - 'Failed to update $patrolCliPackage to version $latestVersion', - ); + progress.fail('Failed to update $patrolCliPackage to version $ver'); + rethrow; + } + } + + Future _downloadArtifacts(String ver) async { + final progress = _logger.progress('Downloading artifacts for version $ver'); + try { + await _artifactsRepository.downloadArtifacts(version: ver); + progress.complete('Downloaded artifacts for version $ver'); + } catch (err) { + progress.fail('Failed to download artifacts for version $ver'); rethrow; } } From 7e9fb1f6bdd395421b17b62a115eb4097efc8fef Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Wed, 19 Oct 2022 09:02:23 +0200 Subject: [PATCH 14/15] fix typo --- packages/patrol_cli/lib/src/common/artifacts_repository.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patrol_cli/lib/src/common/artifacts_repository.dart b/packages/patrol_cli/lib/src/common/artifacts_repository.dart index eafa53891..85e1ffc8e 100644 --- a/packages/patrol_cli/lib/src/common/artifacts_repository.dart +++ b/packages/patrol_cli/lib/src/common/artifacts_repository.dart @@ -76,7 +76,7 @@ class Artifact with _$Artifact { const Artifact._(); - /// Nmae of the file once downloaded and extracted. + /// Name of the file once downloaded and extracted. String get localFileName { var result = name; From b5e02efa43b016c35308c0ce94c344513521f322 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Wed, 19 Oct 2022 09:11:30 +0200 Subject: [PATCH 15/15] remove unnecessary changes to tests --- .../test/features/drive/test_finder_test.dart | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/patrol_cli/test/features/drive/test_finder_test.dart b/packages/patrol_cli/test/features/drive/test_finder_test.dart index 7ad384b1d..195846de1 100644 --- a/packages/patrol_cli/test/features/drive/test_finder_test.dart +++ b/packages/patrol_cli/test/features/drive/test_finder_test.dart @@ -30,31 +30,23 @@ void main() { fs.file('integration_test/app_test.dart').createSync(recursive: true); fs.file('integration_test/permission_test.dart').createSync(); - expect( - testFinder.findAllTests(), - equals([ - '${wd.path}/integration_test/app_test.dart', - '${wd.path}/integration_test/permission_test.dart' - ]), - ); + expect(testFinder.findAllTests(), [ + '${wd.path}/integration_test/app_test.dart', + '${wd.path}/integration_test/permission_test.dart' + ]); }); test('finds tests recursively', () { fs.file('integration_test/app_test.dart').createSync(recursive: true); fs.file('integration_test/permission_test.dart').createSync(); - fs.file('integration_test/webview_test.dart').createSync(); fs .file('integration_test/auth/sign_in_test.dart') .createSync(recursive: true); - expect( - testFinder.findAllTests(), - equals([ - '${wd.path}/integration_test/app_test.dart', - '${wd.path}/integration_test/permission_test.dart', - '${wd.path}/integration_test/webview_test.dart', - '${wd.path}/integration_test/auth/sign_in_test.dart', - ]), - ); + expect(testFinder.findAllTests(), [ + '${wd.path}/integration_test/app_test.dart', + '${wd.path}/integration_test/permission_test.dart', + '${wd.path}/integration_test/auth/sign_in_test.dart', + ]); }); }