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

5 6
// @dart = 2.8

7 8 9
import 'dart:convert';

import 'package:file/file.dart';
10
import 'package:file/memory.dart';
11

12 13
import 'package:flutter_tools/src/asset.dart';
import 'package:flutter_tools/src/base/file_system.dart';
14

15
import 'package:flutter_tools/src/globals.dart' as globals;
16

17 18
import '../src/common.dart';
import '../src/context.dart';
19 20

void main() {
21 22 23 24 25 26
  String fixPath(String path) {
    // The in-memory file system is strict about slashes on Windows being the
    // correct way so until https://github.com/google/file.dart/issues/112 is
    // fixed we fix them here.
    // TODO(dantup): Remove this function once the above issue is fixed and
    // rolls into Flutter.
27
    return path?.replaceAll('/', globals.fs.path.separator);
28
  }
29
  void writePubspecFile(String path, String name, { List<String> assets }) {
30 31 32 33
    String assetsSection;
    if (assets == null) {
      assetsSection = '';
    } else {
34
      final StringBuffer buffer = StringBuffer();
35 36 37 38 39
      buffer.write('''
flutter:
     assets:
''');

40
      for (final String asset in assets) {
41 42 43 44 45 46 47
        buffer.write('''
       - $asset
''');
      }
      assetsSection = buffer.toString();
    }

48
    globals.fs.file(fixPath(path))
49 50 51 52 53 54 55 56 57 58 59
      ..createSync(recursive: true)
      ..writeAsStringSync('''
name: $name
dependencies:
  flutter:
    sdk: flutter
$assetsSection
''');
  }

  void writePackagesFile(String packages) {
60
    globals.fs.file('.packages')
61 62 63 64
      ..createSync()
      ..writeAsStringSync(packages);
  }

65
  Future<void> buildAndVerifyAssets(
66 67
    List<String> assets,
    List<String> packages,
68 69 70
    String expectedAssetManifest, {
    bool expectExists = true,
  }) async {
71
    final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
72
    await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages');
73

74 75
    for (final String packageName in packages) {
      for (final String asset in assets) {
76
        final String entryKey = Uri.encodeFull('packages/$packageName/$asset');
77 78 79 80 81 82 83 84
        expect(bundle.entries.containsKey(entryKey), expectExists,
          reason: 'Cannot find key on bundle: $entryKey');
        if (expectExists) {
          expect(
            utf8.decode(await bundle.entries[entryKey].contentsAsBytes()),
            asset,
          );
        }
85 86 87
      }
    }

88 89 90 91 92 93
    if (expectExists) {
      expect(
        utf8.decode(await bundle.entries['AssetManifest.json'].contentsAsBytes()),
        expectedAssetManifest,
      );
    }
94 95 96
  }

  void writeAssets(String path, List<String> assets) {
97
    for (final String asset in assets) {
98
      final String fullPath = fixPath(globals.fs.path.join(path, asset));
99

100
      globals.fs.file(fullPath)
101 102 103 104 105
        ..createSync(recursive: true)
        ..writeAsStringSync(asset);
    }
  }

106
  FileSystem testFileSystem;
107 108

  setUp(() async {
109
    testFileSystem = MemoryFileSystem(
110
      style: globals.platform.isWindows
111 112 113 114
        ? FileSystemStyle.windows
        : FileSystemStyle.posix,
    );
    testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
115 116
  });

117 118
  group('AssetBundle assets from packages', () {
    testUsingContext('No assets are bundled when the package has no assets', () async {
119 120 121 122
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');
      writePubspecFile('p/p/pubspec.yaml', 'test_package');

123
      final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
124
      await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages');
125
      expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest
126
      const String expectedAssetManifest = '{}';
127
      expect(
128
        utf8.decode(await bundle.entries['AssetManifest.json'].contentsAsBytes()),
129 130
        expectedAssetManifest,
      );
131
      expect(
132
        utf8.decode(await bundle.entries['FontManifest.json'].contentsAsBytes()),
133 134
        '[]',
      );
135 136
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
137
      ProcessManager: () => FakeProcessManager.any(),
138
    });
139

140 141 142 143 144 145 146 147
    testUsingContext('No assets are bundled when the package has an asset that is not listed', () async {
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');
      writePubspecFile('p/p/pubspec.yaml', 'test_package');

      final List<String> assets = <String>['a/foo'];
      writeAssets('p/p/', assets);

148
      final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
149
      await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages');
150
      expect(bundle.entries.length, 3); // LICENSE, AssetManifest, FontManifest
151
      const String expectedAssetManifest = '{}';
152
      expect(
153
        utf8.decode(await bundle.entries['AssetManifest.json'].contentsAsBytes()),
154 155
        expectedAssetManifest,
      );
156
      expect(
157
        utf8.decode(await bundle.entries['FontManifest.json'].contentsAsBytes()),
158 159
        '[]',
      );
160 161
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
162
      ProcessManager: () => FakeProcessManager.any(),
163
    });
164

165 166
    testUsingContext('One asset is bundled when the package has and lists one '
      'asset its pubspec', () async {
167 168 169 170 171 172 173 174 175 176 177 178
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assets = <String>['a/foo'];
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assets,
      );

      writeAssets('p/p/', assets);

179
      const String expectedAssetManifest = '{"packages/test_package/a/foo":'
180 181 182 183 184 185
          '["packages/test_package/a/foo"]}';
      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedAssetManifest,
      );
186 187
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
188
      ProcessManager: () => FakeProcessManager.any(),
189
    });
190

191 192
    testUsingContext('One asset is bundled when the package has one asset, '
      "listed in the app's pubspec", () async {
193 194 195 196 197 198 199 200 201 202 203 204
      final List<String> assetEntries = <String>['packages/test_package/a/foo'];
      writePubspecFile(
        'pubspec.yaml',
        'test',
        assets: assetEntries,
      );
      writePackagesFile('test_package:p/p/lib/');
      writePubspecFile('p/p/pubspec.yaml', 'test_package');

      final List<String> assets = <String>['a/foo'];
      writeAssets('p/p/lib/', assets);

205
      const String expectedAssetManifest = '{"packages/test_package/a/foo":'
206 207 208 209 210 211
          '["packages/test_package/a/foo"]}';
      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedAssetManifest,
      );
212 213
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
214
      ProcessManager: () => FakeProcessManager.any(),
215
    });
