From f402d1d0662c04a4a7881c67f0276046018f5a3f Mon Sep 17 00:00:00 2001 From: Adam Lickel Date: Mon, 13 Nov 2023 11:54:55 -0800 Subject: [PATCH 1/5] Fix for @ProtoDefaulted --- .../src/main/java/com/squareup/wire/swift/SwiftGenerator.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wire-swift-generator/src/main/java/com/squareup/wire/swift/SwiftGenerator.kt b/wire-swift-generator/src/main/java/com/squareup/wire/swift/SwiftGenerator.kt index fce7702c89..56089f55be 100644 --- a/wire-swift-generator/src/main/java/com/squareup/wire/swift/SwiftGenerator.kt +++ b/wire-swift-generator/src/main/java/com/squareup/wire/swift/SwiftGenerator.kt @@ -186,7 +186,8 @@ class SwiftGenerator private constructor( if (type == ProtoType.ANY) return false if (isMessage) { val messageType = schema.getType(type!!) as MessageType - return messageType.supportsEmptyInitialization + + return messageType.supportsEmptyInitialization && messageType.fields.isNotEmpty() } if (isEnum) { val enumType = schema.getType(type!!) as EnumType From e0dd0b2ea8314d83c851b0c90778a1085ba6e412 Mon Sep 17 00:00:00 2001 From: Adam Lickel Date: Mon, 13 Nov 2023 12:34:02 -0800 Subject: [PATCH 2/5] Add unit tests verifying behavior --- ...OneType.swift => SwiftModuleOneEnum.swift} | 6 +- .../module_one/SwiftModuleOneMessage.swift | 82 +++++++++++++++++++ .../SwiftModuleThreeMessage.swift | 20 +++-- .../module_two/SwiftModuleTwoMessage.swift | 29 ++++--- .../proto/kotlin/swift_module_one.proto | 6 +- .../proto/kotlin/swift_module_three.proto | 3 +- .../proto/kotlin/swift_module_two.proto | 3 +- .../proto/kotlin/swift_modules_manifest.yaml | 3 +- 8 files changed, 129 insertions(+), 23 deletions(-) rename wire-tests-swift/manifest/module_one/{SwiftModuleOneType.swift => SwiftModuleOneEnum.swift} (65%) create mode 100644 wire-tests-swift/manifest/module_one/SwiftModuleOneMessage.swift diff --git a/wire-tests-swift/manifest/module_one/SwiftModuleOneType.swift b/wire-tests-swift/manifest/module_one/SwiftModuleOneEnum.swift similarity index 65% rename from wire-tests-swift/manifest/module_one/SwiftModuleOneType.swift rename to wire-tests-swift/manifest/module_one/SwiftModuleOneEnum.swift index c22bdd6d08..d4fedadf3d 100644 --- a/wire-tests-swift/manifest/module_one/SwiftModuleOneType.swift +++ b/wire-tests-swift/manifest/module_one/SwiftModuleOneEnum.swift @@ -1,9 +1,9 @@ // Code generated by Wire protocol buffer compiler, do not edit. -// Source: squareup.protos.kotlin.swift_modules.SwiftModuleOneType in swift_module_one.proto +// Source: squareup.protos.kotlin.swift_modules.SwiftModuleOneEnum in swift_module_one.proto import Foundation import Wire -public enum SwiftModuleOneType : Int32, CaseIterable, ProtoEnum { +public enum SwiftModuleOneEnum : Int32, CaseIterable, ProtoEnum { case DO_NOT_USE = 0 case ONE = 1 @@ -20,6 +20,6 @@ public enum SwiftModuleOneType : Int32, CaseIterable, ProtoEnum { } #if swift(>=5.5) -extension SwiftModuleOneType : Sendable { +extension SwiftModuleOneEnum : Sendable { } #endif diff --git a/wire-tests-swift/manifest/module_one/SwiftModuleOneMessage.swift b/wire-tests-swift/manifest/module_one/SwiftModuleOneMessage.swift new file mode 100644 index 0000000000..b8a1f1bde3 --- /dev/null +++ b/wire-tests-swift/manifest/module_one/SwiftModuleOneMessage.swift @@ -0,0 +1,82 @@ +// Code generated by Wire protocol buffer compiler, do not edit. +// Source: squareup.protos.kotlin.swift_modules.SwiftModuleOneMessage in swift_module_one.proto +import Foundation +import Wire + +public struct SwiftModuleOneMessage { + + public var name: String + public var unknownFields: Foundation.Data = .init() + + public init(name: String) { + self.name = name + } + +} + +#if !WIRE_REMOVE_EQUATABLE +extension SwiftModuleOneMessage : Equatable { +} +#endif + +#if !WIRE_REMOVE_HASHABLE +extension SwiftModuleOneMessage : Hashable { +} +#endif + +#if swift(>=5.5) +extension SwiftModuleOneMessage : Sendable { +} +#endif + +extension SwiftModuleOneMessage : ProtoMessage { + + public static func protoMessageTypeURL() -> Swift.String { + return "type.googleapis.com/squareup.protos.kotlin.swift_modules.SwiftModuleOneMessage" + } + +} + +extension SwiftModuleOneMessage : Proto2Codable { + + public init(from protoReader: Wire.ProtoReader) throws { + var name: Swift.String? = nil + + let token = try protoReader.beginMessage() + while let tag = try protoReader.nextTag(token: token) { + switch tag { + case 1: name = try protoReader.decode(Swift.String.self) + default: try protoReader.readUnknownField(tag: tag) + } + } + self.unknownFields = try protoReader.endMessage(token: token) + + self.name = try SwiftModuleOneMessage.checkIfMissing(name, "name") + } + + public func encode(to protoWriter: Wire.ProtoWriter) throws { + try protoWriter.encode(tag: 1, value: self.name) + try protoWriter.writeUnknownFields(unknownFields) + } + +} + +#if !WIRE_REMOVE_CODABLE +extension SwiftModuleOneMessage : Codable { + + public init(from decoder: Swift.Decoder) throws { + let container = try decoder.container(keyedBy: Wire.StringLiteralCodingKeys.self) + self.name = try container.decode(Swift.String.self, forKey: "name") + } + + public func encode(to encoder: Swift.Encoder) throws { + var container = encoder.container(keyedBy: Wire.StringLiteralCodingKeys.self) + let includeDefaults = encoder.protoDefaultValuesEncodingStrategy == .include + + if includeDefaults || !self.name.isEmpty { + try container.encode(self.name, forKey: "name") + } + } + +} +#endif diff --git a/wire-tests-swift/manifest/module_three/SwiftModuleThreeMessage.swift b/wire-tests-swift/manifest/module_three/SwiftModuleThreeMessage.swift index 2acd718e71..e0547abea8 100644 --- a/wire-tests-swift/manifest/module_three/SwiftModuleThreeMessage.swift +++ b/wire-tests-swift/manifest/module_three/SwiftModuleThreeMessage.swift @@ -106,7 +106,8 @@ extension SwiftModuleThreeMessage { public struct NestedMessage { - public var map_string_types: [Swift.String : module_one.SwiftModuleOneType] = [:] + public var map_string_types: [Swift.String : module_one.SwiftModuleOneEnum] = [:] + public var module_type: module_one.SwiftModuleOneMessage? public var unknownFields: Foundation.Data = .init() public init(configure: (inout Self) -> Swift.Void = { _ in }) { @@ -122,8 +123,9 @@ extension SwiftModuleThreeMessage.NestedMessage { @_disfavoredOverload @available(*, deprecated) - public init(map_string_types: [Swift.String : module_one.SwiftModuleOneType] = [:]) { + public init(map_string_types: [Swift.String : module_one.SwiftModuleOneEnum] = [:], module_type: module_one.SwiftModuleOneMessage? = nil) { self.map_string_types = map_string_types + self.module_type = module_type } } @@ -162,22 +164,26 @@ extension SwiftModuleThreeMessage.NestedMessage : ProtoMessage { extension SwiftModuleThreeMessage.NestedMessage : Proto2Codable { public init(from protoReader: Wire.ProtoReader) throws { - var map_string_types: [Swift.String : module_one.SwiftModuleOneType] = [:] + var map_string_types: [Swift.String : module_one.SwiftModuleOneEnum] = [:] + var module_type: module_one.SwiftModuleOneMessage? = nil let token = try protoReader.beginMessage() while let tag = try protoReader.nextTag(token: token) { switch tag { - case 2: try protoReader.decode(into: &map_string_types) + case 1: try protoReader.decode(into: &map_string_types) + case 2: module_type = try protoReader.decode(module_one.SwiftModuleOneMessage.self) default: try protoReader.readUnknownField(tag: tag) } } self.unknownFields = try protoReader.endMessage(token: token) self.map_string_types = map_string_types + self.module_type = module_type } public func encode(to protoWriter: Wire.ProtoWriter) throws { - try protoWriter.encode(tag: 2, value: self.map_string_types) + try protoWriter.encode(tag: 1, value: self.map_string_types) + try protoWriter.encode(tag: 2, value: self.module_type) try protoWriter.writeUnknownFields(unknownFields) } @@ -188,7 +194,8 @@ extension SwiftModuleThreeMessage.NestedMessage : Codable { public init(from decoder: Swift.Decoder) throws { let container = try decoder.container(keyedBy: Wire.StringLiteralCodingKeys.self) - self.map_string_types = try container.decodeProtoMap([Swift.String : module_one.SwiftModuleOneType].self, firstOfKeys: "mapStringTypes", "map_string_types") + self.map_string_types = try container.decodeProtoMap([Swift.String : module_one.SwiftModuleOneEnum].self, firstOfKeys: "mapStringTypes", "map_string_types") + self.module_type = try container.decodeIfPresent(module_one.SwiftModuleOneMessage.self, firstOfKeys: "moduleType", "module_type") } public func encode(to encoder: Swift.Encoder) throws { @@ -199,6 +206,7 @@ extension SwiftModuleThreeMessage.NestedMessage : Codable { if includeDefaults || !self.map_string_types.isEmpty { try container.encodeProtoMap(self.map_string_types, forKey: preferCamelCase ? "mapStringTypes" : "map_string_types") } + try container.encodeIfPresent(self.module_type, forKey: preferCamelCase ? "moduleType" : "module_type") } } diff --git a/wire-tests-swift/manifest/module_two/SwiftModuleTwoMessage.swift b/wire-tests-swift/manifest/module_two/SwiftModuleTwoMessage.swift index 1b70425176..57fd8b9f96 100644 --- a/wire-tests-swift/manifest/module_two/SwiftModuleTwoMessage.swift +++ b/wire-tests-swift/manifest/module_two/SwiftModuleTwoMessage.swift @@ -106,7 +106,8 @@ extension SwiftModuleTwoMessage { public struct NestedMessage { - public var types: [module_one.SwiftModuleOneType] = [] + public var array_types: [module_one.SwiftModuleOneEnum] = [] + public var module_type: module_one.SwiftModuleOneMessage? public var unknownFields: Foundation.Data = .init() public init(configure: (inout Self) -> Swift.Void = { _ in }) { @@ -122,8 +123,9 @@ extension SwiftModuleTwoMessage.NestedMessage { @_disfavoredOverload @available(*, deprecated) - public init(types: [module_one.SwiftModuleOneType] = []) { - self.types = types + public init(array_types: [module_one.SwiftModuleOneEnum] = [], module_type: module_one.SwiftModuleOneMessage? = nil) { + self.array_types = array_types + self.module_type = module_type } } @@ -162,22 +164,26 @@ extension SwiftModuleTwoMessage.NestedMessage : ProtoMessage { extension SwiftModuleTwoMessage.NestedMessage : Proto2Codable { public init(from protoReader: Wire.ProtoReader) throws { - var types: [module_one.SwiftModuleOneType] = [] + var array_types: [module_one.SwiftModuleOneEnum] = [] + var module_type: module_one.SwiftModuleOneMessage? = nil let token = try protoReader.beginMessage() while let tag = try protoReader.nextTag(token: token) { switch tag { - case 2: try protoReader.decode(into: &types) + case 1: try protoReader.decode(into: &array_types) + case 2: module_type = try protoReader.decode(module_one.SwiftModuleOneMessage.self) default: try protoReader.readUnknownField(tag: tag) } } self.unknownFields = try protoReader.endMessage(token: token) - self.types = types + self.array_types = array_types + self.module_type = module_type } public func encode(to protoWriter: Wire.ProtoWriter) throws { - try protoWriter.encode(tag: 2, value: self.types) + try protoWriter.encode(tag: 1, value: self.array_types) + try protoWriter.encode(tag: 2, value: self.module_type) try protoWriter.writeUnknownFields(unknownFields) } @@ -188,16 +194,19 @@ extension SwiftModuleTwoMessage.NestedMessage : Codable { public init(from decoder: Swift.Decoder) throws { let container = try decoder.container(keyedBy: Wire.StringLiteralCodingKeys.self) - self.types = try container.decodeProtoArray(module_one.SwiftModuleOneType.self, forKey: "types") + self.array_types = try container.decodeProtoArray(module_one.SwiftModuleOneEnum.self, firstOfKeys: "arrayTypes", "array_types") + self.module_type = try container.decodeIfPresent(module_one.SwiftModuleOneMessage.self, firstOfKeys: "moduleType", "module_type") } public func encode(to encoder: Swift.Encoder) throws { var container = encoder.container(keyedBy: Wire.StringLiteralCodingKeys.self) + let preferCamelCase = encoder.protoKeyNameEncodingStrategy == .camelCase let includeDefaults = encoder.protoDefaultValuesEncodingStrategy == .include - if includeDefaults || !self.types.isEmpty { - try container.encodeProtoArray(self.types, forKey: "types") + if includeDefaults || !self.array_types.isEmpty { + try container.encodeProtoArray(self.array_types, forKey: preferCamelCase ? "arrayTypes" : "array_types") } + try container.encodeIfPresent(self.module_type, forKey: preferCamelCase ? "moduleType" : "module_type") } } diff --git a/wire-tests/src/commonTest/proto/kotlin/swift_module_one.proto b/wire-tests/src/commonTest/proto/kotlin/swift_module_one.proto index b875dc3316..0fb9ad4d43 100644 --- a/wire-tests/src/commonTest/proto/kotlin/swift_module_one.proto +++ b/wire-tests/src/commonTest/proto/kotlin/swift_module_one.proto @@ -17,8 +17,12 @@ syntax = "proto2"; package squareup.protos.kotlin.swift_modules; -enum SwiftModuleOneType { +enum SwiftModuleOneEnum { DO_NOT_USE = 0; ONE = 1; TWO = 2; } + +message SwiftModuleOneMessage { + required string name = 1; +} diff --git a/wire-tests/src/commonTest/proto/kotlin/swift_module_three.proto b/wire-tests/src/commonTest/proto/kotlin/swift_module_three.proto index 53555a481f..2557e13c12 100644 --- a/wire-tests/src/commonTest/proto/kotlin/swift_module_three.proto +++ b/wire-tests/src/commonTest/proto/kotlin/swift_module_three.proto @@ -23,6 +23,7 @@ message SwiftModuleThreeMessage { optional string name = 1; message NestedMessage { - map map_string_types = 2; + map map_string_types = 1; + optional SwiftModuleOneMessage module_type = 2; } } diff --git a/wire-tests/src/commonTest/proto/kotlin/swift_module_two.proto b/wire-tests/src/commonTest/proto/kotlin/swift_module_two.proto index aef0a83faf..b199dca1ba 100644 --- a/wire-tests/src/commonTest/proto/kotlin/swift_module_two.proto +++ b/wire-tests/src/commonTest/proto/kotlin/swift_module_two.proto @@ -23,6 +23,7 @@ message SwiftModuleTwoMessage { optional string name = 1; message NestedMessage { - repeated SwiftModuleOneType types = 2; + repeated SwiftModuleOneEnum array_types = 1; + optional SwiftModuleOneMessage module_type = 2; } } diff --git a/wire-tests/src/commonTest/proto/kotlin/swift_modules_manifest.yaml b/wire-tests/src/commonTest/proto/kotlin/swift_modules_manifest.yaml index c3eabdb411..6d40f076e8 100644 --- a/wire-tests/src/commonTest/proto/kotlin/swift_modules_manifest.yaml +++ b/wire-tests/src/commonTest/proto/kotlin/swift_modules_manifest.yaml @@ -19,7 +19,8 @@ --- module_one: roots: - - squareup.protos.kotlin.swift_modules.SwiftModuleOneType + - squareup.protos.kotlin.swift_modules.SwiftModuleOneEnum + - squareup.protos.kotlin.swift_modules.SwiftModuleOneMessage module_two: roots: - squareup.protos.kotlin.swift_modules.SwiftModuleTwoMessage From 3e3dd88370da2ed1743cdc47f39790b8868b742c Mon Sep 17 00:00:00 2001 From: Adam Lickel Date: Mon, 13 Nov 2023 12:37:30 -0800 Subject: [PATCH 3/5] Changelog --- CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0afc8d9382..9579426fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Change Log ========== +Version 4.9.3 +------------- + +_Unreleased_ + +* Fix: Swift `ProtoDefaulted` was incorrectly applied in cross-module contexts + Version 4.9.2 ------------- @@ -12,9 +19,6 @@ _2023-11-13_ not use. Note that scalar types cannot be opaqued. * New: Adds a closure into generate types allowing the creation of an instance via the Kotlin DSL. * Fix: Don't arbitrarily prune `oneOf` options. - -### Swift - * Change: Swift `Defaulted` has been renamed `CustomDefaulted` * New: Swift `ProtoDefaulted` property wrapper and `ProtoDefaultedValue` protocol * Similar to `CustomDefaulted, this adds as projection of the protocol defined default value From 87f2d3f9dedeab978aaba12626572ae818dbffee Mon Sep 17 00:00:00 2001 From: Adam Lickel Date: Mon, 13 Nov 2023 12:21:23 -0800 Subject: [PATCH 4/5] Test harness tweaks --- .../src/test/proto/negative_value_enum.proto | 4 +- .../test/swift/ProtoEnumCodableTests.swift | 8 +++- .../src/test/swift/ProtoEnumTests.swift | 39 +++++++++++++++++++ .../src/test/swift/RoundTripTests.swift | 11 ------ 4 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 wire-runtime-swift/src/test/swift/ProtoEnumTests.swift diff --git a/wire-runtime-swift/src/test/proto/negative_value_enum.proto b/wire-runtime-swift/src/test/proto/negative_value_enum.proto index 83a4d46c67..07afb00eab 100644 --- a/wire-runtime-swift/src/test/proto/negative_value_enum.proto +++ b/wire-runtime-swift/src/test/proto/negative_value_enum.proto @@ -22,5 +22,7 @@ message NegativeValueMessage { } enum NegativeValueEnum { - DO_NOT_USE = -1; + NEGATIVE_VALUE = -1; + ZERO_VALUE = 0; + POSITIVE_VALUE = 1; } diff --git a/wire-runtime-swift/src/test/swift/ProtoEnumCodableTests.swift b/wire-runtime-swift/src/test/swift/ProtoEnumCodableTests.swift index 2c53ac1bc0..f674c299db 100644 --- a/wire-runtime-swift/src/test/swift/ProtoEnumCodableTests.swift +++ b/wire-runtime-swift/src/test/swift/ProtoEnumCodableTests.swift @@ -94,7 +94,9 @@ extension ProtoEnumCodableTests { } func testEncodingNegativeValueEnum() throws { - let expectedStruct = NegativeValueMessage { $0.value = NegativeValueEnum.DO_NOT_USE } + let expectedStruct = NegativeValueMessage { + $0.value = NegativeValueEnum.NEGATIVE_VALUE + } let expectedJson = """ {\ @@ -184,7 +186,9 @@ extension ProtoEnumCodableTests { } func testDecodingNegativeValueEnum() throws { - let expectedStruct = NegativeValueMessage { $0.value = NegativeValueEnum.DO_NOT_USE } + let expectedStruct = NegativeValueMessage { + $0.value = NegativeValueEnum.NEGATIVE_VALUE + } let json = """ {\ diff --git a/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift b/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift new file mode 100644 index 0000000000..acd4dc3b09 --- /dev/null +++ b/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import Foundation + +import Foundation +import XCTest +@testable import Wire + +final class ProtoEnumTests: XCTestCase { + + func testEnumValuesRoundTrip() throws { + let encoder = ProtoEncoder() + let decoder = ProtoDecoder() + + for value in NegativeValueEnum.allCases { + let message = NegativeValueMessage { + $0.value = value + } + let data = try encoder.encode(message) + let decodedMessage = try decoder.decode(NegativeValueMessage.self, from: data) + + XCTAssertEqual(decodedMessage, message, "Could not roundtrip \(value)") + } + } + +} diff --git a/wire-runtime-swift/src/test/swift/RoundTripTests.swift b/wire-runtime-swift/src/test/swift/RoundTripTests.swift index 421f70c705..892e723127 100644 --- a/wire-runtime-swift/src/test/swift/RoundTripTests.swift +++ b/wire-runtime-swift/src/test/swift/RoundTripTests.swift @@ -19,17 +19,6 @@ import XCTest final class RoundTripTests: XCTestCase { - func testNegativeValueEnumRoundTrip() throws { - let negativeValueMessage = NegativeValueMessage { $0.value = NegativeValueEnum.DO_NOT_USE } - let encoder = ProtoEncoder() - let data = try encoder.encode(negativeValueMessage) - - let decoder = ProtoDecoder() - let decodedNegativeValueMessage = try decoder.decode(NegativeValueMessage.self, from: data) - - XCTAssertEqual(decodedNegativeValueMessage, negativeValueMessage) - } - func testPersonEncodeDecode() throws { let personData = Data(json_data: "") let person = Person(name: "Luke Skywalker", id: 42, data: personData) { From be119ba4489edfe7dd63eeac9eaada8e364aebf4 Mon Sep 17 00:00:00 2001 From: Adam Lickel Date: Mon, 13 Nov 2023 13:01:57 -0800 Subject: [PATCH 5/5] More tests --- .../src/test/proto/negative_value_enum.proto | 4 +++ .../src/test/swift/ProtoEnumTests.swift | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/wire-runtime-swift/src/test/proto/negative_value_enum.proto b/wire-runtime-swift/src/test/proto/negative_value_enum.proto index 07afb00eab..aefb04bb5f 100644 --- a/wire-runtime-swift/src/test/proto/negative_value_enum.proto +++ b/wire-runtime-swift/src/test/proto/negative_value_enum.proto @@ -26,3 +26,7 @@ enum NegativeValueEnum { ZERO_VALUE = 0; POSITIVE_VALUE = 1; } + +message RawNegativeValueMessage { + optional int32 value = 1; +} diff --git a/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift b/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift index acd4dc3b09..828fcd411b 100644 --- a/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift +++ b/wire-runtime-swift/src/test/swift/ProtoEnumTests.swift @@ -29,6 +29,7 @@ final class ProtoEnumTests: XCTestCase { let message = NegativeValueMessage { $0.value = value } + let data = try encoder.encode(message) let decodedMessage = try decoder.decode(NegativeValueMessage.self, from: data) @@ -36,4 +37,36 @@ final class ProtoEnumTests: XCTestCase { } } + func testEnumValuesDecodeAsInt32() throws { + let encoder = ProtoEncoder() + let decoder = ProtoDecoder() + + for value in NegativeValueEnum.allCases { + let message = NegativeValueMessage { + $0.value = value + } + + let data = try encoder.encode(message) + let decodedMessage = try decoder.decode(RawNegativeValueMessage.self, from: data) + + XCTAssertEqual(decodedMessage.value, value.rawValue, "Could not roundtrip \(value)") + } + } + + func testInt32ValuesDecodeAsEnumValues() throws { + let encoder = ProtoEncoder() + let decoder = ProtoDecoder() + + for value in NegativeValueEnum.allCases { + let message = RawNegativeValueMessage { + $0.value = value.rawValue + } + + let data = try encoder.encode(message) + let decodedMessage = try decoder.decode(NegativeValueMessage.self, from: data) + + XCTAssertEqual(decodedMessage.value, value, "Could not roundtrip \(value)") + } + } + }