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
:
46 return {}; // silence compiler warnings
51 void AffixUtilsTest::runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *) {
53 logln("TestSuite AffixUtilsTest: ");
56 TESTCASE_AUTO(testEscape
);
57 TESTCASE_AUTO(testUnescape
);
58 TESTCASE_AUTO(testContainsReplaceType
);
59 TESTCASE_AUTO(testInvalid
);
60 TESTCASE_AUTO(testUnescapeWithSymbolProvider
);
64 void AffixUtilsTest::testEscape() {
65 static const char16_t *cases
[][2] = {{u
"", u
""},
75 {u
"a-'-", u
"a'-''-'"}};
77 for (auto &cas
: cases
) {
78 UnicodeString
input(cas
[0]);
79 UnicodeString
expected(cas
[1]);
80 UnicodeString result
= AffixUtils::escape(input
);
81 assertEquals(input
, expected
, result
);
85 void AffixUtilsTest::testUnescape() {
86 static struct TestCase
{
87 const char16_t *input
;
89 int32_t expectedLength
;
90 const char16_t *output
;
91 } cases
[] = {{u
"", false, 0, u
""},
92 {u
"abc", false, 3, u
"abc"},
93 {u
"-", false, 1, u
"−"},
94 {u
"-!", false, 2, u
"−!"},
95 {u
"+", false, 1, u
"\u061C+"},
96 {u
"+!", false, 2, u
"\u061C+!"},
97 {u
"‰", false, 1, u
"؉"},
98 {u
"‰!", false, 2, u
"؉!"},
99 {u
"-x", false, 2, u
"−x"},
100 {u
"'-'x", false, 2, u
"-x"},
101 {u
"'--''-'-x", false, 6, u
"--'-−x"},
102 {u
"''", false, 1, u
"'"},
103 {u
"''''", false, 2, u
"''"},
104 {u
"''''''", false, 3, u
"'''"},
105 {u
"''x''", false, 3, u
"'x'"},
106 {u
"¤", true, 1, u
"$"},
107 {u
"¤¤", true, 2, u
"XXX"},
108 {u
"¤¤¤", true, 3, u
"long name"},
109 {u
"¤¤¤¤", true, 4, u
"\uFFFD"},
110 {u
"¤¤¤¤¤", true, 5, u
"\uFFFD"},
111 {u
"¤¤¤¤¤¤", true, 6, u
"\uFFFD"},
112 {u
"¤¤¤a¤¤¤¤", true, 8, u
"long namea\uFFFD"},
113 {u
"a¤¤¤¤b¤¤¤¤¤c", true, 12, u
"a\uFFFDb\uFFFDc"},
114 {u
"¤!", true, 2, u
"$!"},
115 {u
"¤¤!", true, 3, u
"XXX!"},
116 {u
"¤¤¤!", true, 4, u
"long name!"},
117 {u
"-¤¤", true, 3, u
"−XXX"},
118 {u
"¤¤-", true, 3, u
"XXX−"},
119 {u
"'¤'", false, 1, u
"¤"},
120 {u
"%", false, 1, u
"٪\u061C"},
121 {u
"'%'", false, 1, u
"%"},
122 {u
"¤'-'%", true, 3, u
"$-٪\u061C"},
123 {u
"#0#@#*#;#", false, 9, u
"#0#@#*#;#"}};
125 UErrorCode status
= U_ZERO_ERROR
;
126 DefaultSymbolProvider
defaultProvider(status
);
127 assertSuccess("Constructing DefaultSymbolProvider", status
);
129 for (TestCase cas
: cases
) {
130 UnicodeString
input(cas
.input
);
131 UnicodeString
output(cas
.output
);
133 assertEquals(input
, cas
.currency
, AffixUtils::hasCurrencySymbols(input
, status
));
134 assertSuccess("Spot 1", status
);
135 assertEquals(input
, cas
.expectedLength
, AffixUtils::estimateLength(input
, status
));
136 assertSuccess("Spot 2", status
);
138 UnicodeString actual
= unescapeWithDefaults(defaultProvider
, input
, status
);
139 assertSuccess("Spot 3", status
);
140 assertEquals(input
, output
, actual
);
142 int32_t ulength
= AffixUtils::unescapedCodePointCount(input
, defaultProvider
, status
);
143 assertSuccess("Spot 4", status
);
144 assertEquals(input
, output
.countChar32(), ulength
);
148 void AffixUtilsTest::testContainsReplaceType() {
149 static struct TestCase
{
150 const char16_t *input
;
152 const char16_t *output
;
153 } cases
[] = {{u
"", false, u
""},
155 {u
"-a", true, u
"+a"},
156 {u
"a-", true, u
"a+"},
157 {u
"a-b", true, u
"a+b"},
158 {u
"--", true, u
"++"},
159 {u
"x", false, u
"x"}};
161 UErrorCode status
= U_ZERO_ERROR
;
162 for (TestCase cas
: cases
) {
163 UnicodeString
input(cas
.input
);
164 bool hasMinusSign
= cas
.hasMinusSign
;
165 UnicodeString
output(cas
.output
);
168 input
, hasMinusSign
, AffixUtils::containsType(input
, TYPE_MINUS_SIGN
, status
));
169 assertSuccess("Spot 1", status
);
171 input
, output
, AffixUtils::replaceType(input
, TYPE_MINUS_SIGN
, u
'+', status
));
172 assertSuccess("Spot 2", status
);
176 void AffixUtilsTest::testInvalid() {
177 static const char16_t *invalidExamples
[] = {
178 u
"'", u
"x'", u
"'x", u
"'x''", u
"''x'"};
180 UErrorCode status
= U_ZERO_ERROR
;
181 DefaultSymbolProvider
defaultProvider(status
);
182 assertSuccess("Constructing DefaultSymbolProvider", status
);
184 for (const char16_t *strPtr
: invalidExamples
) {
185 UnicodeString
str(strPtr
);
187 status
= U_ZERO_ERROR
;
188 AffixUtils::hasCurrencySymbols(str
, status
);
189 assertEquals("Should set error code spot 1", status
, U_ILLEGAL_ARGUMENT_ERROR
);
191 status
= U_ZERO_ERROR
;
192 AffixUtils::estimateLength(str
, status
);
193 assertEquals("Should set error code spot 2", status
, U_ILLEGAL_ARGUMENT_ERROR
);
195 status
= U_ZERO_ERROR
;
196 unescapeWithDefaults(defaultProvider
, str
, status
);
197 assertEquals("Should set error code spot 3", status
, U_ILLEGAL_ARGUMENT_ERROR
);
201 class NumericSymbolProvider
: public SymbolProvider
{
203 virtual UnicodeString
getSymbol(AffixPatternType type
) const {
204 return Int64ToUnicodeString(type
< 0 ? -type
: type
);
208 void AffixUtilsTest::testUnescapeWithSymbolProvider() {
209 static const char16_t* cases
[][2] = {
213 {u
"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u
"1 2 3 4 5 6 7 8 9"},
214 {u
"'¤¤¤¤¤¤'", u
"¤¤¤¤¤¤"},
215 {u
"¤¤¤¤¤¤", u
"\uFFFD"}
218 NumericSymbolProvider provider
;
220 UErrorCode status
= U_ZERO_ERROR
;
221 NumberStringBuilder sb
;
222 for (auto& cas
: cases
) {
223 UnicodeString
input(cas
[0]);
224 UnicodeString
expected(cas
[1]);
226 AffixUtils::unescape(input
, sb
, 0, provider
, status
);
227 assertSuccess("Spot 1", status
);
228 assertEquals(input
, expected
, sb
.toUnicodeString());
229 assertEquals(input
, expected
, sb
.toTempUnicodeString());
232 // Test insertion position
234 sb
.append(u
"abcdefg", UNUM_FIELD_COUNT
, status
);
235 assertSuccess("Spot 2", status
);
236 AffixUtils::unescape(u
"-+%", sb
, 4, provider
, status
);
237 assertSuccess("Spot 3", status
);
238 assertEquals(u
"Symbol provider into middle", u
"abcd123efg", sb
.toUnicodeString());
241 UnicodeString
AffixUtilsTest::unescapeWithDefaults(const SymbolProvider
&defaultProvider
,
242 UnicodeString input
, UErrorCode
&status
) {
243 NumberStringBuilder nsb
;
244 int32_t length
= AffixUtils::unescape(input
, nsb
, 0, defaultProvider
, status
);
245 assertEquals("Return value of unescape", nsb
.length(), length
);
246 return nsb
.toUnicodeString();
249 #endif /* #if !UCONFIG_NO_FORMATTING */