prepare_package_test.dart 13.8 KB
Newer Older
1 2 3 4
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:async';
6 7
import 'dart:convert';
import 'dart:io' hide Platform;
8
import 'dart:typed_data';
9 10 11 12

import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
import 'package:path/path.dart' as path;
13
import 'package:platform/platform.dart' show FakePlatform;
14 15

import '../prepare_package.dart';
16
import 'fake_process_manager.dart';
17 18

void main() {
19
  const String testRef = 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef';
20 21 22 23 24 25 26 27
  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 = new ProcessRunner(subprocessOutput: false);
    expect(
        expectAsync1((List<String> commandLine) async {
          return processRunner.runProcess(commandLine);
        })(<String>['this_executable_better_not_exist_2857632534321']),
28
        throwsA(const isInstanceOf<ProcessRunnerException>()));
29 30 31 32 33 34 35 36 37
    try {
      await processRunner.runProcess(<String>['this_executable_better_not_exist_2857632534321']);
    } on ProcessRunnerException catch (e) {
      expect(
        e.message,
        contains('Invalid argument(s): Cannot find executable for this_executable_better_not_exist_2857632534321.'),
      );
    }
  });
38 39 40 41 42
  for (String platformName in <String>['macos', 'linux', 'windows']) {
    final FakePlatform platform = new FakePlatform(
      operatingSystem: platformName,
      environment: <String, String>{},
    );
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
    group('ProcessRunner for $platform', () {
      test('Returns stdout', () async {
        final FakeProcessManager fakeProcessManager = new FakeProcessManager();
        fakeProcessManager.fakeResults = <String, List<ProcessResult>>{
          'echo test': <ProcessResult>[new ProcessResult(0, 0, 'output', 'error')],
        };
        final ProcessRunner processRunner = new 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 = new FakeProcessManager();
        fakeProcessManager.fakeResults = <String, List<ProcessResult>>{
          'echo test': <ProcessResult>[new ProcessResult(0, -1, 'output', 'error')],
        };
        final ProcessRunner processRunner = new ProcessRunner(
            subprocessOutput: false, platform: platform, processManager: fakeProcessManager);
        expect(
            expectAsync1((List<String> commandLine) async {
              return processRunner.runProcess(commandLine);
            })(<String>['echo', 'test']),
65
            throwsA(const isInstanceOf<ProcessRunnerException>()));
66 67
      });
    });
68 69 70 71 72 73 74 75 76
    group('ArchiveCreator for $platformName', () {
      ArchiveCreator creator;
      Directory tmpDir;
      Directory flutterDir;
      FakeProcessManager processManager;
      final List<List<String>> args = <List<String>>[];
      final List<Map<Symbol, dynamic>> namedArgs = <Map<Symbol, dynamic>>[];
      String flutter;

77 78 79 80
      Future<Uint8List> fakeHttpReader(Uri url, {Map<String, String> headers}) {
        return new Future<Uint8List>.value(new Uint8List(0));
      }

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
      setUp(() async {
        processManager = new FakeProcessManager();
        args.clear();
        namedArgs.clear();
        tmpDir = await Directory.systemTemp.createTemp('flutter_');
        flutterDir = new Directory(path.join(tmpDir.path, 'flutter'));
        flutterDir.createSync(recursive: true);
        creator = new ArchiveCreator(
          tmpDir,
          tmpDir,
          testRef,
          Branch.dev,
          processManager: processManager,
          subprocessOutput: false,
          platform: platform,
96
          httpReader: fakeHttpReader,
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
        );
        flutter = path.join(creator.flutterRoot.absolute.path, 'bin', 'flutter');
      });

      tearDown(() async {
        // On Windows, the directory is locked and not able to be deleted yet. So
        // we just leave some (very small, because we're not actually building
        // archives here) trash around to be deleted at the next reboot.
        if (!platform.isWindows) {
          await tmpDir.delete(recursive: true);
        }
      });

      test('sets PUB_CACHE properly', () async {
        final String createBase = path.join(tmpDir.absolute.path, 'create_');
        final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{
113
          'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter': null,
114
          'git reset --hard $testRef': null,
115
          'git remote set-url origin https://github.com/flutter/flutter.git': null,
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
          'git describe --tags --abbrev=0': <ProcessResult>[new ProcessResult(0, 0, 'v1.2.3', '')],
        };
        if (platform.isWindows) {
          calls['7za x ${path.join(tmpDir.path, 'mingit.zip')}'] = null;
        }
        calls.addAll(<String, List<ProcessResult>>{
          '$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,
        });
        final String archiveName = path.join(tmpDir.absolute.path,
132
            'flutter_${platformName}_v1.2.3-dev${platform.isLinux ? '.tar.xz' : '.zip'}');
133 134
        if (platform.isWindows) {
          calls['7za a -tzip -mx=9 $archiveName flutter'] = null;
135 136 137
        } else if (platform.isMacOS) {
          calls['zip -r -9 $archiveName flutter'] = null;
        } else if (platform.isLinux) {
138 139 140 141 142 143 144
          calls['tar cJf $archiveName flutter'] = null;
        }
        processManager.fakeResults = calls;
        await creator.initializeRepo();
        await creator.createArchive();
        expect(
          verify(processManager.start(
145 146 147
            captureAny,
            workingDirectory: captureAnyNamed('workingDirectory'),
            environment: captureAnyNamed('environment'),
148
          )).captured[2]['PUB_CACHE'],
149 150 151 152 153 154 155
          endsWith(path.join('flutter', '.pub-cache')),
        );
      });

      test('calls the right commands for archive output', () async {
        final String createBase = path.join(tmpDir.absolute.path, 'create_');
        final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{
156
          'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter': null,
157
          'git reset --hard $testRef': null,
158
          'git remote set-url origin https://github.com/flutter/flutter.git': null,
159 160 161 162 163 164 165 166 167 168 169
          'git describe --tags --abbrev=0': <ProcessResult>[new ProcessResult(0, 0, 'v1.2.3', '')],
        };
        if (platform.isWindows) {
          calls['7za x ${path.join(tmpDir.path, 'mingit.zip')}'] = null;
        }
        calls.addAll(<String, List<ProcessResult>>{
          '$flutter doctor': null,
          '$flutter update-packages': null,
          '$flutter precache': null,
          '$flutter ide-config': null,
          '$flutter create --template=app ${createBase}app': null,
170
          '$flutter create --template=package ${createBase}package': null,
171 172 173 174
          '$flutter create --template=plugin ${createBase}plugin': null,
          'git clean -f -X **/.packages': null,
        });
        final String archiveName = path.join(tmpDir.absolute.path,
175
            'flutter_${platformName}_v1.2.3-dev${platform.isLinux ? '.tar.xz' : '.zip'}');
176 177
        if (platform.isWindows) {
          calls['7za a -tzip -mx=9 $archiveName flutter'] = null;
178 179 180
        } else if (platform.isMacOS) {
          calls['zip -r -9 $archiveName flutter'] = null;
        } else if (platform.isLinux) {
181 182 183 184 185 186 187 188 189 190 191
          calls['tar cJf $archiveName flutter'] = null;
        }
        processManager.fakeResults = calls;
        creator = new ArchiveCreator(
          tmpDir,
          tmpDir,
          testRef,
          Branch.dev,
          processManager: processManager,
          subprocessOutput: false,
          platform: platform,
192
          httpReader: fakeHttpReader,
193 194 195 196 197 198 199 200 201 202 203 204 205 206
        );
        await creator.initializeRepo();
        await creator.createArchive();
        processManager.verifyCalls(calls.keys.toList());
      });

      test('throws when a command errors out', () async {
        final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{
          'git clone -b dev https://chromium.googlesource.com/external/github.com/flutter/flutter':
              <ProcessResult>[new ProcessResult(0, 0, 'output1', '')],
          'git reset --hard $testRef': <ProcessResult>[new ProcessResult(0, -1, 'output2', '')],
        };
        processManager.fakeResults = calls;
        expect(expectAsync0(creator.initializeRepo),
207
            throwsA(const isInstanceOf<ProcessRunnerException>()));
208
      });
209 210
    });

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    group('ArchivePublisher for $platformName', () {
      FakeProcessManager processManager;
      Directory tempDir;

      setUp(() async {
        processManager = new FakeProcessManager();
        tempDir = await Directory.systemTemp.createTemp('flutter_');
        tempDir.createSync();
      });

      tearDown(() async {
        // On Windows, the directory is locked and not able to be deleted yet. So
        // we just leave some (very small, because we're not actually building
        // archives here) trash around to be deleted at the next reboot.
        if (!platform.isWindows) {
          await tempDir.delete(recursive: true);
        }
      });

      test('calls the right processes', () async {
        final String releasesName = 'releases_$platformName.json';
232 233
        final String archiveName = platform.isLinux ? 'archive.tar.xz' : 'archive.zip';
        final String archiveMime = platform.isLinux ? 'application/x-gtar' : 'application/zip';
234
        final String archivePath = path.join(tempDir.absolute.path, archiveName);
235
        final String gsArchivePath = 'gs://flutter_infra/releases/release/$platformName/$archiveName';
236 237 238
        final String jsonPath = path.join(tempDir.absolute.path, releasesName);
        final String gsJsonPath = 'gs://flutter_infra/releases/$releasesName';
        final String releasesJson = '''{
239 240 241 242 243 244 245 246 247 248 249 250
  "base_url": "https://storage.googleapis.com/flutter_infra/releases",
  "current_release": {
    "beta": "3ea4d06340a97a1e9d7cae97567c64e0569dcaa2",
    "dev": "5a58b36e36b8d7aace89d3950e6deb307956a6a0"
  },
  "releases": [
    {
      "hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0",
      "channel": "dev",
      "version": "v0.2.3",
      "release_date": "2018-03-20T01:47:02.851729Z",
      "archive": "dev/$platformName/flutter_${platformName}_v0.2.3-dev.zip"
251
    },
252 253 254 255 256 257 258 259 260 261 262 263 264
    {
      "hash": "b9bd51cc36b706215915711e580851901faebb40",
      "channel": "beta",
      "version": "v0.2.2",
      "release_date": "2018-03-16T18:48:13.375013Z",
      "archive": "dev/$platformName/flutter_${platformName}_v0.2.2-dev.zip"
    },
    {
      "hash": "$testRef",
      "channel": "release",
      "version": "v0.0.0",
      "release_date": "2018-03-20T01:47:02.851729Z",
      "archive": "release/$platformName/flutter_${platformName}_v0.0.0-dev.zip"
265
    }
266
  ]
267
}
268
''';
269
        new File(jsonPath).writeAsStringSync(releasesJson);
270 271
        final Map<String, List<ProcessResult>> calls = <String, List<ProcessResult>>{
          'gsutil rm $gsArchivePath': null,
272
          'gsutil -h Content-Type:$archiveMime cp $archivePath $gsArchivePath': null,
273
          'gsutil cp $gsJsonPath $jsonPath': null,
274
          'gsutil rm $gsJsonPath': null,
275
          'gsutil -h Content-Type:application/json cp $jsonPath $gsJsonPath': null,
276 277
        };
        processManager.fakeResults = calls;
278
        final File outputFile = new File(path.join(tempDir.absolute.path, archiveName));
279 280 281 282
        assert(tempDir.existsSync());
        final ArchivePublisher publisher = new ArchivePublisher(
          tempDir,
          testRef,
283 284
          Branch.release,
          'v1.2.3',
285 286 287 288 289 290 291 292 293 294 295 296
          outputFile,
          processManager: processManager,
          subprocessOutput: false,
          platform: platform,
        );
        assert(tempDir.existsSync());
        await publisher.publishArchive();
        processManager.verifyCalls(calls.keys.toList());
        final File releaseFile = new File(jsonPath);
        expect(releaseFile.existsSync(), isTrue);
        final String contents = releaseFile.readAsStringSync();
        // Make sure new data is added.
297 298 299
        expect(contents, contains('"hash": "$testRef"'));
        expect(contents, contains('"channel": "release"'));
        expect(contents, contains('"archive": "release/$platformName/$archiveName"'));
300
        // Make sure existing entries are preserved.
301 302 303 304 305 306
        expect(contents, contains('"hash": "5a58b36e36b8d7aace89d3950e6deb307956a6a0"'));
        expect(contents, contains('"hash": "b9bd51cc36b706215915711e580851901faebb40"'));
        expect(contents, contains('"channel": "beta"'));
        expect(contents, contains('"channel": "dev"'));
        // Make sure old matching entries are removed.
        expect(contents, isNot(contains('v0.0.0')));
307
        final Map<String, dynamic> jsonData = json.decode(contents);
308 309 310 311 312 313 314 315
        final List<dynamic> releases = jsonData['releases'];
        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(
          new DateTime.now().difference(DateTime.parse(releases[0]['release_date'])),
          lessThan(const Duration(minutes: 1)),
        );
316
        const JsonEncoder encoder = const JsonEncoder.withIndent('  ');
317 318 319 320
        expect(contents, equals(encoder.convert(jsonData)));
      });
    });
  }
321
}