[pigeon] Consolidate mock handler tests (#4642)

Eliminates the `mock_handler_tester` sub-package, which existed just for a single unit test file and required duplicate `pigeons/message.dart` output generation with bespoke test wrappers, folding that test into the shared harness and generation system. This also eliminates an entire test suite from the test harness since it's now part of the rest of the Dart unit testing.

This surfaced an edge-case problem with the new package name override, which is that if the override doesn't match the actual dart output location, generated Dart test output won't compile because the import will be wrong. This addresses that by passing the actual package as well to the test generator.
This commit is contained in:
stuartmorgan
2023-08-04 12:40:04 -07:00
committed by GitHub
parent ff81b82176
commit b2a30a372c
17 changed files with 41 additions and 487 deletions

View File

@ -1,3 +1,7 @@
## 10.1.5
* Fixes import in generated Dart test output when overriding package name.
## 10.1.4
* Adds package name to method channel strings to avoid potential collisions between plugins.

View File

@ -599,6 +599,7 @@ if (replyList == null) {
Root root,
StringSink sink, {
required String dartPackageName,
required String dartOutputPackageName,
}) {
final Indent indent = Indent(sink);
final String sourceOutPath = generatorOptions.sourceOutPath ?? '';
@ -620,7 +621,7 @@ if (replyList == null) {
} else {
final String path =
relativeDartPath.replaceFirst(RegExp(r'^.*/lib/'), '');
indent.writeln("import 'package:$dartPackageName/$path';");
indent.writeln("import 'package:$dartOutputPackageName/$path';");
}
for (final Api api in root.apis) {
if (api.location == ApiLocation.host && api.dartHostTestHandler != null) {

View File

@ -13,7 +13,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '10.1.4';
const String pigeonVersion = '10.1.5';
/// Read all the content from [stdin] to a String.
String readStdin() {

View File

@ -510,11 +510,16 @@ class DartTestGeneratorAdapter implements GeneratorAdapter {
basePath: options.basePath ?? '',
);
const DartGenerator testGenerator = DartGenerator();
// The test code needs the actual package name of the Dart output, even if
// the package name has been overridden for other uses.
final String outputPackageName =
deducePackageName(options.dartOut ?? '') ?? options.getPackageName();
testGenerator.generateTest(
dartOptionsWithHeader,
root,
sink,
dartPackageName: options.getPackageName(),
dartOutputPackageName: outputPackageName,
);
}

View File

@ -1,44 +0,0 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

View File

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: e6b697a9df5e9ce933024be3334e86b599c60e71
channel: unknown
project_type: app

View File

@ -1,3 +0,0 @@
# mock_handler_tester
A test bed for testing the code generated by `dartHostTestHandler`.

View File

@ -1,9 +0,0 @@
// Copyright 2013 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/material.dart';
void main() {
runApp(Container());
}

View File

@ -1,20 +0,0 @@
name: mock_handler_tester
description: A testbed for testing dartHostTestHandler.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.18.0 <4.0.0"
flutter: ">=3.3.0"
dependencies:
cupertino_icons: ^1.0.2
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true

View File

@ -1,357 +0,0 @@
// Copyright 2013 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.
//
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
/// This comment is to test enum documentation comments.
///
/// This comment also tests multiple line comments.
///
/// ////////////////////////
/// This comment also tests comments that start with '/'
/// ////////////////////////
enum MessageRequestState {
pending,
success,
failure,
}
/// This comment is to test class documentation comments.
///
/// This comment also tests multiple line comments.
class MessageSearchRequest {
MessageSearchRequest({
this.query,
this.anInt,
this.aBool,
});
/// This comment is to test field documentation comments.
String? query;
/// This comment is to test field documentation comments.
int? anInt;
/// This comment is to test field documentation comments.
bool? aBool;
Object encode() {
return <Object?>[
query,
anInt,
aBool,
];
}
static MessageSearchRequest decode(Object result) {
result as List<Object?>;
return MessageSearchRequest(
query: result[0] as String?,
anInt: result[1] as int?,
aBool: result[2] as bool?,
);
}
}
/// This comment is to test class documentation comments.
class MessageSearchReply {
MessageSearchReply({
this.result,
this.error,
this.state,
});
/// This comment is to test field documentation comments.
///
/// This comment also tests multiple line comments.
String? result;
/// This comment is to test field documentation comments.
String? error;
/// This comment is to test field documentation comments.
MessageRequestState? state;
Object encode() {
return <Object?>[
result,
error,
state?.index,
];
}
static MessageSearchReply decode(Object result) {
result as List<Object?>;
return MessageSearchReply(
result: result[0] as String?,
error: result[1] as String?,
state: result[2] != null
? MessageRequestState.values[result[2]! as int]
: null,
);
}
}
/// This comment is to test class documentation comments.
class MessageNested {
MessageNested({
this.request,
});
/// This comment is to test field documentation comments.
MessageSearchRequest? request;
Object encode() {
return <Object?>[
request?.encode(),
];
}
static MessageNested decode(Object result) {
result as List<Object?>;
return MessageNested(
request: result[0] != null
? MessageSearchRequest.decode(result[0]! as List<Object?>)
: null,
);
}
}
class _MessageApiCodec extends StandardMessageCodec {
const _MessageApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is MessageSearchReply) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else if (value is MessageSearchRequest) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return MessageSearchReply.decode(readValue(buffer)!);
case 129:
return MessageSearchRequest.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
}
}
/// This comment is to test api documentation comments.
///
/// This comment also tests multiple line comments.
class MessageApi {
/// Constructor for [MessageApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
MessageApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = _MessageApiCodec();
/// This comment is to test documentation comments.
///
/// This comment also tests multiple line comments.
Future<void> initialize() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.initialize', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else {
return;
}
}
/// This comment is to test method documentation comments.
Future<MessageSearchReply> search(MessageSearchRequest arg_request) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.search', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_request]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else if (replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (replyList[0] as MessageSearchReply?)!;
}
}
}
class _MessageNestedApiCodec extends StandardMessageCodec {
const _MessageNestedApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is MessageNested) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else if (value is MessageSearchReply) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else if (value is MessageSearchRequest) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return MessageNested.decode(readValue(buffer)!);
case 129:
return MessageSearchReply.decode(readValue(buffer)!);
case 130:
return MessageSearchRequest.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
}
}
/// This comment is to test api documentation comments.
class MessageNestedApi {
/// Constructor for [MessageNestedApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
MessageNestedApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = _MessageNestedApiCodec();
/// This comment is to test method documentation comments.
///
/// This comment also tests multiple line comments.
Future<MessageSearchReply> search(MessageNested arg_nested) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageNestedApi.search', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_nested]) as List<Object?>?;
if (replyList == null) {
throw PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel.',
);
} else if (replyList.length > 1) {
throw PlatformException(
code: replyList[0]! as String,
message: replyList[1] as String?,
details: replyList[2],
);
} else if (replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (replyList[0] as MessageSearchReply?)!;
}
}
}
class _MessageFlutterSearchApiCodec extends StandardMessageCodec {
const _MessageFlutterSearchApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is MessageSearchReply) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else if (value is MessageSearchRequest) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return MessageSearchReply.decode(readValue(buffer)!);
case 129:
return MessageSearchRequest.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
}
}
/// This comment is to test api documentation comments.
abstract class MessageFlutterSearchApi {
static const MessageCodec<Object?> codec = _MessageFlutterSearchApiCodec();
/// This comment is to test method documentation comments.
MessageSearchReply search(MessageSearchRequest request);
static void setup(MessageFlutterSearchApi? api,
{BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageFlutterSearchApi.search',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageFlutterSearchApi.search was null.');
final List<Object?> args = (message as List<Object?>?)!;
final MessageSearchRequest? arg_request =
(args[0] as MessageSearchRequest?);
assert(arg_request != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageFlutterSearchApi.search was null, expected non-null MessageSearchRequest.');
final MessageSearchReply output = api.search(arg_request!);
return output;
});
}
}
}
}

