1// © 2017 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html 3 4#include "unicode/utypes.h" 5 6#if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT 7 8#include "putilimp.h" 9#include "unicode/dcfmtsym.h" 10#include "numbertest.h" 11#include "number_utils.h" 12 13using namespace icu::number::impl; 14 15class DefaultSymbolProvider : public SymbolProvider { 16 DecimalFormatSymbols fSymbols; 17 18 public: 19 DefaultSymbolProvider(UErrorCode &status) : fSymbols(Locale("ar_SA"), status) {} 20 21 virtual UnicodeString getSymbol(AffixPatternType type) const U_OVERRIDE { 22 switch (type) { 23 case TYPE_MINUS_SIGN: 24 return u"−"; 25 case TYPE_PLUS_SIGN: 26 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPlusSignSymbol); 27 case TYPE_PERCENT: 28 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPercentSymbol); 29 case TYPE_PERMILLE: 30 return fSymbols.getConstSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol); 31 case TYPE_CURRENCY_SINGLE: 32 return u"$"; 33 case TYPE_CURRENCY_DOUBLE: 34 return u"XXX"; 35 case TYPE_CURRENCY_TRIPLE: 36 return u"long name"; 37 case TYPE_CURRENCY_QUAD: 38 return u"\uFFFD"; 39 case TYPE_CURRENCY_QUINT: 40 // TODO: Add support for narrow currency symbols here. 41 return u"\uFFFD"; 42 case TYPE_CURRENCY_OVERFLOW: 43 return u"\uFFFD"; 44 default: 45 U_ASSERT(false); 46 return {}; // silence compiler warnings 47 } 48 } 49}; 50 51void AffixUtilsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { 52 if (exec) { 53 logln("TestSuite AffixUtilsTest: "); 54 } 55 TESTCASE_AUTO_BEGIN; 56 TESTCASE_AUTO(testEscape); 57 TESTCASE_AUTO(testUnescape); 58 TESTCASE_AUTO(testContainsReplaceType); 59 TESTCASE_AUTO(testInvalid); 60 TESTCASE_AUTO(testUnescapeWithSymbolProvider); 61 TESTCASE_AUTO_END; 62} 63 64void AffixUtilsTest::testEscape() { 65 static const char16_t *cases[][2] = {{u"", u""}, 66 {u"abc", u"abc"}, 67 {u"-", u"'-'"}, 68 {u"-!", u"'-'!"}, 69 {u"−", u"−"}, 70 {u"---", u"'---'"}, 71 {u"-%-", u"'-%-'"}, 72 {u"'", u"''"}, 73 {u"-'", u"'-'''"}, 74 {u"-'-", u"'-''-'"}, 75 {u"a-'-", u"a'-''-'"}}; 76 77 for (auto &cas : cases) { 78 UnicodeString input(cas[0]); 79 UnicodeString expected(cas[1]); 80 UnicodeString result = AffixUtils::escape(UnicodeStringCharSequence(input)); 81 assertEquals(input, expected, result); 82 } 83} 84 85void AffixUtilsTest::testUnescape() { 86 static struct TestCase { 87 const char16_t *input; 88 bool currency; 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#@#*#;#"}}; 124 125 UErrorCode status = U_ZERO_ERROR; 126 DefaultSymbolProvider defaultProvider(status); 127 assertSuccess("Constructing DefaultSymbolProvider", status); 128 129 for (TestCase cas : cases) { 130 UnicodeString input(cas.input); 131 UnicodeString output(cas.output); 132 133 assertEquals(input, cas.currency, AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(input), status)); 134 assertSuccess("Spot 1", status); 135 assertEquals(input, cas.expectedLength, AffixUtils::estimateLength(UnicodeStringCharSequence(input), status)); 136 assertSuccess("Spot 2", status); 137 138 UnicodeString actual = unescapeWithDefaults(defaultProvider, input, status); 139 assertSuccess("Spot 3", status); 140 assertEquals(input, output, actual); 141 142 int32_t ulength = AffixUtils::unescapedCodePointCount(UnicodeStringCharSequence(input), defaultProvider, status); 143 assertSuccess("Spot 4", status); 144 assertEquals(input, output.countChar32(), ulength); 145 } 146} 147 148void AffixUtilsTest::testContainsReplaceType() { 149 static struct TestCase { 150 const char16_t *input; 151 bool hasMinusSign; 152 const char16_t *output; 153 } cases[] = {{u"", false, u""}, 154 {u"-", true, 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"}}; 160 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); 166 167 assertEquals( 168 input, hasMinusSign, AffixUtils::containsType(UnicodeStringCharSequence(input), TYPE_MINUS_SIGN, status)); 169 assertSuccess("Spot 1", status); 170 assertEquals( 171 input, output, AffixUtils::replaceType(UnicodeStringCharSequence(input), TYPE_MINUS_SIGN, u'+', status)); 172 assertSuccess("Spot 2", status); 173 } 174} 175 176void AffixUtilsTest::testInvalid() { 177 static const char16_t *invalidExamples[] = { 178 u"'", u"x'", u"'x", u"'x''", u"''x'"}; 179 180 UErrorCode status = U_ZERO_ERROR; 181 DefaultSymbolProvider defaultProvider(status); 182 assertSuccess("Constructing DefaultSymbolProvider", status); 183 184 for (const char16_t *strPtr : invalidExamples) { 185 UnicodeString str(strPtr); 186 187 status = U_ZERO_ERROR; 188 AffixUtils::hasCurrencySymbols(UnicodeStringCharSequence(str), status); 189 assertEquals("Should set error code spot 1", status, U_ILLEGAL_ARGUMENT_ERROR); 190 191 status = U_ZERO_ERROR; 192 AffixUtils::estimateLength(UnicodeStringCharSequence(str), status); 193 assertEquals("Should set error code spot 2", status, U_ILLEGAL_ARGUMENT_ERROR); 194 195 status = U_ZERO_ERROR; 196 unescapeWithDefaults(defaultProvider, str, status); 197 assertEquals("Should set error code spot 3", status, U_ILLEGAL_ARGUMENT_ERROR); 198 } 199} 200 201class NumericSymbolProvider : public SymbolProvider { 202 public: 203 virtual UnicodeString getSymbol(AffixPatternType type) const { 204 return Int64ToUnicodeString(type < 0 ? -type : type); 205 } 206}; 207 208void AffixUtilsTest::testUnescapeWithSymbolProvider() { 209 static const char16_t* cases[][2] = { 210 {u"", u""}, 211 {u"-", u"1"}, 212 {u"'-'", u"-"}, 213 {u"- + % ‰ ¤ ¤¤ ¤¤¤ ¤¤¤¤ ¤¤¤¤¤", u"1 2 3 4 5 6 7 8 9"}, 214 {u"'¤¤¤¤¤¤'", u"¤¤¤¤¤¤"}, 215 {u"¤¤¤¤¤¤", u"\uFFFD"} 216 }; 217 218 NumericSymbolProvider provider; 219 220 UErrorCode status = U_ZERO_ERROR; 221 NumberStringBuilder sb; 222 for (auto cas : cases) { 223 UnicodeString input(cas[0]); 224 UnicodeString expected(cas[1]); 225 sb.clear(); 226 AffixUtils::unescape(UnicodeStringCharSequence(input), sb, 0, provider, status); 227 assertSuccess("Spot 1", status); 228 assertEquals(input, expected, sb.toUnicodeString()); 229 } 230 231 // Test insertion position 232 sb.clear(); 233 sb.append(u"abcdefg", UNUM_FIELD_COUNT, status); 234 assertSuccess("Spot 2", status); 235 AffixUtils::unescape(UnicodeStringCharSequence(UnicodeString(u"-+%")), sb, 4, provider, status); 236 assertSuccess("Spot 3", status); 237 assertEquals(u"Symbol provider into middle", u"abcd123efg", sb.toUnicodeString()); 238} 239 240UnicodeString AffixUtilsTest::unescapeWithDefaults(const SymbolProvider &defaultProvider, 241 UnicodeString input, UErrorCode &status) { 242 NumberStringBuilder nsb; 243 int32_t length = AffixUtils::unescape(UnicodeStringCharSequence(input), nsb, 0, defaultProvider, status); 244 assertEquals("Return value of unescape", nsb.length(), length); 245 return nsb.toUnicodeString(); 246} 247 248#endif /* #if !UCONFIG_NO_FORMATTING */ 249