flutter_manifest_test.dart 20 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 6
import 'dart:async';

7 8
import 'package:file/file.dart';
import 'package:file/memory.dart';
9
import 'package:flutter_tools/src/base/context.dart';
10
import 'package:flutter_tools/src/base/file_system.dart';
11
import 'package:flutter_tools/src/base/logger.dart';
12 13 14
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';

15 16 17
import '../src/common.dart';
import '../src/context.dart';
import '../src/pubspec_schema.dart';
18 19 20 21 22 23 24

void main() {
  setUpAll(() {
    Cache.flutterRoot = getFlutterRoot();
  });

  group('FlutterManifest', () {
25
    testUsingContext('is empty when the pubspec.yaml file is empty', () async {
26
      final FlutterManifest flutterManifest = FlutterManifest.createFromString('');
27 28 29 30 31 32 33 34
      expect(flutterManifest.isEmpty, true);
      expect(flutterManifest.appName, '');
      expect(flutterManifest.usesMaterialDesign, false);
      expect(flutterManifest.fontsDescriptor, isEmpty);
      expect(flutterManifest.fonts, isEmpty);
      expect(flutterManifest.assets, isEmpty);
    });

35
    test('has no fonts or assets when the "flutter" section is empty', () async {
36
      const String manifest = '''
37 38 39 40 41
name: test
dependencies:
  flutter:
    sdk: flutter
''';
42
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
43
      expect(flutterManifest, isNotNull);
44
      expect(flutterManifest.isEmpty, false);
45 46 47 48 49 50 51 52
      expect(flutterManifest.appName, 'test');
      expect(flutterManifest.usesMaterialDesign, false);
      expect(flutterManifest.fontsDescriptor, isEmpty);
      expect(flutterManifest.fonts, isEmpty);
      expect(flutterManifest.assets, isEmpty);
    });

    test('knows if material design is used', () async {
53
      const String manifest = '''
54 55 56 57 58 59 60
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
''';
61
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
62 63 64 65
      expect(flutterManifest.usesMaterialDesign, true);
    });

    test('has two assets', () async {
66
      const String manifest = '''
67 68 69 70 71 72 73 74 75 76
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  assets:
    - a/foo
    - a/bar
''';
77
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
78 79 80
      expect(flutterManifest.assets.length, 2);
      expect(flutterManifest.assets[0], Uri.parse('a/foo'));
      expect(flutterManifest.assets[1], Uri.parse('a/bar'));
81 82 83
    });

    test('has one font family with one asset', () async {
84
      const String manifest = '''
85 86 87 88 89 90 91 92 93 94 95
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
''';
96
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
97 98
      final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
99 100 101
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 1);
      final Font font = fonts[0];
102 103
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(font.descriptor, fooFontDescriptor);
104 105 106 107
      expect(font.familyName, 'foo');
      final List<FontAsset> assets = font.fontAssets;
      expect(assets.length, 1);
      final FontAsset fontAsset = assets[0];
108
      expect(fontAsset.assetUri.path, 'a/bar');
109 110 111 112 113
      expect(fontAsset.weight, isNull);
      expect(fontAsset.style, isNull);
    });

    test('has one font family with a simple asset and one with weight', () async {
114
      const String manifest = '''
115 116 117 118 119 120 121 122 123 124 125 126 127
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
        - asset: a/bar
          weight: 400
''';
128
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
129 130
      final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
131 132 133
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 1);
      final Font font = fonts[0];
134 135
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(font.descriptor, fooFontDescriptor);
136 137 138 139
      expect(font.familyName, 'foo');
      final List<FontAsset> assets = font.fontAssets;
      expect(assets.length, 2);
      final FontAsset fontAsset0 = assets[0];
140
      expect(fontAsset0.assetUri.path, 'a/bar');
141 142 143
      expect(fontAsset0.weight, isNull);
      expect(fontAsset0.style, isNull);
      final FontAsset fontAsset1 = assets[1];
144
      expect(fontAsset1.assetUri.path, 'a/bar');
145 146 147 148 149
      expect(fontAsset1.weight, 400);
      expect(fontAsset1.style, isNull);
    });

    test('has one font family with a simple asset and one with weight and style', () async {
150
      const String manifest = '''
151 152 153 154 155 156 157 158 159 160 161 162 163 164
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
        - asset: a/bar
          weight: 400
          style: italic
''';
165
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
166
      final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
167

168
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
169 170 171
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 1);
      final Font font = fonts[0];
172 173
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(font.descriptor, fooFontDescriptor);
174 175 176 177
      expect(font.familyName, 'foo');
      final List<FontAsset> assets = font.fontAssets;
      expect(assets.length, 2);
      final FontAsset fontAsset0 = assets[0];
178
      expect(fontAsset0.assetUri.path, 'a/bar');
179 180 181
      expect(fontAsset0.weight, isNull);
      expect(fontAsset0.style, isNull);
      final FontAsset fontAsset1 = assets[1];
182
      expect(fontAsset1.assetUri.path, 'a/bar');
183 184 185 186 187
      expect(fontAsset1.weight, 400);
      expect(fontAsset1.style, 'italic');
    });

    test('has two font families, each with one simple asset and one with weight and style', () async {
188
      const String manifest = '''
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
        - asset: a/bar
          weight: 400
          style: italic
    - family: bar
      fonts:
        - asset: a/baz
        - weight: 400
          asset: a/baz
          style: italic
''';
209
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
210 211 212 213 214
      final dynamic expectedFontsDescriptor = <dynamic>[
          {'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}, // ignore: always_specify_types
          {'fonts': [{'asset': 'a/baz'}, {'style': 'italic', 'weight': 400, 'asset': 'a/baz'}], 'family': 'bar'}, // ignore: always_specify_types
      ];
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
215 216 217 218
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 2);

      final Font fooFont = fonts[0];
219 220
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(fooFont.descriptor, fooFontDescriptor);
221 222 223 224
      expect(fooFont.familyName, 'foo');
      final List<FontAsset> fooAassets = fooFont.fontAssets;
      expect(fooAassets.length, 2);
      final FontAsset fooFontAsset0 = fooAassets[0];
225
      expect(fooFontAsset0.assetUri.path, 'a/bar');
226 227 228
      expect(fooFontAsset0.weight, isNull);
      expect(fooFontAsset0.style, isNull);
      final FontAsset fooFontAsset1 = fooAassets[1];
229
      expect(fooFontAsset1.assetUri.path, 'a/bar');
230 231 232 233
      expect(fooFontAsset1.weight, 400);
      expect(fooFontAsset1.style, 'italic');

      final Font barFont = fonts[1];
234
      const String fontDescriptor = '{family: bar, fonts: [{asset: a/baz}, {weight: 400, style: italic, asset: a/baz}]}'; // ignore: always_specify_types
235 236 237 238 239
      expect(barFont.descriptor.toString(), fontDescriptor);
      expect(barFont.familyName, 'bar');
      final List<FontAsset> barAssets = barFont.fontAssets;
      expect(barAssets.length, 2);
      final FontAsset barFontAsset0 = barAssets[0];
240
      expect(barFontAsset0.assetUri.path, 'a/baz');
241 242 243
      expect(barFontAsset0.weight, isNull);
      expect(barFontAsset0.style, isNull);
      final FontAsset barFontAsset1 = barAssets[1];
244
      expect(barFontAsset1.assetUri.path, 'a/baz');
245 246 247 248
      expect(barFontAsset1.weight, 400);
      expect(barFontAsset1.style, 'italic');
    });

