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 "intltest.h" 10#include "number_stringbuilder.h" 11#include "number_modifiers.h" 12#include "numbertest.h" 13 14void ModifiersTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { 15 if (exec) { 16 logln("TestSuite ModifiersTest: "); 17 } 18 TESTCASE_AUTO_BEGIN; 19 TESTCASE_AUTO(testConstantAffixModifier); 20 TESTCASE_AUTO(testConstantMultiFieldModifier); 21 TESTCASE_AUTO(testSimpleModifier); 22 TESTCASE_AUTO(testCurrencySpacingEnabledModifier); 23 TESTCASE_AUTO_END; 24} 25 26void ModifiersTest::testConstantAffixModifier() { 27 UErrorCode status = U_ZERO_ERROR; 28 ConstantAffixModifier mod0(u"", u"", UNUM_PERCENT_FIELD, true); 29 assertModifierEquals(mod0, 0, true, u"|", u"n", status); 30 assertSuccess("Spot 1", status); 31 32 ConstantAffixModifier mod1(u"a", u"b", UNUM_PERCENT_FIELD, true); 33 assertModifierEquals(mod1, 3, true, u"a|b", u"%%%n%", status); 34 assertSuccess("Spot 2", status); 35} 36 37void ModifiersTest::testConstantMultiFieldModifier() { 38 UErrorCode status = U_ZERO_ERROR; 39 NumberStringBuilder prefix; 40 NumberStringBuilder suffix; 41 ConstantMultiFieldModifier mod1(prefix, suffix, true); 42 assertModifierEquals(mod1, 0, true, u"|", u"n", status); 43 assertSuccess("Spot 1", status); 44 45 prefix.append(u"a", UNUM_PERCENT_FIELD, status); 46 suffix.append(u"b", UNUM_CURRENCY_FIELD, status); 47 ConstantMultiFieldModifier mod2(prefix, suffix, true); 48 assertModifierEquals(mod2, 3, true, u"a|b", u"%%%n$", status); 49 assertSuccess("Spot 2", status); 50 51 // Make sure the first modifier is still the same (that it stayed constant) 52 assertModifierEquals(mod1, 0, true, u"|", u"n", status); 53 assertSuccess("Spot 3", status); 54} 55 56void ModifiersTest::testSimpleModifier() { 57 static const int32_t NUM_CASES = 5; 58 static const int32_t NUM_OUTPUTS = 4; 59 static const char16_t *patterns[] = {u"{0}", u"X{0}Y", u"XX{0}YYY", u"{0}YY", u"XXXX{0}"}; 60 static const struct { 61 const char16_t *baseString; 62 int32_t leftIndex; 63 int32_t rightIndex; 64 } outputs[NUM_OUTPUTS] = {{u"", 0, 0}, {u"abcde", 0, 0}, {u"abcde", 4, 4}, {u"abcde", 3, 5}}; 65 static const int32_t prefixLens[] = {0, 1, 2, 0, 6}; 66 static const char16_t *expectedCharFields[][2] = {{u"|", u"n"}, 67 {u"X|Y", u"%n%"}, 68 {u"XX|YYY", u"%%n%%%"}, 69 {u"|YY", u"n%%"}, 70 {u"XXXX|", u"%%%%%%n"}}; 71 static const char16_t *expecteds[][NUM_CASES] = // force auto-format line break 72 {{ 73 u"", u"XY", u"XXYYY", u"YY", u"XXXX"}, { 74 u"abcde", u"XYabcde", u"XXYYYabcde", u"YYabcde", u"XXXXabcde"}, { 75 u"abcde", u"abXYcde", u"abXXYYYcde", u"abYYcde", u"abXXXXcde"}, { 76 u"abcde", u"aXbcYde", u"aXXbcYYYde", u"abcYYde", u"aXXXXbcde"}}; 77 78 UErrorCode status = U_ZERO_ERROR; 79 for (int32_t i = 0; i < NUM_CASES; i++) { 80 const UnicodeString pattern(patterns[i]); 81 SimpleFormatter compiledFormatter(pattern, 1, 1, status); 82 assertSuccess("Spot 1", status); 83 SimpleModifier mod(compiledFormatter, UNUM_PERCENT_FIELD, false); 84 assertModifierEquals( 85 mod, prefixLens[i], false, expectedCharFields[i][0], expectedCharFields[i][1], status); 86 assertSuccess("Spot 2", status); 87 88 // Test strange insertion positions 89 for (int32_t j = 0; j < NUM_OUTPUTS; j++) { 90 NumberStringBuilder output; 91 output.append(outputs[j].baseString, UNUM_FIELD_COUNT, status); 92 mod.apply(output, outputs[j].leftIndex, outputs[j].rightIndex, status); 93 UnicodeString expected = expecteds[j][i]; 94 UnicodeString actual = output.toUnicodeString(); 95 assertEquals("Strange insertion position", expected, actual); 96 assertSuccess("Spot 3", status); 97 } 98 } 99} 100 101void ModifiersTest::testCurrencySpacingEnabledModifier() { 102 UErrorCode status = U_ZERO_ERROR; 103 DecimalFormatSymbols symbols(Locale("en"), status); 104 assertSuccess("Spot 1", status); 105 106 NumberStringBuilder prefix; 107 NumberStringBuilder suffix; 108 CurrencySpacingEnabledModifier mod1(prefix, suffix, true, symbols, status); 109 assertSuccess("Spot 2", status); 110 assertModifierEquals(mod1, 0, true, u"|", u"n", status); 111 assertSuccess("Spot 3", status); 112 113 prefix.append(u"USD", UNUM_CURRENCY_FIELD, status); 114 assertSuccess("Spot 4", status); 115 CurrencySpacingEnabledModifier mod2(prefix, suffix, true, symbols, status); 116 assertSuccess("Spot 5", status); 117 assertModifierEquals(mod2, 3, true, u"USD|", u"$$$n", status); 118 assertSuccess("Spot 6", status); 119 120 // Test the default currency spacing rules 121 NumberStringBuilder sb; 122 sb.append("123", UNUM_INTEGER_FIELD, status); 123 assertSuccess("Spot 7", status); 124 NumberStringBuilder sb1(sb); 125 assertModifierEquals(mod2, sb1, 3, true, u"USD\u00A0123", u"$$$niii", status); 126 assertSuccess("Spot 8", status); 127 128 // Compare with the unsafe code path 129 NumberStringBuilder sb2(sb); 130 sb2.insert(0, "USD", UNUM_CURRENCY_FIELD, status); 131 assertSuccess("Spot 9", status); 132 CurrencySpacingEnabledModifier::applyCurrencySpacing(sb2, 0, 3, 6, 0, symbols, status); 133 assertSuccess("Spot 10", status); 134 assertTrue(sb1.toDebugString() + " vs " + sb2.toDebugString(), sb1.contentEquals(sb2)); 135 136 // Test custom patterns 137 // The following line means that the last char of the number should be a | (rather than a digit) 138 symbols.setPatternForCurrencySpacing(UNUM_CURRENCY_SURROUNDING_MATCH, true, u"[|]"); 139 suffix.append("XYZ", UNUM_CURRENCY_FIELD, status); 140 assertSuccess("Spot 11", status); 141 CurrencySpacingEnabledModifier mod3(prefix, suffix, true, symbols, status); 142 assertSuccess("Spot 12", status); 143 assertModifierEquals(mod3, 3, true, u"USD|\u00A0XYZ", u"$$$nn$$$", status); 144 assertSuccess("Spot 13", status); 145} 146 147void ModifiersTest::assertModifierEquals(const Modifier &mod, int32_t expectedPrefixLength, 148 bool expectedStrong, UnicodeString expectedChars, 149 UnicodeString expectedFields, UErrorCode &status) { 150 NumberStringBuilder sb; 151 sb.appendCodePoint('|', UNUM_FIELD_COUNT, status); 152 assertModifierEquals( 153 mod, sb, expectedPrefixLength, expectedStrong, expectedChars, expectedFields, status); 154 155} 156 157void ModifiersTest::assertModifierEquals(const Modifier &mod, NumberStringBuilder &sb, 158 int32_t expectedPrefixLength, bool expectedStrong, 159 UnicodeString expectedChars, UnicodeString expectedFields, 160 UErrorCode &status) { 161 int32_t oldCount = sb.codePointCount(); 162 mod.apply(sb, 0, sb.length(), status); 163 assertEquals("Prefix length", expectedPrefixLength, mod.getPrefixLength(status)); 164 assertEquals("Strong", expectedStrong, mod.isStrong()); 165 if (dynamic_cast<const CurrencySpacingEnabledModifier*>(&mod) == nullptr) { 166 // i.e., if mod is not a CurrencySpacingEnabledModifier 167 assertEquals("Code point count equals actual code point count", 168 sb.codePointCount() - oldCount, mod.getCodePointCount(status)); 169 } 170 171 UnicodeString debugString; 172 debugString.append(u"<NumberStringBuilder ["); 173 debugString.append(expectedChars); 174 debugString.append(u"] ["); 175 debugString.append(expectedFields); 176 debugString.append(u"]>"); 177 assertEquals("Debug string", debugString, sb.toDebugString()); 178} 179 180#endif /* #if !UCONFIG_NO_FORMATTING */ 181