Fix decimal separator issues when creating QuantityType from String (#2362)

* Fix decimal separator issues when creating QuantityType from String

Fixes #2360

Signed-off-by: Wouter Born <github@maindrain.net>
This commit is contained in:
Wouter Born 2021-05-14 09:11:25 +02:00 committed by GitHub
parent a8f469e5e7
commit 568881a678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 242 additions and 89 deletions

View File

@ -15,6 +15,7 @@ package org.openhab.core.library.types;
import static org.eclipse.jdt.annotation.DefaultLocation.*;
import java.math.BigDecimal;
import java.text.DecimalFormatSymbols;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
@ -58,6 +59,7 @@ public class QuantityType<T extends Quantity<T>> extends Number
implements PrimitiveType, State, Command, Comparable<QuantityType<T>> {
private static final long serialVersionUID = 8828949721938234629L;
private static final char DOT_DECIMAL_SEPARATOR = '.';
private static final BigDecimal HUNDRED = BigDecimal.valueOf(100);
public static final QuantityType<Dimensionless> ZERO = new QuantityType<>(0, AbstractUnit.ONE);
@ -102,6 +104,14 @@ public class QuantityType<T extends Quantity<T>> extends Number
BigDecimal bd = new BigDecimal(value);
quantity = (Quantity<T>) Quantities.getQuantity(bd, AbstractUnit.ONE, Scale.RELATIVE);
} else {
char defaultDecimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator();
// The quantity is parsed using a NumberFormat based on the default locale.
// To prevent issues, any dot decimal separators are replaced by the default locale decimal separator.
if (DOT_DECIMAL_SEPARATOR != defaultDecimalSeparator
&& formatted.contains(String.valueOf(DOT_DECIMAL_SEPARATOR))) {
formatted = formatted.replace(DOT_DECIMAL_SEPARATOR, defaultDecimalSeparator);
}
Quantity<T> absoluteQuantity = (Quantity<T>) Quantities.getQuantity(formatted);
quantity = Quantities.getQuantity(absoluteQuantity.getValue(), absoluteQuantity.getUnit(), Scale.RELATIVE);
}

View File

@ -17,12 +17,16 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;
import java.util.Locale;
import java.util.stream.Stream;
import javax.measure.quantity.Length;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.core.i18n.UnitProvider;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.NumberItem;
@ -44,6 +48,19 @@ public class ItemStateConverterImplTest {
private @NonNullByDefault({}) ItemStateConverterImpl itemStateConverter;
/**
* Locales having a different decimal separator to test string parsing and generation.
*/
static Stream<Locale> locales() {
return Stream.of(
// ٫ (Arabic, Egypt)
Locale.forLanguageTag("ar-EG"),
// , (German, Germany)
Locale.forLanguageTag("de-DE"),
// . (English, United States)
Locale.forLanguageTag("en-US"));
}
@BeforeEach
public void setup() {
UnitProvider unitProvider = mock(UnitProvider.class);
@ -51,16 +68,22 @@ public class ItemStateConverterImplTest {
itemStateConverter = new ItemStateConverterImpl(unitProvider);
}
@Test
public void testNullState() {
@ParameterizedTest
@MethodSource("locales")
public void testNullState(Locale locale) {
Locale.setDefault(locale);
State undef = itemStateConverter.convertToAcceptedState(null, null);
assertThat(undef, is(UnDefType.NULL));
}
@Test
@ParameterizedTest
@MethodSource("locales")
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public void testNoConversion() {
public void testNoConversion(Locale locale) {
Locale.setDefault(locale);
Item item = new NumberItem("number");
State originalState = new DecimalType(12.34);
State state = itemStateConverter.convertToAcceptedState(originalState, item);
@ -68,8 +91,11 @@ public class ItemStateConverterImplTest {
assertTrue(originalState == state);
}
@Test
public void testStateConversion() {
@ParameterizedTest
@MethodSource("locales")
public void testStateConversion(Locale locale) {
Locale.setDefault(locale);
Item item = new NumberItem("number");
State originalState = new PercentType("42");
State convertedState = itemStateConverter.convertToAcceptedState(originalState, item);
@ -77,8 +103,11 @@ public class ItemStateConverterImplTest {
assertThat(convertedState, is(new DecimalType("0.42")));
}
@Test
public void numberItemWithoutDimensionShouldConvertToDecimalType() {
@ParameterizedTest
@MethodSource("locales")
public void numberItemWithoutDimensionShouldConvertToDecimalType(Locale locale) {
Locale.setDefault(locale);
Item item = new NumberItem("number");
State originalState = new QuantityType<>("12.34 °C");
State convertedState = itemStateConverter.convertToAcceptedState(originalState, item);
@ -86,8 +115,11 @@ public class ItemStateConverterImplTest {
assertThat(convertedState, is(new DecimalType("12.34")));
}
@Test
public void numberItemWitDimensionShouldConvertToItemStateDescriptionUnit() {
@ParameterizedTest
@MethodSource("locales")
public void numberItemWitDimensionShouldConvertToItemStateDescriptionUnit(Locale locale) {
Locale.setDefault(locale);
NumberItem item = mock(NumberItem.class);
StateDescription stateDescription = mock(StateDescription.class);
when(item.getStateDescription()).thenReturn(stateDescription);
@ -100,8 +132,11 @@ public class ItemStateConverterImplTest {
assertThat(convertedState, is(new QuantityType<>("285.49 K")));
}
@Test
public void numberItemWitDimensionShouldConvertToLocaleBasedUnit() {
@ParameterizedTest
@MethodSource("locales")
public void numberItemWitDimensionShouldConvertToLocaleBasedUnit(Locale locale) {
Locale.setDefault(locale);
NumberItem item = mock(NumberItem.class);
doReturn(Temperature.class).when(item).getDimension();
@ -111,8 +146,11 @@ public class ItemStateConverterImplTest {
assertThat(convertedState, is(new QuantityType<>("54.212 °F")));
}
@Test
public void numberItemShouldNotConvertUnitsWhereMeasurmentSystemEquals() {
@ParameterizedTest
@MethodSource("locales")
public void numberItemShouldNotConvertUnitsWhereMeasurmentSystemEquals(Locale locale) {
Locale.setDefault(locale);
NumberItem item = mock(NumberItem.class);
doReturn(Length.class).when(item).getDimension();

View File

@ -15,7 +15,9 @@ package org.openhab.core.library.types;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Stream;
import javax.measure.Quantity;
import javax.measure.quantity.Dimensionless;
@ -23,9 +25,10 @@ import javax.measure.quantity.Power;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Temperature;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openhab.core.i18n.UnitProvider;
@ -41,155 +44,204 @@ import org.openhab.core.types.UnDefType;
* @author Henning Treu - Initial contribution
*/
@ExtendWith(MockitoExtension.class)
@NonNullByDefault
public class QuantityTypeArithmeticGroupFunctionTest {
private GroupFunction function;
private Set<Item> items;
private @NonNullByDefault({}) @Mock UnitProvider unitProvider;
private @Mock UnitProvider unitProvider;
@BeforeEach
public void init() {
items = new LinkedHashSet<>();
/**
* Locales having a different decimal separator to test string parsing and generation.
*/
static Stream<Locale> locales() {
return Stream.of(
// ٫ (Arabic, Egypt)
Locale.forLanguageTag("ar-EG"),
// , (German, Germany)
Locale.forLanguageTag("de-DE"),
// . (English, United States)
Locale.forLanguageTag("en-US"));
}
@Test
public void testSumFunctionQuantityType() {
@ParameterizedTest
@MethodSource("locales")
public void testSumFunctionQuantityType(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("89 °C")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("122.41 °C")));
function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("234.95 °C"), state);
}
@Test
public void testSumFunctionQuantityTypeDifferentUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testSumFunctionQuantityTypeDifferentUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("192.2 °F")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("395.56 K")));
function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("234.95 °C"), state);
}
@Test
public void testSumFunctionQuantityTypeIncompatibleUnits() {
items = new LinkedHashSet<>(); // we need an ordered set to guarantee the Unit of the first entry
@ParameterizedTest
@MethodSource("locales")
public void testSumFunctionQuantityTypeIncompatibleUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>(); // we need an ordered set to guarantee the Unit of the first entry
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("23.54 °C"), state);
}
@Test
public void testAvgFunctionQuantityType() {
@ParameterizedTest
@MethodSource("locales")
public void testAvgFunctionQuantityType(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("200 °C")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("200 °C"), state);
}
@Test
public void testAvgFunctionQuantityTypeDifferentUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testAvgFunctionQuantityTypeDifferentUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("55.33333333333333333333333333333334 °C"), state);
}
@Test
public void testAvgFunctionQuantityTypeIncompatibleUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testAvgFunctionQuantityTypeIncompatibleUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Avg(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("23.54 °C"), state);
}
@Test
public void testMaxFunctionQuantityType() {
@ParameterizedTest
@MethodSource("locales")
public void testMaxFunctionQuantityType(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("200 °C")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("300 °C"), state);
}
@Test
public void testMaxFunctionQuantityTypeDifferentUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testMaxFunctionQuantityTypeDifferentUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("100 °C"), state);
}
@Test
public void testMaxFunctionQuantityTypeIncompatibleUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testMaxFunctionQuantityTypeIncompatibleUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("23.54 °C"), state);
}
@Test
public void testMinFunctionQuantityType() {
@ParameterizedTest
@MethodSource("locales")
public void testMinFunctionQuantityType(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("200 °C")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("300 °C")));
function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("100 °C"), state);
}
@Test
public void testMaxFunctionQuantityTypeOnDimensionless() {
@ParameterizedTest
@MethodSource("locales")
public void testMaxFunctionQuantityTypeOnDimensionless(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Dimensionless.class, new QuantityType<>("48 %")));
items.add(createNumberItem("TestItem2", Dimensionless.class, new QuantityType<>("36 %")));
items.add(createNumberItem("TestItem3", Dimensionless.class, new QuantityType<>("0 %")));
@ -197,44 +249,56 @@ public class QuantityTypeArithmeticGroupFunctionTest {
items.add(createNumberItem("TestItem5", Dimensionless.class, new QuantityType<>("0 %")));
items.add(createNumberItem("TestItem6", Dimensionless.class, new QuantityType<>("0 %")));
function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Max(Dimensionless.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("48 %"), state);
}
@Test
public void testMinFunctionQuantityTypeDifferentUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testMinFunctionQuantityTypeDifferentUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("100 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Temperature.class, new QuantityType<>("113 °F")));
items.add(createNumberItem("TestItem4", Temperature.class, UnDefType.UNDEF));
items.add(createNumberItem("TestItem5", Temperature.class, new QuantityType<>("294.15 K")));
function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("294.15 K"), state);
}
@Test
public void testMinFunctionQuantityTypeIncompatibleUnits() {
@ParameterizedTest
@MethodSource("locales")
public void testMinFunctionQuantityTypeIncompatibleUnits(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Temperature.class, new QuantityType<>("23.54 °C")));
items.add(createNumberItem("TestItem2", Temperature.class, UnDefType.NULL));
items.add(createNumberItem("TestItem3", Pressure.class, new QuantityType<>("192.2 hPa")));
function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Min(Temperature.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("23.54 °C"), state);
}
@Test
public void testSumFunctionQuantityTypeWithGroups() {
@ParameterizedTest
@MethodSource("locales")
public void testSumFunctionQuantityTypeWithGroups(Locale locale) {
Locale.setDefault(locale);
Set<Item> items = new LinkedHashSet<>();
items.add(createNumberItem("TestItem1", Power.class, new QuantityType<>("5 W")));
items.add(createGroupItem("TestGroup1", Power.class, new QuantityType<>("5 W")));
function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class);
GroupFunction function = new QuantityTypeArithmeticGroupFunction.Sum(Power.class);
State state = function.calculate(items);
assertEquals(new QuantityType<>("10 W"), state);

View File

@ -20,6 +20,8 @@ import static org.openhab.core.library.unit.MetricPrefix.CENTI;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.stream.Stream;
import javax.measure.format.MeasurementParseException;
import javax.measure.quantity.Dimensionless;
@ -31,6 +33,8 @@ import javax.measure.quantity.Temperature;
import javax.measure.quantity.Time;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.openhab.core.library.dimension.DataAmount;
import org.openhab.core.library.dimension.DataTransferRate;
import org.openhab.core.library.dimension.Density;
@ -49,8 +53,18 @@ import tech.units.indriya.unit.UnitDimension;
@SuppressWarnings("null")
public class QuantityTypeTest {
// we need to get the decimal separator of the default locale for our tests
private static final char SEP = new DecimalFormatSymbols().getDecimalSeparator();
/**
* Locales having a different decimal separator to test string parsing and generation.
*/
static Stream<Locale> locales() {
return Stream.of(
// ٫ (Arabic, Egypt)
Locale.forLanguageTag("ar-EG"),
// , (German, Germany)
Locale.forLanguageTag("de-DE"),
// . (English, United States)
Locale.forLanguageTag("en-US"));
}
@Test
public void testDimensionless() {
@ -128,12 +142,14 @@ public class QuantityTypeTest {
QuantityType<Time> millis = seconds.toUnit(MetricPrefix.MILLI(Units.SECOND));
QuantityType<Time> minutes = seconds.toUnit(Units.MINUTE);
assertThat(seconds.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("80" + SEP + "0 s"));
assertThat(millis.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("80000" + SEP + "0 ms"));
assertThat(minutes.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("1" + SEP + "3 min"));
char sep = new DecimalFormatSymbols().getDecimalSeparator();
assertThat(seconds.format("%.1f"), is("80" + SEP + "0"));
assertThat(minutes.format("%.1f"), is("1" + SEP + "3"));
assertThat(seconds.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("80" + sep + "0 s"));
assertThat(millis.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("80000" + sep + "0 ms"));
assertThat(minutes.format("%.1f " + UnitUtils.UNIT_PLACEHOLDER), is("1" + sep + "3 min"));
assertThat(seconds.format("%.1f"), is("80" + sep + "0"));
assertThat(minutes.format("%.1f"), is("1" + sep + "3"));
assertThat(seconds.format("%1$tH:%1$tM:%1$tS"), is("00:01:20"));
assertThat(millis.format("%1$tHh %1$tMm %1$tSs"), is("00h 01m 20s"));
@ -196,28 +212,41 @@ public class QuantityTypeTest {
assertNull(new QuantityType<>("0.5").as(OpenClosedType.class));
}
@Test
public void testConversionToHSBType() {
@ParameterizedTest
@MethodSource("locales")
public void testConversionToHSBType(Locale locale) {
Locale.setDefault(locale);
assertEquals(new HSBType("0,0,0"), new QuantityType<>("0.0").as(HSBType.class));
assertEquals(new HSBType("0,0,100"), new QuantityType<>("1.0").as(HSBType.class));
assertEquals(new HSBType("0,0,50"), new QuantityType<>("0.5").as(HSBType.class));
}
@Test
public void testConversionToPercentType() {
@ParameterizedTest
@MethodSource("locales")
public void testConversionToPercentType(Locale locale) {
Locale.setDefault(locale);
assertEquals(PercentType.HUNDRED, new QuantityType<>("100 %").as(PercentType.class));
assertEquals(PercentType.ZERO, new QuantityType<>("0 %").as(PercentType.class));
}
@Test
public void toFullStringShouldParseToEqualState() {
@ParameterizedTest
@MethodSource("locales")
public void toFullStringShouldParseToEqualState(Locale locale) {
Locale.setDefault(locale);
QuantityType<Temperature> temp = new QuantityType<>("20 °C");
assertThat(temp.toFullString(), is("20 °C"));
assertThat(QuantityType.valueOf(temp.toFullString()), is(temp));
}
public void testAdd() {
@ParameterizedTest
@MethodSource("locales")
public void testAdd(Locale locale) {
Locale.setDefault(locale);
QuantityType<?> result = new QuantityType<>("20 m").add(new QuantityType<>("20cm"));
assertThat(result, is(new QuantityType<>("20.20 m")));
}
@ -227,8 +256,11 @@ public class QuantityTypeTest {
assertThat(new QuantityType<>("20 °C").negate(), is(new QuantityType<>("-20 °C")));
}
@Test
public void testSubtract() {
@ParameterizedTest
@MethodSource("locales")
public void testSubtract(Locale locale) {
Locale.setDefault(locale);
QuantityType<?> result = new QuantityType<>("20 m").subtract(new QuantityType<>("20cm"));
assertThat(result, is(new QuantityType<>("19.80 m")));
}
@ -243,18 +275,27 @@ public class QuantityTypeTest {
assertThat(new QuantityType<>("2 m").multiply(new QuantityType<>("4 cm")), is(new QuantityType<>("8 m·cm")));
}
@Test
public void testDivideNumber() {
@ParameterizedTest
@MethodSource("locales")
public void testDivideNumber(Locale locale) {
Locale.setDefault(locale);
assertThat(new QuantityType<>("4 m").divide(BigDecimal.valueOf(2)), is(new QuantityType<>("2 m")));
}
@Test
public void testDivideQuantityType() {
@ParameterizedTest
@MethodSource("locales")
public void testDivideQuantityType(Locale locale) {
Locale.setDefault(locale);
assertThat(new QuantityType<>("4 m").divide(new QuantityType<>("2 cm")), is(new QuantityType<>("2 m/cm")));
}
@Test
public void testDivideZero() {
@ParameterizedTest
@MethodSource("locales")
public void testDivideZero(Locale locale) {
Locale.setDefault(locale);
assertThrows(IllegalArgumentException.class, () -> new QuantityType<>("4 m").divide(QuantityType.ZERO));
}