Add support for streaming delimited messages (#529)
* Add support for streaming delimited messages This allows developers to easily dump and load multiple messages from a stream in a way that is compatible with official protobuf implementations (such as Java's `MessageLite#writeDelimitedTo(...)`). * Add Java compatibility tests for streaming These tests stream data such as messages to output files, have a Java binary read them and then write them back using the `protobuf-java` functions, and then read them back in on the Python side to check that the returned data is as expected. This checks that the official Java implementation (and so any other matching implementations) can properly parse outputs from Betterproto, and vice-versa, ensuring compatibility in these functions between the two. * Replace `xxxxableBuffer` with `SupportsXxxx`
This commit is contained in:
		| @@ -0,0 +1,41 @@ | ||||
| package betterproto; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| public class CompatibilityTest { | ||||
|     public static void main(String[] args) throws IOException { | ||||
|         if (args.length < 2) | ||||
|             throw new RuntimeException("Attempted to run without the required arguments."); | ||||
|         else if (args.length > 2) | ||||
|             throw new RuntimeException( | ||||
|                     "Attempted to run with more than the expected number of arguments (>1)."); | ||||
|  | ||||
|         Tests tests = new Tests(args[1]); | ||||
|  | ||||
|         switch (args[0]) { | ||||
|             case "single_varint": | ||||
|                 tests.testSingleVarint(); | ||||
|                 break; | ||||
|  | ||||
|             case "multiple_varints": | ||||
|                 tests.testMultipleVarints(); | ||||
|                 break; | ||||
|  | ||||
|             case "single_message": | ||||
|                 tests.testSingleMessage(); | ||||
|                 break; | ||||
|  | ||||
|             case "multiple_messages": | ||||
|                 tests.testMultipleMessages(); | ||||
|                 break; | ||||
|  | ||||
|             case "infinite_messages": | ||||
|                 tests.testInfiniteMessages(); | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 throw new RuntimeException( | ||||
|                         "Attempted to run with unknown argument '" + args[0] + "'."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										115
									
								
								tests/streams/java/src/main/java/betterproto/Tests.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								tests/streams/java/src/main/java/betterproto/Tests.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| package betterproto; | ||||
|  | ||||
| import betterproto.nested.NestedOuterClass; | ||||
| import betterproto.oneof.Oneof; | ||||
|  | ||||
| import com.google.protobuf.CodedInputStream; | ||||
| import com.google.protobuf.CodedOutputStream; | ||||
|  | ||||
| import java.io.FileInputStream; | ||||
| import java.io.FileOutputStream; | ||||
| import java.io.IOException; | ||||
|  | ||||
| public class Tests { | ||||
|     String path; | ||||
|  | ||||
|     public Tests(String path) { | ||||
|         this.path = path; | ||||
|     } | ||||
|  | ||||
|     public void testSingleVarint() throws IOException { | ||||
|         // Read in the Python-generated single varint file | ||||
|         FileInputStream inputStream = new FileInputStream(path + "/py_single_varint.out"); | ||||
|         CodedInputStream codedInput = CodedInputStream.newInstance(inputStream); | ||||
|  | ||||
|         int value = codedInput.readUInt32(); | ||||
|  | ||||
|         inputStream.close(); | ||||
|  | ||||
|         // Write the value back to a file | ||||
|         FileOutputStream outputStream = new FileOutputStream(path + "/java_single_varint.out"); | ||||
|         CodedOutputStream codedOutput = CodedOutputStream.newInstance(outputStream); | ||||
|  | ||||
|         codedOutput.writeUInt32NoTag(value); | ||||
|  | ||||
|         codedOutput.flush(); | ||||
|         outputStream.close(); | ||||
|     } | ||||
|  | ||||
|     public void testMultipleVarints() throws IOException { | ||||
|         // Read in the Python-generated multiple varints file | ||||
|         FileInputStream inputStream = new FileInputStream(path + "/py_multiple_varints.out"); | ||||
|         CodedInputStream codedInput = CodedInputStream.newInstance(inputStream); | ||||
|  | ||||
|         int value1 = codedInput.readUInt32(); | ||||
|         int value2 = codedInput.readUInt32(); | ||||
|         long value3 = codedInput.readUInt64(); | ||||
|  | ||||
|         inputStream.close(); | ||||
|  | ||||
|         // Write the values back to a file | ||||
|         FileOutputStream outputStream = new FileOutputStream(path + "/java_multiple_varints.out"); | ||||
|         CodedOutputStream codedOutput = CodedOutputStream.newInstance(outputStream); | ||||
|  | ||||
|         codedOutput.writeUInt32NoTag(value1); | ||||
|         codedOutput.writeUInt64NoTag(value2); | ||||
|         codedOutput.writeUInt64NoTag(value3); | ||||
|  | ||||
|         codedOutput.flush(); | ||||
|         outputStream.close(); | ||||
|     } | ||||
|  | ||||
|     public void testSingleMessage() throws IOException { | ||||
|         // Read in the Python-generated single message file | ||||
|         FileInputStream inputStream = new FileInputStream(path + "/py_single_message.out"); | ||||
|         CodedInputStream codedInput = CodedInputStream.newInstance(inputStream); | ||||
|  | ||||
|         Oneof.Test message = Oneof.Test.parseFrom(codedInput); | ||||
|  | ||||
|         inputStream.close(); | ||||
|  | ||||
|         // Write the message back to a file | ||||
|         FileOutputStream outputStream = new FileOutputStream(path + "/java_single_message.out"); | ||||
|         CodedOutputStream codedOutput = CodedOutputStream.newInstance(outputStream); | ||||
|  | ||||
|         message.writeTo(codedOutput); | ||||
|  | ||||
|         codedOutput.flush(); | ||||
|         outputStream.close(); | ||||
|     } | ||||
|  | ||||
|     public void testMultipleMessages() throws IOException { | ||||
|         // Read in the Python-generated multi-message file | ||||
|         FileInputStream inputStream = new FileInputStream(path + "/py_multiple_messages.out"); | ||||
|  | ||||
|         Oneof.Test oneof = Oneof.Test.parseDelimitedFrom(inputStream); | ||||
|         NestedOuterClass.Test nested = NestedOuterClass.Test.parseDelimitedFrom(inputStream); | ||||
|  | ||||
|         inputStream.close(); | ||||
|  | ||||
|         // Write the messages back to a file | ||||
|         FileOutputStream outputStream = new FileOutputStream(path + "/java_multiple_messages.out"); | ||||
|  | ||||
|         oneof.writeDelimitedTo(outputStream); | ||||
|         nested.writeDelimitedTo(outputStream); | ||||
|  | ||||
|         outputStream.flush(); | ||||
|         outputStream.close(); | ||||
|     } | ||||
|  | ||||
|     public void testInfiniteMessages() throws IOException { | ||||
|         // Read in as many messages as are present in the Python-generated file and write them back | ||||
|         FileInputStream inputStream = new FileInputStream(path + "/py_infinite_messages.out"); | ||||
|         FileOutputStream outputStream = new FileOutputStream(path + "/java_infinite_messages.out"); | ||||
|  | ||||
|         Oneof.Test current = Oneof.Test.parseDelimitedFrom(inputStream); | ||||
|         while (current != null) { | ||||
|             current.writeDelimitedTo(outputStream); | ||||
|             current = Oneof.Test.parseDelimitedFrom(inputStream); | ||||
|         } | ||||
|  | ||||
|         inputStream.close(); | ||||
|         outputStream.flush(); | ||||
|         outputStream.close(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								tests/streams/java/src/main/proto/betterproto/nested.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								tests/streams/java/src/main/proto/betterproto/nested.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| syntax = "proto3"; | ||||
|  | ||||
| package nested; | ||||
| option java_package = "betterproto.nested"; | ||||
|  | ||||
| // A test message with a nested message inside of it. | ||||
| message Test { | ||||
|     // This is the nested type. | ||||
|     message Nested { | ||||
|         // Stores a simple counter. | ||||
|         int32 count = 1; | ||||
|     } | ||||
|     // This is the nested enum. | ||||
|     enum Msg { | ||||
|         NONE = 0; | ||||
|         THIS = 1; | ||||
|     } | ||||
|  | ||||
|     Nested nested = 1; | ||||
|     Sibling sibling = 2; | ||||
|     Sibling sibling2 = 3; | ||||
|     Msg msg = 4; | ||||
| } | ||||
|  | ||||
| message Sibling { | ||||
|   int32 foo = 1; | ||||
| } | ||||
							
								
								
									
										19
									
								
								tests/streams/java/src/main/proto/betterproto/oneof.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								tests/streams/java/src/main/proto/betterproto/oneof.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| syntax = "proto3"; | ||||
|  | ||||
| package oneof; | ||||
| option java_package = "betterproto.oneof"; | ||||
|  | ||||
| message Test { | ||||
|   oneof foo { | ||||
|     int32 pitied = 1; | ||||
|     string pitier = 2; | ||||
|   } | ||||
|  | ||||
|   int32 just_a_regular_field = 3; | ||||
|  | ||||
|   oneof bar { | ||||
|     int32 drinks = 11; | ||||
|     string bar_name = 12; | ||||
|   } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user