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