/** * Copyright (C) 2021, 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 sevenUnits.unit; 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 java.math.RoundingMode; import java.util.List; 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.NameSymbol; import sevenUnits.utils.UncertainDouble; /** * Tests for the UnitValue and LinearUnitValue classes * * @since v1.0.0 */ public final class UnitValueTest { private static Stream testConvertToMultiple() { return Stream.of( Arguments.of( LinearUnitValue.getExact(BritishImperial.Length.INCH, 64), List.of(BritishImperial.Length.FOOT, BritishImperial.Length.INCH), List.of(5.0, 4.0)), Arguments.of(LinearUnitValue.getExact(Metric.SECOND, 44102.5), List.of(Metric.HOUR, Metric.MINUTE, Metric.SECOND), List.of(12.0, 15.0, 2.5))); } @ParameterizedTest @MethodSource public void testConvertToMultiple(LinearUnitValue valFrom, List others, List expectedValues) { final var actual = valFrom.convertToMultiple(others); assertEquals(others.size(), expectedValues.size(), String.format( "%s converted to %s had the wrong number of resulting units (expected: %d, actual: %d).", valFrom, others, others.size(), actual.size())); for (var i = 0; i < others.size(); i++) { final var expectedValue = LinearUnitValue.getExact(others.get(i), expectedValues.get(i)); assertTrue(expectedValue.equivalent(actual.get(i)), String.format("Value %d: expected %s, actual %s.", i, expectedValue, actual.get(i))); } } @Test public void testConvertToMultipleErrors() { assertThrows(IllegalArgumentException.class, () -> LinearUnitValue.ONE.convertToMultiple(List.of())); assertThrows(IllegalArgumentException.class, () -> LinearUnitValue.ONE.convertToMultiple(List.of(Metric.METRE))); } // I am intentionally testing whether or not equals gracefully returns false // when it gets an argument of the wrong type @SuppressWarnings("unlikely-arg-type") @Test public void testEqualsHashCode() { final var v1 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(1.0, 0.01)); final var v2 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(1.0, 0.01)); final var v3 = LinearUnitValue.of(Metric.MILLIMETRE, UncertainDouble.of(1000.0, 10.0)); final var v4 = LinearUnitValue.of(Metric.MILLIMETRE, UncertainDouble.of(1000.0, 20.0)); assertTrue(Objects.equals(v1, v2), "Two identical values are not equal()"); assertTrue(Objects.equals(v1.hashCode(), v2.hashCode()), "Two identical values have different hash codes."); assertTrue(Objects.equals(v1, v3), "Two values with the same value but different units are not equal()"); assertTrue(Objects.equals(v1.hashCode(), v3.hashCode()), "Two values with the same value but different units have different hash codes."); assertFalse(Objects.equals(v1, v4), "Two values with the same value but different uncertainties are equal()"); assertFalse(Objects.equals(v1, "Hello")); assertFalse(v1.equals("Hello", true)); } @Test public void testEqualsNoFP() { final var v1 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(1.0, 0.01)); final var v2 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(1.0, 0.01)); final var v3 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(1.0 + 5e-16, 0.01)); assertTrue(v1.equals(v2, false)); assertFalse(v1.equals(v3, false)); assertTrue(v1.equals(v3, true)); } @Test public void testOperations() { final var v1 = LinearUnitValue.getExact(Metric.METRE, 2.0); final var v2 = LinearUnitValue.getExact(Metric.METRE, 4.0); assertEquals(v2, v1.times(2)); assertEquals(v1, v2.dividedBy(2)); assertEquals(LinearUnitValue.getExact(Metric.SQUARE_METRE, 8.0), v1.times(v2)); assertEquals(LinearUnitValue.getExact(Metric.ONE, 0.5), v1.dividedBy(v2)); assertEquals(LinearUnitValue.getExact(Metric.SQUARE_METRE, 4.0), v1.toExponent(2)); assertEquals(LinearUnitValue.getExact(Metric.SQUARE_METRE, 4.0), v1.toExponentRounded(2.0)); } @Test public void testValueEquivalent() { final var v1 = LinearUnitValue.of(BritishImperial.Length.INCH, UncertainDouble.of(1.0, 0.0)); final var v2 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(0.0255, 0.0)); assertFalse(v1.equivalent(v2)); final var v3 = LinearUnitValue.of(BritishImperial.Length.INCH, UncertainDouble.of(1.0, 1.0)); final var v4 = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(0.0255, 0.025)); assertTrue(v3.equivalent(v4)); } @Test public void testValueEquivalentErrors() { final var v1 = LinearUnitValue.of(BritishImperial.Length.INCH, UncertainDouble.of(1.0, 0.0)); final var v2 = LinearUnitValue.of(BritishImperial.CALORIE, UncertainDouble.of(1.0, 0.0)); assertFalse(v1.equivalent(v2)); assertFalse(v1.equivalent(null)); } /** * Tests converting an uncertain LinearUnitValue to a string. * * @since 2021-11-04 * @since v0.3.2 */ @Test public void testValueToString1() { final var value = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(10, 0.24)); assertEquals("(10.0 ± 0.2) m", value.toString()); assertEquals("(10.0 ± 0.2) m", value.toString(true, RoundingMode.HALF_EVEN)); assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } /** * Tests converting a certain LinearUnitValue to a string. * * @since 2021-11-04 * @since v0.3.2 */ @Test public void testValueToString2() { final var value = LinearUnitValue.of(Metric.METRE, UncertainDouble.of(10, 0)); assertEquals("10.0 m", value.toString()); assertEquals("(10.0 ± 0.0) m", value.toString(true, RoundingMode.HALF_EVEN)); assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } /** * Tests converting an unnamed LinearUnitValue to a string. * * @since 2021-11-04 * @since v0.3.2 */ @Test public void testValueToString3() { final var value = LinearUnitValue.of( Metric.METRE.withName(NameSymbol.EMPTY), UncertainDouble.of(10, 0.24)); assertEquals("10.0 unnamed unit (= 10.0 m)", value.toString(false, RoundingMode.HALF_EVEN)); } /** * Tests converting a named UnitValue to a string. * * @since 2021-11-04 * @since v0.3.2 */ @Test public void testValueToString4() { final var value = UnitValue.of(BritishImperial.FAHRENHEIT, 80); assertEquals("80.0 \u00B0F", value.toString()); } /** * Tests converting an unnamed UnitValue to a string. * * @since 2021-11-04 * @since v0.3.2 */ @Test public void testValueToString5() { final var value = UnitValue .of(USCustomary.FAHRENHEIT.withName(NameSymbol.EMPTY), 50); assertEquals("50.0 unnamed unit (= 283.15 K)", value.toString()); } }