216

217 218
    testUsingContext('One asset and its variant are bundled when the package '
      'has an asset and a variant, and lists the asset in its pubspec', () async {
219 220 221 222 223 224 225 226 227 228 229
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: <String>['a/foo'],
      );

      final List<String> assets = <String>['a/foo', 'a/v/foo'];
      writeAssets('p/p/', assets);

230
      const String expectedManifest = '{"packages/test_package/a/foo":'
231 232 233 234 235 236 237
          '["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';

      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedManifest,
      );
238 239
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
240
      ProcessManager: () => FakeProcessManager.any(),
241
    });
242

243 244
    testUsingContext('One asset and its variant are bundled when the package '
      'has an asset and a variant, and the app lists the asset in its pubspec', () async {
245 246 247 248 249 250 251 252 253 254 255 256 257 258
      writePubspecFile(
        'pubspec.yaml',
        'test',
        assets: <String>['packages/test_package/a/foo'],
      );
      writePackagesFile('test_package:p/p/lib/');
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
      );

      final List<String> assets = <String>['a/foo', 'a/v/foo'];
      writeAssets('p/p/lib/', assets);

259
      const String expectedManifest = '{"packages/test_package/a/foo":'
260 261 262 263 264 265 266
          '["packages/test_package/a/foo","packages/test_package/a/v/foo"]}';

      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedManifest,
      );
267 268
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
269
      ProcessManager: () => FakeProcessManager.any(),
270
    });
271

272 273
    testUsingContext('Two assets are bundled when the package has and lists '
      'two assets in its pubspec', () async {
274 275 276 277 278 279 280 281 282 283 284
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assets = <String>['a/foo', 'a/bar'];
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assets,
      );

      writeAssets('p/p/', assets);
285
      const String expectedAssetManifest =
286 287
          '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
          '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
288 289 290 291 292 293

      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedAssetManifest,
      );
294 295
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
296
      ProcessManager: () => FakeProcessManager.any(),
297
    });
298

299
    testUsingContext("Two assets are bundled when the package has two assets, listed in the app's pubspec", () async {
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
      final List<String> assetEntries = <String>[
        'packages/test_package/a/foo',
        'packages/test_package/a/bar',
      ];
      writePubspecFile(
        'pubspec.yaml',
        'test',
         assets: assetEntries,
      );
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assets = <String>['a/foo', 'a/bar'];
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
      );

      writeAssets('p/p/lib/', assets);
318
      const String expectedAssetManifest =
319 320
          '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
          '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';
321 322 323 324 325 326

      await buildAndVerifyAssets(
        assets,
        <String>['test_package'],
        expectedAssetManifest,
      );
327 328
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
329
      ProcessManager: () => FakeProcessManager.any(),
330
    });
