1// Protocol Buffers - Google's data interchange format 2// Copyright 2015 Google Inc. All rights reserved. 3// 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file or at 6// https://developers.google.com/open-source/licenses/bsd 7 8#import <Foundation/Foundation.h> 9 10#import "Conformance.pbobjc.h" 11#import "editions/golden/TestMessagesProto2Editions.pbobjc.h" 12#import "editions/golden/TestMessagesProto3Editions.pbobjc.h" 13#import "google/protobuf/TestMessagesProto2.pbobjc.h" 14#import "google/protobuf/TestMessagesProto3.pbobjc.h" 15#import "test_protos/TestMessagesEdition2023.pbobjc.h" 16 17static void Die(NSString *format, ...) __dead2; 18 19static BOOL verbose = NO; 20static int32_t testCount = 0; 21 22static void Die(NSString *format, ...) { 23 va_list args; 24 va_start(args, format); 25 NSString *msg = [[NSString alloc] initWithFormat:format arguments:args]; 26 NSLog(@"%@", msg); 27 va_end(args); 28 [msg release]; 29 exit(66); 30} 31 32static NSData *CheckedReadDataOfLength(NSFileHandle *handle, NSUInteger numBytes) { 33 NSData *data = [handle readDataOfLength:numBytes]; 34 NSUInteger dataLen = data.length; 35 if (dataLen == 0) { 36 return nil; // EOF. 37 } 38 if (dataLen != numBytes) { 39 Die(@"Failed to read the request length (%d), only got: %@", numBytes, data); 40 } 41 return data; 42} 43 44static ConformanceResponse *DoTest(ConformanceRequest *request) { 45 ConformanceResponse *response = [ConformanceResponse message]; 46 GPBMessage *testMessage = nil; 47 48 switch (request.payloadOneOfCase) { 49 case ConformanceRequest_Payload_OneOfCase_GPBUnsetOneOfCase: 50 response.runtimeError = 51 [NSString stringWithFormat:@"Request didn't have a payload: %@", request]; 52 break; 53 54 case ConformanceRequest_Payload_OneOfCase_ProtobufPayload: { 55 NSDictionary *mappings = @{ 56 @"protobuf_test_messages.proto2.TestAllTypesProto2" : [Proto2TestAllTypesProto2 class], 57 @"protobuf_test_messages.proto3.TestAllTypesProto3" : [Proto3TestAllTypesProto3 class], 58 @"protobuf_test_messages.editions.TestAllTypesEdition2023" : 59 [EditionsTestAllTypesEdition2023 class], 60 @"protobuf_test_messages.editions.proto2.TestAllTypesProto2" : 61 [EditionsProto2TestAllTypesProto2 class], 62 @"protobuf_test_messages.editions.proto3.TestAllTypesProto3" : 63 [EditionsProto3TestAllTypesProto3 class], 64 }; 65 Class msgClass = mappings[request.messageType]; 66 if (msgClass) { 67 NSError *error = nil; 68 testMessage = [msgClass parseFromData:request.protobufPayload error:&error]; 69 if (!testMessage) { 70 response.parseError = [NSString stringWithFormat:@"Parse error: %@", error]; 71 } 72 } else { 73 response.runtimeError = 74 [NSString stringWithFormat:@"Protobuf request had an unknown message_type: %@", 75 request.messageType]; 76 } 77 break; 78 } 79 80 case ConformanceRequest_Payload_OneOfCase_JsonPayload: 81 response.skipped = @"ObjC doesn't support parsing JSON"; 82 break; 83 84 case ConformanceRequest_Payload_OneOfCase_JspbPayload: 85 response.skipped = @"ConformanceRequest had a jspb_payload ConformanceRequest.payload;" 86 " those aren't supposed to happen with opensource."; 87 break; 88 89 case ConformanceRequest_Payload_OneOfCase_TextPayload: 90 response.skipped = @"ObjC doesn't support parsing TextFormat"; 91 break; 92 } 93 94 if (testMessage) { 95 switch (request.requestedOutputFormat) { 96 case ConformanceWireFormat_GPBUnrecognizedEnumeratorValue: 97 case ConformanceWireFormat_Unspecified: 98 response.runtimeError = 99 [NSString stringWithFormat:@"Unrecognized/unspecified output format: %@", request]; 100 break; 101 102 case ConformanceWireFormat_Protobuf: 103 response.protobufPayload = testMessage.data; 104 if (!response.protobufPayload) { 105 response.serializeError = 106 [NSString stringWithFormat:@"Failed to make data from: %@", testMessage]; 107 } 108 break; 109 110 case ConformanceWireFormat_Json: 111 response.skipped = @"ObjC doesn't support generating JSON"; 112 break; 113 114 case ConformanceWireFormat_Jspb: 115 response.skipped = 116 @"ConformanceRequest had a requested_output_format of JSPB WireFormat; that" 117 " isn't supposed to happen with opensource."; 118 break; 119 120 case ConformanceWireFormat_TextFormat: 121 // ObjC only has partial objc generation, so don't attempt any tests that need 122 // support. 123 response.skipped = @"ObjC doesn't support generating TextFormat"; 124 break; 125 } 126 } 127 128 return response; 129} 130 131static uint32_t UInt32FromLittleEndianData(NSData *data) { 132 if (data.length != sizeof(uint32_t)) { 133 Die(@"Data not the right size for uint32_t: %@", data); 134 } 135 uint32_t value; 136 memcpy(&value, data.bytes, sizeof(uint32_t)); 137 return CFSwapInt32LittleToHost(value); 138} 139 140static NSData *UInt32ToLittleEndianData(uint32_t num) { 141 uint32_t value = CFSwapInt32HostToLittle(num); 142 return [NSData dataWithBytes:&value length:sizeof(uint32_t)]; 143} 144 145static BOOL DoTestIo(NSFileHandle *input, NSFileHandle *output) { 146 // See conformance_test_runner.cc for the wire format. 147 NSData *data = CheckedReadDataOfLength(input, sizeof(uint32_t)); 148 if (!data) { 149 // EOF. 150 return NO; 151 } 152 uint32_t numBytes = UInt32FromLittleEndianData(data); 153 data = CheckedReadDataOfLength(input, numBytes); 154 if (!data) { 155 Die(@"Failed to read request"); 156 } 157 158 NSError *error = nil; 159 ConformanceRequest *request = [ConformanceRequest parseFromData:data error:&error]; 160 if (!request) { 161 Die(@"Failed to parse the message data: %@", error); 162 } 163 164 ConformanceResponse *response = DoTest(request); 165 if (!response) { 166 Die(@"Failed to make a reply from %@", request); 167 } 168 169 data = response.data; 170 [output writeData:UInt32ToLittleEndianData((int32_t)data.length)]; 171 [output writeData:data]; 172 173 if (verbose) { 174 NSLog(@"Request: %@", request); 175 NSLog(@"Response: %@", response); 176 } 177 178 ++testCount; 179 return YES; 180} 181 182int main(int argc, const char *argv[]) { 183 @autoreleasepool { 184 NSFileHandle *input = [[NSFileHandle fileHandleWithStandardInput] retain]; 185 NSFileHandle *output = [[NSFileHandle fileHandleWithStandardOutput] retain]; 186 187 BOOL notDone = YES; 188 while (notDone) { 189 @autoreleasepool { 190 notDone = DoTestIo(input, output); 191 } 192 } 193 194 NSLog(@"Received EOF from test runner after %d tests, exiting.", testCount); 195 } 196 return 0; 197} 198