/**
* 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 2022-07-17
* @since v0.4.0
*/
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 2022-07-17
* @since v0.4.0
*/
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 2022-07-17
* @since v0.4.0
*/
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 2022-07-17
* @since v0.4.0
*/
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 2022-07-17
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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 2022-07-17
* @since v0.4.0
*/
@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));
}
}