Josh Soref's avatar
Josh Soref committed
249
    testUsingContext('has only one of two font families when one declaration is missing the "family" option', () async {
250
      const String manifest = '''
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
        - asset: a/bar
          weight: 400
          style: italic
    - fonts:
        - asset: a/baz
        - asset: a/baz
          weight: 400
          style: italic
''';
270
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
271

272 273
      final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
274 275 276
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 1);
      final Font fooFont = fonts[0];
277 278
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(fooFont.descriptor, fooFontDescriptor);
279 280 281 282
      expect(fooFont.familyName, 'foo');
      final List<FontAsset> fooAassets = fooFont.fontAssets;
      expect(fooAassets.length, 2);
      final FontAsset fooFontAsset0 = fooAassets[0];
283
      expect(fooFontAsset0.assetUri.path, 'a/bar');
284 285 286
      expect(fooFontAsset0.weight, isNull);
      expect(fooFontAsset0.style, isNull);
      final FontAsset fooFontAsset1 = fooAassets[1];
287
      expect(fooFontAsset1.assetUri.path, 'a/bar');
288 289 290 291
      expect(fooFontAsset1.weight, 400);
      expect(fooFontAsset1.style, 'italic');
    });

Josh Soref's avatar
Josh Soref committed
292
    testUsingContext('has only one of two font families when one declaration is missing the "fonts" option', () async {
293
      const String manifest = '''
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
        - asset: a/bar
          weight: 400
          style: italic
    - family: bar
''';
309
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
310 311
      final dynamic expectedFontsDescriptor = [{'fonts': [{'asset': 'a/bar'}, {'style': 'italic', 'weight': 400, 'asset': 'a/bar'}], 'family': 'foo'}]; // ignore: always_specify_types
      expect(flutterManifest.fontsDescriptor, expectedFontsDescriptor);
312 313 314
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 1);
      final Font fooFont = fonts[0];
315 316
      final dynamic fooFontDescriptor = {'family': 'foo', 'fonts': [{'asset': 'a/bar'}, {'weight': 400, 'style': 'italic', 'asset': 'a/bar'}]}; // ignore: always_specify_types
      expect(fooFont.descriptor, fooFontDescriptor);
317 318 319 320
      expect(fooFont.familyName, 'foo');
      final List<FontAsset> fooAassets = fooFont.fontAssets;
      expect(fooAassets.length, 2);
      final FontAsset fooFontAsset0 = fooAassets[0];
321
      expect(fooFontAsset0.assetUri.path, 'a/bar');
322 323 324
      expect(fooFontAsset0.weight, isNull);
      expect(fooFontAsset0.style, isNull);
      final FontAsset fooFontAsset1 = fooAassets[1];
325
      expect(fooFontAsset1.assetUri.path, 'a/bar');
326 327 328 329 330
      expect(fooFontAsset1.weight, 400);
      expect(fooFontAsset1.style, 'italic');
    });

    testUsingContext('has no font family when declaration is missing the "asset" option', () async {
331
      const String manifest = '''
332 333 334 335 336 337 338 339 340 341 342
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - weight: 400
''';
343
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
344

345
      expect(flutterManifest.fontsDescriptor, <dynamic>[]);
346 347 348
      final List<Font> fonts = flutterManifest.fonts;
      expect(fonts.length, 0);
    });
