//
// Copyright © 2014, David Tesler (https://github.com/protobufel)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package com.github.protobufel.grammar;
import static com.github.protobufel.grammar.ExtensionRegistries.buildFullRegistryOf;
import static com.github.protobufel.grammar.Misc.getFileDescriptorProtos;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.assertj.core.api.JUnitSoftAssertions;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.protobufel.grammar.ErrorListeners.IBaseProtoErrorListener;
import com.github.protobufel.grammar.ErrorListeners.LogProtoErrorListener;
import com.github.protobufel.grammar.Misc.FieldTypeRefsMode;
import com.github.protobufel.grammar.Misc.FileDescriptorByNameComparator;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
@RunWith(JUnit4.class)
public class CompareWithOriginalsTest {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(CompareWithOriginalsTest.class);
public static final int MAX_FIELD_NUMBER = 536870912;
private static final String PROTOBUF_ORIGINAL_SUBDIR = "protobuf-original/";
private static final Pattern ALL_PROTOS_PATTERN = Pattern.compile(".*\\.proto");
private File baseDir;
private final Pattern filePattern = ALL_PROTOS_PATTERN;
// private List<String> files;
@Mock
private IBaseProtoErrorListener mockErrorListener;
private LogProtoErrorListener errorListener;
private ProtoFiles.Builder filesBuilder;
private List<FileDescriptorProto> protocFdProtos;
public final ExpectedException expected = ExpectedException.none();
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
@Rule
public final TestRule chain = RuleChain.outerRule(expected).around(new MockitoJUnitRule(this))
.around(softly);
@SuppressWarnings("null")
@Before
public void setUp() throws Exception {
// given
errorListener = new LogProtoErrorListener(mockErrorListener).setLogger(getClass());
filesBuilder = ProtoFiles.newBuilder(errorListener).setProtocCompatible(false);
baseDir = new File(getClass().getResource(PROTOBUF_ORIGINAL_SUBDIR).toURI());
protocFdProtos =
getFileDescriptorProtos(filePattern, false, FieldTypeRefsMode.AS_IS,
PROTOBUF_ORIGINAL_SUBDIR + "FileDescriptorSet", getClass());
}
@After
public void tearDown() throws Exception {}
@Test
public void testRawReserializationProtoc() throws Exception {
// given
final List<FileDescriptorProto> expectedProtoList = protocFdProtos;
for (final FileDescriptorProto expected : expectedProtoList) {
// when
final FileDescriptorProto actual = FileDescriptorProto.parseFrom(expected.toByteString());
// then
softly.assertThat(actual).as("equality").isEqualTo(expected);
softly.assertThat(actual.toString()).as("textual equality").isEqualTo(expected.toString());
}
}
@Test
public void testRawReserialization() throws Exception {
// given
final List<FileDescriptorProto> expectedProtoList =
filesBuilder.setCustomOptionsAsExtensions(false).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").buildProtos(false);
for (final FileDescriptorProto expected : expectedProtoList) {
// when
final FileDescriptorProto actual = FileDescriptorProto.parseFrom(expected.toByteString());
// then
softly.assertThat(actual).as("equality").isEqualTo(expected);
softly.assertThat(actual.toString()).as("textual equality").isEqualTo(expected.toString());
}
}
@Test
public void testCustomOptionsAsUnknown() throws URISyntaxException, IOException {
// given
final List<FileDescriptorProto> expectedProtoList = protocFdProtos;
// when
final List<FileDescriptorProto> actualProtoList =
filesBuilder.setCustomOptionsAsExtensions(false).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").buildProtos(false);
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
softly.assertThat(actualProtoList).as("check actualProtoList is not null").isNotNull();
softly.assertThat(actualProtoList).as("check actualProtoList without nulls")
.doesNotContainNull();
softly.assertThat(actualProtoList).as("check actualProtoList has no duplicates")
.extracting("package", "name").doesNotHaveDuplicates();
softly.assertThat(actualProtoList).as("check actualProtoList size")
.hasSameSizeAs(expectedProtoList);
softly.assertThat(actualProtoList).as("check all protos' by name and package")
.usingElementComparatorOnFields("package", "name")
.containsOnlyElementsOf(expectedProtoList);
final List<FileDescriptorProto> sortedActualFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(actualProtoList);
final List<FileDescriptorProto> sortedExpectedFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(expectedProtoList);
softly.assertThat(sortedActualFds).as("check all protos' by filtered content")
.usingElementComparator(Misc.getDisregardUnsupportedProtoComparator())
.containsExactlyElementsOf(sortedExpectedFds);
}
@Test
public void customOptionsAsUnknownEqualsToFullReserialization() throws URISyntaxException,
IOException {
// given
final List<FileDescriptorProto> protosWithExtensions =
ProtoFiles.newBuilder().setCustomOptionsAsExtensions(true).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").buildProtos(false);
final List<FileDescriptorProto> expectedProtoList =
new ArrayList<FileDescriptorProto>(protosWithExtensions.size());
for (final FileDescriptorProto proto : protosWithExtensions) {
expectedProtoList.add(FileDescriptorProto.parseFrom(proto.toByteString()));
}
// when
final List<FileDescriptorProto> actualProtoList =
filesBuilder.setCustomOptionsAsExtensions(false).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").buildProtos(false);
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
softly.assertThat(actualProtoList).as("check actualProtoList is not null").isNotNull();
softly.assertThat(actualProtoList).as("check actualProtoList without nulls")
.doesNotContainNull();
softly.assertThat(actualProtoList).as("check actualProtoList has no duplicates")
.extracting("package", "name").doesNotHaveDuplicates();
softly.assertThat(actualProtoList).as("check actualProtoList size")
.hasSameSizeAs(expectedProtoList);
softly.assertThat(actualProtoList).as("check all protos' by name and package")
.usingElementComparatorOnFields("package", "name")
.containsOnlyElementsOf(expectedProtoList);
final List<FileDescriptorProto> sortedActualFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(actualProtoList);
final List<FileDescriptorProto> sortedExpectedFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(expectedProtoList);
softly.assertThat(sortedActualFds).as("proto equlity unfiltered")
.containsExactlyElementsOf(sortedExpectedFds);
}
@Test
public void testCustomOptionsAsExtensions() throws URISyntaxException, IOException {
// given
final List<FileDescriptorProto> expectedProtoList =
ProtoFiles.newBuilder(errorListener).setCustomOptionsAsExtensions(true)
.addProtos(protocFdProtos).buildProtos(false);
// when
final List<FileDescriptorProto> actualProtoList =
filesBuilder.setCustomOptionsAsExtensions(true).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").buildProtos(false);
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
softly.assertThat(actualProtoList).as("check actualProtoList is not null").isNotNull();
softly.assertThat(actualProtoList).as("check actualProtoList without nulls")
.doesNotContainNull();
softly.assertThat(actualProtoList).as("check actualProtoList has no duplicates")
.extracting("package", "name").doesNotHaveDuplicates();
softly.assertThat(actualProtoList).as("check actualProtoList size")
.hasSameSizeAs(expectedProtoList);
softly.assertThat(actualProtoList).as("check all protos' by name and package")
.usingElementComparatorOnFields("package", "name")
.containsOnlyElementsOf(expectedProtoList);
final List<FileDescriptorProto> sortedActualFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(actualProtoList);
final List<FileDescriptorProto> sortedExpectedFds =
FileDescriptorByNameComparator.of().immutableSortedCopy(expectedProtoList);
softly.assertThat(sortedActualFds).as("check all protos' by filtered content")
.usingElementComparator(Misc.getDisregardUnsupportedProtoComparator())
.containsExactlyElementsOf(sortedExpectedFds);
}
/**
* protoc custom options' UnknownFieldSet serialization is different from Java; the resulted
* extensions are the same, but UnknownFieldSets are different, thus the invariant has been
* violated. When the serializations become identical, the test will start failing, indicating
* everything is alright, so the expected exception will have to be safely removed!
*/
@Test
public void unknownToExtensionsToUnknownShouldBeEqualProtoc() throws URISyntaxException,
IOException {
// when
final Collection<FileDescriptor> files =
filesBuilder.setCustomOptionsAsExtensions(false).setProtocCompatible(false)
.addProtos(protocFdProtos).build(false).values();
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
expected.expect(Throwable.class);
for (final FileDescriptor file : files) {
assertReserializationInvariant(file);
}
}
@Test
public void unknownToExtensionsToUnknownShouldBeEqual() throws URISyntaxException, IOException {
// when
final Collection<FileDescriptor> files =
filesBuilder.setCustomOptionsAsExtensions(false).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").build(false).values();
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
for (final FileDescriptor file : files) {
assertReserializationInvariant(file);
}
}
private void assertReserializationInvariant(final FileDescriptor fileWithUnknownFieldsProto)
throws InvalidProtocolBufferException {
assertReserializationInvariant(fileWithUnknownFieldsProto,
buildFullRegistryOf(fileWithUnknownFieldsProto));
}
private void assertReserializationInvariant(final FileDescriptor fileWithUnknownFieldsProto,
final ExtensionRegistry registry) throws InvalidProtocolBufferException {
final FileDescriptorProto expectedProtoWithUnknownFields = fileWithUnknownFieldsProto.toProto();
final FileDescriptorProto actualProtoWithExtensions =
FileDescriptorProto.parseFrom(expectedProtoWithUnknownFields.toByteString(), registry);
final FileDescriptorProto actualProtoWithUnknownFields =
FileDescriptorProto.parseFrom(actualProtoWithExtensions.toByteString());
softly
.assertThat(actualProtoWithUnknownFields.toString())
.as("check reserialization invariant textual equality for %s",
fileWithUnknownFieldsProto.getName())
.isEqualTo(expectedProtoWithUnknownFields.toString());
softly.assertThat(actualProtoWithUnknownFields)
.as("check reserialization invariant for %s", fileWithUnknownFieldsProto.getName())
.isEqualTo(expectedProtoWithUnknownFields);
}
@Test
public void extensionsToUnknownToExtensionsShouldBeEqualProtoc() throws URISyntaxException,
IOException {
// when
final Collection<FileDescriptor> files =
filesBuilder.setCustomOptionsAsExtensions(true).setProtocCompatible(false)
.addProtos(protocFdProtos).build(false).values();
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
for (final FileDescriptor file : files) {
assertReserializationInvariant2(file);
}
}
@Test
public void extensionsToUnknownToExtensionsShouldBeEqual() throws URISyntaxException, IOException {
// when
final Collection<FileDescriptor> files =
filesBuilder.setCustomOptionsAsExtensions(true).setProtocCompatible(false)
.addFilesByGlob(baseDir, "**/*.proto").build(false).values();
// then
// no errors logged!
verify(mockErrorListener, never()).validationError(anyInt(), anyInt(), anyString(),
any(RuntimeException.class));
verify(mockErrorListener, never()).syntaxError(any(Recognizer.class), any(), anyInt(),
anyInt(), anyString(), any(RecognitionException.class));
for (final FileDescriptor file : files) {
assertReserializationInvariant2(file);
}
}
private void assertReserializationInvariant2(final FileDescriptor fileWithExtensionsProto)
throws InvalidProtocolBufferException {
assertReserializationInvariant2(fileWithExtensionsProto,
buildFullRegistryOf(fileWithExtensionsProto));
}
private void assertReserializationInvariant2(final FileDescriptor fileWithExtensionsProto,
final ExtensionRegistry registry) throws InvalidProtocolBufferException {
final FileDescriptorProto expectedProtoWithExtensions = fileWithExtensionsProto.toProto();
final FileDescriptorProto actualProtoWithUnknownFields =
FileDescriptorProto.parseFrom(expectedProtoWithExtensions.toByteString());
final FileDescriptorProto actualProtoWithExtensions =
FileDescriptorProto.parseFrom(actualProtoWithUnknownFields.toByteString(), registry);
softly.assertThat(actualProtoWithExtensions)
.as("check reserialization invariant2 for %s", fileWithExtensionsProto.getName())
.isEqualTo(expectedProtoWithExtensions);
}
}