blob: 6962b50a6aaead2de74ec51dc300ce0c3e2e5471 [file] [log] [blame]
// 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/android/deferred_components_gen_snapshot_validator.dart';
import 'package:flutter_tools/src/android/deferred_components_validator.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/deferred_component.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import '../../src/common.dart';
import '../../src/fake_process_manager.dart';
void main() {
late FileSystem fileSystem;
late BufferLogger logger;
late Environment env;
Environment createEnvironment() {
final Map<String, String> defines = <String, String>{ kDeferredComponents: 'true' };
final Environment result = Environment.test(
fileSystem.directory('/project'),
defines: defines,
artifacts: Artifacts.test(),
fileSystem: fileSystem,
logger: logger,
processManager: FakeProcessManager.any(),
);
return result;
}
setUp(() {
fileSystem = MemoryFileSystem.test();
logger = BufferLogger.test();
env = createEnvironment();
});
testWithoutContext('No checks passes', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, 'test check passed.\n');
});
testWithoutContext('writeCache passes', () async {
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
validator.writeLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, 'test check passed.\n');
final File expectedFile = env.projectDir.childFile('deferred_components_loading_units.yaml');
expect(expectedFile.existsSync(), true);
const String expectedContents =
'''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries:
- lib2
- lib3
''';
expect(expectedFile.readAsStringSync(), contains(expectedContents));
});
testWithoutContext('loadingUnitCache identical passes', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries:
- lib2
- lib3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
]
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, 'test check passed.\n');
});
testWithoutContext('loadingUnitCache finds new loading units', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 3
libraries:
- lib2
- lib3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('New loading units were found:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'));
});
testWithoutContext('loadingUnitCache finds missing loading units', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries:
- lib2
- lib3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Previously existing loading units no longer exist:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'));
});
testWithoutContext('missing cache file counts as all new loading units', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('New loading units were found:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'));
});
testWithoutContext('loadingUnitCache validator detects malformed file: missing main entry', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units-spelled-wrong:
- id: 2
libraries:
- lib1
- id: 3
libraries:
- lib2
- lib3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'loading-units' entry did not exist."));
expect(logger.statusText.contains('Previously existing loading units no longer exist:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'), false);
});
testWithoutContext('loadingUnitCache validator detects malformed file: not a list', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units: hello
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'loading-units' is not a list."));
});
testWithoutContext('loadingUnitCache validator detects malformed file: not a list', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- 2
- 3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'loading-units' is not a list of maps."));
});
testWithoutContext('loadingUnitCache validator detects malformed file: missing id', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- libraries:
- lib2
- lib3
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, all loading units must have an 'id'"));
});
testWithoutContext('loadingUnitCache validator detects malformed file: libraries is list', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries: hello
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'libraries' is not a list."));
});
testWithoutContext('loadingUnitCache validator detects malformed file: libraries is list of strings', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries:
- blah: hello
blah2: hello2
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Errors checking the following files:'));
expect(logger.statusText, contains("Invalid loading units yaml file, 'libraries' is not a list of strings."));
});
testWithoutContext('loadingUnitCache validator detects malformed file: empty libraries allowed', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- id: 2
libraries:
- lib1
- id: 3
libraries:
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), false);
});
testWithoutContext('androidStringMapping modifies strings file', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
final File manifest = baseModuleDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
if (manifest.existsSync()) {
manifest.deleteSync();
}
manifest.createSync(recursive: true);
manifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splitaot">
<application
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
android:label="splitaot"
android:extractNativeLibs="false">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping"
android:value="invalidmapping"
/>
</application>
</manifest>
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
DeferredComponent(name: 'component2', libraries: <String>['lib1', 'lib4']),
],
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
LoadingUnit(id: 4, libraries: <String>['lib4', 'lib5']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Modified android files:\n'));
expect(logger.statusText, contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'));
final File manifestOutput = env.projectDir
.childDirectory('build')
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
.childDirectory('app')
.childDirectory('src')
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
expect(manifestOutput.readAsStringSync().contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'), true);
expect(manifestOutput.readAsStringSync().contains('android:value="invalidmapping"'), false);
expect(manifestOutput.readAsStringSync().contains("<!-- Don't delete the meta-data below."), true);
});
testWithoutContext('androidStringMapping adds mapping when no existing mapping', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
final File manifest = baseModuleDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
manifest.createSync(recursive: true);
manifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splitaot">
<application
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
android:label="splitaot"
android:extractNativeLibs="false">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
DeferredComponent(name: 'component2', libraries: <String>['lib1', 'lib4']),
],
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
LoadingUnit(id: 4, libraries: <String>['lib4', 'lib5']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Modified android files:\n'));
expect(logger.statusText, contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'));
final File manifestOutput = env.projectDir
.childDirectory('build')
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
.childDirectory('app')
.childDirectory('src')
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
expect(manifestOutput.readAsStringSync(), contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'));
expect(manifestOutput.readAsStringSync(), contains("<!-- Don't delete the meta-data below."));
});
// The mapping is incorrectly placed in the activity instead of application.
testWithoutContext('androidStringMapping detects improperly placed metadata mapping', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
final File manifest = baseModuleDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
manifest.createSync(recursive: true);
manifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splitaot">
<application
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
android:label="splitaot"
android:extractNativeLibs="false">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping"
android:value="3:component1,2:component2,4:component2"/>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
DeferredComponent(name: 'component2', libraries: <String>['lib1', 'lib4']),
],
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
LoadingUnit(id: 4, libraries: <String>['lib4', 'lib5']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Modified android files:\n'));
expect(logger.statusText, contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'));
final File manifestOutput = env.projectDir
.childDirectory('build')
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
.childDirectory('app')
.childDirectory('src')
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
expect(manifestOutput.readAsStringSync(), contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'));
expect(manifestOutput.readAsStringSync(), contains("<!-- Don't delete the meta-data below."));
});
testWithoutContext('androidStringMapping generates base module loading unit mapping', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
final File manifest = baseModuleDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
manifest.createSync(recursive: true);
manifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splitaot">
<application
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
android:label="splitaot"
android:extractNativeLibs="false">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
DeferredComponent(name: 'component2', libraries: <String>['lib1', 'lib4']),
],
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
LoadingUnit(id: 4, libraries: <String>['lib4', 'lib5']),
// Loading units 6 and 7 are in base module
LoadingUnit(id: 5, libraries: <String>['lib6', 'lib7']),
LoadingUnit(id: 6, libraries: <String>['lib8', 'lib9']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Modified android files:\n'));
expect(logger.statusText, contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'));
final File manifestOutput = env.projectDir
.childDirectory('build')
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
.childDirectory('app')
.childDirectory('src')
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
expect(manifestOutput.readAsStringSync(), contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2,5:,6:"/>'));
expect(manifestOutput.readAsStringSync(), contains("<!-- Don't delete the meta-data below."));
});
// Tests if all of the regexp whitespace detection is working.
testWithoutContext('androidStringMapping handles whitespace within entry', () async {
final DeferredComponentsGenSnapshotValidator validator = DeferredComponentsGenSnapshotValidator(
env,
exitOnFail: false,
title: 'test check',
);
final Directory baseModuleDir = env.projectDir.childDirectory('android').childDirectory('app');
final File manifest = baseModuleDir.childDirectory('src').childDirectory('main').childFile('AndroidManifest.xml');
manifest.createSync(recursive: true);
manifest.writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splitaot">
<application
android:name="io.flutter.app.FlutterPlayStoreSplitApplication"
android:label="splitaot"
android:extractNativeLibs="false">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize">
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data
android:name = "io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping"
android:value = "invalidmapping"
/>
</application>
</manifest>
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
DeferredComponent(name: 'component2', libraries: <String>['lib1', 'lib4']),
],
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
LoadingUnit(id: 4, libraries: <String>['lib4', 'lib5']),
],
);
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText, contains('Modified android files:\n'));
expect(logger.statusText, contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'));
final File manifestOutput = env.projectDir
.childDirectory('build')
.childDirectory(DeferredComponentsValidator.kDeferredComponentsTempDirectory)
.childDirectory('app')
.childDirectory('src')
.childDirectory('main')
.childFile('AndroidManifest.xml');
expect(manifestOutput.existsSync(), true);
expect(manifestOutput.readAsStringSync().contains('<meta-data android:name="io.flutter.embedding.engine.deferredcomponents.DeferredComponentManager.loadingUnitMapping" android:value="3:component1,2:component2,4:component2"/>'), true);
expect(manifestOutput.readAsStringSync().contains(RegExp(r'android:value[\s\n]*=[\s\n]*"invalidmapping"')), false);
expect(manifestOutput.readAsStringSync().contains("<!-- Don't delete the meta-data below."), true);
});
}