mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add a test of an Android platform view that draws a gradient (#153878)
Closes https://github.com/flutter/flutter/issues/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
This commit is contained in:
parent
327193af9a
commit
5e194383af
@ -44,6 +44,27 @@ Future<void> runFlutterDriverAndroidTests() async {
|
|||||||
'flutter',
|
'flutter',
|
||||||
<String>[
|
<String>[
|
||||||
'drive',
|
'drive',
|
||||||
|
'lib/blue_rectangle_main.dart',
|
||||||
|
// There are no reason to enable development flags for this test.
|
||||||
|
// Disable them to work around flakiness issues, and in general just
|
||||||
|
// make less things start up unnecessarily.
|
||||||
|
'--no-dds',
|
||||||
|
'--no-enable-dart-profiling',
|
||||||
|
'--test-arguments=test',
|
||||||
|
'--test-arguments=--reporter=expanded',
|
||||||
|
],
|
||||||
|
workingDirectory: path.join(
|
||||||
|
'dev',
|
||||||
|
'integration_tests',
|
||||||
|
'android_driver_test',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await runCommand(
|
||||||
|
'flutter',
|
||||||
|
<String>[
|
||||||
|
'drive',
|
||||||
|
'lib/blue_orange_gradient_platform_view_main.dart',
|
||||||
// There are no reason to enable development flags for this test.
|
// There are no reason to enable development flags for this test.
|
||||||
// Disable them to work around flakiness issues, and in general just
|
// Disable them to work around flakiness issues, and in general just
|
||||||
// make less things start up unnecessarily.
|
// make less things start up unnecessarily.
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
@file:Suppress("PackageName")
|
||||||
|
|
||||||
|
package com.example.android_driver_test
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.LinearGradient
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Shader
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import io.flutter.plugin.platform.PlatformView
|
||||||
|
import io.flutter.plugin.platform.PlatformViewFactory
|
||||||
|
|
||||||
|
class BlueOrangeGradientPlatformViewFactory : PlatformViewFactory(null) {
|
||||||
|
override fun create(
|
||||||
|
context: Context,
|
||||||
|
viewId: Int,
|
||||||
|
args: Any?
|
||||||
|
): PlatformView = GradientPlatformView(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GradientPlatformView(
|
||||||
|
context: Context
|
||||||
|
) : View(context),
|
||||||
|
PlatformView {
|
||||||
|
val paint = Paint()
|
||||||
|
|
||||||
|
init {
|
||||||
|
layoutParams =
|
||||||
|
ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getView(): View = this
|
||||||
|
|
||||||
|
override fun dispose() {}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
|
||||||
|
super.onDraw(canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSizeChanged(
|
||||||
|
w: Int,
|
||||||
|
h: Int,
|
||||||
|
oldw: Int,
|
||||||
|
oldh: Int
|
||||||
|
) {
|
||||||
|
paint.shader =
|
||||||
|
LinearGradient(
|
||||||
|
0f,
|
||||||
|
0f,
|
||||||
|
w.toFloat(),
|
||||||
|
h.toFloat(),
|
||||||
|
intArrayOf(
|
||||||
|
Color.rgb(0x41, 0x69, 0xE1),
|
||||||
|
Color.rgb(0xFF, 0xA5, 0x00)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
Shader.TileMode.CLAMP
|
||||||
|
)
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh)
|
||||||
|
}
|
||||||
|
}
|
@ -11,8 +11,18 @@ import androidx.core.view.WindowCompat
|
|||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
|
||||||
class MainActivity : FlutterActivity() {
|
class MainActivity : FlutterActivity() {
|
||||||
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
|
// Intentionally do not use GeneratedPluginRegistrant.
|
||||||
|
|
||||||
|
flutterEngine
|
||||||
|
.platformViewsController
|
||||||
|
.registry
|
||||||
|
.registerViewFactory("blue_orange_gradient_platform_view", BlueOrangeGradientPlatformViewFactory())
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
// 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:io' as io;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_driver/driver_extension.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
enableFlutterDriverExtension();
|
||||||
|
|
||||||
|
if (kIsWeb || !io.Platform.isAndroid) {
|
||||||
|
throw UnsupportedError('This app should only run on Android devices.');
|
||||||
|
}
|
||||||
|
|
||||||
|
runApp(const MainApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
final class MainApp extends StatelessWidget {
|
||||||
|
const MainApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: AndroidView(viewType: 'blue_orange_gradient_platform_view'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
// 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 'package:flutter_driver/flutter_driver.dart';
|
||||||
|
import 'package:flutter_driver/src/native_driver.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '_flutter_goldens_fork.dart';
|
||||||
|
|
||||||
|
// TODO(matanlurey): This is done automatically by 'flutter test' but not by
|
||||||
|
// 'flutter drive'. If we get closer to shipping the native 'flutter drive'
|
||||||
|
// command, we should look into if 'flutter_test_config.dart', or a similar
|
||||||
|
// mechanism, can be used to configure this automatically.
|
||||||
|
void main() async {
|
||||||
|
await testExecutable(_main);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _main() async {
|
||||||
|
// To generate golden files locally, uncomment the following line.
|
||||||
|
// autoUpdateGoldenFiles = true;
|
||||||
|
|
||||||
|
late FlutterDriver flutterDriver;
|
||||||
|
late NativeDriver nativeDriver;
|
||||||
|
|
||||||
|
setUpAll(() async {
|
||||||
|
flutterDriver = await FlutterDriver.connect();
|
||||||
|
nativeDriver = await AndroidNativeDriver.connect();
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDownAll(() async {
|
||||||
|
await nativeDriver.close();
|
||||||
|
await flutterDriver.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should screenshot and match a blue (top left) -> orange (bottom right) gradient',
|
||||||
|
() async {
|
||||||
|
await flutterDriver.waitFor(find.byType('AndroidView'));
|
||||||
|
await expectLater(
|
||||||
|
nativeDriver.screenshot(),
|
||||||
|
matchesGoldenFile('android_driver_test.BlueOrangeGradient.png'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
timeout: Timeout.none,
|
||||||
|
);
|
||||||
|
}
|
@ -2,8 +2,6 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:io' as io;
|
|
||||||
|
|
||||||
import 'package:flutter_driver/flutter_driver.dart';
|
import 'package:flutter_driver/flutter_driver.dart';
|
||||||
import 'package:flutter_driver/src/native_driver.dart';
|
import 'package:flutter_driver/src/native_driver.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
@ -18,34 +16,27 @@ void main() async {
|
|||||||
await testExecutable(_main);
|
await testExecutable(_main);
|
||||||
}
|
}
|
||||||
|
|
||||||
final bool _isLuciCi = io.Platform.environment['LUCI_CI'] == 'True';
|
|
||||||
|
|
||||||
Future<void> _main() async {
|
Future<void> _main() async {
|
||||||
// To generate golden files locally, uncomment the following line.
|
// To generate golden files locally, uncomment the following line.
|
||||||
// autoUpdateGoldenFiles = true;
|
// autoUpdateGoldenFiles = true;
|
||||||
|
|
||||||
FlutterDriver? flutterDriver;
|
late FlutterDriver flutterDriver;
|
||||||
NativeDriver? nativeDriver;
|
late NativeDriver nativeDriver;
|
||||||
|
|
||||||
setUpAll(() async {
|
setUpAll(() async {
|
||||||
flutterDriver = await FlutterDriver.connect(
|
flutterDriver = await FlutterDriver.connect();
|
||||||
// TODO(matanlurey): Workaround log uploading in LUCI not being enabled.
|
|
||||||
// Default to true on CI because log uploading doesn't work.
|
|
||||||
// See <https://github.com/flutter/flutter/issues/152775>.
|
|
||||||
printCommunication: _isLuciCi,
|
|
||||||
);
|
|
||||||
nativeDriver = await AndroidNativeDriver.connect();
|
nativeDriver = await AndroidNativeDriver.connect();
|
||||||
});
|
});
|
||||||
|
|
||||||
tearDownAll(() async {
|
tearDownAll(() async {
|
||||||
await nativeDriver?.close();
|
await nativeDriver.close();
|
||||||
await flutterDriver?.close();
|
await flutterDriver.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should screenshot and match a full-screen blue rectangle', () async {
|
test('should screenshot and match a full-screen blue rectangle', () async {
|
||||||
await flutterDriver?.waitFor(find.byType('DecoratedBox'));
|
await flutterDriver.waitFor(find.byType('DecoratedBox'));
|
||||||
await expectLater(
|
await expectLater(
|
||||||
nativeDriver?.screenshot(),
|
nativeDriver.screenshot(),
|
||||||
matchesGoldenFile('android_driver_test.BlueRectangle.png'),
|
matchesGoldenFile('android_driver_test.BlueRectangle.png'),
|
||||||
);
|
);
|
||||||
}, timeout: Timeout.none);
|
}, timeout: Timeout.none);
|
@ -124,7 +124,7 @@ final class NaiveLocalFileComparator with GoldenFileComparator {
|
|||||||
try {
|
try {
|
||||||
goldenBytes = await goldenFile.readAsBytes();
|
goldenBytes = await goldenFile.readAsBytes();
|
||||||
} on io.PathNotFoundException {
|
} on io.PathNotFoundException {
|
||||||
throw TestFailure('Golden file not found: $golden');
|
throw TestFailure('Golden file not found: ${goldenFile.path}');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (goldenBytes.length != imageBytes.length) {
|
if (goldenBytes.length != imageBytes.length) {
|
||||||
|
Loading…
Reference in New Issue
Block a user