331

332
    testUsingContext('Two assets are bundled when two packages each have and list an asset their pubspec', () async {
333 334 335 336
      writePubspecFile(
        'pubspec.yaml',
        'test',
      );
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
      writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: <String>['a/foo'],
      );
      writePubspecFile(
        'p2/p/pubspec.yaml',
        'test_package2',
        assets: <String>['a/foo'],
      );

      final List<String> assets = <String>['a/foo', 'a/v/foo'];
      writeAssets('p/p/', assets);
      writeAssets('p2/p/', assets);

353
      const String expectedAssetManifest =
354 355 356 357 358 359 360 361 362 363
          '{"packages/test_package/a/foo":'
          '["packages/test_package/a/foo","packages/test_package/a/v/foo"],'
          '"packages/test_package2/a/foo":'
          '["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';

      await buildAndVerifyAssets(
        assets,
        <String>['test_package', 'test_package2'],
        expectedAssetManifest,
      );
364 365
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
366
      ProcessManager: () => FakeProcessManager.any(),
367
    });
368

369
    testUsingContext("Two assets are bundled when two packages each have an asset, listed in the app's pubspec", () async {
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
      final List<String> assetEntries = <String>[
        'packages/test_package/a/foo',
        'packages/test_package2/a/foo',
      ];
      writePubspecFile(
        'pubspec.yaml',
        'test',
        assets: assetEntries,
      );
      writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
      );
      writePubspecFile(
        'p2/p/pubspec.yaml',
        'test_package2',
      );

      final List<String> assets = <String>['a/foo', 'a/v/foo'];
      writeAssets('p/p/lib/', assets);
      writeAssets('p2/p/lib/', assets);

393
      const String expectedAssetManifest =
394 395 396 397 398 399 400 401 402 403
          '{"packages/test_package/a/foo":'
          '["packages/test_package/a/foo","packages/test_package/a/v/foo"],'
          '"packages/test_package2/a/foo":'
          '["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';

      await buildAndVerifyAssets(
        assets,
        <String>['test_package', 'test_package2'],
        expectedAssetManifest,
      );
404 405
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
406
      ProcessManager: () => FakeProcessManager.any(),
407
    });
408

409 410
    testUsingContext('One asset is bundled when the app depends on a package, '
      'listing in its pubspec an asset from another package', () async {
411 412 413 414 415 416 417 418
      writePubspecFile(
        'pubspec.yaml',
        'test',
      );
      writePackagesFile('test_package:p/p/lib/\ntest_package2:p2/p/lib/');
      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
419
        assets: <String>['packages/test_package2/a/foo'],
420 421 422 423 424 425 426 427 428
      );
      writePubspecFile(
        'p2/p/pubspec.yaml',
        'test_package2',
      );

      final List<String> assets = <String>['a/foo', 'a/v/foo'];
      writeAssets('p2/p/lib/', assets);

429
      const String expectedAssetManifest =
430 431 432 433 434 435 436 437
          '{"packages/test_package2/a/foo":'
          '["packages/test_package2/a/foo","packages/test_package2/a/v/foo"]}';

      await buildAndVerifyAssets(
        assets,
        <String>['test_package2'],
        expectedAssetManifest,
      );
438 439
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
440
      ProcessManager: () => FakeProcessManager.any(),
441
    });
442
  });
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

  testUsingContext('Asset paths can contain URL reserved characters', () async {
    writePubspecFile('pubspec.yaml', 'test');
    writePackagesFile('test_package:p/p/lib/');

    final List<String> assets = <String>['a/foo', 'a/foo[x]'];
    writePubspecFile(
      'p/p/pubspec.yaml',
      'test_package',
      assets: assets,
    );

    writeAssets('p/p/', assets);
    const String expectedAssetManifest =
        '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
        '"packages/test_package/a/foo%5Bx%5D":["packages/test_package/a/foo%5Bx%5D"]}';

    await buildAndVerifyAssets(
      assets,
      <String>['test_package'],
      expectedAssetManifest,
    );
465 466
  }, overrides: <Type, Generator>{
    FileSystem: () => testFileSystem,
467
      ProcessManager: () => FakeProcessManager.any(),
468
  });
