Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 5 | import 'dart:async'; |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 6 | import 'dart:convert'; |
| 7 | import 'dart:io' hide Platform; |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 8 | import 'dart:typed_data'; |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 9 | |
| 10 | import 'package:mockito/mockito.dart'; |
| 11 | import 'package:test/test.dart'; |
| 12 | import 'package:path/path.dart' as path; |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 13 | import 'package:platform/platform.dart' show FakePlatform; |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 14 | |
| 15 | import '../prepare_package.dart'; |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 16 | import 'fake_process_manager.dart'; |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 17 | |
| 18 | void main() { |
Alexandre Ardhuin | 7667db6 | 2018-03-14 06:24:49 +0100 | [diff] [blame] | 19 | const String testRef = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef'; |
Greg Spencer | 984a24c | 2018-03-09 18:58:41 -0800 | [diff] [blame] | 20 | test('Throws on missing executable', () async { |
| 21 | // Uses a *real* process manager, since we want to know what happens if |
| 22 | // it can't find an executable. |
| 23 | final ProcessRunner processRunner = new ProcessRunner(subprocessOutput: false); |
| 24 | expect( |
| 25 | expectAsync1((List<String> commandLine) async { |
| 26 | return processRunner.runProcess(commandLine); |
| 27 | })(<String>['this_executable_better_not_exist_2857632534321']), |
| 28 | throwsA(const isInstanceOf<ProcessRunnerException>())); |
| 29 | try { |
| 30 | await processRunner.runProcess(<String>['this_executable_better_not_exist_2857632534321']); |
| 31 | } on ProcessRunnerException catch (e) { |
| 32 | expect( |
| 33 | e.message, |
| 34 | contains('Invalid argument(s): Cannot find executable for this_executable_better_not_exist_2857632534321.'), |
| 35 | ); |
| 36 | } |
| 37 | }); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 38 | for (String platformName in <String>['macos', 'linux', 'windows']) { |
| 39 | final FakePlatform platform = new FakePlatform( |
| 40 | operatingSystem: platformName, |
| 41 | environment: <String, String>{}, |
| 42 | ); |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 43 | group('ProcessRunner for $platform', () { |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 44 | test('Returns stdout', () async { |
| 45 | final FakeProcessManager fakeProcessManager = new FakeProcessManager(); |
| 46 | fakeProcessManager.fakeResults = <String, List<ProcessResult>>{ |
| 47 | 'echo test': <ProcessResult>[new ProcessResult(0, 0, 'output', 'error')], |
| 48 | }; |
| 49 | final ProcessRunner processRunner = new ProcessRunner( |
| 50 | subprocessOutput: false, platform: platform, processManager: fakeProcessManager); |
| 51 | final String output = await processRunner.runProcess(<String>['echo', 'test']); |
| 52 | expect(output, equals('output')); |
| 53 | }); |
| 54 | test('Throws on process failure', () async { |
| 55 | final FakeProcessManager fakeProcessManager = new FakeProcessManager(); |
| 56 | fakeProcessManager.fakeResults = <String, List<ProcessResult>>{ |
| 57 | 'echo test': <ProcessResult>[new ProcessResult(0, -1, 'output', 'error')], |
| 58 | }; |
| 59 | final ProcessRunner processRunner = new ProcessRunner( |
| 60 | subprocessOutput: false, platform: platform, processManager: fakeProcessManager); |
| 61 | expect( |
| 62 | expectAsync1((List<String> commandLine) async { |
| 63 | return processRunner.runProcess(commandLine); |
| 64 | })(<String>['echo', 'test']), |
| 65 | throwsA(const isInstanceOf<ProcessRunnerException>())); |
| 66 | }); |
| 67 | }); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 68 | group('ArchiveCreator for $platformName', () { |
| 69 | ArchiveCreator creator; |
| 70 | Directory tmpDir; |
| 71 | Directory flutterDir; |
| 72 | FakeProcessManager processManager; |
| 73 | final List<List<String>> args = <List<String>>[]; |
| 74 | final List<Map<Symbol, dynamic>> namedArgs = <Map<Symbol, dynamic>>[]; |
| 75 | String flutter; |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 76 | |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 77 | Future<Uint8List> fakeHttpReader(Uri url, {Map<String, String> headers}) { |
| 78 | return new Future<Uint8List>.value(new Uint8List(0)); |
| 79 | } |
| 80 | |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 81 | setUp(() async { |
| 82 | processManager = new FakeProcessManager(); |
| 83 | args.clear(); |
| 84 | namedArgs.clear(); |
| 85 | tmpDir = await Directory.systemTemp.createTemp('flutter_'); |
| 86 | flutterDir = new Directory(path.join(tmpDir.path, 'flutter')); |
| 87 | flutterDir.createSync(recursive: true); |
| 88 | creator = new ArchiveCreator( |
| 89 | tmpDir, |
| 90 | tmpDir, |
| 91 | testRef, |
| 92 | Branch.dev, |
| 93 | processManager: processManager, |
| 94 | subprocessOutput: false, |
| 95 | platform: platform, |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 96 | httpReader: fakeHttpReader, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 97 | ); |
| 98 | flutter = path.join(creator.flutterRoot.absolute.path, 'bin', 'flutter'); |
| 99 | }); |
| 100 | |
| 101 | tearDown(() async { |
| 102 | // On Windows, the directory is locked and not able to be deleted yet. So |
| 103 | // we just leave some (very small, because we're not actually building |
| 104 | // archives here) trash around to be deleted at the next reboot. |
| 105 | if (!platform.isWindows) { |
| 106 | await tmpDir.delete(recursive: true); |
| 107 | } |
| 108 | }); |
| 109 | |
| 110 | test('sets PUB_CACHE properly', () async { |
| 111 | final String createBase = path.join(tmpDir.absolute.path, 'create_'); |
| 112 | final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| 113 | 'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter': |
| 114 | null, |
| 115 | 'git reset --hard $testRef': null, |
Greg Spencer | c345c1b | 2018-03-14 12:56:44 -0700 | [diff] [blame] | 116 | 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 117 | 'git describe --tags --abbrev=0': <ProcessResult>[new ProcessResult(0, 0, 'v1.2.3', '')], |
| 118 | }; |
| 119 | if (platform.isWindows) { |
| 120 | calls['7za x ${path.join(tmpDir.path, 'mingit.zip')}'] = null; |
| 121 | } |
| 122 | calls.addAll(<String, List<ProcessResult>>{ |
| 123 | '$flutter doctor': null, |
| 124 | '$flutter update-packages': null, |
| 125 | '$flutter precache': null, |
| 126 | '$flutter ide-config': null, |
| 127 | '$flutter create --template=app ${createBase}app': null, |
| 128 | '$flutter create --template=package ${createBase}package': null, |
| 129 | '$flutter create --template=plugin ${createBase}plugin': null, |
| 130 | 'git clean -f -X **/.packages': null, |
| 131 | }); |
| 132 | final String archiveName = path.join(tmpDir.absolute.path, |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 133 | 'flutter_${platformName}_v1.2.3-dev${platform.isLinux ? '.tar.xz' : '.zip'}'); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 134 | if (platform.isWindows) { |
| 135 | calls['7za a -tzip -mx=9 $archiveName flutter'] = null; |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 136 | } else if (platform.isMacOS) { |
| 137 | calls['zip -r -9 $archiveName flutter'] = null; |
| 138 | } else if (platform.isLinux) { |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 139 | calls['tar cJf $archiveName flutter'] = null; |
| 140 | } |
| 141 | processManager.fakeResults = calls; |
| 142 | await creator.initializeRepo(); |
| 143 | await creator.createArchive(); |
| 144 | expect( |
| 145 | verify(processManager.start( |
Greg Spencer | d9e3715 | 2018-03-20 08:14:08 -0700 | [diff] [blame^] | 146 | typed(captureAny), |
| 147 | workingDirectory: typed(captureAny, named: 'workingDirectory'), |
| 148 | environment: typed(captureAny, named: 'environment'), |
| 149 | )).captured[2]['PUB_CACHE'], |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 150 | endsWith(path.join('flutter', '.pub-cache')), |
| 151 | ); |
| 152 | }); |
| 153 | |
| 154 | test('calls the right commands for archive output', () async { |
| 155 | final String createBase = path.join(tmpDir.absolute.path, 'create_'); |
| 156 | final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| 157 | 'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter': |
| 158 | null, |
| 159 | 'git reset --hard $testRef': null, |
Greg Spencer | c345c1b | 2018-03-14 12:56:44 -0700 | [diff] [blame] | 160 | 'git remote set-url origin https://github.com/flutter/flutter.git': null, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 161 | 'git describe --tags --abbrev=0': <ProcessResult>[new ProcessResult(0, 0, 'v1.2.3', '')], |
| 162 | }; |
| 163 | if (platform.isWindows) { |
| 164 | calls['7za x ${path.join(tmpDir.path, 'mingit.zip')}'] = null; |
| 165 | } |
| 166 | calls.addAll(<String, List<ProcessResult>>{ |
| 167 | '$flutter doctor': null, |
| 168 | '$flutter update-packages': null, |
| 169 | '$flutter precache': null, |
| 170 | '$flutter ide-config': null, |
| 171 | '$flutter create --template=app ${createBase}app': null, |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 172 | '$flutter create --template=package ${createBase}package': null, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 173 | '$flutter create --template=plugin ${createBase}plugin': null, |
| 174 | 'git clean -f -X **/.packages': null, |
| 175 | }); |
| 176 | final String archiveName = path.join(tmpDir.absolute.path, |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 177 | 'flutter_${platformName}_v1.2.3-dev${platform.isLinux ? '.tar.xz' : '.zip'}'); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 178 | if (platform.isWindows) { |
| 179 | calls['7za a -tzip -mx=9 $archiveName flutter'] = null; |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 180 | } else if (platform.isMacOS) { |
| 181 | calls['zip -r -9 $archiveName flutter'] = null; |
| 182 | } else if (platform.isLinux) { |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 183 | calls['tar cJf $archiveName flutter'] = null; |
| 184 | } |
| 185 | processManager.fakeResults = calls; |
| 186 | creator = new ArchiveCreator( |
| 187 | tmpDir, |
| 188 | tmpDir, |
| 189 | testRef, |
| 190 | Branch.dev, |
| 191 | processManager: processManager, |
| 192 | subprocessOutput: false, |
| 193 | platform: platform, |
Greg Spencer | df79127 | 2018-02-07 12:21:14 -0800 | [diff] [blame] | 194 | httpReader: fakeHttpReader, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 195 | ); |
| 196 | await creator.initializeRepo(); |
| 197 | await creator.createArchive(); |
| 198 | processManager.verifyCalls(calls.keys.toList()); |
| 199 | }); |
| 200 | |
| 201 | test('throws when a command errors out', () async { |
| 202 | final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| 203 | 'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter': |
| 204 | <ProcessResult>[new ProcessResult(0, 0, 'output1', '')], |
| 205 | 'git reset --hard $testRef': <ProcessResult>[new ProcessResult(0, -1, 'output2', '')], |
| 206 | }; |
| 207 | processManager.fakeResults = calls; |
| 208 | expect(expectAsync0(creator.initializeRepo), |
| 209 | throwsA(const isInstanceOf<ProcessRunnerException>())); |
| 210 | }); |
| 211 | }); |
| 212 | |
| 213 | group('ArchivePublisher for $platformName', () { |
| 214 | FakeProcessManager processManager; |
| 215 | Directory tempDir; |
| 216 | |
| 217 | setUp(() async { |
| 218 | processManager = new FakeProcessManager(); |
| 219 | tempDir = await Directory.systemTemp.createTemp('flutter_'); |
| 220 | tempDir.createSync(); |
| 221 | }); |
| 222 | |
| 223 | tearDown(() async { |
| 224 | // On Windows, the directory is locked and not able to be deleted yet. So |
| 225 | // we just leave some (very small, because we're not actually building |
| 226 | // archives here) trash around to be deleted at the next reboot. |
| 227 | if (!platform.isWindows) { |
| 228 | await tempDir.delete(recursive: true); |
| 229 | } |
| 230 | }); |
| 231 | |
| 232 | test('calls the right processes', () async { |
| 233 | final String releasesName = 'releases_$platformName.json'; |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 234 | final String archiveName = platform.isLinux ? 'archive.tar.xz' : 'archive.zip'; |
| 235 | final String archiveMime = platform.isLinux ? 'application/x-gtar' : 'application/zip'; |
Greg Spencer | 97e7793 | 2018-02-09 15:42:51 -0800 | [diff] [blame] | 236 | final String archivePath = path.join(tempDir.absolute.path, archiveName); |
| 237 | final String gsArchivePath = 'gs://flutter_infra/releases/dev/$platformName/$archiveName'; |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 238 | final String jsonPath = path.join(tempDir.absolute.path, releasesName); |
| 239 | final String gsJsonPath = 'gs://flutter_infra/releases/$releasesName'; |
| 240 | final String releasesJson = '''{ |
| 241 | "base_url": "https://storage.googleapis.com/flutter_infra/releases", |
| 242 | "current_release": { |
| 243 | "beta": "6da8ec6bd0c4801b80d666869e4069698561c043", |
| 244 | "dev": "f88c60b38c3a5ef92115d24e3da4175b4890daba" |
| 245 | }, |
| 246 | "releases": { |
| 247 | "6da8ec6bd0c4801b80d666869e4069698561c043": { |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 248 | "${platformName}_archive": "dev/linux/flutter_${platformName}_0.21.0-beta.zip", |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 249 | "release_date": "2017-12-19T10:30:00,847287019-08:00", |
| 250 | "version": "0.21.0-beta" |
| 251 | }, |
| 252 | "f88c60b38c3a5ef92115d24e3da4175b4890daba": { |
Greg Spencer | 29d59e3 | 2018-02-27 13:15:32 -0800 | [diff] [blame] | 253 | "${platformName}_archive": "dev/linux/flutter_${platformName}_0.22.0-dev.zip", |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 254 | "release_date": "2018-01-19T13:30:09,728487019-08:00", |
| 255 | "version": "0.22.0-dev" |
| 256 | } |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 257 | } |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 258 | } |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 259 | '''; |
Greg Spencer | d9e3715 | 2018-03-20 08:14:08 -0700 | [diff] [blame^] | 260 | new File(jsonPath).writeAsStringSync(releasesJson); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 261 | final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{ |
| 262 | 'gsutil rm $gsArchivePath': null, |
Greg Spencer | 0b6c193 | 2018-02-22 09:03:10 -0800 | [diff] [blame] | 263 | 'gsutil -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null, |
Greg Spencer | d9e3715 | 2018-03-20 08:14:08 -0700 | [diff] [blame^] | 264 | 'gsutil cp $gsJsonPath $jsonPath': null, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 265 | 'gsutil rm $gsJsonPath': null, |
Greg Spencer | 0b6c193 | 2018-02-22 09:03:10 -0800 | [diff] [blame] | 266 | 'gsutil -h Content-Type:application/json cp $jsonPath $gsJsonPath': null, |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 267 | }; |
| 268 | processManager.fakeResults = calls; |
Greg Spencer | 97e7793 | 2018-02-09 15:42:51 -0800 | [diff] [blame] | 269 | final File outputFile = new File(path.join(tempDir.absolute.path, archiveName)); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 270 | assert(tempDir.existsSync()); |
| 271 | final ArchivePublisher publisher = new ArchivePublisher( |
| 272 | tempDir, |
| 273 | testRef, |
| 274 | Branch.dev, |
| 275 | '1.2.3', |
| 276 | outputFile, |
| 277 | processManager: processManager, |
| 278 | subprocessOutput: false, |
| 279 | platform: platform, |
| 280 | ); |
| 281 | assert(tempDir.existsSync()); |
| 282 | await publisher.publishArchive(); |
| 283 | processManager.verifyCalls(calls.keys.toList()); |
| 284 | final File releaseFile = new File(jsonPath); |
| 285 | expect(releaseFile.existsSync(), isTrue); |
| 286 | final String contents = releaseFile.readAsStringSync(); |
| 287 | // Make sure new data is added. |
| 288 | expect(contents, contains('"dev": "$testRef"')); |
| 289 | expect(contents, contains('"$testRef": {')); |
Greg Spencer | 97e7793 | 2018-02-09 15:42:51 -0800 | [diff] [blame] | 290 | expect(contents, contains('"${platformName}_archive": "dev/$platformName/$archiveName"')); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 291 | // Make sure existing entries are preserved. |
| 292 | expect(contents, contains('"6da8ec6bd0c4801b80d666869e4069698561c043": {')); |
| 293 | expect(contents, contains('"f88c60b38c3a5ef92115d24e3da4175b4890daba": {')); |
| 294 | expect(contents, contains('"beta": "6da8ec6bd0c4801b80d666869e4069698561c043"')); |
| 295 | // Make sure it's valid JSON, and in the right format. |
| 296 | final Map<String, dynamic> jsonData = json.decode(contents); |
Alexandre Ardhuin | 7667db6 | 2018-03-14 06:24:49 +0100 | [diff] [blame] | 297 | const JsonEncoder encoder = const JsonEncoder.withIndent(' '); |
Greg Spencer | 8a2df39 | 2018-02-06 15:32:19 -0800 | [diff] [blame] | 298 | expect(contents, equals(encoder.convert(jsonData))); |
| 299 | }); |
| 300 | }); |
| 301 | } |
Greg Spencer | f00c902 | 2017-12-15 15:01:30 -0800 | [diff] [blame] | 302 | } |