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 7 8 #include "putilimp.h" 9 #include "numbertest.h" 10 11 static const char16_t *EXAMPLE_STRINGS[] = { 12 u"", 13 u"xyz", 14 u"The quick brown fox jumps over the lazy dog", 15 u"", 16 u"mixed and ASCII", 17 u"with combining characters like ", 18 u"A very very very very very very very very very very long string to force heap"}; 19 20 void NumberStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) { 21 if (exec) { 22 logln("TestSuite NumberStringBuilderTest: "); 23 } 24 TESTCASE_AUTO_BEGIN; 25 TESTCASE_AUTO(testInsertAppendUnicodeString); 26 TESTCASE_AUTO(testSplice); 27 TESTCASE_AUTO(testInsertAppendCodePoint); 28 TESTCASE_AUTO(testCopy); 29 TESTCASE_AUTO(testFields); 30 TESTCASE_AUTO(testUnlimitedCapacity); 31 TESTCASE_AUTO(testCodePoints); 32 TESTCASE_AUTO_END; 33 } 34 35 void NumberStringBuilderTest::testInsertAppendUnicodeString() { 36 UErrorCode status = U_ZERO_ERROR; 37 UnicodeString sb1; 38 NumberStringBuilder sb2; 39 for (const char16_t* strPtr : EXAMPLE_STRINGS) { 40 UnicodeString str(strPtr); 41 42 NumberStringBuilder sb3; 43 sb1.append(str); 44 // Note: UNUM_FIELD_COUNT is like passing null in Java 45 sb2.append(str, UNUM_FIELD_COUNT, status); 46 assertSuccess("Appending to sb2", status); 47 sb3.append(str, UNUM_FIELD_COUNT, status); 48 assertSuccess("Appending to sb3", status); 49 assertEqualsImpl(sb1, sb2); 50 assertEqualsImpl(str, sb3); 51 52 UnicodeString sb4; 53 NumberStringBuilder sb5; 54 sb4.append(u""); 55 sb4.append(str); 56 sb4.append(u"xx"); 57 sb5.append(u"xx", UNUM_FIELD_COUNT, status); 58 assertSuccess("Appending to sb5", status); 59 sb5.insert(2, str, UNUM_FIELD_COUNT, status); 60 assertSuccess("Inserting into sb5", status); 61 assertEqualsImpl(sb4, sb5); 62 63 int start = uprv_min(1, str.length()); 64 int end = uprv_min(10, str.length()); 65 sb4.insert(3, str, start, end - start); // UnicodeString uses length instead of end index 66 sb5.insert(3, str, start, end, UNUM_FIELD_COUNT, status); 67 assertSuccess("Inserting into sb5 again", status); 68 assertEqualsImpl(sb4, sb5); 69 70 UnicodeString sb4cp(sb4); 71 NumberStringBuilder sb5cp(sb5); 72 sb4.append(sb4cp); 73 sb5.append(sb5cp, status); 74 assertSuccess("Appending again to sb5", status); 75 assertEqualsImpl(sb4, sb5); 76 } 77 } 78 79 void NumberStringBuilderTest::testSplice() { 80 static const struct TestCase { 81 const char16_t* input; 82 const int32_t startThis; 83 const int32_t endThis; 84 } cases[] = { 85 { u"", 0, 0 }, 86 { u"abc", 0, 0 }, 87 { u"abc", 1, 1 }, 88 { u"abc", 1, 2 }, 89 { u"abc", 0, 2 }, 90 { u"abc", 0, 3 }, 91 { u"lorem ipsum dolor sit amet", 8, 8 }, 92 { u"lorem ipsum dolor sit amet", 8, 11 }, // 3 chars, equal to replacement "xyz" 93 { u"lorem ipsum dolor sit amet", 8, 18 } }; // 10 chars, larger than several replacements 94 95 UErrorCode status = U_ZERO_ERROR; 96 UnicodeString sb1; 97 NumberStringBuilder sb2; 98 for (auto cas : cases) { 99 for (const char16_t* replacementPtr : EXAMPLE_STRINGS) { 100 UnicodeString replacement(replacementPtr); 101 102 // Test replacement with full string 103 sb1.remove(); 104 sb1.append(cas.input); 105 sb1.replace(cas.startThis, cas.endThis - cas.startThis, replacement); 106 sb2.clear(); 107 sb2.append(cas.input, UNUM_FIELD_COUNT, status); 108 sb2.splice(cas.startThis, cas.endThis, replacement, 0, replacement.length(), UNUM_FIELD_COUNT, status); 109 assertSuccess("Splicing into sb2 first time", status); 110 assertEqualsImpl(sb1, sb2); 111 112 // Test replacement with partial string 113 if (replacement.length() <= 2) { 114 continue; 115 } 116 sb1.remove(); 117 sb1.append(cas.input); 118 sb1.replace(cas.startThis, cas.endThis - cas.startThis, UnicodeString(replacement, 1, 2)); 119 sb2.clear(); 120 sb2.append(cas.input, UNUM_FIELD_COUNT, status); 121 sb2.splice(cas.startThis, cas.endThis, replacement, 1, 3, UNUM_FIELD_COUNT, status); 122 assertSuccess("Splicing into sb2 second time", status); 123 assertEqualsImpl(sb1, sb2); 124 } 125 } 126 } 127 128 void NumberStringBuilderTest::testInsertAppendCodePoint() { 129 static const UChar32 cases[] = { 130 0, 1, 60, 127, 128, 0x7fff, 0x8000, 0xffff, 0x10000, 0x1f000, 0x10ffff}; 131 UErrorCode status = U_ZERO_ERROR; 132 UnicodeString sb1; 133 NumberStringBuilder sb2; 134 for (UChar32 cas : cases) { 135 NumberStringBuilder sb3; 136 sb1.append(cas); 137 sb2.appendCodePoint(cas, UNUM_FIELD_COUNT, status); 138 assertSuccess("Appending to sb2", status); 139 sb3.appendCodePoint(cas, UNUM_FIELD_COUNT, status); 140 assertSuccess("Appending to sb3", status); 141 assertEqualsImpl(sb1, sb2); 142 assertEquals("Length of sb3", U16_LENGTH(cas), sb3.length()); 143 assertEquals("Code point count of sb3", 1, sb3.codePointCount()); 144 assertEquals( 145 "First code unit in sb3", 146 !U_IS_SUPPLEMENTARY(cas) ? (char16_t) cas : U16_LEAD(cas), 147 sb3.charAt(0)); 148 149 UnicodeString sb4; 150 NumberStringBuilder sb5; 151 sb4.append(u"xx"); 152 sb4.insert(2, cas); 153 sb5.append(u"xx", UNUM_FIELD_COUNT, status); 154 assertSuccess("Appending to sb5", status); 155 sb5.insertCodePoint(2, cas, UNUM_FIELD_COUNT, status); 156 assertSuccess("Inserting into sb5", status); 157 assertEqualsImpl(sb4, sb5); 158 } 159 } 160 161 void NumberStringBuilderTest::testCopy() { 162 UErrorCode status = U_ZERO_ERROR; 163 for (UnicodeString str : EXAMPLE_STRINGS) { 164 NumberStringBuilder sb1; 165 sb1.append(str, UNUM_FIELD_COUNT, status); 166 assertSuccess("Appending to sb1 first time", status); 167 NumberStringBuilder sb2(sb1); 168 assertTrue("Content should equal itself", sb1.contentEquals(sb2)); 169 170 sb1.append("12345", UNUM_FIELD_COUNT, status); 171 assertSuccess("Appending to sb1 second time", status); 172 assertFalse("Content should no longer equal itself", sb1.contentEquals(sb2)); 173 } 174 } 175 176 void NumberStringBuilderTest::testFields() { 177 UErrorCode status = U_ZERO_ERROR; 178 // Note: This is a C++11 for loop that calls the UnicodeString constructor on each iteration. 179 for (UnicodeString str : EXAMPLE_STRINGS) { 180 NumberStringBuilder sb; 181 sb.append(str, UNUM_FIELD_COUNT, status); 182 assertSuccess("Appending to sb", status); 183 sb.append(str, UNUM_CURRENCY_FIELD, status); 184 assertSuccess("Appending to sb", status); 185 assertEquals("Reference string copied twice", str.length() * 2, sb.length()); 186 for (int32_t i = 0; i < str.length(); i++) { 187 assertEquals("Null field first", UNUM_FIELD_COUNT, sb.fieldAt(i)); 188 assertEquals("Currency field second", UNUM_CURRENCY_FIELD, sb.fieldAt(i + str.length())); 189 } 190 191 // Very basic FieldPosition test. More robust tests happen in NumberFormatTest. 192 // Let NumberFormatTest also take care of FieldPositionIterator material. 193 FieldPosition fp(UNUM_CURRENCY_FIELD); 194 sb.nextFieldPosition(fp, status); 195 assertSuccess("Populating the FieldPosition", status); 196 assertEquals("Currency start position", str.length(), fp.getBeginIndex()); 197 assertEquals("Currency end position", str.length() * 2, fp.getEndIndex()); 198 199 if (str.length() > 0) { 200 sb.insertCodePoint(2, 100, UNUM_INTEGER_FIELD, status); 201 assertSuccess("Inserting code point into sb", status); 202 assertEquals("New length", str.length() * 2 + 1, sb.length()); 203 assertEquals("Integer field", UNUM_INTEGER_FIELD, sb.fieldAt(2)); 204 } 205 206 NumberStringBuilder old(sb); 207 sb.append(old, status); 208 assertSuccess("Appending to myself", status); 209 int32_t numNull = 0; 210 int32_t numCurr = 0; 211 int32_t numInt = 0; 212 for (int32_t i = 0; i < sb.length(); i++) { 213 UNumberFormatFields field = sb.fieldAt(i); 214 assertEquals("Field should equal location in old", old.fieldAt(i % old.length()), field); 215 if (field == UNUM_FIELD_COUNT) { 216 numNull++; 217 } else if (field == UNUM_CURRENCY_FIELD) { 218 numCurr++; 219 } else if (field == UNUM_INTEGER_FIELD) { 220 numInt++; 221 } else { 222 errln("Encountered unknown field"); 223 } 224 } 225 assertEquals("Number of null fields", str.length() * 2, numNull); 226 assertEquals("Number of currency fields", numNull, numCurr); 227 assertEquals("Number of integer fields", str.length() > 0 ? 2 : 0, numInt); 228 } 229 } 230 231 void NumberStringBuilderTest::testUnlimitedCapacity() { 232 UErrorCode status = U_ZERO_ERROR; 233 NumberStringBuilder builder; 234 // The builder should never fail upon repeated appends. 235 for (int i = 0; i < 1000; i++) { 236 UnicodeString message("Iteration #"); 237 message += Int64ToUnicodeString(i); 238 assertEquals(message, builder.length(), i); 239 builder.appendCodePoint(u'x', UNUM_FIELD_COUNT, status); 240 assertSuccess(message, status); 241 assertEquals(message, builder.length(), i + 1); 242 } 243 } 244 245 void NumberStringBuilderTest::testCodePoints() { 246 UErrorCode status = U_ZERO_ERROR; 247 NumberStringBuilder nsb; 248 assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint()); 249 assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint()); 250 assertEquals("Length is 0 on empty string", 0, nsb.codePointCount()); 251 252 nsb.append(u"q", UNUM_FIELD_COUNT, status); 253 assertSuccess("Spot 1", status); 254 assertEquals("First is q", u'q', nsb.getFirstCodePoint()); 255 assertEquals("Last is q", u'q', nsb.getLastCodePoint()); 256 assertEquals("0th is q", u'q', nsb.codePointAt(0)); 257 assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1)); 258 assertEquals("Code point count is 1", 1, nsb.codePointCount()); 259 260 // is two char16s 261 nsb.append(u"", UNUM_FIELD_COUNT, status); 262 assertSuccess("Spot 2" ,status); 263 assertEquals("First is still q", u'q', nsb.getFirstCodePoint()); 264 assertEquals("Last is space ship", 128640, nsb.getLastCodePoint()); 265 assertEquals("1st is space ship", 128640, nsb.codePointAt(1)); 266 assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1)); 267 assertEquals("Before 3rd is space ship", 128640, nsb.codePointBefore(3)); 268 assertEquals("Code point count is 2", 2, nsb.codePointCount()); 269 } 270 271 void NumberStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const NumberStringBuilder &b) { 272 // TODO: Why won't this compile without the IntlTest:: qualifier? 273 IntlTest::assertEquals("Lengths should be the same", a.length(), b.length()); 274 IntlTest::assertEquals("Code point counts should be the same", a.countChar32(), b.codePointCount()); 275 276 if (a.length() != b.length()) { 277 return; 278 } 279 280 for (int32_t i = 0; i < a.length(); i++) { 281 IntlTest::assertEquals( 282 UnicodeString(u"Char at position ") + Int64ToUnicodeString(i) + 283 UnicodeString(u" in \"") + a + UnicodeString("\" versus \"") + 284 b.toUnicodeString() + UnicodeString("\""), a.charAt(i), b.charAt(i)); 285 } 286 } 287 288 #endif /* #if !UCONFIG_NO_FORMATTING */ 289