Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update test reports to show all exceptions thrown in flutter #2362

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dev/e2e_app/integration_test/overflow_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Uncomment to test `Multiple exceptions were thrown` issue
// import 'package:flutter/widgets.dart';

import 'common.dart';

void main() {
patrol('opens the overflow screen', ($) async {
await createApp($);

await $('Open overflow screen').scrollTo().tap();

// Uncomment to test `Multiple exceptions were thrown` issue
// return $(ValueKey('key')).scrollTo().tap();
});
}
9 changes: 9 additions & 0 deletions dev/e2e_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:e2e_app/applink_screen.dart';
import 'package:e2e_app/loading_screen.dart';
import 'package:e2e_app/location_screen.dart';
import 'package:e2e_app/notifications_screen.dart';
import 'package:e2e_app/overflow_screen.dart';
import 'package:e2e_app/overlay_screen.dart';
import 'package:e2e_app/permissions_screen.dart';
import 'package:e2e_app/scrolling_screen.dart';
Expand Down Expand Up @@ -264,6 +265,14 @@ class _ExampleHomePageState extends State<ExampleHomePage> {
),
child: const Text('Open permissions screen'),
),
TextButton(
onPressed: () async => Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const OverflowScreen(),
),
),
child: const Text('Open overflow screen'),
),
Text('EXAMPLE_KEY: ${const String.fromEnvironment('EXAMPLE_KEY')}'),
],
),
Expand Down
47 changes: 47 additions & 0 deletions dev/e2e_app/lib/overflow_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
// Uncomment to test `Multiple exceptions were thrown` issue
// import 'package:flutter/services.dart';

class OverflowScreen extends StatelessWidget {
const OverflowScreen({super.key});

@override
Widget build(BuildContext context) {
// Uncomment to test `Multiple exceptions were thrown` issue
// Future<void> throwStackOverflowException() async {
// await Future<void>.delayed(const Duration(milliseconds: 100));
// throw StackOverflowError();
// }
//
// Future<void> throwPlatformException() async {
// await Future<void>.delayed(const Duration(milliseconds: 110));
// throw PlatformException(code: 'code');
// }
//
// Future<void> throwFormatException() async {
// await Future<void>.delayed(const Duration(milliseconds: 120));
// throw FormatException();
// }
//
// throwStackOverflowException();
// throwPlatformException();
// throwFormatException();

return Scaffold(
appBar: AppBar(
title: const Text('Overflow'),
),
body: Row(
children: const <Widget>[
// Uncomment to test `Multiple exceptions were thrown` issue
// SizedBox(
// width: 500,
// child: Text('This container is too wide for the row'),
// ),
Icon(Icons.star, size: 50),
Icon(Icons.abc, size: 50),
],
),
);
}
}
4 changes: 4 additions & 0 deletions packages/patrol/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.12.0

- Make `PatrolBinding`'s `testResults` public. (#2362)

## 3.11.1

- Replace whitespace in test case name in `PatrolJUnitRunner.java`. (#2361)
Expand Down
33 changes: 5 additions & 28 deletions packages/patrol/lib/src/binding.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io' as io;
import 'dart:isolate';

import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -44,28 +43,6 @@ class PatrolBinding extends LiveTestWidgetsFlutterBinding {
/// You most likely don't want to call it yourself.
PatrolBinding(NativeAutomatorConfig config)
: _serviceExtensions = DevtoolsServiceExtensions(config) {
final oldTestExceptionReporter = reportTestException;

/// Wraps the default test exception reporter to report the test results to
/// the native side of Patrol.
reportTestException = (details, testDescription) {
final currentDartTest = _currentDartTest;
if (currentDartTest != null) {
assert(!constants.hotRestartEnabled);
// On iOS in release mode, diagnostics are compacted or truncated.
// We use the exceptionAsString() and stack to get the information
// about the exception. See [DiagnosticLevel].
final detailsAsString = (kReleaseMode && io.Platform.isIOS)
? '${details.exceptionAsString()} \n ${details.stack}'
: details.toString();
_testResults[currentDartTest] = Failure(
testDescription,
detailsAsString,
);
}
oldTestExceptionReporter(details, testDescription);
};

setUp(() {
if (constants.hotRestartEnabled) {
return;
Expand All @@ -89,7 +66,7 @@ class PatrolBinding extends LiveTestWidgetsFlutterBinding {
return;
} else {
logger(
'tearDown(): count: ${_testResults.length}, results: $_testResults',
'tearDown(): count: ${testResults.length}, results: $testResults',
);
}

Expand Down Expand Up @@ -127,8 +104,8 @@ class PatrolBinding extends LiveTestWidgetsFlutterBinding {
await patrolAppService.markDartTestAsCompleted(
dartFileName: _currentDartTest!,
passed: passed,
details: _testResults[_currentDartTest!] is Failure
? (_testResults[_currentDartTest!] as Failure?)?.details
details: testResults[_currentDartTest!] is Failure
? (testResults[_currentDartTest!] as Failure?)?.details
: null,
);
} else {
Expand Down Expand Up @@ -178,7 +155,7 @@ class PatrolBinding extends LiveTestWidgetsFlutterBinding {

/// Keys are the test descriptions, and values are either [_success] or a
/// [Failure].
final Map<String, Object> _testResults = <String, Object>{};
final Map<String, Object> testResults = <String, Object>{};

final DevtoolsServiceExtensions _serviceExtensions;

Expand Down Expand Up @@ -235,7 +212,7 @@ class PatrolBinding extends LiveTestWidgetsFlutterBinding {
invariantTester,
description: description,
);
_testResults[description] ??= _success;
testResults[description] ??= _success;
}

@override
Expand Down
26 changes: 26 additions & 0 deletions packages/patrol/lib/src/native/patrol_app_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:patrol/src/binding.dart';
import 'package:patrol/src/common.dart';
import 'package:patrol/src/native/contracts/contracts.dart';
import 'package:patrol/src/native/contracts/patrol_app_service_server.dart';
import 'package:patrol/src/native/native.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;

Expand Down Expand Up @@ -150,7 +153,30 @@ class PatrolAppService extends PatrolAppServiceServer {
print('PatrolAppService.runDartTest(${request.name}) called');
_testExecutionRequested.complete(request.name);

final patrolBinding =
PatrolBinding.ensureInitialized(const NativeAutomatorConfig());

final previousOnError = FlutterError.onError;
FlutterError.onError = (details) {
final previousDetails = switch (patrolBinding.testResults[request.name]) {
Failure(:final details?) => FlutterErrorDetails(exception: details),
_ => null,
};
final detailsAsString = (kReleaseMode && Platform.isIOS)
? '${details.exceptionAsString()} \n ${details.stack}'
: details.toString();

patrolBinding.testResults[request.name] = Failure(
request.name,
'$detailsAsString\n$previousDetails',
);

previousOnError?.call(details);
};

final testExecutionResult = await testExecutionCompleted;
FlutterError.onError = previousOnError;

return RunDartTestResponse(
result: testExecutionResult.passed
? RunDartTestResponseResult.success
Expand Down
2 changes: 1 addition & 1 deletion packages/patrol/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: patrol
description: >
Powerful Flutter-native UI testing framework overcoming limitations of
existing Flutter testing tools. Ready for action!
version: 3.11.1
version: 3.12.0
homepage: https://patrol.leancode.co
repository: https://github.com/leancodepl/patrol/tree/master/packages/patrol
issue_tracker: https://github.com/leancodepl/patrol/issues
Expand Down
Loading