Skip to content

Commit 5e19438

Browse files
authored
Add a test of an Android platform view that draws a gradient (#153878)
Closes #152376. The gradient, for posterity, looks like this: <img src="https://github.com/user-attachments/assets/bed9599a-4e16-499c-af79-b51980095e89" width="150"> Let's see if CI agrees! /cc @johnmccutchan
1 parent 327193a commit 5e19438

File tree

8 files changed

+189
-17
lines changed

8 files changed

+189
-17
lines changed

dev/bots/suite_runners/run_flutter_driver_android_tests.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ Future<void> runFlutterDriverAndroidTests() async {
4444
'flutter',
4545
<String>[
4646
'drive',
47+
'lib/blue_rectangle_main.dart',
48+
// There are no reason to enable development flags for this test.
49+
// Disable them to work around flakiness issues, and in general just
50+
// make less things start up unnecessarily.
51+
'--no-dds',
52+
'--no-enable-dart-profiling',
53+
'--test-arguments=test',
54+
'--test-arguments=--reporter=expanded',
55+
],
56+
workingDirectory: path.join(
57+
'dev',
58+
'integration_tests',
59+
'android_driver_test',
60+
),
61+
);
62+
63+
await runCommand(
64+
'flutter',
65+
<String>[
66+
'drive',
67+
'lib/blue_orange_gradient_platform_view_main.dart',
4768
// There are no reason to enable development flags for this test.
4869
// Disable them to work around flakiness issues, and in general just
4970
// make less things start up unnecessarily.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2014 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+
@file:Suppress("PackageName")
6+
7+
package com.example.android_driver_test
8+
9+
import android.content.Context
10+
import android.graphics.Canvas
11+
import android.graphics.Color
12+
import android.graphics.LinearGradient
13+
import android.graphics.Paint
14+
import android.graphics.Shader
15+
import android.view.View
16+
import android.view.ViewGroup
17+
import io.flutter.plugin.platform.PlatformView
18+
import io.flutter.plugin.platform.PlatformViewFactory
19+
20+
class BlueOrangeGradientPlatformViewFactory : PlatformViewFactory(null) {
21+
override fun create(
22+
context: Context,
23+
viewId: Int,
24+
args: Any?
25+
): PlatformView = GradientPlatformView(context)
26+
}
27+
28+
private class GradientPlatformView(
29+
context: Context
30+
) : View(context),
31+
PlatformView {
32+
val paint = Paint()
33+
34+
init {
35+
layoutParams =
36+
ViewGroup.LayoutParams(
37+
ViewGroup.LayoutParams.MATCH_PARENT,
38+
ViewGroup.LayoutParams.MATCH_PARENT
39+
)
40+
}
41+
42+
override fun getView(): View = this
43+
44+
override fun dispose() {}
45+
46+
override fun onDraw(canvas: Canvas) {
47+
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
48+
super.onDraw(canvas)
49+
}
50+
51+
override fun onSizeChanged(
52+
w: Int,
53+
h: Int,
54+
oldw: Int,
55+
oldh: Int
56+
) {
57+
paint.shader =
58+
LinearGradient(
59+
0f,
60+
0f,
61+
w.toFloat(),
62+
h.toFloat(),
63+
intArrayOf(
64+
Color.rgb(0x41, 0x69, 0xE1),
65+
Color.rgb(0xFF, 0xA5, 0x00)
66+
),
67+
null,
68+
Shader.TileMode.CLAMP
69+
)
70+
super.onSizeChanged(w, h, oldw, oldh)
71+
}
72+
}

dev/integration_tests/android_driver_test/android/app/src/main/kotlin/com/example/android_driver_test/MainActivity.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,18 @@ import androidx.core.view.WindowCompat
1111
import androidx.core.view.WindowInsetsCompat
1212
import androidx.core.view.WindowInsetsControllerCompat
1313
import io.flutter.embedding.android.FlutterActivity
14+
import io.flutter.embedding.engine.FlutterEngine
1415

1516
class MainActivity : FlutterActivity() {
17+
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
18+
// Intentionally do not use GeneratedPluginRegistrant.
19+
20+
flutterEngine
21+
.platformViewsController
22+
.registry
23+
.registerViewFactory("blue_orange_gradient_platform_view", BlueOrangeGradientPlatformViewFactory())
24+
}
25+
1626
override fun onCreate(savedInstanceState: Bundle?) {
1727
super.onCreate(savedInstanceState)
1828

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2014 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' as io;
6+
7+
import 'package:flutter/foundation.dart';
8+
import 'package:flutter/material.dart';
9+
import 'package:flutter_driver/driver_extension.dart';
10+
11+
void main() {
12+
enableFlutterDriverExtension();
13+
14+
if (kIsWeb || !io.Platform.isAndroid) {
15+
throw UnsupportedError('This app should only run on Android devices.');
16+
}
17+
18+
runApp(const MainApp());
19+
}
20+
21+
final class MainApp extends StatelessWidget {
22+
const MainApp({super.key});
23+
24+
@override
25+
Widget build(BuildContext context) {
26+
return const MaterialApp(
27+
debugShowCheckedModeBanner: false,
28+
home: AndroidView(viewType: 'blue_orange_gradient_platform_view'),
29+
);
30+
}
31+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2014 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 'package:flutter_driver/flutter_driver.dart';
6+
import 'package:flutter_driver/src/native_driver.dart';
7+
import 'package:test/test.dart';
8+
9+
import '_flutter_goldens_fork.dart';
10+
11+
// TODO(matanlurey): This is done automatically by 'flutter test' but not by
12+
// 'flutter drive'. If we get closer to shipping the native 'flutter drive'
13+
// command, we should look into if 'flutter_test_config.dart', or a similar
14+
// mechanism, can be used to configure this automatically.
15+
void main() async {
16+
await testExecutable(_main);
17+
}
18+
19+
Future<void> _main() async {
20+
// To generate golden files locally, uncomment the following line.
21+
// autoUpdateGoldenFiles = true;
22+
23+
late FlutterDriver flutterDriver;
24+
late NativeDriver nativeDriver;
25+
26+
setUpAll(() async {
27+
flutterDriver = await FlutterDriver.connect();
28+
nativeDriver = await AndroidNativeDriver.connect();
29+
});
30+
31+
tearDownAll(() async {
32+
await nativeDriver.close();
33+
await flutterDriver.close();
34+
});
35+
36+
test(
37+
'should screenshot and match a blue (top left) -> orange (bottom right) gradient',
38+
() async {
39+
await flutterDriver.waitFor(find.byType('AndroidView'));
40+
await expectLater(
41+
nativeDriver.screenshot(),
42+
matchesGoldenFile('android_driver_test.BlueOrangeGradient.png'),
43+
);
44+
},
45+
timeout: Timeout.none,
46+
);
47+
}

dev/integration_tests/android_driver_test/test_driver/main_test.dart renamed to dev/integration_tests/android_driver_test/test_driver/blue_rectangle_main_test.dart

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:io' as io;
6-
75
import 'package:flutter_driver/flutter_driver.dart';
86
import 'package:flutter_driver/src/native_driver.dart';
97
import 'package:test/test.dart';
@@ -18,34 +16,27 @@ void main() async {
1816
await testExecutable(_main);
1917
}
2018

21-
final bool _isLuciCi = io.Platform.environment['LUCI_CI'] == 'True';
22-
2319
Future<void> _main() async {
2420
// To generate golden files locally, uncomment the following line.
2521
// autoUpdateGoldenFiles = true;
2622

27-
FlutterDriver? flutterDriver;
28-
NativeDriver? nativeDriver;
23+
late FlutterDriver flutterDriver;
24+
late NativeDriver nativeDriver;
2925

3026
setUpAll(() async {
31-
flutterDriver = await FlutterDriver.connect(
32-
// TODO(matanlurey): Workaround log uploading in LUCI not being enabled.
33-
// Default to true on CI because log uploading doesn't work.
34-
// See <https://github.com/flutter/flutter/issues/152775>.
35-
printCommunication: _isLuciCi,
36-
);
27+
flutterDriver = await FlutterDriver.connect();
3728
nativeDriver = await AndroidNativeDriver.connect();
3829
});
3930

4031
tearDownAll(() async {
41-
await nativeDriver?.close();
42-
await flutterDriver?.close();
32+
await nativeDriver.close();
33+
await flutterDriver.close();
4334
});
4435

4536
test('should screenshot and match a full-screen blue rectangle', () async {
46-
await flutterDriver?.waitFor(find.byType('DecoratedBox'));
37+
await flutterDriver.waitFor(find.byType('DecoratedBox'));
4738
await expectLater(
48-
nativeDriver?.screenshot(),
39+
nativeDriver.screenshot(),
4940
matchesGoldenFile('android_driver_test.BlueRectangle.png'),
5041
);
5142
}, timeout: Timeout.none);

packages/flutter_driver/lib/src/native/goldens.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ final class NaiveLocalFileComparator with GoldenFileComparator {
124124
try {
125125
goldenBytes = await goldenFile.readAsBytes();
126126
} on io.PathNotFoundException {
127-
throw TestFailure('Golden file not found: $golden');
127+
throw TestFailure('Golden file not found: ${goldenFile.path}');
128128
}
129129

130130
if (goldenBytes.length != imageBytes.length) {

0 commit comments

Comments
 (0)