web_test.dart 13.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_tools/src/base/file_system.dart';
6

7
import 'package:flutter_tools/src/build_system/build_system.dart';
8
import 'package:flutter_tools/src/build_system/depfile.dart';
9 10
import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/web.dart';
11
import 'package:flutter_tools/src/dart/package_map.dart';
12
import 'package:flutter_tools/src/globals.dart' as globals;
13 14
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
15
import 'package:platform/platform.dart';
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

import '../../../src/common.dart';
import '../../../src/mocks.dart';
import '../../../src/testbed.dart';

void main() {
  Testbed testbed;
  Environment environment;
  MockPlatform mockPlatform;
  MockPlatform  mockWindowsPlatform;

  setUp(() {
    mockPlatform = MockPlatform();
    mockWindowsPlatform = MockPlatform();

    when(mockPlatform.isWindows).thenReturn(false);
    when(mockPlatform.isMacOS).thenReturn(true);
    when(mockPlatform.isLinux).thenReturn(false);

    when(mockWindowsPlatform.isWindows).thenReturn(true);
    when(mockWindowsPlatform.isMacOS).thenReturn(false);
    when(mockWindowsPlatform.isLinux).thenReturn(false);

    testbed = Testbed(setup: () {
40
      final File packagesFile = globals.fs.file(globals.fs.path.join('foo', '.packages'))
41 42 43 44
        ..createSync(recursive: true)
        ..writeAsStringSync('foo:lib/\n');
      PackageMap.globalPackagesPath = packagesFile.path;

45
      environment = Environment(
46 47 48
        projectDir: globals.fs.currentDirectory.childDirectory('foo'),
        outputDir: globals.fs.currentDirectory,
        buildDir: globals.fs.currentDirectory,
49
        defines: <String, String>{
50
          kTargetFile: globals.fs.path.join('foo', 'lib', 'main.dart'),
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        }
      );
      environment.buildDir.createSync(recursive: true);
    }, overrides: <Type, Generator>{
      Platform: () => mockPlatform,
    });
  });

  test('WebEntrypointTarget generates an entrypoint with plugins and init platform', () => testbed.run(() async {
    environment.defines[kHasWebPlugins] = 'true';
    environment.defines[kInitializePlatform] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
67
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
68 69 70 71 72 73 74 75 76
    expect(generated, contains('registerPlugins(webPluginRegistry);'));

    // Platform
    expect(generated, contains('if (true) {'));

    // Main
    expect(generated, contains('entrypoint.main();'));

    // Import.
77
    expect(generated, contains("import 'package:foo/main.dart' as entrypoint;"));
78 79
  }));

80
  test('WebEntrypointTarget generates an entrypoint for a file outside of main', () => testbed.run(() async {
81
    environment.defines[kTargetFile] = globals.fs.path.join('other', 'lib', 'main.dart');
82 83 84 85 86 87 88 89 90
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Import.
    expect(generated, contains("import 'file:///other/lib/main.dart' as entrypoint;"));
  }));


91 92 93 94 95 96 97 98
  test('WebEntrypointTarget generates an entrypoint with plugins and init platform on windows', () => testbed.run(() async {
    environment.defines[kHasWebPlugins] = 'true';
    environment.defines[kInitializePlatform] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
99
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
100 101 102 103 104 105 106 107 108
    expect(generated, contains('registerPlugins(webPluginRegistry);'));

    // Platform
    expect(generated, contains('if (true) {'));

    // Main
    expect(generated, contains('entrypoint.main();'));

    // Import.
109
    expect(generated, contains("import 'package:foo/main.dart' as entrypoint;"));
110 111 112 113 114 115 116 117 118 119 120 121
  }, overrides: <Type, Generator>{
    Platform: () => mockWindowsPlatform,
  }));

  test('WebEntrypointTarget generates an entrypoint without plugins and init platform', () => testbed.run(() async {
    environment.defines[kHasWebPlugins] = 'false';
    environment.defines[kInitializePlatform] = 'true';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
122
    expect(generated, isNot(contains("import 'package:foo/generated_plugin_registrant.dart';")));
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    expect(generated, isNot(contains('registerPlugins(webPluginRegistry);')));

    // Platform
    expect(generated, contains('if (true) {'));

    // Main
    expect(generated, contains('entrypoint.main();'));
  }));

  test('WebEntrypointTarget generates an entrypoint with plugins and without init platform', () => testbed.run(() async {
    environment.defines[kHasWebPlugins] = 'true';
    environment.defines[kInitializePlatform] = 'false';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
140
    expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';"));
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
    expect(generated, contains('registerPlugins(webPluginRegistry);'));

    // Platform
    expect(generated, contains('if (false) {'));

    // Main
    expect(generated, contains('entrypoint.main();'));
  }));

  test('WebEntrypointTarget generates an entrypoint without plugins and without init platform', () => testbed.run(() async {
    environment.defines[kHasWebPlugins] = 'false';
    environment.defines[kInitializePlatform] = 'false';
    await const WebEntrypointTarget().build(environment);

    final String generated = environment.buildDir.childFile('main.dart').readAsStringSync();

    // Plugins
158
    expect(generated, isNot(contains("import 'package:foo/generated_plugin_registrant.dart';")));
159 160 161 162 163 164 165 166 167 168 169
    expect(generated, isNot(contains('registerPlugins(webPluginRegistry);')));

    // Platform
    expect(generated, contains('if (false) {'));

    // Main
    expect(generated, contains('entrypoint.main();'));
  }));

  test('Dart2JSTarget calls dart2js with expected args in profile mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
170
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
171 172 173 174 175
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    final List<String> expected = <String>[
176 177 178
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
      '--libraries-spec=' + globals.fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
179 180
      '-O4', // highest optimizations
      '--no-minify', // but uses unminified names for debugging
181 182
      '-o',
      environment.buildDir.childFile('main.dart.js').absolute.path,
183
      '--packages=${globals.fs.path.join('foo', '.packages')}',
184 185 186
      '-Ddart.vm.profile=true',
      environment.buildDir.childFile('main.dart').absolute.path,
    ];
187
    verify(globals.processManager.run(expected)).called(1);
188 189 190 191 192 193
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));

  test('Dart2JSTarget calls dart2js with expected args in release mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
194
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
195 196 197 198 199
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    final List<String> expected = <String>[
200 201 202
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
      '--libraries-spec=' + globals.fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
203 204 205
      '-O4', // highest optimizations.
      '-o',
      environment.buildDir.childFile('main.dart.js').absolute.path,
206
      '--packages=${globals.fs.path.join('foo', '.packages')}',
207 208 209
      '-Ddart.vm.product=true',
      environment.buildDir.childFile('main.dart').absolute.path,
    ];
210
    verify(globals.processManager.run(expected)).called(1);
211 212 213 214 215 216 217
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));

  test('Dart2JSTarget calls dart2js with expected args in release with dart2js optimization override', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    environment.defines[kDart2jsOptimization] = 'O3';
218
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
219 220 221 222 223
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    final List<String> expected = <String>[
224 225 226
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
      '--libraries-spec=' + globals.fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
227 228 229
      '-O3', // configured optimizations.
      '-o',
      environment.buildDir.childFile('main.dart.js').absolute.path,
230
      '--packages=${globals.fs.path.join('foo', '.packages')}',
231 232 233
      '-Ddart.vm.product=true',
      environment.buildDir.childFile('main.dart').absolute.path,
    ];
234
    verify(globals.processManager.run(expected)).called(1);
235 236 237
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));
238 239 240

  test('Dart2JSTarget produces expected depfile', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
241
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
242 243 244 245 246 247 248 249 250
      environment.buildDir.childFile('main.dart.js.deps')
        ..writeAsStringSync('file:///a.dart');
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    expect(environment.buildDir.childFile('dart2js.d').existsSync(), true);
    final Depfile depfile = Depfile.parse(environment.buildDir.childFile('dart2js.d'));

251
    expect(depfile.inputs.single.path, globals.fs.path.absolute('a.dart'));
252 253 254 255 256
    expect(depfile.outputs.single.path,
      environment.buildDir.childFile('main.dart.js').absolute.path);
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));
257 258 259 260

  test('Dart2JSTarget calls dart2js with Dart defines in release mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'release';
    environment.defines[kDartDefines] = '["FOO=bar","BAZ=qux"]';
261
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
262 263 264 265 266
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    final List<String> expected = <String>[
267 268 269
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
      '--libraries-spec=' + globals.fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
270 271 272
      '-O4',
      '-o',
      environment.buildDir.childFile('main.dart.js').absolute.path,
273
      '--packages=${globals.fs.path.join('foo', '.packages')}',
274 275 276 277 278
      '-Ddart.vm.product=true',
      '-DFOO=bar',
      '-DBAZ=qux',
      environment.buildDir.childFile('main.dart').absolute.path,
    ];
279
    verify(globals.processManager.run(expected)).called(1);
280 281 282 283 284 285 286
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));

  test('Dart2JSTarget calls dart2js with Dart defines in profile mode', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
    environment.defines[kDartDefines] = '["FOO=bar","BAZ=qux"]';
287
    when(globals.processManager.run(any)).thenAnswer((Invocation invocation) async {
288 289 290 291 292
      return FakeProcessResult(exitCode: 0);
    });
    await const Dart2JSTarget().build(environment);

    final List<String> expected = <String>[
293 294 295
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
      globals.fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
      '--libraries-spec=' + globals.fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
296 297 298 299
      '-O4',
      '--no-minify',
      '-o',
      environment.buildDir.childFile('main.dart.js').absolute.path,
300
      '--packages=${globals.fs.path.join('foo', '.packages')}',
301 302 303 304 305
      '-Ddart.vm.profile=true',
      '-DFOO=bar',
      '-DBAZ=qux',
      environment.buildDir.childFile('main.dart').absolute.path,
    ];
306
    verify(globals.processManager.run(expected)).called(1);
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));

  test('Dart2JSTarget throws developer-friendly exception on misformatted DartDefines', () => testbed.run(() async {
    environment.defines[kBuildMode] = 'profile';
    environment.defines[kDartDefines] = '[misformatted json';
    try {
      await const Dart2JSTarget().build(environment);
      fail('Call to build() must not have succeeded.');
    } on Exception catch(exception) {
      expect(
        '$exception',
        'Exception: The value of -D$kDartDefines is not formatted correctly.\n'
        'The value must be a JSON-encoded list of strings but was:\n'
        '[misformatted json',
      );
    }

    // Should not attempt to run any processes.
327
    verifyNever(globals.processManager.run(any));
328 329 330
  }, overrides: <Type, Generator>{
    ProcessManager: () => MockProcessManager(),
  }));
331 332 333 334
}

class MockProcessManager extends Mock implements ProcessManager {}
class MockPlatform extends Mock implements Platform {}