349 350 351 352 353 354 355 356 357

    test('allows a blank flutter section', () async {
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
358
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
359
      expect(flutterManifest.isEmpty, false);
360
      expect(flutterManifest.isModule, false);
361 362 363 364
      expect(flutterManifest.isPlugin, false);
      expect(flutterManifest.androidPackage, null);
    });

365
    test('allows a module declaration', () async {
366 367 368
      const String manifest = '''
name: test
flutter:
369
  module:
370 371
    androidPackage: com.example
''';
372
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
373
      expect(flutterManifest.isModule, true);
374 375 376
      expect(flutterManifest.androidPackage, 'com.example');
    });

377
    test('allows a legacy plugin declaration', () async {
378 379 380 381 382 383
      const String manifest = '''
name: test
flutter:
  plugin:
    androidPackage: com.example
''';
384
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
385 386
      expect(flutterManifest.isPlugin, true);
      expect(flutterManifest.androidPackage, 'com.example');
387
    });
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
    test('allows a multi-plat plugin declaration', () async {
      const String manifest = '''
name: test
flutter:
    plugin:
      platforms:
        android:
          package: com.example
          pluginClass: TestPlugin
''';
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
      expect(flutterManifest.isPlugin, true);
      expect(flutterManifest.androidPackage, 'com.example');
    });

403 404 405 406 407

    Future<void> checkManifestVersion({
      String manifest,
      String expectedAppVersion,
      String expectedBuildName,
408
      String expectedBuildNumber,
409
    }) async {
410
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
411 412 413 414 415
      expect(flutterManifest.appVersion, expectedAppVersion);
      expect(flutterManifest.buildName, expectedBuildName);
      expect(flutterManifest.buildNumber, expectedBuildNumber);
    }

