@@ -9,6 +9,7 @@ import 'dart:math' as math;
99
1010import 'package:meta/meta.dart' ;
1111import 'package:path/path.dart' as path;
12+ import 'package:xml/xml.dart' ;
1213
1314import '../framework/devices.dart' ;
1415import '../framework/framework.dart' ;
@@ -691,6 +692,63 @@ Map<String, dynamic> _average(List<Map<String, dynamic>> results, int iterations
691692 return tally;
692693}
693694
695+ /// Opens the file at testDirectory + 'android/app/src/main/AndroidManifest.xml'
696+ /// and adds the following entry to the application.
697+ /// <meta-data
698+ /// android:name="io.flutter.embedding.android.ImpellerBackend"
699+ /// android:value="opengles" />
700+ void _addOpenGLESToManifest (String testDirectory) {
701+ final String manifestPath = path.join (
702+ testDirectory, 'android' , 'app' , 'src' , 'main' , 'AndroidManifest.xml' );
703+ final File file = File (manifestPath);
704+
705+ if (! file.existsSync ()) {
706+ throw Exception ('AndroidManifest.xml not found at $manifestPath ' );
707+ }
708+
709+ final String xmlStr = file.readAsStringSync ();
710+ final XmlDocument xmlDoc = XmlDocument .parse (xmlStr);
711+ const String key = 'io.flutter.embedding.android.ImpellerBackend' ;
712+ const String value = 'opengles' ;
713+
714+ final XmlElement applicationNode =
715+ xmlDoc.findAllElements ('application' ).first;
716+
717+ // Check if the meta-data node already exists.
718+ final Iterable <XmlElement > existingMetaData = applicationNode
719+ .findAllElements ('meta-data' )
720+ .where ((XmlElement node) => node.getAttribute ('android:name' ) == key);
721+
722+ if (existingMetaData.isNotEmpty) {
723+ final XmlElement existingEntry = existingMetaData.first;
724+ existingEntry.setAttribute ('android:value' , value);
725+ } else {
726+ final XmlElement metaData = XmlElement (
727+ XmlName ('meta-data' ),
728+ < XmlAttribute > [
729+ XmlAttribute (XmlName ('android:name' ), key),
730+ XmlAttribute (XmlName ('android:value' ), value)
731+ ],
732+ );
733+
734+ applicationNode.children.add (metaData);
735+ }
736+
737+ file.writeAsStringSync (xmlDoc.toXmlString (pretty: true , indent: ' ' ));
738+ }
739+
740+ Future <void > _resetManifest (String testDirectory) async {
741+ final String manifestPath = path.join (
742+ testDirectory, 'android' , 'app' , 'src' , 'main' , 'AndroidManifest.xml' );
743+ final File file = File (manifestPath);
744+
745+ if (! file.existsSync ()) {
746+ throw Exception ('AndroidManifest.xml not found at $manifestPath ' );
747+ }
748+
749+ await exec ('git' , < String > ['checkout' , file.path]);
750+ }
751+
694752/// Measure application startup performance.
695753class StartupTest {
696754 const StartupTest (this .testDirectory, { this .reportMetrics = true , this .target = 'lib/main.dart' });
@@ -978,6 +1036,7 @@ class PerfTest {
9781036 this .flutterDriveCallback,
9791037 this .timeoutSeconds,
9801038 this .enableImpeller,
1039+ this .forceOpenGLES,
9811040 }): _resultFilename = resultFilename;
9821041
9831042 const PerfTest .e2e (
@@ -995,6 +1054,7 @@ class PerfTest {
9951054 this .flutterDriveCallback,
9961055 this .timeoutSeconds,
9971056 this .enableImpeller,
1057+ this .forceOpenGLES,
9981058 }) : saveTraceFile = false , timelineFileName = null , _resultFilename = resultFilename;
9991059
10001060 /// The directory where the app under test is defined.
@@ -1031,6 +1091,9 @@ class PerfTest {
10311091 /// Whether the perf test should enable Impeller.
10321092 final bool ? enableImpeller;
10331093
1094+ /// Whether the perf test force Impeller's OpenGLES backend.
1095+ final bool ? forceOpenGLES;
1096+
10341097 /// Number of seconds to time out the test after, allowing debug callbacks to run.
10351098 final int ? timeoutSeconds;
10361099
@@ -1079,40 +1142,55 @@ class PerfTest {
10791142 final String ? localEngine = localEngineFromEnv;
10801143 final String ? localEngineSrcPath = localEngineSrcPathFromEnv;
10811144
1082- final List <String > options = < String > [
1083- if (localEngine != null )
1084- ...< String > ['--local-engine' , localEngine],
1085- if (localEngineSrcPath != null )
1086- ...< String > ['--local-engine-src-path' , localEngineSrcPath],
1087- '--no-dds' ,
1088- '--no-android-gradle-daemon' ,
1089- '-v' ,
1090- '--verbose-system-logs' ,
1091- '--profile' ,
1092- if (timeoutSeconds != null )
1093- ...< String > [
1145+ Future <void > Function ()? manifestReset;
1146+ if (forceOpenGLES ?? false ) {
1147+ assert (enableImpeller! );
1148+ _addOpenGLESToManifest (testDirectory);
1149+ manifestReset = () => _resetManifest (testDirectory);
1150+ }
1151+
1152+ try {
1153+ final List <String > options = < String > [
1154+ if (localEngine != null ) ...< String > ['--local-engine' , localEngine],
1155+ if (localEngineSrcPath != null ) ...< String > [
1156+ '--local-engine-src-path' ,
1157+ localEngineSrcPath
1158+ ],
1159+ '--no-dds' ,
1160+ '--no-android-gradle-daemon' ,
1161+ '-v' ,
1162+ '--verbose-system-logs' ,
1163+ '--profile' ,
1164+ if (timeoutSeconds != null ) ...< String > [
10941165 '--timeout' ,
10951166 timeoutSeconds.toString (),
10961167 ],
1097- if (needsFullTimeline)
1098- '--trace-startup' , // Enables "endless" timeline event buffering.
1099- '-t' , testTarget,
1100- if (testDriver != null )
1101- ...< String > ['--driver' , testDriver! ],
1102- if (existingApp != null )
1103- ...< String > ['--use-existing-app' , existingApp],
1104- if (dartDefine.isNotEmpty)
1105- ...< String > ['--dart-define' , dartDefine],
1106- if (enableImpeller != null && enableImpeller! ) '--enable-impeller' ,
1107- if (enableImpeller != null && ! enableImpeller! ) '--no-enable-impeller' ,
1108- '-d' ,
1109- deviceId,
1110- ];
1111- if (flutterDriveCallback != null ) {
1112- flutterDriveCallback !(options);
1113- } else {
1114- await flutter ('drive' , options: options);
1168+ if (needsFullTimeline)
1169+ '--trace-startup' , // Enables "endless" timeline event buffering.
1170+ '-t' , testTarget,
1171+ if (testDriver != null ) ...< String > ['--driver' , testDriver! ],
1172+ if (existingApp != null ) ...< String > [
1173+ '--use-existing-app' ,
1174+ existingApp
1175+ ],
1176+ if (dartDefine.isNotEmpty) ...< String > ['--dart-define' , dartDefine],
1177+ if (enableImpeller != null && enableImpeller! ) '--enable-impeller' ,
1178+ if (enableImpeller != null && ! enableImpeller! )
1179+ '--no-enable-impeller' ,
1180+ '-d' ,
1181+ deviceId,
1182+ ];
1183+ if (flutterDriveCallback != null ) {
1184+ flutterDriveCallback !(options);
1185+ } else {
1186+ await flutter ('drive' , options: options);
1187+ }
1188+ } finally {
1189+ if (manifestReset != null ) {
1190+ await manifestReset ();
1191+ }
11151192 }
1193+
11161194 final Map <String , dynamic > data = json.decode (
11171195 file ('${_testOutputDirectory (testDirectory )}/$resultFilename .json' ).readAsStringSync (),
11181196 ) as Map <String , dynamic >;
0 commit comments