| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| import 'dart:io' hide Platform; |
| import 'dart:typed_data'; |
| |
| import 'package:path/path.dart' as path; |
| import 'package:platform/platform.dart' show FakePlatform, Platform; |
| |
| import '../../../packages/flutter_tools/test/src/fake_process_manager.dart'; |
| import '../prepare_package.dart'; |
| import 'common.dart'; |
| |
| void main() { |
| const String testRef = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'; |
| test('Throws on missing executable', () async { |
| // Uses a *real* process manager, since we want to know what happens if |
| // it can't find an executable. |
| final ProcessRunner processRunner = ProcessRunner(subprocessOutput: false); |
| expect( |
| expectAsync1((List<String> commandLine) async { |
| return processRunner.runProcess(commandLine); |
| })(<String>['this_executable_better_not_exist_2857632534321']), |
| throwsA(isA<PreparePackageException>())); |
| |
| await expectLater( |
| () => processRunner.runProcess(<String>['this_executable_better_not_exist_2857632534321']), |
| throwsA(isA<PreparePackageException>().having( |
| (PreparePackageException error) => error.message, |
| 'message', |
| contains('ProcessException: Failed to find "this_executable_better_not_exist_2857632534321" in the search path'), |
| )), |
| ); |
| }); |
| for (final String platformName in <String>[Platform.macOS, Platform.linux, Platform.windows]) { |
| final FakePlatform platform = FakePlatform( |
| operatingSystem: platformName, |
| environment: <String, String>{ |
| 'DEPOT_TOOLS': path.join('D:', 'depot_tools'), |
| }, |
| ); |
| group('ProcessRunner for $platform', () { |
| test('Returns stdout', () async { |
| final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[ |
| const FakeCommand( |
| command: <String>['echo', 'test',], |
| stdout: 'output', |
| stderr: 'error', |
| ), |
| ]); |
| final ProcessRunner processRunner = ProcessRunner( |
| subprocessOutput: false, platform: platform, processManager: fakeProcessManager); |
| final String output = await processRunner.runProcess(<String>['echo', 'test']); |
| expect(output, equals('output')); |
| }); |
| test('Throws on process failure', () async { |
| final FakeProcessManager fakeProcessManager = FakeProcessManager.list(<FakeCommand>[ |
| const FakeCommand( |
| command: <String>['echo', 'test',], |
| stdout: 'output', |
| stderr: 'error', |
| exitCode: -1, |
| ), |
| ]); |
| final ProcessRunner processRunner = ProcessRunner( |
| subprocessOutput: false, platform: platform, processManager: fakeProcessManager); |
| expect( |
| expectAsync1((List<String> commandLine) async { |
| return processRunner.runProcess(commandLine); |
| })(<String>['echo', 'test']), |
| throwsA(isA<PreparePackageException>())); |
| }); |
| }); |
| group('ArchiveCreator for $platformName', () { |
| late ArchiveCreator creator; |
| late Directory tempDir; |
| Directory flutterDir; |
| Directory cacheDir; |
| late FakeProcessManager processManager; |
| final List<List<String>> args = <List<String>>[]; |
| final List<Map<Symbol, dynamic>> namedArgs = <Map<Symbol, dynamic>>[]; |
| late String flutter; |
| late String dart; |
| |
| Future<Uint8List> fakeHttpReader(Uri url, {Map<String, String>? headers}) { |
| return Future<Uint8List>.value(Uint8List(0)); |
| } |
| |
| setUp(() async { |
| processManager = FakeProcessManager.list(<FakeCommand>[]); |
| args.clear(); |
| namedArgs.clear(); |
| tempDir = Directory.systemTemp.createTempSync('flutter_prepage_package_test.'); |
| flutterDir = Directory(path.join(tempDir.path, 'flutter')); |
| flutterDir.createSync(recursive: true); |
| cacheDir = Directory(path.join(flutterDir.path, 'bin', 'cache')); |
| cacheDir.createSync(recursive: true); |
| creator = ArchiveCreator( |
| tempDir, |
| tempDir, |
| testRef, |
| Branch.beta, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| httpReader: fakeHttpReader, |
| ); |
| flutter = path.join(creator.flutterRoot.absolute.path, |
| 'bin', 'flutter'); |
| dart = path.join(creator.flutterRoot.absolute.path, |
| 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); |
| }); |
| |
| tearDown(() async { |
| tryToDelete(tempDir); |
| }); |
| |
| test('sets PUB_CACHE properly', () async { |
| final String createBase = path.join(tempDir.absolute.path, 'create_'); |
| final String archiveName = path.join(tempDir.absolute.path, |
| 'flutter_${platformName}_v1.2.3-beta${platform.isLinux ? '.tar.xz' : '.zip'}'); |
| |
| processManager.addCommands(convertResults(<String, List<ProcessResult>?>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': null, |
| 'git reset --hard $testRef': null, |
| 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
| 'git gc --prune=now --aggressive': null, |
| 'git describe --tags --exact-match $testRef': <ProcessResult>[ProcessResult(0, 0, 'v1.2.3', '')], |
| '$flutter --version --machine': <ProcessResult>[ |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ], |
| '$dart --version': <ProcessResult>[ |
| ProcessResult(0, 0, 'Dart SDK version: 2.17.0-63.0.beta (beta) (Wed Jan 26 03:48:52 2022 -0800) on "${platformName}_x64"', ''), |
| ], |
| if (platform.isWindows) '7za x ${path.join(tempDir.path, 'mingit.zip')}': null, |
| '$flutter doctor': null, |
| '$flutter update-packages': null, |
| '$flutter precache': null, |
| '$flutter ide-config': null, |
| '$flutter create --template=app ${createBase}app': null, |
| '$flutter create --template=package ${createBase}package': null, |
| '$flutter create --template=plugin ${createBase}plugin': null, |
| 'git clean -f -x -- **/.packages': null, |
| 'git clean -f -x -- **/.dart_tool/': null, |
| if (platform.isMacOS) 'codesign -vvvv --check-notarization ${path.join(tempDir.path, 'flutter', 'bin', 'cache', 'dart-sdk', 'bin', 'dart')}': null, |
| if (platform.isWindows) 'attrib -h .git': null, |
| if (platform.isWindows) '7za a -tzip -mx=9 $archiveName flutter': null |
| else if (platform.isMacOS) 'zip -r -9 --symlinks $archiveName flutter': null |
| else if (platform.isLinux) 'tar cJf $archiveName flutter': null, |
| })); |
| await creator.initializeRepo(); |
| await creator.createArchive(); |
| }); |
| |
| test('calls the right commands for archive output', () async { |
| final String createBase = path.join(tempDir.absolute.path, 'create_'); |
| final String archiveName = path.join(tempDir.absolute.path, |
| 'flutter_${platformName}_v1.2.3-beta${platform.isLinux ? '.tar.xz' : '.zip'}'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': null, |
| 'git reset --hard $testRef': null, |
| 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
| 'git gc --prune=now --aggressive': null, |
| 'git describe --tags --exact-match $testRef': <ProcessResult>[ProcessResult(0, 0, 'v1.2.3', '')], |
| '$flutter --version --machine': <ProcessResult>[ |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ], |
| '$dart --version': <ProcessResult>[ |
| ProcessResult(0, 0, 'Dart SDK version: 2.17.0-63.0.beta (beta) (Wed Jan 26 03:48:52 2022 -0800) on "${platformName}_x64"', ''), |
| ], |
| if (platform.isWindows) '7za x ${path.join(tempDir.path, 'mingit.zip')}': null, |
| '$flutter doctor': null, |
| '$flutter update-packages': null, |
| '$flutter precache': null, |
| '$flutter ide-config': null, |
| '$flutter create --template=app ${createBase}app': null, |
| '$flutter create --template=package ${createBase}package': null, |
| '$flutter create --template=plugin ${createBase}plugin': null, |
| 'git clean -f -x -- **/.packages': null, |
| 'git clean -f -x -- **/.dart_tool/': null, |
| if (platform.isMacOS) 'codesign -vvvv --check-notarization ${path.join(tempDir.path, 'flutter', 'bin', 'cache', 'dart-sdk', 'bin', 'dart')}': null, |
| if (platform.isWindows) 'attrib -h .git': null, |
| if (platform.isWindows) '7za a -tzip -mx=9 $archiveName flutter': null |
| else if (platform.isMacOS) 'zip -r -9 --symlinks $archiveName flutter': null |
| else if (platform.isLinux) 'tar cJf $archiveName flutter': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| creator = ArchiveCreator( |
| tempDir, |
| tempDir, |
| testRef, |
| Branch.beta, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| httpReader: fakeHttpReader, |
| ); |
| await creator.initializeRepo(); |
| await creator.createArchive(); |
| }); |
| |
| test('adds the arch name to the archive for non-x64', () async { |
| final String createBase = path.join(tempDir.absolute.path, 'create_'); |
| final String archiveName = path.join(tempDir.absolute.path, |
| 'flutter_${platformName}_arm64_v1.2.3-beta${platform.isLinux ? '.tar.xz' : '.zip'}'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': null, |
| 'git reset --hard $testRef': null, |
| 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
| 'git gc --prune=now --aggressive': null, |
| 'git describe --tags --exact-match $testRef': <ProcessResult>[ProcessResult(0, 0, 'v1.2.3', '')], |
| '$flutter --version --machine': <ProcessResult>[ |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ], |
| '$dart --version': <ProcessResult>[ |
| ProcessResult(0, 0, 'Dart SDK version: 2.17.0-63.0.beta (beta) (Wed Jan 26 03:48:52 2022 -0800) on "${platformName}_arm64"', ''), |
| ], |
| if (platform.isWindows) '7za x ${path.join(tempDir.path, 'mingit.zip')}': null, |
| '$flutter doctor': null, |
| '$flutter update-packages': null, |
| '$flutter precache': null, |
| '$flutter ide-config': null, |
| '$flutter create --template=app ${createBase}app': null, |
| '$flutter create --template=package ${createBase}package': null, |
| '$flutter create --template=plugin ${createBase}plugin': null, |
| 'git clean -f -x -- **/.packages': null, |
| 'git clean -f -x -- **/.dart_tool/': null, |
| if (platform.isMacOS) 'codesign -vvvv --check-notarization ${path.join(tempDir.path, 'flutter', 'bin', 'cache', 'dart-sdk', 'bin', 'dart')}': null, |
| if (platform.isWindows) 'attrib -h .git': null, |
| if (platform.isWindows) '7za a -tzip -mx=9 $archiveName flutter': null |
| else if (platform.isMacOS) 'zip -r -9 --symlinks $archiveName flutter': null |
| else if (platform.isLinux) 'tar cJf $archiveName flutter': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| creator = ArchiveCreator( |
| tempDir, |
| tempDir, |
| testRef, |
| Branch.beta, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| httpReader: fakeHttpReader, |
| ); |
| await creator.initializeRepo(); |
| await creator.createArchive(); |
| }); |
| |
| test('throws when a command errors out', () async { |
| final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': |
| <ProcessResult>[ProcessResult(0, 0, 'output1', '')], |
| 'git reset --hard $testRef': <ProcessResult>[ProcessResult(0, -1, 'output2', '')], |
| }; |
| processManager.addCommands(convertResults(calls)); |
| expect(expectAsync0(creator.initializeRepo), throwsA(isA<PreparePackageException>())); |
| }); |
| |
| test('non-strict mode calls the right commands', () async { |
| final String createBase = path.join(tempDir.absolute.path, 'create_'); |
| final String archiveName = path.join(tempDir.absolute.path, |
| 'flutter_${platformName}_v1.2.3-beta${platform.isLinux ? '.tar.xz' : '.zip'}'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': null, |
| 'git reset --hard $testRef': null, |
| 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
| 'git gc --prune=now --aggressive': null, |
| 'git describe --tags --abbrev=0 $testRef': <ProcessResult>[ProcessResult(0, 0, 'v1.2.3', '')], |
| '$flutter --version --machine': <ProcessResult>[ |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ], |
| '$dart --version': <ProcessResult>[ |
| ProcessResult(0, 0, 'Dart SDK version: 2.17.0-63.0.beta (beta) (Wed Jan 26 03:48:52 2022 -0800) on "${platformName}_x64"', ''), |
| ], |
| if (platform.isWindows) '7za x ${path.join(tempDir.path, 'mingit.zip')}': null, |
| '$flutter doctor': null, |
| '$flutter update-packages': null, |
| '$flutter precache': null, |
| '$flutter ide-config': null, |
| '$flutter create --template=app ${createBase}app': null, |
| '$flutter create --template=package ${createBase}package': null, |
| '$flutter create --template=plugin ${createBase}plugin': null, |
| 'git clean -f -x -- **/.packages': null, |
| 'git clean -f -x -- **/.dart_tool/': null, |
| if (platform.isWindows) 'attrib -h .git': null, |
| if (platform.isWindows) '7za a -tzip -mx=9 $archiveName flutter': null |
| else if (platform.isMacOS) 'zip -r -9 --symlinks $archiveName flutter': null |
| else if (platform.isLinux) 'tar cJf $archiveName flutter': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| creator = ArchiveCreator( |
| tempDir, |
| tempDir, |
| testRef, |
| Branch.beta, |
| strict: false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| httpReader: fakeHttpReader, |
| ); |
| await creator.initializeRepo(); |
| await creator.createArchive(); |
| }); |
| |
| test('fails if binary is not codesigned', () async { |
| final String createBase = path.join(tempDir.absolute.path, 'create_'); |
| final String archiveName = path.join(tempDir.absolute.path, |
| 'flutter_${platformName}_v1.2.3-beta${platform.isLinux ? '.tar.xz' : '.zip'}'); |
| final ProcessResult codesignFailure = ProcessResult(1, 1, '', 'code object is not signed at all'); |
| final String binPath = path.join(tempDir.path, 'flutter', 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| 'git clone -b beta https://flutter.googlesource.com/mirrors/flutter': null, |
| 'git reset --hard $testRef': null, |
| 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
| 'git gc --prune=now --aggressive': null, |
| 'git describe --tags --exact-match $testRef': <ProcessResult>[ProcessResult(0, 0, 'v1.2.3', '')], |
| '$flutter --version --machine': <ProcessResult>[ |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ProcessResult(0, 0, '{"dartSdkVersion": "3.2.1"}', ''), |
| ], |
| '$dart --version': <ProcessResult>[ |
| ProcessResult(0, 0, 'Dart SDK version: 2.17.0-63.0.beta (beta) (Wed Jan 26 03:48:52 2022 -0800) on "${platformName}_x64"', ''), |
| ], |
| if (platform.isWindows) '7za x ${path.join(tempDir.path, 'mingit.zip')}': null, |
| '$flutter doctor': null, |
| '$flutter update-packages': null, |
| '$flutter precache': null, |
| '$flutter ide-config': null, |
| '$flutter create --template=app ${createBase}app': null, |
| '$flutter create --template=package ${createBase}package': null, |
| '$flutter create --template=plugin ${createBase}plugin': null, |
| 'git clean -f -x -- **/.packages': null, |
| 'git clean -f -x -- **/.dart_tool/': null, |
| if (platform.isMacOS) 'codesign -vvvv --check-notarization $binPath': <ProcessResult>[codesignFailure], |
| if (platform.isWindows) 'attrib -h .git': null, |
| if (platform.isWindows) '7za a -tzip -mx=9 $archiveName flutter': null |
| else if (platform.isMacOS) 'zip -r -9 --symlinks $archiveName flutter': null |
| else if (platform.isLinux) 'tar cJf $archiveName flutter': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| creator = ArchiveCreator( |
| tempDir, |
| tempDir, |
| testRef, |
| Branch.beta, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| httpReader: fakeHttpReader, |
| ); |
| await creator.initializeRepo(); |
| |
| await expectLater( |
| () => creator.createArchive(), |
| throwsA(isA<PreparePackageException>().having( |
| (PreparePackageException exception) => exception.message, |
| 'message', |
| contains('The binary $binPath was not codesigned!'), |
| )), |
| ); |
| }, skip: !platform.isMacOS); // [intended] codesign is only available on macOS |
| }); |
| |
| group('ArchivePublisher for $platformName', () { |
| late FakeProcessManager processManager; |
| late Directory tempDir; |
| final String gsutilCall = platform.isWindows |
| ? 'python3 ${path.join("D:", "depot_tools", "gsutil.py")}' |
| : 'gsutil.py'; |
| final String releasesName = 'releases_$platformName.json'; |
| final String archiveName = platform.isLinux ? 'archive.tar.xz' : 'archive.zip'; |
| final String archiveMime = platform.isLinux ? 'application/x-gtar' : 'application/zip'; |
| final String gsArchivePath = 'gs://flutter_infra_release/releases/stable/$platformName/$archiveName'; |
| |
| setUp(() async { |
| processManager = FakeProcessManager.list(<FakeCommand>[]); |
| tempDir = Directory.systemTemp.createTempSync('flutter_prepage_package_test.'); |
| }); |
| |
| tearDown(() async { |
| tryToDelete(tempDir); |
| }); |
| |
| test('calls the right processes', () async { |
| final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| final String gsJsonPath = 'gs://flutter_infra_release/releases/$releasesName'; |
| final String releasesJson = ''' |
| { |
| "base_url": "https://storage.googleapis.com/flutter_infra_release/releases", |
| "current_release": { |
| "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2", |
| "beta": "5a58b36e36b8d7aace89d3950e6deb307956a6a0" |
| }, |
| "releases": [ |
| { |
| "hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0", |
| "channel": "beta", |
| "version": "v0.2.3", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.3-beta.zip", |
| "sha256": "4fe85a822093e81cb5a66c7fc263f68de39b5797b294191b6d75e7afcc86aff8", |
| "dart_sdk_arch": "x64" |
| }, |
| { |
| "hash": "b9bd51cc36b706215915711e580851901faebb40", |
| "channel": "beta", |
| "version": "v0.2.2", |
| "release_date": "2018-03-16T18:48:13.375013Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.2-beta.zip", |
| "sha256": "6073331168cdb37a4637a5dc073d6a7ef4e466321effa2c529fa27d2253a4d4b", |
| "dart_sdk_arch": "x64" |
| }, |
| { |
| "hash": "$testRef", |
| "channel": "stable", |
| "version": "v0.0.0", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "stable/$platformName/flutter_${platformName}_v0.0.0-beta.zip", |
| "sha256": "5dd34873b3a3e214a32fd30c2c319a0f46e608afb72f0d450b2d621a6d02aebd", |
| "dart_sdk_arch": "x64" |
| } |
| ] |
| } |
| '''; |
| File(jsonPath).writeAsStringSync(releasesJson); |
| File(archivePath).writeAsStringSync('archive contents'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| // This process fails because the file does NOT already exist |
| '$gsutilCall -- cp $gsJsonPath $jsonPath': null, |
| '$gsutilCall -- stat $gsArchivePath': <ProcessResult>[ProcessResult(0, 1, '', '')], |
| '$gsutilCall -- rm $gsArchivePath': null, |
| '$gsutilCall -- -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
| '$gsutilCall -- rm $gsJsonPath': null, |
| '$gsutilCall -- -h Content-Type:application/json -h Cache-Control:max-age=60 cp $jsonPath $gsJsonPath': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| outputFile.createSync(); |
| assert(tempDir.existsSync()); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'x64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| assert(tempDir.existsSync()); |
| await publisher.generateLocalMetadata(); |
| await publisher.publishArchive(); |
| |
| final File releaseFile = File(jsonPath); |
| expect(releaseFile.existsSync(), isTrue); |
| final String contents = releaseFile.readAsStringSync(); |
| // Make sure new data is added. |
| expect(contents, contains('"hash": "$testRef"')); |
| expect(contents, contains('"channel": "stable"')); |
| expect(contents, contains('"archive": "stable/$platformName/$archiveName"')); |
| expect(contents, contains('"sha256": "f69f4865f861193a91d1c5544a894167a7137b788d10bac8edbf5d095f45cb4d"')); |
| // Make sure existing entries are preserved. |
| expect(contents, contains('"hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0"')); |
| expect(contents, contains('"hash": "b9bd51cc36b706215915711e580851901faebb40"')); |
| expect(contents, contains('"channel": "beta"')); |
| expect(contents, contains('"channel": "beta"')); |
| // Make sure old matching entries are removed. |
| expect(contents, isNot(contains('v0.0.0'))); |
| final Map<String, dynamic> jsonData = json.decode(contents) as Map<String, dynamic>; |
| final List<dynamic> releases = jsonData['releases'] as List<dynamic>; |
| expect(releases.length, equals(3)); |
| // Make sure the new entry is first (and hopefully it takes less than a |
| // minute to go from publishArchive above to this line!). |
| expect( |
| DateTime.now().difference(DateTime.parse((releases[0] as Map<String, dynamic>)['release_date'] as String)), |
| lessThan(const Duration(minutes: 1)), |
| ); |
| const JsonEncoder encoder = JsonEncoder.withIndent(' '); |
| expect(contents, equals(encoder.convert(jsonData))); |
| }); |
| |
| test('contains Dart SDK version info', () async { |
| final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| final String gsJsonPath = 'gs://flutter_infra_release/releases/$releasesName'; |
| final String releasesJson = ''' |
| { |
| "base_url": "https://storage.googleapis.com/flutter_infra_release/releases", |
| "current_release": { |
| "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2", |
| "beta": "5a58b36e36b8d7aace89d3950e6deb307956a6a0" |
| }, |
| "releases": [ |
| { |
| "hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0", |
| "channel": "beta", |
| "version": "v0.2.3", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.3-beta.zip", |
| "sha256": "4fe85a822093e81cb5a66c7fc263f68de39b5797b294191b6d75e7afcc86aff8" |
| }, |
| { |
| "hash": "b9bd51cc36b706215915711e580851901faebb40", |
| "channel": "beta", |
| "version": "v0.2.2", |
| "release_date": "2018-03-16T18:48:13.375013Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.2-beta.zip", |
| "sha256": "6073331168cdb37a4637a5dc073d6a7ef4e466321effa2c529fa27d2253a4d4b" |
| }, |
| { |
| "hash": "$testRef", |
| "channel": "stable", |
| "version": "v0.0.0", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "stable/$platformName/flutter_${platformName}_v0.0.0-beta.zip", |
| "sha256": "5dd34873b3a3e214a32fd30c2c319a0f46e608afb72f0d450b2d621a6d02aebd" |
| } |
| ] |
| } |
| '''; |
| File(jsonPath).writeAsStringSync(releasesJson); |
| File(archivePath).writeAsStringSync('archive contents'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| // This process fails because the file does NOT already exist |
| '$gsutilCall -- cp $gsJsonPath $jsonPath': null, |
| '$gsutilCall -- stat $gsArchivePath': <ProcessResult>[ProcessResult(0, 1, '', '')], |
| '$gsutilCall -- rm $gsArchivePath': null, |
| '$gsutilCall -- -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
| '$gsutilCall -- rm $gsJsonPath': null, |
| '$gsutilCall -- -h Content-Type:application/json -h Cache-Control:max-age=60 cp $jsonPath $gsJsonPath': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| outputFile.createSync(); |
| assert(tempDir.existsSync()); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'x64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| assert(tempDir.existsSync()); |
| await publisher.generateLocalMetadata(); |
| await publisher.publishArchive(); |
| |
| final File releaseFile = File(jsonPath); |
| expect(releaseFile.existsSync(), isTrue); |
| final String contents = releaseFile.readAsStringSync(); |
| expect(contents, contains('"dart_sdk_version": "3.2.1"')); |
| expect(contents, contains('"dart_sdk_arch": "x64"')); |
| }); |
| |
| test('Supports multiple architectures', () async { |
| final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| final String gsJsonPath = 'gs://flutter_infra_release/releases/$releasesName'; |
| final String releasesJson = ''' |
| { |
| "base_url": "https://storage.googleapis.com/flutter_infra_release/releases", |
| "current_release": { |
| "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2", |
| "beta": "5a58b36e36b8d7aace89d3950e6deb307956a6a0" |
| }, |
| "releases": [ |
| { |
| "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", |
| "channel": "stable", |
| "version": "v1.2.3", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.3-beta.zip", |
| "sha256": "4fe85a822093e81cb5a66c7fc263f68de39b5797b294191b6d75e7afcc86aff8", |
| "dart_sdk_arch": "x64" |
| } |
| ] |
| } |
| '''; |
| File(jsonPath).writeAsStringSync(releasesJson); |
| File(archivePath).writeAsStringSync('archive contents'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| // This process fails because the file does NOT already exist |
| '$gsutilCall -- cp $gsJsonPath $jsonPath': null, |
| '$gsutilCall -- stat $gsArchivePath': <ProcessResult>[ProcessResult(0, 1, '', '')], |
| '$gsutilCall -- rm $gsArchivePath': null, |
| '$gsutilCall -- -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
| '$gsutilCall -- rm $gsJsonPath': null, |
| '$gsutilCall -- -h Content-Type:application/json -h Cache-Control:max-age=60 cp $jsonPath $gsJsonPath': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| outputFile.createSync(); |
| assert(tempDir.existsSync()); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'arm64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| assert(tempDir.existsSync()); |
| await publisher.generateLocalMetadata(); |
| await publisher.publishArchive(); |
| |
| final File releaseFile = File(jsonPath); |
| expect(releaseFile.existsSync(), isTrue); |
| final String contents = releaseFile.readAsStringSync(); |
| final Map<String, dynamic> releases = jsonDecode(contents) as Map<String, dynamic>; |
| expect((releases['releases'] as List<dynamic>).length, equals(2)); |
| }); |
| |
| |
| test('updates base_url from old bucket to new bucket', () async { |
| final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| final String gsJsonPath = 'gs://flutter_infra_release/releases/$releasesName'; |
| final String releasesJson = ''' |
| { |
| "base_url": "https://storage.googleapis.com/flutter_infra_release/releases", |
| "current_release": { |
| "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2", |
| "beta": "5a58b36e36b8d7aace89d3950e6deb307956a6a0" |
| }, |
| "releases": [ |
| { |
| "hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0", |
| "channel": "beta", |
| "version": "v0.2.3", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.3-beta.zip", |
| "sha256": "4fe85a822093e81cb5a66c7fc263f68de39b5797b294191b6d75e7afcc86aff8" |
| }, |
| { |
| "hash": "b9bd51cc36b706215915711e580851901faebb40", |
| "channel": "beta", |
| "version": "v0.2.2", |
| "release_date": "2018-03-16T18:48:13.375013Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.2-beta.zip", |
| "sha256": "6073331168cdb37a4637a5dc073d6a7ef4e466321effa2c529fa27d2253a4d4b" |
| }, |
| { |
| "hash": "$testRef", |
| "channel": "stable", |
| "version": "v0.0.0", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "stable/$platformName/flutter_${platformName}_v0.0.0-beta.zip", |
| "sha256": "5dd34873b3a3e214a32fd30c2c319a0f46e608afb72f0d450b2d621a6d02aebd" |
| } |
| ] |
| } |
| '''; |
| File(jsonPath).writeAsStringSync(releasesJson); |
| File(archivePath).writeAsStringSync('archive contents'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| // This process fails because the file does NOT already exist |
| '$gsutilCall -- cp $gsJsonPath $jsonPath': null, |
| '$gsutilCall -- stat $gsArchivePath': <ProcessResult>[ProcessResult(0, 1, '', '')], |
| '$gsutilCall -- rm $gsArchivePath': null, |
| '$gsutilCall -- -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
| '$gsutilCall -- rm $gsJsonPath': null, |
| '$gsutilCall -- -h Content-Type:application/json -h Cache-Control:max-age=60 cp $jsonPath $gsJsonPath': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| outputFile.createSync(); |
| assert(tempDir.existsSync()); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'x64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| assert(tempDir.existsSync()); |
| await publisher.generateLocalMetadata(); |
| await publisher.publishArchive(); |
| |
| final File releaseFile = File(jsonPath); |
| expect(releaseFile.existsSync(), isTrue); |
| final String contents = releaseFile.readAsStringSync(); |
| final Map<String, dynamic> jsonData = json.decode(contents) as Map<String, dynamic>; |
| expect(jsonData['base_url'], 'https://storage.googleapis.com/flutter_infra_release/releases'); |
| }); |
| |
| test('publishArchive throws if forceUpload is false and artifact already exists on cloud storage', () async { |
| final String archiveName = platform.isLinux ? 'archive.tar.xz' : 'archive.zip'; |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'x64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| // This process returns 0 because file already exists |
| '$gsutilCall -- stat $gsArchivePath': <ProcessResult>[ProcessResult(0, 0, '', '')], |
| }; |
| processManager.addCommands(convertResults(calls)); |
| expect(() async => publisher.publishArchive(), throwsException); |
| }); |
| |
| test('publishArchive does not throw if forceUpload is true and artifact already exists on cloud storage', () async { |
| final String archiveName = platform.isLinux ? 'archive.tar.xz' : 'archive.zip'; |
| final File outputFile = File(path.join(tempDir.absolute.path, archiveName)); |
| final ArchivePublisher publisher = ArchivePublisher( |
| tempDir, |
| testRef, |
| Branch.stable, |
| <String, String>{ |
| 'frameworkVersionFromGit': 'v1.2.3', |
| 'dartSdkVersion': '3.2.1', |
| 'dartTargetArch': 'x64', |
| }, |
| outputFile, |
| false, |
| processManager: processManager, |
| subprocessOutput: false, |
| platform: platform, |
| ); |
| final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| final String gsJsonPath = 'gs://flutter_infra_release/releases/$releasesName'; |
| final String releasesJson = ''' |
| { |
| "base_url": "https://storage.googleapis.com/flutter_infra_release/releases", |
| "current_release": { |
| "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2", |
| "beta": "5a58b36e36b8d7aace89d3950e6deb307956a6a0" |
| }, |
| "releases": [ |
| { |
| "hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0", |
| "channel": "beta", |
| "version": "v0.2.3", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.3-beta.zip", |
| "sha256": "4fe85a822093e81cb5a66c7fc263f68de39b5797b294191b6d75e7afcc86aff8" |
| }, |
| { |
| "hash": "b9bd51cc36b706215915711e580851901faebb40", |
| "channel": "beta", |
| "version": "v0.2.2", |
| "release_date": "2018-03-16T18:48:13.375013Z", |
| "archive": "beta/$platformName/flutter_${platformName}_v0.2.2-beta.zip", |
| "sha256": "6073331168cdb37a4637a5dc073d6a7ef4e466321effa2c529fa27d2253a4d4b" |
| }, |
| { |
| "hash": "$testRef", |
| "channel": "stable", |
| "version": "v0.0.0", |
| "release_date": "2018-03-20T01:47:02.851729Z", |
| "archive": "stable/$platformName/flutter_${platformName}_v0.0.0-beta.zip", |
| "sha256": "5dd34873b3a3e214a32fd30c2c319a0f46e608afb72f0d450b2d621a6d02aebd" |
| } |
| ] |
| } |
| '''; |
| File(jsonPath).writeAsStringSync(releasesJson); |
| File(archivePath).writeAsStringSync('archive contents'); |
| final Map<String, List<ProcessResult>?> calls = <String, List<ProcessResult>?>{ |
| '$gsutilCall -- cp $gsJsonPath $jsonPath': null, |
| '$gsutilCall -- rm $gsArchivePath': null, |
| '$gsutilCall -- -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
| '$gsutilCall -- rm $gsJsonPath': null, |
| '$gsutilCall -- -h Content-Type:application/json -h Cache-Control:max-age=60 cp $jsonPath $gsJsonPath': null, |
| }; |
| processManager.addCommands(convertResults(calls)); |
| assert(tempDir.existsSync()); |
| await publisher.generateLocalMetadata(); |
| await publisher.publishArchive(true); |
| }); |
| }); |
| } |
| } |
| |
| List<FakeCommand> convertResults(Map<String, List<ProcessResult>?> results) { |
| final List<FakeCommand> commands = <FakeCommand>[]; |
| for (final String key in results.keys) { |
| final List<ProcessResult>? candidates = results[key]; |
| final List<String> args = key.split(' '); |
| if (candidates == null) { |
| commands.add(FakeCommand( |
| command: args, |
| )); |
| } else { |
| for (final ProcessResult result in candidates) { |
| commands.add(FakeCommand( |
| command: args, |
| exitCode: result.exitCode, |
| stderr: result.stderr.toString(), |
| stdout: result.stdout.toString(), |
| )); |
| } |
| } |
| } |
| return commands; |
| } |