416
    test('parses major.minor.patch+build version clause 1', () async {
417 418 419 420 421 422 423 424 425 426 427 428
      const String manifest = '''
name: test
version: 1.0.0+2
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
        expectedAppVersion: '1.0.0+2',
        expectedBuildName: '1.0.0',
429
        expectedBuildNumber: '2',
430 431 432
      );
    });

433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
    test('parses major.minor.patch with no build version', () async {
      const String manifest = '''
name: test
version: 0.0.1
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
        expectedAppVersion: '0.0.1',
        expectedBuildName: '0.0.1',
        expectedBuildNumber: null,
      );
    });

450
    test('parses major.minor.patch+build version clause 2', () async {
451 452
      const String manifest = '''
name: test
453
version: 1.0.0-beta+exp.sha.5114f85
454 455 456 457 458 459 460
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
461 462 463
        expectedAppVersion: '1.0.0-beta+exp.sha.5114f85',
        expectedBuildName: '1.0.0-beta',
        expectedBuildNumber: 'exp.sha.5114f85',
464 465 466
      );
    });

467
    test('parses major.minor+build version clause', () async {
468 469
      const String manifest = '''
name: test
470
version: 1.0+2
471 472 473 474 475 476 477
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
478 479 480
        expectedAppVersion: '1.0+2',
        expectedBuildName: '1.0',
        expectedBuildNumber: '2',
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
      );
    });

    test('parses empty version clause', () async {
      const String manifest = '''
name: test
version:
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
        expectedAppVersion: null,
        expectedBuildName: null,
        expectedBuildNumber: null,
      );
    });
500

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    test('parses no version clause', () async {
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
''';
      await checkManifestVersion(
        manifest: manifest,
        expectedAppVersion: null,
        expectedBuildName: null,
        expectedBuildNumber: null,
      );
    });
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535

    // Regression test for https://github.com/flutter/flutter/issues/31764
    testUsingContext('Returns proper error when font detail is malformed', () async {
      final BufferLogger logger = context.get<Logger>();
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  fonts:
    - family: foo
      fonts:
        -asset: a/bar
''';
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);

      expect(flutterManifest, null);
      expect(logger.errorText, contains('Expected "fonts" to either be null or a list.'));
    });
536

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
    testUsingContext('Returns proper error when font is a map instead of a list', () async {
      final BufferLogger logger = context.get<Logger>();
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  fonts:
    family: foo
    fonts:
      -asset: a/bar
''';
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);

      expect(flutterManifest, null);
      expect(logger.errorText, contains('Expected "fonts" to be a list'));
    });

556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574
    testUsingContext('Returns proper error when second font family is invalid', () async {
      final BufferLogger logger = context.get<Logger>();
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
  uses-material-design: true
  fonts:
    - family: foo
      fonts:
        - asset: a/bar
    - string
''';
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
      expect(flutterManifest, null);
      expect(logger.errorText, contains('Expected a map.'));
    });
575
  });
576 577

  group('FlutterManifest with MemoryFileSystem', () {
578
    Future<void> assertSchemaIsReadable() async {
579 580 581 582 583 584 585 586
      const String manifest = '''
name: test
dependencies:
  flutter:
    sdk: flutter
flutter:
''';

587
      final FlutterManifest flutterManifest = FlutterManifest.createFromString(manifest);
588 589 590
      expect(flutterManifest.isEmpty, false);
    }

591 592 593 594 595
    void testUsingContextAndFs(
      String description,
      FileSystem filesystem,
      dynamic testMethod(),
    ) {
596 597 598 599 600 601 602 603 604
      testUsingContext(
        description,
        () async {
          writeEmptySchemaFile(filesystem);
          testMethod();
        },
        overrides: <Type, Generator>{
          FileSystem: () => filesystem,
        },
605 606 607
      );
    }

608
    testUsingContext('Validate manifest on original fs', () {
609 610 611
      assertSchemaIsReadable();
    });

612 613 614 615 616 617
    testUsingContextAndFs(
      'Validate manifest on Posix FS',
      MemoryFileSystem(style: FileSystemStyle.posix),
      () {
        assertSchemaIsReadable();
      },
618 619
    );

620 621 622 623 624 625
    testUsingContextAndFs(
      'Validate manifest on Windows FS',
      MemoryFileSystem(style: FileSystemStyle.windows),
      () {
        assertSchemaIsReadable();
      },
626 627 628 629
    );

  });

630
}
631