diff options
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/java/sevenUnits/unit/MultiUnitTest.java | 16 | ||||
-rw-r--r-- | src/test/java/sevenUnits/unit/UnitDatabaseTest.java | 3 | ||||
-rw-r--r-- | src/test/java/sevenUnits/unit/UnitTest.java | 15 | ||||
-rw-r--r-- | src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java | 54 | ||||
-rw-r--r-- | src/test/java/sevenUnits/utils/SemanticVersionTest.java | 399 | ||||
-rw-r--r-- | src/test/java/sevenUnits/utils/UncertainDoubleTest.java | 20 | ||||
-rw-r--r-- | src/test/java/sevenUnitsGUI/PrefixRepetitionTest.java | 123 | ||||
-rw-r--r-- | src/test/java/sevenUnitsGUI/PrefixSearchTest.java | 158 | ||||
-rw-r--r-- | src/test/java/sevenUnitsGUI/PresenterTest.java | 423 | ||||
-rw-r--r-- | src/test/java/sevenUnitsGUI/RoundingTest.java | 287 | ||||
-rw-r--r-- | src/test/java/sevenUnitsGUI/TabbedViewTest.java | 95 |
11 files changed, 1558 insertions, 35 deletions
diff --git a/src/test/java/sevenUnits/unit/MultiUnitTest.java b/src/test/java/sevenUnits/unit/MultiUnitTest.java index 39ee21c..30f2941 100644 --- a/src/test/java/sevenUnits/unit/MultiUnitTest.java +++ b/src/test/java/sevenUnits/unit/MultiUnitTest.java @@ -32,30 +32,34 @@ import org.junit.jupiter.api.Test; */ class MultiUnitTest { + /** + * Ensures that the {@code MultiUnit} can convert properly. + */ @Test final void testConvert() { final Random rng = ThreadLocalRandom.current(); final MultiUnit footInch = MultiUnit.of(BritishImperial.Length.FOOT, BritishImperial.Length.INCH); - assertEquals(1702.0, footInch.convertTo(Metric.METRE.withPrefix(Metric.MILLI), - Arrays.asList(5.0, 7.0)), 1.0); + assertEquals(1702.0, + footInch.convertTo(Metric.METRE.withPrefix(Metric.MILLI), + Arrays.asList(5.0, 7.0)), + 1.0); for (int i = 0; i < 1000; i++) { final double feet = rng.nextInt(1000); final double inches = rng.nextDouble() * 12; final double millimetres = feet * 304.8 + inches * 25.4; - final List<Double> feetAndInches = Metric.METRE.withPrefix(Metric.MILLI) - .convertTo(footInch, millimetres); + final List<Double> feetAndInches = Metric.METRE + .withPrefix(Metric.MILLI).convertTo(footInch, millimetres); assertEquals(feet, feetAndInches.get(0), 1e-10); assertEquals(inches, feetAndInches.get(1), 1e-10); } } /** - * Test method for - * {@link sevenUnits.unit.MultiUnit#convertFromBase(double)}. + * Test method for {@link sevenUnits.unit.MultiUnit#convertFromBase(double)}. */ @Test final void testConvertFromBase() { diff --git a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java index 2276d7c..4be33dd 100644 --- a/src/test/java/sevenUnits/unit/UnitDatabaseTest.java +++ b/src/test/java/sevenUnits/unit/UnitDatabaseTest.java @@ -39,6 +39,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import sevenUnits.utils.NameSymbol; import sevenUnits.utils.UncertainDouble; /** @@ -595,7 +596,7 @@ class UnitDatabaseTest { database.addPrefix("C", C); final int NUM_UNITS = database.unitMapPrefixless(true).size(); - final int NUM_PREFIXES = database.prefixMap().size(); + final int NUM_PREFIXES = database.prefixMap(true).size(); final Iterator<String> nameIterator = database.unitMap().keySet() .iterator(); diff --git a/src/test/java/sevenUnits/unit/UnitTest.java b/src/test/java/sevenUnits/unit/UnitTest.java index bb2e6a4..d3699ca 100644 --- a/src/test/java/sevenUnits/unit/UnitTest.java +++ b/src/test/java/sevenUnits/unit/UnitTest.java @@ -21,12 +21,14 @@ 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.Random; import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.Test; import sevenUnits.utils.DecimalComparison; +import sevenUnits.utils.NameSymbol; import sevenUnits.utils.UncertainDouble; /** @@ -163,8 +165,9 @@ class UnitTest { UncertainDouble.of(10, 0.24)); assertEquals("(10.0 ± 0.2) m", value.toString()); - assertEquals("(10.0 ± 0.2) m", value.toString(true)); - assertEquals("10.0 m", value.toString(false)); + assertEquals("(10.0 ± 0.2) m", + value.toString(true, RoundingMode.HALF_EVEN)); + assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } /** @@ -178,8 +181,9 @@ class UnitTest { UncertainDouble.of(10, 0)); assertEquals("10.0 m", value.toString()); - assertEquals("(10.0 ± 0.0) m", value.toString(true)); - assertEquals("10.0 m", value.toString(false)); + assertEquals("(10.0 ± 0.0) m", + value.toString(true, RoundingMode.HALF_EVEN)); + assertEquals("10.0 m", value.toString(false, RoundingMode.HALF_EVEN)); } /** @@ -193,7 +197,8 @@ class UnitTest { Metric.METRE.withName(NameSymbol.EMPTY), UncertainDouble.of(10, 0.24)); - assertEquals("10.0 unnamed unit (= 10.0 m)", value.toString(false)); + assertEquals("10.0 unnamed unit (= 10.0 m)", + value.toString(false, RoundingMode.HALF_EVEN)); } /** diff --git a/src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java b/src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java index d653848..6b5f9cf 100644 --- a/src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java +++ b/src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java @@ -34,13 +34,16 @@ import org.junit.jupiter.api.Test; import sevenUnits.utils.ConditionalExistenceCollections.ConditionalExistenceIterator; /** - * Tests the {@link #ConditionalExistenceCollections}. + * Tests the {@link #ConditionalExistenceCollections}. Specifically, it runs + * normal operations on conditional existence collections and ensures that + * elements that do not pass the existence condition are not included in the + * results. * * @author Adrien Hopkins * @since 2019-10-16 */ class ConditionalExistenceCollectionsTest { - + /** * The returned iterator ignores elements that don't start with "a". * @@ -54,7 +57,7 @@ class ConditionalExistenceCollectionsTest { .conditionalExistenceIterator(it, s -> s.startsWith("a")); return cit; } - + /** * The returned map ignores mappings where the value is zero. * @@ -67,13 +70,14 @@ class ConditionalExistenceCollectionsTest { map.put("two", 2); map.put("zero", 0); map.put("ten", 10); - final Map<String, Integer> conditionalMap = ConditionalExistenceCollections.conditionalExistenceMap(map, - e -> !Integer.valueOf(0).equals(e.getValue())); + final Map<String, Integer> conditionalMap = ConditionalExistenceCollections + .conditionalExistenceMap(map, + e -> !Integer.valueOf(0).equals(e.getValue())); return conditionalMap; } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#containsKey(java.lang.Object)}. + * Test method for the ConditionalExistenceMap's containsKey method. */ @Test void testContainsKeyObject() { @@ -83,9 +87,9 @@ class ConditionalExistenceCollectionsTest { assertFalse(map.containsKey("five")); assertFalse(map.containsKey("zero")); } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#containsValue(java.lang.Object)}. + * Test method for the ConditionalExistenceMap's containsValue method. */ @Test void testContainsValueObject() { @@ -95,9 +99,9 @@ class ConditionalExistenceCollectionsTest { assertFalse(map.containsValue(5)); assertFalse(map.containsValue(0)); } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#entrySet()}. + * Test method for the ConditionalExistenceMap's entrySet method. */ @Test void testEntrySet() { @@ -106,9 +110,9 @@ class ConditionalExistenceCollectionsTest { assertTrue(e.getValue() != 0); } } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#get(java.lang.Object)}. + * Test method for the ConditionalExistenceMap's get method. */ @Test void testGetObject() { @@ -118,43 +122,47 @@ class ConditionalExistenceCollectionsTest { assertEquals(null, map.get("five")); assertEquals(null, map.get("zero")); } - + + /** + * Test method for the ConditionalExistenceCollection's iterator. + */ @Test void testIterator() { - final ConditionalExistenceIterator<String> testIterator = this.getTestIterator(); - + final ConditionalExistenceIterator<String> testIterator = this + .getTestIterator(); + assertTrue(testIterator.hasNext); assertTrue(testIterator.hasNext()); assertEquals("aa", testIterator.nextElement); assertEquals("aa", testIterator.next()); - + assertTrue(testIterator.hasNext); assertTrue(testIterator.hasNext()); assertEquals("ab", testIterator.nextElement); assertEquals("ab", testIterator.next()); - + assertFalse(testIterator.hasNext); assertFalse(testIterator.hasNext()); assertEquals(null, testIterator.nextElement); assertThrows(NoSuchElementException.class, testIterator::next); } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#keySet()}. + * Test method for the ConditionalExistenceMap's keySet operation. */ @Test void testKeySet() { final Map<String, Integer> map = this.getTestMap(); assertFalse(map.keySet().contains("zero")); } - + /** - * Test method for {@link org.unitConverter.math.ZeroIsNullMap#values()}. + * Test method for the ConditionalExistenceMap's values operation. */ @Test void testValues() { final Map<String, Integer> map = this.getTestMap(); assertFalse(map.values().contains(0)); } - + } diff --git a/src/test/java/sevenUnits/utils/SemanticVersionTest.java b/src/test/java/sevenUnits/utils/SemanticVersionTest.java new file mode 100644 index 0000000..877b258 --- /dev/null +++ b/src/test/java/sevenUnits/utils/SemanticVersionTest.java @@ -0,0 +1,399 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnits.utils; + +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 static sevenUnits.utils.SemanticVersionNumber.BUILD_METADATA_COMPARATOR; +import static sevenUnits.utils.SemanticVersionNumber.builder; +import static sevenUnits.utils.SemanticVersionNumber.fromString; +import static sevenUnits.utils.SemanticVersionNumber.isValidVersionString; +import static sevenUnits.utils.SemanticVersionNumber.preRelease; +import static sevenUnits.utils.SemanticVersionNumber.stableVersion; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link SemanticVersionNumber} + * + * @since 2022-02-19 + */ +public final class SemanticVersionTest { + /** + * Test for {@link SemanticVersionNumber#compatible} + * + * @since 2022-02-20 + */ + @Test + public void testCompatibility() { + assertTrue(stableVersion(1, 0, 0).compatibleWith(stableVersion(1, 0, 5)), + "1.0.0 not compatible with 1.0.5"); + assertTrue(stableVersion(1, 3, 1).compatibleWith(stableVersion(1, 4, 0)), + "1.3.1 not compatible with 1.4.0"); + + // 0.y.z should not be compatible with any other version + assertFalse(stableVersion(0, 4, 0).compatibleWith(stableVersion(0, 4, 1)), + "0.4.0 compatible with 0.4.1 (0.y.z versions should be treated as unstable/incompatbile)"); + + // upgrading major version should = incompatible + assertFalse(stableVersion(1, 0, 0).compatibleWith(stableVersion(2, 0, 0)), + "1.0.0 compatible with 2.0.0"); + + // dowgrade should = incompatible + assertFalse(stableVersion(1, 1, 0).compatibleWith(stableVersion(1, 0, 0)), + "1.1.0 compatible with 1.0.0"); + } + + /** + * Tests {@link SemanticVersionNumber#toString} for complex version numbers + * + * @since 2022-02-19 + */ + @Test + public void testComplexToString() { + final SemanticVersionNumber v1 = builder(1, 2, 3).preRelease(1, 2, 3) + .build(); + assertEquals("1.2.3-1.2.3", v1.toString()); + final SemanticVersionNumber v2 = builder(4, 5, 6).preRelease("abc", 123) + .buildMetadata("2022-02-19").build(); + assertEquals("4.5.6-abc.123+2022-02-19", v2.toString()); + final SemanticVersionNumber v3 = builder(1, 0, 0) + .preRelease("x-y-z", "--").build(); + assertEquals("1.0.0-x-y-z.--", v3.toString()); + } + + /** + * Tests that complex version can be created and their parts read + * + * @since 2022-02-19 + */ + @Test + public void testComplexVersions() { + final SemanticVersionNumber v1 = builder(1, 2, 3).preRelease(1, 2, 3) + .build(); + assertEquals(1, v1.majorVersion()); + assertEquals(2, v1.minorVersion()); + assertEquals(3, v1.patchVersion()); + assertEquals(List.of("1", "2", "3"), v1.preReleaseIdentifiers()); + assertEquals(List.of(), v1.buildMetadata()); + + final SemanticVersionNumber v2 = builder(4, 5, 6).preRelease("abc", 123) + .buildMetadata("2022-02-19").build(); + assertEquals(4, v2.majorVersion()); + assertEquals(5, v2.minorVersion()); + assertEquals(6, v2.patchVersion()); + assertEquals(List.of("abc", "123"), v2.preReleaseIdentifiers()); + assertEquals(List.of("2022-02-19"), v2.buildMetadata()); + + final SemanticVersionNumber v3 = builder(1, 0, 0) + .preRelease("x-y-z", "--").build(); + assertEquals(1, v3.majorVersion()); + assertEquals(0, v3.minorVersion()); + assertEquals(0, v3.patchVersion()); + assertEquals(List.of("x-y-z", "--"), v3.preReleaseIdentifiers()); + assertEquals(List.of(), v3.buildMetadata()); + } + + /** + * Test that semantic version strings can be parsed correctly + * + * @since 2022-02-19 + * @see SemanticVersionNumber#fromString + * @see SemanticVersionNumber#isValidVersionString + */ + @Test + public void testFromString() { + // test that the regex can match version strings + assertTrue(isValidVersionString("1.0.0"), "1.0.0 is treated as invalid"); + assertTrue(isValidVersionString("1.3.9"), "1.3.9 is treated as invalid"); + assertTrue(isValidVersionString("2.0.0-a.1"), + "2.0.0-a.1 is treated as invalid"); + assertTrue(isValidVersionString("1.0.0-a.b.c.d"), + "1.0.0-a.b.c.d is treated as invalid"); + assertTrue(isValidVersionString("1.0.0+abc"), + "1.0.0+abc is treated as invalid"); + assertTrue(isValidVersionString("1.0.0-abc+def"), + "1.0.0-abc+def is treated as invalid"); + + // test that invalid versions don't match + assertFalse(isValidVersionString("1.0"), + "1.0 is treated as valid (patch should be required)"); + assertFalse(isValidVersionString("1.A.0"), + "1.A.0 is treated as valid (main versions must be numbers)"); + assertFalse(isValidVersionString("1.0.0-"), + "1.0.0- is treated as valid (pre-release must not be empty)"); + assertFalse(isValidVersionString("1.0.0+"), + "1.0.0+ is treated as valid (build metadata must not be empty)"); + + // test that versions can be parsed + assertEquals(stableVersion(1, 0, 0), fromString("1.0.0"), + "Could not parse 1.0.0"); + assertEquals( + builder(1, 2, 3).preRelease("abc", "56", "def") + .buildMetadata("2022abc99").build(), + fromString("1.2.3-abc.56.def+2022abc99"), + "Could not parse 1.2.3-abc.56.def+2022abc99"); + } + + /** + * Ensures it is impossible to create invalid version numbers + */ + @Test + public void testInvalidVersionNumbers() { + // stableVersion() + assertThrows(IllegalArgumentException.class, + () -> stableVersion(1, 0, -1), + "Negative patch tolerated by stableVersion"); + assertThrows(IllegalArgumentException.class, + () -> stableVersion(1, -2, 1), + "Negative minor version number tolerated by stableVersion"); + assertThrows(IllegalArgumentException.class, + () -> stableVersion(-3, 0, 7), + "Negative major version number tolerated by stableVersion"); + + // preRelease() + assertThrows(IllegalArgumentException.class, + () -> preRelease(1, 0, -1, "test", 2), + "Negative patch tolerated by preRelease"); + assertThrows(IllegalArgumentException.class, + () -> preRelease(1, -2, 1, "test", 2), + "Negative minor version number tolerated by preRelease"); + assertThrows(IllegalArgumentException.class, + () -> preRelease(-3, 0, 7, "test", 2), + "Negative major version number tolerated by preRelease"); + assertThrows(IllegalArgumentException.class, + () -> preRelease(1, 0, 0, "test", -1), + "Negative pre release number tolerated by preRelease"); + assertThrows(NullPointerException.class, + () -> preRelease(1, 0, 0, null, 1), "Null tolerated by preRelease"); + assertThrows(IllegalArgumentException.class, + () -> preRelease(1, 0, 0, "", 1), + "Empty string tolerated by preRelease"); + assertThrows(IllegalArgumentException.class, + () -> preRelease(1, 0, 0, "abc+cde", 1), + "Invalid string tolerated by preRelease"); + + // builder() + assertThrows(IllegalArgumentException.class, () -> builder(1, 0, -1), + "Negative patch tolerated by builder"); + assertThrows(IllegalArgumentException.class, () -> builder(1, -2, 1), + "Negative minor version number tolerated by builder"); + assertThrows(IllegalArgumentException.class, () -> builder(-3, 0, 7), + "Negative major version number tolerated by builder"); + + final SemanticVersionNumber.Builder testBuilder = builder(1, 2, 3); + // note: builder.buildMetadata(null) doesn't even compile lol + // builder.buildMetadata + assertThrows(NullPointerException.class, + () -> testBuilder.buildMetadata(null, "abc"), + "Null tolerated by builder.buildMetadata(String...)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.buildMetadata(""), + "Empty string tolerated by builder.buildMetadata(String...)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.buildMetadata("c%4"), + "Invalid string tolerated by builder.buildMetadata(String...)"); + assertThrows(NullPointerException.class, + () -> testBuilder.buildMetadata(List.of("abc", null)), + "Null tolerated by builder.buildMetadata(List<String>)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.buildMetadata(List.of("")), + "Empty string tolerated by builder.buildMetadata(List<String>)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.buildMetadata(List.of("")), + "Invalid string tolerated by builder.buildMetadata(List<String>)"); + + // builder.preRelease + assertThrows(NullPointerException.class, + () -> testBuilder.preRelease(null, "abc"), + "Null tolerated by builder.preRelease(String...)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease(""), + "Empty string tolerated by builder.preRelease(String...)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease("c%4"), + "Invalid string tolerated by builder.preRelease(String...)"); + assertThrows(NullPointerException.class, + () -> testBuilder.preRelease(List.of("abc", null)), + "Null tolerated by builder.preRelease(List<String>)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease(List.of("")), + "Empty string tolerated by builder.preRelease(List<String>)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease(List.of("")), + "Invalid string tolerated by builder.preRelease(List<String>)"); + + // the overloadings that accept numeric arguments + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease(-1), + "Negative number tolerated by builder.preRelease(int...)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease("abc", -1), + "Negative number tolerated by builder.preRelease(String, int)"); + assertThrows(NullPointerException.class, + () -> testBuilder.preRelease(null, 1), + "Null tolerated by builder.preRelease(String, int)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease("", 1), + "Empty string tolerated by builder.preRelease(String, int)"); + assertThrows(IllegalArgumentException.class, + () -> testBuilder.preRelease("#$#c", 1), + "Invalid string tolerated by builder.preRelease(String, int)"); + + // ensure all these attempts didn't change the builder + assertEquals(builder(1, 2, 3), testBuilder, + "Attempts at making invalid version number succeeded despite throwing errors"); + } + + /** + * Test for {@link SemanticVersionNumber#isStable} + * + * @since 2022-02-19 + */ + @Test + public void testIsStable() { + assertTrue(stableVersion(1, 0, 0).isStable(), + "1.0.0 should be stable but is not"); + assertFalse(stableVersion(0, 1, 2).isStable(), + "0.1.2 should not be stable but is"); + assertFalse(preRelease(1, 2, 3, "alpha", 5).isStable(), + "1.2.3a5 should not be stable but is"); + assertTrue( + builder(9, 9, 99) + .buildMetadata("lots-of-metadata", "abc123", "2022").build() + .isStable(), + "9.9.99+lots-of-metadata.abc123.2022 should be stable but is not"); + } + + /** + * Tests that the versions are ordered by + * {@link SemanticVersionNumber#compareTo} according to official rules. Tests + * all of the versions compared in section 11 of the SemVer 2.0.0 document + * and some more. + * + * @since 2022-02-19 + */ + @Test + public void testOrder() { + final SemanticVersionNumber v100a = builder(1, 0, 0).preRelease("alpha") + .build(); // 1.0.0-alpha + final SemanticVersionNumber v100a1 = preRelease(1, 0, 0, "alpha", 1); // 1.0.0-alpha.1 + final SemanticVersionNumber v100ab = builder(1, 0, 0) + .preRelease("alpha", "beta").build(); // 1.0.0-alpha.beta + final SemanticVersionNumber v100b = builder(1, 0, 0).preRelease("beta") + .build(); // 1.0.0-alpha + final SemanticVersionNumber v100b2 = preRelease(1, 0, 0, "beta", 2); // 1.0.0-beta.2 + final SemanticVersionNumber v100b11 = preRelease(1, 0, 0, "beta", 11); // 1.0.0-beta.11 + final SemanticVersionNumber v100rc1 = preRelease(1, 0, 0, "rc", 1); // 1.0.0-rc.1 + final SemanticVersionNumber v100 = stableVersion(1, 0, 0); + final SemanticVersionNumber v100plus = builder(1, 0, 0) + .buildMetadata("blah", "blah", "blah").build(); // 1.0.0+blah.blah.blah + final SemanticVersionNumber v200 = stableVersion(2, 0, 0); + final SemanticVersionNumber v201 = stableVersion(2, 0, 1); + final SemanticVersionNumber v210 = stableVersion(2, 1, 0); + final SemanticVersionNumber v211 = stableVersion(2, 1, 1); + final SemanticVersionNumber v300 = stableVersion(3, 0, 0); + + // test order of version numbers + assertTrue(v100a.compareTo(v100a1) < 0, "1.0.0-alpha >= 1.0.0-alpha.1"); + assertTrue(v100a1.compareTo(v100ab) < 0, + "1.0.0-alpha.1 >= 1.0.0-alpha.beta"); + assertTrue(v100ab.compareTo(v100b) < 0, "1.0.0-alpha.beta >= 1.0.0-beta"); + assertTrue(v100b.compareTo(v100b2) < 0, "1.0.0-beta >= 1.0.0-beta.2"); + assertTrue(v100b2.compareTo(v100b11) < 0, + "1.0.0-beta.2 >= 1.0.0-beta.11"); + assertTrue(v100b11.compareTo(v100rc1) < 0, "1.0.0-beta.11 >= 1.0.0-rc.1"); + assertTrue(v100rc1.compareTo(v100) < 0, "1.0.0-rc.1 >= 1.0.0"); + assertTrue(v100.compareTo(v200) < 0, "1.0.0 >= 2.0.0"); + assertTrue(v200.compareTo(v201) < 0, "2.0.0 >= 2.0.1"); + assertTrue(v201.compareTo(v210) < 0, "2.0.1 >= 2.1.0"); + assertTrue(v210.compareTo(v211) < 0, "2.1.0 >= 2.1.1"); + assertTrue(v211.compareTo(v300) < 0, "2.1.1 >= 3.0.0"); + + // test symmetry - assume previous tests passed + assertTrue(v100a1.compareTo(v100a) > 0, "1.0.0-alpha.1 <= 1.0.0-alpha"); + assertTrue(v100.compareTo(v100rc1) > 0, "1.0.0 <= 1.0.0-rc.1"); + assertTrue(v300.compareTo(v211) > 0, "3.0.0 <= 2.1.1"); + + // test transitivity + assertTrue(v100a.compareTo(v100b11) < 0, "1.0.0-alpha >= 1.0.0-beta.11"); + assertTrue(v100b.compareTo(v200) < 0, "1.0.0-beta >= 2.0.0"); + assertTrue(v100.compareTo(v300) < 0, "1.0.0 >= 3.0.0"); + assertTrue(v100a.compareTo(v300) < 0, "1.0.0-alpha >= 3.0.0"); + + // test metadata is ignored + assertEquals(0, v100.compareTo(v100plus), "Build metadata not ignored"); + // test metadata is NOT ignored by alternative comparator + assertTrue(BUILD_METADATA_COMPARATOR.compare(v100, v100plus) > 0, + "Build metadata ignored by BUILD_METADATA_COMPARATOR"); + } + + /** + * Tests that simple stable versions can be created and their parts read + * + * @since 2022-02-19 + */ + @Test + public void testSimpleStableVersions() { + final SemanticVersionNumber v100 = stableVersion(1, 0, 0); + assertEquals(1, v100.majorVersion()); + assertEquals(0, v100.minorVersion()); + assertEquals(0, v100.patchVersion()); + + final SemanticVersionNumber v925 = stableVersion(9, 2, 5); + assertEquals(9, v925.majorVersion()); + assertEquals(2, v925.minorVersion()); + assertEquals(5, v925.patchVersion()); + } + + /** + * Tests that {@link SemanticVersionNumber#toString} works for simple version + * numbers + * + * @since 2022-02-19 + */ + @Test + public void testSimpleToString() { + final SemanticVersionNumber v100 = stableVersion(1, 0, 0); + assertEquals("1.0.0", v100.toString()); + + final SemanticVersionNumber v845a1 = preRelease(8, 4, 5, "alpha", 1); + assertEquals("8.4.5-alpha.1", v845a1.toString()); + } + + /** + * Tests that simple unstable versions can be created and their parts read + * + * @since 2022-02-19 + */ + @Test + public void testSimpleUnstableVersions() { + final SemanticVersionNumber v350a1 = preRelease(3, 5, 0, "alpha", 1); + assertEquals(3, v350a1.majorVersion(), + "Incorrect major version for v3.5.0a1"); + assertEquals(5, v350a1.minorVersion(), + "Incorrect minor version for v3.5.0a1"); + assertEquals(0, v350a1.patchVersion(), + "Incorrect patch version for v3.5.0a1"); + assertEquals(List.of("alpha", "1"), v350a1.preReleaseIdentifiers(), + "Incorrect pre-release identifiers for v3.5.0a1"); + } +} diff --git a/src/test/java/sevenUnits/utils/UncertainDoubleTest.java b/src/test/java/sevenUnits/utils/UncertainDoubleTest.java index c891f20..5ccef28 100644 --- a/src/test/java/sevenUnits/utils/UncertainDoubleTest.java +++ b/src/test/java/sevenUnits/utils/UncertainDoubleTest.java @@ -19,6 +19,7 @@ package sevenUnits.utils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static sevenUnits.utils.UncertainDouble.fromRoundedString; import static sevenUnits.utils.UncertainDouble.fromString; import static sevenUnits.utils.UncertainDouble.of; @@ -33,6 +34,9 @@ import org.junit.jupiter.api.Test; * @since 2021-11-29 */ class UncertainDoubleTest { + /** + * Ensures that the compareTo function behaves correctly. + */ @Test final void testCompareTo() { assertTrue(of(2.0, 0.5).compareTo(of(2.0, 0.1)) == 0); @@ -40,6 +44,9 @@ class UncertainDoubleTest { assertTrue(of(2.0, 0.5).compareTo(of(3.0, 0.1)) < 0); } + /** + * Tests the ___exact operations + */ @Test final void testExactOperations() { final UncertainDouble x = UncertainDouble.of(Math.PI, 0.1); @@ -66,6 +73,19 @@ class UncertainDoubleTest { x.toExponentExact(Math.E).value()); } + /** + * Test for {@link UncertainDouble#fromRoundedString} + * + * @since 2022-04-18 + */ + @Test + final void testFromRoundedString() { + assertEquals(of(12345.678, 0.001), fromRoundedString("12345.678")); + } + + /** + * Test for {@link UncertainDouble#fromString} + */ @Test final void testFromString() { // valid strings diff --git a/src/test/java/sevenUnitsGUI/PrefixRepetitionTest.java b/src/test/java/sevenUnitsGUI/PrefixRepetitionTest.java new file mode 100644 index 0000000..8ea3fd0 --- /dev/null +++ b/src/test/java/sevenUnitsGUI/PrefixRepetitionTest.java @@ -0,0 +1,123 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static sevenUnitsGUI.DefaultPrefixRepetitionRule.COMPLEX_REPETITION; +import static sevenUnitsGUI.DefaultPrefixRepetitionRule.NO_REPETITION; +import static sevenUnitsGUI.DefaultPrefixRepetitionRule.NO_RESTRICTION; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +import sevenUnits.unit.Metric; + +/** + * Tests for the default prefix repetition rules. + * + * @since v0.4.0 + * @since 2022-07-17 + */ +class PrefixRepetitionTest { + /** + * Ensures that the complex repetition rule disallows invalid prefix lists. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testInvalidComplexRepetition() { + assertFalse(COMPLEX_REPETITION.test(List.of(Metric.KILO, Metric.YOTTA)), + "Complex repetition does not factor order of prefixes"); + assertFalse(COMPLEX_REPETITION.test(List.of(Metric.MEGA, Metric.KILO)), + "\"kilomega\" allowed (should use \"giga\")"); + assertFalse( + COMPLEX_REPETITION + .test(List.of(Metric.YOTTA, Metric.MEGA, Metric.KILO)), + "\"kilomega\" allowed after yotta (should use \"giga\")"); + assertFalse(COMPLEX_REPETITION.test(List.of(Metric.YOTTA, Metric.MILLI)), + "Complex repetition does not factor direction of prefixes"); + } + + /** + * Tests the {@code NO_REPETITION} rule. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testNoRepetition() { + assertTrue(NO_REPETITION.test(List.of(Metric.KILO))); + assertFalse(NO_REPETITION.test(List.of(Metric.KILO, Metric.YOTTA))); + assertTrue(NO_REPETITION.test(List.of(Metric.MILLI))); + assertTrue(NO_REPETITION.test(List.of()), "Empty list yields false"); + } + + /** + * Tests the {@code NO_RESTRICTION} rule. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testNoRestriction() { + assertTrue(NO_RESTRICTION.test(List.of(Metric.KILO))); + assertTrue(NO_RESTRICTION.test(List.of(Metric.KILO, Metric.YOTTA))); + assertTrue(NO_RESTRICTION.test(List.of(Metric.MILLI))); + assertTrue(NO_RESTRICTION.test(List.of())); + } + + /** + * Ensures that the complex repetition rule allows valid prefix lists. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testValidComplexRepetition() { + // simple situations + assertTrue(COMPLEX_REPETITION.test(List.of(Metric.KILO)), + "Single prefix not allowed"); + assertTrue(COMPLEX_REPETITION.test(List.of()), "No prefixes not allowed"); + + // valid complex repetition + assertTrue(COMPLEX_REPETITION.test(List.of(Metric.YOTTA, Metric.KILO)), + "Valid complex repetition (kiloyotta) not allowed"); + assertTrue(COMPLEX_REPETITION.test(List.of(Metric.KILO, Metric.DEKA)), + "Valid complex repetition (dekakilo) not allowed"); + assertTrue( + COMPLEX_REPETITION + .test(List.of(Metric.YOTTA, Metric.KILO, Metric.DEKA)), + "Valid complex repetition (dekakiloyotta) not allowed"); + assertTrue( + COMPLEX_REPETITION.test(List.of(Metric.YOTTA, Metric.YOTTA, + Metric.KILO, Metric.DEKA)), + "Valid complex repetition (dekakiloyottayotta) not allowed"); + + // valid with negative prefixes + assertTrue(COMPLEX_REPETITION.test(List.of(Metric.YOCTO, Metric.MILLI)), + "Valid complex repetition (milliyocto) not allowed"); + assertTrue(COMPLEX_REPETITION.test(List.of(Metric.MILLI, Metric.CENTI)), + "Valid complex repetition (centimilli) not allowed"); + assertTrue( + COMPLEX_REPETITION + .test(List.of(Metric.YOCTO, Metric.MILLI, Metric.CENTI)), + "Valid complex repetition (centimilliyocto) not allowed"); + } +} diff --git a/src/test/java/sevenUnitsGUI/PrefixSearchTest.java b/src/test/java/sevenUnitsGUI/PrefixSearchTest.java new file mode 100644 index 0000000..ca238fe --- /dev/null +++ b/src/test/java/sevenUnitsGUI/PrefixSearchTest.java @@ -0,0 +1,158 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static sevenUnitsGUI.PrefixSearchRule.COMMON_PREFIXES; +import static sevenUnitsGUI.PrefixSearchRule.NO_PREFIXES; +import static sevenUnitsGUI.PrefixSearchRule.getCoherentOnlyRule; +import static sevenUnitsGUI.PrefixSearchRule.getUniversalRule; + +import java.util.Map; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import sevenUnits.unit.BritishImperial; +import sevenUnits.unit.LinearUnit; +import sevenUnits.unit.Metric; + +/** + * Tests for {@link PrefixSearchRule} + * + * @since v0.4.0 + * @since 2022-07-17 + */ +class PrefixSearchTest { + /** + * A method that creates duplicate copies of the common prefix rule. + */ + private static final PrefixSearchRule getCommonRuleCopy() { + return getCoherentOnlyRule(Set.of(Metric.KILO, Metric.MILLI)); + } + + /** + * Test method for + * {@link sevenUnitsGUI.PrefixSearchRule#apply(java.util.Map.Entry)}, for a + * coherent unit and {@link PrefixSearchRule#COMMON_PREFIXES}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testCoherentPrefixSearch() { + final var expected = Map.of("metre", Metric.METRE, "kilometre", + Metric.KILOMETRE, "millimetre", Metric.MILLIMETRE); + final var actual = COMMON_PREFIXES + .apply(Map.entry("metre", Metric.METRE)); + + assertEquals(expected, actual, + "Prefixes not correctly applied to coherent unit."); + } + + /** + * Test method for + * {@link sevenUnitsGUI.PrefixSearchRule#equals(java.lang.Object)}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testEquals() { + assertEquals(getCommonRuleCopy(), getCommonRuleCopy(), + "equals considers something other than prefixes/rule"); + + assertNotEquals(getCoherentOnlyRule(Set.of()), getUniversalRule(Set.of()), + "equals ignores rule"); + } + + /** + * Test method for {@link sevenUnitsGUI.PrefixSearchRule#getPrefixes()}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testGetPrefixes() { + assertEquals(Set.of(), NO_PREFIXES.getPrefixes()); + assertEquals(Metric.ALL_PREFIXES, + PrefixSearchRule.ALL_METRIC_PREFIXES.getPrefixes()); + } + + /** + * Test method for {@link sevenUnitsGUI.PrefixSearchRule#hashCode()}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testHashCode() { + assertEquals(getCommonRuleCopy().hashCode(), + getCommonRuleCopy().hashCode()); + } + + /** + * Tests prefix searching for a non-coherent unit and + * {@link PrefixSearchRule#COMMON_PREFIXES}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testNonCoherentPrefixSearch() { + final var input = Map.entry("inch", BritishImperial.Length.INCH); + final var expected = Map.ofEntries(input); + final var actual = COMMON_PREFIXES.apply(input); + + assertEquals(expected, actual, "Prefixes applied to non-coherent unit."); + } + + /** + * Tests that {@link PrefixSearchRule#NO_PREFIXES} returns the original unit. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testNoPrefixes() { + for (final String name : Set.of("name1", "hello", "there")) { + for (final LinearUnit unit : Set.of(Metric.METRE, Metric.KILOMETRE, + BritishImperial.Length.INCH)) { + final var testEntry = Map.entry(name, unit); + final var expected = Map.ofEntries(testEntry); + final var actual = NO_PREFIXES.apply(testEntry); + assertEquals(expected, actual, () -> String + .format("NO_PREFIXES.apply(%s) != %s", testEntry, actual)); + } + } + } + + /** + * Test method for {@link sevenUnitsGUI.PrefixSearchRule#toString()}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + final void testToString() { + assertEquals( + "Apply the following prefixes: [kilo (\u00D7 1000.0), milli (\u00D7 0.001)]", + COMMON_PREFIXES.toString()); + } + +} diff --git a/src/test/java/sevenUnitsGUI/PresenterTest.java b/src/test/java/sevenUnitsGUI/PresenterTest.java new file mode 100644 index 0000000..13d7986 --- /dev/null +++ b/src/test/java/sevenUnitsGUI/PresenterTest.java @@ -0,0 +1,423 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.math.RoundingMode; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import sevenUnits.unit.BaseDimension; +import sevenUnits.unit.BritishImperial; +import sevenUnits.unit.LinearUnit; +import sevenUnits.unit.LinearUnitValue; +import sevenUnits.unit.Metric; +import sevenUnits.unit.Unit; +import sevenUnits.unit.UnitDatabase; +import sevenUnits.unit.UnitType; +import sevenUnits.utils.NameSymbol; +import sevenUnits.utils.Nameable; +import sevenUnits.utils.ObjectProduct; +import sevenUnits.utils.UncertainDouble; + +/** + * Various tests for the {@link Presenter}. + * <p> + * <em>Note: this test outputs a lot to the standard output, because creating a + * {@link UnitDatabase} and converting with a {@link ViewBot} both trigger + * println statements.</em> + * + * @author Adrien Hopkins + * + * @since v0.4.0 + * @since 2022-02-10 + */ +public final class PresenterTest { + private static final Path TEST_SETTINGS = Path.of("src", "test", "resources", + "test-settings.txt"); + static final Set<Unit> testUnits = Set.of(Metric.METRE, Metric.KILOMETRE, + Metric.METRE_PER_SECOND, Metric.KILOMETRE_PER_HOUR); + + static final Set<ObjectProduct<BaseDimension>> testDimensions = Set + .of(Metric.Dimensions.LENGTH, Metric.Dimensions.VELOCITY); + + private static final Stream<Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>>> SEARCH_RULES = Stream + .of(PrefixSearchRule.NO_PREFIXES, PrefixSearchRule.COMMON_PREFIXES, + PrefixSearchRule.ALL_METRIC_PREFIXES); + + /** + * @return rounding rules used by {@link #testRoundingRules} + * @since v0.4.0 + * @since 2022-04-16 + */ + private static final Stream<Function<UncertainDouble, String>> getRoundingRules() { + final var SCIENTIFIC_ROUNDING = StandardDisplayRules.uncertaintyBased(); + final var INTEGER_ROUNDING = StandardDisplayRules.fixedDecimals(0); + final var SIG_FIG_ROUNDING = StandardDisplayRules.fixedPrecision(4); + + return Stream.of(SCIENTIFIC_ROUNDING, INTEGER_ROUNDING, SIG_FIG_ROUNDING); + } + + private static final Stream<Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>>> getSearchRules() { + return SEARCH_RULES; + } + + private static final Set<String> names(Set<? extends Nameable> units) { + return units.stream().map(Nameable::getName).collect(Collectors.toSet()); + } + + /** + * Test method for {@link Presenter#convertExpressions} + * + * @since v0.4.0 + * @since 2022-02-12 + */ + @Test + void testConvertExpressions() { + // setup + final ViewBot viewBot = new ViewBot(); + final Presenter presenter = new Presenter(viewBot); + + viewBot.setFromExpression("10000.0 m"); + viewBot.setToExpression("km"); + + // convert expression + presenter.convertExpressions(); + + // test result + final List<UnitConversionRecord> outputs = viewBot + .expressionConversionList(); + assertEquals("10000.0 m = 10.00000 km", + outputs.get(outputs.size() - 1).toString()); + } + + /** + * Tests that unit-conversion Views can correctly convert units + * + * @since v0.4.0 + * @since 2022-02-12 + */ + @Test + void testConvertUnits() { + // setup + final ViewBot viewBot = new ViewBot(); + final Presenter presenter = new Presenter(viewBot); + + viewBot.setFromUnitNames(names(testUnits)); + viewBot.setToUnitNames(names(testUnits)); + viewBot.setFromSelection("metre"); + viewBot.setToSelection("kilometre"); + viewBot.setInputValue("10000.0"); + + // convert units + presenter.convertUnits(); + + /* + * use result from system as expected - I'm not testing unit conversion + * here (that's for the backend tests), I'm just testing that it correctly + * calls the unit conversion system + */ + final LinearUnitValue expectedInput = LinearUnitValue.of(Metric.METRE, + UncertainDouble.fromRoundedString("10000.0")); + final LinearUnitValue expectedOutput = expectedInput + .convertTo(Metric.KILOMETRE); + final UnitConversionRecord expectedUC = UnitConversionRecord.valueOf( + expectedInput.getUnit().getName(), + expectedOutput.getUnit().getName(), "10000.0", + expectedOutput.getValue().toString(false, RoundingMode.HALF_EVEN)); + assertEquals(List.of(expectedUC), viewBot.unitConversionList()); + } + + /** + * Tests that duplicate units are successfully removed, if that is asked for + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @Test + void testDuplicateUnits() { + final var metre = Metric.METRE; + final var meter = Metric.METRE.withName(NameSymbol.of("meter", "m")); + + // load 2 duplicate units + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + presenter.database.clear(); + presenter.database.addUnit("metre", metre); + presenter.database.addUnit("meter", meter); + presenter.setOneWayConversionEnabled(false); + presenter.setSearchRule(PrefixSearchRule.NO_PREFIXES); + + // test that only one of them is included if duplicate units disabled + presenter.setShowDuplicates(false); + presenter.updateView(); + assertEquals(1, viewBot.getFromUnitNames().size()); + assertEquals(1, viewBot.getToUnitNames().size()); + + // test that both of them is included if duplicate units enabled + presenter.setShowDuplicates(true); + presenter.updateView(); + assertEquals(2, viewBot.getFromUnitNames().size()); + assertEquals(2, viewBot.getToUnitNames().size()); + } + + /** + * Tests that one-way conversion correctly filters From and To units + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @Test + void testOneWayConversion() { + // metre is metric, inch is non-metric, tempC is semi-metric + final var allNames = Set.of("metre", "inch", "tempC"); + final var metricNames = Set.of("metre", "tempC"); + final var nonMetricNames = Set.of("inch", "tempC"); + + // load view with one metric and one non-metric unit + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + presenter.database.clear(); + presenter.database.addUnit("metre", Metric.METRE); + presenter.database.addUnit("inch", BritishImperial.Length.INCH); + presenter.database.addUnit("tempC", Metric.CELSIUS); + presenter.setSearchRule(PrefixSearchRule.NO_PREFIXES); + + // test that units are removed from each side when one-way conversion is + // enabled + presenter.setOneWayConversionEnabled(true); + assertEquals(nonMetricNames, viewBot.getFromUnitNames()); + assertEquals(metricNames, viewBot.getToUnitNames()); + + // test that units are kept when one-way conversion is disabled + presenter.setOneWayConversionEnabled(false); + assertEquals(allNames, viewBot.getFromUnitNames()); + assertEquals(allNames, viewBot.getToUnitNames()); + } + + /** + * Tests the prefix-viewing functionality. + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @Test + void testPrefixViewing() { + // setup + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + viewBot.setViewablePrefixNames(Set.of("kilo", "milli")); + presenter.setNumberDisplayRule(UncertainDouble::toString); + + // view prefix + viewBot.setViewedPrefixName("kilo"); + presenter.prefixSelected(); // just in case + + // get correct values + final var expectedNameSymbol = presenter.database.getPrefix("kilo") + .getNameSymbol(); + final var expectedMultiplierString = String + .valueOf(Metric.KILO.getMultiplier()); + + // test that presenter's values are correct + final var prefixRecord = viewBot.prefixViewList().get(0); + assertEquals(expectedNameSymbol, prefixRecord.getNameSymbol()); + assertEquals(expectedMultiplierString, prefixRecord.multiplierString()); + } + + /** + * Tests that rounding rules are used correctly. + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @ParameterizedTest + @MethodSource("getRoundingRules") + void testRoundingRules(Function<UncertainDouble, String> roundingRule) { + // setup + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + presenter.setNumberDisplayRule(roundingRule); + + // convert and round + viewBot.setInputValue("12345.6789"); + viewBot.setFromSelection("metre"); + viewBot.setToSelection("kilometre"); + presenter.convertUnits(); + + // test the result of the rounding + final String expectedOutputString = roundingRule + .apply(UncertainDouble.fromRoundedString("12.3456789")); + final String actualOutputString = viewBot.unitConversionList().get(0) + .outputValueString(); + assertEquals(expectedOutputString, actualOutputString); + } + + /** + * Tests that the Presenter correctly applies search rules. + * + * @param searchRule search rule to test + * @since v0.4.0 + * @since 2022-07-08 + */ + @ParameterizedTest + @MethodSource("getSearchRules") + void testSearchRules( + Function<Map.Entry<String, LinearUnit>, Map<String, LinearUnit>> searchRule) { + // setup + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + + presenter.setSearchRule(searchRule); + presenter.setOneWayConversionEnabled(false); + + presenter.database.clear(); + presenter.database.addUnit("metre", Metric.METRE); + presenter.database.addUnit("inch", BritishImperial.Length.INCH); + presenter.updateView(); + + // create expected output based on rule + final Set<String> expectedOutput = new HashSet<>(); + expectedOutput.addAll(searchRule + .apply(Map.entry("inch", BritishImperial.Length.INCH)).keySet()); + expectedOutput.addAll( + searchRule.apply(Map.entry("metre", Metric.METRE)).keySet()); + final Set<String> actualOutput = viewBot.getFromUnitNames(); + + // test output + assertEquals(expectedOutput, actualOutput); + } + + /** + * Tests that settings can be saved to and loaded from a file. + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @Test + void testSettingsSaving() { + // setup + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + + // set and save custom settings + presenter.setOneWayConversionEnabled(true); + presenter.setShowDuplicates(true); + presenter.setNumberDisplayRule(StandardDisplayRules.fixedPrecision(11)); + presenter.setPrefixRepetitionRule( + DefaultPrefixRepetitionRule.COMPLEX_REPETITION); + presenter.saveSettings(TEST_SETTINGS); + + // overwrite custom settings + presenter.setOneWayConversionEnabled(false); + presenter.setShowDuplicates(false); + presenter.setNumberDisplayRule(StandardDisplayRules.uncertaintyBased()); + + // load settings & test that they're the same + presenter.loadSettings(TEST_SETTINGS); + assertTrue(presenter.oneWayConversionEnabled()); + assertTrue(presenter.duplicatesShown()); + assertEquals(StandardDisplayRules.fixedPrecision(11), + presenter.getNumberDisplayRule()); + } + + /** + * Ensures the Presenter generates the correct data upon a unit-viewing. + * + * @since v0.4.0 + * @since 2022-04-16 + */ + @Test + void testUnitViewing() { + // setup + final var viewBot = new ViewBot(); + final var presenter = new Presenter(viewBot); + viewBot.setViewableUnitNames(names(testUnits)); + + // view unit + viewBot.setViewedUnitName("metre"); + presenter.unitNameSelected(); // just in case this isn't triggered + // automatically + + // get correct values + final var expectedNameSymbol = presenter.database.getUnit("metre") + .getNameSymbol(); + final var expectedDefinition = "(Base unit)"; + final var expectedDimensionName = presenter + .getDimensionName(Metric.METRE.getDimension()); + final var expectedUnitType = UnitType.METRIC; + + // test for correctness + final var viewRecord = viewBot.unitViewList().get(0); + assertEquals(expectedNameSymbol, viewRecord.getNameSymbol()); + assertEquals(expectedDefinition, viewRecord.definition()); + assertEquals(expectedDimensionName, viewRecord.dimensionName()); + assertEquals(expectedUnitType, viewRecord.unitType()); + } + + /** + * Test for {@link Presenter#updateView()} + * + * @since v0.4.0 + * @since 2022-02-12 + */ + @Test + void testUpdateView() { + // setup + final ViewBot viewBot = new ViewBot(); + final Presenter presenter = new Presenter(viewBot); + presenter.setOneWayConversionEnabled(false); + presenter.setSearchRule(PrefixSearchRule.NO_PREFIXES); + + // override default database units + presenter.database.clear(); + for (final Unit unit : testUnits) { + presenter.database.addUnit(unit.getPrimaryName().orElseThrow(), unit); + } + for (final var dimension : testDimensions) { + presenter.database.addDimension( + dimension.getPrimaryName().orElseThrow(), dimension); + } + + // set from and to units + viewBot.setFromUnitNames(names(testUnits)); + viewBot.setToUnitNames(names(testUnits)); + viewBot.setDimensionNames(names(testDimensions)); + viewBot.setSelectedDimensionName(Metric.Dimensions.LENGTH.getName()); + + // filter to length units only, then get the filtered sets of units + presenter.updateView(); + final Set<String> fromUnits = viewBot.getFromUnitNames(); + final Set<String> toUnits = viewBot.getToUnitNames(); + + // test that fromUnits/toUnits is [METRE, KILOMETRE] + assertEquals(Set.of("metre", "kilometre"), fromUnits); + assertEquals(Set.of("metre", "kilometre"), toUnits); + } +} diff --git a/src/test/java/sevenUnitsGUI/RoundingTest.java b/src/test/java/sevenUnitsGUI/RoundingTest.java new file mode 100644 index 0000000..ca1a272 --- /dev/null +++ b/src/test/java/sevenUnitsGUI/RoundingTest.java @@ -0,0 +1,287 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +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 static sevenUnitsGUI.StandardDisplayRules.fixedDecimals; +import static sevenUnitsGUI.StandardDisplayRules.fixedPrecision; +import static sevenUnitsGUI.StandardDisplayRules.getStandardRule; +import static sevenUnitsGUI.StandardDisplayRules.uncertaintyBased; + +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.UncertainDouble; +import sevenUnitsGUI.StandardDisplayRules.FixedDecimals; +import sevenUnitsGUI.StandardDisplayRules.FixedPrecision; +import sevenUnitsGUI.StandardDisplayRules.UncertaintyBased; + +/** + * Tests that ensure the rounding rules work as intended. + * + * @since v0.4.0 + * @since 2022-07-17 + */ +class RoundingTest { + // rounding rules to test + private static final FixedDecimals ZERO_DECIMALS = fixedDecimals(0); + private static final FixedDecimals TWO_DECIMALS = fixedDecimals(2); + private static final FixedDecimals SIX_DECIMALS = fixedDecimals(6); + + private static final FixedPrecision ONE_SIG_FIG = fixedPrecision(1); + private static final FixedPrecision THREE_SIG_FIGS = fixedPrecision(3); + private static final FixedPrecision TWELVE_SIG_FIGS = fixedPrecision(12); + + private static final UncertaintyBased UNCERTAINTY_BASED = uncertaintyBased(); + + // numbers to test rounding with + private static final UncertainDouble INPUT1 = UncertainDouble.of(12.3456789, + 0.0); + private static final UncertainDouble INPUT2 = UncertainDouble.of(300.9, + 0.005); + private static final UncertainDouble INPUT3 = UncertainDouble.of(12345432.1, + 0.0); + private static final UncertainDouble INPUT4 = UncertainDouble.of(0.00001234, + 0.000001); + + /** + * @return arguments for + * {@link #testFixedDecimalRounding(UncertainDouble, String, String, String)} + * @since v0.4.0 + * @since 2022-07-17 + */ + private static final Stream<Arguments> fixedDecimalRoundingExamples() { + // input, zero decimal string, two decimal string, six decimal string + return Stream.of(Arguments.of(INPUT1, "12", "12.35", "12.345679"), + Arguments.of(INPUT2, "301", "300.90", "300.900000"), + Arguments.of(INPUT3, "12345432", "12345432.10", "12345432.100000"), + Arguments.of(INPUT4, "0", "0.00", "0.000012")); + } + + /** + * @return arguments for + * {@link #testFixedPrecisionRounding(UncertainDouble, String, String, String)} + * @since v0.4.0 + * @since 2022-07-17 + */ + private static final Stream<Arguments> fixedPrecisionRoundingExamples() { + // input, one sig fig string, three s.f. string, six s.f. string + return Stream.of(Arguments.of(INPUT1, "1E+1", "12.3", "12.3456789000"), + Arguments.of(INPUT2, "3E+2", "301", "300.900000000"), + Arguments.of(INPUT3, "1E+7", "1.23E+7", "12345432.1000"), + Arguments.of(INPUT4, "0.00001", "0.0000123", "0.0000123400000000")); + } + + /** + * @return arguments for + * {@link #testUncertaintyRounding(UncertainDouble, String)} + * @since v0.4.0 + * @since 2022-07-17 + */ + private static final Stream<Arguments> uncertaintyRoundingExamples() { + // input, uncertainty rounding string + return Stream.of(Arguments.of(INPUT1, "12.3456789"), + Arguments.of(INPUT2, "300.900"), + Arguments.of(INPUT3, "1.23454321E7"), + Arguments.of(INPUT4, "0.0000123")); + } + + /** + * Test for {@link FixedDecimals#decimalPlaces()} and + * {@link FixedPrecision#significantFigures()}. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testDataMethods() { + // ensure # of decimal places can be accessed + assertEquals(0, ZERO_DECIMALS.decimalPlaces(), + "ZERO_DECIMALS has " + ZERO_DECIMALS.decimalPlaces() + " decimals"); + assertEquals(2, TWO_DECIMALS.decimalPlaces(), + "TWO_DECIMALS has " + TWO_DECIMALS.decimalPlaces() + " decimals"); + assertEquals(6, SIX_DECIMALS.decimalPlaces(), + "SIX_DECIMALS has " + SIX_DECIMALS.decimalPlaces() + " decimals"); + + // ensure # of sig figs can be accessed + assertEquals(1, ONE_SIG_FIG.significantFigures(), "ONE_SIG_FIG has " + + ONE_SIG_FIG.significantFigures() + " significant figures"); + assertEquals(3, THREE_SIG_FIGS.significantFigures(), "THREE_SIG_FIGS has " + + THREE_SIG_FIGS.significantFigures() + " significant figures"); + assertEquals(12, TWELVE_SIG_FIGS.significantFigures(), + "TWELVE_SIG_FIGS has " + TWELVE_SIG_FIGS.significantFigures() + + " significant figures"); + } + + /** + * Tests that the rounding methods' equals() methods work. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testEquals() { + // basic equals tests + assertTrue(ZERO_DECIMALS.equals(ZERO_DECIMALS), + "ZERO_DECIMALS does not equal itself"); + assertFalse(TWO_DECIMALS.equals(SIX_DECIMALS), + "TWO_DECIMALS == SIX_DECIMALS"); + assertTrue(Objects.equals(fixedDecimals(0), fixedDecimals(0)), + "FixedDecimals.equals() depends on something other than decimal places."); + + assertTrue(ONE_SIG_FIG.equals(ONE_SIG_FIG), + "ONE_SIG_FIG does not equal itself"); + assertFalse(THREE_SIG_FIGS.equals(TWELVE_SIG_FIGS), + "THREE_SIG_FIGS == TWELVE_SIG_FIGS"); + assertTrue(Objects.equals(fixedPrecision(1), fixedPrecision(1)), + "FixedPrecision.equals() depends on something other than significant figures."); + + // test that FixedDecimals is never equal to FixedPrecision + // this unlikely argument is the test - the equals should return false! + @SuppressWarnings("unlikely-arg-type") + final boolean differentRulesEqual = Objects.equals(fixedDecimals(4), + fixedPrecision(4)); + assertFalse(differentRulesEqual, "fixedDecimals(4) == fixedPrecision(4)"); + } + + /** + * Ensures that fixed decimal rounding works as expected + * + * @param input number to test + * @param zeroDecimalString expected string for zero decimal places + * @param twoDecimalString expected string for two decimal places + * @param sixDecimalString expected string for six decimal places + * @since 2022-07-17 + */ + @ParameterizedTest + @MethodSource("fixedDecimalRoundingExamples") + void testFixedDecimalRounding(UncertainDouble input, + String zeroDecimalString, String twoDecimalString, + String sixDecimalString) { + // test the three rounding rules against the provided strings + assertEquals(zeroDecimalString, ZERO_DECIMALS.apply(input), + "ZERO_DECIMALS rounded " + input + " as " + + ZERO_DECIMALS.apply(input) + " (should be " + + zeroDecimalString + ")"); + assertEquals(twoDecimalString, TWO_DECIMALS.apply(input), + "TWO_DECIMALS rounded " + input + " as " + TWO_DECIMALS.apply(input) + + " (should be " + twoDecimalString + ")"); + assertEquals(sixDecimalString, SIX_DECIMALS.apply(input), + "TWO_DECIMALS rounded " + input + " as " + SIX_DECIMALS.apply(input) + + " (should be " + sixDecimalString + ")"); + } + + /** + * Ensures that fixed precision rounding works as expected + * + * @param input number to test + * @param oneSigFigString expected string for one significant figure + * @param threeSigFigString expected string for three significant figures + * @param twelveSigFigString expected string for twelve significant figures + * @since v0.4.0 + * @since 2022-07-17 + */ + @ParameterizedTest + @MethodSource("fixedPrecisionRoundingExamples") + void testFixedPrecisionRounding(UncertainDouble input, + String oneSigFigString, String threeSigFigString, + String twelveSigFigString) { + // test the three rounding rules against the provided strings + assertEquals(oneSigFigString, ONE_SIG_FIG.apply(input), + "ONE_SIG_FIG rounded " + input + " as " + ONE_SIG_FIG.apply(input) + + " (should be " + oneSigFigString + ")"); + assertEquals(threeSigFigString, THREE_SIG_FIGS.apply(input), + "THREE_SIG_FIGS rounded " + input + " as " + + THREE_SIG_FIGS.apply(input) + " (should be " + + threeSigFigString + ")"); + assertEquals(twelveSigFigString, TWELVE_SIG_FIGS.apply(input), + "TWELVE_SIG_FIGS rounded " + input + " as " + + TWELVE_SIG_FIGS.apply(input) + " (should be " + + twelveSigFigString + ")"); + } + + /** + * Tests that {@link StandardDisplayRules#getStandardRule} gets rounding + * rules as intended. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testGetStandardRule() { + assertEquals(ZERO_DECIMALS, getStandardRule("Round to 0 decimal places")); + assertEquals(THREE_SIG_FIGS, + getStandardRule("Round to 3 significant figures")); + assertEquals(UNCERTAINTY_BASED, + getStandardRule("Uncertainty-Based Rounding")); + + assertThrows(IllegalArgumentException.class, + () -> getStandardRule("Not a rounding rule")); + } + + /** + * Tests that the rounding methods' equals() methods work. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testHashCode() { + assertEquals(ZERO_DECIMALS.hashCode(), ZERO_DECIMALS.hashCode()); + assertEquals(ONE_SIG_FIG.hashCode(), ONE_SIG_FIG.hashCode()); + assertEquals(UNCERTAINTY_BASED.hashCode(), UNCERTAINTY_BASED.hashCode()); + } + + /** + * Tests that the {@code toString()} methods of the three rounding rule + * classes work correctly. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testToString() { + assertEquals("Round to 0 decimal places", ZERO_DECIMALS.toString()); + assertEquals("Round to 3 significant figures", THREE_SIG_FIGS.toString()); + assertEquals("Uncertainty-Based Rounding", UNCERTAINTY_BASED.toString()); + } + + /** + * Tests that Uncertainty Rounding works as expected + * + * @param input input number + * @param output expected output string + * @since v0.4.0 + * @since 2022-07-17 + */ + @ParameterizedTest + @MethodSource("uncertaintyRoundingExamples") + void testUncertaintyRounding(UncertainDouble input, String output) { + assertEquals(output, UNCERTAINTY_BASED.apply(input), + () -> String.format( + "Uncertainty Rounding rounded %s as %s (should be %s)", input, + UNCERTAINTY_BASED.apply(input), output)); + } +} diff --git a/src/test/java/sevenUnitsGUI/TabbedViewTest.java b/src/test/java/sevenUnitsGUI/TabbedViewTest.java new file mode 100644 index 0000000..00092a4 --- /dev/null +++ b/src/test/java/sevenUnitsGUI/TabbedViewTest.java @@ -0,0 +1,95 @@ +/** + * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. + */ +package sevenUnitsGUI; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Test for the TabbedView + * + * @since v0.4.0 + * @since 2022-07-17 + */ +class TabbedViewTest { + /** + * @return a view with all settings set to standard values + * + * @since v0.4.0 + * @since 2022-07-17 + */ + private static final TabbedView setupView() { + final var view = new TabbedView(); + final var presenter = view.getPresenter(); + + presenter.setNumberDisplayRule(StandardDisplayRules.uncertaintyBased()); + presenter.setPrefixRepetitionRule( + DefaultPrefixRepetitionRule.NO_RESTRICTION); + presenter.setSearchRule(PrefixSearchRule.COMMON_PREFIXES); + presenter.setOneWayConversionEnabled(false); + presenter.setShowDuplicates(true); + + return view; + } + + /** + * Simulates an expression conversion operation, and ensures it works + * properly. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testExpressionConversion() { + final var view = setupView(); + + // prepare for unit conversion + view.masterPane.setSelectedIndex(1); + view.fromEntry.setText("250.0 inch"); + view.toEntry.setText("metre"); + + view.convertExpressionButton.doClick(); + + // check result of conversion + assertEquals("250.0 inch = 6.350 metre", view.expressionOutput.getText()); + } + + /** + * Simulates a unit conversion operation, and ensures it works properly. + * + * @since v0.4.0 + * @since 2022-07-17 + */ + @Test + void testUnitConversion() { + final var view = setupView(); + + // prepare for unit conversion + view.masterPane.setSelectedIndex(0); + view.dimensionSelector.setSelectedItem("Length"); + view.fromSearch.getSearchList().setSelectedValue("inch", true); + view.toSearch.getSearchList().setSelectedValue("metre", true); + view.valueInput.setText("250.0"); + + view.convertUnitButton.doClick(); + + // check result of conversion + assertEquals("250.0 inch = 6.350 metre", view.unitOutput.getText()); + } + +} |