/** * Copyright (C) 2022, 2024, 2025 Adrien Hopkins * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package sevenUnitsGUI; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static sevenUnitsGUI.StandardDisplayRules.fixedDecimals; import static sevenUnitsGUI.StandardDisplayRules.fixedPrecision; import static sevenUnitsGUI.StandardDisplayRules.getStandardRule; import static sevenUnitsGUI.StandardDisplayRules.uncertaintyBased; import java.util.Objects; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import sevenUnits.utils.UncertainDouble; import sevenUnitsGUI.StandardDisplayRules.FixedDecimals; import sevenUnitsGUI.StandardDisplayRules.FixedPrecision; import sevenUnitsGUI.StandardDisplayRules.UncertaintyBased; /** * Tests that ensure the rounding rules work as intended. * * @since v0.4.0 * @since 2022-07-17 */ class RoundingTest { // rounding rules to test private static final FixedDecimals ZERO_DECIMALS = fixedDecimals(0); private static final FixedDecimals TWO_DECIMALS = fixedDecimals(2); private static final FixedDecimals SIX_DECIMALS = fixedDecimals(6); private static final FixedPrecision ONE_SIG_FIG = fixedPrecision(1); private static final FixedPrecision THREE_SIG_FIGS = fixedPrecision(3); private static final FixedPrecision TWELVE_SIG_FIGS = fixedPrecision(12); private static final UncertaintyBased UNCERTAINTY_BASED = uncertaintyBased(); // numbers to test rounding with private static final UncertainDouble INPUT1 = UncertainDouble.of(12.3456789, 0.0); private static final UncertainDouble INPUT2 = UncertainDouble.of(300.9, 0.005); private static final UncertainDouble INPUT3 = UncertainDouble.of(12345432.1, 0.0); private static final UncertainDouble INPUT4 = UncertainDouble.of(0.00001234, 0.000001); /** * @return arguments for * {@link #testFixedDecimalRounding(UncertainDouble, String, String, String)} * @since v0.4.0 * @since 2022-07-17 */ private static final Stream fixedDecimalRoundingExamples() { // input, zero decimal string, two decimal string, six decimal string return Stream.of(Arguments.of(INPUT1, "12", "12.35", "12.345679"), Arguments.of(INPUT2, "301", "300.90", "300.900000"), Arguments.of(INPUT3, "12345432", "12345432.10", "12345432.100000"), Arguments.of(INPUT4, "0", "0.00", "0.000012")); } /** * @return arguments for * {@link #testFixedPrecisionRounding(UncertainDouble, String, String, String)} * @since v0.4.0 * @since 2022-07-17 */ private static final Stream fixedPrecisionRoundingExamples() { // input, one sig fig string, three s.f. string, six s.f. string return Stream.of(Arguments.of(INPUT1, "1E+1", "12.3", "12.3456789000"), Arguments.of(INPUT2, "3E+2", "301", "300.900000000"), Arguments.of(INPUT3, "1E+7", "1.23E+7", "12345432.1000"), Arguments.of(INPUT4, "0.00001", "0.0000123", "0.0000123400000000")); } /** * @return arguments for * {@link #testUncertaintyRounding(UncertainDouble, String)} * @since v0.4.0 * @since 2022-07-17 */ private static final Stream uncertaintyRoundingExamples() { // input, uncertainty rounding string return Stream.of(Arguments.of(INPUT1, "12.3456789"), Arguments.of(INPUT2, "300.900"), Arguments.of(INPUT3, "1.23454321E7"), Arguments.of(INPUT4, "0.0000123")); } /** * Test for {@link FixedDecimals#decimalPlaces()} and * {@link FixedPrecision#significantFigures()}. * * @since v0.4.0 * @since 2022-07-17 */ @Test void testDataMethods() { // ensure # of decimal places can be accessed assertEquals(0, ZERO_DECIMALS.decimalPlaces(), "ZERO_DECIMALS has " + ZERO_DECIMALS.decimalPlaces() + " decimals"); assertEquals(2, TWO_DECIMALS.decimalPlaces(), "TWO_DECIMALS has " + TWO_DECIMALS.decimalPlaces() + " decimals"); assertEquals(6, SIX_DECIMALS.decimalPlaces(), "SIX_DECIMALS has " + SIX_DECIMALS.decimalPlaces() + " decimals"); // ensure # of sig figs can be accessed assertEquals(1, ONE_SIG_FIG.significantFigures(), "ONE_SIG_FIG has " + ONE_SIG_FIG.significantFigures() + " significant figures"); assertEquals(3, THREE_SIG_FIGS.significantFigures(), "THREE_SIG_FIGS has " + THREE_SIG_FIGS.significantFigures() + " significant figures"); assertEquals(12, TWELVE_SIG_FIGS.significantFigures(), "TWELVE_SIG_FIGS has " + TWELVE_SIG_FIGS.significantFigures() + " significant figures"); } /** * Tests that the rounding methods' equals() methods work. * * @since v0.4.0 * @since 2022-07-17 */ @Test void testEquals() { // basic equals tests assertTrue(ZERO_DECIMALS.equals(ZERO_DECIMALS), "ZERO_DECIMALS does not equal itself"); assertFalse(TWO_DECIMALS.equals(SIX_DECIMALS), "TWO_DECIMALS == SIX_DECIMALS"); assertTrue(Objects.equals(fixedDecimals(0), fixedDecimals(0)), "FixedDecimals.equals() depends on something other than decimal places."); assertTrue(ONE_SIG_FIG.equals(ONE_SIG_FIG), "ONE_SIG_FIG does not equal itself"); assertFalse(THREE_SIG_FIGS.equals(TWELVE_SIG_FIGS), "THREE_SIG_FIGS == TWELVE_SIG_FIGS"); assertTrue(Objects.equals(fixedPrecision(1), fixedPrecision(1)), "FixedPrecision.equals() depends on something other than significant figures."); // test that FixedDecimals is never equal to FixedPrecision // this unlikely argument is the test - the equals should return false! @SuppressWarnings("unlikely-arg-type") final boolean differentRulesEqual = Objects.equals(fixedDecimals(4), fixedPrecision(4)); assertFalse(differentRulesEqual, "fixedDecimals(4) == fixedPrecision(4)"); } /** * Ensures that fixed decimal rounding works as expected * * @param input number to test * @param zeroDecimalString expected string for zero decimal places * @param twoDecimalString expected string for two decimal places * @param sixDecimalString expected string for six decimal places * @since 2022-07-17 */ @ParameterizedTest @MethodSource("fixedDecimalRoundingExamples") void testFixedDecimalRounding(UncertainDouble input, String zeroDecimalString, String twoDecimalString, String sixDecimalString) { // test the three rounding rules against the provided strings assertEquals(zeroDecimalString, ZERO_DECIMALS.apply(input), "ZERO_DECIMALS rounded " + input + " as " + ZERO_DECIMALS.apply(input) + " (should be " + zeroDecimalString + ")"); assertEquals(twoDecimalString, TWO_DECIMALS.apply(input), "TWO_DECIMALS rounded " + input + " as " + TWO_DECIMALS.apply(input) + " (should be " + twoDecimalString + ")"); assertEquals(sixDecimalString, SIX_DECIMALS.apply(input), "TWO_DECIMALS rounded " + input + " as " + SIX_DECIMALS.apply(input) + " (should be " + sixDecimalString + ")"); } /** * Ensures that fixed precision rounding works as expected * * @param input number to test * @param oneSigFigString expected string for one significant figure * @param threeSigFigString expected string for three significant figures * @param twelveSigFigString expected string for twelve significant figures * @since v0.4.0 * @since 2022-07-17 */ @ParameterizedTest @MethodSource("fixedPrecisionRoundingExamples") void testFixedPrecisionRounding(UncertainDouble input, String oneSigFigString, String threeSigFigString, String twelveSigFigString) { // test the three rounding rules against the provided strings assertEquals(oneSigFigString, ONE_SIG_FIG.apply(input), "ONE_SIG_FIG rounded " + input + " as " + ONE_SIG_FIG.apply(input) + " (should be " + oneSigFigString + ")"); assertEquals(threeSigFigString, THREE_SIG_FIGS.apply(input), "THREE_SIG_FIGS rounded " + input + " as " + THREE_SIG_FIGS.apply(input) + " (should be " + threeSigFigString + ")"); assertEquals(twelveSigFigString, TWELVE_SIG_FIGS.apply(input), "TWELVE_SIG_FIGS rounded " + input + " as " + TWELVE_SIG_FIGS.apply(input) + " (should be " + twelveSigFigString + ")"); } /** * Tests that {@link StandardDisplayRules#getStandardRule} gets rounding * rules as intended. * * @since v0.4.0 * @since 2022-07-17 */ @Test void testGetStandardRule() { assertEquals(ZERO_DECIMALS, getStandardRule("Round to 0 decimal places")); assertEquals(THREE_SIG_FIGS, getStandardRule("Round to 3 significant figures")); assertEquals(UNCERTAINTY_BASED, getStandardRule("Uncertainty-Based Rounding")); assertThrows(IllegalArgumentException.class, () -> getStandardRule("Not a rounding rule")); } /** * Tests that the rounding methods' equals() methods work. * * @since v0.4.0 * @since 2022-07-17 */ @Test void testHashCode() { assertEquals(ZERO_DECIMALS.hashCode(), ZERO_DECIMALS.hashCode()); assertEquals(ONE_SIG_FIG.hashCode(), ONE_SIG_FIG.hashCode()); assertEquals(UNCERTAINTY_BASED.hashCode(), UNCERTAINTY_BASED.hashCode()); } /** * Tests that the {@code toString()} methods of the three rounding rule * classes work correctly. * * @since v0.4.0 * @since 2022-07-17 */ @Test void testToString() { assertEquals("Round to 0 decimal places", ZERO_DECIMALS.toString()); assertEquals("Round to 3 significant figures", THREE_SIG_FIGS.toString()); assertEquals("Uncertainty-Based Rounding", UNCERTAINTY_BASED.toString()); } /** * Tests that Uncertainty Rounding works as expected * * @param input input number * @param output expected output string * @since v0.4.0 * @since 2022-07-17 */ @ParameterizedTest @MethodSource("uncertaintyRoundingExamples") void testUncertaintyRounding(UncertainDouble input, String output) { assertEquals(output, UNCERTAINTY_BASED.apply(input), () -> String.format( "Uncertainty Rounding rounded %s as %s (should be %s)", input, UNCERTAINTY_BASED.apply(input), output)); } }