Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Swift: Fix for @ProtoDefaulted #2706

Merged
merged 5 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
-------------

Expand All @@ -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
Expand Down
8 changes: 7 additions & 1 deletion wire-runtime-swift/src/test/proto/negative_value_enum.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ message NegativeValueMessage {
}

enum NegativeValueEnum {
DO_NOT_USE = -1;
NEGATIVE_VALUE = -1;
ZERO_VALUE = 0;
POSITIVE_VALUE = 1;
}

message RawNegativeValueMessage {
optional int32 value = 1;
}
8 changes: 6 additions & 2 deletions wire-runtime-swift/src/test/swift/ProtoEnumCodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 = """
{\
Expand Down Expand Up @@ -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 = """
{\
Expand Down
72 changes: 72 additions & 0 deletions wire-runtime-swift/src/test/swift/ProtoEnumTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are new tests for other work that happened in v4.9.2.


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)")
}
}

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)")
}
}

}
11 changes: 0 additions & 11 deletions wire-runtime-swift/src/test/swift/RoundTripTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix/change. I believe it is the correct fix overall if for any reason we cannot load the fields of a message we do not know whether we can provide a default value for it.

}
if (isEnum) {
val enumType = schema.getType(type!!) as EnumType
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -20,6 +20,6 @@ public enum SwiftModuleOneType : Int32, CaseIterable, ProtoEnum {
}

#if swift(>=5.5)
extension SwiftModuleOneType : Sendable {
extension SwiftModuleOneEnum : Sendable {
}
#endif
82 changes: 82 additions & 0 deletions wire-tests-swift/manifest/module_one/SwiftModuleOneMessage.swift
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 }) {
Expand All @@ -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
}

}
Expand Down Expand Up @@ -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)
}

Expand All @@ -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 {
Expand All @@ -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")
}

}
Expand Down
Loading
Loading