Skip to content

Commit

Permalink
Add a test of an Android platform view that draws a gradient (flutter…
Browse files Browse the repository at this point in the history
…#153878)

Closes flutter#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
  • Loading branch information
matanlurey authored and Buchimi committed Sep 2, 2024
1 parent fa8f29e commit 1b4df3b
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 17 deletions.
21 changes: 21 additions & 0 deletions dev/bots/suite_runners/run_flutter_driver_android_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -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'),
);
}
}
Original file line number Diff line number Diff line change
@@ -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,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_driver/lib/src/native/goldens.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 1b4df3b

Please sign in to comment.