blob: 947791dc8729b275c83786e60ae26bb76fe8c26a [file] [log] [blame]
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.common.options.testing;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.devtools.common.options.Converter;
import com.google.devtools.common.options.Converters;
import com.google.devtools.common.options.OptionsParsingException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests to exercise the functionality of {@link ConverterTester}. */
@RunWith(JUnit4.class)
public final class ConverterTesterTest {
@Test
public void construction_throwsAssertionErrorIfConverterCreationFails() throws Exception {
try {
new ConverterTester(UnconstructableConverter.class);
} catch (AssertionError expected) {
assertThat(expected) // AssertionError
.hasCauseThat() // InvocationTargetException
.hasCauseThat() // UnsupportedOperationException
.hasMessageThat()
.contains("YOU CAN'T MAKE ME!");
return;
}
fail("expected tester creation to fail");
}
/** Test converter for construction_throwsAssertionErrorIfConverterCreationFails. */
public static final class UnconstructableConverter implements Converter<String> {
public UnconstructableConverter() {
throw new UnsupportedOperationException("YOU CAN'T MAKE ME!");
}
@Override
public String convert(String input) throws OptionsParsingException {
return input;
}
@Override
public String getTypeDescription() {
return "anything, if you can get an instance";
}
}
@Test
public void getConverterClass_returnsConstructorArg() throws Exception {
ConverterTester tester = new ConverterTester(Converters.BooleanConverter.class);
assertThat(tester.getConverterClass()).isEqualTo(Converters.BooleanConverter.class);
}
@Test
public void hasTestForInput_returnsTrueIffInputPassedToAddEqualityGroup() throws Exception {
ConverterTester tester =
new ConverterTester(Converters.DoubleConverter.class)
.addEqualityGroup("1.0", "1", "1.00")
.addEqualityGroup("2");
assertThat(tester.hasTestForInput("1.0")).isTrue();
assertThat(tester.hasTestForInput("1")).isTrue();
assertThat(tester.hasTestForInput("1.00")).isTrue();
assertThat(tester.hasTestForInput("2")).isTrue();
assertThat(tester.hasTestForInput("3")).isFalse();
assertThat(tester.hasTestForInput("1.000")).isFalse();
assertThat(tester.hasTestForInput("not a double")).isFalse();
}
@Test
public void addEqualityGroup_throwsIfConversionFails() throws Exception {
ConverterTester tester =
new ConverterTester(ThrowingConverter.class)
.addEqualityGroup("okay")
.addEqualityGroup("also okay", "pretty fine");
try {
tester.addEqualityGroup("wrong");
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"wrong\"");
assertThat(expected).hasCauseThat().hasMessageThat().contains("HOW DARE YOU");
return;
}
fail("expected addEqualityGroup to fail");
}
/** Test converter for addEqualityGroup_throwsIfConversionFails. */
public static final class ThrowingConverter implements Converter<String> {
@Override
public String convert(String input) throws OptionsParsingException {
if ("wrong".equals(input)) {
throw new OptionsParsingException("HOW DARE YOU");
}
return input;
}
@Override
public String getTypeDescription() {
return "just don't give the wrong answer";
}
}
@Test
public void testConvert_passesWhenAllInstancesObeyEqualsAndItemsOnlyEqualToOthersInSameGroup() {
new ConverterTester(Converters.DoubleConverter.class)
.addEqualityGroup("1.0", "1", "1.00")
.addEqualityGroup("2", "2", "2.0000", "2.0", "+2")
.addEqualityGroup("3")
.addEqualityGroup("3.1415")
.testConvert();
}
@Test
public void testConvert_testsHashCodeConsistencyForConvertedInstance() {
ConverterTester tester =
new ConverterTester(InconsistentHashCodeConverter.class)
.addEqualityGroup("input doesn't matter");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"input doesn't matter\"");
assertThat(expected).hasMessageThat().contains("hashCode");
assertThat(expected).hasMessageThat().contains("must be consistent");
return;
}
fail("expected the tester to notice the bad hash code implementation");
}
/** A class with a badly implemented hashCode which is not consistent across calls. */
public static final class InconsistentHashCode {
@Override
public boolean equals(Object other) {
return other instanceof InconsistentHashCode;
}
private int howManyTimesHaveIBeenHashedAlready = 0;
@Override
public int hashCode() {
howManyTimesHaveIBeenHashedAlready += 1;
return howManyTimesHaveIBeenHashedAlready;
}
}
/** Test converter for testConvert_testsHashCodeConsistencyForConvertedInstance. */
public static final class InconsistentHashCodeConverter
implements Converter<InconsistentHashCode> {
@Override
public InconsistentHashCode convert(String input) throws OptionsParsingException {
return new InconsistentHashCode();
}
@Override
public String getTypeDescription() {
return "anything, I don't even look at it";
}
}
@Test
public void testConvert_testsHashCodeConsistencyForSameConverter() {
ConverterTester tester =
new ConverterTester(IncrementingHashCodeConverter.class)
.addEqualityGroup("meaningless input");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"meaningless input\"");
assertThat(expected).hasMessageThat().contains("consistent hashCode");
assertThat(expected).hasMessageThat().contains("same Converter");
return;
}
fail("expected the tester to notice the mismatched hash codes");
}
/** A class with a configurable hashCode set in the constructor. */
public static final class SettableHashCode {
private final int hashCode;
public SettableHashCode(int hashCode) {
this.hashCode = hashCode;
}
@Override
public boolean equals(Object other) {
return other instanceof SettableHashCode;
}
@Override
public int hashCode() {
return hashCode;
}
}
/** Test converter for testConvert_testsHashCodeConsistencyForSameConverter. */
public static final class IncrementingHashCodeConverter
implements Converter<SettableHashCode> {
private int howManyInstancesHaveIMadeAlready = 0;
@Override
public SettableHashCode convert(String input) throws OptionsParsingException {
howManyInstancesHaveIMadeAlready += 1;
return new SettableHashCode(howManyInstancesHaveIMadeAlready);
}
@Override
public String getTypeDescription() {
return "whatever, I'm pretty much just going to ignore it";
}
}
@Test
public void testConvert_testsHashCodeConsistencyForDifferentConverters() {
ConverterTester tester =
new ConverterTester(StaticIncrementingHashCodeConverter.class)
.addEqualityGroup("some kind of input");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"some kind of input\"");
assertThat(expected).hasMessageThat().contains("consistent hashCode");
assertThat(expected).hasMessageThat().contains("different Converter");
return;
}
fail("expected the tester to notice the mismatched hash codes");
}
/** Test converter for testConvert_testsHashCodeConsistencyForDifferentConverters. */
public static final class StaticIncrementingHashCodeConverter
implements Converter<SettableHashCode> {
private static int howManyInstancesHaveIMadeAlready = 0;
private final int hashCode;
public StaticIncrementingHashCodeConverter() {
howManyInstancesHaveIMadeAlready += 1;
this.hashCode = howManyInstancesHaveIMadeAlready;
}
@Override
public SettableHashCode convert(String input) throws OptionsParsingException {
return new SettableHashCode(hashCode);
}
@Override
public String getTypeDescription() {
return "a string or null, I'm easy";
}
}
@Test
public void testConvert_testsSelfEqualityForConvertedInstance() {
ConverterTester tester =
new ConverterTester(SelfLoathingConverter.class)
.addEqualityGroup("self-loathing");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"self-loathing\"");
assertThat(expected).hasMessageThat().contains("must be Object#equals to itself");
return;
}
fail("expected the tester to notice the bad equals implementation");
}
/** A class which is equal to every instance of its class except itself. */
public static final class SelfLoathingObject {
@Override
public boolean equals(Object other) {
return other instanceof SelfLoathingObject && other != this;
}
@Override
public int hashCode() {
return 4; // chosen by fair hashing algorithm
}
}
/** Test converter for testConvert_testsSelfEqualityForConvertedInstance. */
public static final class SelfLoathingConverter implements Converter<SelfLoathingObject> {
@Override
public SelfLoathingObject convert(String input) throws OptionsParsingException {
return new SelfLoathingObject();
}
@Override
public String getTypeDescription() {
return "whatever... why even ask me to convert anything..............";
}
}
@Test
public void testConvert_testsEqualityForSameConverter() {
ConverterTester tester =
new ConverterTester(CountingConverter.class)
.addEqualityGroup("countables");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"countables\"");
assertThat(expected).hasMessageThat().contains("equal to itself");
assertThat(expected).hasMessageThat().contains("same Converter");
return;
}
fail("expected the tester to notice the converter giving unequal objects");
}
/** Test converter for testConvert_testsEqualityForSameConverter. */
public static final class CountingConverter implements Converter<Integer> {
private int howManyInstancesHaveIMadeAlready = 0;
@Override
public Integer convert(String input) throws OptionsParsingException {
howManyInstancesHaveIMadeAlready += 1;
return howManyInstancesHaveIMadeAlready;
}
@Override
public String getTypeDescription() {
return "I can count anything!";
}
}
@Test
public void testConvert_testsEqualityForDifferentConverters() {
ConverterTester tester =
new ConverterTester(StaticCountingConverter.class)
.addEqualityGroup("words I like");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"words I like\"");
assertThat(expected).hasMessageThat().contains("equal to itself");
assertThat(expected).hasMessageThat().contains("different Converter");
return;
}
fail("expected the tester to notice the converters giving unequal objects");
}
/** Test converter for testConvert_testsEqualityForDifferentConverters. */
public static final class StaticCountingConverter implements Converter<Integer> {
private static int howManyInstancesHaveIMadeAlready = 0;
private final int output;
public StaticCountingConverter() {
howManyInstancesHaveIMadeAlready += 1;
this.output = howManyInstancesHaveIMadeAlready;
}
@Override
public Integer convert(String input) throws OptionsParsingException {
return output;
}
@Override
public String getTypeDescription() {
return "your favorite text";
}
}
@Test
public void testConvert_testsEqualityForItemsInSameGroup() {
ConverterTester tester =
new ConverterTester(Converters.DoubleConverter.class)
.addEqualityGroup("+1.000", "2.30");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"+1.000\"");
assertThat(expected).hasMessageThat().contains("\"2.30\"");
return;
}
fail("expected the tester to notice the two non-equal conversion results in the same group");
}
@Test
public void testConvert_testsNonEqualityForItemsInDifferentGroups() {
ConverterTester tester =
new ConverterTester(Converters.DoubleConverter.class)
.addEqualityGroup("+1.000")
.addEqualityGroup("1.0");
try {
tester.testConvert();
} catch (AssertionError expected) {
assertThat(expected).hasMessageThat().contains("\"+1.000\"");
assertThat(expected).hasMessageThat().contains("\"1.0\"");
return;
}
fail("expected the tester to notice the two equal conversion results in different groups");
}
}