469 470

  group('AssetBundle assets from scanned paths', () {
471
    testUsingContext('Two assets are bundled when scanning their directory', () async {
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetsOnDisk = <String>['a/foo', 'a/bar'];
      final List<String> assetsOnManifest = <String>['a/'];

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetsOnManifest,
      );

      writeAssets('p/p/', assetsOnDisk);
      const String expectedAssetManifest =
          '{"packages/test_package/a/bar":["packages/test_package/a/bar"],'
          '"packages/test_package/a/foo":["packages/test_package/a/foo"]}';

      await buildAndVerifyAssets(
        assetsOnDisk,
        <String>['test_package'],
        expectedAssetManifest,
      );
494 495
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
496
      ProcessManager: () => FakeProcessManager.any(),
497 498
    });

499
    testUsingContext('Two assets are bundled when listing one and scanning second directory', () async {
500 501 502 503 504 505 506 507 508 509 510 511 512 513
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetsOnDisk = <String>['a/foo', 'abc/bar'];
      final List<String> assetOnManifest = <String>['a/foo', 'abc/'];

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetOnManifest,
      );

      writeAssets('p/p/', assetsOnDisk);
      const String expectedAssetManifest =
514 515
          '{"packages/test_package/a/foo":["packages/test_package/a/foo"],'
          '"packages/test_package/abc/bar":["packages/test_package/abc/bar"]}';
516 517 518 519 520 521

      await buildAndVerifyAssets(
        assetsOnDisk,
        <String>['test_package'],
        expectedAssetManifest,
      );
522 523
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
524
      ProcessManager: () => FakeProcessManager.any(),
525 526
    });

527
    testUsingContext('One asset is bundled with variant, scanning wrong directory', () async {
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetsOnDisk = <String>['a/foo','a/b/foo','a/bar'];
      final List<String> assetOnManifest = <String>['a','a/bar']; // can't list 'a' as asset, should be 'a/'

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetOnManifest,
      );

      writeAssets('p/p/', assetsOnDisk);

      final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
543
      await bundle.build(manifestPath: 'pubspec.yaml', packagesPath: '.packages');
544 545 546

      expect(bundle.entries['AssetManifest.json'], isNull,
        reason: 'Invalid pubspec.yaml should not generate AssetManifest.json'  );
547 548
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
549
      ProcessManager: () => FakeProcessManager.any(),
550 551 552 553
    });
  });

  group('AssetBundle assets from scanned paths with MemoryFileSystem', () {
554
    testUsingContext('One asset is bundled with variant, scanning directory', () async {
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetsOnDisk = <String>['a/foo','a/b/foo'];
      final List<String> assetOnManifest = <String>['a/',];

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetOnManifest,
      );

      writeAssets('p/p/', assetsOnDisk);
      const String expectedAssetManifest =
          '{"packages/test_package/a/foo":["packages/test_package/a/foo","packages/test_package/a/b/foo"]}';

      await buildAndVerifyAssets(
        assetsOnDisk,
        <String>['test_package'],
        expectedAssetManifest,
      );
576 577
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
578
      ProcessManager: () => FakeProcessManager.any(),
579 580
    });

581
    testUsingContext('No asset is bundled with variant, no assets or directories are listed', () async {
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetsOnDisk = <String>['a/foo', 'a/b/foo'];
      final List<String> assetOnManifest = <String>[];

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetOnManifest,
      );

      writeAssets('p/p/', assetsOnDisk);
      const String expectedAssetManifest = '{}';

      await buildAndVerifyAssets(
        assetOnManifest,
        <String>['test_package'],
        expectedAssetManifest,
      );
602 603
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
604
      ProcessManager: () => FakeProcessManager.any(),
605 606
    });

607
    testUsingContext('Expect error generating manifest, wrong non-existing directory is listed', () async {
608 609 610 611 612 613 614 615 616 617 618
      writePubspecFile('pubspec.yaml', 'test');
      writePackagesFile('test_package:p/p/lib/');

      final List<String> assetOnManifest = <String>['c/'];

      writePubspecFile(
        'p/p/pubspec.yaml',
        'test_package',
        assets: assetOnManifest,
      );

619 620 621 622 623 624
      await buildAndVerifyAssets(
        assetOnManifest,
        <String>['test_package'],
        null,
        expectExists: false,
      );
625 626
    }, overrides: <Type, Generator>{
      FileSystem: () => testFileSystem,
627
      ProcessManager: () => FakeProcessManager.any(),
628 629
    });
  });
630
}