Skip to content

Commit c75e876

Browse files
authored
Merge branch 'master' into integration_test_for_debug_syms
2 parents 1c49045 + 53e7e99 commit c75e876

File tree

25 files changed

+325
-272
lines changed

25 files changed

+325
-272
lines changed

dev/devicelab/bin/tasks/module_test_ios.dart

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,6 @@ end
627627
swiftHostApp,
628628
);
629629

630-
final File swiftAnalyticsOutputFile = File(path.join(tempDir.path, 'analytics-swift.log'));
631630
final Directory swiftBuildDirectory = Directory(path.join(tempDir.path, 'build-swift'));
632631

633632
await inDirectory(swiftHostApp, () async {
@@ -652,9 +651,7 @@ end
652651
'BUILD_DIR=${swiftBuildDirectory.path}',
653652
'COMPILER_INDEX_STORE_ENABLE=NO',
654653
],
655-
environment: <String, String>{
656-
'FLUTTER_ANALYTICS_LOG_FILE': swiftAnalyticsOutputFile.path,
657-
},
654+
environment: <String, String>{'FLUTTER_SUPPRESS_ANALYTICS': 'true'},
658655
);
659656
});
660657

@@ -665,16 +662,6 @@ end
665662
return TaskResult.failure('Failed to build existing Swift app .app');
666663
}
667664

668-
final String swiftAnalyticsOutput = swiftAnalyticsOutputFile.readAsStringSync();
669-
if (!swiftAnalyticsOutput.contains('cd24: ios') ||
670-
!swiftAnalyticsOutput.contains('cd25: true') ||
671-
!swiftAnalyticsOutput.contains('viewName: assemble')) {
672-
return TaskResult.failure(
673-
'Building outer Swift app produced the following analytics: "$swiftAnalyticsOutput" '
674-
'but not the expected strings: "cd24: ios", "cd25: true", "viewName: assemble"',
675-
);
676-
}
677-
678665
return TaskResult.success(null);
679666
} catch (e) {
680667
return TaskResult.failure(e.toString());

dev/integration_tests/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ For example:
1818
```sh
1919
flutter drive -t lib/keyboard_resize.dart --driver test_driver/keyboard_resize_test.dart
2020
```
21+
22+
## New tests require new CI runner
23+
Adding code to this directory will not automatically cause it to be run by any already existing ci tooling.
24+
This directory is intentinally a "choose your own adventure" piece of tooling.

dev/tools/bin/config/lockfile_exclusion.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
# hello_world uses a Kotlin Gradle settings file (settings.gradle.kts). We don't have a template
66
# Kotlin Gradle settings file (only a groovy one), so we currently can't generate it.
77
- examples/hello_world/android
8+
# Also uses kts files.
9+
- dev/integration_tests/display_cutout_rotation/android
810

911
# The following files must also be manually updated at the moment, because they don't use a
1012
# lib/main.dart (but they don't need exclusion, as we already skip that case).

engine/src/flutter/fml/logging.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,27 @@ int GetVlogVerbosity();
6060
// kLogFatal and above is always true.
6161
bool ShouldCreateLogMessage(LogSeverity severity);
6262

63+
constexpr bool ShouldCreateLogMessageConstexpr(LogSeverity severity,
64+
bool true_arg) {
65+
if (true_arg) {
66+
return ShouldCreateLogMessage(severity);
67+
}
68+
return false;
69+
}
70+
6371
[[noreturn]] void KillProcess();
6472

73+
[[noreturn]] constexpr void KillProcessConstexpr(bool true_arg) {
74+
if (true_arg) {
75+
KillProcess();
76+
}
77+
#if defined(_MSC_VER) && !defined(__clang__)
78+
__assume(false);
79+
#else // defined(_MSC_VER) && !defined(__clang__)
80+
__builtin_unreachable();
81+
#endif // defined(_MSC_VER) && !defined(__clang__)
82+
}
83+
6584
} // namespace fml
6685

6786
#define FML_LOG_STREAM(severity) \
@@ -77,7 +96,7 @@ bool ShouldCreateLogMessage(LogSeverity severity);
7796
::fml::LogMessage(::fml::kLogFatal, 0, 0, nullptr).stream()
7897