View File

@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file specifically tests the test code generated by dartTestOut.
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_test_plugin_code/src/generated/message.gen.dart';
import 'message.dart';
import 'test.dart';
import 'test_message.gen.dart';
class Mock implements TestHostApi {
List<String> log = <String>[];
@ -74,14 +76,14 @@ void main() {
TestHostApi.setup(mock);
expect(
await const BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.initialize',
'dev.flutter.pigeon.pigeon_integration_tests.MessageApi.initialize',
StandardMessageCodec(),
).send(<Object?>[null]),
isEmpty,
);
try {
await const BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.search',
'dev.flutter.pigeon.pigeon_integration_tests.MessageApi.search',
StandardMessageCodec(),
).send(<Object?>[null]) as List<Object?>?;
expect(true, isFalse); // should not reach here
@ -90,7 +92,7 @@ void main() {
expect(
error.toString(),
contains(
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageApi.search was null, expected non-null MessageSearchRequest.',
'Argument for dev.flutter.pigeon.pigeon_integration_tests.MessageApi.search was null, expected non-null MessageSearchRequest.',
),
);
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Autogenerated from Pigeon (v10.1.4), do not edit directly.
// Autogenerated from Pigeon, do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import
// ignore_for_file: avoid_relative_lib_imports
@ -12,7 +12,7 @@ import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'message.dart';
import 'package:shared_test_plugin_code/src/generated/message.gen.dart';
class _TestHostApiCodec extends StandardMessageCodec {
const _TestHostApiCodec();
@ -61,7 +61,8 @@ abstract class TestHostApi {
static void setup(TestHostApi? api, {BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.initialize', codec,
'dev.flutter.pigeon.pigeon_integration_tests.MessageApi.initialize',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
_testBinaryMessengerBinding!.defaultBinaryMessenger
@ -78,7 +79,8 @@ abstract class TestHostApi {
}
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageApi.search', codec,
'dev.flutter.pigeon.pigeon_integration_tests.MessageApi.search',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
_testBinaryMessengerBinding!.defaultBinaryMessenger
@ -88,12 +90,12 @@ abstract class TestHostApi {
.setMockDecodedMessageHandler<Object?>(channel,
(Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageApi.search was null.');
'Argument for dev.flutter.pigeon.pigeon_integration_tests.MessageApi.search was null.');
final List<Object?> args = (message as List<Object?>?)!;
final MessageSearchRequest? arg_request =
(args[0] as MessageSearchRequest?);
assert(arg_request != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageApi.search was null, expected non-null MessageSearchRequest.');
'Argument for dev.flutter.pigeon.pigeon_integration_tests.MessageApi.search was null, expected non-null MessageSearchRequest.');
final MessageSearchReply output = api.search(arg_request!);
return <Object?>[output];
});
@ -149,7 +151,7 @@ abstract class TestNestedApi {
static void setup(TestNestedApi? api, {BinaryMessenger? binaryMessenger}) {
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.mock_handler_tester.MessageNestedApi.search',
'dev.flutter.pigeon.pigeon_integration_tests.MessageNestedApi.search',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
@ -160,11 +162,11 @@ abstract class TestNestedApi {
.setMockDecodedMessageHandler<Object?>(channel,
(Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageNestedApi.search was null.');
'Argument for dev.flutter.pigeon.pigeon_integration_tests.MessageNestedApi.search was null.');
final List<Object?> args = (message as List<Object?>?)!;
final MessageNested? arg_nested = (args[0] as MessageNested?);
assert(arg_nested != null,
'Argument for dev.flutter.pigeon.mock_handler_tester.MessageNestedApi.search was null, expected non-null MessageNested.');
'Argument for dev.flutter.pigeon.pigeon_integration_tests.MessageNestedApi.search was null, expected non-null MessageNested.');
final MessageSearchReply output = api.search(arg_nested!);
return <Object?>[output];
});

View File

@ -2,7 +2,7 @@ name: pigeon
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
repository: https://github.com/flutter/packages/tree/main/packages/pigeon
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
version: 10.1.4 # This must match the version in lib/generator_tools.dart
version: 10.1.5 # This must match the version in lib/generator_tools.dart
environment:
sdk: ">=2.19.0 <4.0.0"

View File

@ -686,6 +686,7 @@ void main() {
root,
testCodeSink,
dartPackageName: DEFAULT_PACKAGE_NAME,
dartOutputPackageName: DEFAULT_PACKAGE_NAME,
);
final String testCode = testCodeSink.toString();
expect(testCode, contains(r"import 'fo\'o.dart';"));
@ -1351,7 +1352,10 @@ void main() {
expect(code, contains('void doit(int? foo);'));
});
test('uses defined package name', () {
test('uses output package name for imports', () {
const String overriddenPackageName = 'custom_name';
const String outputPackageName = 'some_output_package';
assert(outputPackageName != DEFAULT_PACKAGE_NAME);
final Directory tempDir = Directory.systemTemp.createTempSync('pigeon');
try {
final Directory foo = Directory(path.join(tempDir.path, 'lib', 'foo'));
@ -1371,10 +1375,12 @@ name: foobar
),
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
dartPackageName: overriddenPackageName,
dartOutputPackageName: outputPackageName,
);
final String code = sink.toString();
expect(code, contains("import 'package:test_package/foo/bar.dart';"));
expect(
code, contains("import 'package:$outputPackageName/foo/bar.dart';"));
} finally {
tempDir.deleteSync(recursive: true);
}
@ -1598,6 +1604,7 @@ name: foobar
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
dartOutputPackageName: DEFAULT_PACKAGE_NAME,
);
final String testCode = sink.toString();

View File

@ -160,7 +160,6 @@ Future<List<String>> _modifiedFiles(
Future<void> main(List<String> args) async {
// Run most tests on Linux, since Linux tends to be the easiest and cheapest.
const List<String> linuxHostTests = <String>[
mockHandlerTests,
commandLineTests,
androidJavaUnitTests,
androidJavaLint,

View File

@ -50,7 +50,6 @@ const String windowsUnitTests = 'windows_unittests';
const String windowsIntegrationTests = 'windows_integration_tests';
const String dartUnitTests = 'dart_unittests';
const String flutterUnitTests = 'flutter_unittests';
const String mockHandlerTests = 'mock_handler_tests';
const String commandLineTests = 'command_line_tests';
const Map<String, TestInfo> testSuites = <String, TestInfo>{
@ -101,9 +100,6 @@ const Map<String, TestInfo> testSuites = <String, TestInfo>{
macOSSwiftIntegrationTests: TestInfo(
function: _runMacOSSwiftIntegrationTests,
description: 'Integration tests on generated Swift code on macOS.'),
mockHandlerTests: TestInfo(
function: _runMockHandlerTests,
description: 'Unit tests on generated Dart mock handler code.'),
commandLineTests: TestInfo(
function: _runCommandLineTests,
description: 'Tests running pigeon with various command-line options.'),
@ -310,24 +306,6 @@ Future<int> _runIOSSwiftIntegrationTests() async {
return _runMobileIntegrationTests('iOS', _testPluginRelativePath);
}
Future<int> _runMockHandlerTests() async {
const String unitTestsPath = './mock_handler_tester';
final int generateCode = await runPigeon(
input: './pigeons/message.dart',
dartOut: './mock_handler_tester/test/message.dart',
dartTestOut: './mock_handler_tester/test/test.dart',
);
if (generateCode != 0) {
return generateCode;
}
final int testCode = await runFlutterCommand(unitTestsPath, 'test');
if (testCode != 0) {
return testCode;
}
return 0;
}
Future<int> _runWindowsUnitTests() async {
const String examplePath = './$_testPluginRelativePath/example';
final int compileCode = await runFlutterBuild(examplePath, 'windows');

View File

@ -57,7 +57,6 @@ ${parser.usage}''');
const List<String> dartTests = <String>[
dartUnitTests,
flutterUnitTests,
mockHandlerTests,
commandLineTests,
];
const List<String> androidTests = <String>[