//
// 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.test.util;
import static com.github.protobufel.test.util.ProtoMatchers.IsDeeplyImmutableMessage.deeplyImmutableMessage;
import static com.github.protobufel.test.util.ProtoMatchers.IsImmutableMessage.immutableMessage;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.isA;
//import static org.mockito.BDDMockito.mock;
import static org.powermock.api.mockito.PowerMockito.mock;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.SortedMap;
import java.util.TreeMap;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.JavaType;
import com.google.protobuf.Message;
@Ignore // PowerMock fails creating a mock of FieldDescriptor.class, since Protobuf 2.6.1RC1
@PrepareForTest({FieldDescriptor.class})
@RunWith(Parameterized.class)
public class ProtoMatchersTest {
private static final Logger log = LoggerFactory.getLogger(ProtoMatchersTest.class);
private Message testMessage;
@Parameters(name = "{index}:immutable={1}")
public static Collection<Object[]> data() {
return ImmutableList.of(
//Mutable collections
new Object[]{Lists.newArrayList(1, 2, 3), false},
//Immutable collections
new Object[]{ImmutableList.of(1, 2, 3), true},
new Object[]{Collection.class.cast(ImmutableList.of(1, 2)), true},
new Object[]{Collections.unmodifiableList(Lists.newArrayList(1, 2, 3)), true},
new Object[]{Collections.unmodifiableCollection(Lists.newArrayList(1, 2)), true},
new Object[]{Collections.unmodifiableSet(new HashSet<>(Arrays.asList(1, 2, 3))), true}
);
}
@Parameter(value = 0)
public Collection<?> testCollection;
@Parameter(value = 1)
public boolean isImmutable;
@Rule
public PowerMockRule powerMockRule = new PowerMockRule();
@Before
public void setUp() throws Exception {
}
private Message createMockMessage(final boolean isMapImmutable, final int depth,
final Collection<?> testCollection, final boolean... deepRepeatedPattern) {
final Message mocked = mock(Message.class);
final SortedMap<FieldDescriptor, Object> map = new TreeMap<FieldDescriptor, Object>(new Comparator<FieldDescriptor>() {
@Override
public int compare(FieldDescriptor o1, FieldDescriptor o2) {
return Integer.compare(o1.getNumber(), o2.getNumber());
}
});
map.put(getMockFieldDescriptor(1, false, JavaType.STRING), "immutable string");
map.put(getMockFieldDescriptor(2, true, JavaType.STRING),
Collections.unmodifiableList(Arrays.asList("one", "two")));
if (depth == 0) {
map.put(getMockFieldDescriptor(3, true, JavaType.INT), testCollection);
} else {
final Message child = createMockMessage(true, depth - 1, testCollection,
deepRepeatedPattern);
boolean isRepeated = deepRepeatedPattern.length < depth
? false
: deepRepeatedPattern[deepRepeatedPattern.length - depth];
map.put(getMockFieldDescriptor(3, isRepeated, JavaType.MESSAGE),
(isRepeated ? Collections.singletonList(child) : child));
}
given(mocked.getAllFields())
.willReturn(isMapImmutable ? Collections.unmodifiableSortedMap(map) : map);
return mocked;
}
private FieldDescriptor getMockFieldDescriptor(int fieldNumber, boolean isRepeated, JavaType fieldType) {
final FieldDescriptor field = mock(FieldDescriptor.class);
given(field.getNumber()).willReturn(fieldNumber);
given(field.isRepeated()).willReturn(isRepeated);
given(field.getJavaType()).willReturn(fieldType);
given(field.compareTo(isA(FieldDescriptor.class))).willAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
FieldDescriptor other = (FieldDescriptor) invocation.getArguments()[0];
FieldDescriptor mock = (FieldDescriptor) invocation.getMock();
return Integer.compare(mock.getNumber(), other.getNumber());
}
});
return field;
}
@Test
public void testIsImmutableMessageMatcherWithMutableMapClass() throws Exception {
final Message mocked = createMockMessage(false, 0, testCollection);
assertThat(immutableMessage().matches(mocked), equalTo(false));
}
@Test
public void testIsImmutableMessageMatcherWithShallowRepeatedFieldMessage() throws Exception {
final Message mocked = createMockMessage(true, 0, testCollection);
assertThat(immutableMessage().matches(mocked), equalTo(isImmutable));
}
@Test
public void testIsDeeplyImmutableMessageMatcher1() throws Exception {
final Message mocked = createMockMessage(true, 2, testCollection, true, false, false);
assertThat(deeplyImmutableMessage().matches(mocked), equalTo(isImmutable));
}
@Test
public void testIsDeeplyImmutableMessageMatcher2() throws Exception {
final Message mocked = createMockMessage(true, 3, testCollection, true, true, true);
assertThat(deeplyImmutableMessage().matches(mocked), equalTo(isImmutable));
}
@Test
public void testIsDeeplyImmutableMessageMatcher3() throws Exception {
final Message mocked = createMockMessage(true, 4, testCollection, false, true, false);
assertThat(deeplyImmutableMessage().matches(mocked), equalTo(isImmutable));
}
@Test
public void testIsImmutableMessageMatcherDescribe() throws Exception {
assertThat(immutableMessage().toString(), is("an immutable message"));
}
@Test
public void testIsDeeplyImmutableMessageMatcherDescribe() throws Exception {
assertThat(deeplyImmutableMessage().toString(), is("a deeply immutable message"));
}
}