flutter_flutter/packages/flutter_test/test/platform_dispatcher_test.dart
Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

349 lines
12 KiB
Dart

// 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:ui'
show
AccessibilityFeatures,
Brightness,
Display,
FlutterView,
Locale,
PlatformDispatcher,
ViewFocusChangeCallback,
VoidCallback;
import 'package:flutter/widgets.dart' show WidgetsBinding, WidgetsBindingObserver;
import 'package:flutter_test/flutter_test.dart';
import 'utils/fake_and_mock_utils.dart';
void main() {
test('TestPlatformDispatcher can handle new methods without breaking', () {
final dynamic testPlatformDispatcher = TestPlatformDispatcher(
platformDispatcher: PlatformDispatcher.instance,
);
// ignore: avoid_dynamic_calls
expect(testPlatformDispatcher.someNewProperty, null);
});
testWidgets('TestPlatformDispatcher can fake locale', (WidgetTester tester) async {
verifyPropertyFaked<Locale>(
tester: tester,
realValue: PlatformDispatcher.instance.locale,
fakeValue: const Locale('fake_language_code'),
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.locale;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Locale fakeValue) {
binding.platformDispatcher.localeTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake locales', (WidgetTester tester) async {
verifyPropertyFaked<List<Locale>>(
tester: tester,
realValue: PlatformDispatcher.instance.locales,
fakeValue: <Locale>[const Locale('fake_language_code')],
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.locales;
},
propertyFaker: (TestWidgetsFlutterBinding binding, List<Locale> fakeValue) {
binding.platformDispatcher.localesTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake text scale factor', (WidgetTester tester) async {
verifyPropertyFaked<double>(
tester: tester,
realValue: PlatformDispatcher.instance.textScaleFactor,
fakeValue: 2.5,
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.textScaleFactor;
},
propertyFaker: (TestWidgetsFlutterBinding binding, double fakeValue) {
binding.platformDispatcher.textScaleFactorTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake clock format', (WidgetTester tester) async {
verifyPropertyFaked<bool>(
tester: tester,
realValue: PlatformDispatcher.instance.alwaysUse24HourFormat,
fakeValue: !PlatformDispatcher.instance.alwaysUse24HourFormat,
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.alwaysUse24HourFormat;
},
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.platformDispatcher.alwaysUse24HourFormatTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake supportsShowingSystemContextMenu', (
WidgetTester tester,
) async {
verifyPropertyFaked<bool>(
tester: tester,
realValue: PlatformDispatcher.instance.supportsShowingSystemContextMenu,
fakeValue: !PlatformDispatcher.instance.supportsShowingSystemContextMenu,
propertyRetriever: () =>
WidgetsBinding.instance.platformDispatcher.supportsShowingSystemContextMenu,
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.platformDispatcher.supportsShowingSystemContextMenu = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake brieflyShowPassword', (WidgetTester tester) async {
verifyPropertyFaked<bool>(
tester: tester,
realValue: PlatformDispatcher.instance.brieflyShowPassword,
fakeValue: !PlatformDispatcher.instance.brieflyShowPassword,
propertyRetriever: () => WidgetsBinding.instance.platformDispatcher.brieflyShowPassword,
propertyFaker: (TestWidgetsFlutterBinding binding, bool fakeValue) {
binding.platformDispatcher.brieflyShowPasswordTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake default route name', (WidgetTester tester) async {
verifyPropertyFaked<String>(
tester: tester,
realValue: PlatformDispatcher.instance.defaultRouteName,
fakeValue: 'fake_route',
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.defaultRouteName;
},
propertyFaker: (TestWidgetsFlutterBinding binding, String fakeValue) {
binding.platformDispatcher.defaultRouteNameTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake accessibility features', (
WidgetTester tester,
) async {
verifyPropertyFaked<AccessibilityFeatures>(
tester: tester,
realValue: PlatformDispatcher.instance.accessibilityFeatures,
fakeValue: const FakeAccessibilityFeatures(),
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.accessibilityFeatures;
},
propertyFaker: (TestWidgetsFlutterBinding binding, AccessibilityFeatures fakeValue) {
binding.platformDispatcher.accessibilityFeaturesTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can fake platform brightness', (WidgetTester tester) async {
verifyPropertyFaked<Brightness>(
tester: tester,
realValue: Brightness.light,
fakeValue: Brightness.dark,
propertyRetriever: () {
return WidgetsBinding.instance.platformDispatcher.platformBrightness;
},
propertyFaker: (TestWidgetsFlutterBinding binding, Brightness fakeValue) {
binding.platformDispatcher.platformBrightnessTestValue = fakeValue;
},
);
});
testWidgets('TestPlatformDispatcher can clear out fake properties all at once', (
WidgetTester tester,
) async {
final Locale originalLocale = PlatformDispatcher.instance.locale;
final double originalTextScaleFactor = PlatformDispatcher.instance.textScaleFactor;
final TestPlatformDispatcher testPlatformDispatcher = retrieveTestBinding(
tester,
).platformDispatcher;
// Set fake values for window properties.
testPlatformDispatcher.localeTestValue = const Locale('foobar');
testPlatformDispatcher.textScaleFactorTestValue = 3.0;
// Erase fake window property values.
testPlatformDispatcher.clearAllTestValues();
// Verify that the window once again reports real property values.
expect(WidgetsBinding.instance.platformDispatcher.locale, originalLocale);
expect(WidgetsBinding.instance.platformDispatcher.textScaleFactor, originalTextScaleFactor);
});
testWidgets(
'TestPlatformDispatcher sends fake locales when WidgetsBindingObserver notifiers are called',
(WidgetTester tester) async {
final List<Locale> defaultLocales = WidgetsBinding.instance.platformDispatcher.locales;
final observer = TestObserver();
retrieveTestBinding(tester).addObserver(observer);
final expectedValue = <Locale>[const Locale('fake_language_code')];
retrieveTestBinding(tester).platformDispatcher.localesTestValue = expectedValue;
expect(observer.locales, equals(expectedValue));
retrieveTestBinding(tester).platformDispatcher.localesTestValue = defaultLocales;
},
);
testWidgets('TestPlatformDispatcher.view getter returns the implicit view', (
WidgetTester tester,
) async {
expect(
WidgetsBinding.instance.platformDispatcher.view(id: tester.view.viewId),
same(tester.view),
);
});
testWidgets('TestPlatformDispatcher has a working scaleFontSize implementation', (
WidgetTester tester,
) async {
expect(
TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[_FakeDisplay(id: 2)],
views: <FlutterView>[_FakeFlutterView(display: _FakeDisplay(id: 1))],
),
).scaleFontSize(2.0),
2.0,
);
});
// TODO(pdblasi-google): Removed this group of tests when the Display API is stable and supported on all platforms.
group('TestPlatformDispatcher with unsupported Display API', () {
testWidgets('can initialize with empty displays', (WidgetTester tester) async {
expect(() {
TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[],
views: <FlutterView>[_FakeFlutterView()],
),
);
}, isNot(throwsA(anything)));
});
testWidgets('can initialize with mismatched displays', (WidgetTester tester) async {
expect(() {
TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[_FakeDisplay(id: 2)],
views: <FlutterView>[_FakeFlutterView(display: _FakeDisplay(id: 1))],
),
);
}, isNot(throwsA(anything)));
});
testWidgets('creates test views for all views', (WidgetTester tester) async {
final PlatformDispatcher backingDispatcher = _FakePlatformDispatcher(
displays: <Display>[],
views: <FlutterView>[_FakeFlutterView()],
);
final testDispatcher = TestPlatformDispatcher(platformDispatcher: backingDispatcher);
expect(testDispatcher.views.length, backingDispatcher.views.length);
});
group('creates TestFlutterViews', () {
testWidgets('that defaults to the correct devicePixelRatio', (WidgetTester tester) async {
const expectedDpr = 2.5;
final testDispatcher = TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[],
views: <FlutterView>[_FakeFlutterView(devicePixelRatio: expectedDpr)],
),
);
expect(testDispatcher.views.single.devicePixelRatio, expectedDpr);
});
testWidgets('with working devicePixelRatio setter', (WidgetTester tester) async {
const expectedDpr = 2.5;
const double defaultDpr = 4;
final testDispatcher = TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[],
views: <FlutterView>[_FakeFlutterView(devicePixelRatio: defaultDpr)],
),
);
testDispatcher.views.single.devicePixelRatio = expectedDpr;
expect(testDispatcher.views.single.devicePixelRatio, expectedDpr);
});
testWidgets('with working resetDevicePixelRatio', (WidgetTester tester) async {
const changedDpr = 2.5;
const double defaultDpr = 4;
final testDispatcher = TestPlatformDispatcher(
platformDispatcher: _FakePlatformDispatcher(
displays: <Display>[],
views: <FlutterView>[_FakeFlutterView(devicePixelRatio: defaultDpr)],
),
);
testDispatcher.views.single.devicePixelRatio = changedDpr;
testDispatcher.views.single.resetDevicePixelRatio();
expect(testDispatcher.views.single.devicePixelRatio, defaultDpr);
});
});
});
}
class TestObserver with WidgetsBindingObserver {
List<Locale>? locales;
@override
void didChangeLocales(List<Locale>? locales) {
this.locales = locales;
}
}
class _FakeDisplay extends Fake implements Display {
_FakeDisplay({this.id = 0});
@override
final int id;
}
class _FakeFlutterView extends Fake implements FlutterView {
_FakeFlutterView({this.devicePixelRatio = 1, Display? display}) : _display = display;
@override
final double devicePixelRatio;
// This emulates the PlatformDispatcher not having a display on the engine
// side. We don't have access to the `_displayId` used in the engine to try
// to find it and can't directly extend `FlutterView` to emulate it closer.
@override
Display get display {
assert(_display != null);
return _display!;
}
final Display? _display;
@override
final int viewId = 1;
}
class _FakePlatformDispatcher extends Fake implements PlatformDispatcher {
_FakePlatformDispatcher({required this.displays, required this.views});
@override
final Iterable<Display> displays;
@override
final Iterable<FlutterView> views;
@override
VoidCallback? onMetricsChanged;
@override
ViewFocusChangeCallback? onViewFocusChange;
@override
double get textScaleFactor => 1.0;
}