mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Optionally invert oversized images (#61209)
* Optionally invert oversized images
This commit is contained in:
parent
8048c0332d
commit
eadc35f62b
|
@ -127,6 +127,37 @@ class ImageSizeInfo {
|
|||
/// time.
|
||||
PaintImageCallback debugOnPaintImage;
|
||||
|
||||
/// If true, the framework will color invert and horizontally flip images that
|
||||
/// have been decoded to a size taking at least [debugImageOverheadAllowance]
|
||||
/// bytes more than necessary.
|
||||
///
|
||||
/// It will also call [FlutterError.reportError] with information about the
|
||||
/// image's decoded size and its display size, which can be used resize the
|
||||
/// asset before shipping it, apply `cacheHeight` or `cacheWidth` parameters, or
|
||||
/// directly use a [ResizeImage]. Whenever possible, resizing the image asset
|
||||
/// itself should be preferred, to avoid unnecessary network traffic, disk space
|
||||
/// usage, and other memory overhead incurred during decoding.
|
||||
///
|
||||
/// Developers using this flag should test their application on appropriate
|
||||
/// devices and display sizes for their expected deployment targets when using
|
||||
/// these parameters. For example, an application that responsively resizes
|
||||
/// images for a desktop and mobile layout should avoid decoding all images at
|
||||
/// sizes appropriate for mobile when on desktop. Applications should also avoid
|
||||
/// animating these parameters, as each change will result in a newly decoded
|
||||
/// image. For example, an image that always grows into view should decode only
|
||||
/// at its largest size, whereas an image that normally is a thumbnail and then
|
||||
/// pops into view should be decoded at its smallest size for the thumbnail and
|
||||
/// the largest size when needed.
|
||||
///
|
||||
/// This has no effect unless asserts are enabled.
|
||||
bool debugInvertOversizedImages = false;
|
||||
|
||||
/// The number of bytes an image must use before it triggers inversion when
|
||||
/// [debugInvertOversizedImages] is true.
|
||||
///
|
||||
/// Default is 1024 (1kb).
|
||||
int debugImageOverheadAllowance = 1024;
|
||||
|
||||
/// Returns true if none of the painting library debug variables have been changed.
|
||||
///
|
||||
/// This function is used by the test framework to ensure that debug variables
|
||||
|
@ -142,7 +173,9 @@ bool debugAssertAllPaintingVarsUnset(String reason, { bool debugDisableShadowsOv
|
|||
assert(() {
|
||||
if (debugDisableShadows != debugDisableShadowsOverride ||
|
||||
debugNetworkImageHttpClientProvider != null ||
|
||||
debugOnPaintImage != null) {
|
||||
debugOnPaintImage != null ||
|
||||
debugInvertOversizedImages == true ||
|
||||
debugImageOverheadAllowance != 1024) {
|
||||
throw FlutterError(reason);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -456,42 +456,6 @@ void paintImage({
|
|||
assert(sourceSize == inputSize, 'centerSlice was used with a BoxFit that does not guarantee that the image is fully visible.');
|
||||
}
|
||||
|
||||
// Output size is fully calculated.
|
||||
if (!kReleaseMode) {
|
||||
final ImageSizeInfo sizeInfo = ImageSizeInfo(
|
||||
// Some ImageProvider implementations may not have given this.
|
||||
source: debugImageLabel ?? '<Unknown Image(${image.width}×${image.height})>',
|
||||
imageSize: Size(image.width.toDouble(), image.height.toDouble()),
|
||||
displaySize: outputSize,
|
||||
);
|
||||
// Avoid emitting events that are the same as those emitted in the last frame.
|
||||
if (!_lastFrameImageSizeInfo.contains(sizeInfo)) {
|
||||
final ImageSizeInfo existingSizeInfo = _pendingImageSizeInfo[sizeInfo.source];
|
||||
if (existingSizeInfo == null || existingSizeInfo.displaySizeInBytes < sizeInfo.displaySizeInBytes) {
|
||||
_pendingImageSizeInfo[sizeInfo.source] = sizeInfo;
|
||||
}
|
||||
// _pendingImageSizeInfo.add(sizeInfo);
|
||||
if (debugOnPaintImage != null) {
|
||||
debugOnPaintImage(sizeInfo);
|
||||
}
|
||||
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||
_lastFrameImageSizeInfo = _pendingImageSizeInfo.values.toSet();
|
||||
if (_pendingImageSizeInfo.isEmpty) {
|
||||
return;
|
||||
}
|
||||
developer.postEvent(
|
||||
'Flutter.ImageSizesForFrame',
|
||||
<Object, Object>{
|
||||
for (ImageSizeInfo imageSizeInfo in _pendingImageSizeInfo.values)
|
||||
imageSizeInfo.source: imageSizeInfo.toJson()
|
||||
},
|
||||
);
|
||||
_pendingImageSizeInfo = <String, ImageSizeInfo>{};
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (repeat != ImageRepeat.noRepeat && destinationSize == outputSize) {
|
||||
// There's no need to repeat the image because we're exactly filling the
|
||||
// output rect with the image.
|
||||
|
@ -510,6 +474,79 @@ void paintImage({
|
|||
final double dy = halfHeightDelta + alignment.y * halfHeightDelta;
|
||||
final Offset destinationPosition = rect.topLeft.translate(dx, dy);
|
||||
final Rect destinationRect = destinationPosition & destinationSize;
|
||||
|
||||
// Set to true if we added a saveLayer to the canvas to invert/flip the image.
|
||||
bool invertedCanvas = false;
|
||||
// Output size and destination rect are fully calculated.
|
||||
if (!kReleaseMode) {
|
||||
final ImageSizeInfo sizeInfo = ImageSizeInfo(
|
||||
// Some ImageProvider implementations may not have given this.
|
||||
source: debugImageLabel ?? '<Unknown Image(${image.width}×${image.height})>',
|
||||
imageSize: Size(image.width.toDouble(), image.height.toDouble()),
|
||||
displaySize: outputSize,
|
||||
);
|
||||
assert(() {
|
||||
if (debugInvertOversizedImages &&
|
||||
sizeInfo.decodedSizeInBytes > sizeInfo.displaySizeInBytes + debugImageOverheadAllowance) {
|
||||
final int overheadInKilobytes = (sizeInfo.decodedSizeInBytes - sizeInfo.displaySizeInBytes) ~/ 1024;
|
||||
final int outputWidth = outputSize.width.toInt();
|
||||
final int outputHeight = outputSize.height.toInt();
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: 'Image $debugImageLabel has a display size of '
|
||||
'$outputWidth×$outputHeight but a decode size of '
|
||||
'${image.width}×${image.height}, which uses an additional '
|
||||
'${overheadInKilobytes}kb.\n\n'
|
||||
'Consider resizing the asset ahead of time, supplying a cacheWidth '
|
||||
'parameter of $outputWidth, a cacheHeight parameter of '
|
||||
'$outputHeight, or using a ResizeImage.',
|
||||
library: 'painting library',
|
||||
context: ErrorDescription('while painting an image'),
|
||||
));
|
||||
// Invert the colors of the canvas.
|
||||
canvas.saveLayer(
|
||||
destinationRect,
|
||||
Paint()..colorFilter = const ColorFilter.matrix(<double>[
|
||||
-1, 0, 0, 0, 255,
|
||||
0, -1, 0, 0, 255,
|
||||
0, 0, -1, 0, 255,
|
||||
0, 0, 0, 1, 0,
|
||||
]),
|
||||
);
|
||||
// Flip the canvas vertically.
|
||||
final double dy = -(rect.top + rect.height / 2.0);
|
||||
canvas.translate(0.0, -dy);
|
||||
canvas.scale(1.0, -1.0);
|
||||
canvas.translate(0.0, dy);
|
||||
invertedCanvas = true;
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
// Avoid emitting events that are the same as those emitted in the last frame.
|
||||
if (!_lastFrameImageSizeInfo.contains(sizeInfo)) {
|
||||
final ImageSizeInfo existingSizeInfo = _pendingImageSizeInfo[sizeInfo.source];
|
||||
if (existingSizeInfo == null || existingSizeInfo.displaySizeInBytes < sizeInfo.displaySizeInBytes) {
|
||||
_pendingImageSizeInfo[sizeInfo.source] = sizeInfo;
|
||||
}
|
||||
if (debugOnPaintImage != null) {
|
||||
debugOnPaintImage(sizeInfo);
|
||||
}
|
||||
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
|
||||
_lastFrameImageSizeInfo = _pendingImageSizeInfo.values.toSet();
|
||||
if (_pendingImageSizeInfo.isEmpty) {
|
||||
return;
|
||||
}
|
||||
developer.postEvent(
|
||||
'Flutter.ImageSizesForFrame',
|
||||
<Object, Object>{
|
||||
for (ImageSizeInfo imageSizeInfo in _pendingImageSizeInfo.values)
|
||||
imageSizeInfo.source: imageSizeInfo.toJson()
|
||||
},
|
||||
);
|
||||
_pendingImageSizeInfo = <String, ImageSizeInfo>{};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final bool needSave = repeat != ImageRepeat.noRepeat || flipHorizontally;
|
||||
if (needSave)
|
||||
canvas.save();
|
||||
|
@ -541,6 +578,10 @@ void paintImage({
|
|||
}
|
||||
if (needSave)
|
||||
canvas.restore();
|
||||
|
||||
if (invertedCanvas) {
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<Rect> _generateImageTileRects(Rect outputRect, Rect fundamentalRect, ImageRepeat repeat) sync* {
|
||||
|
|
|
@ -448,6 +448,18 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
|
|||
}
|
||||
|
||||
assert(() {
|
||||
registerBoolServiceExtension(
|
||||
name: 'invertOversizedImages',
|
||||
getter: () async => debugInvertOversizedImages,
|
||||
setter: (bool value) async {
|
||||
if (debugInvertOversizedImages != value) {
|
||||
debugInvertOversizedImages = value;
|
||||
return _forceRebuild();
|
||||
}
|
||||
return Future<void>.value();
|
||||
},
|
||||
);
|
||||
|
||||
registerBoolServiceExtension(
|
||||
name: 'debugAllowBanner',
|
||||
getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
|
||||
|
|
|
@ -171,7 +171,7 @@ void main() {
|
|||
const int disabledExtensions = kIsWeb ? 2 : 0;
|
||||
// If you add a service extension... TEST IT! :-)
|
||||
// ...then increment this number.
|
||||
expect(binding.extensions.length, 28 + widgetInspectorExtensionCount - disabledExtensions);
|
||||
expect(binding.extensions.length, 29 + widgetInspectorExtensionCount - disabledExtensions);
|
||||
|
||||
expect(console, isEmpty);
|
||||
debugPrint = debugPrintThrottled;
|
||||
|
@ -401,6 +401,29 @@ void main() {
|
|||
expect(binding.frameScheduled, isFalse);
|
||||
});
|
||||
|
||||
test('Service extensions - invertOversizedImages', () async {
|
||||
Map<String, dynamic> result;
|
||||
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
expect(debugInvertOversizedImages, false);
|
||||
result = await binding.testExtension('invertOversizedImages', <String, String>{});
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugInvertOversizedImages, false);
|
||||
result = await binding.testExtension('invertOversizedImages', <String, String>{'enabled': 'true'});
|
||||
expect(result, <String, String>{'enabled': 'true'});
|
||||
expect(debugInvertOversizedImages, true);
|
||||
result = await binding.testExtension('invertOversizedImages', <String, String>{});
|
||||
expect(result, <String, String>{'enabled': 'true'});
|
||||
expect(debugInvertOversizedImages, true);
|
||||
result = await binding.testExtension('invertOversizedImages', <String, String>{'enabled': 'false'});
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugInvertOversizedImages, false);
|
||||
result = await binding.testExtension('invertOversizedImages', <String, String>{});
|
||||
expect(result, <String, String>{'enabled': 'false'});
|
||||
expect(debugInvertOversizedImages, false);
|
||||
expect(binding.frameScheduled, isFalse);
|
||||
});
|
||||
|
||||
test('Service extensions - profileWidgetBuilds', () async {
|
||||
Map<String, dynamic> result;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'dart:async';
|
|||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
|
@ -64,6 +65,66 @@ void main() {
|
|||
expect(command.positionalArguments[2], equals(const Rect.fromLTWH(50.0, 75.0, 200.0, 100.0)));
|
||||
});
|
||||
|
||||
test('debugInvertOversizedImages', () {
|
||||
debugInvertOversizedImages = true;
|
||||
final FlutterExceptionHandler oldFlutterError = FlutterError.onError;
|
||||
|
||||
final List<String> messages = <String>[];
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
messages.add(details.exceptionAsString());
|
||||
};
|
||||
|
||||
final TestImage image = TestImage(width: 300, height: 300);
|
||||
final TestCanvas canvas = TestCanvas();
|
||||
const Rect rect = Rect.fromLTWH(50.0, 50.0, 200.0, 100.0);
|
||||
|
||||
paintImage(
|
||||
canvas: canvas,
|
||||
rect: rect,
|
||||
image: image,
|
||||
debugImageLabel: 'TestImage',
|
||||
fit: BoxFit.fill,
|
||||
);
|
||||
|
||||
final List<Invocation> commands = canvas.invocations
|
||||
.skipWhile((Invocation invocation) => invocation.memberName != #saveLayer)
|
||||
.take(4)
|
||||
.toList();
|
||||
|
||||
expect(commands[0].positionalArguments[0], rect);
|
||||
final Paint paint = commands[0].positionalArguments[1] as Paint;
|
||||
expect(
|
||||
paint.colorFilter,
|
||||
const ColorFilter.matrix(<double>[
|
||||
-1, 0, 0, 0, 255,
|
||||
0, -1, 0, 0, 255,
|
||||
0, 0, -1, 0, 255,
|
||||
0, 0, 0, 1, 0,
|
||||
]),
|
||||
);
|
||||
expect(commands[1].memberName, #translate);
|
||||
expect(commands[1].positionalArguments[0], 0.0);
|
||||
expect(commands[1].positionalArguments[1], 100.0);
|
||||
|
||||
expect(commands[2].memberName, #scale);
|
||||
expect(commands[2].positionalArguments[0], 1.0);
|
||||
expect(commands[2].positionalArguments[1], -1.0);
|
||||
|
||||
|
||||
expect(commands[3].memberName, #translate);
|
||||
expect(commands[3].positionalArguments[0], 0.0);
|
||||
expect(commands[3].positionalArguments[1], -100.0);
|
||||
|
||||
expect(
|
||||
messages.single,
|
||||
'Image TestImage has a display size of 200×100 but a decode size of 300×300, which uses an additional 364kb.\n\n'
|
||||
'Consider resizing the asset ahead of time, supplying a cacheWidth parameter of 200, a cacheHeight parameter of 100, or using a ResizeImage.',
|
||||
);
|
||||
|
||||
debugInvertOversizedImages = false;
|
||||
FlutterError.onError = oldFlutterError;
|
||||
});
|
||||
|
||||
testWidgets('Reports Image painting', (WidgetTester tester) async {
|
||||
ImageSizeInfo imageSizeInfo;
|
||||
int count = 0;
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'terminal.dart';
|
|||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
const String fire = '🔥';
|
||||
const String image = '🖼️';
|
||||
const int maxLineWidth = 84;
|
||||
|
||||
/// Encapsulates the help text construction and printing.
|
||||
|
@ -35,6 +36,13 @@ class CommandHelp {
|
|||
|
||||
final OutputPreferences _outputPreferences;
|
||||
|
||||
CommandHelpOption _I;
|
||||
CommandHelpOption get I => _I ??= _makeOption(
|
||||
'I',
|
||||
'Toggle oversized image inversion $image.',
|
||||
'debugInvertOversizedImages',
|
||||
);
|
||||
|
||||
CommandHelpOption _L;
|
||||
CommandHelpOption get L => _L ??= _makeOption(
|
||||
'L',
|
||||
|
|
|
@ -364,6 +364,7 @@ class WindowsStdoutLogger extends StdoutLogger {
|
|||
final String windowsMessage = _terminal.supportsEmoji
|
||||
? message
|
||||
: message.replaceAll('🔥', '')
|
||||
.replaceAll('🖼️', '')
|
||||
.replaceAll('✗', 'X')
|
||||
.replaceAll('✓', '√')
|
||||
.replaceAll('🔨', '');
|
||||
|
|
|
@ -354,6 +354,18 @@ abstract class ResidentWebRunner extends ResidentRunner {
|
|||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> debugToggleInvertOversizedImages() async {
|
||||
try {
|
||||
await _vmService
|
||||
?.flutterToggleInvertOversizedImages(
|
||||
isolateId: null,
|
||||
);
|
||||
} on vmservice.RPCError {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> debugToggleProfileWidgetBuilds() async {
|
||||
try {
|
||||
|
|
|
@ -436,6 +436,15 @@ class FlutterDevice {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> toggleInvertOversizedImages() async {
|
||||
final List<FlutterView> views = await vmService.getFlutterViews();
|
||||
for (final FlutterView view in views) {
|
||||
await vmService.flutterToggleInvertOversizedImages(
|
||||
isolateId: view.uiIsolate.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleProfileWidgetBuilds() async {
|
||||
final List<FlutterView> views = await vmService.getFlutterViews();
|
||||
for (final FlutterView view in views) {
|
||||
|
@ -1009,6 +1018,12 @@ abstract class ResidentRunner {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> debugToggleInvertOversizedImages() async {
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
await device.toggleInvertOversizedImages();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> debugToggleProfileWidgetBuilds() async {
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
await device.toggleProfileWidgetBuilds();
|
||||
|
@ -1289,6 +1304,7 @@ abstract class ResidentRunner {
|
|||
commandHelp.S.print();
|
||||
commandHelp.U.print();
|
||||
commandHelp.i.print();
|
||||
commandHelp.I.print();
|
||||
commandHelp.p.print();
|
||||
commandHelp.o.print();
|
||||
commandHelp.z.print();
|
||||
|
@ -1443,12 +1459,17 @@ class TerminalHandler {
|
|||
residentRunner.printHelp(details: true);
|
||||
return true;
|
||||
case 'i':
|
||||
case 'I':
|
||||
if (residentRunner.supportsServiceProtocol) {
|
||||
await residentRunner.debugToggleWidgetInspector();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'I':
|
||||
if (residentRunner.supportsServiceProtocol && residentRunner.isRunningDebug) {
|
||||
await residentRunner.debugToggleInvertOversizedImages();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case 'k':
|
||||
if (residentRunner.supportsCanvasKit) {
|
||||
final bool result = await residentRunner.toggleCanvaskit();
|
||||
|
@ -1499,13 +1520,6 @@ class TerminalHandler {
|
|||
// exit
|
||||
await residentRunner.exit();
|
||||
return true;
|
||||
case 's':
|
||||
for (final FlutterDevice device in residentRunner.flutterDevices) {
|
||||
if (device.device.supportsScreenshot) {
|
||||
await residentRunner.screenshot(device);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case 'r':
|
||||
if (!residentRunner.canHotReload) {
|
||||
return false;
|
||||
|
@ -1531,6 +1545,13 @@ class TerminalHandler {
|
|||
globals.printStatus('Try again after fixing the above error(s).', emphasis: true);
|
||||
}
|
||||
return true;
|
||||
case 's':
|
||||
for (final FlutterDevice device in residentRunner.flutterDevices) {
|
||||
if (device.device.supportsScreenshot) {
|
||||
await residentRunner.screenshot(device);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case 'S':
|
||||
if (residentRunner.supportsServiceProtocol) {
|
||||
await residentRunner.debugDumpSemanticsTreeInTraversalOrder();
|
||||
|
|
|
@ -601,6 +601,10 @@ extension FlutterVmService on vm_service.VmService {
|
|||
@required String isolateId,
|
||||
}) => _flutterToggle('inspector.show', isolateId: isolateId);
|
||||
|
||||
Future<Map<String,dynamic>> flutterToggleInvertOversizedImages({
|
||||
@required String isolateId,
|
||||
}) => _flutterToggle('invertOversizedImages', isolateId: isolateId);
|
||||
|
||||
Future<Map<String, dynamic>> flutterToggleProfileWidgetBuilds({
|
||||
@required String isolateId,
|
||||
}) => _flutterToggle('profileWidgetBuilds', isolateId: isolateId);
|
||||
|
|
|
@ -50,6 +50,10 @@ void _testMessageLength({
|
|||
expectedWidth += ansiMetaCharactersLength;
|
||||
}
|
||||
|
||||
expect(
|
||||
commandHelp.I.toString().length,
|
||||
lessThanOrEqualTo(expectedWidth),
|
||||
);
|
||||
expect(commandHelp.L.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.P.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
expect(commandHelp.R.toString().length, lessThanOrEqualTo(expectedWidth));
|
||||
|
|
|
@ -858,6 +858,7 @@ void main() {
|
|||
commandHelp.S,
|
||||
commandHelp.U,
|
||||
commandHelp.i,
|
||||
commandHelp.I,
|
||||
commandHelp.p,
|
||||
commandHelp.o,
|
||||
commandHelp.z,
|
||||
|
@ -1284,6 +1285,36 @@ void main() {
|
|||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
testUsingContext('ResidentRunner debugToggleInvertOversizedImages calls flutter device', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
await residentRunner.debugToggleInvertOversizedImages();
|
||||
|
||||
verify(mockFlutterDevice.toggleInvertOversizedImages()).called(1);
|
||||
}));
|
||||
|
||||
testUsingContext('FlutterDevice.toggleInvertOversizedImages invokes correct VM service request', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
listViews,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.invertOversizedImages',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'value': 'false'
|
||||
},
|
||||
),
|
||||
]);
|
||||
final FlutterDevice device = FlutterDevice(
|
||||
mockDevice,
|
||||
buildInfo: BuildInfo.debug,
|
||||
);
|
||||
device.vmService = fakeVmServiceHost.vmService;
|
||||
|
||||
await device.toggleInvertOversizedImages();
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
testUsingContext('ResidentRunner debugToggleDebugCheckElevationsEnabled calls flutter device', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
await residentRunner.debugToggleDebugCheckElevationsEnabled();
|
||||
|
|
|
@ -1179,6 +1179,47 @@ void main() {
|
|||
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
|
||||
});
|
||||
|
||||
testUsingContext('debugToggleInvertOversizedImagesOverride', () async {
|
||||
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
...kAttachExpectations,
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.invertOversizedImages',
|
||||
args: <String, Object>{
|
||||
'isolateId': null,
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'enabled': 'false'
|
||||
},
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
method: 'ext.flutter.invertOversizedImages',
|
||||
args: <String, Object>{
|
||||
'isolateId': null,
|
||||
'enabled': 'true',
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'enabled': 'true'
|
||||
},
|
||||
)
|
||||
]);
|
||||
_setupMocks();
|
||||
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
|
||||
unawaited(residentWebRunner.run(
|
||||
connectionInfoCompleter: connectionInfoCompleter,
|
||||
));
|
||||
await connectionInfoCompleter.future;
|
||||
|
||||
await residentWebRunner.debugToggleInvertOversizedImages();
|
||||
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
Pub: () => MockPub(),
|
||||
Platform: () => FakePlatform(operatingSystem: 'linux', environment: <String, String>{}),
|
||||
});
|
||||
|
||||
testUsingContext('debugToggleWidgetInspector', () async {
|
||||
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
|
|
|
@ -122,21 +122,39 @@ void main() {
|
|||
verify(mockResidentRunner.toggleCanvaskit()).called(1);
|
||||
});
|
||||
|
||||
testUsingContext('i, I - debugToggleWidgetInspector with service protocol', () async {
|
||||
testUsingContext('i - debugToggleWidgetInspector with service protocol', () async {
|
||||
await terminalHandler.processTerminalInput('i');
|
||||
await terminalHandler.processTerminalInput('I');
|
||||
|
||||
verify(mockResidentRunner.debugToggleWidgetInspector()).called(2);
|
||||
verify(mockResidentRunner.debugToggleWidgetInspector()).called(1);
|
||||
});
|
||||
|
||||
testUsingContext('i, I - debugToggleWidgetInspector without service protocol', () async {
|
||||
testUsingContext('i - debugToggleWidgetInspector without service protocol', () async {
|
||||
when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
|
||||
await terminalHandler.processTerminalInput('i');
|
||||
await terminalHandler.processTerminalInput('I');
|
||||
|
||||
verifyNever(mockResidentRunner.debugToggleWidgetInspector());
|
||||
});
|
||||
|
||||
testUsingContext('I - debugToggleInvertOversizedImages with service protocol/debug', () async {
|
||||
when(mockResidentRunner.isRunningDebug).thenReturn(true);
|
||||
await terminalHandler.processTerminalInput('I');
|
||||
|
||||
verify(mockResidentRunner.debugToggleInvertOversizedImages()).called(1);
|
||||
});
|
||||
|
||||
testUsingContext('I - debugToggleInvertOversizedImages with service protocol/ndebug', () async {
|
||||
when(mockResidentRunner.isRunningDebug).thenReturn(false);
|
||||
await terminalHandler.processTerminalInput('I');
|
||||
|
||||
verifyNever(mockResidentRunner.debugToggleInvertOversizedImages());
|
||||
});
|
||||
|
||||
testUsingContext('I - debugToggleInvertOversizedImages without service protocol', () async {
|
||||
when(mockResidentRunner.supportsServiceProtocol).thenReturn(false);
|
||||
await terminalHandler.processTerminalInput('I');
|
||||
|
||||
});
|
||||
|
||||
testUsingContext('l - list flutter views', () async {
|
||||
final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
|
||||
when(mockResidentRunner.isRunningDebug).thenReturn(true);
|
||||
|
|
Loading…
Reference in a new issue