| // 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"); | |
| } | |
| } |