add a benchmark test for stack size (#75039)
This commit is contained in:
parent
24d8dbb1e5
commit
9d9109aa6f
@ -19,5 +19,8 @@ const String kImageFilteredTransformAnimationRouteName = '/imagefiltered_transfo
|
|||||||
const String kMultiWidgetConstructionRouteName = '/multi_widget_construction';
|
const String kMultiWidgetConstructionRouteName = '/multi_widget_construction';
|
||||||
const String kHeavyGridViewRouteName = '/heavy_gridview';
|
const String kHeavyGridViewRouteName = '/heavy_gridview';
|
||||||
const String kSimpleScrollRouteName = '/simple_scroll';
|
const String kSimpleScrollRouteName = '/simple_scroll';
|
||||||
|
const String kStackSizeRouteName = '/stack_size';
|
||||||
|
|
||||||
const String kScrollableName = '/macrobenchmark_listview';
|
const String kScrollableName = '/macrobenchmark_listview';
|
||||||
|
|
||||||
|
const String kStackSizeKey = 'stack_size';
|
||||||
|
@ -21,6 +21,7 @@ import 'src/picture_cache.dart';
|
|||||||
import 'src/post_backdrop_filter.dart';
|
import 'src/post_backdrop_filter.dart';
|
||||||
import 'src/simple_animation.dart';
|
import 'src/simple_animation.dart';
|
||||||
import 'src/simple_scroll.dart';
|
import 'src/simple_scroll.dart';
|
||||||
|
import 'src/stack_size.dart';
|
||||||
import 'src/text.dart';
|
import 'src/text.dart';
|
||||||
|
|
||||||
const String kMacrobenchmarks = 'Macrobenchmarks';
|
const String kMacrobenchmarks = 'Macrobenchmarks';
|
||||||
@ -54,6 +55,7 @@ class MacrobenchmarksApp extends StatelessWidget {
|
|||||||
kMultiWidgetConstructionRouteName: (BuildContext context) => const MultiWidgetConstructTable(10, 20),
|
kMultiWidgetConstructionRouteName: (BuildContext context) => const MultiWidgetConstructTable(10, 20),
|
||||||
kHeavyGridViewRouteName: (BuildContext context) => HeavyGridViewPage(),
|
kHeavyGridViewRouteName: (BuildContext context) => HeavyGridViewPage(),
|
||||||
kSimpleScrollRouteName: (BuildContext context) => SimpleScroll(),
|
kSimpleScrollRouteName: (BuildContext context) => SimpleScroll(),
|
||||||
|
kStackSizeRouteName: (BuildContext context) => StackSizePage(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -181,6 +183,13 @@ class HomePage extends StatelessWidget {
|
|||||||
Navigator.pushNamed(context, kLargeImageChangerRouteName);
|
Navigator.pushNamed(context, kLargeImageChangerRouteName);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
key: const Key(kStackSizeRouteName),
|
||||||
|
child: const Text('Stack Size'),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, kStackSizeRouteName);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
122
dev/benchmarks/macrobenchmarks/lib/src/stack_size.dart
Normal file
122
dev/benchmarks/macrobenchmarks/lib/src/stack_size.dart
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// 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 'dart:ffi' as ffi;
|
||||||
|
import 'dart:io' as io;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../common.dart';
|
||||||
|
|
||||||
|
typedef GetStackPointerCallback = int Function();
|
||||||
|
|
||||||
|
// c interop function:
|
||||||
|
// void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
|
||||||
|
typedef CMmap = ffi.Pointer<ffi.Void> Function(
|
||||||
|
ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Int32, ffi.Int32, ffi.Int32, ffi.IntPtr);
|
||||||
|
typedef DartMmap = ffi.Pointer<ffi.Void> Function(
|
||||||
|
ffi.Pointer<ffi.Void>, int, int, int, int, int);
|
||||||
|
final DartMmap mmap = ffi.DynamicLibrary.process().lookupFunction<CMmap, DartMmap>('mmap');
|
||||||
|
|
||||||
|
// c interop function:
|
||||||
|
// int mprotect(void* addr, size_t len, int prot);
|
||||||
|
typedef CMprotect = ffi.Int32 Function(ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Int32);
|
||||||
|
typedef DartMprotect = int Function(ffi.Pointer<ffi.Void>, int, int);
|
||||||
|
final DartMprotect mprotect = ffi.DynamicLibrary.process()
|
||||||
|
.lookupFunction<CMprotect, DartMprotect>('mprotect');
|
||||||
|
|
||||||
|
const int kProtRead = 1;
|
||||||
|
const int kProtWrite = 2;
|
||||||
|
const int kProtExec = 4;
|
||||||
|
|
||||||
|
const int kMapPrivate = 0x02;
|
||||||
|
const int kMapJit = 0x0;
|
||||||
|
const int kMapAnon = 0x20;
|
||||||
|
|
||||||
|
const int kMemorySize = 16;
|
||||||
|
const int kInvalidFileDescriptor = -1;
|
||||||
|
const int kkFileMappingOffset = 0;
|
||||||
|
|
||||||
|
const int kMemoryStartingIndex = 0;
|
||||||
|
|
||||||
|
const int kExitCodeSuccess = 0;
|
||||||
|
|
||||||
|
final GetStackPointerCallback getStackPointer = () {
|
||||||
|
// Makes sure we are running on an Android arm64 device.
|
||||||
|
if (!io.Platform.isAndroid)
|
||||||
|
throw 'This benchmark test can only be run on Android arm64 devices.';
|
||||||
|
final io.ProcessResult result = io.Process.runSync('getprop', <String>['ro.product.cpu.abi']);
|
||||||
|
if (result.exitCode != 0)
|
||||||
|
throw 'Failed to retrieve CPU information.';
|
||||||
|
if (!result.stdout.toString().contains('arm64'))
|
||||||
|
throw 'This benchmark test can only be run on Android arm64 devices.';
|
||||||
|
|
||||||
|
// Creates a block of memory to store the assembly code.
|
||||||
|
final ffi.Pointer<ffi.Void> region = mmap(ffi.nullptr, kMemorySize, kProtRead | kProtWrite,
|
||||||
|
kMapPrivate | kMapAnon | kMapJit, kInvalidFileDescriptor, kkFileMappingOffset);
|
||||||
|
if (region == ffi.nullptr) {
|
||||||
|
throw 'Failed to acquire memory for the test.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the assembly code into the memory block. This assembly code returns
|
||||||
|
// the memory address of the stack pointer.
|
||||||
|
region.cast<ffi.Uint8>().asTypedList(kMemorySize).setAll(
|
||||||
|
kMemoryStartingIndex,
|
||||||
|
<int>[
|
||||||
|
// "mov x0, sp" in machine code: E0030091.
|
||||||
|
0xe0, 0x03, 0x00, 0x91,
|
||||||
|
// "ret" in machine code: C0035FD6.
|
||||||
|
0xc0, 0x03, 0x5f, 0xd6
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Makes sure the memory block is executable.
|
||||||
|
if (mprotect(region, kMemorySize, kProtRead | kProtExec) != kExitCodeSuccess) {
|
||||||
|
throw 'Failed to write executable code to the memory.';
|
||||||
|
}
|
||||||
|
return region
|
||||||
|
.cast<ffi.NativeFunction<ffi.IntPtr Function()>>()
|
||||||
|
.asFunction<int Function()>();
|
||||||
|
}();
|
||||||
|
|
||||||
|
class StackSizePage extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
width: 200,
|
||||||
|
height: 100,
|
||||||
|
child: ParentWidget(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParentWidget extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int myStackSize = getStackPointer();
|
||||||
|
return ChildWidget(parentStackSize: myStackSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChildWidget extends StatelessWidget {
|
||||||
|
const ChildWidget({this.parentStackSize, Key key}) : super(key: key);
|
||||||
|
final int parentStackSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final int myStackSize = getStackPointer();
|
||||||
|
// Captures the stack size difference between parent widget and child widget
|
||||||
|
// during the rendering pipeline, i.e. one layer of stateless widget.
|
||||||
|
return Text(
|
||||||
|
'${parentStackSize - myStackSize}',
|
||||||
|
key: const ValueKey<String>(kStackSizeKey),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
// 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 'dart:convert' show JsonEncoder;
|
||||||
|
|
||||||
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
|
||||||
|
|
||||||
|
import 'package:macrobenchmarks/common.dart';
|
||||||
|
|
||||||
|
import 'util.dart';
|
||||||
|
|
||||||
|
const JsonEncoder _prettyEncoder = JsonEncoder.withIndent(' ');
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('stack_size', () async {
|
||||||
|
int stackSizeInBytes;
|
||||||
|
await runDriverTestForRoute(kStackSizeRouteName, (FlutterDriver driver) async {
|
||||||
|
final String stackSize = await driver.getText(find.byValueKey(kStackSizeKey));
|
||||||
|
expect(stackSize.isNotEmpty, isTrue);
|
||||||
|
stackSizeInBytes = int.parse(stackSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(stackSizeInBytes > 0, isTrue);
|
||||||
|
|
||||||
|
await fs.directory(testOutputsDirectory).create(recursive: true);
|
||||||
|
final File file = fs.file(path.join(testOutputsDirectory, 'stack_size.json'));
|
||||||
|
await file.writeAsString(_encodeJson(<String, dynamic>{
|
||||||
|
'stack_size': stackSizeInBytes,
|
||||||
|
}));
|
||||||
|
}, timeout: const Timeout(kTimeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
String _encodeJson(Map<String, dynamic> jsonObject) {
|
||||||
|
return _prettyEncoder.convert(jsonObject);
|
||||||
|
}
|
@ -7,51 +7,64 @@ import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
|
|||||||
|
|
||||||
import 'package:macrobenchmarks/common.dart';
|
import 'package:macrobenchmarks/common.dart';
|
||||||
|
|
||||||
|
const Duration kTimeout = Duration(seconds: 30);
|
||||||
|
|
||||||
|
typedef DriverTestCallBack = Future<void> Function(FlutterDriver driver);
|
||||||
|
|
||||||
|
Future<void> runDriverTestForRoute(String routeName, DriverTestCallBack body) async {
|
||||||
|
final FlutterDriver driver = await FlutterDriver.connect();
|
||||||
|
|
||||||
|
// The slight initial delay avoids starting the timing during a
|
||||||
|
// period of increased load on the device. Without this delay, the
|
||||||
|
// benchmark has greater noise.
|
||||||
|
// See: https://github.com/flutter/flutter/issues/19434
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 250));
|
||||||
|
|
||||||
|
await driver.forceGC();
|
||||||
|
|
||||||
|
final SerializableFinder scrollable = find.byValueKey(kScrollableName);
|
||||||
|
expect(scrollable, isNotNull);
|
||||||
|
final SerializableFinder button = find.byValueKey(kStackSizeRouteName);
|
||||||
|
expect(button, isNotNull);
|
||||||
|
await driver.scrollUntilVisible(scrollable, button, dyScroll: -50.0);
|
||||||
|
await driver.tap(button);
|
||||||
|
|
||||||
|
await body(driver);
|
||||||
|
|
||||||
|
driver.close();
|
||||||
|
}
|
||||||
|
|
||||||
void macroPerfTest(
|
void macroPerfTest(
|
||||||
String testName,
|
String testName,
|
||||||
String routeName,
|
String routeName,
|
||||||
{ Duration pageDelay,
|
{ Duration pageDelay,
|
||||||
Duration duration = const Duration(seconds: 3),
|
Duration duration = const Duration(seconds: 3),
|
||||||
Duration timeout = const Duration(seconds: 30),
|
Duration timeout = kTimeout,
|
||||||
Future<void> driverOps(FlutterDriver driver),
|
Future<void> driverOps(FlutterDriver driver),
|
||||||
Future<void> setupOps(FlutterDriver driver),
|
Future<void> setupOps(FlutterDriver driver),
|
||||||
}) {
|
}) {
|
||||||
test(testName, () async {
|
test(testName, () async {
|
||||||
final FlutterDriver driver = await FlutterDriver.connect();
|
Timeline timeline;
|
||||||
|
await runDriverTestForRoute(routeName, (FlutterDriver driver) async {
|
||||||
|
if (pageDelay != null) {
|
||||||
|
// Wait for the page to load
|
||||||
|
await Future<void>.delayed(pageDelay);
|
||||||
|
}
|
||||||
|
|
||||||
// The slight initial delay avoids starting the timing during a
|
if (setupOps != null) {
|
||||||
// period of increased load on the device. Without this delay, the
|
|
||||||
// benchmark has greater noise.
|
|
||||||
// See: https://github.com/flutter/flutter/issues/19434
|
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 250));
|
|
||||||
|
|
||||||
await driver.forceGC();
|
|
||||||
|
|
||||||
final SerializableFinder scrollable = find.byValueKey(kScrollableName);
|
|
||||||
expect(scrollable, isNotNull);
|
|
||||||
final SerializableFinder button = find.byValueKey(routeName);
|
|
||||||
expect(button, isNotNull);
|
|
||||||
await driver.scrollUntilVisible(scrollable, button, dyScroll: -50.0);
|
|
||||||
await driver.tap(button);
|
|
||||||
|
|
||||||
if (pageDelay != null) {
|
|
||||||
// Wait for the page to load
|
|
||||||
await Future<void>.delayed(pageDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setupOps != null) {
|
|
||||||
await setupOps(driver);
|
await setupOps(driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Timeline timeline = await driver.traceAction(() async {
|
timeline = await driver.traceAction(() async {
|
||||||
final Future<void> durationFuture = Future<void>.delayed(duration);
|
final Future<void> durationFuture = Future<void>.delayed(duration);
|
||||||
if (driverOps != null) {
|
if (driverOps != null) {
|
||||||
await driverOps(driver);
|
await driverOps(driver);
|
||||||
}
|
}
|
||||||
await durationFuture;
|
await durationFuture;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
driver.close();
|
expect(timeline, isNotNull);
|
||||||
|
|
||||||
final TimelineSummary summary = TimelineSummary.summarize(timeline);
|
final TimelineSummary summary = TimelineSummary.summarize(timeline);
|
||||||
await summary.writeSummaryToFile(testName, pretty: true);
|
await summary.writeSummaryToFile(testName, pretty: true);
|
||||||
|
12
dev/devicelab/bin/tasks/android_stack_size_test.dart
Normal file
12
dev/devicelab/bin/tasks/android_stack_size_test.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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:flutter_devicelab/tasks/perf_tests.dart';
|
||||||
|
import 'package:flutter_devicelab/framework/adb.dart';
|
||||||
|
import 'package:flutter_devicelab/framework/framework.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
deviceOperatingSystem = DeviceOperatingSystem.androidArm64;
|
||||||
|
await task(createStackSizeTest());
|
||||||
|
}
|
@ -52,7 +52,7 @@ String _findMatchId(List<String> idList, String idPattern) {
|
|||||||
DeviceDiscovery get devices => DeviceDiscovery();
|
DeviceDiscovery get devices => DeviceDiscovery();
|
||||||
|
|
||||||
/// Device operating system the test is configured to test.
|
/// Device operating system the test is configured to test.
|
||||||
enum DeviceOperatingSystem { android, ios, fuchsia, fake }
|
enum DeviceOperatingSystem { android, androidArm64 ,ios, fuchsia, fake }
|
||||||
|
|
||||||
/// Device OS to test on.
|
/// Device OS to test on.
|
||||||
DeviceOperatingSystem deviceOperatingSystem = DeviceOperatingSystem.android;
|
DeviceOperatingSystem deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||||
@ -63,6 +63,8 @@ abstract class DeviceDiscovery {
|
|||||||
switch (deviceOperatingSystem) {
|
switch (deviceOperatingSystem) {
|
||||||
case DeviceOperatingSystem.android:
|
case DeviceOperatingSystem.android:
|
||||||
return AndroidDeviceDiscovery();
|
return AndroidDeviceDiscovery();
|
||||||
|
case DeviceOperatingSystem.androidArm64:
|
||||||
|
return AndroidDeviceDiscovery(cpu: _AndroidCPU.arm64);
|
||||||
case DeviceOperatingSystem.ios:
|
case DeviceOperatingSystem.ios:
|
||||||
return IosDeviceDiscovery();
|
return IosDeviceDiscovery();
|
||||||
case DeviceOperatingSystem.fuchsia:
|
case DeviceOperatingSystem.fuchsia:
|
||||||
@ -155,12 +157,18 @@ abstract class Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _AndroidCPU {
|
||||||
|
arm64,
|
||||||
|
}
|
||||||
|
|
||||||
class AndroidDeviceDiscovery implements DeviceDiscovery {
|
class AndroidDeviceDiscovery implements DeviceDiscovery {
|
||||||
factory AndroidDeviceDiscovery() {
|
factory AndroidDeviceDiscovery({_AndroidCPU cpu}) {
|
||||||
return _instance ??= AndroidDeviceDiscovery._();
|
return _instance ??= AndroidDeviceDiscovery._(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidDeviceDiscovery._();
|
AndroidDeviceDiscovery._(this.cpu);
|
||||||
|
|
||||||
|
final _AndroidCPU cpu;
|
||||||
|
|
||||||
// Parses information about a device. Example:
|
// Parses information about a device. Example:
|
||||||
//
|
//
|
||||||
@ -185,6 +193,16 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
|
|||||||
return _workingDevice;
|
return _workingDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _matchesCPURequirement(AndroidDevice device) async {
|
||||||
|
if (cpu == null)
|
||||||
|
return true;
|
||||||
|
switch (cpu) {
|
||||||
|
case _AndroidCPU.arm64:
|
||||||
|
return device.isArm64();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Picks a random Android device out of connected devices and sets it as
|
/// Picks a random Android device out of connected devices and sets it as
|
||||||
/// [workingDevice].
|
/// [workingDevice].
|
||||||
@override
|
@override
|
||||||
@ -196,8 +214,22 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
|
|||||||
if (allDevices.isEmpty)
|
if (allDevices.isEmpty)
|
||||||
throw const DeviceException('No Android devices detected');
|
throw const DeviceException('No Android devices detected');
|
||||||
|
|
||||||
// TODO(yjbanov): filter out and warn about those with low battery level
|
if (cpu != null) {
|
||||||
_workingDevice = allDevices[math.Random().nextInt(allDevices.length)];
|
for (final AndroidDevice device in allDevices) {
|
||||||
|
if (await _matchesCPURequirement(device)) {
|
||||||
|
_workingDevice = device;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// TODO(yjbanov): filter out and warn about those with low battery level
|
||||||
|
_workingDevice = allDevices[math.Random().nextInt(allDevices.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_workingDevice == null)
|
||||||
|
throw const DeviceException('Cannot find a suitable Android device');
|
||||||
|
|
||||||
print('Device chosen: $_workingDevice');
|
print('Device chosen: $_workingDevice');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +238,11 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
|
|||||||
final String matchedId = _findMatchId(await discoverDevices(), deviceId);
|
final String matchedId = _findMatchId(await discoverDevices(), deviceId);
|
||||||
if (matchedId != null) {
|
if (matchedId != null) {
|
||||||
_workingDevice = AndroidDevice(deviceId: matchedId);
|
_workingDevice = AndroidDevice(deviceId: matchedId);
|
||||||
|
if (cpu != null) {
|
||||||
|
if (!await _matchesCPURequirement(_workingDevice)) {
|
||||||
|
throw DeviceException('The selected device $matchedId does not match the cpu requirement');
|
||||||
|
}
|
||||||
|
}
|
||||||
print('Choose device by ID: $matchedId');
|
print('Choose device by ID: $matchedId');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -444,6 +481,11 @@ class AndroidDevice extends Device {
|
|||||||
return wakefulness;
|
return wakefulness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> isArm64() async {
|
||||||
|
final String cpuInfo = await shellEval('getprop', const <String>['ro.product.cpu.abi']);
|
||||||
|
return cpuInfo.contains('arm64');
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _updateDeviceInfo() async {
|
Future<void> _updateDeviceInfo() async {
|
||||||
String info;
|
String info;
|
||||||
try {
|
try {
|
||||||
|
@ -271,6 +271,43 @@ TaskFunction createTextfieldPerfE2ETest() {
|
|||||||
).run;
|
).run;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskFunction createStackSizeTest() {
|
||||||
|
final String testDirectory =
|
||||||
|
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks';
|
||||||
|
const String testTarget = 'test_driver/run_app.dart';
|
||||||
|
const String testDriver = 'test_driver/stack_size_perf_test.dart';
|
||||||
|
return () {
|
||||||
|
return inDirectory<TaskResult>(testDirectory, () async {
|
||||||
|
final Device device = await devices.workingDevice;
|
||||||
|
await device.unlock();
|
||||||
|
final String deviceId = device.deviceId;
|
||||||
|
await flutter('packages', options: <String>['get']);
|
||||||
|
|
||||||
|
await flutter('drive', options: <String>[
|
||||||
|
'--no-android-gradle-daemon',
|
||||||
|
'-v',
|
||||||
|
'--verbose-system-logs',
|
||||||
|
'--profile',
|
||||||
|
'-t', testTarget,
|
||||||
|
'--driver', testDriver,
|
||||||
|
'-d',
|
||||||
|
deviceId,
|
||||||
|
]);
|
||||||
|
final Map<String, dynamic> data = json.decode(
|
||||||
|
file('$testDirectory/build/stack_size.json').readAsStringSync(),
|
||||||
|
) as Map<String, dynamic>;
|
||||||
|
|
||||||
|
final Map<String, dynamic> result = <String, dynamic>{
|
||||||
|
'stack_size_per_nesting_level': data['stack_size'],
|
||||||
|
};
|
||||||
|
return TaskResult.success(
|
||||||
|
result,
|
||||||
|
benchmarkScoreKeys: result.keys.toList(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
TaskFunction createFullscreenTextfieldPerfTest() {
|
TaskFunction createFullscreenTextfieldPerfTest() {
|
||||||
return PerfTest(
|
return PerfTest(
|
||||||
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
||||||
@ -466,6 +503,15 @@ class StartupTest {
|
|||||||
]);
|
]);
|
||||||
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||||
break;
|
break;
|
||||||
|
case DeviceOperatingSystem.androidArm64:
|
||||||
|
await flutter('build', options: <String>[
|
||||||
|
'apk',
|
||||||
|
'-v',
|
||||||
|
'--profile',
|
||||||
|
'--target-platform=android-arm64',
|
||||||
|
]);
|
||||||
|
applicationBinaryPath = '$testDirectory/build/app/outputs/flutter-apk/app-profile.apk';
|
||||||
|
break;
|
||||||
case DeviceOperatingSystem.ios:
|
case DeviceOperatingSystem.ios:
|
||||||
await flutter('build', options: <String>[
|
await flutter('build', options: <String>[
|
||||||
'ios',
|
'ios',
|
||||||
@ -998,6 +1044,20 @@ class CompileTest {
|
|||||||
if (reportPackageContentSizes)
|
if (reportPackageContentSizes)
|
||||||
metrics.addAll(await getSizesFromApk(apkPath));
|
metrics.addAll(await getSizesFromApk(apkPath));
|
||||||
break;
|
break;
|
||||||
|
case DeviceOperatingSystem.androidArm64:
|
||||||
|
options.insert(0, 'apk');
|
||||||
|
options.add('--target-platform=android-arm64');
|
||||||
|
options.add('--tree-shake-icons');
|
||||||
|
options.add('--split-debug-info=infos/');
|
||||||
|
watch.start();
|
||||||
|
await flutter('build', options: options);
|
||||||
|
watch.stop();
|
||||||
|
final String apkPath = '$cwd/build/app/outputs/flutter-apk/app-release.apk';
|
||||||
|
final File apk = file(apkPath);
|
||||||
|
releaseSizeInBytes = apk.lengthSync();
|
||||||
|
if (reportPackageContentSizes)
|
||||||
|
metrics.addAll(await getSizesFromApk(apkPath));
|
||||||
|
break;
|
||||||
case DeviceOperatingSystem.fuchsia:
|
case DeviceOperatingSystem.fuchsia:
|
||||||
throw Exception('Unsupported option for Fuchsia devices');
|
throw Exception('Unsupported option for Fuchsia devices');
|
||||||
case DeviceOperatingSystem.fake:
|
case DeviceOperatingSystem.fake:
|
||||||
@ -1024,6 +1084,10 @@ class CompileTest {
|
|||||||
options.insert(0, 'apk');
|
options.insert(0, 'apk');
|
||||||
options.add('--target-platform=android-arm');
|
options.add('--target-platform=android-arm');
|
||||||
break;
|
break;
|
||||||
|
case DeviceOperatingSystem.androidArm64:
|
||||||
|
options.insert(0, 'apk');
|
||||||
|
options.add('--target-platform=android-arm64');
|
||||||
|
break;
|
||||||
case DeviceOperatingSystem.fuchsia:
|
case DeviceOperatingSystem.fuchsia:
|
||||||
throw Exception('Unsupported option for Fuchsia devices');
|
throw Exception('Unsupported option for Fuchsia devices');
|
||||||
case DeviceOperatingSystem.fake:
|
case DeviceOperatingSystem.fake:
|
||||||
|
@ -22,6 +22,18 @@ void main() {
|
|||||||
tearDown(() {
|
tearDown(() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('cpu check', () {
|
||||||
|
test('arm64', () async {
|
||||||
|
FakeDevice.pretendArm64();
|
||||||
|
final AndroidDevice androidDevice = device as AndroidDevice;
|
||||||
|
expect(await androidDevice.isArm64(), isTrue);
|
||||||
|
expectLog(<CommandArgs>[
|
||||||
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk'], environment: null),
|
||||||
|
cmd(command: 'getprop', arguments: <String>['ro.product.cpu.abi'], environment: null),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('isAwake/isAsleep', () {
|
group('isAwake/isAsleep', () {
|
||||||
test('reads Awake', () async {
|
test('reads Awake', () async {
|
||||||
FakeDevice.pretendAwake();
|
FakeDevice.pretendAwake();
|
||||||
@ -187,6 +199,12 @@ class FakeDevice extends AndroidDevice {
|
|||||||
''';
|
''';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pretendArm64() {
|
||||||
|
output = '''
|
||||||
|
arm64
|
||||||
|
''';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> shellEval(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) async {
|
Future<String> shellEval(String command, List<String> arguments, { Map<String, String> environment, bool silent = false }) async {
|
||||||
commandLog.add(CommandArgs(
|
commandLog.add(CommandArgs(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user