Skip to content

[integration_test] allow custom binding initialization when running integration_test tests #113097

@bartekpacia

Description

@bartekpacia

This is a revival of, and copy-paste from, #92254. We need the feature mentioned in there in our Patrol testing framework.

Use case

I'd like to be in control of the test binding I choose for integration tests. In my case, I have a custom binding to set the TestBindingEventSource of all touch events as test, not device.

That's how my custom binding looks like.

The cookbook contains documentation about adding the ensureInitialized call, but (at least on the latest beta version) flutter run and flutter test generate an intermediate file that calls:

IntegrationTestWidgetsFlutterBinding.ensureInitialized();

That intermediate file is called listener.dart autogenerated in a path like /var/folders/z8/vl6s_16515s6h_j8z7bs1tmm0000gn/T/flutter_tools.LfIUH0/flutter_test_listener.SK8Sr1/listener.dart. To find its location, run flutter test integration_test -v (without -v the path to the autogenerated file is not shown).

Here's its code:

listener.dart

Here's the method which generates this file.

// @dart=2.17
import 'dart:async';
import 'dart:convert';  // flutter_ignore: dart_convert_import
import 'dart:io';  // flutter_ignore: dart_io_import
import 'dart:isolate';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'dart:developer' as developer;
import 'package:test_api/src/remote_listener.dart';
import 'package:stream_channel/stream_channel.dart';
import 'package:stack_trace/stack_trace.dart';

import 'file:///Users/bartek/dev/leancode/patrol/packages/patrol/example/integration_test/example_test.dart' as test;

/// Returns a serialized test suite.
StreamChannel<dynamic> serializeSuite(Function getMain()) {
  return RemoteListener.start(getMain);
}

Future<void> _testMain() async {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  await Future(test.main);
}

/// Capture any top-level errors (mostly lazy syntax errors, since other are
/// caught below) and report them to the parent isolate.
void catchIsolateErrors() {
  final ReceivePort errorPort = ReceivePort();
  // Treat errors non-fatal because otherwise they'll be double-printed.
  Isolate.current.setErrorsFatal(false);
  Isolate.current.addErrorListener(errorPort.sendPort);
  errorPort.listen((dynamic message) {
    // Masquerade as an IsolateSpawnException because that's what this would
    // be if the error had been detected statically.
    final IsolateSpawnException error = IsolateSpawnException(
        message[0] as String);
    final Trace stackTrace = message[1] == null ?
        Trace(const <Frame>[]) : Trace.parse(message[1] as String);
    Zone.current.handleUncaughtError(error, stackTrace);
  });
}

void main() {
  String serverPort = Platform.environment['SERVER_PORT'] ?? '';
  String server = Uri.decodeComponent('ws%3A%2F%2F127.0.0.1:$serverPort');
  StreamChannel<dynamic> testChannel = serializeSuite(() {
    catchIsolateErrors();
    goldenFileComparator = LocalFileComparator(Uri.parse('file:///Users/bartek/dev/leancode/patrol/packages/patrol/example/integration_test/example_test.dart'));
    autoUpdateGoldenFiles = false;
    return _testMain;
  });
  final callback = (method, params) async {
    testChannel.sink.add(json.decode(params['data'] as String));

    // Result is ignored but null is not accepted here.
    return developer.ServiceExtensionResponse.result('{}');
  };

  developer.registerExtension('ext.flutter.integrationTest', callback);

  testChannel.stream.listen((x) {
    developer.postEvent(
      'Flutter.IntegrationTest',
      {'data': json.encode(x)},
    );
  });
} 

Proposal

Remove ensureInitialized from the generated file so users can choose what binding (and what configuration) they want the test to use (or at least allow to initialize only subclasses of IntegrationTestWidgetsFlutterBinding).

Also I think that generating this file is not communicated clearly enough (or at all) by the integration testing docs, which are focused only on very simple cases.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listf: integration_testThe flutter/packages/integration_test pluginframeworkflutter/packages/flutter repository. See also f: labels.team-frameworkOwned by Framework teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions