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
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)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
testInsertAppendUnicodeString()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
testSplice()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
testInsertAppendCodePoint()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
testCopy()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
testFields()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
testUnlimitedCapacity()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
testCodePoints()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
assertEqualsImpl(const UnicodeString & a,const NumberStringBuilder & b)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