ProtobufUtil { static void verifyDeterministic(ProtoCoder<?> coder) throws NonDeterministicException { Class<? extends Message> message = coder.getMessageType(); ExtensionRegistry registry = coder.getExtensionRegistry(); Set<Descriptor> descriptors = getRecursiveDescriptorsForClass(message, registry); for (Descriptor d : descriptors) { for (FieldDescriptor fd : d.getFields()) { if (fd.isMapField()) { String reason = String.format( "Protocol Buffers message %s transitively includes Map field %s (from file %s)." + " Maps cannot be deterministically encoded.", message.getName(), fd.getFullName(), fd.getFile().getFullName()); throw new NonDeterministicException(coder, reason); } } } } private  ProtobufUtil();   }
@Test public void testDurationIsDeterministic() throws NonDeterministicException { verifyDeterministic(ProtoCoder.of(Duration.class)); }
@Test public void testMessageWithMapIsNotDeterministic() throws NonDeterministicException { String mapFieldName = MessageWithMap.getDescriptor().findFieldByNumber(1).getFullName(); thrown.expect(NonDeterministicException.class); thrown.expectMessage(MessageWithMap.class.getName()); thrown.expectMessage("transitively includes Map field " + mapFieldName); thrown.expectMessage("file " + MessageWithMap.getDescriptor().getFile().getName()); verifyDeterministic(ProtoCoder.of(MessageWithMap.class)); }
@Test public void testMessageWithTransitiveMapIsNotDeterministic() throws NonDeterministicException { String mapFieldName = MessageWithMap.getDescriptor().findFieldByNumber(1).getFullName(); thrown.expect(NonDeterministicException.class); thrown.expectMessage(ReferencesMessageWithMap.class.getName()); thrown.expectMessage("transitively includes Map field " + mapFieldName); thrown.expectMessage("file " + MessageWithMap.getDescriptor().getFile().getName()); verifyDeterministic(ProtoCoder.of(ReferencesMessageWithMap.class)); }