Created
June 26, 2023 17:58
-
-
Save willtemperley/ed780f3977b70064f6f878e4a9353c4d to your computer and use it in GitHub Desktop.
Demonstrates Issue 1410 in Swift Protobuf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SwiftProtobufIssue1410Tests.swift | |
// | |
// Created by Will Temperley on 26/06/2023. | |
// | |
import Foundation | |
import XCTest | |
import SwiftProtobuf | |
class ParseBinaryDelimitedProtobufFileTest: XCTestCase { | |
/** | |
Creates a URL for a temporary file on disk. Registers a teardown block to | |
delete a file at that URL (if one exists) during test teardown. | |
*/ | |
func temporaryFileURL() -> URL { | |
let directory = NSTemporaryDirectory() | |
let filename = UUID().uuidString | |
let fileURL = URL(fileURLWithPath: directory).appendingPathComponent(filename) | |
addTeardownBlock { | |
do { | |
let fileManager = FileManager.default | |
if fileManager.fileExists(atPath: fileURL.path) { | |
try fileManager.removeItem(at: fileURL) | |
XCTAssertFalse(fileManager.fileExists(atPath: fileURL.path)) | |
} | |
} catch { | |
XCTFail("Error while deleting temporary file: \(error)") | |
} | |
} | |
return fileURL | |
} | |
func writeTestProtobuf(_ fileURL: URL) throws { | |
let outputStream = OutputStream(url: fileURL, append: false)! | |
outputStream.open() | |
for index in 1...1000 { | |
var message = SearchRequest() | |
message.pageNumber = Int64(index) | |
message.resultsPerPage = 100 | |
try BinaryDelimited.serialize(message: message, to: outputStream) | |
} | |
outputStream.close() | |
} | |
//Reading directly from a file fails with a truncated error | |
func testReadDirectFromFile() throws { | |
let fileURL = temporaryFileURL() | |
try writeTestProtobuf(fileURL) | |
let inputStream = InputStream(url: fileURL)! | |
inputStream.open() | |
var messages = [SearchRequest]() | |
while (inputStream.hasBytesAvailable) { | |
let message = try BinaryDelimited.parse(messageType: SearchRequest.self, from: inputStream) | |
messages.append(message) | |
} | |
inputStream.close() | |
assert(messages.count == 1000) | |
} | |
//Reading the file contents into a data object first runs fine | |
func testReadFileContentsToData() throws { | |
let fileURL = temporaryFileURL() | |
try writeTestProtobuf(fileURL) | |
let data = try Data(contentsOf: fileURL) | |
let inputStream = InputStream(data: data) | |
inputStream.open() | |
var messages = [SearchRequest]() | |
while (inputStream.hasBytesAvailable) { | |
let message = try BinaryDelimited.parse(messageType: SearchRequest.self, from: inputStream) | |
messages.append(message) | |
} | |
inputStream.close() | |
assert(messages.count == 1000) | |
} | |
} | |
// If the compiler emits an error on this type, it is because this file | |
// was generated by a version of the `protoc` Swift plug-in that is | |
// incompatible with the version of SwiftProtobuf to which you are linking. | |
// Please ensure that you are building against the same version of the API | |
// that was used to generate this file. | |
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { | |
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} | |
typealias Version = _2 | |
} | |
struct SearchRequest { | |
// SwiftProtobuf.Message conformance is added in an extension below. See the | |
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for | |
// methods supported on all messages. | |
var query: String = String() | |
var pageNumber: Int64 = 0 | |
var resultsPerPage: Int64 = 0 | |
var unknownFields = SwiftProtobuf.UnknownStorage() | |
init() {} | |
} | |
#if swift(>=5.5) && canImport(_Concurrency) | |
extension SearchRequest: @unchecked Sendable {} | |
#endif // swift(>=5.5) && canImport(_Concurrency) | |
// MARK: - Code below here is support for the SwiftProtobuf runtime. | |
extension SearchRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { | |
static let protoMessageName: String = "SearchRequest" | |
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ | |
1: .same(proto: "query"), | |
2: .standard(proto: "page_number"), | |
3: .standard(proto: "results_per_page"), | |
] | |
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws { | |
while let fieldNumber = try decoder.nextFieldNumber() { | |
// The use of inline closures is to circumvent an issue where the compiler | |
// allocates stack space for every case branch when no optimizations are | |
// enabled. https://github.com/apple/swift-protobuf/issues/1034 | |
switch fieldNumber { | |
case 1: try { try decoder.decodeSingularStringField(value: &self.query) }() | |
case 2: try { try decoder.decodeSingularInt64Field(value: &self.pageNumber) }() | |
case 3: try { try decoder.decodeSingularInt64Field(value: &self.resultsPerPage) }() | |
default: break | |
} | |
} | |
} | |
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws { | |
if !self.query.isEmpty { | |
try visitor.visitSingularStringField(value: self.query, fieldNumber: 1) | |
} | |
if self.pageNumber != 0 { | |
try visitor.visitSingularInt64Field(value: self.pageNumber, fieldNumber: 2) | |
} | |
if self.resultsPerPage != 0 { | |
try visitor.visitSingularInt64Field(value: self.resultsPerPage, fieldNumber: 3) | |
} | |
try unknownFields.traverse(visitor: &visitor) | |
} | |
static func ==(lhs: SearchRequest, rhs: SearchRequest) -> Bool { | |
if lhs.query != rhs.query {return false} | |
if lhs.pageNumber != rhs.pageNumber {return false} | |
if lhs.resultsPerPage != rhs.resultsPerPage {return false} | |
if lhs.unknownFields != rhs.unknownFields {return false} | |
return true | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment