1 #region Copyright notice and license 2 // Protocol Buffers - Google's data interchange format 3 // Copyright 2015 Google Inc. All rights reserved. 4 // 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file or at 7 // https://developers.google.com/open-source/licenses/bsd 8 #endregion 9 10 using Conformance; 11 using Google.Protobuf.Reflection; 12 using System; 13 using System.IO; 14 15 namespace Google.Protobuf.Conformance 16 { 17 /// <summary> 18 /// Conformance tests. The test runner will provide JSON or proto data on stdin, 19 /// and this program will produce its output on stdout. 20 /// </summary> 21 class Program 22 { Main()23 private static void Main() 24 { 25 // This way we get the binary streams instead of readers/writers. 26 var input = new BinaryReader(Console.OpenStandardInput()); 27 var output = new BinaryWriter(Console.OpenStandardOutput()); 28 var typeRegistry = TypeRegistry.FromMessages( 29 ProtobufTestMessages.Proto3.TestAllTypesProto3.Descriptor, 30 ProtobufTestMessages.Proto2.TestAllTypesProto2.Descriptor, 31 ProtobufTestMessages.Editions.TestAllTypesEdition2023.Descriptor, 32 ProtobufTestMessages.Editions.Proto3.TestAllTypesProto3.Descriptor, 33 ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Descriptor); 34 35 int count = 0; 36 while (RunTest(input, output, typeRegistry)) 37 { 38 count++; 39 } 40 Console.Error.WriteLine("Received EOF after {0} tests", count); 41 } 42 RunTest(BinaryReader input, BinaryWriter output, TypeRegistry typeRegistry)43 private static bool RunTest(BinaryReader input, BinaryWriter output, TypeRegistry typeRegistry) 44 { 45 int? size = ReadInt32(input); 46 if (size == null) 47 { 48 return false; 49 } 50 byte[] inputData = input.ReadBytes(size.Value); 51 if (inputData.Length != size.Value) 52 { 53 throw new EndOfStreamException("Read " + inputData.Length + " bytes of data when expecting " + size); 54 } 55 ConformanceRequest request = ConformanceRequest.Parser.ParseFrom(inputData); 56 ConformanceResponse response = PerformRequest(request, typeRegistry); 57 byte[] outputData = response.ToByteArray(); 58 output.Write(outputData.Length); 59 output.Write(outputData); 60 // Ready for another test... 61 return true; 62 } 63 PerformRequest(ConformanceRequest request, TypeRegistry typeRegistry)64 private static ConformanceResponse PerformRequest(ConformanceRequest request, TypeRegistry typeRegistry) 65 { 66 ExtensionRegistry proto2ExtensionRegistry = new ExtensionRegistry 67 { 68 ProtobufTestMessages.Proto2.TestMessagesProto2Extensions.ExtensionInt32, 69 ProtobufTestMessages.Proto2.TestAllTypesProto2.Types.MessageSetCorrectExtension1.Extensions.MessageSetExtension, 70 ProtobufTestMessages.Proto2.TestAllTypesProto2.Types.MessageSetCorrectExtension2.Extensions.MessageSetExtension 71 }; 72 ExtensionRegistry editionsProto2ExtensionRegistry = new ExtensionRegistry 73 { 74 ProtobufTestMessages.Editions.Proto2.TestMessagesProto2EditionsExtensions 75 .ExtensionInt32, 76 ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Types 77 .MessageSetCorrectExtension1.Extensions.MessageSetExtension, 78 ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Types 79 .MessageSetCorrectExtension2.Extensions.MessageSetExtension 80 }; 81 ExtensionRegistry edition2023ExtensionRegistry = new ExtensionRegistry { 82 ProtobufTestMessages.Editions.TestMessagesEdition2023Extensions.ExtensionInt32, 83 ProtobufTestMessages.Editions.TestMessagesEdition2023Extensions.DelimitedExt, 84 ProtobufTestMessages.Editions.TestMessagesEdition2023Extensions.GroupLikeType 85 }; 86 IMessage message; 87 try 88 { 89 switch (request.PayloadCase) 90 { 91 case ConformanceRequest.PayloadOneofCase.JsonPayload: 92 bool ignoreUnknownFields = request.TestCategory == global::Conformance.TestCategory.JsonIgnoreUnknownParsingTest; 93 JsonParser parser = new JsonParser(new JsonParser.Settings(20, typeRegistry).WithIgnoreUnknownFields(ignoreUnknownFields)); 94 message = request.MessageType switch 95 { 96 "protobuf_test_messages.proto2.TestAllTypesProto2" => 97 parser.Parse<ProtobufTestMessages.Proto2.TestAllTypesProto2>( 98 request.JsonPayload), 99 "protobuf_test_messages.proto3.TestAllTypesProto3" => 100 parser.Parse<ProtobufTestMessages.Proto3.TestAllTypesProto3>( 101 request.JsonPayload), 102 "protobuf_test_messages.editions.TestAllTypesEdition2023" => 103 parser.Parse<ProtobufTestMessages.Editions.TestAllTypesEdition2023>( 104 request.JsonPayload), 105 "protobuf_test_messages.editions.proto2.TestAllTypesProto2" => 106 parser.Parse<ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2>( 107 request.JsonPayload), 108 "protobuf_test_messages.editions.proto3.TestAllTypesProto3" => 109 parser.Parse<ProtobufTestMessages.Editions.Proto3.TestAllTypesProto3>( 110 request.JsonPayload), 111 _ => throw new Exception( 112 $" Protobuf request doesn't have specific payload type ({request.MessageType})"), 113 }; 114 break; 115 case ConformanceRequest.PayloadOneofCase.ProtobufPayload: 116 message = request.MessageType switch 117 { 118 "protobuf_test_messages.proto2.TestAllTypesProto2" => 119 ProtobufTestMessages.Proto2.TestAllTypesProto2.Parser 120 .WithExtensionRegistry(proto2ExtensionRegistry) 121 .ParseFrom(request.ProtobufPayload), 122 "protobuf_test_messages.proto3.TestAllTypesProto3" => 123 ProtobufTestMessages.Proto3.TestAllTypesProto3.Parser.ParseFrom( 124 request.ProtobufPayload), 125 "protobuf_test_messages.editions.TestAllTypesEdition2023" => 126 ProtobufTestMessages.Editions.TestAllTypesEdition2023.Parser 127 .WithExtensionRegistry(edition2023ExtensionRegistry) 128 .ParseFrom(request.ProtobufPayload), 129 "protobuf_test_messages.editions.proto2.TestAllTypesProto2" => 130 ProtobufTestMessages.Editions.Proto2.TestAllTypesProto2.Parser 131 .WithExtensionRegistry(editionsProto2ExtensionRegistry) 132 .ParseFrom(request.ProtobufPayload), 133 "protobuf_test_messages.editions.proto3.TestAllTypesProto3" => 134 ProtobufTestMessages.Editions.Proto3.TestAllTypesProto3.Parser 135 .ParseFrom(request.ProtobufPayload), 136 _ => throw new Exception( 137 $" Protobuf request doesn't have specific payload type ({request.MessageType})"), 138 }; 139 break; 140 case ConformanceRequest.PayloadOneofCase.TextPayload: 141 return new ConformanceResponse { Skipped = 142 "CSharp doesn't support text format" }; 143 default: 144 throw new Exception("Unsupported request payload: " + request.PayloadCase); 145 } 146 } 147 catch (InvalidProtocolBufferException e) 148 { 149 return new ConformanceResponse { ParseError = e.Message }; 150 } 151 catch (InvalidJsonException e) 152 { 153 return new ConformanceResponse { ParseError = e.Message }; 154 } 155 try 156 { 157 switch (request.RequestedOutputFormat) 158 { 159 case global::Conformance.WireFormat.Json: 160 var formatter = new JsonFormatter(new JsonFormatter.Settings(false, typeRegistry)); 161 return new ConformanceResponse { JsonPayload = formatter.Format(message) }; 162 case global::Conformance.WireFormat.Protobuf: 163 return new ConformanceResponse { ProtobufPayload = message.ToByteString() }; 164 default: 165 return new ConformanceResponse { Skipped = "CSharp doesn't support text format" }; 166 } 167 } 168 catch (InvalidOperationException e) 169 { 170 return new ConformanceResponse { SerializeError = e.Message }; 171 } 172 } 173 ReadInt32(BinaryReader input)174 private static int? ReadInt32(BinaryReader input) 175 { 176 byte[] bytes = input.ReadBytes(4); 177 if (bytes.Length == 0) 178 { 179 // Cleanly reached the end of the stream 180 return null; 181 } 182 if (bytes.Length != 4) 183 { 184 throw new EndOfStreamException("Read " + bytes.Length + " bytes of size when expecting 4"); 185 } 186 return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); 187 } 188 } 189 } 190