diff --git a/.ci.yaml b/.ci.yaml index 5ec84a638f..712d3228f9 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -1628,6 +1628,14 @@ targets: ["devicelab"] scheduler: luci + - name: Mac_android integration_ui_frame_number + builder: Mac_android integration_ui_frame_number + presubmit: false + properties: + tags: > + ["devicelab"] + scheduler: luci + - name: Mac_android integration_ui_screenshot builder: Mac_android integration_ui_screenshot presubmit: false @@ -1912,6 +1920,14 @@ targets: ["devicelab"] scheduler: luci + - name: Mac_ios integration_ui_ios_frame_number + builder: Mac_ios integration_ui_ios_frame_number + presubmit: false + properties: + tags: > + ["devicelab"] + scheduler: luci + - name: Mac_ios integration_ui_ios_screenshot builder: Mac_ios integration_ui_ios_screenshot presubmit: false diff --git a/TESTOWNERS b/TESTOWNERS index 2a76234f95..11bdf64c06 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -93,6 +93,7 @@ /dev/devicelab/bin/tasks/imagefiltered_transform_animation_perf__timeline_summary.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/integration_test_test.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/integration_ui_driver.dart @zanderso @flutter/tool +/dev/devicelab/bin/tasks/integration_ui_frame_number.dart @iskakaushik @flutter/engine /dev/devicelab/bin/tasks/integration_ui_keyboard_resize.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/integration_ui_screenshot.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/integration_ui_textfield.dart @zanderso @flutter/tool @@ -126,6 +127,7 @@ /dev/devicelab/bin/tasks/hot_mode_dev_cycle_macos_target__benchmark.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/integration_test_test_ios.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/integration_ui_ios_driver.dart @zanderso @flutter/tool +/dev/devicelab/bin/tasks/integration_ui_ios_frame_number.dart @iskakaushik @flutter/engine /dev/devicelab/bin/tasks/integration_ui_ios_keyboard_resize.dart @zanderso @flutter/engine /dev/devicelab/bin/tasks/integration_ui_ios_screenshot.dart @zanderso @flutter/tool /dev/devicelab/bin/tasks/integration_ui_ios_textfield.dart @zanderso @flutter/tool diff --git a/dev/devicelab/bin/tasks/integration_ui_frame_number.dart b/dev/devicelab/bin/tasks/integration_ui_frame_number.dart new file mode 100644 index 0000000000..9cbf7306da --- /dev/null +++ b/dev/devicelab/bin/tasks/integration_ui_frame_number.dart @@ -0,0 +1,12 @@ +// 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_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/integration_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.android; + await task(createEndToEndFrameNumberTest()); +} diff --git a/dev/devicelab/bin/tasks/integration_ui_ios_frame_number.dart b/dev/devicelab/bin/tasks/integration_ui_ios_frame_number.dart new file mode 100644 index 0000000000..75c075d719 --- /dev/null +++ b/dev/devicelab/bin/tasks/integration_ui_ios_frame_number.dart @@ -0,0 +1,12 @@ +// 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_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/integration_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.ios; + await task(createEndToEndFrameNumberTest()); +} diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart index 7a8f783607..3718307d24 100644 --- a/dev/devicelab/lib/tasks/integration_tests.dart +++ b/dev/devicelab/lib/tasks/integration_tests.dart @@ -92,6 +92,13 @@ TaskFunction createEndToEndKeyboardTest() { ); } +TaskFunction createEndToEndFrameNumberTest() { + return DriverTest( + '${flutterDirectory.path}/dev/integration_tests/ui', + 'lib/frame_number.dart', + ); +} + TaskFunction createEndToEndDriverTest() { return DriverTest( '${flutterDirectory.path}/dev/integration_tests/ui', diff --git a/dev/integration_tests/ui/lib/frame_number.dart b/dev/integration_tests/ui/lib/frame_number.dart new file mode 100644 index 0000000000..764ec9d288 --- /dev/null +++ b/dev/integration_tests/ui/lib/frame_number.dart @@ -0,0 +1,63 @@ +// 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:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_driver/driver_extension.dart'; + +/// This application shows empty screen until first frame timings are acquired. +void main() { + enableFlutterDriverExtension(); + + final Completer> completer = Completer>(); + SchedulerBinding.instance!.addTimingsCallback((List timings) { + completer.complete(timings); + }); + + runApp(Directionality( + textDirection: TextDirection.ltr, + child: _FirstFrameTimings(completer: completer), + )); +} + +class _FirstFrameTimings extends StatefulWidget { + const _FirstFrameTimings({ + Key? key, + required this.completer, + }) : super(key: key); + + final Completer> completer; + + @override + _FirstFrameTimingsState createState() => _FirstFrameTimingsState(); +} + +class _FirstFrameTimingsState extends State<_FirstFrameTimings> { + int? _minFrameNumber; + + @override + Widget build(BuildContext context) { + widget.completer.future.then(_setMinFrameNumber); + if (_minFrameNumber != null) { + return Text( + _minFrameNumber.toString(), + key: const Key('minFrameNumber'), + ); + } else { + return const Text('Waiting...'); + } + } + + void _setMinFrameNumber(List timings) { + final int minFrameNumber = timings + .map((FrameTiming timing) => timing.frameNumber) + .reduce(min); + setState(() { + _minFrameNumber = minFrameNumber; + }); + } +} diff --git a/dev/integration_tests/ui/test_driver/frame_number_test.dart b/dev/integration_tests/ui/test_driver/frame_number_test.dart new file mode 100644 index 0000000000..f5825ea2cf --- /dev/null +++ b/dev/integration_tests/ui/test_driver/frame_number_test.dart @@ -0,0 +1,38 @@ +// 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:test/test.dart' hide TypeMatcher, isInstanceOf; + +bool _isNumeric(String s) { + return double.tryParse(s) != null; +} + +// Connect and disconnect from the empty app. +void main() { + group('FrameNumber', () { + late FlutterDriver driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + await driver.close(); + }); + + test('minFrameNumber is numeric', () async { + final SerializableFinder minFrameNumberFinder = + find.byValueKey('minFrameNumber'); + await driver.waitFor( + minFrameNumberFinder, + timeout: const Duration(seconds: 5), + ); + final String minFrameNumber = await driver.getText(minFrameNumberFinder); + // TODO(iskakaushik): enable the stronger check of _minFrameNumber == '1', + // once this is fixed. https://github.com/flutter/flutter/issues/86487 + expect(_isNumeric(minFrameNumber), true); + }, timeout: Timeout.none); + }); +} diff --git a/dev/manual_tests/windows/flutter/generated_plugin_registrant.cc b/dev/manual_tests/windows/flutter/generated_plugin_registrant.cc index 4bfa0f3a3a..8b6d4680af 100644 --- a/dev/manual_tests/windows/flutter/generated_plugin_registrant.cc +++ b/dev/manual_tests/windows/flutter/generated_plugin_registrant.cc @@ -2,6 +2,8 @@ // Generated file. Do not edit. // +// clang-format off + #include "generated_plugin_registrant.h" diff --git a/packages/integration_test/test/binding_test.dart b/packages/integration_test/test/binding_test.dart index 83d4813743..cc3cc29a5d 100644 --- a/packages/integration_test/test/binding_test.dart +++ b/packages/integration_test/test/binding_test.dart @@ -72,6 +72,7 @@ Future main() async { testWidgets('Test traceAction', (WidgetTester tester) async { await integrationBinding.enableTimeline(vmService: fakeVM); await integrationBinding.traceAction(() async {}); + print(integrationBinding.reportData); expect(integrationBinding.reportData, isNotNull); expect(integrationBinding.reportData!.containsKey('timeline'), true); expect(