summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/sevenUnits/unit/MultiUnitTest.java16
-rw-r--r--src/test/java/sevenUnits/unit/UnitDatabaseTest.java3
-rw-r--r--src/test/java/sevenUnits/unit/UnitTest.java15
-rw-r--r--src/test/java/sevenUnits/utils/ConditionalExistenceCollectionsTest.java54
-rw-r--r--src/test/java/sevenUnits/utils/SemanticVersionTest.java399
-rw-r--r--src/test/java/sevenUnits/utils/UncertainDoubleTest.java20
-rw-r--r--src/test/java/sevenUnitsGUI/PrefixRepetitionTest.java123
-rw-r--r--src/test/java/sevenUnitsGUI/PrefixSearchTest.java158
-rw-r--r--src/test/java/sevenUnitsGUI/PresenterTest.java423
-rw-r--r--src/test/java/sevenUnitsGUI/RoundingTest.java287
-rw-r--r--src/test/java/sevenUnitsGUI/TabbedViewTest.java95
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());
+ }
+
+}