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 "numbertest.h"
10
11static 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
20void 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(testInsertAppendCodePoint);
27        TESTCASE_AUTO(testCopy);
28        TESTCASE_AUTO(testFields);
29        TESTCASE_AUTO(testUnlimitedCapacity);
30        TESTCASE_AUTO(testCodePoints);
31    TESTCASE_AUTO_END;
32}
33
34void NumberStringBuilderTest::testInsertAppendUnicodeString() {
35    UErrorCode status = U_ZERO_ERROR;
36    UnicodeString sb1;
37    NumberStringBuilder sb2;
38    for (const char16_t* strPtr : EXAMPLE_STRINGS) {
39        UnicodeString str(strPtr);
40
41        NumberStringBuilder sb3;
42        sb1.append(str);
43        // Note: UNUM_FIELD_COUNT is like passing null in Java
44        sb2.append(str, UNUM_FIELD_COUNT, status);
45        assertSuccess("Appending to sb2", status);
46        sb3.append(str, UNUM_FIELD_COUNT, status);
47        assertSuccess("Appending to sb3", status);
48        assertEqualsImpl(sb1, sb2);
49        assertEqualsImpl(str, sb3);
50
51        UnicodeString sb4;
52        NumberStringBuilder sb5;
53        sb4.append(u"��");
54        sb4.append(str);
55        sb4.append(u"xx");
56        sb5.append(u"��xx", UNUM_FIELD_COUNT, status);
57        assertSuccess("Appending to sb5", status);
58        sb5.insert(2, str, UNUM_FIELD_COUNT, status);
59        assertSuccess("Inserting into sb5", status);
60        assertEqualsImpl(sb4, sb5);
61
62        int start = uprv_min(1, str.length());
63        int end = uprv_min(10, str.length());
64        sb4.insert(3, str, start, end - start); // UnicodeString uses length instead of end index
65        sb5.insert(3, str, start, end, UNUM_FIELD_COUNT, status);
66        assertSuccess("Inserting into sb5 again", status);
67        assertEqualsImpl(sb4, sb5);
68
69        UnicodeString sb4cp(sb4);
70        NumberStringBuilder sb5cp(sb5);
71        sb4.append(sb4cp);
72        sb5.append(sb5cp, status);
73        assertSuccess("Appending again to sb5", status);
74        assertEqualsImpl(sb4, sb5);
75    }
76}
77
78void NumberStringBuilderTest::testInsertAppendCodePoint() {
79    static const UChar32 cases[] = {
80            0, 1, 60, 127, 128, 0x7fff, 0x8000, 0xffff, 0x10000, 0x1f000, 0x10ffff};
81    UErrorCode status = U_ZERO_ERROR;
82    UnicodeString sb1;
83    NumberStringBuilder sb2;
84    for (UChar32 cas : cases) {
85        NumberStringBuilder sb3;
86        sb1.append(cas);
87        sb2.appendCodePoint(cas, UNUM_FIELD_COUNT, status);
88        assertSuccess("Appending to sb2", status);
89        sb3.appendCodePoint(cas, UNUM_FIELD_COUNT, status);
90        assertSuccess("Appending to sb3", status);
91        assertEqualsImpl(sb1, sb2);
92        assertEquals("Length of sb3", U16_LENGTH(cas), sb3.length());
93        assertEquals("Code point count of sb3", 1, sb3.codePointCount());
94        assertEquals(
95                "First code unit in sb3",
96                !U_IS_SUPPLEMENTARY(cas) ? (char16_t) cas : U16_LEAD(cas),
97                sb3.charAt(0));
98
99        UnicodeString sb4;
100        NumberStringBuilder sb5;
101        sb4.append(u"��xx");
102        sb4.insert(2, cas);
103        sb5.append(u"��xx", UNUM_FIELD_COUNT, status);
104        assertSuccess("Appending to sb5", status);
105        sb5.insertCodePoint(2, cas, UNUM_FIELD_COUNT, status);
106        assertSuccess("Inserting into sb5", status);
107        assertEqualsImpl(sb4, sb5);
108    }
109}
110
111void NumberStringBuilderTest::testCopy() {
112    UErrorCode status = U_ZERO_ERROR;
113    for (UnicodeString str : EXAMPLE_STRINGS) {
114        NumberStringBuilder sb1;
115        sb1.append(str, UNUM_FIELD_COUNT, status);
116        assertSuccess("Appending to sb1 first time", status);
117        NumberStringBuilder sb2(sb1);
118        assertTrue("Content should equal itself", sb1.contentEquals(sb2));
119
120        sb1.append("12345", UNUM_FIELD_COUNT, status);
121        assertSuccess("Appending to sb1 second time", status);
122        assertFalse("Content should no longer equal itself", sb1.contentEquals(sb2));
123    }
124}
125
126void NumberStringBuilderTest::testFields() {
127    UErrorCode status = U_ZERO_ERROR;
128    // Note: This is a C++11 for loop that calls the UnicodeString constructor on each iteration.
129    for (UnicodeString str : EXAMPLE_STRINGS) {
130        NumberStringBuilder sb;
131        sb.append(str, UNUM_FIELD_COUNT, status);
132        assertSuccess("Appending to sb", status);
133        sb.append(str, UNUM_CURRENCY_FIELD, status);
134        assertSuccess("Appending to sb", status);
135        assertEquals("Reference string copied twice", str.length() * 2, sb.length());
136        for (int32_t i = 0; i < str.length(); i++) {
137            assertEquals("Null field first", UNUM_FIELD_COUNT, sb.fieldAt(i));
138            assertEquals("Currency field second", UNUM_CURRENCY_FIELD, sb.fieldAt(i + str.length()));
139        }
140
141        // Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
142        // Let NumberFormatTest also take care of FieldPositionIterator material.
143        FieldPosition fp(UNUM_CURRENCY_FIELD);
144        sb.populateFieldPosition(fp, 0, status);
145        assertSuccess("Populating the FieldPosition", status);
146        assertEquals("Currency start position", str.length(), fp.getBeginIndex());
147        assertEquals("Currency end position", str.length() * 2, fp.getEndIndex());
148
149        if (str.length() > 0) {
150            sb.insertCodePoint(2, 100, UNUM_INTEGER_FIELD, status);
151            assertSuccess("Inserting code point into sb", status);
152            assertEquals("New length", str.length() * 2 + 1, sb.length());
153            assertEquals("Integer field", UNUM_INTEGER_FIELD, sb.fieldAt(2));
154        }
155
156        NumberStringBuilder old(sb);
157        sb.append(old, status);
158        assertSuccess("Appending to myself", status);
159        int32_t numNull = 0;
160        int32_t numCurr = 0;
161        int32_t numInt = 0;
162        for (int32_t i = 0; i < sb.length(); i++) {
163            UNumberFormatFields field = sb.fieldAt(i);
164            assertEquals("Field should equal location in old", old.fieldAt(i % old.length()), field);
165            if (field == UNUM_FIELD_COUNT) {
166                numNull++;
167            } else if (field == UNUM_CURRENCY_FIELD) {
168                numCurr++;
169            } else if (field == UNUM_INTEGER_FIELD) {
170                numInt++;
171            } else {
172                errln("Encountered unknown field");
173            }
174        }
175        assertEquals("Number of null fields", str.length() * 2, numNull);
176        assertEquals("Number of currency fields", numNull, numCurr);
177        assertEquals("Number of integer fields", str.length() > 0 ? 2 : 0, numInt);
178    }
179}
180
181void NumberStringBuilderTest::testUnlimitedCapacity() {
182    UErrorCode status = U_ZERO_ERROR;
183    NumberStringBuilder builder;
184    // The builder should never fail upon repeated appends.
185    for (int i = 0; i < 1000; i++) {
186        UnicodeString message("Iteration #");
187        message += Int64ToUnicodeString(i);
188        assertEquals(message, builder.length(), i);
189        builder.appendCodePoint(u'x', UNUM_FIELD_COUNT, status);
190        assertSuccess(message, status);
191        assertEquals(message, builder.length(), i + 1);
192    }
193}
194
195void NumberStringBuilderTest::testCodePoints() {
196    UErrorCode status = U_ZERO_ERROR;
197    NumberStringBuilder nsb;
198    assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint());
199    assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint());
200    assertEquals("Length is 0 on empty string", 0, nsb.codePointCount());
201
202    nsb.append(u"q", UNUM_FIELD_COUNT, status);
203    assertSuccess("Spot 1", status);
204    assertEquals("First is q", u'q', nsb.getFirstCodePoint());
205    assertEquals("Last is q", u'q', nsb.getLastCodePoint());
206    assertEquals("0th is q", u'q', nsb.codePointAt(0));
207    assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
208    assertEquals("Code point count is 1", 1, nsb.codePointCount());
209
210    // �� is two char16s
211    nsb.append(u"��", UNUM_FIELD_COUNT, status);
212    assertSuccess("Spot 2" ,status);
213    assertEquals("First is still q", u'q', nsb.getFirstCodePoint());
214    assertEquals("Last is space ship", 128640, nsb.getLastCodePoint());
215    assertEquals("1st is space ship", 128640, nsb.codePointAt(1));
216    assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
217    assertEquals("Before 3rd is space ship", 128640, nsb.codePointBefore(3));
218    assertEquals("Code point count is 2", 2, nsb.codePointCount());
219}
220
221void NumberStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const NumberStringBuilder &b) {
222    // TODO: Why won't this compile without the IntlTest:: qualifier?
223    IntlTest::assertEquals("Lengths should be the same", a.length(), b.length());
224    IntlTest::assertEquals("Code point counts should be the same", a.countChar32(), b.codePointCount());
225
226    if (a.length() != b.length()) {
227        return;
228    }
229
230    for (int32_t i = 0; i < a.length(); i++) {
231        IntlTest::assertEquals(
232                UnicodeString(u"Char at position ") + Int64ToUnicodeString(i) +
233                UnicodeString(u" in string ") + a, a.charAt(i), b.charAt(i));
234    }
235}
236
237#endif /* #if !UCONFIG_NO_FORMATTING */
238