/** * 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.unit; 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; /** * A test for the {@link UnitDatabase} class. This is NOT part of this program's public API. * * @author Adrien Hopkins * @since 2019-04-14 * @since v0.2.0 */ class UnitDatabaseTest { // 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.getBase(), o -> o + 1, o -> o - 1); // make the prefix values prime so I can tell which multiplications were made private static final UnitPrefix A = UnitPrefix.valueOf(2); private static final UnitPrefix B = UnitPrefix.valueOf(3); private static final UnitPrefix C = UnitPrefix.valueOf(5); private static final UnitPrefix AB = UnitPrefix.valueOf(7); private static final UnitPrefix BC = UnitPrefix.valueOf(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 UnitDatabase infiniteDatabase = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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 UnitDatabase database = new UnitDatabase(); 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", UnitPrefix.valueOf(17)); final Unit expected2 = J.times(17); final Unit actual2 = database.getUnit("ABCJ"); assertEquals(expected2, actual2); } }