// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:file/memory.dart'; import 'package:flutter_tools/src/asset.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/user_messages.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/project.dart'; import '../src/common.dart'; import '../src/package_config.dart'; void main() { Future buildBundleWithFlavor( String? flavor, { required Logger logger, required FileSystem fileSystem, required Platform platform, }) async { final bundle = ManifestAssetBundle( logger: logger, fileSystem: fileSystem, platform: platform, flutterRoot: Cache.defaultFlutterRoot( platform: platform, fileSystem: fileSystem, userMessages: UserMessages(), ), splitDeferredAssets: true, ); await bundle.build( packageConfigPath: '.dart_tool/package_config.json', flutterProject: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory), flavor: flavor, targetPlatform: TargetPlatform.tester, ); return bundle; } testWithoutContext( 'correctly bundles assets given a simple asset manifest with flavors', () async { final fileSystem = MemoryFileSystem(); fileSystem.currentDirectory = fileSystem.systemTempDirectory.createTempSync( 'flutter_asset_bundle_test.', ); final logger = BufferLogger.test(); final platform = FakePlatform(); writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'example'); fileSystem .file(fileSystem.path.join('assets', 'common', 'image.png')) .createSync(recursive: true); fileSystem .file(fileSystem.path.join('assets', 'vanilla', 'ice-cream.png')) .createSync(recursive: true); fileSystem .file(fileSystem.path.join('assets', 'strawberry', 'ice-cream.png')) .createSync(recursive: true); fileSystem .file(fileSystem.path.join('assets', 'orange', 'ice-cream.png')) .createSync(recursive: true); fileSystem.file('pubspec.yaml') ..createSync() ..writeAsStringSync(r''' name: example flutter: assets: - assets/common/ - path: assets/vanilla/ flavors: - vanilla - path: assets/strawberry/ flavors: - strawberry - path: assets/orange/ice-cream.png flavors: - orange '''); ManifestAssetBundle bundle; bundle = await buildBundleWithFlavor( null, logger: logger, fileSystem: fileSystem, platform: platform, ); expect(bundle.entries.keys, contains('assets/common/image.png')); expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png'))); expect(bundle.entries.keys, isNot(contains('assets/strawberry/ice-cream.png'))); expect(bundle.entries.keys, isNot(contains('assets/orange/ice-cream.png'))); bundle = await buildBundleWithFlavor( 'strawberry', logger: logger, fileSystem: fileSystem, platform: platform, ); expect(bundle.entries.keys, contains('assets/common/image.png')); expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png'))); expect(bundle.entries.keys, contains('assets/strawberry/ice-cream.png')); expect(bundle.entries.keys, isNot(contains('assets/orange/ice-cream.png'))); bundle = await buildBundleWithFlavor( 'orange', logger: logger, fileSystem: fileSystem, platform: platform, ); expect(bundle.entries.keys, contains('assets/common/image.png')); expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png'))); expect(bundle.entries.keys, isNot(contains('assets/strawberry/ice-cream.png'))); expect(bundle.entries.keys, contains('assets/orange/ice-cream.png')); }, ); testWithoutContext( 'throws a tool exit when a non-flavored folder contains a flavored asset', () async { final fileSystem = MemoryFileSystem(); fileSystem.currentDirectory = fileSystem.systemTempDirectory.createTempSync( 'flutter_asset_bundle_test.', ); final logger = BufferLogger.test(); final platform = FakePlatform(); writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'example'); fileSystem.file(fileSystem.path.join('assets', 'unflavored.png')).createSync(recursive: true); fileSystem .file(fileSystem.path.join('assets', 'vanillaOrange.png')) .createSync(recursive: true); fileSystem.file('pubspec.yaml') ..createSync() ..writeAsStringSync(r''' name: example flutter: assets: - assets/ - path: assets/vanillaOrange.png flavors: - vanilla - orange '''); expect( buildBundleWithFlavor(null, logger: logger, fileSystem: fileSystem, platform: platform), throwsToolExit( message: 'Multiple assets entries include the file ' '"assets/vanillaOrange.png", but they specify different lists of flavors.\n' 'An entry with the path "assets/" does not specify any flavors.\n' 'An entry with the path "assets/vanillaOrange.png" specifies the flavor(s): "vanilla", "orange".\n\n' 'Consider organizing assets with different flavors into different directories.', ), ); }, ); testWithoutContext( 'throws a tool exit when a flavored folder contains a flavorless asset', () async { final fileSystem = MemoryFileSystem(); fileSystem.currentDirectory = fileSystem.systemTempDirectory.createTempSync( 'flutter_asset_bundle_test.', ); final logger = BufferLogger.test(); final platform = FakePlatform(); writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'example'); fileSystem.file(fileSystem.path.join('vanilla', 'vanilla.png')).createSync(recursive: true); fileSystem .file(fileSystem.path.join('vanilla', 'flavorless.png')) .createSync(recursive: true); fileSystem.file('pubspec.yaml') ..createSync() ..writeAsStringSync(r''' name: example flutter: assets: - path: vanilla/ flavors: - vanilla - vanilla/flavorless.png '''); expect( buildBundleWithFlavor(null, logger: logger, fileSystem: fileSystem, platform: platform), throwsToolExit( message: 'Multiple assets entries include the file ' '"vanilla/flavorless.png", but they specify different lists of flavors.\n' 'An entry with the path "vanilla/" specifies the flavor(s): "vanilla".\n' 'An entry with the path "vanilla/flavorless.png" does not specify any flavors.\n\n' 'Consider organizing assets with different flavors into different directories.', ), ); }, ); testWithoutContext( 'tool exits when two file-explicit entries give the same asset different flavors', () { final fileSystem = MemoryFileSystem(); fileSystem.currentDirectory = fileSystem.systemTempDirectory.createTempSync( 'flutter_asset_bundle_test.', ); final logger = BufferLogger.test(); final platform = FakePlatform(); writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'example'); fileSystem.file('orange.png').createSync(recursive: true); fileSystem.file('pubspec.yaml') ..createSync() ..writeAsStringSync(r''' name: example flutter: assets: - path: orange.png flavors: - orange - path: orange.png flavors: - mango '''); expect( buildBundleWithFlavor(null, logger: logger, fileSystem: fileSystem, platform: platform), throwsToolExit( message: 'Multiple assets entries include the file ' '"orange.png", but they specify different lists of flavors.\n' 'An entry with the path "orange.png" specifies the flavor(s): "orange".\n' 'An entry with the path "orange.png" specifies the flavor(s): "mango".', ), ); }, ); testWithoutContext( 'throws ToolExit when flavor from file-level declaration has different flavor from containing folder flavor declaration', () async { final fileSystem = MemoryFileSystem(); fileSystem.currentDirectory = fileSystem.systemTempDirectory.createTempSync( 'flutter_asset_bundle_test.', ); final logger = BufferLogger.test(); final platform = FakePlatform(); writePackageConfigFiles(directory: fileSystem.currentDirectory, mainLibName: 'example'); fileSystem .file(fileSystem.path.join('vanilla', 'actually-strawberry.png')) .createSync(recursive: true); fileSystem.file(fileSystem.path.join('vanilla', 'vanilla.png')).createSync(recursive: true); fileSystem.file('pubspec.yaml') ..createSync() ..writeAsStringSync(r''' name: example flutter: assets: - path: vanilla/ flavors: - vanilla - path: vanilla/actually-strawberry.png flavors: - strawberry '''); expect( buildBundleWithFlavor(null, logger: logger, fileSystem: fileSystem, platform: platform), throwsToolExit( message: 'Multiple assets entries include the file ' '"vanilla/actually-strawberry.png", but they specify different lists of flavors.\n' 'An entry with the path "vanilla/" specifies the flavor(s): "vanilla".\n' 'An entry with the path "vanilla/actually-strawberry.png" ' 'specifies the flavor(s): "strawberry".', ), ); }, ); }