7998
#define FML_LOG_IS_ON(severity) \
80-
(::fml::ShouldCreateLogMessage(::fml::LOG_##severity))
99+
(::fml::ShouldCreateLogMessageConstexpr(::fml::LOG_##severity, true))
81100

82101
#define FML_LOG(severity) \
83102
FML_LAZY_STREAM(FML_LOG_STREAM(severity), FML_LOG_IS_ON(severity))
@@ -109,7 +128,7 @@ bool ShouldCreateLogMessage(LogSeverity severity);
109128
#define FML_UNREACHABLE() \
110129
{ \
111130
FML_LOG(ERROR) << "Reached unreachable code."; \
112-
::fml::KillProcess(); \
131+
::fml::KillProcessConstexpr(true); \
113132
}
114133

115134
#endif // FLUTTER_FML_LOGGING_H_

engine/src/flutter/impeller/display_list/aiks_dl_text_unittests.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct TextRenderOptions {
3131
DlColor color = DlColor::kYellow();
3232
DlPoint position = DlPoint(100, 200);
3333
std::shared_ptr<DlMaskFilter> filter;
34+
bool is_subpixel = false;
3435
};
3536

3637
bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
@@ -57,6 +58,9 @@ bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
5758
}
5859
sk_sp<SkFontMgr> font_mgr = txt::GetDefaultFontManager();
5960
SkFont sk_font(font_mgr->makeFromData(mapping), options.font_size);
61+
if (options.is_subpixel) {
62+
sk_font.setSubpixel(true);
63+
}
6064
auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
6165
if (!blob) {
6266
return false;
@@ -153,10 +157,12 @@ TEST_P(AiksTest, CanRenderTextFrameWithHalfScaling) {
153157

154158
TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
155159
Scalar fine_scale = 0.f;
160+
bool is_subpixel = false;
156161
auto callback = [&]() -> sk_sp<DisplayList> {
157162
if (AiksTest::ImGuiBegin("Controls", nullptr,
158163
ImGuiWindowFlags_AlwaysAutoResize)) {
159164
ImGui::SliderFloat("Fine Scale", &fine_scale, -1, 1);
165+
ImGui::Checkbox("subpixel", &is_subpixel);
160166
ImGui::End();
161167
}
162168

@@ -168,7 +174,8 @@ TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
168174
builder.Scale(scale, scale);
169175
RenderTextInCanvasSkia(GetContext(), builder,
170176
"the quick brown fox jumped over the lazy dog!.?",
171-
"Roboto-Regular.ttf");
177+
"Roboto-Regular.ttf",
178+
TextRenderOptions{.is_subpixel = is_subpixel});
172179
return builder.Build();
173180
};
174181

engine/src/flutter/impeller/display_list/dl_golden_unittests.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,22 @@ int32_t CalculateMaxY(const impeller::testing::Screenshot* img) {
366366
}
367367
return max_y;
368368
}
369+
370+
int32_t CalculateSpaceBetweenUI(const impeller::testing::Screenshot* img) {
371+
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(img->GetBytes());
372+
ptr += img->GetWidth() * static_cast<int32_t>(img->GetHeight() / 2.0);
373+
std::vector<size_t> boundaries;
374+
uint32_t value = *ptr++;
375+
for (size_t i = 1; i < img->GetWidth(); ++i) {
376+
if (((*ptr & 0x00ffffff) != 0) != ((value & 0x00ffffff) != 0)) {
377+
boundaries.push_back(i);
378+
}
379+
value = *ptr++;
380+
}
381+
382+
assert(boundaries.size() == 6);
383+
return boundaries[4] - boundaries[3];
384+
}
369385
} // namespace
370386

371387
TEST_P(DlGoldenTest, BaselineHE) {
@@ -399,5 +415,42 @@ TEST_P(DlGoldenTest, BaselineHE) {
399415
int32_t y_diff = std::abs(left_max_y - right_max_y);
400416
EXPECT_TRUE(y_diff <= 2) << "y diff: " << y_diff;
401417
}
418+
419+
TEST_P(DlGoldenTest, MaintainsSpace) {
420+
SetWindowSize(impeller::ISize(1024, 200));
421+
impeller::Scalar font_size = 300;
422+
auto callback = [&](const char* text,
423+
impeller::Scalar scale) -> sk_sp<DisplayList> {
424+
DisplayListBuilder builder;
425+
DlPaint paint;
426+
paint.setColor(DlColor::ARGB(1, 0, 0, 0));
427+
builder.DrawPaint(paint);
428+
builder.Scale(scale, scale);
429+
RenderTextInCanvasSkia(&builder, text, "Roboto-Regular.ttf",
430+
DlPoint::MakeXY(10, 300),
431+
TextRenderOptions{
432+
.font_size = font_size,
433+
});
434+
return builder.Build();
435+
};
436+
437+
std::optional<int32_t> last_space;
438+
for (int i = 0; i <= 100; ++i) {
439+
Scalar scale = 0.440 + i / 1000.0;
440+
std::unique_ptr<impeller::testing::Screenshot> right =
441+
MakeScreenshot(callback("ui", scale));
442+
if (!right) {
443+
GTEST_SKIP() << "making screenshots not supported.";
444+
}
445+
446+
int32_t space = CalculateSpaceBetweenUI(right.get());
447+
if (last_space.has_value()) {
448+
int32_t diff = abs(space - *last_space);
449+
EXPECT_TRUE(diff <= 1)
450+
<< "i:" << i << " space:" << space << " last_space:" << *last_space;
451+
}
452+
last_space = space;
453+
}
454+
}
402455
} // namespace testing
403456
} // namespace flutter

engine/src/flutter/impeller/entity/contents/text_contents.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ void TextContents::ComputeVertexData(
183183
Point screen_glyph_position =
184184
(screen_offset + unrounded_glyph_position + subpixel_adjustment)
185185
.Floor();
186-
187186
for (const Point& point : unit_points) {
188187
Point position;
189188
if (is_translation_scale) {

engine/src/flutter/impeller/typographer/text_frame.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Scalar TextFrame::RoundScaledFontSize(Scalar scale) {
5656
// CTM, a glyph will fit in the atlas. If we clamp significantly, this may
5757
// reduce fidelity but is preferable to the alternative of failing to render.
5858
constexpr Scalar kMaximumTextScale = 48;
59-
Scalar result = std::round(scale * 100) / 100;
59+
Scalar result = std::round(scale * 200) / 200;
6060
return std::clamp(result, 0.0f, kMaximumTextScale);
6161
}
6262

packages/flutter_goldens/lib/flutter_goldens.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,17 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator {
320320
golden = _addPrefix(golden);
321321
await update(golden, imageBytes);
322322
final File goldenFile = getGoldenFile(golden);
323-
return skiaClient.imgtestAdd(golden.path, goldenFile);
323+
try {
324+
return await skiaClient.imgtestAdd(golden.path, goldenFile);
325+
} on SkiaException catch (e) {
326+
// Convert SkiaException -> TestFailure so that this class implements the
327+
// contract of GoldenFileComparator, and matchesGoldenFile() converts the
328+
// TestFailure into a standard reported test error (with a better stack
329+
// trace, for example).
330+
//
331+
// https://github.com/flutter/flutter/issues/162621
332+
throw TestFailure('$e');
333+
}
324334
}
325335

326336
/// Decides based on the current environment if goldens tests should be

packages/flutter_goldens/test/flutter_goldens_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,43 @@ void main() {
853853
);
854854
expect(fakeSkiaClient.initCalls, 0);
855855
});
856+
857+
test('reports a failure as a TestFailure', () async {
858+
final List<String> log = <String>[];
859+
final MemoryFileSystem fs = MemoryFileSystem();
860+
final FakePlatform platform = FakePlatform(
861+
environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
862+
operatingSystem: 'macos',
863+
);
864+
fs.directory(_kFlutterRoot).createSync(recursive: true);
865+
final Directory basedir = fs.directory('flutter/test/library/')
866+
..createSync(recursive: true);
867+
final FlutterGoldenFileComparator comparator = FlutterPostSubmitFileComparator(
868+
basedir.uri,
869+
ThrowsOnImgTestAddSkiaClient(
870+
message: 'Skia Gold received an unapproved image in post-submit',
871+
),
872+
fs: fs,
873+
platform: platform,
874+
log: log.add,
875+
);
876+
await expectLater(
877+
() async {
878+
return comparator.compare(
879+
Uint8List.fromList(_kTestPngBytes),
880+
Uri.parse('flutter.golden_test.1.png'),
881+
);
882+
},
883+
throwsA(
884+
isA<TestFailure>().having(
885+
(TestFailure error) => error.toString(),
886+
'description',
887+
contains('Skia Gold received an unapproved image in post-submit'),
888+
),
889+
),
890+
);
891+
expect(log, isEmpty);
892+
});
856893
});
857894

858895
group('Pre-Submit', () {
@@ -1210,6 +1247,21 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient {
12101247
String cleanTestName(String fileName) => cleanTestNameValues[fileName] ?? '';
12111248
}
12121249

1250+
class ThrowsOnImgTestAddSkiaClient extends Fake implements SkiaGoldClient {
1251+
ThrowsOnImgTestAddSkiaClient({required this.message});
1252+
final String message;
1253+
1254+
@override
1255+
Future<void> imgtestInit() async {
1256+
// Assume this function works.
1257+
}
1258+
1259+
@override
1260+
Future<bool> imgtestAdd(String testName, File goldenFile) {
1261+
throw SkiaException(message);
1262+
}
1263+
}
1264+
12131265
class FakeLocalFileComparator extends Fake implements LocalFileComparator {
12141266
@override
12151267
late Uri basedir;

0 commit comments

Comments
 (0)