1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
9 #include "unicode/dcfmtsym.h"
10 #include "numbertest.h"
11 #include "number_utils.h"
13 using namespace icu::number::impl
;
15 class DefaultSymbolProvider
: public SymbolProvider
{
16 DecimalFormatSymbols fSymbols
;
19 DefaultSymbolProvider(UErrorCode
&status
) : fSymbols(Locale("ar_SA"), status
) {}
21 UnicodeString
getSymbol(AffixPatternType type
) const U_OVERRIDE
{
26 return fSymbols
.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol
);
28 return fSymbols
.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol
);
30 return fSymbols
.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol
);
31 case TYPE_CURRENCY_SINGLE
:
33 case TYPE_CURRENCY_DOUBLE
:
35 case TYPE_CURRENCY_TRIPLE
:
37 case TYPE_CURRENCY_QUAD
:
39 case TYPE_CURRENCY_QUINT
:
40 // TODO: Add support for narrow currency symbols here.
42 case TYPE_CURRENCY_OVERFLOW
:
50 void AffixUtilsTest::runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *) {
52 logln("TestSuite AffixUtilsTest: ");
55 TESTCASE_AUTO(testEscape
);
56 TESTCASE_AUTO(testUnescape
);
57 TESTCASE_AUTO(testContainsReplaceType
);
58 TESTCASE_AUTO(testInvalid
);
59 TESTCASE_AUTO(testUnescapeWithSymbolProvider
);
63 void AffixUtilsTest::testEscape() {
64 static const char16_t *cases
[][2] = {{u
"", u
""},
74 {u
"a-'-", u
"a'-''-'"}};
76 for (auto &cas
: cases
) {
77 UnicodeString
input(cas
[0]);
78 UnicodeString
expected(cas
[1]);
79 UnicodeString result
= AffixUtils::escape(input
);
80 assertEquals(input
, expected
, result
);
84 void AffixUtilsTest::testUnescape() {
85 static struct TestCase
{
86 const char16_t *input
;
88 int32_t expectedLength
;
89 const char16_t *output
;
90 } cases
[] = {{u
"", false, 0, u
""},
91 {u
"abc", false, 3, u
"abc"},
92 {u
"-", false, 1, u
"−"},
93 {u
"-!", false, 2, u
"−!"},
94 {u
"+", false, 1, u
"\u061C+"},
95 {u
"+!", false, 2, u
"\u061C+!"},
96 {u
"‰", false, 1, u
"؉"},
97 {u
"‰!", false, 2, u
"؉!"},
98 {u
"-x", false, 2, u
"−x"},
99 {u
"'-'x", false, 2, u
"-x"},
100 {u
"'--''-'-x", false, 6, u
"--'-−x"},
101 {u
"''", false, 1, u
"'"},
102 {u
"''''", false, 2, u
"''"},
103 {u
"''''''", false, 3, u
"'''"},
104 {u
"''x''", false, 3, u
"'x'"},
105 {u
"¤", true, 1, u
"$"},
106 {u
"¤¤", true, 2, u
"XXX"},
107 {u
"¤¤¤", true, 3, u
"long name"},
108 {u
"¤¤¤¤", true, 4, u
"\uFFFD"},
109 {u
"¤¤¤¤¤", true, 5, u
"\uFFFD"},
110 {u
"¤¤¤¤¤¤", true, 6, u
"\uFFFD"},
111 {u
"¤¤¤a¤¤¤¤", true, 8, u
"long namea\uFFFD"},
112 {u
"a¤¤¤¤b¤¤¤¤¤c", true, 12, u
"a\uFFFDb\uFFFDc"},
113 {u
"¤!", true, 2, u
"$!"},
114 {u
"¤¤!", true, 3, u
"XXX!"},
115 {u
"¤¤¤!", true, 4, u
"long name!"},
116 {u
"-¤¤", true, 3, u
"−XXX"},
117 {u
"¤¤-", true, 3, u
"XXX−"},
118 {u
"'¤'", false, 1, u
"¤"},
119 {u
"%", false, 1, u
"٪\u061C"},
120 {u
"'%'", false, 1, u
"%"},
121 {u
"¤'-'%", true, 3, u
"$-٪\u061C"},
122 {u
"#0#@#*#;#", false, 9, u
"#0#@#*#;#"}};
124 UErrorCode status
= U_ZERO_ERROR
;
125 DefaultSymbolProvider
defaultProvider(status
);
126 assertSuccess("Constructing DefaultSymbolProvider", status
);
128 for (TestCase cas
: cases
) {
129 UnicodeString
input(cas
.input
);
130 UnicodeString
output(cas
.output
);
132 assertEquals(input
, cas
.currency
, AffixUtils::hasCurrencySymbols(input
, status
));
133 assertSuccess("Spot 1", status
);
134 assertEquals(input
, cas
.expectedLength
, AffixUtils::estimateLength(input
, status
));
135 assertSuccess("Spot 2", status
);
137 UnicodeString actual
= unescapeWithDefaults(defaultProvider
, input
, status
);
138 assertSuccess("Spot 3", status
);
139 assertEquals(input
, output
, actual
);
141 int32_t ulength
= AffixUtils::unescapedCodePointCount(input
, defaultProvider
, status
);
142 assertSuccess("Spot 4", status
);
143 assertEquals(input
, output
.countChar32(), ulength
);
147 void AffixUtilsTest::testContainsReplaceType() {
148 static struct TestCase
{
149 const char16_t *input
;
151 const char16_t *output
;
152 } cases
[] = {{u
"", false, u
""},
154 {u
"-a", true, u
"+a"},
155 {u
"a-", true, u
"a+"},
156 {u
"a-b", true, u
"a+b"},
157 {u
"--", true, u
"++"},
158 {u
"x", false, u
"x"}};
160 UErrorCode status
= U_ZERO_ERROR
;
161 for (TestCase cas
: cases
) {
162 UnicodeString
input(cas
.input
);
163 bool hasMinusSign
= cas
.hasMinusSign
;
164 UnicodeString
output(cas
.output
);
167 input
, hasMinusSign
, AffixUtils::containsType(input
, TYPE_MINUS_SIGN
, status
));
168 assertSuccess("Spot 1", status
);
170 input
, output
, AffixUtils::replaceType(input
, TYPE_MINUS_SIGN
, u
'+', status
));
171 assertSuccess("Spot 2", status
);
175 void AffixUtilsTest::testInvalid() {
176 static const char16_t *invalidExamples
[] = {
177 u
"'", u
"x'", u
"'x", u
"'x''", u
"''x'"};
179 UErrorCode status
= U_ZERO_ERROR
;
180 DefaultSymbolProvider
defaultProvider(status
);
181 assertSuccess("Constructing DefaultSymbolProvider", status
);
183 for (const char16_t *strPtr
: invalidExamples
) {
184 UnicodeString
str(strPtr
);
186 status
= U_ZERO_ERROR
;
187 AffixUtils::hasCurrencySymbols(str
, status
);
188 assertEquals("Should set error code spot 1", status
, U_ILLEGAL_ARGUMENT_ERROR
);
190 status
= U_ZERO_ERROR
;
191 AffixUtils::estimateLength(str
, status
);
192 assertEquals("Should set error code spot 2", status
, U_ILLEGAL_ARGUMENT_ERROR
);
194 status
= U_ZERO_ERROR
;
195 unescapeWithDefaults(defaultProvider
, str
, status
);
196 assertEquals("Should set error code spot 3", status
, U_ILLEGAL_ARGUMENT_ERROR
);
200 class NumericSymbolProvider
: public SymbolProvider
{
202 virtual UnicodeString
getSymbol(AffixPatternType type
) const {
203 return Int64ToUnicodeString(type
< 0 ? -type
: type
);
207 void AffixUtilsTest::testUnescapeWithSymbolProvider() {
208 static const char16_t* cases
[][2] = {
212 {u
"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u
"1 2 3 4 5 6 7 8 9"},
213 {u
"'¤¤¤¤¤¤'", u
"¤¤¤¤¤¤"},
214 {u
"¤¤¤¤¤¤", u
"\uFFFD"}
217 NumericSymbolProvider provider
;
219 UErrorCode status
= U_ZERO_ERROR
;
220 FormattedStringBuilder sb
;
221 for (auto& cas
: cases
) {
222 UnicodeString
input(cas
[0]);
223 UnicodeString
expected(cas
[1]);
225 AffixUtils::unescape(input
, sb
, 0, provider
, UNUM_FIELD_COUNT
, status
);
226 assertSuccess("Spot 1", status
);
227 assertEquals(input
, expected
, sb
.toUnicodeString());
228 assertEquals(input
, expected
, sb
.toTempUnicodeString());
231 // Test insertion position
233 sb
.append(u
"abcdefg", UNUM_FIELD_COUNT
, status
);
234 assertSuccess("Spot 2", status
);
235 AffixUtils::unescape(u
"-+%", sb
, 4, provider
, UNUM_FIELD_COUNT
, status
);
236 assertSuccess("Spot 3", status
);
237 assertEquals(u
"Symbol provider into middle", u
"abcd123efg", sb
.toUnicodeString());
240 UnicodeString
AffixUtilsTest::unescapeWithDefaults(const SymbolProvider
&defaultProvider
,
241 UnicodeString input
, UErrorCode
&status
) {
242 FormattedStringBuilder nsb
;
243 int32_t length
= AffixUtils::unescape(input
, nsb
, 0, defaultProvider
, UNUM_FIELD_COUNT
, status
);
244 assertEquals("Return value of unescape", nsb
.length(), length
);
245 return nsb
.toUnicodeString();
248 #endif /* #if !UCONFIG_NO_FORMATTING */