Skip to content

Commit

Permalink
generate unrecognized option to plugin + tests with protoc
Browse files Browse the repository at this point in the history
  • Loading branch information
oldergod committed Mar 28, 2024
1 parent feb5bed commit 8c471cd
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 38 deletions.
10 changes: 6 additions & 4 deletions wire-compiler/api/wire-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,16 @@ public final class com/squareup/wire/schema/JavaTarget : com/squareup/wire/schem
}

public final class com/squareup/wire/schema/KotlinTarget : com/squareup/wire/schema/Target {
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZ)V
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZ)V
public synthetic fun <init> (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/util/List;
public final fun component10 ()Lcom/squareup/wire/kotlin/RpcRole;
public final fun component11 ()Z
public final fun component12 ()I
public final fun component13 ()Ljava/lang/String;
public final fun component14 ()Z
public final fun component15 ()Z
public final fun component16 ()Z
public final fun component2 ()Ljava/util/List;
public final fun component3 ()Z
public final fun component4 ()Ljava/lang/String;
Expand All @@ -127,8 +128,8 @@ public final class com/squareup/wire/schema/KotlinTarget : com/squareup/wire/sch
public final fun component7 ()Z
public final fun component8 ()Z
public final fun component9 ()Lcom/squareup/wire/kotlin/RpcCallStyle;
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZ)Lcom/squareup/wire/schema/KotlinTarget;
public static synthetic fun copy$default (Lcom/squareup/wire/schema/KotlinTarget;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZILjava/lang/Object;)Lcom/squareup/wire/schema/KotlinTarget;
public final fun copy (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZ)Lcom/squareup/wire/schema/KotlinTarget;
public static synthetic fun copy$default (Lcom/squareup/wire/schema/KotlinTarget;Ljava/util/List;Ljava/util/List;ZLjava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZILjava/lang/Object;)Lcom/squareup/wire/schema/KotlinTarget;
public fun copyTarget (Ljava/util/List;Ljava/util/List;ZLjava/lang/String;)Lcom/squareup/wire/schema/Target;
public fun equals (Ljava/lang/Object;)Z
public final fun getAndroid ()Z
Expand All @@ -139,6 +140,7 @@ public final class com/squareup/wire/schema/KotlinTarget : com/squareup/wire/sch
public final fun getEscapeKotlinKeywords ()Z
public fun getExcludes ()Ljava/util/List;
public fun getExclusive ()Z
public final fun getGenerateUnrecognizedEnumConstant ()Z
public fun getIncludes ()Ljava/util/List;
public final fun getJavaInterop ()Z
public final fun getNameSuffix ()Ljava/lang/String;
Expand Down
12 changes: 11 additions & 1 deletion wire-compiler/src/main/java/com/squareup/wire/schema/Target.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,17 @@ data class KotlinTarget(
*/
val buildersOnly: Boolean = false,

/** If true, Kotlin keywords are escaped with backticks. If false, an underscore is added as a suffix. */
/**
* If true, Kotlin keywords are escaped with backticks. If false, an underscore is added as a
* suffix.
*/
val escapeKotlinKeywords: Boolean = false,

/**
* If true, generated enums will have an extra `UNRECOGNIZED` constant with a value of `-1`. This
* only applies to enum which syntax is proto3.
*/
val generateUnrecognizedEnumConstant: Boolean = false,
) : Target() {
override fun newHandler(): SchemaHandler {
return KotlinSchemaHandler(
Expand All @@ -146,6 +155,7 @@ data class KotlinTarget(
nameSuffix = nameSuffix,
buildersOnly = buildersOnly,
escapeKotlinKeywords = escapeKotlinKeywords,
generateUnrecognizedEnumConstant = generateUnrecognizedEnumConstant,
)
}

Expand Down
2 changes: 2 additions & 0 deletions wire-gradle-plugin/api/wire-gradle-plugin.api
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class com/squareup/wire/gradle/KotlinOutput : com/squareup/wire/gradle/Wi
public final fun getEscapeKotlinKeywords ()Z
public final fun getExcludes ()Ljava/util/List;
public final fun getExclusive ()Z
public final fun getGenerateUnrecognizedEnumConstant ()Z
public final fun getGrpcServerCompatible ()Z
public final fun getIncludes ()Ljava/util/List;
public final fun getJavaInterop ()Z
Expand All @@ -65,6 +66,7 @@ public class com/squareup/wire/gradle/KotlinOutput : com/squareup/wire/gradle/Wi
public final fun setEscapeKotlinKeywords (Z)V
public final fun setExcludes (Ljava/util/List;)V
public final fun setExclusive (Z)V
public final fun setGenerateUnrecognizedEnumConstant (Z)V
public final fun setGrpcServerCompatible (Z)V
public final fun setIncludes (Ljava/util/List;)V
public final fun setJavaInterop (Z)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ open class KotlinOutput @Inject constructor() : WireOutput() {
/** If true, Kotlin keywords are escaped with backticks. If false, an underscore is added as a suffix. */
var escapeKotlinKeywords: Boolean = false

/**
* If true, generated enums will have an extra `UNRECOGNIZED` constant with a value of `-1`. This
* only applies to enum which syntax is proto3.
*/
var generateUnrecognizedEnumConstant = false

override fun toTarget(outputDirectory: String): KotlinTarget {
if (grpcServerCompatible) {
throw IllegalArgumentException(
Expand Down Expand Up @@ -125,6 +131,7 @@ open class KotlinOutput @Inject constructor() : WireOutput() {
nameSuffix = nameSuffix,
buildersOnly = buildersOnly,
escapeKotlinKeywords = escapeKotlinKeywords,
generateUnrecognizedEnumConstant = generateUnrecognizedEnumConstant,
)
}
}
Expand Down
4 changes: 2 additions & 2 deletions wire-kotlin-generator/api/wire-kotlin-generator.api
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public final class com/squareup/wire/kotlin/KotlinGenerator$Companion {

public final class com/squareup/wire/kotlin/KotlinSchemaHandler : com/squareup/wire/schema/SchemaHandler {
public static final field Companion Lcom/squareup/wire/kotlin/KotlinSchemaHandler$Companion;
public fun <init> (Ljava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZ)V
public synthetic fun <init> (Ljava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZ)V
public synthetic fun <init> (Ljava/lang/String;ZZZZLcom/squareup/wire/kotlin/RpcCallStyle;Lcom/squareup/wire/kotlin/RpcRole;ZILjava/lang/String;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun handle (Lcom/squareup/wire/schema/Extend;Lcom/squareup/wire/schema/Field;Lcom/squareup/wire/schema/SchemaHandler$Context;)Lokio/Path;
public fun handle (Lcom/squareup/wire/schema/Schema;Lcom/squareup/wire/schema/SchemaHandler$Context;)V
public fun handle (Lcom/squareup/wire/schema/Service;Lcom/squareup/wire/schema/SchemaHandler$Context;)Ljava/util/List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,17 @@ class KotlinSchemaHandler(
*/
private val buildersOnly: Boolean = false,

/** If true, Kotlin keywords are escaped with backticks. If false, an underscore is added as a suffix. */
/**
* If true, Kotlin keywords are escaped with backticks. If false, an underscore is added as a
* suffix.
*/
private val escapeKotlinKeywords: Boolean = false,

/**
* If true, generated enums will have an extra `UNRECOGNIZED` constant with a value of `-1`. This
* only applies to enum which syntax is proto3.
*/
private val generateUnrecognizedEnumConstant: Boolean = false,
) : SchemaHandler() {
private lateinit var kotlinGenerator: KotlinGenerator

Expand All @@ -93,6 +102,7 @@ class KotlinSchemaHandler(
nameSuffix = nameSuffix,
buildersOnly = buildersOnly,
escapeKotlinKeywords = escapeKotlinKeywords,
generateUnrecognizedEnumConstant = generateUnrecognizedEnumConstant,
)
context.fileSystem.createDirectories(context.outDirectory)
super.handle(schema, context)
Expand Down
1 change: 1 addition & 0 deletions wire-protoc-compatibility-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ wire {
kotlin {
javaInterop = true
boxOneOfsMinSize = 5
generateUnrecognizedEnumConstant = true

includes = listOf(
"squareup.proto2.kotlin.*",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
syntax = "proto2";
package squareup.proto2.kotlin.unrecognized_constant;

import "squareup/proto3/kotlin/unrecognized_constant/easter.proto";

// Proto2 message with a proto3 enum.
message Easter {
optional squareup.proto3.kotlin.unrecognized_constant.EasterAnimal optional_easter_animal = 2;
required squareup.proto3.kotlin.unrecognized_constant.EasterAnimal required_easter_animal = 3;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
syntax = "proto3";
package squareup.proto3.kotlin.unrecognized_constant;

// Proto3 message with a proto3 enum.
message Easter {
optional EasterAnimal optional_easter_animal = 2;
EasterAnimal identity_easter_animal = 3;
}

enum EasterAnimal {
EASTER_ANIMAL_DEFAULT = 0;
BUNNY = 1;
HEN = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,17 @@ import squareup.proto2.kotlin.interop.type.EnumProto2 as EnumProto2K
import squareup.proto2.kotlin.interop.type.InteropTypes.EnumProto2
import squareup.proto2.kotlin.interop.type.InteropTypes.MessageProto2
import squareup.proto2.kotlin.interop.type.MessageProto2 as MessageProto2K
import squareup.proto2.kotlin.unrecognized_constant.Easter as EasterK2
import squareup.proto2.kotlin.unrecognized_constant.EasterOuterClass.Easter as EasterP2
import squareup.proto2.wire.extensions.WireMessage
import squareup.proto3.java.interop.type.EnumProto3 as EnumProto3J
import squareup.proto3.java.interop.type.MessageProto3 as MessageProto3J
import squareup.proto3.kotlin.interop.type.EnumProto3 as EnumProto3K
import squareup.proto3.kotlin.interop.type.InteropTypes.EnumProto3
import squareup.proto3.kotlin.interop.type.InteropTypes.MessageProto3
import squareup.proto3.kotlin.interop.type.MessageProto3 as MessageProto3K
import squareup.proto3.kotlin.unrecognized_constant.EasterAnimal as EasterAnimalK3
import squareup.proto3.kotlin.unrecognized_constant.EasterOuterClass.EasterAnimal as EasterAnimalP3

class Proto2WireProtocCompatibilityTests {
@Test fun simpleMessage() {
Expand Down Expand Up @@ -195,6 +199,57 @@ class Proto2WireProtocCompatibilityTests {
assertThat(googleMessageDecodedFromWireMessage).isEqualTo(googleMessage)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_negativeValue_proto2Message() {
// ┌─ 2: -1
// ╰- 3: 2
val bytes = "10ffffffffffffffffff011802"
val wireMessage: EasterK2 = EasterK2.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP2 = EasterP2.parseFrom(bytes.decodeHex().toByteArray())

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.EASTER_ANIMAL_DEFAULT)
assertThat(protocMessage.optionalEasterAnimal.number).isEqualTo(0)

// Keeping that around to clearly show that Wire has a different behavior that protoc. Not sure
// that we want to fix this.
assertThat(wireMessage.optional_easter_animal).isEqualTo(EasterAnimalK3.UNRECOGNIZED)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_knownValue_proto2Message() {
// ┌─ 2: 1
// ╰- 3: 1
val bytes = "10011801"
val wireMessage: EasterK2 = EasterK2.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP2 = EasterP2.parseFrom(bytes.decodeHex().toByteArray())

assertThat(wireMessage.required_easter_animal.value).isEqualTo(protocMessage.requiredEasterAnimal.number)
assertThat(wireMessage.optional_easter_animal!!.value).isEqualTo(protocMessage.optionalEasterAnimal.number)

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.BUNNY)
assertThat(protocMessage.optionalEasterAnimal.number).isEqualTo(EasterAnimalP3.BUNNY_VALUE)
assertThat(protocMessage.requiredEasterAnimal).isEqualTo(EasterAnimalP3.BUNNY)
assertThat(protocMessage.requiredEasterAnimal.number).isEqualTo(EasterAnimalP3.BUNNY_VALUE)

assertThat(wireMessage.optional_easter_animal).isEqualTo(EasterAnimalK3.BUNNY)
assertThat(wireMessage.required_easter_animal).isEqualTo(EasterAnimalK3.BUNNY)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_unknownValue_proto2Message() {
// Both Wire and Protoc throw if the required field isn't known.
// ┌─ 2: 5
// ╰- 3: 2
val bytes = "10051802"

val wireMessage: EasterK2 = EasterK2.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP2 = EasterP2.parseFrom(bytes.decodeHex().toByteArray())

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.EASTER_ANIMAL_DEFAULT)
assertThat(protocMessage.optionalEasterAnimal.number).isEqualTo(0)

// Keeping that around to clearly show that Wire has a different behavior that protoc. Not sure
// that we want to fix this.
assertThat(wireMessage.optional_easter_animal).isNull()
}

companion object {
private val allTypesRegistry = ExtensionRegistry.newInstance().apply {
add(extOptBool)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import com.squareup.wire.proto3.kotlin.requiredextension.RequiredExtension as Re
import com.squareup.wire.proto3.kotlin.requiredextension.RequiredExtensionMessage as RequiredExtensionMessageK
import java.io.File
import okio.ByteString
import okio.ByteString.Companion.decodeHex
import okio.buffer
import okio.source
import org.assertj.core.api.Assertions.assertThat
Expand Down Expand Up @@ -62,6 +63,10 @@ import squareup.proto3.kotlin.interop.type.InteropTypes.MessageProto3
import squareup.proto3.kotlin.interop.type.MessageProto3 as MessageProto3K
import squareup.proto3.kotlin.pizza.PizzaDelivery as PizzaDeliveryK
import squareup.proto3.kotlin.pizza.PizzaOuterClass
import squareup.proto3.kotlin.unrecognized_constant.Easter as EasterK3
import squareup.proto3.kotlin.unrecognized_constant.EasterAnimal as EasterAnimalK3
import squareup.proto3.kotlin.unrecognized_constant.EasterOuterClass.Easter as EasterP3
import squareup.proto3.kotlin.unrecognized_constant.EasterOuterClass.EasterAnimal as EasterAnimalP3
import squareup.proto3.wire.extensions.WireMessage

class Proto3WireProtocCompatibilityTests {
Expand Down Expand Up @@ -195,6 +200,65 @@ class Proto3WireProtocCompatibilityTests {
)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_negativeValue_proto3Message() {
// ┌─ 2: -1
// ╰- 3: -1
val bytes = "10ffffffffffffffffff0118ffffffffffffffffff01"
val wireMessage: EasterK3 = EasterK3.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP3 = EasterP3.parseFrom(bytes.decodeHex().toByteArray())

assertThat(wireMessage.identity_easter_animal.value).isEqualTo(protocMessage.identityEasterAnimalValue)
assertThat(wireMessage.optional_easter_animal!!.value).isEqualTo(protocMessage.optionalEasterAnimalValue)

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.UNRECOGNIZED)
assertThat(protocMessage.optionalEasterAnimalValue).isEqualTo(-1)
assertThat(protocMessage.identityEasterAnimal).isEqualTo(EasterAnimalP3.UNRECOGNIZED)
assertThat(protocMessage.identityEasterAnimalValue).isEqualTo(-1)

assertThat(wireMessage.optional_easter_animal).isEqualTo(EasterAnimalK3.UNRECOGNIZED)
assertThat(wireMessage.identity_easter_animal).isEqualTo(EasterAnimalK3.UNRECOGNIZED)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_knownValue_proto3Message() {
// ┌─ 2: 1
// ╰- 3: 1
val bytes = "10011801"
val wireMessage: EasterK3 = EasterK3.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP3 = EasterP3.parseFrom(bytes.decodeHex().toByteArray())

assertThat(wireMessage.identity_easter_animal.value).isEqualTo(protocMessage.identityEasterAnimalValue)
assertThat(wireMessage.optional_easter_animal!!.value).isEqualTo(protocMessage.optionalEasterAnimalValue)

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.BUNNY)
assertThat(protocMessage.optionalEasterAnimalValue).isEqualTo(EasterAnimalP3.BUNNY_VALUE)
assertThat(protocMessage.identityEasterAnimal).isEqualTo(EasterAnimalP3.BUNNY)
assertThat(protocMessage.identityEasterAnimalValue).isEqualTo(EasterAnimalP3.BUNNY_VALUE)

assertThat(wireMessage.optional_easter_animal).isEqualTo(EasterAnimalK3.BUNNY)
assertThat(wireMessage.identity_easter_animal).isEqualTo(EasterAnimalK3.BUNNY)
}

@Test fun encodingAndDecodingOfUnrecognizedEnumConstants_unknownValue_proto3Message() {
// ┌─ 2: 5
// ╰- 3: 5
val bytes = "10051805"
val wireMessage: EasterK3 = EasterK3.ADAPTER.decode(bytes.decodeHex())
val protocMessage: EasterP3 = EasterP3.parseFrom(bytes.decodeHex().toByteArray())

// TODO(Benoit) Wrong Wire API called for the next two lines. We need something new to fetch
// the real unknown value.
// assertThat(wireMessage.identity_easter_animal.value).isEqualTo(protocMessage.identityEasterAnimalValue)
// assertThat(wireMessage.optional_easter_animal).isEqualTo(protocMessage.optionalEasterAnimalValue)

assertThat(protocMessage.optionalEasterAnimal).isEqualTo(EasterAnimalP3.UNRECOGNIZED)
assertThat(protocMessage.optionalEasterAnimalValue).isEqualTo(5)
assertThat(protocMessage.identityEasterAnimal).isEqualTo(EasterAnimalP3.UNRECOGNIZED)
assertThat(protocMessage.identityEasterAnimalValue).isEqualTo(5)

assertThat(wireMessage.optional_easter_animal).isEqualTo(EasterAnimalK3.UNRECOGNIZED)
assertThat(wireMessage.identity_easter_animal).isEqualTo(EasterAnimalK3.UNRECOGNIZED)
}

@Test fun deserializeIdentityAllTypesProtoc() {
val identityAllTypes = AllTypesOuterClass.AllTypes.newBuilder().build()
val jsonParser = JsonFormat.parser()
Expand Down
Loading

0 comments on commit 8c471cd

Please sign in to comment.