/**
* Copyright (C) 2019 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 org.unitConverter;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.junit.jupiter.api.Test;
import org.unitConverter.unit.DefaultUnitPrefix;
import org.unitConverter.unit.LinearUnit;
import org.unitConverter.unit.SI;
import org.unitConverter.unit.Unit;
import org.unitConverter.unit.UnitPrefix;
/**
* A test for the {@link UnitsDatabase} class. This is NOT part of this program's public API.
*
* @author Adrien Hopkins
* @since 2019-04-14
* @since v0.2.0
*/
class UnitsDatabaseTest {
// some linear units and one nonlinear
private static final Unit U = SI.METRE;
private static final Unit V = SI.KILOGRAM;
private static final Unit W = SI.SECOND;
// used for testing expressions
// J = U^2 * V / W^2
private static final LinearUnit J = SI.KILOGRAM.times(SI.METRE.toExponent(2)).dividedBy(SI.SECOND.toExponent(2));
private static final LinearUnit K = SI.KELVIN;
private static final Unit NONLINEAR = Unit.fromConversionFunctions(SI.METRE, o -> o + 1, o -> o - 1);
// make the prefix values prime so I can tell which multiplications were made
private static final UnitPrefix A = new DefaultUnitPrefix(2);
private static final UnitPrefix B = new DefaultUnitPrefix(3);
private static final UnitPrefix C = new DefaultUnitPrefix(5);
private static final UnitPrefix AB = new DefaultUnitPrefix(7);
private static final UnitPrefix BC = new DefaultUnitPrefix(11);
/**
* Confirms that operations that shouldn't function for infinite databases throw an {@code IllegalStateException}.
*
* @since 2019-05-03
*/
@Test
public void testInfiniteSetExceptions() {
// load units
final UnitsDatabase infiniteDatabase = new UnitsDatabase();
infiniteDatabase.addUnit("J", J);
infiniteDatabase.addUnit("K", K);
infiniteDatabase.addPrefix("A", A);
infiniteDatabase.addPrefix("B", B);
infiniteDatabase.addPrefix("C", C);
{
boolean exceptionThrown = false;
try {
infiniteDatabase.unitMap().entrySet().toArray();
} catch (final IllegalStateException e) {
exceptionThrown = true;
// pass!
} finally {
if (!exceptionThrown) {
fail("No IllegalStateException thrown");
}
}
}
{
boolean exceptionThrown = false;
try {
infiniteDatabase.unitMap().keySet().toArray();
} catch (final IllegalStateException e) {
exceptionThrown = true;
// pass!
} finally {
if (!exceptionThrown) {
fail("No IllegalStateException thrown");
}
}
}
}
/**
* Test that prefixes correctly apply to units.
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testPrefixes() {
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("U", U);
database.addUnit("V", V);
database.addUnit("W", W);
database.addPrefix("A", A);
database.addPrefix("B", B);
database.addPrefix("C", C);
// get the product
final Unit abcuNonlinear = database.getUnit("ABCU");
assert abcuNonlinear instanceof LinearUnit;
final LinearUnit abcu = (LinearUnit) abcuNonlinear;
assertEquals(A.getMultiplier() * B.getMultiplier() * C.getMultiplier(), abcu.getConversionFactor(), 1e-15);
}
/**
* Tests the functionnalites of the prefixless unit map.
*
*
* The map should be an auto-updating view of the units in the database.
*
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testPrefixlessUnitMap() {
final UnitsDatabase database = new UnitsDatabase();
final Map prefixlessUnits = database.unitMapPrefixless();
database.addUnit("U", U);
database.addUnit("V", V);
database.addUnit("W", W);
// this should work because the map should be an auto-updating view
assertTrue(prefixlessUnits.containsKey("U"));
assertFalse(prefixlessUnits.containsKey("Z"));
assertTrue(prefixlessUnits.containsValue(U));
assertFalse(prefixlessUnits.containsValue(NONLINEAR));
}
/**
* Tests that the database correctly stores and retrieves units, ignoring prefixes.
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testPrefixlessUnits() {
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("U", U);
database.addUnit("V", V);
database.addUnit("W", W);
assertTrue(database.containsUnitName("U"));
assertFalse(database.containsUnitName("Z"));
assertEquals(U, database.getUnit("U"));
assertEquals(null, database.getUnit("Z"));
}
/**
* Test that unit expressions return the correct value.
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testUnitExpressions() {
// load units
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("U", U);
database.addUnit("V", V);
database.addUnit("W", W);
database.addUnit("fj", J.times(5));
database.addUnit("ej", J.times(8));
database.addPrefix("A", A);
database.addPrefix("B", B);
database.addPrefix("C", C);
// first test - test prefixes and operations
final Unit expected1 = J.withPrefix(A).withPrefix(B).withPrefix(C).withPrefix(C);
final Unit actual1 = database.getUnitFromExpression("ABV * CU^2 / W / W");
assertEquals(expected1, actual1);
// second test - test addition and subtraction
final Unit expected2 = J.times(58);
final Unit actual2 = database.getUnitFromExpression("2 fj + 6 ej");
assertEquals(expected2, actual2);
}
/**
* Tests both the unit name iterator and the name-unit entry iterator
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testUnitIterator() {
// load units
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("J", J);
database.addUnit("K", K);
database.addPrefix("A", A);
database.addPrefix("B", B);
database.addPrefix("C", C);
final int NUM_UNITS = database.unitMapPrefixless().size();
final int NUM_PREFIXES = database.prefixMap().size();
final Iterator nameIterator = database.unitMap().keySet().iterator();
final Iterator> entryIterator = database.unitMap().entrySet().iterator();
int expectedLength = 1;
int unitsWithThisLengthSoFar = 0;
// loop 1000 times
for (int i = 0; i < 1000; i++) {
// expected length of next
if (unitsWithThisLengthSoFar >= NUM_UNITS * (int) Math.pow(NUM_PREFIXES, expectedLength - 1)) {
expectedLength++;
unitsWithThisLengthSoFar = 0;
}
// test that stuff is valid
final String nextName = nameIterator.next();
final Unit nextUnit = database.getUnit(nextName);
final Entry nextEntry = entryIterator.next();
assertEquals(expectedLength, nextName.length());
assertEquals(nextName, nextEntry.getKey());
assertEquals(nextUnit, nextEntry.getValue());
unitsWithThisLengthSoFar++;
}
// test toString for consistency
final String entryIteratorString = entryIterator.toString();
for (int i = 0; i < 3; i++) {
assertEquals(entryIteratorString, entryIterator.toString());
}
final String nameIteratorString = nameIterator.toString();
for (int i = 0; i < 3; i++) {
assertEquals(nameIteratorString, nameIterator.toString());
}
}
/**
* Determine, given a unit name that could mean multiple things, which meaning is chosen.
*
* For example, "ABCU" could mean "A-B-C-U", "AB-C-U", or "A-BC-U". In this case, "AB-C-U" is the correct choice.
*
*
* @since 2019-04-14
* @since v0.2.0
*/
@Test
public void testUnitPrefixCombinations() {
// load units
final UnitsDatabase database = new UnitsDatabase();
database.addUnit("J", J);
database.addPrefix("A", A);
database.addPrefix("B", B);
database.addPrefix("C", C);
database.addPrefix("AB", AB);
database.addPrefix("BC", BC);
// test 1 - AB-C-J vs A-BC-J vs A-B-C-J
final Unit expected1 = J.withPrefix(AB).withPrefix(C);
final Unit actual1 = database.getUnit("ABCJ");
assertEquals(expected1, actual1);
// test 2 - ABC-J vs AB-CJ vs AB-C-J
database.addUnit("CJ", J.times(13));
database.addPrefix("ABC", new DefaultUnitPrefix(17));
final Unit expected2 = J.times(17);
final Unit actual2 = database.getUnit("ABCJ");
assertEquals(expected2, actual2);
}
}