Skip to content

Commit ef81058

Browse files
Cupertino icons golden tests (#7421)
This is for flutter/flutter#148075, we want to make sure the glyphs don't look different when the font generation script is migrated from python. Also it will be nice to be able to inspect glyph changes whenever the ttf file gets updated. The `flutter test` command takes 6s to complete on my computer. Unfortunately this test: - is sensitive to how flutter renders text on Linux, and how the `Icon` widget positions the glyph. - will require contributors to have access to a Linux box in order to generate new goldens. The tests are disabled on the web since it needs the `dart:io` import. The `icons_list.dart` file is automatically generated. Not sure if there's a way to incorporate the code gen step on CI?
1 parent b9715b7 commit ef81058

File tree

68 files changed

+107
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+107
-0
lines changed

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ packages/web_benchmarks/** @yjbanov
4545
packages/webview_flutter/** @bparrishMines
4646
packages/xdg_directories/** @stuartmorgan
4747
third_party/packages/cupertino_icons/** @MitchellGoodwin
48+
third_party/packages/cupertino_icons/test/goldens/** @LongCatIsLooong
4849

4950
# Plugin platform implementation rules. These should stay last, since the last
5051
# matching entry takes precedence.

third_party/packages/cupertino_icons/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ environment:
99
sdk: ^3.3.0
1010

1111
dev_dependencies:
12+
collection: ^1.18.0
1213
flutter:
1314
sdk: flutter
1415
flutter_test:
1516
sdk: flutter
17+
path: ^1.9.0
1618

1719
flutter:
1820
fonts:
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:io' show File, Platform;
6+
7+
import 'package:collection/collection.dart';
8+
import 'package:flutter/cupertino.dart';
9+
import 'package:flutter/foundation.dart';
10+
import 'package:flutter/services.dart';
11+
import 'package:flutter_test/flutter_test.dart';
12+
import 'package:path/path.dart' as path;
13+
14+
import 'icons_list.dart';
15+
16+
// The EM of the font is 512. Keep this a power of 2 for fixed-point arithmetic.
17+
const double iconSize = 128.0;
18+
const int iconsPerRow = 5;
19+
const int iconsPerCol = 5;
20+
const int iconsPerImage = iconsPerRow * iconsPerCol;
21+
22+
void main() async {
23+
// Do not run on web since this test uses dart:io.
24+
// The golden test runs on Linux only to avoid platform rendering differences.
25+
if (kIsWeb || !Platform.isLinux) {
26+
return;
27+
}
28+
final bool isMainChannel = !Platform.environment.containsKey('CHANNEL')
29+
|| Platform.environment['CHANNEL'] == 'main'
30+
|| Platform.environment['CHANNEL'] == 'master';
31+
// Only test against main to avoid rendering differences between flutter channels.
32+
if (!isMainChannel) {
33+
return;
34+
}
35+
// Load font.
36+
final String effectiveFontFamily = const TextStyle(fontFamily: CupertinoIcons.iconFont, package: CupertinoIcons.iconFontPackage).fontFamily!;
37+
final FontLoader fontLoader = FontLoader(effectiveFontFamily);
38+
final String filePath = path.canonicalize('assets/CupertinoIcons.ttf');
39+
final File file = File(filePath);
40+
fontLoader.addFont(file.readAsBytes().then((Uint8List v) => v.buffer.asByteData()));
41+
await fontLoader.load();
42+
43+
assert(icons.isNotEmpty);
44+
for (int index = 0; index < icons.length;) {
45+
final int groupEndCodePoint = (icons[index].codePoint ~/ iconsPerImage + 1) * iconsPerImage;
46+
final int next = icons.indexWhere((IconData icon) => icon.codePoint >= groupEndCodePoint, index);
47+
final int nextIndex = next < 0 ? icons.length : next;
48+
registerTestForIconGroup(icons.slice(index, nextIndex));
49+
index = nextIndex;
50+
}
51+
}
52+
53+
// Generating goldens for each glyph is very slow. Group the sorted icons
54+
// into codepoint-aligned groups (each of which has a max capacity of
55+
// iconsPerRow * iconsPerCol), so the goldens are easier to review when
56+
// symbols are added or removed.
57+
void registerTestForIconGroup(List<IconData> iconGroup) {
58+
assert(iconGroup.isNotEmpty);
59+
String hexCodePoint(int codePoint) => codePoint.toRadixString(16).toUpperCase().padLeft(4, '0');
60+
final int groupStartCodePoint = (iconGroup.first.codePoint ~/ iconsPerImage) * iconsPerImage;
61+
final String range = 'U+${hexCodePoint(groupStartCodePoint)}-${hexCodePoint(groupStartCodePoint + iconsPerImage - 1)}';
62+
63+
testWidgets('font golden test: $range', (WidgetTester tester) async {
64+
addTearDown(tester.view.reset);
65+
const Size canvasSize = Size(iconSize * iconsPerRow, iconSize * iconsPerCol);
66+
tester.view.physicalSize = canvasSize * tester.view.devicePixelRatio;
67+
68+
const Widget fillerBox = SizedBox.square(dimension: iconSize);
69+
final List<Widget> children = List<Widget>.filled(iconsPerImage, fillerBox);
70+
for (final IconData icon in iconGroup) {
71+
children[icon.codePoint - groupStartCodePoint] = Icon(icon, size: iconSize);
72+
}
73+
74+
final Widget widget = Directionality(
75+
textDirection: TextDirection.ltr,
76+
child: Center(
77+
child: SizedBox(
78+
height: iconSize * iconsPerCol,
79+
width: iconSize * iconsPerRow,
80+
child: RepaintBoundary(child: Wrap(children: children)),
81+
),
82+
),
83+
);
84+
await tester.pumpWidget(widget);
85+
await expectLater(find.byType(Wrap) , matchesGoldenFile('goldens/glyph_$range.png'));
86+
});
87+
}
7.05 KB
12.6 KB
5.12 KB
2.47 KB
5.28 KB
9.06 KB
8.86 KB

0 commit comments

Comments
 (0)