From 544ba103f9903bf885e346d34639c05934655f3f Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Mon, 27 Sep 2021 17:31:22 -0500 Subject: Added some tests for invalid unitfiles --- src/main/java/sevenUnits/unit/UnitDatabase.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/main/java/sevenUnits/unit/UnitDatabase.java') diff --git a/src/main/java/sevenUnits/unit/UnitDatabase.java b/src/main/java/sevenUnits/unit/UnitDatabase.java index b45d9cf..a1dbb0a 100644 --- a/src/main/java/sevenUnits/unit/UnitDatabase.java +++ b/src/main/java/sevenUnits/unit/UnitDatabase.java @@ -1427,10 +1427,11 @@ public final class UnitDatabase { final String expression = lineMatcher.group(2); - if (name.endsWith(" ")) { - System.err.printf("Warning - line %d's unit name ends in a space", - lineCounter); - } + // this code should never occur + // if (name.endsWith(" ")) { + // System.err.printf("Warning - line %d's unit name ends in a space", + // lineCounter); + // } // if expression is "!", search for an existing unit // if no unit found, throw an error -- cgit v1.2.3 From b59082c6b558705d4bd5effce2ae4b98c8a3ebe5 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Mon, 4 Oct 2021 18:26:58 -0500 Subject: Added tests for valid & invalid dimension files --- src/main/java/sevenUnits/unit/UnitDatabase.java | 32 +++++++--- .../java/sevenUnits/unit/UnitDatabaseTest.java | 68 +++++++++++++++++++++- src/test/resources/test-dimensionfile-invalid1.txt | 3 + src/test/resources/test-dimensionfile-invalid2.txt | 1 + src/test/resources/test-dimensionfile-invalid3.txt | 5 ++ src/test/resources/test-dimensionfile-valid1.txt | 12 ++++ 6 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/test/resources/test-dimensionfile-invalid1.txt create mode 100644 src/test/resources/test-dimensionfile-invalid2.txt create mode 100644 src/test/resources/test-dimensionfile-invalid3.txt create mode 100644 src/test/resources/test-dimensionfile-valid1.txt (limited to 'src/main/java/sevenUnits/unit/UnitDatabase.java') diff --git a/src/main/java/sevenUnits/unit/UnitDatabase.java b/src/main/java/sevenUnits/unit/UnitDatabase.java index a1dbb0a..7c72570 100644 --- a/src/main/java/sevenUnits/unit/UnitDatabase.java +++ b/src/main/java/sevenUnits/unit/UnitDatabase.java @@ -1344,10 +1344,10 @@ public final class UnitDatabase { final String name = lineMatcher.group(1); final String expression = lineMatcher.group(2); - if (name.endsWith(" ")) { - System.err.printf("Warning - line %d's dimension name ends in a space", - lineCounter); - } + // if (name.endsWith(" ")) { + // System.err.printf("Warning - line %d's dimension name ends in a space", + // lineCounter); + // } // if expression is "!", search for an existing dimension // if no unit found, throw an error @@ -1360,7 +1360,7 @@ public final class UnitDatabase { final ObjectProduct dimension; try { dimension = this.getDimensionFromExpression(expression); - } catch (final IllegalArgumentException e) { + } catch (final IllegalArgumentException | NoSuchElementException e) { System.err.printf("Parsing error on line %d:%n", lineCounter); throw e; } @@ -1444,7 +1444,8 @@ public final class UnitDatabase { final UnitPrefix prefix; try { prefix = this.getPrefixFromExpression(expression); - } catch (final IllegalArgumentException e) { + } catch (final IllegalArgumentException + | NoSuchElementException e) { System.err.printf("Parsing error on line %d:%n", lineCounter); throw e; } @@ -1454,7 +1455,8 @@ public final class UnitDatabase { final Unit unit; try { unit = this.getUnitFromExpression(expression); - } catch (final IllegalArgumentException e) { + } catch (final IllegalArgumentException + | NoSuchElementException e) { System.err.printf("Parsing error on line %d:%n", lineCounter); throw e; } @@ -1582,8 +1584,15 @@ public final class UnitDatabase { } return base.toExponent(exponent); + } else { + final ObjectProduct dimension = this.dimensions + .get(name); + if (dimension == null) + throw new NoSuchElementException( + "No dimension with name \"" + name + "\"."); + else + return dimension; } - return this.dimensions.get(name); } /** @@ -1696,7 +1705,12 @@ public final class UnitDatabase { try { return UnitPrefix.valueOf(Double.parseDouble(name)); } catch (final NumberFormatException e) { - return this.prefixes.get(name); + final UnitPrefix prefix = this.prefixes.get(name); + if (prefix == null) + throw new NoSuchElementException( + "No prefix with name \"" + name + "\"."); + else + return prefix; } } diff --git a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java index 7612fc5..ed3b6b5 100644 --- a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java +++ b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java @@ -72,6 +72,23 @@ class UnitDatabaseTest { private static final UnitPrefix AB = UnitPrefix.valueOf(7); private static final UnitPrefix BC = UnitPrefix.valueOf(11); + /** + * Loads the dimensionfile at src/test/resources/[path] to the database + * {@code loadTo}. + * + * @param loadTo database to load to + * @param path path of file to load + * @since 2021-10-04 + */ + private static void loadDimensionFile(UnitDatabase loadTo, String path) { + try (final InputStream testFile = UnitDatabaseTest.class + .getResourceAsStream(path)) { + loadTo.loadDimensionsFromStream(testFile); + } catch (final IOException e) { + fail(e.getClass() + " occurred upon loading file \"" + path + "\"."); + } + } + /** * Loads the unitfile at src/test/resources/[path] to the database * {@code loadTo}. @@ -140,6 +157,27 @@ class UnitDatabaseTest { assertThrows(IllegalStateException.class, () -> keySet.toArray()); } + /** + * A bunch of tests for invalid dimension files + * + * @param num which file to test + * @since 2021-10-04 + */ + @ParameterizedTest + @ValueSource(ints = { 1, 2, 3 }) + public void testLoadingInvalidDimensionFile(int num) { + final UnitDatabase database = new UnitDatabase(); + database.addDimension("LENGTH", Metric.Dimensions.LENGTH); + database.addDimension("MASS", Metric.Dimensions.MASS); + database.addDimension("TIME", Metric.Dimensions.TIME); + final String filename = String.format("/test-dimensionfile-invalid%d.txt", + num); + final RuntimeException e = assertThrows(RuntimeException.class, + () -> loadDimensionFile(database, filename)); + assertTrue(e instanceof IllegalArgumentException + || e instanceof NoSuchElementException); + } + /** * A bunch of tests for invalid unit files * @@ -148,12 +186,32 @@ class UnitDatabaseTest { */ @ParameterizedTest @ValueSource(ints = { 1, 2, 3, 4, 5 }) - public void testLoadingInvalidFile(int num) { + public void testLoadingInvalidUnitFile(int num) { final UnitDatabase database = new UnitDatabase(); final String filename = String.format("/test-unitsfile-invalid%d.txt", num); - assertThrows(IllegalArgumentException.class, + final RuntimeException e = assertThrows(RuntimeException.class, () -> loadUnitsFile(database, filename)); + assertTrue(e instanceof IllegalArgumentException + || e instanceof NoSuchElementException); + } + + /** + * Tests loading a valid dimension-file with some derived dimensions. + * + * @since 2021-10-04 + */ + @Test + public void testLoadingValidDimensions() { + final UnitDatabase database = new UnitDatabase(); + database.addDimension("LENGTH", Metric.Dimensions.LENGTH); + database.addDimension("MASS", Metric.Dimensions.MASS); + database.addDimension("TIME", Metric.Dimensions.TIME); + + loadDimensionFile(database, "/test-dimensionfile-valid1.txt"); + assertEquals(Metric.Dimensions.ENERGY, database.getDimension("ENERGY")); + assertEquals(Metric.Dimensions.POWER, database.getDimension("POWER")); + } /** @@ -207,6 +265,12 @@ class UnitDatabaseTest { final Unit actual3 = database.getUnit("test3"); assertEquals(expected3, actual3); + final UnitValue expected4 = UnitValue.of(U, 1); + final UnitValue actual4 = database + .evaluateUnitExpression("-5 * U + -3 * U + 12 * U - 3 * U") + .asUnitValue(); + assertEquals(expected4, actual4); + assertTrue(System.err.toString().length() > 0); } diff --git a/src/test/resources/test-dimensionfile-invalid1.txt b/src/test/resources/test-dimensionfile-invalid1.txt new file mode 100644 index 0000000..ff9ccd8 --- /dev/null +++ b/src/test/resources/test-dimensionfile-invalid1.txt @@ -0,0 +1,3 @@ +LENGTH +MASS +TIME diff --git a/src/test/resources/test-dimensionfile-invalid2.txt b/src/test/resources/test-dimensionfile-invalid2.txt new file mode 100644 index 0000000..2818cfc --- /dev/null +++ b/src/test/resources/test-dimensionfile-invalid2.txt @@ -0,0 +1 @@ +NONEXISTENT ! diff --git a/src/test/resources/test-dimensionfile-invalid3.txt b/src/test/resources/test-dimensionfile-invalid3.txt new file mode 100644 index 0000000..a16f941 --- /dev/null +++ b/src/test/resources/test-dimensionfile-invalid3.txt @@ -0,0 +1,5 @@ +LENGTH ! +MASS ! +TIME ! + +BAD LENGTH +-+ TIME \ No newline at end of file diff --git a/src/test/resources/test-dimensionfile-valid1.txt b/src/test/resources/test-dimensionfile-valid1.txt new file mode 100644 index 0000000..fc6a426 --- /dev/null +++ b/src/test/resources/test-dimensionfile-valid1.txt @@ -0,0 +1,12 @@ +LENGTH ! +MASS ! +TIME ! + +ENERGY MASS * LENGTH^2 / TIME^2 +POWER ENERGY / TIME + +# doesn't work, but would require major changes to fix properly +# for now, just don't use brackets in dimension expressions +# (note that the unit/prefix expressions use a complete hack +# to enable this, one that doesn't work for dimensions) +# POWER MASS * (LENGTH / TIME)^2 / TIME \ No newline at end of file -- cgit v1.2.3 From b4fd2b39e85e2a086e65555ad7c45244bec8ae37 Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Thu, 7 Oct 2021 16:21:00 -0500 Subject: Added tests for getUnit and the prefixed unit map Also fixed a bug where a prefixed unit map with units but no prefixes would appear empty --- src/main/java/sevenUnits/unit/UnitDatabase.java | 12 +- .../utils/ConditionalExistenceCollections.java | 2 +- .../java/sevenUnits/unit/UnitDatabaseTest.java | 140 ++++++++++++++++++++- 3 files changed, 146 insertions(+), 8 deletions(-) (limited to 'src/main/java/sevenUnits/unit/UnitDatabase.java') diff --git a/src/main/java/sevenUnits/unit/UnitDatabase.java b/src/main/java/sevenUnits/unit/UnitDatabase.java index 7c72570..d3c65b2 100644 --- a/src/main/java/sevenUnits/unit/UnitDatabase.java +++ b/src/main/java/sevenUnits/unit/UnitDatabase.java @@ -243,7 +243,8 @@ public final class UnitDatabase { return false; else { if (this.prefixNames.isEmpty()) - return this.unitNamePosition >= this.unitNames.size() - 1; + return this.prefixCoordinates.isEmpty() + && this.unitNamePosition < this.unitNames.size(); else return true; } @@ -557,7 +558,8 @@ public final class UnitDatabase { return false; else { if (this.prefixNames.isEmpty()) - return this.unitNamePosition >= this.unitNames.size() - 1; + return this.prefixCoordinates.isEmpty() + && this.unitNamePosition < this.unitNames.size(); else return true; } @@ -1038,7 +1040,7 @@ public final class UnitDatabase { @Override public String toString() { if (this.units.isEmpty() || this.prefixes.isEmpty()) - return super.toString(); + return new HashMap<>(this).toString(); else return String.format( "Infinite map of name-unit entries created from units %s and prefixes %s", @@ -1645,7 +1647,7 @@ public final class UnitDatabase { * @since 2019-03-22 * @since v0.2.0 */ - private LinearUnit getLinearUnit(final String name) { + LinearUnit getLinearUnit(final String name) { // see if I am using a function-unit like tempC(100) Objects.requireNonNull(name, "name may not be null"); if (name.contains("(") && name.contains(")")) { @@ -1680,7 +1682,7 @@ public final class UnitDatabase { * @return {@code LinearUnitValue} instance * @since 2020-08-04 */ - private LinearUnitValue getLinearUnitValue(final String name) { + LinearUnitValue getLinearUnitValue(final String name) { try { // try to parse it as a number - otherwise it is not a number! final BigDecimal number = new BigDecimal(name); diff --git a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java index 2adb579..bee4dd1 100644 --- a/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java +++ b/src/main/java/sevenUnits/utils/ConditionalExistenceCollections.java @@ -283,7 +283,7 @@ public final class ConditionalExistenceCollections { @Override public Set keySet() { - return conditionalExistenceSet(super.keySet(), + return conditionalExistenceSet(this.map.keySet(), k -> this.entryExistenceCondition.test(this.getEntry(k))); } diff --git a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java index ed3b6b5..90c18e6 100644 --- a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java +++ b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java @@ -25,11 +25,13 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Set; import org.junit.jupiter.api.Test; @@ -47,21 +49,70 @@ import sevenUnits.utils.UncertainDouble; * @since v0.2.0 */ class UnitDatabaseTest { + private static final class SimpleEntry implements Map.Entry { + private final K key; + + private V value; + + /** + * + * @since 2021-10-07 + */ + public SimpleEntry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof Map.Entry)) + return false; + final Map.Entry other = (Map.Entry) obj; + return Objects.equals(this.key, other.getKey()) + && Objects.equals(this.value, other.getValue()); + } + + @Override + public K getKey() { + return this.key; + } + + @Override + public V getValue() { + return this.value; + } + + @Override + public int hashCode() { + return (this.key == null ? 0 : this.key.hashCode()) + ^ (this.value == null ? 0 : this.value.hashCode()); + } + + @Override + public V setValue(V value) { + final V oldValue = this.value; + this.value = value; + return oldValue; + } + } + // some linear units and one nonlinear private static final Unit U = Metric.METRE; private static final Unit V = Metric.KILOGRAM; - private static final Unit W = Metric.SECOND; + private static final Unit W = Metric.SECOND; // used for testing expressions // J = U^2 * V / W^2 private static final LinearUnit J = Metric.KILOGRAM .times(Metric.METRE.toExponent(2)) .dividedBy(Metric.SECOND.toExponent(2)); + private static final LinearUnit K = Metric.KELVIN; private static final Unit NONLINEAR = Unit.fromConversionFunctions( Metric.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) .withName(NameSymbol.ofName("A")); @@ -70,8 +121,23 @@ class UnitDatabaseTest { private static final UnitPrefix C = UnitPrefix.valueOf(5) .withName(NameSymbol.ofName("C")); private static final UnitPrefix AB = UnitPrefix.valueOf(7); + private static final UnitPrefix BC = UnitPrefix.valueOf(11); + /** + * Gets a map entry. + * + * @param type of key + * @param type of value + * @param key key in entry + * @param value value in entry + * @return entry + * @since 2021-10-07 + */ + private static Map.Entry entry(K key, V value) { + return new SimpleEntry<>(key, value); + } + /** * Loads the dimensionfile at src/test/resources/[path] to the database * {@code loadTo}. @@ -131,6 +197,39 @@ class UnitDatabaseTest { assertEquals(expected, actual); } + /** + * Test for {@link UnitDatabase#getUnit}, {@link UnitDatabase#getLinearUnit} + * and {@link UnitDatabase#getLinearUnitValue}. + * + * @since 2021-10-07 + */ + @Test + public void testGetUnit() { + final UnitDatabase database = new UnitDatabase(); + + database.addUnit("m", Metric.METRE); + database.addUnit("meter", Metric.METRE); + database.addUnit("metre", Metric.METRE); + database.addUnit("badname", Metric.METRE); + database.addUnit("K", Metric.KELVIN); + database.addUnit("degC", Metric.CELSIUS); + + // ensure getUnit returns units, regardless of whether the name is one of + // the unit's names + assertEquals(Metric.METRE, database.getUnit("m")); + assertEquals(Metric.METRE, database.getUnit("metre")); + assertEquals(Metric.METRE, database.getUnit("meter")); + assertEquals(Metric.METRE, database.getUnit("badname")); + assertThrows(NoSuchElementException.class, + () -> database.getUnit("blabla")); + + assertEquals(Metric.KELVIN, database.getLinearUnit("K")); + assertThrows(IllegalArgumentException.class, + () -> database.getLinearUnit("degC")); + assertEquals(Metric.KELVIN.times(373.15), + database.getLinearUnit("degC(100)")); + } + /** * Confirms that operations that shouldn't function for infinite databases * throw an {@code IllegalStateException}. @@ -274,6 +373,43 @@ class UnitDatabaseTest { assertTrue(System.err.toString().length() > 0); } + /** + * Tests the iterator of the prefixless unit map. These tests are simple, as + * the unit map iterator is simple. + * + * @since 2021-10-07 + */ + @Test + public void testPrefixedUnitMapIterator() { + final UnitDatabase database1 = new UnitDatabase(); + + database1.addUnit("U", U); + database1.addUnit("V", V); + database1.addUnit("W", W); + + final Map map1 = database1.unitMap(); + final Iterator keyIterator1 = map1.keySet().iterator(); + final Iterator> entryIterator1 = map1.entrySet() + .iterator(); + + final Set expectedKeys = Set.of("U", "V", "W"); + final Set actualKeys = new HashSet<>(); + while (keyIterator1.hasNext()) { + actualKeys.add(keyIterator1.next()); + } + assertEquals(expectedKeys, actualKeys); + assertEquals(expectedKeys, map1.keySet()); + + final Set> expectedEntries = Set.of(entry("U", U), + entry("V", V), entry("W", W)); + final Set> actualEntries = new HashSet<>(); + while (entryIterator1.hasNext()) { + actualEntries.add(entryIterator1.next()); + } + assertEquals(expectedEntries, actualEntries); + assertEquals(expectedEntries, map1.entrySet()); + } + /** * Test that prefixes correctly apply to units. * -- cgit v1.2.3 From f422742fb7bf45eb0ce6577bd2f9f7e22b739a6b Mon Sep 17 00:00:00 2001 From: Adrien Hopkins Date: Tue, 2 Nov 2021 16:49:10 -0500 Subject: Added a test for UnitDatabase.isRemovableDuplicate --- src/main/java/sevenUnits/unit/UnitDatabase.java | 2 +- .../java/sevenUnits/unit/UnitDatabaseTest.java | 44 ++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) (limited to 'src/main/java/sevenUnits/unit/UnitDatabase.java') diff --git a/src/main/java/sevenUnits/unit/UnitDatabase.java b/src/main/java/sevenUnits/unit/UnitDatabase.java index d3c65b2..18ac619 100644 --- a/src/main/java/sevenUnits/unit/UnitDatabase.java +++ b/src/main/java/sevenUnits/unit/UnitDatabase.java @@ -1163,7 +1163,7 @@ public final class UnitDatabase { * @return true if entry represents a removable duplicate entry of unitMap. * @since 2021-05-22 */ - private static boolean isRemovableDuplicate(Map unitMap, + static boolean isRemovableDuplicate(Map unitMap, Entry entry) { for (final Entry e : unitMap.entrySet()) { final String name = e.getKey(); diff --git a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java index 90c18e6..033d763 100644 --- a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java +++ b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java @@ -25,6 +25,7 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -195,6 +196,10 @@ class UnitDatabaseTest { final LinearUnitValue actual = database .evaluateUnitExpression("J + (2 * 3) J + (20 / 4) J"); assertEquals(expected, actual); + + // check that negation works properly + assertEquals(2, + database.evaluateUnitExpression("J - -1 * J").getValueExact()); } /** @@ -491,6 +496,45 @@ class UnitDatabaseTest { assertThrows(NoSuchElementException.class, () -> database.getUnit("Z")); } + @Test + public void testRemovableDuplicates() { + final Map unitMap = new HashMap<>(); + unitMap.put("meter", Metric.METRE); + unitMap.put("metre", Metric.METRE); + unitMap.put("m", Metric.METRE); + unitMap.put("second", Metric.SECOND); + + assertTrue(UnitDatabase.isRemovableDuplicate(unitMap, + entry("m", Metric.METRE))); + assertTrue(UnitDatabase.isRemovableDuplicate(unitMap, + entry("meter", Metric.METRE))); + assertFalse(UnitDatabase.isRemovableDuplicate(unitMap, + entry("metre", Metric.METRE))); + assertFalse(UnitDatabase.isRemovableDuplicate(unitMap, + entry("second", Metric.SECOND))); + } + + @Test + public void testToString() { + final UnitDatabase database = new UnitDatabase(); + + database.addUnit("J", J); + database.addUnit("K", J); + + database.addPrefix("A", A); + database.addPrefix("B", B); + database.addPrefix("C", C); + + if ("Unit Database with 1 units, 3 unit prefixes and 0 dimensions" + .equals(database.toString())) { + fail("Database counts by number of units, not number of unit names."); + } + + assertEquals( + "Unit Database with 2 units, 3 unit prefixes and 0 dimensions", + database.toString()); + } + /** * Test that unit expressions return the correct value. * -- cgit v1.2.3