/** * 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 . */ package sevenUnitsGUI; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.nio.file.Path; import java.util.List; 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.Metric; import sevenUnits.unit.Unit; import sevenUnits.unit.UnitType; import sevenUnits.unit.UnitValue; import sevenUnits.utils.NameSymbol; import sevenUnits.utils.Nameable; import sevenUnits.utils.ObjectProduct; import sevenUnits.utils.UncertainDouble; /** * @author Adrien Hopkins * * @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 testUnits = Set.of(Metric.METRE, Metric.KILOMETRE, Metric.METRE_PER_SECOND, Metric.KILOMETRE_PER_HOUR); static final Set> testDimensions = Set .of(Metric.Dimensions.LENGTH, Metric.Dimensions.VELOCITY); /** * @return rounding rules used by {@link #testRoundingRules} * @since 2022-04-16 */ private static final Stream> 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 Set names(Set units) { return units.stream().map(Nameable::getName).collect(Collectors.toSet()); } /** * Test method for {@link Presenter#convertExpressions} * * @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 outputs = viewBot .expressionConversionList(); assertEquals("10000.0 m = 10.0 km", outputs.get(outputs.size() - 1).toString()); } /** * Tests that unit-conversion Views can correctly convert units * * @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 UnitValue expectedInput = UnitValue.of(Metric.METRE, 10000.0); final UnitValue expectedOutput = expectedInput .convertTo(Metric.KILOMETRE); final UnitConversionRecord expectedUC = UnitConversionRecord .fromValues(expectedInput, expectedOutput); assertEquals(List.of(expectedUC), viewBot.unitConversionList()); } /** * Tests that duplicate units are successfully removed, if that is asked for * * @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); // test that only one of them is included if duplicate units disabled presenter.setShowDuplicateUnits(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.setShowDuplicateUnits(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 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); // test that units are removed from each side when one-way conversion is // enabled presenter.setOneWayConversionEnabled(true); presenter.updateView(); assertEquals(metricNames, viewBot.getFromUnitNames()); assertEquals(nonMetricNames, viewBot.getToUnitNames()); // test that units are kept when one-way conversion is disabled presenter.setOneWayConversionEnabled(false); presenter.updateView(); assertEquals(allNames, viewBot.getFromUnitNames()); assertEquals(allNames, viewBot.getToUnitNames()); } /** * Tests the prefix-viewing functionality. * * @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 2022-04-16 */ @ParameterizedTest @MethodSource("getRoundingRules") void testRoundingRules(Function 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.of(12.3456789, 0)); final String actualOutputString = viewBot.unitConversionList().get(0) .outputValueString(); assertEquals(expectedOutputString, actualOutputString); } /** * Tests that settings can be saved to and loaded from a file. * * @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.setShowDuplicateUnits(true); presenter.setNumberDisplayRule(StandardDisplayRules.fixedPrecision(11)); presenter.saveSettings(TEST_SETTINGS); // overwrite custom settings presenter.setOneWayConversionEnabled(false); presenter.setShowDuplicateUnits(false); presenter.setNumberDisplayRule(StandardDisplayRules.uncertaintyBased()); // load settings & test that they're the same presenter.loadSettings(TEST_SETTINGS); assertTrue(presenter.oneWayConversionEnabled()); assertTrue(presenter.duplicateUnitsShown()); assertEquals(StandardDisplayRules.fixedPrecision(11), presenter.getNumberDisplayRule()); } /** * Ensures the Presenter generates the correct data upon a unit-viewing. * * @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 2022-02-12 */ @Test void testUpdateView() { // setup final ViewBot viewBot = new ViewBot(); final Presenter presenter = new Presenter(viewBot); // 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 fromUnits = viewBot.getFromUnitNames(); final Set toUnits = viewBot.getToUnitNames(); // test that fromUnits/toUnits is [METRE, KILOMETRE] assertEquals(Set.of("metre", "kilometre"), fromUnits); assertEquals(Set.of("metre", "kilometre"), toUnits); } }