1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "WTFStringUtilities.h"
34
35#include <wtf/unicode/CharacterNames.h>
36
37namespace TestWebKitAPI {
38
39static void expectBuilderContent(const String& expected, const StringBuilder& builder)
40{
41 // Not using builder.toString() or builder.toStringPreserveCapacity() because they all
42 // change internal state of builder.
43 if (builder.is8Bit())
44 EXPECT_EQ(expected, String(builder.characters8(), builder.length()));
45 else
46 EXPECT_EQ(expected, String(builder.characters16(), builder.length()));
47}
48
49void expectEmpty(const StringBuilder& builder)
50{
51 EXPECT_EQ(0U, builder.length());
52 EXPECT_TRUE(builder.isEmpty());
53 EXPECT_EQ(0, builder.characters8());
54}
55
56TEST(StringBuilderTest, DefaultConstructor)
57{
58 StringBuilder builder;
59 expectEmpty(builder);
60}
61
62TEST(StringBuilderTest, Append)
63{
64 StringBuilder builder;
65 builder.append(String("0123456789"));
66 expectBuilderContent("0123456789", builder);
67 builder.append("abcd");
68 expectBuilderContent("0123456789abcd", builder);
69 builder.appendCharacters("efgh", 3);
70 expectBuilderContent("0123456789abcdefg", builder);
71 builder.append("");
72 expectBuilderContent("0123456789abcdefg", builder);
73 builder.append('#');
74 expectBuilderContent("0123456789abcdefg#", builder);
75
76 builder.toString(); // Test after reifyString().
77 StringBuilder builder1;
78 builder.appendCharacters("", 0);
79 expectBuilderContent("0123456789abcdefg#", builder);
80 builder1.appendCharacters(builder.characters8(), builder.length());
81 builder1.append("XYZ");
82 builder.appendCharacters(builder1.characters8(), builder1.length());
83 expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder);
84
85 StringBuilder builder2;
86 builder2.reserveCapacity(100);
87 builder2.append("xyz");
88 const LChar* characters = builder2.characters8();
89 builder2.append("0123456789");
90 EXPECT_EQ(characters, builder2.characters8());
91 builder2.toStringPreserveCapacity(); // Test after reifyString with buffer preserved.
92 builder2.append("abcd");
93 EXPECT_EQ(characters, builder2.characters8());
94
95 // Test appending UChar32 characters to StringBuilder.
96 StringBuilder builderForUChar32Append;
97 UChar32 frakturAChar = 0x1D504;
98 builderForUChar32Append.appendCharacter(frakturAChar); // The fraktur A is not in the BMP, so it's two UTF-16 code units long.
99 EXPECT_EQ(2U, builderForUChar32Append.length());
100 builderForUChar32Append.appendCharacter(static_cast<UChar32>('A'));
101 EXPECT_EQ(3U, builderForUChar32Append.length());
102 const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), 'A' };
103 expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builderForUChar32Append);
104 {
105 StringBuilder builder;
106 StringBuilder builder2;
107 UChar32 frakturAChar = 0x1D504;
108 const UChar data[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar) };
109 builder2.appendCharacters(data, 2);
110 EXPECT_EQ(2U, builder2.length());
111 String result2 = builder2.toString();
112 EXPECT_EQ(2U, result2.length());
113 builder.append(builder2);
114 builder.appendCharacters(data, 2);
115 EXPECT_EQ(4U, builder.length());
116 const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar) };
117 expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builder);
118 }
119}
120
121TEST(StringBuilderTest, VariadicAppend)
122{
123 {
124 StringBuilder builder;
125 builder.append(String("0123456789"));
126 expectBuilderContent("0123456789", builder);
127 builder.append("abcd");
128 expectBuilderContent("0123456789abcd", builder);
129 builder.append('e');
130 expectBuilderContent("0123456789abcde", builder);
131 builder.append("");
132 expectBuilderContent("0123456789abcde", builder);
133 }
134
135 {
136 StringBuilder builder;
137 builder.append(String("0123456789"), "abcd", 'e', "");
138 expectBuilderContent("0123456789abcde", builder);
139 builder.append(String("A"), "B", 'C', "");
140 expectBuilderContent("0123456789abcdeABC", builder);
141 }
142
143 {
144 StringBuilder builder;
145 builder.append(String("0123456789"), "abcd", bullseye, "");
146 expectBuilderContent("0123456789abcd" + String(&bullseye, 1), builder);
147 builder.append(String("A"), "B", 'C', "");
148 expectBuilderContent("0123456789abcd" + String(&bullseye, 1) + "ABC", builder);
149 }
150
151 {
152 // Test where we upconvert the StringBuilder from 8-bit to 16-bit, and don't fit in the existing capacity.
153 StringBuilder builder;
154 builder.append(String("0123456789"), "abcd", 'e', "");
155 expectBuilderContent("0123456789abcde", builder);
156 EXPECT_TRUE(builder.is8Bit());
157 EXPECT_LT(builder.capacity(), builder.length() + 3);
158 builder.append(String("A"), "B", bullseye, "");
159 expectBuilderContent("0123456789abcdeAB" + String(&bullseye, 1), builder);
160 }
161
162 {
163 // Test where we upconvert the StringBuilder from 8-bit to 16-bit, but would have fit in the capacity if the upconvert wasn't necessary.
164 StringBuilder builder;
165 builder.append(String("0123456789"), "abcd", 'e', "");
166 expectBuilderContent("0123456789abcde", builder);
167 builder.reserveCapacity(32);
168 EXPECT_TRUE(builder.is8Bit());
169 EXPECT_GE(builder.capacity(), builder.length() + 3);
170 builder.append(String("A"), "B", bullseye, "");
171 expectBuilderContent("0123456789abcdeAB" + String(&bullseye, 1), builder);
172 }
173}
174
175TEST(StringBuilderTest, ToString)
176{
177 StringBuilder builder;
178 builder.append("0123456789");
179 String string = builder.toString();
180 EXPECT_EQ(String("0123456789"), string);
181 EXPECT_EQ(string.impl(), builder.toString().impl());
182
183 // Changing the StringBuilder should not affect the original result of toString().
184 builder.append("abcdefghijklmnopqrstuvwxyz");
185 EXPECT_EQ(String("0123456789"), string);
186
187 // Changing the StringBuilder should not affect the original result of toString() in case the capacity is not changed.
188 builder.reserveCapacity(200);
189 string = builder.toString();
190 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
191 builder.append("ABC");
192 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
193
194 // Changing the original result of toString() should not affect the content of the StringBuilder.
195 String string1 = builder.toString();
196 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
197 string1.append("DEF");
198 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toString());
199 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
200
201 // Resizing the StringBuilder should not affect the original result of toString().
202 string1 = builder.toString();
203 builder.resize(10);
204 builder.append("###");
205 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
206}
207
208TEST(StringBuilderTest, ToStringPreserveCapacity)
209{
210 StringBuilder builder;
211 builder.append("0123456789");
212 unsigned capacity = builder.capacity();
213 String string = builder.toStringPreserveCapacity();
214 EXPECT_EQ(capacity, builder.capacity());
215 EXPECT_EQ(String("0123456789"), string);
216 EXPECT_EQ(string.impl(), builder.toStringPreserveCapacity().impl());
217 EXPECT_EQ(string.characters8(), builder.characters8());
218
219 // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity().
220 builder.append("abcdefghijklmnopqrstuvwxyz");
221 EXPECT_EQ(String("0123456789"), string);
222
223 // Changing the StringBuilder should not affect the original result of toStringPreserveCapacity() in case the capacity is not changed.
224 builder.reserveCapacity(200);
225 capacity = builder.capacity();
226 string = builder.toStringPreserveCapacity();
227 EXPECT_EQ(capacity, builder.capacity());
228 EXPECT_EQ(string.characters8(), builder.characters8());
229 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
230 builder.append("ABC");
231 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
232
233 // Changing the original result of toStringPreserveCapacity() should not affect the content of the StringBuilder.
234 capacity = builder.capacity();
235 String string1 = builder.toStringPreserveCapacity();
236 EXPECT_EQ(capacity, builder.capacity());
237 EXPECT_EQ(string1.characters8(), builder.characters8());
238 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
239 string1.append("DEF");
240 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toStringPreserveCapacity());
241 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
242
243 // Resizing the StringBuilder should not affect the original result of toStringPreserveCapacity().
244 capacity = builder.capacity();
245 string1 = builder.toStringPreserveCapacity();
246 EXPECT_EQ(capacity, builder.capacity());
247 EXPECT_EQ(string.characters8(), builder.characters8());
248 builder.resize(10);
249 builder.append("###");
250 EXPECT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
251}
252
253TEST(StringBuilderTest, Clear)
254{
255 StringBuilder builder;
256 builder.append("0123456789");
257 builder.clear();
258 expectEmpty(builder);
259}
260
261TEST(StringBuilderTest, Array)
262{
263 StringBuilder builder;
264 builder.append("0123456789");
265 EXPECT_EQ('0', static_cast<char>(builder[0]));
266 EXPECT_EQ('9', static_cast<char>(builder[9]));
267 builder.toString(); // Test after reifyString().
268 EXPECT_EQ('0', static_cast<char>(builder[0]));
269 EXPECT_EQ('9', static_cast<char>(builder[9]));
270}
271
272TEST(StringBuilderTest, Resize)
273{
274 StringBuilder builder;
275 builder.append("0123456789");
276 builder.resize(10);
277 EXPECT_EQ(10U, builder.length());
278 expectBuilderContent("0123456789", builder);
279 builder.resize(8);
280 EXPECT_EQ(8U, builder.length());
281 expectBuilderContent("01234567", builder);
282
283 builder.toString();
284 builder.resize(7);
285 EXPECT_EQ(7U, builder.length());
286 expectBuilderContent("0123456", builder);
287 builder.resize(0);
288 expectEmpty(builder);
289}
290
291TEST(StringBuilderTest, Equal)
292{
293 StringBuilder builder1;
294 StringBuilder builder2;
295 EXPECT_TRUE(builder1 == builder2);
296 EXPECT_TRUE(equal(builder1, static_cast<LChar*>(0), 0));
297 EXPECT_TRUE(builder1 == String());
298 EXPECT_TRUE(String() == builder1);
299 EXPECT_TRUE(builder1 != String("abc"));
300
301 builder1.append("123");
302 builder1.reserveCapacity(32);
303 builder2.append("123");
304 builder1.reserveCapacity(64);
305 EXPECT_TRUE(builder1 == builder2);
306 EXPECT_TRUE(builder1 == String("123"));
307 EXPECT_TRUE(String("123") == builder1);
308
309 builder2.append("456");
310 EXPECT_TRUE(builder1 != builder2);
311 EXPECT_TRUE(builder2 != builder1);
312 EXPECT_TRUE(String("123") != builder2);
313 EXPECT_TRUE(builder2 != String("123"));
314 builder2.toString(); // Test after reifyString().
315 EXPECT_TRUE(builder1 != builder2);
316
317 builder2.resize(3);
318 EXPECT_TRUE(builder1 == builder2);
319
320 builder1.toString(); // Test after reifyString().
321 EXPECT_TRUE(builder1 == builder2);
322}
323
324TEST(StringBuilderTest, CanShrink)
325{
326 StringBuilder builder;
327 builder.reserveCapacity(256);
328 EXPECT_TRUE(builder.canShrink());
329 for (int i = 0; i < 256; i++)
330 builder.append('x');
331 EXPECT_EQ(builder.length(), builder.capacity());
332 EXPECT_FALSE(builder.canShrink());
333}
334
335TEST(StringBuilderTest, ToAtomString)
336{
337 StringBuilder builder;
338 builder.append("123");
339 AtomString atomString = builder.toAtomString();
340 EXPECT_EQ(String("123"), atomString);
341
342 builder.reserveCapacity(256);
343 EXPECT_TRUE(builder.canShrink());
344 for (int i = builder.length(); i < 128; i++)
345 builder.append('x');
346 AtomString atomString1 = builder.toAtomString();
347 EXPECT_EQ(128u, atomString1.length());
348 EXPECT_EQ('x', atomString1[127]);
349
350 // Later change of builder should not affect the atomic string.
351 for (int i = builder.length(); i < 256; i++)
352 builder.append('x');
353 EXPECT_EQ(128u, atomString1.length());
354
355 EXPECT_FALSE(builder.canShrink());
356 String string = builder.toString();
357 AtomString atomString2 = builder.toAtomString();
358 // They should share the same StringImpl.
359 EXPECT_EQ(atomString2.impl(), string.impl());
360}
361
362TEST(StringBuilderTest, ToAtomStringOnEmpty)
363{
364 { // Default constructed.
365 StringBuilder builder;
366 AtomString atomString = builder.toAtomString();
367 EXPECT_EQ(emptyAtom(), atomString);
368 }
369 { // With capacity.
370 StringBuilder builder;
371 builder.reserveCapacity(64);
372 AtomString atomString = builder.toAtomString();
373 EXPECT_EQ(emptyAtom(), atomString);
374 }
375 { // AtomString constructed from a null string.
376 StringBuilder builder;
377 builder.append(String());
378 AtomString atomString = builder.toAtomString();
379 EXPECT_EQ(emptyAtom(), atomString);
380 }
381 { // AtomString constructed from an empty string.
382 StringBuilder builder;
383 builder.append(emptyString());
384 AtomString atomString = builder.toAtomString();
385 EXPECT_EQ(emptyAtom(), atomString);
386 }
387 { // AtomString constructed from an empty StringBuilder.
388 StringBuilder builder;
389 StringBuilder emptyBuilder;
390 builder.append(emptyBuilder);
391 AtomString atomString = builder.toAtomString();
392 EXPECT_EQ(emptyAtom(), atomString);
393 }
394 { // AtomString constructed from an empty char* string.
395 StringBuilder builder;
396 builder.appendCharacters("", 0);
397 AtomString atomString = builder.toAtomString();
398 EXPECT_EQ(emptyAtom(), atomString);
399 }
400 { // Cleared StringBuilder.
401 StringBuilder builder;
402 builder.appendLiteral("WebKit");
403 builder.clear();
404 AtomString atomString = builder.toAtomString();
405 EXPECT_EQ(emptyAtom(), atomString);
406 }
407}
408
409} // namespace
410