[pigeon] kotlin equality methods (#8887)
Adds `equals` and `hash` methods to Kotlin classes.
More classes may be added to this pr if reviews are late enough.
work towards https://github.com/flutter/flutter/issues/118087
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index c0b1841..da10f9f 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 25.2.0
+
+* [kotlin] Adds equality methods to generated data classes.
+
## 25.1.0
* [dart] Adds equality methods to generated data classes.
diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt
index 0c0512d..519c9e2 100644
--- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt
+++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt
@@ -31,6 +31,18 @@
data,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is IntEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return data == other.data
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -47,6 +59,18 @@
data,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is StringEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return data == other.data
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
private open class EventChannelMessagesPigeonCodec : StandardMessageCodec() {
diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
index c6b3271..279f731 100644
--- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
+++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt
@@ -46,6 +46,29 @@
val details: Any? = null
) : Throwable()
+private fun deepEqualsMessages(a: Any?, b: Any?): Boolean {
+ if (a is ByteArray && b is ByteArray) {
+ return a.contentEquals(b)
+ }
+ if (a is IntArray && b is IntArray) {
+ return a.contentEquals(b)
+ }
+ if (a is LongArray && b is LongArray) {
+ return a.contentEquals(b)
+ }
+ if (a is DoubleArray && b is DoubleArray) {
+ return a.contentEquals(b)
+ }
+ if (a is Array<*> && b is Array<*>) {
+ return a.size == b.size && a.indices.all { deepEqualsMessages(a[it], b[it]) }
+ }
+ if (a is Map<*, *> && b is Map<*, *>) {
+ return a.size == b.size &&
+ a.keys.all { (b as Map<Any?, Any?>).containsKey(it) && deepEqualsMessages(a[it], b[it]) }
+ }
+ return a == b
+}
+
enum class Code(val raw: Int) {
ONE(0),
TWO(1);
@@ -82,6 +105,21 @@
data,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is MessageData) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return name == other.name &&
+ description == other.description &&
+ code == other.code &&
+ deepEqualsMessages(data, other.data)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
private open class MessagesPigeonCodec : StandardMessageCodec() {
diff --git a/packages/pigeon/lib/src/cpp/cpp_generator.dart b/packages/pigeon/lib/src/cpp/cpp_generator.dart
index 3db417a..cad25aa 100644
--- a/packages/pigeon/lib/src/cpp/cpp_generator.dart
+++ b/packages/pigeon/lib/src/cpp/cpp_generator.dart
@@ -98,7 +98,7 @@
/// Options that control how C++ code will be generated.
///
/// For internal use only.
-class InternalCppOptions {
+class InternalCppOptions extends PigeonInternalOptions {
/// Creates a [InternalCppOptions] object.
const InternalCppOptions({
required this.headerIncludePath,
diff --git a/packages/pigeon/lib/src/dart/dart_generator.dart b/packages/pigeon/lib/src/dart/dart_generator.dart
index 1ea9b65..b8897e4 100644
--- a/packages/pigeon/lib/src/dart/dart_generator.dart
+++ b/packages/pigeon/lib/src/dart/dart_generator.dart
@@ -87,7 +87,7 @@
}
/// Options that control how Dart code will be generated.
-class InternalDartOptions {
+class InternalDartOptions extends PigeonInternalOptions {
/// Constructor for InternalDartOptions.
const InternalDartOptions({
this.copyrightHeader,
@@ -354,12 +354,7 @@
indent.writeScoped('return ', '', () {
indent.format(
classDefinition.fields
- .map((NamedType field) => field.type.baseName == 'List' ||
- field.type.baseName == 'Float64List' ||
- field.type.baseName == 'Int32List' ||
- field.type.baseName == 'Int64List' ||
- field.type.baseName == 'Uint8List' ||
- field.type.baseName == 'Map'
+ .map((NamedType field) => isCollectionType(field.type)
? '_deepEquals(${field.name}, other.${field.name})'
: '${field.name} == other.${field.name}')
.join('\n&& '),
@@ -1089,10 +1084,8 @@
_writeWrapResponse(generatorOptions, root, indent);
}
if (root.classes.isNotEmpty &&
- root.classes.any((Class dataClass) => dataClass.fields.any(
- (NamedType field) =>
- field.type.baseName.startsWith('List') ||
- field.type.baseName.startsWith('Map')))) {
+ root.classes.any((Class dataClass) => dataClass.fields
+ .any((NamedType field) => isCollectionType(field.type)))) {
_writeDeepEquals(indent);
}
}
diff --git a/packages/pigeon/lib/src/generator.dart b/packages/pigeon/lib/src/generator.dart
index 66961c4..fc0b8a1 100644
--- a/packages/pigeon/lib/src/generator.dart
+++ b/packages/pigeon/lib/src/generator.dart
@@ -6,18 +6,21 @@
import 'generator_tools.dart';
/// The internal options used by the generator.
-abstract class InternalOptions {}
+abstract class PigeonInternalOptions {
+ /// Constructor.
+ const PigeonInternalOptions();
+}
/// An abstract base class of generators.
///
/// This provides the structure that is common across generators for different languages.
-abstract class Generator<InternalOptions> {
+abstract class Generator<PigeonInternalOptions> {
/// Constructor.
const Generator();
/// Generates files for specified language with specified [generatorOptions]
void generate(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
StringSink sink, {
required String dartPackageName,
@@ -25,14 +28,14 @@
}
/// An abstract base class that enforces code generation across platforms.
-abstract class StructuredGenerator<InternalOptions>
- extends Generator<InternalOptions> {
+abstract class StructuredGenerator<PigeonInternalOptions>
+ extends Generator<PigeonInternalOptions> {
/// Constructor.
const StructuredGenerator();
@override
void generate(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
StringSink sink, {
required String dartPackageName,
@@ -123,7 +126,7 @@
/// Adds specified headers to [indent].
void writeFilePrologue(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -131,7 +134,7 @@
/// Writes specified imports to [indent].
void writeFileImports(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -141,7 +144,7 @@
///
/// This method is not required, and does not need to be overridden.
void writeOpenNamespace(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -151,7 +154,7 @@
///
/// This method is not required, and does not need to be overridden.
void writeCloseNamespace(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -161,7 +164,7 @@
///
/// This method is not required, and does not need to be overridden.
void writeGeneralUtilities(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -171,7 +174,7 @@
///
/// Can be overridden to add extra code before/after enums.
void writeEnums(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -189,7 +192,7 @@
/// Writes a single Enum to [indent]. This is needed in most generators.
void writeEnum(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Enum anEnum, {
@@ -200,7 +203,7 @@
///
/// Can be overridden to add extra code before/after apis.
void writeDataClasses(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -218,7 +221,7 @@
/// Writes the custom codec to [indent].
void writeGeneralCodec(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -226,7 +229,7 @@
/// Writes a single data class to [indent].
void writeDataClass(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
@@ -235,7 +238,7 @@
/// Writes a single class encode method to [indent].
void writeClassEncode(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
@@ -244,7 +247,7 @@
/// Writes a single class decode method to [indent].
void writeClassDecode(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
@@ -253,7 +256,7 @@
/// Writes a single class decode method to [indent].
void writeClassEquality(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
@@ -264,7 +267,7 @@
///
/// Can be overridden to add extra code before/after classes.
void writeApis(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -309,7 +312,7 @@
/// Writes a single Flutter Api to [indent].
void writeFlutterApi(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstFlutterApi api, {
@@ -318,7 +321,7 @@
/// Writes a single Host Api to [indent].
void writeHostApi(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstHostApi api, {
@@ -327,7 +330,7 @@
/// Writes the implementation of an `InstanceManager` to [indent].
void writeInstanceManager(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -336,7 +339,7 @@
/// Writes the implementation of the API for the `InstanceManager` to
/// [indent].
void writeInstanceManagerApi(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
@@ -353,14 +356,14 @@
/// needs to create its own codec (it has methods/fields/constructor that use
/// a data class) it should extend this codec and not `StandardMessageCodec`.
void writeProxyApiBaseCodec(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
) {}
/// Writes a single Proxy Api to [indent].
void writeProxyApi(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstProxyApi api, {
@@ -369,7 +372,7 @@
/// Writes a single event channel Api to [indent].
void writeEventChannelApi(
- InternalOptions generatorOptions,
+ PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstEventChannelApi api, {
diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart
index dd1b3d7..58cc1ab 100644
--- a/packages/pigeon/lib/src/generator_tools.dart
+++ b/packages/pigeon/lib/src/generator_tools.dart
@@ -14,7 +14,7 @@
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
-const String pigeonVersion = '25.1.0';
+const String pigeonVersion = '25.2.0';
/// Read all the content from [stdin] to a String.
String readStdin() {
@@ -861,3 +861,11 @@
dartPackageName: dartPackageName,
);
}
+
+/// Whether the type is a collection.
+bool isCollectionType(TypeDeclaration type) {
+ return !type.isClass &&
+ !type.isEnum &&
+ !type.isProxyApi &&
+ (type.baseName.contains('List') || type.baseName == 'Map');
+}
diff --git a/packages/pigeon/lib/src/gobject/gobject_generator.dart b/packages/pigeon/lib/src/gobject/gobject_generator.dart
index 3469c98..9cb1796 100644
--- a/packages/pigeon/lib/src/gobject/gobject_generator.dart
+++ b/packages/pigeon/lib/src/gobject/gobject_generator.dart
@@ -74,7 +74,7 @@
}
/// Options that control how GObject code will be generated.
-class InternalGObjectOptions {
+class InternalGObjectOptions extends PigeonInternalOptions {
/// Creates a [InternalGObjectOptions] object
const InternalGObjectOptions({
required this.headerIncludePath,
diff --git a/packages/pigeon/lib/src/java/java_generator.dart b/packages/pigeon/lib/src/java/java_generator.dart
index ec9c37a..26b346d 100644
--- a/packages/pigeon/lib/src/java/java_generator.dart
+++ b/packages/pigeon/lib/src/java/java_generator.dart
@@ -90,7 +90,7 @@
}
/// Options that control how Java code will be generated.
-class InternalJavaOptions {
+class InternalJavaOptions extends PigeonInternalOptions {
/// Creates a [InternalJavaOptions] object
const InternalJavaOptions({
required this.javaOut,
diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart
index 31f2983..1368fa3 100644
--- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart
+++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart
@@ -99,7 +99,7 @@
}
///
-class InternalKotlinOptions {
+class InternalKotlinOptions extends PigeonInternalOptions {
/// Creates a [InternalKotlinOptions] object
const InternalKotlinOptions({
this.package,
@@ -291,9 +291,46 @@
classDefinition,
dartPackageName: dartPackageName,
);
+ writeClassEquality(
+ generatorOptions,
+ root,
+ indent,
+ classDefinition,
+ dartPackageName: dartPackageName,
+ );
});
}
+ @override
+ void writeClassEquality(
+ InternalKotlinOptions generatorOptions,
+ Root root,
+ Indent indent,
+ Class classDefinition, {
+ required String dartPackageName,
+ }) {
+ indent.writeScoped('override fun equals(other: Any?): Boolean {', '}', () {
+ indent.writeScoped('if (other !is ${classDefinition.name}) {', '}', () {
+ indent.writeln('return false');
+ });
+ indent.writeScoped('if (this === other) {', '}', () {
+ indent.writeln('return true');
+ });
+ indent.write('return ');
+ indent.format(
+ classDefinition.fields
+ .map((NamedType field) => isCollectionType(field.type)
+ ? 'deepEquals${generatorOptions.fileSpecificClassNameComponent}(${field.name}, other.${field.name})'
+ : '${field.name} == other.${field.name}')
+ .join('\n&& '),
+ leadingSpace: false,
+ );
+ });
+
+ indent.newln();
+ indent.writeln('override fun hashCode(): Int = toList().hashCode()');
+ }
+
void _writeDataClassSignature(
Indent indent,
Class classDefinition, {
@@ -507,7 +544,7 @@
indent.newln();
if (root.containsEventChannel) {
indent.writeln(
- 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName());');
+ 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName())');
indent.newln();
}
}
@@ -1219,6 +1256,36 @@
});
}
+ void _writeDeepEquals(InternalKotlinOptions generatorOptions, Indent indent) {
+ indent.format('''
+private fun deepEquals${generatorOptions.fileSpecificClassNameComponent}(a: Any?, b: Any?): Boolean {
+ if (a is ByteArray && b is ByteArray) {
+ return a.contentEquals(b)
+ }
+ if (a is IntArray && b is IntArray) {
+ return a.contentEquals(b)
+ }
+ if (a is LongArray && b is LongArray) {
+ return a.contentEquals(b)
+ }
+ if (a is DoubleArray && b is DoubleArray) {
+ return a.contentEquals(b)
+ }
+ if (a is Array<*> && b is Array<*>) {
+ return a.size == b.size &&
+ a.indices.all{ deepEquals${generatorOptions.fileSpecificClassNameComponent}(a[it], b[it]) }
+ }
+ if (a is Map<*, *> && b is Map<*, *>) {
+ return a.size == b.size && a.keys.all {
+ (b as Map<Any?, Any?>).containsKey(it) &&
+ deepEquals${generatorOptions.fileSpecificClassNameComponent}(a[it], b[it])
+ }
+ }
+ return a == b;
+}
+ ''');
+ }
+
@override
void writeGeneralUtilities(
InternalKotlinOptions generatorOptions,
@@ -1236,6 +1303,11 @@
if (generatorOptions.includeErrorClass) {
_writeErrorClass(generatorOptions, indent);
}
+ if (root.classes.isNotEmpty &&
+ root.classes.any((Class dataClass) => dataClass.fields
+ .any((NamedType field) => isCollectionType(field.type)))) {
+ _writeDeepEquals(generatorOptions, indent);
+ }
}
static void _writeMethodDeclaration(
diff --git a/packages/pigeon/lib/src/objc/objc_generator.dart b/packages/pigeon/lib/src/objc/objc_generator.dart
index 85b92bd..0a2e913 100644
--- a/packages/pigeon/lib/src/objc/objc_generator.dart
+++ b/packages/pigeon/lib/src/objc/objc_generator.dart
@@ -93,7 +93,7 @@
}
/// Options that control how Objective-C code will be generated.
-class InternalObjcOptions {
+class InternalObjcOptions extends PigeonInternalOptions {
/// Parametric constructor for InternalObjcOptions.
const InternalObjcOptions({
required this.headerIncludePath,
diff --git a/packages/pigeon/lib/src/swift/swift_generator.dart b/packages/pigeon/lib/src/swift/swift_generator.dart
index 88aa792..c9d2cdc 100644
--- a/packages/pigeon/lib/src/swift/swift_generator.dart
+++ b/packages/pigeon/lib/src/swift/swift_generator.dart
@@ -79,7 +79,7 @@
}
/// Options that control how Swift code will be generated.
-class InternalSwiftOptions {
+class InternalSwiftOptions extends PigeonInternalOptions {
/// Creates a [InternalSwiftOptions] object
const InternalSwiftOptions({
this.copyrightHeader,
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
index 467de3c..6871e5f 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt
@@ -49,6 +49,29 @@
val details: Any? = null
) : Throwable()
+private fun deepEqualsCoreTests(a: Any?, b: Any?): Boolean {
+ if (a is ByteArray && b is ByteArray) {
+ return a.contentEquals(b)
+ }
+ if (a is IntArray && b is IntArray) {
+ return a.contentEquals(b)
+ }
+ if (a is LongArray && b is LongArray) {
+ return a.contentEquals(b)
+ }
+ if (a is DoubleArray && b is DoubleArray) {
+ return a.contentEquals(b)
+ }
+ if (a is Array<*> && b is Array<*>) {
+ return a.size == b.size && a.indices.all { deepEqualsCoreTests(a[it], b[it]) }
+ }
+ if (a is Map<*, *> && b is Map<*, *>) {
+ return a.size == b.size &&
+ a.keys.all { (b as Map<Any?, Any?>).containsKey(it) && deepEqualsCoreTests(a[it], b[it]) }
+ }
+ return a == b
+}
+
enum class AnEnum(val raw: Int) {
ONE(0),
TWO(1),
@@ -87,6 +110,18 @@
aField,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is UnusedClass) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return aField == other.aField
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -218,6 +253,45 @@
mapMap,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is AllTypes) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return aBool == other.aBool &&
+ anInt == other.anInt &&
+ anInt64 == other.anInt64 &&
+ aDouble == other.aDouble &&
+ deepEqualsCoreTests(aByteArray, other.aByteArray) &&
+ deepEqualsCoreTests(a4ByteArray, other.a4ByteArray) &&
+ deepEqualsCoreTests(a8ByteArray, other.a8ByteArray) &&
+ deepEqualsCoreTests(aFloatArray, other.aFloatArray) &&
+ anEnum == other.anEnum &&
+ anotherEnum == other.anotherEnum &&
+ aString == other.aString &&
+ anObject == other.anObject &&
+ deepEqualsCoreTests(list, other.list) &&
+ deepEqualsCoreTests(stringList, other.stringList) &&
+ deepEqualsCoreTests(intList, other.intList) &&
+ deepEqualsCoreTests(doubleList, other.doubleList) &&
+ deepEqualsCoreTests(boolList, other.boolList) &&
+ deepEqualsCoreTests(enumList, other.enumList) &&
+ deepEqualsCoreTests(objectList, other.objectList) &&
+ deepEqualsCoreTests(listList, other.listList) &&
+ deepEqualsCoreTests(mapList, other.mapList) &&
+ deepEqualsCoreTests(map, other.map) &&
+ deepEqualsCoreTests(stringMap, other.stringMap) &&
+ deepEqualsCoreTests(intMap, other.intMap) &&
+ deepEqualsCoreTests(enumMap, other.enumMap) &&
+ deepEqualsCoreTests(objectMap, other.objectMap) &&
+ deepEqualsCoreTests(listMap, other.listMap) &&
+ deepEqualsCoreTests(mapMap, other.mapMap)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -361,6 +435,48 @@
recursiveClassMap,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is AllNullableTypes) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return aNullableBool == other.aNullableBool &&
+ aNullableInt == other.aNullableInt &&
+ aNullableInt64 == other.aNullableInt64 &&
+ aNullableDouble == other.aNullableDouble &&
+ deepEqualsCoreTests(aNullableByteArray, other.aNullableByteArray) &&
+ deepEqualsCoreTests(aNullable4ByteArray, other.aNullable4ByteArray) &&
+ deepEqualsCoreTests(aNullable8ByteArray, other.aNullable8ByteArray) &&
+ deepEqualsCoreTests(aNullableFloatArray, other.aNullableFloatArray) &&
+ aNullableEnum == other.aNullableEnum &&
+ anotherNullableEnum == other.anotherNullableEnum &&
+ aNullableString == other.aNullableString &&
+ aNullableObject == other.aNullableObject &&
+ allNullableTypes == other.allNullableTypes &&
+ deepEqualsCoreTests(list, other.list) &&
+ deepEqualsCoreTests(stringList, other.stringList) &&
+ deepEqualsCoreTests(intList, other.intList) &&
+ deepEqualsCoreTests(doubleList, other.doubleList) &&
+ deepEqualsCoreTests(boolList, other.boolList) &&
+ deepEqualsCoreTests(enumList, other.enumList) &&
+ deepEqualsCoreTests(objectList, other.objectList) &&
+ deepEqualsCoreTests(listList, other.listList) &&
+ deepEqualsCoreTests(mapList, other.mapList) &&
+ deepEqualsCoreTests(recursiveClassList, other.recursiveClassList) &&
+ deepEqualsCoreTests(map, other.map) &&
+ deepEqualsCoreTests(stringMap, other.stringMap) &&
+ deepEqualsCoreTests(intMap, other.intMap) &&
+ deepEqualsCoreTests(enumMap, other.enumMap) &&
+ deepEqualsCoreTests(objectMap, other.objectMap) &&
+ deepEqualsCoreTests(listMap, other.listMap) &&
+ deepEqualsCoreTests(mapMap, other.mapMap) &&
+ deepEqualsCoreTests(recursiveClassMap, other.recursiveClassMap)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -493,6 +609,45 @@
mapMap,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is AllNullableTypesWithoutRecursion) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return aNullableBool == other.aNullableBool &&
+ aNullableInt == other.aNullableInt &&
+ aNullableInt64 == other.aNullableInt64 &&
+ aNullableDouble == other.aNullableDouble &&
+ deepEqualsCoreTests(aNullableByteArray, other.aNullableByteArray) &&
+ deepEqualsCoreTests(aNullable4ByteArray, other.aNullable4ByteArray) &&
+ deepEqualsCoreTests(aNullable8ByteArray, other.aNullable8ByteArray) &&
+ deepEqualsCoreTests(aNullableFloatArray, other.aNullableFloatArray) &&
+ aNullableEnum == other.aNullableEnum &&
+ anotherNullableEnum == other.anotherNullableEnum &&
+ aNullableString == other.aNullableString &&
+ aNullableObject == other.aNullableObject &&
+ deepEqualsCoreTests(list, other.list) &&
+ deepEqualsCoreTests(stringList, other.stringList) &&
+ deepEqualsCoreTests(intList, other.intList) &&
+ deepEqualsCoreTests(doubleList, other.doubleList) &&
+ deepEqualsCoreTests(boolList, other.boolList) &&
+ deepEqualsCoreTests(enumList, other.enumList) &&
+ deepEqualsCoreTests(objectList, other.objectList) &&
+ deepEqualsCoreTests(listList, other.listList) &&
+ deepEqualsCoreTests(mapList, other.mapList) &&
+ deepEqualsCoreTests(map, other.map) &&
+ deepEqualsCoreTests(stringMap, other.stringMap) &&
+ deepEqualsCoreTests(intMap, other.intMap) &&
+ deepEqualsCoreTests(enumMap, other.enumMap) &&
+ deepEqualsCoreTests(objectMap, other.objectMap) &&
+ deepEqualsCoreTests(listMap, other.listMap) &&
+ deepEqualsCoreTests(mapMap, other.mapMap)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -544,6 +699,24 @@
nullableClassMap,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is AllClassesWrapper) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return allNullableTypes == other.allNullableTypes &&
+ allNullableTypesWithoutRecursion == other.allNullableTypesWithoutRecursion &&
+ allTypes == other.allTypes &&
+ deepEqualsCoreTests(classList, other.classList) &&
+ deepEqualsCoreTests(nullableClassList, other.nullableClassList) &&
+ deepEqualsCoreTests(classMap, other.classMap) &&
+ deepEqualsCoreTests(nullableClassMap, other.nullableClassMap)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -564,6 +737,18 @@
testList,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is TestMessage) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return deepEqualsCoreTests(testList, other.testList)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
private open class CoreTestsPigeonCodec : StandardMessageCodec() {
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt
index 1f24c42..6d39a54 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt
@@ -28,6 +28,31 @@
val details: Any? = null
) : Throwable()
+private fun deepEqualsEventChannelTests(a: Any?, b: Any?): Boolean {
+ if (a is ByteArray && b is ByteArray) {
+ return a.contentEquals(b)
+ }
+ if (a is IntArray && b is IntArray) {
+ return a.contentEquals(b)
+ }
+ if (a is LongArray && b is LongArray) {
+ return a.contentEquals(b)
+ }
+ if (a is DoubleArray && b is DoubleArray) {
+ return a.contentEquals(b)
+ }
+ if (a is Array<*> && b is Array<*>) {
+ return a.size == b.size && a.indices.all { deepEqualsEventChannelTests(a[it], b[it]) }
+ }
+ if (a is Map<*, *> && b is Map<*, *>) {
+ return a.size == b.size &&
+ a.keys.all {
+ (b as Map<Any?, Any?>).containsKey(it) && deepEqualsEventChannelTests(a[it], b[it])
+ }
+ }
+ return a == b
+}
+
enum class EventEnum(val raw: Int) {
ONE(0),
TWO(1),
@@ -193,6 +218,48 @@
recursiveClassMap,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is EventAllNullableTypes) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return aNullableBool == other.aNullableBool &&
+ aNullableInt == other.aNullableInt &&
+ aNullableInt64 == other.aNullableInt64 &&
+ aNullableDouble == other.aNullableDouble &&
+ deepEqualsEventChannelTests(aNullableByteArray, other.aNullableByteArray) &&
+ deepEqualsEventChannelTests(aNullable4ByteArray, other.aNullable4ByteArray) &&
+ deepEqualsEventChannelTests(aNullable8ByteArray, other.aNullable8ByteArray) &&
+ deepEqualsEventChannelTests(aNullableFloatArray, other.aNullableFloatArray) &&
+ aNullableEnum == other.aNullableEnum &&
+ anotherNullableEnum == other.anotherNullableEnum &&
+ aNullableString == other.aNullableString &&
+ aNullableObject == other.aNullableObject &&
+ allNullableTypes == other.allNullableTypes &&
+ deepEqualsEventChannelTests(list, other.list) &&
+ deepEqualsEventChannelTests(stringList, other.stringList) &&
+ deepEqualsEventChannelTests(intList, other.intList) &&
+ deepEqualsEventChannelTests(doubleList, other.doubleList) &&
+ deepEqualsEventChannelTests(boolList, other.boolList) &&
+ deepEqualsEventChannelTests(enumList, other.enumList) &&
+ deepEqualsEventChannelTests(objectList, other.objectList) &&
+ deepEqualsEventChannelTests(listList, other.listList) &&
+ deepEqualsEventChannelTests(mapList, other.mapList) &&
+ deepEqualsEventChannelTests(recursiveClassList, other.recursiveClassList) &&
+ deepEqualsEventChannelTests(map, other.map) &&
+ deepEqualsEventChannelTests(stringMap, other.stringMap) &&
+ deepEqualsEventChannelTests(intMap, other.intMap) &&
+ deepEqualsEventChannelTests(enumMap, other.enumMap) &&
+ deepEqualsEventChannelTests(objectMap, other.objectMap) &&
+ deepEqualsEventChannelTests(listMap, other.listMap) &&
+ deepEqualsEventChannelTests(mapMap, other.mapMap) &&
+ deepEqualsEventChannelTests(recursiveClassMap, other.recursiveClassMap)
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/**
@@ -214,6 +281,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is IntEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -230,6 +309,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is StringEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -246,6 +337,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is BoolEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -262,6 +365,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is DoubleEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -278,6 +393,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is ObjectsEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -294,6 +421,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is EnumEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
@@ -310,6 +449,18 @@
value,
)
}
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is ClassEvent) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return value == other.value
+ }
+
+ override fun hashCode(): Int = toList().hashCode()
}
private open class EventChannelTestsPigeonCodec : StandardMessageCodec() {
diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
index aa42535..a630b27 100644
--- a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
+++ b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt
@@ -10,70 +10,13 @@
import java.nio.ByteBuffer
import java.util.ArrayList
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Test
internal class AllDatatypesTest {
- fun compareAllTypes(firstTypes: AllTypes?, secondTypes: AllTypes?) {
- assertEquals(firstTypes == null, secondTypes == null)
- if (firstTypes == null || secondTypes == null) {
- return
- }
- assertEquals(firstTypes.aBool, secondTypes.aBool)
- assertEquals(firstTypes.anInt, secondTypes.anInt)
- assertEquals(firstTypes.anInt64, secondTypes.anInt64)
- assertEquals(firstTypes.aDouble, secondTypes.aDouble, 0.0)
- assertEquals(firstTypes.aString, secondTypes.aString)
- assertTrue(firstTypes.aByteArray.contentEquals(secondTypes.aByteArray))
- assertTrue(firstTypes.a4ByteArray.contentEquals(secondTypes.a4ByteArray))
- assertTrue(firstTypes.a8ByteArray.contentEquals(secondTypes.a8ByteArray))
- assertTrue(firstTypes.aFloatArray.contentEquals(secondTypes.aFloatArray))
- assertEquals(firstTypes.anEnum, secondTypes.anEnum)
- assertEquals(firstTypes.anotherEnum, secondTypes.anotherEnum)
- assertEquals(firstTypes.anObject, secondTypes.anObject)
- assertEquals(firstTypes.list, secondTypes.list)
- assertEquals(firstTypes.boolList, secondTypes.boolList)
- assertEquals(firstTypes.doubleList, secondTypes.doubleList)
- assertEquals(firstTypes.intList, secondTypes.intList)
- assertEquals(firstTypes.stringList, secondTypes.stringList)
- assertEquals(firstTypes.map, secondTypes.map)
- }
-
- private fun compareAllNullableTypes(
- firstTypes: AllNullableTypes?,
- secondTypes: AllNullableTypes?
- ) {
- assertEquals(firstTypes == null, secondTypes == null)
- if (firstTypes == null || secondTypes == null) {
- return
- }
- assertEquals(firstTypes.aNullableBool, secondTypes.aNullableBool)
- assertEquals(firstTypes.aNullableInt, secondTypes.aNullableInt)
- assertEquals(firstTypes.aNullableDouble, secondTypes.aNullableDouble)
- assertEquals(firstTypes.aNullableString, secondTypes.aNullableString)
- assertTrue(firstTypes.aNullableByteArray.contentEquals(secondTypes.aNullableByteArray))
- assertTrue(firstTypes.aNullable4ByteArray.contentEquals(secondTypes.aNullable4ByteArray))
- assertTrue(firstTypes.aNullable8ByteArray.contentEquals(secondTypes.aNullable8ByteArray))
- assertTrue(firstTypes.aNullableFloatArray.contentEquals(secondTypes.aNullableFloatArray))
- assertEquals(firstTypes.aNullableObject, secondTypes.aNullableObject)
- assertEquals(firstTypes.aNullableEnum, secondTypes.aNullableEnum)
- assertEquals(firstTypes.anotherNullableEnum, secondTypes.anotherNullableEnum)
- assertEquals(firstTypes.list, secondTypes.list)
- assertEquals(firstTypes.boolList, secondTypes.boolList)
- assertEquals(firstTypes.doubleList, secondTypes.doubleList)
- assertEquals(firstTypes.intList, secondTypes.intList)
- assertEquals(firstTypes.stringList, secondTypes.stringList)
- assertEquals(firstTypes.listList, secondTypes.listList)
- assertEquals(firstTypes.mapList, secondTypes.mapList)
- assertEquals(firstTypes.map, secondTypes.map)
- assertEquals(firstTypes.stringMap, secondTypes.stringMap)
- assertEquals(firstTypes.intMap, secondTypes.intMap)
- assertEquals(firstTypes.listMap, secondTypes.listMap)
- assertEquals(firstTypes.mapMap, secondTypes.mapMap)
- }
-
@Test
fun testNullValues() {
val everything = AllNullableTypes()
@@ -95,7 +38,7 @@
var didCall = false
api.echoAllNullableTypes(everything) { result ->
didCall = true
- val output = (result.getOrNull())?.let { compareAllNullableTypes(it, everything) }
+ val output = (result.getOrNull())?.let { it == everything }
assertNotNull(output)
}
@@ -148,9 +91,111 @@
var didCall = false
api.echoAllNullableTypes(everything) {
didCall = true
- compareAllNullableTypes(everything, it.getOrNull())
+ assertTrue(everything == it.getOrNull())
}
assertTrue(didCall)
}
+
+ private val correctList = listOf<Any?>("a", 2, "three")
+ private val matchingList = correctList.toMutableList()
+ private val differentList = listOf<Any?>("a", 2, "three", 4.0)
+
+ private val correctMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "c" to "three")
+ private val matchingMap = correctMap.toMap()
+ private val differentKeyMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "d" to "three")
+ private val differentValueMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "c" to "five")
+
+ private val correctListInMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "c" to correctList)
+ private val matchingListInMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "c" to matchingList)
+ private val differentListInMap = mapOf<Any, Any?>("a" to 1, "b" to 2, "c" to differentList)
+
+ private val correctMapInList = listOf<Any?>("a", 2, correctMap)
+ private val matchingMapInList = listOf<Any?>("a", 2, matchingMap)
+ private val differentKeyMapInList = listOf<Any?>("a", 2, differentKeyMap)
+ private val differentValueMapInList = listOf<Any?>("a", 2, differentValueMap)
+
+ @Test
+ fun `equality method correctly checks deep equality`() {
+ val generic = AllNullableTypes(list = correctList, map = correctMap)
+ val identical = generic.copy()
+ assertEquals(generic, identical)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching classes`() {
+ val generic = AllNullableTypes(list = correctList, map = correctMap)
+ val allNull = AllNullableTypes()
+ assertNotEquals(allNull, generic)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching lists in classes`() {
+ val withList = AllNullableTypes(list = correctList)
+ val withDifferentList = AllNullableTypes(list = differentList)
+ assertNotEquals(withList, withDifferentList)
+ }
+
+ @Test
+ fun `equality method correctly identifies matching -but unique- lists in classes`() {
+ val withList = AllNullableTypes(list = correctList)
+ val withDifferentList = AllNullableTypes(list = matchingList)
+ assertEquals(withList, withDifferentList)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching keys in maps in classes`() {
+ val withMap = AllNullableTypes(map = correctMap)
+ val withDifferentMap = AllNullableTypes(map = differentKeyMap)
+ assertNotEquals(withMap, withDifferentMap)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching values in maps in classes`() {
+ val withMap = AllNullableTypes(map = correctMap)
+ val withDifferentMap = AllNullableTypes(map = differentValueMap)
+ assertNotEquals(withMap, withDifferentMap)
+ }
+
+ @Test
+ fun `equality method correctly identifies matching -but unique- maps in classes`() {
+ val withMap = AllNullableTypes(map = correctMap)
+ val withDifferentMap = AllNullableTypes(map = matchingMap)
+ assertEquals(withMap, withDifferentMap)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching lists nested in maps in classes`() {
+ val withListInMap = AllNullableTypes(map = correctListInMap)
+ val withDifferentListInMap = AllNullableTypes(map = differentListInMap)
+ assertNotEquals(withListInMap, withDifferentListInMap)
+ }
+
+ @Test
+ fun `equality method correctly identifies matching -but unique- lists nested in maps in classes`() {
+ val withListInMap = AllNullableTypes(map = correctListInMap)
+ val withDifferentListInMap = AllNullableTypes(map = matchingListInMap)
+ assertEquals(withListInMap, withDifferentListInMap)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching keys in maps nested in lists in classes`() {
+ val withMapInList = AllNullableTypes(list = correctMapInList)
+ val withDifferentMapInList = AllNullableTypes(list = differentKeyMapInList)
+ assertNotEquals(withMapInList, withDifferentMapInList)
+ }
+
+ @Test
+ fun `equality method correctly identifies non-matching values in maps nested in lists in classes`() {
+ val withMapInList = AllNullableTypes(list = correctMapInList)
+ val withDifferentMapInList = AllNullableTypes(list = differentValueMapInList)
+ assertNotEquals(withMapInList, withDifferentMapInList)
+ }
+
+ @Test
+ fun `equality method correctly identifies matching -but unique- maps nested in lists in classes`() {
+ val withMapInList = AllNullableTypes(list = correctMapInList)
+ val withDifferentMapInList = AllNullableTypes(list = matchingMapInList)
+ assertEquals(withMapInList, withDifferentMapInList)
+ }
}
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index a1e5e43..3459232 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
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%3A%22p%3A+pigeon%22
-version: 25.1.0 # This must match the version in lib/src/generator_tools.dart
+version: 25.2.0 # This must match the version in lib/src/generator_tools.dart
environment:
sdk: ^3.4.0