Unverified Commit 2a8b296a authored by Gary Qian's avatar Gary Qian Committed by GitHub

[flutter_tools] Deferred components: Encode base module loading units in manifest metadata (#78079)

parent f3725d0a
......@@ -107,6 +107,13 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
}
}
}
for (final LoadingUnit unit in generatedLoadingUnits) {
if (!mapping.containsKey(unit.id)) {
// Store an empty string for unassigned loading units,
// indicating that it is in the base component.
mapping[unit.id] = '';
}
}
// Encode the mapping as a string.
final StringBuffer mappingBuffer = StringBuffer();
for (final int key in mapping.keys) {
......@@ -120,7 +127,8 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
// Check for existing metadata entry and see if needs changes.
bool exists = false;
bool modified = false;
for (final XmlElement metaData in document.findAllElements('meta-data')) {
for (final XmlElement application in document.findAllElements('application')) {
for (final XmlElement metaData in application.findElements('meta-data')) {
final String name = metaData.getAttribute('android:name');
if (name == _mappingKey) {
exists = true;
......@@ -131,6 +139,7 @@ class DeferredComponentsGenSnapshotValidator extends DeferredComponentsValidator
}
}
}
}
if (!exists) {
// Create an meta-data XmlElement that contains the mapping.
final XmlElement mappingMetadataElement = XmlElement(XmlName.fromString('meta-data'),
......
......@@ -93,7 +93,7 @@ loading-units:
- lib2
- lib3
''';
expect(expectedFile.readAsStringSync().contains(expectedContents), true);
expect(expectedFile.readAsStringSync(), contains(expectedContents));
});
testWithoutContext('loadingUnitCache identical passes', () async {
......@@ -103,9 +103,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -116,7 +113,7 @@ loading-units:
libraries:
- lib2
- lib3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
......@@ -136,9 +133,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -146,7 +140,7 @@ loading-units:
libraries:
- lib2
- lib3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
......@@ -156,7 +150,7 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('New loading units were found:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'), true);
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 {
......@@ -166,9 +160,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -179,7 +170,7 @@ loading-units:
libraries:
- lib2
- lib3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -188,7 +179,7 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Previously existing loading units no longer exist:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'), true);
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 {
......@@ -197,10 +188,6 @@ loading-units:
exitOnFail: false,
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 2, libraries: <String>['lib1']),
......@@ -209,7 +196,7 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('New loading units were found:\n\n LoadingUnit 2\n Libraries:\n - lib1\n'), true);
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 {
......@@ -219,9 +206,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units-spelled-wrong:
......@@ -232,7 +216,7 @@ loading-units-spelled-wrong:
libraries:
- lib2
- lib3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -241,8 +225,8 @@ loading-units-spelled-wrong:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, \'loading-units\' entry did not exist.'), true);
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);
});
......@@ -254,13 +238,10 @@ loading-units-spelled-wrong:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units: hello
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -269,8 +250,8 @@ loading-units: hello
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, \'loading-units\' is not a list.'), true);
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 {
......@@ -280,15 +261,12 @@ loading-units: hello
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
- 2
- 3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -297,8 +275,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, \'loading-units\' is not a list of maps.'), true);
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 {
......@@ -308,9 +286,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -320,7 +295,7 @@ loading-units:
- libraries:
- lib2
- lib3
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -329,8 +304,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, all loading units must have an \'id\''), true);
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 {
......@@ -340,9 +315,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -351,7 +323,7 @@ loading-units:
- lib1
- id: 3
libraries: hello
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -360,8 +332,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, \'libraries\' is not a list.'), true);
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 {
......@@ -371,9 +343,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -384,7 +353,7 @@ loading-units:
libraries:
- blah: hello
blah2: hello2
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -393,8 +362,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Errors checking the following files:'), true);
expect(logger.statusText.contains('Invalid loading units yaml file, \'libraries\' is not a list of strings.'), true);
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 {
......@@ -404,9 +373,6 @@ loading-units:
title: 'test check',
);
final File cacheFile = env.projectDir.childFile(DeferredComponentsValidator.kLoadingUnitsCacheFileName);
if (cacheFile.existsSync()) {
cacheFile.deleteSync();
}
cacheFile.createSync(recursive: true);
cacheFile.writeAsStringSync('''
loading-units:
......@@ -415,7 +381,7 @@ loading-units:
- lib1
- id: 3
libraries:
''', flush: true, mode: FileMode.append);
''');
validator.checkAgainstLoadingUnitsCache(
<LoadingUnit>[
LoadingUnit(id: 3, libraries: <String>['lib2', 'lib3']),
......@@ -464,7 +430,7 @@ loading-units:
/>
</application>
</manifest>
''', flush: true, mode: FileMode.append);
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
......@@ -479,8 +445,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Modified android files:\n'), true);
expect(logger.statusText.contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'), true);
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')
......@@ -503,9 +469,6 @@ loading-units:
);
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"
......@@ -526,7 +489,7 @@ loading-units:
android:value="2" />
</application>
</manifest>
''', flush: true, mode: FileMode.append);
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
......@@ -541,8 +504,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Modified android files:\n'), true);
expect(logger.statusText.contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'), true);
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')
......@@ -552,8 +515,130 @@ loading-units:
.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('<!-- Don\'t delete the meta-data below.'), 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.
......@@ -565,9 +650,6 @@ loading-units:
);
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"
......@@ -595,7 +677,7 @@ loading-units:
/>
</application>
</manifest>
''', flush: true, mode: FileMode.append);
''');
validator.checkAppAndroidManifestComponentLoadingUnitMapping(
<DeferredComponent>[
DeferredComponent(name: 'component1', libraries: <String>['lib2']),
......@@ -610,8 +692,8 @@ loading-units:
validator.displayResults();
validator.attemptToolExit();
expect(logger.statusText.contains('Modified android files:\n'), true);
expect(logger.statusText.contains('build/${DeferredComponentsValidator.kDeferredComponentsTempDirectory}/app/src/main/AndroidManifest.xml\n'), true);
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')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment