blob: 57db08860a9d097ac0f5d76149bd4a386a93f3f7 [file] [log] [blame]
/* GENERATED SOURCE. DO NOT MODIFY. */
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
package android.icu.dev.test.number;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.junit.Ignore;
import org.junit.Test;
import android.icu.impl.number.Grouper;
import android.icu.impl.number.MacroProps;
import android.icu.impl.number.Padder;
import android.icu.impl.number.Padder.PadPosition;
import android.icu.impl.number.PatternStringParser;
import android.icu.number.CompactNotation;
import android.icu.number.FractionRounder;
import android.icu.number.IntegerWidth;
import android.icu.number.LocalizedNumberFormatter;
import android.icu.number.Notation;
import android.icu.number.NumberFormatter;
import android.icu.number.NumberFormatter.DecimalSeparatorDisplay;
import android.icu.number.NumberFormatter.GroupingStrategy;
import android.icu.number.NumberFormatter.SignDisplay;
import android.icu.number.NumberFormatter.UnitWidth;
import android.icu.number.Rounder;
import android.icu.number.ScientificNotation;
import android.icu.number.UnlocalizedNumberFormatter;
import android.icu.text.DecimalFormatSymbols;
import android.icu.text.NumberingSystem;
import android.icu.util.Currency;
import android.icu.util.Currency.CurrencyUsage;
import android.icu.util.CurrencyAmount;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.icu.util.NoUnit;
import android.icu.util.ULocale;
import android.icu.testsharding.MainTestShard;
@MainTestShard
public class NumberFormatterApiTest {
private static final Currency USD = Currency.getInstance("USD");
private static final Currency GBP = Currency.getInstance("GBP");
private static final Currency CZK = Currency.getInstance("CZK");
private static final Currency CAD = Currency.getInstance("CAD");
private static final Currency ESP = Currency.getInstance("ESP");
private static final Currency PTE = Currency.getInstance("PTE");
@Test
public void notationSimple() {
assertFormatDescending(
"Basic",
"",
NumberFormatter.with(),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescendingBig(
"Big Simple",
"",
NumberFormatter.with().notation(Notation.simple()),
ULocale.ENGLISH,
"87,650,000",
"8,765,000",
"876,500",
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatSingle(
"Basic with Negative Sign",
"",
NumberFormatter.with(),
ULocale.ENGLISH,
-9876543.21,
"-9,876,543.21");
}
@Test
public void notationScientific() {
assertFormatDescending(
"Scientific",
"E",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
"8.765E4",
"8.765E3",
"8.765E2",
"8.765E1",
"8.765E0",
"8.765E-1",
"8.765E-2",
"8.765E-3",
"0E0");
assertFormatDescending(
"Engineering",
"E3",
NumberFormatter.with().notation(Notation.engineering()),
ULocale.ENGLISH,
"87.65E3",
"8.765E3",
"876.5E0",
"87.65E0",
"8.765E0",
"876.5E-3",
"87.65E-3",
"8.765E-3",
"0E0");
assertFormatDescending(
"Scientific sign always shown",
"E+",
NumberFormatter.with().notation(Notation.scientific().withExponentSignDisplay(SignDisplay.ALWAYS)),
ULocale.ENGLISH,
"8.765E+4",
"8.765E+3",
"8.765E+2",
"8.765E+1",
"8.765E+0",
"8.765E-1",
"8.765E-2",
"8.765E-3",
"0E+0");
assertFormatDescending(
"Scientific min exponent digits",
"E00",
NumberFormatter.with().notation(Notation.scientific().withMinExponentDigits(2)),
ULocale.ENGLISH,
"8.765E04",
"8.765E03",
"8.765E02",
"8.765E01",
"8.765E00",
"8.765E-01",
"8.765E-02",
"8.765E-03",
"0E00");
assertFormatSingle(
"Scientific Negative",
"E",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
-1000000,
"-1E6");
}
@Test
public void notationCompact() {
assertFormatDescendingBig(
"Compact Short",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
"88M",
"8.8M",
"876K",
"88K",
"8.8K",
"876",
"88",
"8.8",
"0");
assertFormatDescendingBig(
"Compact Long",
"CC",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.ENGLISH,
"88 million",
"8.8 million",
"876 thousand",
"88 thousand",
"8.8 thousand",
"876",
"88",
"8.8",
"0");
assertFormatDescending(
"Compact Short Currency",
"C $USD",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD),
ULocale.ENGLISH,
"$88K",
"$8.8K",
"$876",
"$88",
"$8.8",
"$0.88",
"$0.088",
"$0.0088",
"$0");
assertFormatDescending(
"Compact Short with ISO Currency",
"C $USD unit-width=ISO_CODE",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K",
"USD 8.8K",
"USD 876",
"USD 88",
"USD 8.8",
"USD 0.88",
"USD 0.088",
"USD 0.0088",
"USD 0");
assertFormatDescending(
"Compact Short with Long Name Currency",
"C $USD unit-width=FULL_NAME",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88K US dollars",
"8.8K US dollars",
"876 US dollars",
"88 US dollars",
"8.8 US dollars",
"0.88 US dollars",
"0.088 US dollars",
"0.0088 US dollars",
"0 US dollars");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
"Compact Long Currency",
"CC $USD",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD),
ULocale.ENGLISH,
"$88K", // should be something like "$88 thousand"
"$8.8K",
"$876",
"$88",
"$8.8",
"$0.88",
"$0.088",
"$0.0088",
"$0");
// Note: Most locales don't have compact long currency, so this currently falls back to short.
// This test case should be fixed when proper compact long currency patterns are added.
assertFormatDescending(
"Compact Long with ISO Currency",
"CC $USD unit-width=ISO_CODE",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K", // should be something like "USD 88 thousand"
"USD 8.8K",
"USD 876",
"USD 88",
"USD 8.8",
"USD 0.88",
"USD 0.088",
"USD 0.0088",
"USD 0");
// TODO: This behavior could be improved and should be revisited.
assertFormatDescending(
"Compact Long with Long Name Currency",
"CC $USD unit-width=FULL_NAME",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88 thousand US dollars",
"8.8 thousand US dollars",
"876 US dollars",
"88 US dollars",
"8.8 US dollars",
"0.88 US dollars",
"0.088 US dollars",
"0.0088 US dollars",
"0 US dollars");
assertFormatSingle(
"Compact Plural One",
"CC",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.forLanguageTag("es"),
1000000,
"1 millón");
assertFormatSingle(
"Compact Plural Other",
"CC",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.forLanguageTag("es"),
2000000,
"2 millones");
assertFormatSingle(
"Compact with Negative Sign",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
-9876543.21,
"-9.9M");
assertFormatSingle(
"Compact Rounding",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
990000,
"990K");
assertFormatSingle(
"Compact Rounding",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
999000,
"999K");
assertFormatSingle(
"Compact Rounding",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
999900,
"1M");
assertFormatSingle(
"Compact Rounding",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
9900000,
"9.9M");
assertFormatSingle(
"Compact Rounding",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
9990000,
"10M");
Map<String, Map<String, String>> compactCustomData = new HashMap<String, Map<String, String>>();
Map<String, String> entry = new HashMap<String, String>();
entry.put("one", "Kun");
entry.put("other", "0KK");
compactCustomData.put("1000", entry);
assertFormatSingle(
"Compact Somali No Figure",
"",
NumberFormatter.with().notation(CompactNotation.forCustomData(compactCustomData)),
ULocale.ENGLISH,
1000,
"Kun");
}
@Test
public void unitMeasure() {
assertFormatDescending(
"Meters Short",
"U:length:meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
"87,650 m",
"8,765 m",
"876.5 m",
"87.65 m",
"8.765 m",
"0.8765 m",
"0.08765 m",
"0.008765 m",
"0 m");
assertFormatDescending(
"Meters Long",
"U:length:meter unit-width=FULL_NAME",
NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"87,650 meters",
"8,765 meters",
"876.5 meters",
"87.65 meters",
"8.765 meters",
"0.8765 meters",
"0.08765 meters",
"0.008765 meters",
"0 meters");
assertFormatDescending(
"Compact Meters Long",
"CC U:length:meter unit-width=FULL_NAME",
NumberFormatter.with().notation(Notation.compactLong()).unit(MeasureUnit.METER)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88 thousand meters",
"8.8 thousand meters",
"876 meters",
"88 meters",
"8.8 meters",
"0.88 meters",
"0.088 meters",
"0.0088 meters",
"0 meters");
assertFormatSingleMeasure(
"Meters with Measure Input",
"unit-width=FULL_NAME",
NumberFormatter.with().unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
new Measure(5.43, MeasureUnit.METER),
"5.43 meters");
assertFormatSingleMeasure(
"Measure format method takes precedence over fluent chain",
"U:length:meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
new Measure(5.43, USD),
"$5.43");
assertFormatSingle(
"Meters with Negative Sign",
"U:length:meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
-9876543.21,
"-9,876,543.21 m");
// The locale string "सान" appears only in brx.txt:
assertFormatSingle(
"Interesting Data Fallback 1",
"U:duration:day unit-width=FULL_NAME",
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("brx"),
5.43,
"5.43 सान");
// Requires following the alias from unitsNarrow to unitsShort:
assertFormatSingle(
"Interesting Data Fallback 2",
"U:duration:day unit-width=NARROW",
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("brx"),
5.43,
"5.43 d");
// en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit,
// requiring fallback to the root.
assertFormatSingle(
"Interesting Data Fallback 3",
"U:area:square-meter unit-width=NARROW",
NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("en-GB"),
5.43,
"5.43 m²");
// es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
// NOTE: This example is in the documentation.
assertFormatSingle(
"MeasureUnit Difference between Narrow and Short (Narrow Version)",
"",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("es-US"),
5.43,
"5.43°");
assertFormatSingle(
"MeasureUnit Difference between Narrow and Short (Short Version)",
"",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("es-US"),
5.43,
"5.43 °F");
assertFormatSingle(
"MeasureUnit form without {0} in CLDR pattern",
"",
NumberFormatter.with().unit(MeasureUnit.KELVIN).unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("es-MX"),
1,
"kelvin");
assertFormatSingle(
"MeasureUnit form without {0} in CLDR pattern and wide base form",
"",
NumberFormatter.with()
.rounding(Rounder.fixedFraction(20))
.unit(MeasureUnit.KELVIN)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("es-MX"),
1,
"kelvin");
}
@Test
public void unitCompoundMeasure() {
assertFormatDescending(
"Meters Per Second Short (unit that simplifies)",
"",
NumberFormatter.with().unit(MeasureUnit.METER).perUnit(MeasureUnit.SECOND),
ULocale.ENGLISH,
"87,650 m/s",
"8,765 m/s",
"876.5 m/s",
"87.65 m/s",
"8.765 m/s",
"0.8765 m/s",
"0.08765 m/s",
"0.008765 m/s",
"0 m/s");
assertFormatDescending(
"Pounds Per Square Mile Short (secondary unit has per-format)",
"",
NumberFormatter.with().unit(MeasureUnit.POUND).perUnit(MeasureUnit.SQUARE_MILE),
ULocale.ENGLISH,
"87,650 lb/mi²",
"8,765 lb/mi²",
"876.5 lb/mi²",
"87.65 lb/mi²",
"8.765 lb/mi²",
"0.8765 lb/mi²",
"0.08765 lb/mi²",
"0.008765 lb/mi²",
"0 lb/mi²");
assertFormatDescending(
"Joules Per Furlong Short (unit with no simplifications or special patterns)",
"",
NumberFormatter.with().unit(MeasureUnit.JOULE).perUnit(MeasureUnit.FURLONG),
ULocale.ENGLISH,
"87,650 J/fur",
"8,765 J/fur",
"876.5 J/fur",
"87.65 J/fur",
"8.765 J/fur",
"0.8765 J/fur",
"0.08765 J/fur",
"0.008765 J/fur",
"0 J/fur");
}
@Test
public void unitCurrency() {
assertFormatDescending(
"Currency",
"$GBP",
NumberFormatter.with().unit(GBP),
ULocale.ENGLISH,
"£87,650.00",
"£8,765.00",
"£876.50",
"£87.65",
"£8.76",
"£0.88",
"£0.09",
"£0.01",
"£0.00");
assertFormatDescending(
"Currency ISO",
"$GBP unit-width=ISO_CODE",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"GBP 87,650.00",
"GBP 8,765.00",
"GBP 876.50",
"GBP 87.65",
"GBP 8.76",
"GBP 0.88",
"GBP 0.09",
"GBP 0.01",
"GBP 0.00");
assertFormatDescending(
"Currency Long Name",
"$GBP unit-width=FULL_NAME",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"87,650.00 British pounds",
"8,765.00 British pounds",
"876.50 British pounds",
"87.65 British pounds",
"8.76 British pounds",
"0.88 British pounds",
"0.09 British pounds",
"0.01 British pounds",
"0.00 British pounds");
assertFormatDescending(
"Currency Hidden",
"$GBP unit-width=HIDDEN",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.HIDDEN),
ULocale.ENGLISH,
"87,650.00",
"8,765.00",
"876.50",
"87.65",
"8.76",
"0.88",
"0.09",
"0.01",
"0.00");
assertFormatSingleMeasure(
"Currency with CurrencyAmount Input",
"",
NumberFormatter.with(),
ULocale.ENGLISH,
new CurrencyAmount(5.43, GBP),
"£5.43");
assertFormatSingle(
"Currency Long Name from Pattern Syntax",
"$GBP F0 grouping=none integer-width=1- symbols=loc:en sign=AUTO decimal=AUTO",
NumberFormatter.fromDecimalFormat(
PatternStringParser.parseToProperties("0 ¤¤¤"),
DecimalFormatSymbols.getInstance(ULocale.ENGLISH),
null).unit(GBP),
ULocale.ENGLISH,
1234567.89,
"1234568 British pounds");
assertFormatSingle(
"Currency with Negative Sign",
"$GBP",
NumberFormatter.with().unit(GBP),
ULocale.ENGLISH,
-9876543.21,
"-£9,876,543.21");
// The full currency symbol is not shown in NARROW format.
// NOTE: This example is in the documentation.
assertFormatSingle(
"Currency Difference between Narrow and Short (Narrow Version)",
"",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("en-CA"),
5.43,
"$5.43");
assertFormatSingle(
"Currency Difference between Narrow and Short (Short Version)",
"",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("en-CA"),
5.43,
"US$5.43");
assertFormatSingle(
"Currency-dependent format (Control)",
"",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("ca"),
444444.55,
"444.444,55 USD");
assertFormatSingle(
"Currency-dependent format (Test)",
"",
NumberFormatter.with().unit(ESP).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("ca"),
444444.55,
"₧ 444.445");
assertFormatSingle(
"Currency-dependent symbols (Control)",
"",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("pt-PT"),
444444.55,
"444 444,55 US$");
// NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero-
// width space), and they set the decimal separator to the $ symbol.
assertFormatSingle(
"Currency-dependent symbols (Test)",
"",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("pt-PT"),
444444.55,
"444,444$55 \u200B");
assertFormatSingle(
"Currency-dependent symbols (Test)",
"",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("pt-PT"),
444444.55,
"444,444$55 PTE");
assertFormatSingle(
"Currency-dependent symbols (Test)",
"",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.ISO_CODE),
ULocale.forLanguageTag("pt-PT"),
444444.55,
"444,444$55 PTE");
}
@Test
public void unitPercent() {
assertFormatDescending(
"Percent",
"%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
"87,650%",
"8,765%",
"876.5%",
"87.65%",
"8.765%",
"0.8765%",
"0.08765%",
"0.008765%",
"0%");
assertFormatDescending(
"Permille",
"%%",
NumberFormatter.with().unit(NoUnit.PERMILLE),
ULocale.ENGLISH,
"87,650‰",
"8,765‰",
"876.5‰",
"87.65‰",
"8.765‰",
"0.8765‰",
"0.08765‰",
"0.008765‰",
"0‰");
assertFormatSingle(
"NoUnit Base",
"B",
NumberFormatter.with().unit(NoUnit.BASE),
ULocale.ENGLISH,
51423,
"51,423");
assertFormatSingle(
"Percent with Negative Sign",
"%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
-98.7654321,
"-98.765432%");
}
@Test
public void roundingFraction() {
assertFormatDescending(
"Integer",
"F0",
NumberFormatter.with().rounding(Rounder.integer()),
ULocale.ENGLISH,
"87,650",
"8,765",
"876",
"88",
"9",
"1",
"0",
"0",
"0");
assertFormatDescending(
"Fixed Fraction",
"F3",
NumberFormatter.with().rounding(Rounder.fixedFraction(3)),
ULocale.ENGLISH,
"87,650.000",
"8,765.000",
"876.500",
"87.650",
"8.765",
"0.876",
"0.088",
"0.009",
"0.000");
assertFormatDescending(
"Min Fraction",
"F1-",
NumberFormatter.with().rounding(Rounder.minFraction(1)),
ULocale.ENGLISH,
"87,650.0",
"8,765.0",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0.0");
assertFormatDescending(
"Max Fraction",
"F-1",
NumberFormatter.with().rounding(Rounder.maxFraction(1)),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.6",
"8.8",
"0.9",
"0.1",
"0",
"0");
assertFormatDescending(
"Min/Max Fraction",
"F1-3",
NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 3)),
ULocale.ENGLISH,
"87,650.0",
"8,765.0",
"876.5",
"87.65",
"8.765",
"0.876",
"0.088",
"0.009",
"0.0");
}
@Test
public void roundingFigures() {
assertFormatSingle(
"Fixed Significant",
"S3",
NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
ULocale.ENGLISH,
-98,
"-98.0");
assertFormatSingle(
"Fixed Significant Rounding",
"S3",
NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
ULocale.ENGLISH,
-98.7654321,
"-98.8");
assertFormatSingle(
"Fixed Significant Zero",
"S3",
NumberFormatter.with().rounding(Rounder.fixedDigits(3)),
ULocale.ENGLISH,
0,
"0.00");
assertFormatSingle(
"Min Significant",
"S2-",
NumberFormatter.with().rounding(Rounder.minDigits(2)),
ULocale.ENGLISH,
-9,
"-9.0");
assertFormatSingle(
"Max Significant",
"S-4",
NumberFormatter.with().rounding(Rounder.maxDigits(4)),
ULocale.ENGLISH,
98.7654321,
"98.77");
assertFormatSingle(
"Min/Max Significant",
"S3-4",
NumberFormatter.with().rounding(Rounder.minMaxDigits(3, 4)),
ULocale.ENGLISH,
9.99999,
"10.0");
}
@Test
public void roundingFractionFigures() {
assertFormatDescending(
"Basic Significant", // for comparison
"S-2",
NumberFormatter.with().rounding(Rounder.maxDigits(2)),
ULocale.ENGLISH,
"88,000",
"8,800",
"880",
"88",
"8.8",
"0.88",
"0.088",
"0.0088",
"0");
assertFormatDescending(
"FracSig minMaxFrac minSig",
"F1-2>3",
NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 2).withMinDigits(3)),
ULocale.ENGLISH,
"87,650.0",
"8,765.0",
"876.5",
"87.65",
"8.76",
"0.876", // minSig beats maxFrac
"0.0876", // minSig beats maxFrac
"0.00876", // minSig beats maxFrac
"0.0");
assertFormatDescending(
"FracSig minMaxFrac maxSig A",
"F1-3<2",
NumberFormatter.with().rounding(Rounder.minMaxFraction(1, 3).withMaxDigits(2)),
ULocale.ENGLISH,
"88,000.0", // maxSig beats maxFrac
"8,800.0", // maxSig beats maxFrac
"880.0", // maxSig beats maxFrac
"88.0", // maxSig beats maxFrac
"8.8", // maxSig beats maxFrac
"0.88", // maxSig beats maxFrac
"0.088",
"0.009",
"0.0");
assertFormatDescending(
"FracSig minMaxFrac maxSig B",
"F2<2",
NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMaxDigits(2)),
ULocale.ENGLISH,
"88,000.00", // maxSig beats maxFrac
"8,800.00", // maxSig beats maxFrac
"880.00", // maxSig beats maxFrac
"88.00", // maxSig beats maxFrac
"8.80", // maxSig beats maxFrac
"0.88",
"0.09",
"0.01",
"0.00");
assertFormatSingle(
"FracSig with trailing zeros A",
"",
NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMinDigits(3)),
ULocale.ENGLISH,
0.1,
"0.10");
assertFormatSingle(
"FracSig with trailing zeros B",
"",
NumberFormatter.with().rounding(Rounder.fixedFraction(2).withMinDigits(3)),
ULocale.ENGLISH,
0.0999999,
"0.10");
}
@Test
public void roundingOther() {
assertFormatDescending(
"Rounding None",
"Y",
NumberFormatter.with().rounding(Rounder.unlimited()),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescending(
"Increment",
"M0.5",
NumberFormatter.with().rounding(Rounder.increment(BigDecimal.valueOf(0.5))),
ULocale.ENGLISH,
"87,650.0",
"8,765.0",
"876.5",
"87.5",
"9.0",
"1.0",
"0.0",
"0.0",
"0.0");
assertFormatDescending(
"Increment with Min Fraction",
"M0.5",
NumberFormatter.with().rounding(Rounder.increment(new BigDecimal("0.50"))),
ULocale.ENGLISH,
"87,650.00",
"8,765.00",
"876.50",
"87.50",
"9.00",
"1.00",
"0.00",
"0.00",
"0.00");
assertFormatDescending(
"Currency Standard",
"$CZK GSTANDARD",
NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.STANDARD)).unit(CZK),
ULocale.ENGLISH,
"CZK 87,650.00",
"CZK 8,765.00",
"CZK 876.50",
"CZK 87.65",
"CZK 8.76",
"CZK 0.88",
"CZK 0.09",
"CZK 0.01",
"CZK 0.00");
assertFormatDescending(
"Currency Cash",
"$CZK GCASH",
NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH)).unit(CZK),
ULocale.ENGLISH,
"CZK 87,650",
"CZK 8,765",
"CZK 876",
"CZK 88",
"CZK 9",
"CZK 1",
"CZK 0",
"CZK 0",
"CZK 0");
assertFormatDescending(
"Currency Cash with Nickel Rounding",
"$CAD GCASH",
NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH)).unit(CAD),
ULocale.ENGLISH,
"CA$87,650.00",
"CA$8,765.00",
"CA$876.50",
"CA$87.65",
"CA$8.75",
"CA$0.90",
"CA$0.10",
"CA$0.00",
"CA$0.00");
assertFormatDescending(
"Currency not in top-level fluent chain",
"F0",
NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH).withCurrency(CZK)),
ULocale.ENGLISH,
"87,650",
"8,765",
"876",
"88",
"9",
"1",
"0",
"0",
"0");
// NOTE: Other tests cover the behavior of the other rounding modes.
assertFormatDescending(
"Rounding Mode CEILING",
"",
NumberFormatter.with().rounding(Rounder.integer().withMode(RoundingMode.CEILING)),
ULocale.ENGLISH,
"87,650",
"8,765",
"877",
"88",
"9",
"1",
"1",
"1",
"0");
}
@Test
public void grouping() {
assertFormatDescendingBig(
"Western Grouping",
"grouping=defaults",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
ULocale.ENGLISH,
"87,650,000",
"8,765,000",
"876,500",
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatDescendingBig(
"Indic Grouping",
"grouping=defaults",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
new ULocale("en-IN"),
"8,76,50,000",
"87,65,000",
"8,76,500",
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatDescendingBig(
"Western Grouping, Min 2",
"grouping=min2",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
ULocale.ENGLISH,
"87,650,000",
"8,765,000",
"876,500",
"87,650",
"8765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatDescendingBig(
"Indic Grouping, Min 2",
"grouping=min2",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
new ULocale("en-IN"),
"8,76,50,000",
"87,65,000",
"8,76,500",
"87,650",
"8765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatDescendingBig(
"No Grouping",
"grouping=none",
NumberFormatter.with().grouping(GroupingStrategy.OFF),
new ULocale("en-IN"),
"87650000",
"8765000",
"876500",
"87650",
"8765",
"876.5",
"87.65",
"8.765",
"0");
assertFormatDescendingBig(
"Indic locale with THOUSANDS grouping",
"",
NumberFormatter.with().grouping(GroupingStrategy.THOUSANDS),
new ULocale("en-IN"),
"87,650,000",
"8,765,000",
"876,500",
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0");
// NOTE: Hungarian is interesting because it has minimumGroupingDigits=4 in locale data
// If this test breaks due to data changes, find another locale that has minimumGroupingDigits.
assertFormatDescendingBig(
"Hungarian Grouping",
"",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
new ULocale("hu"),
"87 650 000",
"8 765 000",
"876500",
"87650",
"8765",
"876,5",
"87,65",
"8,765",
"0");
assertFormatDescendingBig(
"Hungarian Grouping, Min 2",
"",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
new ULocale("hu"),
"87 650 000",
"8 765 000",
"876500",
"87650",
"8765",
"876,5",
"87,65",
"8,765",
"0");
assertFormatDescendingBig(
"Hungarian Grouping, Always",
"",
NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED),
new ULocale("hu"),
"87 650 000",
"8 765 000",
"876 500",
"87 650",
"8 765",
"876,5",
"87,65",
"8,765",
"0");
// NOTE: Bulgarian is interesting because it has no grouping in the default currency format.
// If this test breaks due to data changes, find another locale that has no default grouping.
assertFormatDescendingBig(
"Bulgarian Currency Grouping",
"",
NumberFormatter.with().grouping(GroupingStrategy.AUTO).unit(USD),
new ULocale("bg"),
"87650000,00 щ.д.",
"8765000,00 щ.д.",
"876500,00 щ.д.",
"87650,00 щ.д.",
"8765,00 щ.д.",
"876,50 щ.д.",
"87,65 щ.д.",
"8,76 щ.д.",
"0,00 щ.д.");
assertFormatDescendingBig(
"Bulgarian Currency Grouping, Always",
"",
NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED).unit(USD),
new ULocale("bg"),
"87 650 000,00 щ.д.",
"8 765 000,00 щ.д.",
"876 500,00 щ.д.",
"87 650,00 щ.д.",
"8 765,00 щ.д.",
"876,50 щ.д.",
"87,65 щ.д.",
"8,76 щ.д.",
"0,00 щ.д.");
MacroProps macros = new MacroProps();
macros.grouping = Grouper.getInstance((short) 4, (short) 1, (short) 3);
assertFormatDescendingBig(
"Custom Grouping via Internal API",
"",
NumberFormatter.with().macros(macros),
ULocale.ENGLISH,
"8,7,6,5,0000",
"8,7,6,5000",
"876500",
"87650",
"8765",
"876.5",
"87.65",
"8.765",
"0");
}
@Test
public void padding() {
assertFormatDescending(
"Padding",
"",
NumberFormatter.with().padding(Padder.none()),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescending(
"Padding",
"",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
"**87,650",
"***8,765",
"***876.5",
"***87.65",
"***8.765",
"**0.8765",
"*0.08765",
"0.008765",
"*******0");
assertFormatDescending(
"Padding with code points",
"",
NumberFormatter.with().padding(Padder.codePoints(0x101E4, 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
"𐇤𐇤87,650",
"𐇤𐇤𐇤8,765",
"𐇤𐇤𐇤876.5",
"𐇤𐇤𐇤87.65",
"𐇤𐇤𐇤8.765",
"𐇤𐇤0.8765",
"𐇤0.08765",
"0.008765",
"𐇤𐇤𐇤𐇤𐇤𐇤𐇤0");
assertFormatDescending(
"Padding with wide digits",
"symbols=ns:mathsanb",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX))
.symbols(NumberingSystem.getInstanceByName("mathsanb")),
ULocale.ENGLISH,
"**𝟴𝟳,𝟲𝟱𝟬",
"***𝟴,𝟳𝟲𝟱",
"***𝟴𝟳𝟲.𝟱",
"***𝟴𝟳.𝟲𝟱",
"***𝟴.𝟳𝟲𝟱",
"**𝟬.𝟴𝟳𝟲𝟱",
"*𝟬.𝟬𝟴𝟳𝟲𝟱",
"𝟬.𝟬𝟬𝟴𝟳𝟲𝟱",
"*******𝟬");
assertFormatDescending(
"Padding with currency spacing",
"$GBP unit-width=ISO_CODE",
NumberFormatter.with().padding(Padder.codePoints('*', 10, PadPosition.AFTER_PREFIX)).unit(GBP)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"GBP 87,650.00",
"GBP 8,765.00",
"GBP*876.50",
"GBP**87.65",
"GBP***8.76",
"GBP***0.88",
"GBP***0.09",
"GBP***0.01",
"GBP***0.00");
assertFormatSingle(
"Pad Before Prefix",
"",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_PREFIX)),
ULocale.ENGLISH,
-88.88,
"**-88.88");
assertFormatSingle(
"Pad After Prefix",
"",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
-88.88,
"-**88.88");
assertFormatSingle(
"Pad Before Suffix",
"%",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
88.88,
"88.88**%");
assertFormatSingle(
"Pad After Suffix",
"%",
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
88.88,
"88.88%**");
assertFormatSingle(
"Currency Spacing with Zero Digit Padding Broken",
"$GBP unit-width=ISO_CODE",
NumberFormatter.with().padding(Padder.codePoints('0', 12, PadPosition.AFTER_PREFIX)).unit(GBP)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
514.23,
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
}
@Test
public void integerWidth() {
assertFormatDescending(
"Integer Width Default",
"integer-width=1-",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1)),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescending(
"Integer Width Zero Fill 0",
"integer-width=0-",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(0)),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
".8765",
".08765",
".008765",
""); // TODO: Avoid the empty string here?
assertFormatDescending(
"Integer Width Zero Fill 3",
"integer-width=3-",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(3)),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"087.65",
"008.765",
"000.8765",
"000.08765",
"000.008765",
"000");
assertFormatDescending(
"Integer Width Max 3",
"integer-width=1-3",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1).truncateAt(3)),
ULocale.ENGLISH,
"650",
"765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescending(
"Integer Width Fixed 2",
"integer-width=2",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
ULocale.ENGLISH,
"50",
"65",
"76.5",
"87.65",
"08.765",
"00.8765",
"00.08765",
"00.008765",
"00");
}
@Test
public void symbols() {
assertFormatDescending(
"French Symbols with Japanese Data 1",
"symbols=loc:fr",
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)),
ULocale.JAPAN,
"87 650",
"8 765",
"876,5",
"87,65",
"8,765",
"0,8765",
"0,08765",
"0,008765",
"0");
assertFormatSingle(
"French Symbols with Japanese Data 2",
"C symbols=loc:fr",
NumberFormatter.with().notation(Notation.compactShort())
.symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)),
ULocale.JAPAN,
12345,
"1,2\u4E07");
assertFormatDescending(
"Latin Numbering System with Arabic Data",
"$USD symbols=ns:latn",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar"),
"US$ 87,650.00",
"US$ 8,765.00",
"US$ 876.50",
"US$ 87.65",
"US$ 8.76",
"US$ 0.88",
"US$ 0.09",
"US$ 0.01",
"US$ 0.00");
assertFormatDescending(
"Math Numbering System with French Data",
"symbols=ns:mathsanb",
NumberFormatter.with().symbols(NumberingSystem.getInstanceByName("mathsanb")),
ULocale.FRENCH,
"𝟴𝟳 𝟲𝟱𝟬",
"𝟴 𝟳𝟲𝟱",
"𝟴𝟳𝟲,𝟱",
"𝟴𝟳,𝟲𝟱",
"𝟴,𝟳𝟲𝟱",
"𝟬,𝟴𝟳𝟲𝟱",
"𝟬,𝟬𝟴𝟳𝟲𝟱",
"𝟬,𝟬𝟬𝟴𝟳𝟲𝟱",
"𝟬");
assertFormatSingle(
"Swiss Symbols (used in documentation)",
"symbols=loc:de_CH",
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("de-CH"))),
ULocale.ENGLISH,
12345.67,
"12’345.67");
assertFormatSingle(
"Myanmar Symbols (used in documentation)",
"symbols=loc:my_MY",
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("my_MY"))),
ULocale.ENGLISH,
12345.67,
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
// NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
assertFormatSingle(
"Currency symbol should precede number in ar with NS latn",
"",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar"),
12345.67,
"US$ 12,345.67");
assertFormatSingle(
"Currency symbol should precede number in ar@numbers=latn",
"",
NumberFormatter.with().unit(USD),
new ULocale("ar@numbers=latn"),
12345.67,
"US$ 12,345.67");
assertFormatSingle(
"Currency symbol should follow number in ar-EG with NS arab",
"",
NumberFormatter.with().unit(USD),
new ULocale("ar-EG"),
12345.67,
"١٢٬٣٤٥٫٦٧ US$");
assertFormatSingle(
"Currency symbol should follow number in ar@numbers=arab",
"",
NumberFormatter.with().unit(USD),
new ULocale("ar@numbers=arab"),
12345.67,
"١٢٬٣٤٥٫٦٧ US$");
assertFormatSingle(
"NumberingSystem in API should win over @numbers keyword",
"",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar@numbers=arab"),
12345.67,
"US$ 12,345.67");
assertEquals("NumberingSystem in API should win over @numbers keyword in reverse order",
"US$ 12,345.67",
NumberFormatter.withLocale(new ULocale("ar@numbers=arab"))
.symbols(NumberingSystem.LATIN)
.unit(USD)
.format(12345.67)
.toString());
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(new ULocale("de-CH"));
UnlocalizedNumberFormatter f = NumberFormatter.with().symbols(symbols);
symbols.setGroupingSeparatorString("!");
assertFormatSingle(
"Symbols object should be copied",
"symbols=loc:de_CH",
f,
ULocale.ENGLISH,
12345.67,
"12’345.67");
assertFormatSingle(
"The last symbols setter wins",
"symbols=ns:latn",
NumberFormatter.with().symbols(symbols).symbols(NumberingSystem.LATIN),
ULocale.ENGLISH,
12345.67,
"12,345.67");
assertFormatSingle(
"The last symbols setter wins",
"symbols=loc:de_CH",
NumberFormatter.with().symbols(NumberingSystem.LATIN).symbols(symbols),
ULocale.ENGLISH,
12345.67,
"12!345.67");
}
@Test
@Ignore("This feature is not currently available.")
public void symbolsOverride() {
DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(ULocale.ENGLISH);
dfs.setCurrencySymbol("@");
dfs.setInternationalCurrencySymbol("foo");
assertFormatSingle(
"Custom Short Currency Symbol",
"$XXX",
NumberFormatter.with().unit(Currency.getInstance("XXX")).symbols(dfs),
ULocale.ENGLISH,
12.3,
"@ 12.30");
}
@Test
public void sign() {
assertFormatSingle(
"Sign Auto Positive",
"sign=AUTO",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
444444,
"444,444");
assertFormatSingle(
"Sign Auto Negative",
"sign=AUTO",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
-444444,
"-444,444");
assertFormatSingle(
"Sign Auto Zero",
"",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
0,
"0");
assertFormatSingle(
"Sign Always Positive",
"sign=ALWAYS",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
444444,
"+444,444");
assertFormatSingle(
"Sign Always Negative",
"sign=ALWAYS",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
-444444,
"-444,444");
assertFormatSingle(
"Sign Always Zero",
"",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
0,
"+0");
assertFormatSingle(
"Sign Never Positive",
"sign=NEVER",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
444444,
"444,444");
assertFormatSingle(
"Sign Never Negative",
"sign=NEVER",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
-444444,
"444,444");
assertFormatSingle(
"Sign Never Zero",
"",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
0,
"0");
assertFormatSingle(
"Sign Accounting Positive",
"$USD sign=ACCOUNTING",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
444444,
"$444,444.00");
assertFormatSingle(
"Sign Accounting Negative",
"$USD sign=ACCOUNTING",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
-444444,
"($444,444.00)");
assertFormatSingle(
"Sign Accounting Zero",
"",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
0,
"$0.00");
assertFormatSingle(
"Sign Accounting-Always Positive",
"$USD sign=ACCOUNTING_ALWAYS",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
444444,
"+$444,444.00");
assertFormatSingle(
"Sign Accounting-Always Negative",
"$USD sign=ACCOUNTING_ALWAYS",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
-444444,
"($444,444.00)");
assertFormatSingle(
"Sign Accounting-Always Zero",
"",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
0,
"+$0.00");
assertFormatSingle(
"Sign Except-Zero Positive",
"",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
444444,
"+444,444");
assertFormatSingle(
"Sign Except-Zero Negative",
"",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
-444444,
"-444,444");
assertFormatSingle(
"Sign Except-Zero Zero",
"",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
0,
"0");
assertFormatSingle(
"Sign Accounting-Except-Zero Positive",
"$USD sign=ACCOUNTING_ALWAYS",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
444444,
"+$444,444.00");
assertFormatSingle(
"Sign Accounting-Except-Zero Negative",
"$USD sign=ACCOUNTING_ALWAYS",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
-444444,
"($444,444.00)");
assertFormatSingle(
"Sign Accounting-Except-Zero Zero",
"",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
0,
"$0.00");
assertFormatSingle(
"Sign Accounting Negative Hidden",
"$USD unit-width=HIDDEN sign=ACCOUNTING",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.HIDDEN),
ULocale.ENGLISH,
-444444,
"(444,444.00)");
}
@Test
public void decimal() {
assertFormatDescending(
"Decimal Default",
"decimal=AUTO",
NumberFormatter.with().decimal(DecimalSeparatorDisplay.AUTO),
ULocale.ENGLISH,
"87,650",
"8,765",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0");
assertFormatDescending(
"Decimal Always Shown",
"decimal=ALWAYS",
NumberFormatter.with().decimal(DecimalSeparatorDisplay.ALWAYS),
ULocale.ENGLISH,
"87,650.",
"8,765.",
"876.5",
"87.65",
"8.765",
"0.8765",
"0.08765",
"0.008765",
"0.");
}
@Test
public void locale() {
// Coverage for the locale setters.
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.with().locale(Locale.ENGLISH));
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.withLocale(ULocale.ENGLISH));
assertEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.withLocale(Locale.ENGLISH));
assertNotEquals(NumberFormatter.with().locale(ULocale.ENGLISH), NumberFormatter.with().locale(Locale.FRENCH));
}
@Test
public void plurals() {
// TODO: Expand this test.
assertFormatSingle(
"Plural 1",
"$USD F0 unit-width=FULL_NAME",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).rounding(Rounder.fixedFraction(0)),
ULocale.ENGLISH,
1,
"1 US dollar");
assertFormatSingle(
"Plural 1.00",
"$USD F2 unit-width=FULL_NAME",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).rounding(Rounder.fixedFraction(2)),
ULocale.ENGLISH,
1,
"1.00 US dollars");
}
@Test
public void validRanges() throws NoSuchMethodException, IllegalAccessException {
Method[] methodsWithOneArgument = new Method[] { Rounder.class.getDeclaredMethod("fixedFraction", Integer.TYPE),
Rounder.class.getDeclaredMethod("minFraction", Integer.TYPE),
Rounder.class.getDeclaredMethod("maxFraction", Integer.TYPE),
Rounder.class.getDeclaredMethod("fixedDigits", Integer.TYPE),
Rounder.class.getDeclaredMethod("minDigits", Integer.TYPE),
Rounder.class.getDeclaredMethod("maxDigits", Integer.TYPE),
FractionRounder.class.getDeclaredMethod("withMinDigits", Integer.TYPE),
FractionRounder.class.getDeclaredMethod("withMaxDigits", Integer.TYPE),
ScientificNotation.class.getDeclaredMethod("withMinExponentDigits", Integer.TYPE),
IntegerWidth.class.getDeclaredMethod("zeroFillTo", Integer.TYPE),
IntegerWidth.class.getDeclaredMethod("truncateAt", Integer.TYPE), };
Method[] methodsWithTwoArguments = new Method[] {
Rounder.class.getDeclaredMethod("minMaxFraction", Integer.TYPE, Integer.TYPE),
Rounder.class.getDeclaredMethod("minMaxDigits", Integer.TYPE, Integer.TYPE), };
final int EXPECTED_MAX_INT_FRAC_SIG = 999;
final String expectedSubstring0 = "between 0 and 999 (inclusive)";
final String expectedSubstring1 = "between 1 and 999 (inclusive)";
final String expectedSubstringN1 = "between -1 and 999 (inclusive)";
// We require that the upper bounds all be 999 inclusive.
// The lower bound may be either -1, 0, or 1.
Set<String> methodsWithLowerBound1 = new HashSet();
methodsWithLowerBound1.add("fixedDigits");
methodsWithLowerBound1.add("minDigits");
methodsWithLowerBound1.add("maxDigits");
methodsWithLowerBound1.add("minMaxDigits");
methodsWithLowerBound1.add("withMinDigits");
methodsWithLowerBound1.add("withMaxDigits");
methodsWithLowerBound1.add("withMinExponentDigits");
// Methods with lower bound 0:
// fixedFraction
// minFraction
// maxFraction
// minMaxFraction
// zeroFillTo
Set<String> methodsWithLowerBoundN1 = new HashSet();
methodsWithLowerBoundN1.add("truncateAt");
// Some of the methods require an object to be called upon.
Map<String, Object> targets = new HashMap<String, Object>();
targets.put("withMinDigits", Rounder.integer());
targets.put("withMaxDigits", Rounder.integer());
targets.put("withMinExponentDigits", Notation.scientific());
targets.put("truncateAt", IntegerWidth.zeroFillTo(0));
for (int argument = -2; argument <= EXPECTED_MAX_INT_FRAC_SIG + 2; argument++) {
for (Method method : methodsWithOneArgument) {
String message = "i = " + argument + "; method = " + method.getName();
int lowerBound = methodsWithLowerBound1.contains(method.getName()) ? 1
: methodsWithLowerBoundN1.contains(method.getName()) ? -1 : 0;
String expectedSubstring = lowerBound == 0 ? expectedSubstring0
: lowerBound == 1 ? expectedSubstring1 : expectedSubstringN1;
Object target = targets.get(method.getName());
try {
method.invoke(target, argument);
assertTrue(message, argument >= lowerBound && argument <= EXPECTED_MAX_INT_FRAC_SIG);
} catch (InvocationTargetException e) {
assertTrue(message, argument < lowerBound || argument > EXPECTED_MAX_INT_FRAC_SIG);
// Ensure the exception message contains the expected substring
String actualMessage = e.getCause().getMessage();
assertNotEquals(message + ": " + actualMessage, -1, actualMessage.indexOf(expectedSubstring));
}
}
for (Method method : methodsWithTwoArguments) {
String message = "i = " + argument + "; method = " + method.getName();
int lowerBound = methodsWithLowerBound1.contains(method.getName()) ? 1
: methodsWithLowerBoundN1.contains(method.getName()) ? -1 : 0;
String expectedSubstring = lowerBound == 0 ? expectedSubstring0 : expectedSubstring1;
Object target = targets.get(method.getName());
// Check range on the first argument
try {
// Pass EXPECTED_MAX_INT_FRAC_SIG as the second argument so arg1 <= arg2 in expected cases
method.invoke(target, argument, EXPECTED_MAX_INT_FRAC_SIG);
assertTrue(message, argument >= lowerBound && argument <= EXPECTED_MAX_INT_FRAC_SIG);
} catch (InvocationTargetException e) {
assertTrue(message, argument < lowerBound || argument > EXPECTED_MAX_INT_FRAC_SIG);
// Ensure the exception message contains the expected substring
String actualMessage = e.getCause().getMessage();
assertNotEquals(message + ": " + actualMessage, -1, actualMessage.indexOf(expectedSubstring));
}
// Check range on the second argument
try {
// Pass lowerBound as the first argument so arg1 <= arg2 in expected cases
method.invoke(target, lowerBound, argument);
assertTrue(message, argument >= lowerBound && argument <= EXPECTED_MAX_INT_FRAC_SIG);
} catch (InvocationTargetException e) {
assertTrue(message, argument < lowerBound || argument > EXPECTED_MAX_INT_FRAC_SIG);
// Ensure the exception message contains the expected substring
String actualMessage = e.getCause().getMessage();
assertNotEquals(message + ": " + actualMessage, -1, actualMessage.indexOf(expectedSubstring));
}
// Check that first argument must be less than or equal to second argument
try {
method.invoke(target, argument, argument - 1);
org.junit.Assert.fail();
} catch (InvocationTargetException e) {
// Pass
}
}
}
// Check first argument less than or equal to second argument on IntegerWidth
try {
IntegerWidth.zeroFillTo(4).truncateAt(2);
org.junit.Assert.fail();
} catch (IllegalArgumentException e) {
// Pass
}
}
private static void assertFormatDescending(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
String... expected) {
final double[] inputs = new double[] { 87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0 };
assertFormatDescending(message, skeleton, f, locale, inputs, expected);
}
private static void assertFormatDescendingBig(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
String... expected) {
final double[] inputs = new double[] { 87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0 };
assertFormatDescending(message, skeleton, f, locale, inputs, expected);
}
private static void assertFormatDescending(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
double[] inputs,
String... expected) {
assert expected.length == 9;
// TODO: Add a check for skeleton.
// assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
for (int i = 0; i < 9; i++) {
double d = inputs[i];
String actual1 = l1.format(d).toString();
assertEquals(message + ": Unsafe Path: " + d, expected[i], actual1);
String actual2 = l2.format(d).toString();
assertEquals(message + ": Safe Path: " + d, expected[i], actual2);
}
}
private static void assertFormatSingle(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
Number input,
String expected) {
// TODO: Add a check for skeleton.
// assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
String actual1 = l1.format(input).toString();
assertEquals(message + ": Unsafe Path: " + input, expected, actual1);
String actual2 = l2.format(input).toString();
assertEquals(message + ": Safe Path: " + input, expected, actual2);
}
private static void assertFormatSingleMeasure(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
Measure input,
String expected) {
// TODO: Add a check for skeleton.
// assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
String actual1 = l1.format(input).toString();
assertEquals(message + ": Unsafe Path: " + input, expected, actual1);
String actual2 = l2.format(input).toString();
assertEquals(message + ": Safe Path: " + input, expected, actual2);
}
}