1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * Copyright (C) 2013 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
34#include "wtf/Assertions.h"
35#include "wtf/text/CString.h"
36#include "wtf/text/StringBuilder.h"
37#include "wtf/text/WTFString.h"
38#include <gtest/gtest.h>
39
40namespace WTF {
41
42static std::ostream& operator<<(std::ostream& os, const String& string)
43{
44    return os << string.utf8().data();
45}
46
47}
48
49namespace {
50
51static void expectBuilderContent(const String& expected, const StringBuilder& builder)
52{
53    // Not using builder.toString() because it changes internal state of builder.
54    if (builder.is8Bit())
55        EXPECT_EQ(expected, String(builder.characters8(), builder.length()));
56    else
57        EXPECT_EQ(expected, String(builder.characters16(), builder.length()));
58}
59
60void expectEmpty(const StringBuilder& builder)
61{
62    EXPECT_EQ(0U, builder.length());
63    EXPECT_TRUE(builder.isEmpty());
64    EXPECT_EQ(0, builder.characters8());
65}
66
67TEST(StringBuilderTest, DefaultConstructor)
68{
69    StringBuilder builder;
70    expectEmpty(builder);
71}
72
73TEST(StringBuilderTest, Append)
74{
75    StringBuilder builder;
76    builder.append(String("0123456789"));
77    expectBuilderContent("0123456789", builder);
78    builder.append("abcd");
79    expectBuilderContent("0123456789abcd", builder);
80    builder.append("efgh", 3);
81    expectBuilderContent("0123456789abcdefg", builder);
82    builder.append("");
83    expectBuilderContent("0123456789abcdefg", builder);
84    builder.append('#');
85    expectBuilderContent("0123456789abcdefg#", builder);
86
87    builder.toString(); // Test after reifyString().
88    StringBuilder builder1;
89    builder.append("", 0);
90    expectBuilderContent("0123456789abcdefg#", builder);
91    builder1.append(builder.characters8(), builder.length());
92    builder1.append("XYZ");
93    builder.append(builder1.characters8(), builder1.length());
94    expectBuilderContent("0123456789abcdefg#0123456789abcdefg#XYZ", builder);
95
96    StringBuilder builder2;
97    builder2.reserveCapacity(100);
98    builder2.append("xyz");
99    const LChar* characters = builder2.characters8();
100    builder2.append("0123456789");
101    ASSERT_EQ(characters, builder2.characters8());
102
103    // Test appending UChar32 characters to StringBuilder.
104    StringBuilder builderForUChar32Append;
105    UChar32 frakturAChar = 0x1D504;
106    builderForUChar32Append.append(frakturAChar); // The fraktur A is not in the BMP, so it's two UTF-16 code units long.
107    ASSERT_EQ(2U, builderForUChar32Append.length());
108    builderForUChar32Append.append(static_cast<UChar32>('A'));
109    ASSERT_EQ(3U, builderForUChar32Append.length());
110    const UChar resultArray[] = { U16_LEAD(frakturAChar), U16_TRAIL(frakturAChar), 'A' };
111    expectBuilderContent(String(resultArray, WTF_ARRAY_LENGTH(resultArray)), builderForUChar32Append);
112}
113
114TEST(StringBuilderTest, ToString)
115{
116    StringBuilder builder;
117    builder.append("0123456789");
118    String string = builder.toString();
119    ASSERT_EQ(String("0123456789"), string);
120    ASSERT_EQ(string.impl(), builder.toString().impl());
121
122    // Changing the StringBuilder should not affect the original result of toString().
123    builder.append("abcdefghijklmnopqrstuvwxyz");
124    ASSERT_EQ(String("0123456789"), string);
125
126    // Changing the StringBuilder should not affect the original result of toString() in case the capacity is not changed.
127    builder.reserveCapacity(200);
128    string = builder.toString();
129    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
130    builder.append("ABC");
131    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyz"), string);
132
133    // Changing the original result of toString() should not affect the content of the StringBuilder.
134    String string1 = builder.toString();
135    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
136    string1.append("DEF");
137    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), builder.toString());
138    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABCDEF"), string1);
139
140    // Resizing the StringBuilder should not affect the original result of toString().
141    string1 = builder.toString();
142    builder.resize(10);
143    builder.append("###");
144    ASSERT_EQ(String("0123456789abcdefghijklmnopqrstuvwxyzABC"), string1);
145}
146
147TEST(StringBuilderTest, Clear)
148{
149    StringBuilder builder;
150    builder.append("0123456789");
151    builder.clear();
152    expectEmpty(builder);
153}
154
155TEST(StringBuilderTest, Array)
156{
157    StringBuilder builder;
158    builder.append("0123456789");
159    EXPECT_EQ('0', static_cast<char>(builder[0]));
160    EXPECT_EQ('9', static_cast<char>(builder[9]));
161    builder.toString(); // Test after reifyString().
162    EXPECT_EQ('0', static_cast<char>(builder[0]));
163    EXPECT_EQ('9', static_cast<char>(builder[9]));
164}
165
166TEST(StringBuilderTest, Resize)
167{
168    StringBuilder builder;
169    builder.append("0123456789");
170    builder.resize(10);
171    EXPECT_EQ(10U, builder.length());
172    expectBuilderContent("0123456789", builder);
173    builder.resize(8);
174    EXPECT_EQ(8U, builder.length());
175    expectBuilderContent("01234567", builder);
176
177    builder.toString();
178    builder.resize(7);
179    EXPECT_EQ(7U, builder.length());
180    expectBuilderContent("0123456", builder);
181    builder.resize(0);
182    expectEmpty(builder);
183}
184
185TEST(StringBuilderTest, Equal)
186{
187    StringBuilder builder1;
188    StringBuilder builder2;
189    ASSERT_TRUE(builder1 == builder2);
190    ASSERT_TRUE(equal(builder1, static_cast<LChar*>(0), 0));
191    ASSERT_TRUE(builder1 == String());
192    ASSERT_TRUE(String() == builder1);
193    ASSERT_TRUE(builder1 != String("abc"));
194
195    builder1.append("123");
196    builder1.reserveCapacity(32);
197    builder2.append("123");
198    builder1.reserveCapacity(64);
199    ASSERT_TRUE(builder1 == builder2);
200    ASSERT_TRUE(builder1 == String("123"));
201    ASSERT_TRUE(String("123") == builder1);
202
203    builder2.append("456");
204    ASSERT_TRUE(builder1 != builder2);
205    ASSERT_TRUE(builder2 != builder1);
206    ASSERT_TRUE(String("123") != builder2);
207    ASSERT_TRUE(builder2 != String("123"));
208    builder2.toString(); // Test after reifyString().
209    ASSERT_TRUE(builder1 != builder2);
210
211    builder2.resize(3);
212    ASSERT_TRUE(builder1 == builder2);
213
214    builder1.toString(); // Test after reifyString().
215    ASSERT_TRUE(builder1 == builder2);
216}
217
218TEST(StringBuilderTest, CanShrink)
219{
220    StringBuilder builder;
221    builder.reserveCapacity(256);
222    ASSERT_TRUE(builder.canShrink());
223    for (int i = 0; i < 256; i++)
224        builder.append('x');
225    ASSERT_EQ(builder.length(), builder.capacity());
226    ASSERT_FALSE(builder.canShrink());
227}
228
229TEST(StringBuilderTest, ToAtomicString)
230{
231    StringBuilder builder;
232    builder.append("123");
233    AtomicString atomicString = builder.toAtomicString();
234    ASSERT_EQ(String("123"), atomicString);
235
236    builder.reserveCapacity(256);
237    ASSERT_TRUE(builder.canShrink());
238    for (int i = builder.length(); i < 128; i++)
239        builder.append('x');
240    AtomicString atomicString1 = builder.toAtomicString();
241    ASSERT_EQ(128u, atomicString1.length());
242    ASSERT_EQ('x', atomicString1[127]);
243
244    // Later change of builder should not affect the atomic string.
245    for (int i = builder.length(); i < 256; i++)
246        builder.append('x');
247    ASSERT_EQ(128u, atomicString1.length());
248
249    ASSERT_FALSE(builder.canShrink());
250    String string = builder.toString();
251    AtomicString atomicString2 = builder.toAtomicString();
252    // They should share the same StringImpl.
253    ASSERT_EQ(atomicString2.impl(), string.impl());
254}
255
256TEST(StringBuilderTest, ToAtomicStringOnEmpty)
257{
258    { // Default constructed.
259        StringBuilder builder;
260        AtomicString atomicString = builder.toAtomicString();
261        ASSERT_EQ(emptyAtom, atomicString);
262    }
263    { // With capacity.
264        StringBuilder builder;
265        builder.reserveCapacity(64);
266        AtomicString atomicString = builder.toAtomicString();
267        ASSERT_EQ(emptyAtom, atomicString);
268    }
269    { // AtomicString constructed from a null string.
270        StringBuilder builder;
271        builder.append(String());
272        AtomicString atomicString = builder.toAtomicString();
273        ASSERT_EQ(emptyAtom, atomicString);
274    }
275    { // AtomicString constructed from an empty string.
276        StringBuilder builder;
277        builder.append(emptyString());
278        AtomicString atomicString = builder.toAtomicString();
279        ASSERT_EQ(emptyAtom, atomicString);
280    }
281    { // AtomicString constructed from an empty StringBuilder.
282        StringBuilder builder;
283        StringBuilder emptyBuilder;
284        builder.append(emptyBuilder);
285        AtomicString atomicString = builder.toAtomicString();
286        ASSERT_EQ(emptyAtom, atomicString);
287    }
288    { // AtomicString constructed from an empty char* string.
289        StringBuilder builder;
290        builder.append("", 0);
291        AtomicString atomicString = builder.toAtomicString();
292        ASSERT_EQ(emptyAtom, atomicString);
293    }
294    { // Cleared StringBuilder.
295        StringBuilder builder;
296        builder.appendLiteral("WebKit");
297        builder.clear();
298        AtomicString atomicString = builder.toAtomicString();
299        ASSERT_EQ(emptyAtom, atomicString);
300    }
301}
302
303} // namespace
304