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',
|
||||
<String>[
|
||||
'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.
|
||||
// Disable them to work around flakiness issues, and in general just
|
||||
// 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.WindowInsetsControllerCompat
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
|
||||
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?) {
|
||||
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
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:flutter_driver/src/native_driver.dart';
|
||||
import 'package:test/test.dart';
|
||||
@ -18,34 +16,27 @@ void main() async {
|
||||
await testExecutable(_main);
|
||||
}
|
||||
|
||||
final bool _isLuciCi = io.Platform.environment['LUCI_CI'] == 'True';
|
||||
|
||||
Future<void> _main() async {
|
||||
// To generate golden files locally, uncomment the following line.
|
||||
// autoUpdateGoldenFiles = true;
|
||||
|
||||
FlutterDriver? flutterDriver;
|
||||
NativeDriver? nativeDriver;
|
||||
late FlutterDriver flutterDriver;
|
||||
late NativeDriver nativeDriver;
|
||||
|
||||
setUpAll(() async {
|
||||
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,
|
||||
);
|
||||
flutterDriver = await FlutterDriver.connect();
|
||||
nativeDriver = await AndroidNativeDriver.connect();
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
await nativeDriver?.close();
|
||||
await flutterDriver?.close();
|
||||
await nativeDriver.close();
|
||||
await flutterDriver.close();
|
||||
});
|
||||
|
||||
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(
|
||||
nativeDriver?.screenshot(),
|
||||
nativeDriver.screenshot(),
|
||||
matchesGoldenFile('android_driver_test.BlueRectangle.png'),
|
||||
);
|
||||
}, timeout: Timeout.none);
|
@ -124,7 +124,7 @@ final class NaiveLocalFileComparator with GoldenFileComparator {
|
||||
try {
|
||||
goldenBytes = await goldenFile.readAsBytes();
|
||||
} on io.PathNotFoundException {
|
||||
throw TestFailure('Golden file not found: $golden');
|
||||
throw TestFailure('Golden file not found: ${goldenFile.path}');
|
||||
}
|
||||
|
||||
if (goldenBytes.length != imageBytes.length) {
|
||||
|
Loading…
Reference in New Issue
Block a user