/* * Copyright 2015 protobuf-dynamic developers * * 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 * * http://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. */ package com.github.os72.protobuf.dynamic; import java.io.FileInputStream; import org.junit.Test; import org.junit.Assert; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; import com.google.protobuf.DynamicMessage; public class DynamicSchemaTest { /** * testBasic - basic usage */ @Test public void testBasic() throws Exception { log("--- testBasic ---"); // Create dynamic schema DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); schemaBuilder.setName("PersonSchemaDynamic.proto"); MessageDefinition msgDef = MessageDefinition.newBuilder("Person") // message Person .addField("required", "int32", "id", 1) // required int32 id = 1 .addField("required", "string", "name", 2) // required string name = 2 .addField("optional", "string", "email", 3) // optional string email = 3 .build(); schemaBuilder.addMessageDefinition(msgDef); DynamicSchema schema = schemaBuilder.build(); log(schema); // Create dynamic message from schema DynamicMessage.Builder msgBuilder = schema.newMessageBuilder("Person"); Descriptor msgDesc = msgBuilder.getDescriptorForType(); DynamicMessage msg = msgBuilder .setField(msgDesc.findFieldByName("id"), 1) .setField(msgDesc.findFieldByName("name"), "Alan Turing") .setField(msgDesc.findFieldByName("email"), "[email protected]") .build(); log(msg); // Create data object traditional way using generated code PersonSchema.Person person = PersonSchema.Person.newBuilder() .setId(1) .setName("Alan Turing") .setEmail("[email protected]") .build(); // Should be equivalent Assert.assertEquals(person.toString(), msg.toString()); } /** * testOneof - oneof usage */ @Test public void testOneof() throws Exception { log("--- testOneof ---"); // Create dynamic schema DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); schemaBuilder.setName("PersonSchemaDynamic.proto"); MessageDefinition msgDef = MessageDefinition.newBuilder("Person") // message Person .addField("required", "int32", "id", 1) // required int32 id = 1 .addField("required", "string", "name", 2) // required string name = 2 .addField("optional", "string", "email", 3) // optional string email = 3 .addOneof("address") // oneof address .addField("string", "home_addr", 4) // string home_addr = 4 .addField("string", "work_addr", 5) // string work_addr = 5 .msgDefBuilder() .build(); // Demo OneofBuilder MessageDefinition.Builder msgDefBuilder = MessageDefinition.newBuilder("SomeOneofDef"); msgDefBuilder.addField("required", "int32", "id", 1); MessageDefinition.OneofBuilder oneofBuilder1 = msgDefBuilder.addOneof("addr1"); MessageDefinition.OneofBuilder oneofBuilder2 = msgDefBuilder.addOneof("addr2"); oneofBuilder1.addField("string", "addr11", 11); oneofBuilder1.addField("string", "addr12", 12); oneofBuilder2.addField("string", "addr21", 21, "default21"); oneofBuilder2.addField("string", "addr22", 22, "default22"); schemaBuilder.addMessageDefinition(msgDefBuilder.build()); schemaBuilder.addMessageDefinition(msgDef); DynamicSchema schema = schemaBuilder.build(); log(schema); // Create dynamic message from schema DynamicMessage.Builder msgBuilder = schema.newMessageBuilder("Person"); Descriptor msgDesc = msgBuilder.getDescriptorForType(); DynamicMessage msg = msgBuilder .setField(msgDesc.findFieldByName("id"), 1) .setField(msgDesc.findFieldByName("name"), "Alan Turing") .setField(msgDesc.findFieldByName("work_addr"), "85 Albert Embankment") .build(); log(msg); // Create data object traditional way using generated code PersonSchema.Person person = PersonSchema.Person.newBuilder() .setId(1) .setName("Alan Turing") .setWorkAddr("85 Albert Embankment") .build(); // Should be equivalent Assert.assertEquals(person.toString(), msg.toString()); } /** * testAdvanced - nested messages, enums, default values, repeated fields */ @Test public void testAdvanced() throws Exception { log("--- testAdvanced ---"); // Create dynamic schema DynamicSchema.Builder schemaBuilder = DynamicSchema.newBuilder(); schemaBuilder.setName("PersonSchemaDynamic.proto"); EnumDefinition enumDefPhoneType = EnumDefinition.newBuilder("PhoneType") // enum PhoneType .addValue("MOBILE", 0) // MOBILE = 0 .addValue("HOME", 1) // HOME = 1 .addValue("WORK", 2) // WORK = 2 .build(); MessageDefinition msgDefPhoneNumber = MessageDefinition.newBuilder("PhoneNumber") // message PhoneNumber .addField("required", "string", "number", 1) // required string number = 1 .addField("optional", "PhoneType", "type", 2, "HOME") // optional PhoneType type = 2 [default = HOME] .build(); MessageDefinition msgDefPerson = MessageDefinition.newBuilder("Person") // message Person .addEnumDefinition(enumDefPhoneType) // enum PhoneType (nested) .addMessageDefinition(msgDefPhoneNumber) // message PhoneNumber (nested) .addField("required", "int32", "id", 1) // required int32 id = 1 .addField("required", "string", "name", 2) // required string name = 2 .addField("optional", "string", "email", 3) // optional string email = 3 .addField("repeated", "PhoneNumber", "phone", 4) // repeated PhoneNumber phone = 4 .build(); schemaBuilder.addMessageDefinition(msgDefPerson); DynamicSchema schema = schemaBuilder.build(); log(schema); // Create dynamic message from schema Descriptor phoneDesc = schema.getMessageDescriptor("Person.PhoneNumber"); DynamicMessage phoneMsg1 = schema.newMessageBuilder("Person.PhoneNumber") .setField(phoneDesc.findFieldByName("number"), "+44-111") .build(); DynamicMessage phoneMsg2 = schema.newMessageBuilder("Person.PhoneNumber") .setField(phoneDesc.findFieldByName("number"), "+44-222") .setField(phoneDesc.findFieldByName("type"), schema.getEnumValue("Person.PhoneType", "WORK")) .build(); Descriptor personDesc = schema.getMessageDescriptor("Person"); DynamicMessage personMsg = schema.newMessageBuilder("Person") .setField(personDesc.findFieldByName("id"), 1) .setField(personDesc.findFieldByName("name"), "Alan Turing") .setField(personDesc.findFieldByName("email"), "[email protected]") .addRepeatedField(personDesc.findFieldByName("phone"), phoneMsg1) .addRepeatedField(personDesc.findFieldByName("phone"), phoneMsg2) .build(); log(personMsg); phoneMsg1 = (DynamicMessage)personMsg.getRepeatedField(personDesc.findFieldByName("phone"), 0); phoneMsg2 = (DynamicMessage)personMsg.getRepeatedField(personDesc.findFieldByName("phone"), 1); String phoneNumber1 = (String)phoneMsg1.getField(phoneDesc.findFieldByName("number")); String phoneNumber2 = (String)phoneMsg2.getField(phoneDesc.findFieldByName("number")); EnumValueDescriptor phoneType1 = (EnumValueDescriptor)phoneMsg1.getField(phoneDesc.findFieldByName("type")); EnumValueDescriptor phoneType2 = (EnumValueDescriptor)phoneMsg2.getField(phoneDesc.findFieldByName("type")); log(phoneNumber1 + ", " + phoneType1.getName()); log(phoneNumber2 + ", " + phoneType2.getName()); Assert.assertEquals("+44-111", phoneNumber1); Assert.assertEquals("HOME", phoneType1.getName()); // [default = HOME] Assert.assertEquals("+44-222", phoneNumber2); Assert.assertEquals("WORK", phoneType2.getName()); } /** * testSchemaMerge - schema merging */ @Test public void testSchemaMerge() throws Exception { log("--- testSchemaMerge ---"); DynamicSchema.Builder schemaBuilder1 = DynamicSchema.newBuilder().setName("Schema1.proto").setPackage("package1"); schemaBuilder1.addMessageDefinition(MessageDefinition.newBuilder("Msg1").build()); DynamicSchema.Builder schemaBuilder2 = DynamicSchema.newBuilder().setName("Schema2.proto").setPackage("package2"); schemaBuilder2.addMessageDefinition(MessageDefinition.newBuilder("Msg2").build()); schemaBuilder1.addSchema(schemaBuilder2.build()); DynamicSchema schema1 = schemaBuilder1.build(); log(schema1); // schema1 should contain both Msg1 and Msg2 Assert.assertNotNull(schema1.getMessageDescriptor("Msg1")); Assert.assertNotNull(schema1.getMessageDescriptor("Msg2")); DynamicSchema.Builder schemaBuilder3 = DynamicSchema.newBuilder().setName("Schema3.proto").setPackage("package3"); schemaBuilder3.addMessageDefinition(MessageDefinition.newBuilder("Msg1").build()); // Msg1 to force collision schemaBuilder1.addSchema(schemaBuilder3.build()); schema1 = schemaBuilder1.build(); log(schema1); // Msg1 now ambiguous, must fully qualify name (package1, package3); Msg2 still unique Assert.assertNull(schema1.getMessageDescriptor("Msg1")); Assert.assertNotNull(schema1.getMessageDescriptor("Msg2")); Assert.assertNotNull(schema1.getMessageDescriptor("package1.Msg1")); Assert.assertNotNull(schema1.getMessageDescriptor("package2.Msg2")); Assert.assertNotNull(schema1.getMessageDescriptor("package3.Msg1")); // Trying to add duplicate name (fully qualified) should throw exception IllegalArgumentException ex = null; try { schemaBuilder1.addSchema(schemaBuilder3.build()); schema1 = schemaBuilder1.build(); } catch (IllegalArgumentException e) { log("expected: " + e); ex = e; } Assert.assertNotNull(ex); } /** * testSchemaSerialization - serialization, deserialization, protoc output parsing */ @Test public void testSchemaSerialization() throws Exception { log("--- testSchemaSerialization ---"); // Read protoc compiler output (deserialize) DynamicSchema schema1 = DynamicSchema.parseFrom(new FileInputStream("src/test/resources/PersonSchema.desc")); log(schema1); byte[] descBuf = schema1.toByteArray(); // serialize DynamicSchema schema2 = DynamicSchema.parseFrom(descBuf); // deserialize // Should be equivalent Assert.assertEquals(schema1.toString(), schema2.toString()); } /** * testSchemaDependency - nested dependencies (imports) */ @Test public void testSchemaDependency() throws Exception { log("--- testSchemaDependency ---"); // Read protoc compiler output (deserialize) DynamicSchema schema1 = DynamicSchema.parseFrom(new FileInputStream("src/test/resources/Schema1.desc")); log(schema1); // schema1 should contain all imported types Assert.assertNotNull(schema1.getMessageDescriptor("Msg1")); Assert.assertNotNull(schema1.getMessageDescriptor("Msg2")); Assert.assertNotNull(schema1.getMessageDescriptor("Msg3")); Assert.assertNotNull(schema1.getMessageDescriptor("Person")); Assert.assertNotNull(schema1.getMessageDescriptor("Person.PhoneNumber")); Assert.assertNotNull(schema1.getEnumDescriptor("Person.PhoneType")); } /** * testSchemaDependencyNoImports - missing nested dependencies (imports) */ @Test public void testSchemaDependencyNoImports() throws Exception { log("--- testSchemaDependencyNoImports ---"); // Trying to parse schema descriptor with missing dependencies should throw exception IllegalArgumentException ex = null; try { // Read protoc compiler output (deserialize) DynamicSchema schema1 = DynamicSchema.parseFrom(new FileInputStream("src/test/resources/Schema1_no_imports.desc")); log(schema1); } catch (IllegalArgumentException e) { log("expected: " + e); ex = e; } Assert.assertNotNull(ex); } static void log(Object o) { System.out.println(o); } }