View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.text;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotEquals;
24  import static org.junit.jupiter.api.Assertions.assertNotNull;
25  import static org.junit.jupiter.api.Assertions.assertNotSame;
26  import static org.junit.jupiter.api.Assertions.assertNull;
27  import static org.junit.jupiter.api.Assertions.assertSame;
28  import static org.junit.jupiter.api.Assertions.assertThrows;
29  import static org.junit.jupiter.api.Assertions.assertTrue;
30  
31  import java.io.IOException;
32  import java.io.Reader;
33  import java.io.StringReader;
34  import java.io.StringWriter;
35  import java.io.Writer;
36  import java.nio.ByteBuffer;
37  import java.nio.CharBuffer;
38  import java.nio.charset.Charset;
39  import java.util.Arrays;
40  import java.util.Locale;
41  
42  import org.apache.commons.io.output.NullAppendable;
43  import org.apache.commons.lang3.ArrayUtils;
44  import org.apache.commons.lang3.StringUtils;
45  import org.apache.commons.text.matcher.StringMatcher;
46  import org.apache.commons.text.matcher.StringMatcherFactory;
47  import org.junit.jupiter.api.Assumptions;
48  import org.junit.jupiter.api.Test;
49  
50  /**
51   * Tests {@link TextStringBuilder}.
52   */
53  class TextStringBuilderTest {
54  
55      private static final class MockReadable implements Readable {
56  
57          private final CharBuffer src;
58  
59          MockReadable(final String src) {
60              this.src = CharBuffer.wrap(src);
61          }
62  
63          @Override
64          public int read(final CharBuffer cb) throws IOException {
65              return src.read(cb);
66          }
67      }
68  
69      static final StringMatcher A_NUMBER_MATCHER = (buffer, start, bufferStart, bufferEnd) -> {
70          if (buffer[start] == 'A') {
71              start++;
72              if (start < bufferEnd && buffer[start] >= '0' && buffer[start] <= '9') {
73                  return 2;
74              }
75          }
76          return 0;
77      };
78  
79      /**
80       * Clear the string builder and fill up to the specified length.
81       *
82       * @param sb     The string builder
83       * @param length The length
84       */
85      private static void fill(final TextStringBuilder sb, final int length) {
86          sb.clear();
87          // Some initial data.
88          final int limit = Math.min(64, length);
89          for (int i = 0; i < limit; i++) {
90              sb.append(' ');
91          }
92          // Fill by doubling
93          while (sb.length() * 2L <= length) {
94              sb.append(sb);
95          }
96          // Remaining fill
97          sb.append(sb, 0, length - sb.length());
98          assertEquals(length, sb.length(), "Expected the buffer to be full to the given length");
99      }
100 
101     @Test
102     void test_LANG_1131_EqualsWithNullTextStringBuilder() throws Exception {
103         final TextStringBuilder sb = new TextStringBuilder();
104         final TextStringBuilder other = null;
105         assertNotEquals(sb, other);
106     }
107 
108     @Test
109     void testAppendCharBuffer() {
110         final TextStringBuilder sb1 = new TextStringBuilder();
111         final CharBuffer buf = CharBuffer.allocate(10);
112         buf.append("0123456789");
113         buf.flip();
114         sb1.append(buf);
115         assertEquals("0123456789", sb1.toString());
116 
117         final TextStringBuilder sb2 = new TextStringBuilder();
118         sb2.append(buf, 1, 8);
119         assertEquals("12345678", sb2.toString());
120     }
121 
122     @Test
123     void testAppendCharBufferException() throws Exception {
124         final TextStringBuilder sb = new TextStringBuilder("1234567890");
125         final String text = "Test";
126         final CharBuffer buffer = CharBuffer.allocate(sb.size() + text.length());
127         buffer.put(text);
128         buffer.flip();
129         try {
130             sb.append(buffer, -1, 12);
131         } catch (final StringIndexOutOfBoundsException e) {
132             assertEquals("startIndex must be valid", e.getMessage());
133         }
134 
135         try {
136             sb.append(buffer, 0, -1);
137         } catch (final StringIndexOutOfBoundsException e) {
138             assertEquals("length must be valid", e.getMessage());
139         }
140 
141         sb.append(buffer);
142         assertEquals("1234567890Test", sb.toString());
143     }
144 
145     @Test
146     void testAppendCharBufferNull() throws Exception {
147         final TextStringBuilder sb = new TextStringBuilder("1234567890");
148         final CharBuffer buffer = null;
149         sb.append(buffer);
150         assertEquals("1234567890", sb.toString());
151 
152         final TextStringBuilder sb1 = new TextStringBuilder("1234567890");
153         final CharBuffer buffer1 = null;
154         sb.append(buffer1, 0, 0);
155         assertEquals("1234567890", sb1.toString());
156     }
157 
158     @Test
159     void testAppendCharSequence() {
160         final CharSequence obj0 = null;
161         final CharSequence obj1 = new TextStringBuilder("test1");
162         final CharSequence obj2 = new StringBuilder("test2");
163         final CharSequence obj3 = new StringBuffer("test3");
164         final CharBuffer obj4 = CharBuffer.wrap("test4".toCharArray());
165 
166         final TextStringBuilder sb0 = new TextStringBuilder();
167         assertEquals("", sb0.append(obj0).toString());
168 
169         final TextStringBuilder sb1 = new TextStringBuilder();
170         assertEquals("test1", sb1.append(obj1).toString());
171 
172         final TextStringBuilder sb2 = new TextStringBuilder();
173         assertEquals("test2", sb2.append(obj2).toString());
174 
175         final TextStringBuilder sb3 = new TextStringBuilder();
176         assertEquals("test3", sb3.append(obj3).toString());
177 
178         final TextStringBuilder sb4 = new TextStringBuilder();
179         assertEquals("test4", sb4.append(obj4).toString());
180 
181         final TextStringBuilder sb5 = new TextStringBuilder();
182         assertEquals("", sb5.append(obj0, 0, 0).toString());
183     }
184 
185     @Test
186     void testAppendln() {
187         final TextStringBuilder sb1 = new TextStringBuilder();
188         final char ch = 'c';
189         assertEquals("c" + System.lineSeparator(), sb1.appendln(ch).toString());
190     }
191 
192     @Test
193     void testAppendStringBuilderNull() {
194         final TextStringBuilder sb1 = new TextStringBuilder();
195         final StringBuilder b = null;
196         assertEquals("", sb1.append(b).toString());
197 
198         final TextStringBuilder sb2 = new TextStringBuilder();
199         assertEquals("", sb2.append(b, 0, 0).toString());
200     }
201 
202     @Test
203     void testAppendTakingTwoIntsWithIndexOutOfBoundsThrowsStringIndexOutOfBoundsExceptionTwo() {
204         assertThrows(StringIndexOutOfBoundsException.class, () -> {
205             final Charset charset = Charset.defaultCharset();
206             final ByteBuffer byteBuffer = charset.encode("asdf");
207             final CharBuffer charBuffer = charset.decode(byteBuffer);
208 
209             new TextStringBuilder().append(charBuffer, 933, 654);
210         });
211     }
212 
213     @Test
214     void testAppendTakingTwoIntsWithZeroThrowsStringIndexOutOfBoundsException() {
215         assertThrows(StringIndexOutOfBoundsException.class, () -> {
216             final Charset charset = Charset.defaultCharset();
217             final ByteBuffer byteBuffer = charset.encode("end < start");
218             final CharBuffer charBuffer = charset.decode(byteBuffer);
219 
220             new TextStringBuilder(630).append(charBuffer, 0, 630);
221         });
222     }
223 
224     @Test
225     void testAppendToAppendable() throws Exception {
226         final TextStringBuilder sb = new TextStringBuilder("1234567890");
227         final StringWriter writer = new StringWriter();
228         writer.append("Test");
229         sb.appendTo(NullAppendable.INSTANCE);
230         assertEquals("Test", writer.toString());
231     }
232 
233     @Test
234     void testAppendToCharBuffer() throws Exception {
235         final TextStringBuilder sb = new TextStringBuilder("1234567890");
236         final String text = "Test ";
237         final CharBuffer buffer = CharBuffer.allocate(sb.size() + text.length());
238         buffer.put(text);
239 
240         sb.appendTo(buffer);
241 
242         buffer.flip();
243         assertEquals("Test 1234567890", buffer.toString());
244     }
245 
246     @Test
247     void testAppendToStringBuffer() throws Exception {
248         final TextStringBuilder sb = new TextStringBuilder("1234567890");
249         final StringBuffer buffer = new StringBuffer("Test ");
250 
251         sb.appendTo(buffer);
252 
253         assertEquals("Test 1234567890", buffer.toString());
254     }
255 
256     @Test
257     void testAppendToStringBuilder() throws Exception {
258         final TextStringBuilder sb = new TextStringBuilder("1234567890");
259         final StringBuilder builder = new StringBuilder("Test ");
260 
261         sb.appendTo(builder);
262 
263         assertEquals("Test 1234567890", builder.toString());
264     }
265 
266     @Test
267     void testAppendToWriter() throws Exception {
268         final TextStringBuilder sb = new TextStringBuilder("1234567890");
269         final StringWriter writer = new StringWriter();
270         writer.append("Test ");
271 
272         sb.appendTo(writer);
273 
274         assertEquals("Test 1234567890", writer.toString());
275     }
276 
277     @Test
278     void testAsBuilder() {
279         final TextStringBuilder sb = new TextStringBuilder().appendAll("Lorem", " ", "ipsum", " ", "dolor");
280         assertEquals(sb.toString(), sb.build());
281     }
282 
283     @Test
284     void testAsReader() throws Exception {
285         final TextStringBuilder sb = new TextStringBuilder("some text");
286         try (Reader reader = sb.asReader()) {
287             assertTrue(reader.ready());
288             final char[] buf = new char[40];
289             assertEquals(9, reader.read(buf));
290             assertEquals("some text", new String(buf, 0, 9));
291 
292             assertEquals(-1, reader.read());
293             assertFalse(reader.ready());
294             assertEquals(0, reader.skip(2));
295             assertEquals(0, reader.skip(-1));
296 
297             assertTrue(reader.markSupported());
298         }
299         try (Reader reader = sb.asReader()) {
300             assertEquals('s', reader.read());
301             reader.mark(-1);
302             final char[] array = new char[3];
303             assertEquals(3, reader.read(array, 0, 3));
304             assertEquals('o', array[0]);
305             assertEquals('m', array[1]);
306             assertEquals('e', array[2]);
307             reader.reset();
308             assertEquals(1, reader.read(array, 1, 1));
309             assertEquals('o', array[0]);
310             assertEquals('o', array[1]);
311             assertEquals('e', array[2]);
312             assertEquals(2, reader.skip(2));
313             assertEquals(' ', reader.read());
314 
315             assertTrue(reader.ready());
316             reader.close();
317             assertTrue(reader.ready());
318         }
319         try (Reader reader = sb.asReader()) {
320             final char[] array = new char[3];
321             assertThrows(IndexOutOfBoundsException.class, () -> reader.read(array, -1, 0));
322             assertThrows(IndexOutOfBoundsException.class, () -> reader.read(array, 0, -1));
323             assertThrows(IndexOutOfBoundsException.class, () -> reader.read(array, 100, 1));
324             assertThrows(IndexOutOfBoundsException.class, () -> reader.read(array, 0, 100));
325             assertThrows(IndexOutOfBoundsException.class, () -> reader.read(array, Integer.MAX_VALUE, Integer.MAX_VALUE));
326 
327             assertEquals(0, reader.read(array, 0, 0));
328             assertEquals(0, array[0]);
329             assertEquals(0, array[1]);
330             assertEquals(0, array[2]);
331 
332             reader.skip(9);
333             assertEquals(-1, reader.read(array, 0, 1));
334 
335             reader.reset();
336             final char[] array2 = new char[30];
337             assertEquals(9, reader.read(array2, 0, 30));
338         }
339     }
340 
341     @Test
342     void testAsSupplier() {
343         final TextStringBuilder sb = new TextStringBuilder().appendAll("Lorem", " ", "ipsum", " ", "dolor");
344         assertEquals(sb.toString(), sb.get());
345     }
346 
347     @Test
348     void testAsTokenizer() throws Exception {
349         // from Javadoc
350         final TextStringBuilder b = new TextStringBuilder();
351         b.append("a b ");
352         final StringTokenizer t = b.asTokenizer();
353 
354         final String[] tokens1 = t.getTokenArray();
355         assertEquals(2, tokens1.length);
356         assertEquals("a", tokens1[0]);
357         assertEquals("b", tokens1[1]);
358         assertEquals(2, t.size());
359 
360         b.append("c d ");
361         final String[] tokens2 = t.getTokenArray();
362         assertEquals(2, tokens2.length);
363         assertEquals("a", tokens2[0]);
364         assertEquals("b", tokens2[1]);
365         assertEquals(2, t.size());
366         assertEquals("a", t.next());
367         assertEquals("b", t.next());
368 
369         t.reset();
370         final String[] tokens3 = t.getTokenArray();
371         assertEquals(4, tokens3.length);
372         assertEquals("a", tokens3[0]);
373         assertEquals("b", tokens3[1]);
374         assertEquals("c", tokens3[2]);
375         assertEquals("d", tokens3[3]);
376         assertEquals(4, t.size());
377         assertEquals("a", t.next());
378         assertEquals("b", t.next());
379         assertEquals("c", t.next());
380         assertEquals("d", t.next());
381 
382         assertEquals("a b c d ", t.getContent());
383     }
384 
385     @Test
386     void testAsWriter() throws Exception {
387         final TextStringBuilder sb = new TextStringBuilder("base");
388         try (Writer writer = sb.asWriter()) {
389 
390             writer.write('l');
391             assertEquals("basel", sb.toString());
392 
393             writer.write(new char[] { 'i', 'n' });
394             assertEquals("baselin", sb.toString());
395 
396             writer.write(new char[] { 'n', 'e', 'r' }, 1, 2);
397             assertEquals("baseliner", sb.toString());
398 
399             writer.write(" rout");
400             assertEquals("baseliner rout", sb.toString());
401 
402             writer.write("ping that server", 1, 3);
403             assertEquals("baseliner routing", sb.toString());
404 
405             writer.flush(); // no effect
406             assertEquals("baseliner routing", sb.toString());
407 
408             writer.close(); // no effect
409             assertEquals("baseliner routing", sb.toString());
410 
411             writer.write(" hi"); // works after close
412             assertEquals("baseliner routing hi", sb.toString());
413 
414             sb.setLength(4); // mix and match
415             writer.write('d');
416             assertEquals("based", sb.toString());
417         }
418     }
419 
420     @Test
421     void testCapacity() {
422         final TextStringBuilder sb = new TextStringBuilder();
423         assertEquals(sb.getBuffer().length, sb.capacity());
424 
425         sb.append("HelloWorldHelloWorldHelloWorldHelloWorld");
426         assertEquals(sb.getBuffer().length, sb.capacity());
427     }
428 
429     @Test
430     void testCapacityAndLength() {
431         final TextStringBuilder sb = new TextStringBuilder();
432         assertEquals(32, sb.capacity());
433         assertEquals(0, sb.length());
434         assertEquals(0, sb.size());
435         assertTrue(sb.isEmpty());
436 
437         sb.minimizeCapacity();
438         assertEquals(0, sb.capacity());
439         assertEquals(0, sb.length());
440         assertEquals(0, sb.size());
441         assertTrue(sb.isEmpty());
442 
443         sb.ensureCapacity(32);
444         assertTrue(sb.capacity() >= 32);
445         assertEquals(0, sb.length());
446         assertEquals(0, sb.size());
447         assertTrue(sb.isEmpty());
448 
449         sb.append("foo");
450         assertTrue(sb.capacity() >= 32);
451         assertEquals(3, sb.length());
452         assertEquals(3, sb.size());
453         assertFalse(sb.isEmpty());
454 
455         sb.clear();
456         assertTrue(sb.capacity() >= 32);
457         assertEquals(0, sb.length());
458         assertEquals(0, sb.size());
459         assertTrue(sb.isEmpty());
460 
461         sb.append("123456789012345678901234567890123");
462         assertTrue(sb.capacity() > 32);
463         assertEquals(33, sb.length());
464         assertEquals(33, sb.size());
465         assertFalse(sb.isEmpty());
466 
467         sb.ensureCapacity(16);
468         assertTrue(sb.capacity() > 16);
469         assertEquals(33, sb.length());
470         assertEquals(33, sb.size());
471         assertFalse(sb.isEmpty());
472 
473         sb.minimizeCapacity();
474         assertEquals(33, sb.capacity());
475         assertEquals(33, sb.length());
476         assertEquals(33, sb.size());
477         assertFalse(sb.isEmpty());
478 
479         assertThrows(IndexOutOfBoundsException.class, () -> sb.setLength(-1));
480 
481         sb.setLength(33);
482         assertEquals(33, sb.capacity());
483         assertEquals(33, sb.length());
484         assertEquals(33, sb.size());
485         assertFalse(sb.isEmpty());
486 
487         sb.setLength(16);
488         assertTrue(sb.capacity() >= 16);
489         assertEquals(16, sb.length());
490         assertEquals(16, sb.size());
491         assertEquals("1234567890123456", sb.toString());
492         assertFalse(sb.isEmpty());
493 
494         sb.setLength(32);
495         assertTrue(sb.capacity() >= 32);
496         assertEquals(32, sb.length());
497         assertEquals(32, sb.size());
498         assertEquals("1234567890123456\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sb.toString());
499         assertFalse(sb.isEmpty());
500 
501         sb.setLength(0);
502         assertTrue(sb.capacity() >= 32);
503         assertEquals(0, sb.length());
504         assertEquals(0, sb.size());
505         assertTrue(sb.isEmpty());
506     }
507 
508     @Test
509     void testChaining() {
510         final TextStringBuilder sb = new TextStringBuilder();
511         assertSame(sb, sb.setNewLineText(null));
512         assertSame(sb, sb.setNullText(null));
513         assertSame(sb, sb.setLength(1));
514         assertSame(sb, sb.setCharAt(0, 'a'));
515         assertSame(sb, sb.ensureCapacity(0));
516         assertSame(sb, sb.minimizeCapacity());
517         assertSame(sb, sb.clear());
518         assertSame(sb, sb.reverse());
519         assertSame(sb, sb.trim());
520     }
521 
522     @Test
523     void testCharAt() {
524         final TextStringBuilder sb = new TextStringBuilder();
525         assertThrows(IndexOutOfBoundsException.class, () -> sb.charAt(0));
526         assertThrows(IndexOutOfBoundsException.class, () -> sb.charAt(-1));
527         sb.append("foo");
528         assertEquals('f', sb.charAt(0));
529         assertEquals('o', sb.charAt(1));
530         assertEquals('o', sb.charAt(2));
531         assertThrows(IndexOutOfBoundsException.class, () -> sb.charAt(-1));
532         assertThrows(IndexOutOfBoundsException.class, () -> sb.charAt(3));
533     }
534 
535     @Test
536     void testClear() {
537         final TextStringBuilder sb = new TextStringBuilder();
538         sb.append("Hello");
539         sb.clear();
540         assertEquals(0, sb.length());
541         assertTrue(sb.getBuffer().length >= 5);
542     }
543 
544     @Test
545     void testConstructorCharSequence() {
546         final CharBuffer str = CharBuffer.wrap("A");
547         final int length = str.length();
548         final TextStringBuilder sb = new TextStringBuilder(str);
549         assertEquals(TextStringBuilder.CAPACITY + length, sb.capacity());
550         assertEquals(length, sb.toCharArray().length);
551     }
552 
553     @Test
554     void testConstructorCharSequenceNull() {
555         final TextStringBuilder sb = new TextStringBuilder((CharSequence) null);
556         assertEquals(TextStringBuilder.CAPACITY, sb.capacity());
557         assertEquals(0, sb.toCharArray().length);
558     }
559 
560     @Test
561     void testConstructorDefault() {
562         final TextStringBuilder sb = new TextStringBuilder();
563         assertEquals(TextStringBuilder.CAPACITY, sb.capacity());
564         assertEquals(0, sb.toCharArray().length);
565     }
566 
567     @Test
568     void testConstructors() {
569         final TextStringBuilder sb0 = new TextStringBuilder();
570         assertEquals(32, sb0.capacity());
571         assertEquals(0, sb0.length());
572         assertEquals(0, sb0.size());
573 
574         final TextStringBuilder sb1 = new TextStringBuilder(32);
575         assertEquals(32, sb1.capacity());
576         assertEquals(0, sb1.length());
577         assertEquals(0, sb1.size());
578 
579         final TextStringBuilder sb2 = new TextStringBuilder(0);
580         assertEquals(32, sb2.capacity());
581         assertEquals(0, sb2.length());
582         assertEquals(0, sb2.size());
583 
584         final TextStringBuilder sb3 = new TextStringBuilder(-1);
585         assertEquals(32, sb3.capacity());
586         assertEquals(0, sb3.length());
587         assertEquals(0, sb3.size());
588 
589         final TextStringBuilder sb4 = new TextStringBuilder(1);
590         assertEquals(1, sb4.capacity());
591         assertEquals(0, sb4.length());
592         assertEquals(0, sb4.size());
593 
594         final TextStringBuilder sb5 = new TextStringBuilder((String) null);
595         assertEquals(32, sb5.capacity());
596         assertEquals(0, sb5.length());
597         assertEquals(0, sb5.size());
598 
599         final TextStringBuilder sb6 = new TextStringBuilder("");
600         assertEquals(32, sb6.capacity());
601         assertEquals(0, sb6.length());
602         assertEquals(0, sb6.size());
603 
604         final TextStringBuilder sb7 = new TextStringBuilder("foo");
605         assertEquals(35, sb7.capacity());
606         assertEquals(3, sb7.length());
607         assertEquals(3, sb7.size());
608     }
609 
610     @Test
611     void testConstructorString() {
612         final String str = "A";
613         final int length = str.length();
614         final TextStringBuilder sb = new TextStringBuilder(str);
615         assertEquals(TextStringBuilder.CAPACITY + length, sb.capacity());
616         assertEquals(length, sb.toCharArray().length);
617     }
618 
619     @Test
620     void testContains_char() {
621         final TextStringBuilder sb = new TextStringBuilder("abcdefghijklmnopqrstuvwxyz");
622         assertTrue(sb.contains('a'));
623         assertTrue(sb.contains('o'));
624         assertTrue(sb.contains('z'));
625         assertFalse(sb.contains('1'));
626     }
627 
628     @Test
629     void testContains_String() {
630         final TextStringBuilder sb = new TextStringBuilder("abcdefghijklmnopqrstuvwxyz");
631         assertTrue(sb.contains("a"));
632         assertTrue(sb.contains("pq"));
633         assertTrue(sb.contains("z"));
634         assertFalse(sb.contains("zyx"));
635         assertFalse(sb.contains((String) null));
636     }
637 
638     @Test
639     void testContains_StringMatcher() {
640         TextStringBuilder sb = new TextStringBuilder("abcdefghijklmnopqrstuvwxyz");
641         assertTrue(sb.contains(StringMatcherFactory.INSTANCE.charMatcher('a')));
642         assertTrue(sb.contains(StringMatcherFactory.INSTANCE.stringMatcher("pq")));
643         assertTrue(sb.contains(StringMatcherFactory.INSTANCE.charMatcher('z')));
644         assertFalse(sb.contains(StringMatcherFactory.INSTANCE.stringMatcher("zy")));
645         assertFalse(sb.contains((StringMatcher) null));
646 
647         sb = new TextStringBuilder();
648         assertFalse(sb.contains(A_NUMBER_MATCHER));
649         sb.append("B A1 C");
650         assertTrue(sb.contains(A_NUMBER_MATCHER));
651     }
652 
653     @Test
654     void testDeleteAll_char() {
655         TextStringBuilder sb = new TextStringBuilder("abcbccba");
656         sb.deleteAll('X');
657         assertEquals("abcbccba", sb.toString());
658         sb.deleteAll('a');
659         assertEquals("bcbccb", sb.toString());
660         sb.deleteAll('c');
661         assertEquals("bbb", sb.toString());
662         sb.deleteAll('b');
663         assertEquals("", sb.toString());
664 
665         sb = new TextStringBuilder("");
666         sb.deleteAll('b');
667         assertEquals("", sb.toString());
668     }
669 
670     @Test
671     void testDeleteAll_String() {
672         TextStringBuilder sb = new TextStringBuilder("abcbccba");
673         sb.deleteAll((String) null);
674         assertEquals("abcbccba", sb.toString());
675         sb.deleteAll("");
676         assertEquals("abcbccba", sb.toString());
677 
678         sb.deleteAll("X");
679         assertEquals("abcbccba", sb.toString());
680         sb.deleteAll("a");
681         assertEquals("bcbccb", sb.toString());
682         sb.deleteAll("c");
683         assertEquals("bbb", sb.toString());
684         sb.deleteAll("b");
685         assertEquals("", sb.toString());
686 
687         sb = new TextStringBuilder("abcbccba");
688         sb.deleteAll("bc");
689         assertEquals("acba", sb.toString());
690 
691         sb = new TextStringBuilder("");
692         sb.deleteAll("bc");
693         assertEquals("", sb.toString());
694     }
695 
696     @Test
697     void testDeleteAll_StringMatcher() {
698         TextStringBuilder sb = new TextStringBuilder("A0xA1A2yA3");
699         sb.deleteAll((StringMatcher) null);
700         assertEquals("A0xA1A2yA3", sb.toString());
701         sb.deleteAll(A_NUMBER_MATCHER);
702         assertEquals("xy", sb.toString());
703 
704         sb = new TextStringBuilder("Ax1");
705         sb.deleteAll(A_NUMBER_MATCHER);
706         assertEquals("Ax1", sb.toString());
707 
708         sb = new TextStringBuilder("");
709         sb.deleteAll(A_NUMBER_MATCHER);
710         assertEquals("", sb.toString());
711     }
712 
713     @Test
714     void testDeleteCharAt() {
715         final String str = "abc";
716         //
717         final TextStringBuilder sb1 = new TextStringBuilder(str);
718         sb1.deleteCharAt(0);
719         assertEquals("bc", sb1.toString());
720         //
721         final TextStringBuilder sb2 = new TextStringBuilder(str);
722         sb2.deleteCharAt(str.length() - 1);
723         assertEquals("ab", sb2.toString());
724         //
725         final TextStringBuilder sb3 = new TextStringBuilder(str);
726         assertThrows(IndexOutOfBoundsException.class, () -> sb3.deleteCharAt(str.length()));
727         assertThrows(IndexOutOfBoundsException.class, () -> sb3.deleteCharAt(1000));
728     }
729 
730     @Test
731     void testDeleteCharAtWithNegative() {
732         assertThrows(StringIndexOutOfBoundsException.class, () -> new TextStringBuilder().deleteCharAt(-1258));
733     }
734 
735     @Test
736     void testDeleteFirst_char() {
737         TextStringBuilder sb = new TextStringBuilder("abcba");
738         sb.deleteFirst('X');
739         assertEquals("abcba", sb.toString());
740         sb.deleteFirst('a');
741         assertEquals("bcba", sb.toString());
742         sb.deleteFirst('c');
743         assertEquals("bba", sb.toString());
744         sb.deleteFirst('b');
745         assertEquals("ba", sb.toString());
746 
747         sb = new TextStringBuilder("");
748         sb.deleteFirst('b');
749         assertEquals("", sb.toString());
750     }
751 
752     @Test
753     void testDeleteFirst_String() {
754         TextStringBuilder sb = new TextStringBuilder("abcbccba");
755         sb.deleteFirst((String) null);
756         assertEquals("abcbccba", sb.toString());
757         sb.deleteFirst("");
758         assertEquals("abcbccba", sb.toString());
759 
760         sb.deleteFirst("X");
761         assertEquals("abcbccba", sb.toString());
762         sb.deleteFirst("a");
763         assertEquals("bcbccba", sb.toString());
764         sb.deleteFirst("c");
765         assertEquals("bbccba", sb.toString());
766         sb.deleteFirst("b");
767         assertEquals("bccba", sb.toString());
768 
769         sb = new TextStringBuilder("abcbccba");
770         sb.deleteFirst("bc");
771         assertEquals("abccba", sb.toString());
772 
773         sb = new TextStringBuilder("");
774         sb.deleteFirst("bc");
775         assertEquals("", sb.toString());
776     }
777 
778     @Test
779     void testDeleteFirst_StringMatcher() {
780         TextStringBuilder sb = new TextStringBuilder("A0xA1A2yA3");
781         sb.deleteFirst((StringMatcher) null);
782         assertEquals("A0xA1A2yA3", sb.toString());
783         sb.deleteFirst(A_NUMBER_MATCHER);
784         assertEquals("xA1A2yA3", sb.toString());
785 
786         sb = new TextStringBuilder("Ax1");
787         sb.deleteFirst(A_NUMBER_MATCHER);
788         assertEquals("Ax1", sb.toString());
789 
790         sb = new TextStringBuilder("");
791         sb.deleteFirst(A_NUMBER_MATCHER);
792         assertEquals("", sb.toString());
793     }
794 
795     @Test
796     void testDeleteIntInt() {
797         final TextStringBuilder sb = new TextStringBuilder("abc");
798         sb.delete(0, 1);
799         assertEquals("bc", sb.toString());
800         sb.delete(1, 2);
801         assertEquals("b", sb.toString());
802         sb.delete(0, 1);
803         assertEquals("", sb.toString());
804         sb.delete(0, 1000);
805         assertEquals("", sb.toString());
806 
807         assertThrows(IndexOutOfBoundsException.class, () -> sb.delete(1, 2));
808         assertThrows(IndexOutOfBoundsException.class, () -> sb.delete(-1, 1));
809 
810         assertThrows(IndexOutOfBoundsException.class, () -> new TextStringBuilder("anything").delete(2, 1));
811     }
812 
813     @Test
814     void testDrainChar() {
815         final String str = "abc";
816         //
817         final TextStringBuilder sb1 = new TextStringBuilder(str);
818         assertEquals('a', sb1.drainChar(0));
819         assertEquals("bc", sb1.toString());
820         //
821         final TextStringBuilder sb2 = new TextStringBuilder(str);
822         assertEquals('c', sb2.drainChar(str.length() - 1));
823         assertEquals("ab", sb2.toString());
824         //
825         final TextStringBuilder sb3 = new TextStringBuilder(str);
826         assertThrows(IndexOutOfBoundsException.class, () -> sb3.drainChar(str.length()));
827         assertThrows(IndexOutOfBoundsException.class, () -> sb3.drainChar(1000));
828     }
829 
830     @Test
831     void testDrainCharsIntIntCharArrayInt() {
832         final String data = "junit";
833         final char[] array = new char[data.length()];
834         final TextStringBuilder sb = new TextStringBuilder();
835         // empty buffer
836         assertEquals(0, sb.drainChars(0, 5, array, 1));
837         assertEquals(0, sb.drainChars(0, 5, ArrayUtils.EMPTY_CHAR_ARRAY, 1));
838         // empty buffer, 0 length request
839         assertEquals(0, sb.drainChars(5, 5, array, 1));
840 
841         sb.append(data);
842         assertEquals(0, sb.drainChars(5, 5, array, 1));
843         assertEquals(5, sb.drainChars(0, 5, array, 0));
844         assertArrayEquals(data.toCharArray(), array);
845 
846         final char[] b = new char[5];
847         sb.set(data);
848         assertEquals(2, sb.drainChars(0, 2, b, 3));
849         assertArrayEquals(new char[] { 0, 0, 0, 'j', 'u' }, b);
850 
851         assertThrows(IndexOutOfBoundsException.class, () -> sb.drainChars(-1, 0, b, 0));
852         assertThrows(IndexOutOfBoundsException.class, () -> sb.drainChars(0, -1, array, 0));
853         assertThrows(IndexOutOfBoundsException.class, () -> sb.drainChars(4, 2, array, 0));
854 
855         // get and delete it all.
856         sb.set(data);
857         assertEquals(data.length(), sb.drainChars(0, sb.length() + 1, array, 0));
858         assertArrayEquals(data.toCharArray(), array);
859 
860         // get and delete more than there is
861         sb.set(data);
862         assertEquals(data.length(), sb.drainChars(0, sb.length() + 10, array, 0));
863         assertArrayEquals(data.toCharArray(), array);
864 
865         // get and delete more than can fit
866         sb.set(data);
867         int targetIndex = 1;
868         Arrays.fill(array, '-');
869         assertEquals(data.length() - targetIndex, sb.drainChars(0, sb.length() + 10, array, targetIndex));
870         assertArrayEquals("-juni".toCharArray(), array);
871 
872         // get and delete more than can fit
873         sb.set(data);
874         Arrays.fill(array, '-');
875         assertEquals(data.length() - targetIndex, sb.drainChars(0, sb.length() + 1, array, targetIndex));
876         assertArrayEquals("-juni".toCharArray(), array);
877 
878         // get and delete more than can fit
879         sb.set(data);
880         targetIndex = 2;
881         Arrays.fill(array, '-');
882         assertEquals(data.length() - targetIndex, sb.drainChars(0, sb.length() + 1, array, targetIndex));
883         assertArrayEquals("--jun".toCharArray(), array);
884     }
885 
886     @Test
887     void testEndsWith() {
888         final TextStringBuilder sb = new TextStringBuilder();
889         assertFalse(sb.endsWith("a"));
890         assertFalse(sb.endsWith("c"));
891         assertTrue(sb.endsWith(""));
892         assertFalse(sb.endsWith(null));
893         sb.append("abc");
894         assertTrue(sb.endsWith("c"));
895         assertTrue(sb.endsWith("bc"));
896         assertTrue(sb.endsWith("abc"));
897         assertFalse(sb.endsWith("cba"));
898         assertFalse(sb.endsWith("abcd"));
899         assertFalse(sb.endsWith(" abc"));
900         assertFalse(sb.endsWith("abc "));
901     }
902 
903     @Test
904     void testEnsureCapacity() {
905         final TextStringBuilder sb = new TextStringBuilder();
906         sb.ensureCapacity(2);
907         assertTrue(sb.capacity() >= 2);
908 
909         sb.ensureCapacity(-1);
910         assertTrue(sb.capacity() >= 0);
911 
912         sb.append("HelloWorld");
913         sb.ensureCapacity(40);
914         assertTrue(sb.capacity() >= 40);
915     }
916 
917     @Test
918     void testEnsureCapacityOutOfMemoryError() {
919         final TextStringBuilder sb = new TextStringBuilder();
920         // Should not be a NegativeArraySizeException
921         sb.ensureCapacity(Integer.MIN_VALUE);
922         sb.ensureCapacity(-1);
923         sb.ensureCapacity(0);
924         // Might fail in a CI:
925         // sb.ensureCapacity(Integer.MAX_VALUE / 2);
926         sb.ensureCapacity(10_000);
927         assertThrows(OutOfMemoryError.class, () -> sb.ensureCapacity(Integer.MAX_VALUE));
928     }
929 
930     @Test
931     void testEquals() {
932         final TextStringBuilder sb1 = new TextStringBuilder(50);
933         final TextStringBuilder sb2 = new TextStringBuilder(100);
934         assertEquals(sb1, sb2);
935         assertEquals(sb1, sb1);
936         assertEquals(sb2, sb2);
937         assertEquals(sb1, sb2);
938 
939         sb1.append("abc");
940         assertNotEquals(sb1, sb2);
941         assertNotEquals(sb1, sb2);
942 
943         sb2.append("ABC");
944         assertNotEquals(sb1, sb2);
945         assertNotEquals(sb1, sb2);
946 
947         sb2.set("abc");
948         assertEquals(sb1, sb2);
949         assertEquals(sb1, sb2);
950 
951         assertNotEquals(sb1, Integer.valueOf(1));
952         assertNotEquals("abc", sb1);
953     }
954 
955     @Test
956     void testEqualsIgnoreCase() {
957         final TextStringBuilder sb1 = new TextStringBuilder();
958         final TextStringBuilder sb2 = new TextStringBuilder();
959         assertTrue(sb1.equalsIgnoreCase(sb1));
960         assertTrue(sb1.equalsIgnoreCase(sb2));
961         assertTrue(sb2.equalsIgnoreCase(sb2));
962 
963         sb1.append("abc");
964         assertFalse(sb1.equalsIgnoreCase(sb2));
965 
966         sb2.append("ABC");
967         assertTrue(sb1.equalsIgnoreCase(sb2));
968 
969         sb2.set("abc");
970         assertTrue(sb1.equalsIgnoreCase(sb2));
971         assertTrue(sb1.equalsIgnoreCase(sb1));
972         assertTrue(sb2.equalsIgnoreCase(sb2));
973 
974         sb2.set("aBc");
975         assertTrue(sb1.equalsIgnoreCase(sb2));
976 
977         // characters that only fold together via toLowerCase, matching String.equalsIgnoreCase
978         sb1.set("\u004B"); // LATIN CAPITAL LETTER K
979         sb2.set("\u212A"); // KELVIN SIGN, lower-cases to 'k'
980         assertTrue(sb1.equalsIgnoreCase(sb2));
981         assertTrue(sb2.equalsIgnoreCase(sb1));
982 
983         sb1.set("\u00E5"); // LATIN SMALL LETTER A WITH RING ABOVE
984         sb2.set("\u212B"); // ANGSTROM SIGN, lower-cases to the same
985         assertTrue(sb1.equalsIgnoreCase(sb2));
986 
987         final Locale turkish = Locale.forLanguageTag("tr");
988         assertTrue(new TextStringBuilder("title").equalsIgnoreCase(new TextStringBuilder("title".toLowerCase(turkish))));
989         assertTrue(new TextStringBuilder("title").equalsIgnoreCase(new TextStringBuilder("TITLE".toLowerCase(turkish))));
990         assertTrue(new TextStringBuilder("TITLE").equalsIgnoreCase(new TextStringBuilder("TITLE".toLowerCase(turkish))));
991         assertTrue(new TextStringBuilder("TITLE").equalsIgnoreCase(new TextStringBuilder("title".toLowerCase(turkish))));
992         //
993         // assertTrue(new TextStringBuilder("title").equalsIgnoreCase(new
994         // TextStringBuilder("title".toUpperCase(turkish))));
995         // assertTrue(new TextStringBuilder("title").equalsIgnoreCase(new
996         // TextStringBuilder("TITLE".toUpperCase(turkish))));
997         assertTrue(new TextStringBuilder("TITLE").equalsIgnoreCase(new TextStringBuilder("TITLE".toUpperCase(turkish))));
998         // assertTrue(new TextStringBuilder("TITLE").equalsIgnoreCase(new
999         // TextStringBuilder("title".toUpperCase(turkish))));
1000     }
1001 
1002     @Test
1003     void testErrorMessageShowsCorrectVariable() {
1004         final TextStringBuilder sb = new TextStringBuilder("Hello");
1005         final char[] chars = { 'a', 'b', 'c' };
1006         StringIndexOutOfBoundsException ex = assertThrows(StringIndexOutOfBoundsException.class, () -> sb.append(chars, 1, 4));
1007         assertTrue(ex.getMessage().contains("length: 4"));
1008         ex = assertThrows(StringIndexOutOfBoundsException.class, () -> sb.append(chars, 7, 3));
1009         assertTrue(ex.getMessage().contains("startIndex: 7"));
1010     }
1011 
1012     @Test
1013     void testGetChars() {
1014         final TextStringBuilder sb = new TextStringBuilder();
1015 
1016         char[] input = new char[10];
1017         char[] a = sb.getChars(input);
1018         assertSame(input, a);
1019         assertArrayEquals(new char[10], a);
1020 
1021         sb.append("junit");
1022         a = sb.getChars(input);
1023         assertSame(input, a);
1024         assertArrayEquals(new char[] { 'j', 'u', 'n', 'i', 't', 0, 0, 0, 0, 0 }, a);
1025 
1026         a = sb.getChars(null);
1027         assertNotSame(input, a);
1028         assertEquals(5, a.length);
1029         assertArrayEquals("junit".toCharArray(), a);
1030 
1031         input = new char[5];
1032         a = sb.getChars(input);
1033         assertSame(input, a);
1034 
1035         input = new char[4];
1036         a = sb.getChars(input);
1037         assertNotSame(input, a);
1038     }
1039 
1040     @Test
1041     void testGetCharsIntIntCharArrayInt() {
1042         final TextStringBuilder sb = new TextStringBuilder();
1043 
1044         final char[] array1 = new char[1];
1045         sb.getChars(0, 0, array1, 0);
1046         assertArrayEquals(new char[1], array1);
1047 
1048         sb.set("junit");
1049         sb.getChars(0, 1, array1, 0);
1050         assertArrayEquals(new char[] { 'j' }, array1);
1051 
1052         final char[] array = new char[5];
1053         sb.getChars(0, 5, array, 0);
1054         assertArrayEquals(new char[] { 'j', 'u', 'n', 'i', 't' }, array);
1055 
1056         final char[] b = new char[5];
1057         sb.getChars(0, 2, b, 3);
1058         assertArrayEquals(new char[] { 0, 0, 0, 'j', 'u' }, b);
1059 
1060         assertThrows(IndexOutOfBoundsException.class, () -> sb.getChars(-1, 0, b, 0));
1061         assertThrows(IndexOutOfBoundsException.class, () -> sb.getChars(0, -1, array, 0));
1062         assertThrows(IndexOutOfBoundsException.class, () -> sb.getChars(0, 20, array, 0));
1063         assertThrows(IndexOutOfBoundsException.class, () -> sb.getChars(4, 2, array, 0));
1064     }
1065 
1066     @Test
1067     void testGetSetNewLineText() {
1068         final TextStringBuilder sb = new TextStringBuilder();
1069         assertNull(sb.getNewLineText());
1070 
1071         sb.setNewLineText("#");
1072         assertEquals("#", sb.getNewLineText());
1073 
1074         sb.setNewLineText("");
1075         assertEquals("", sb.getNewLineText());
1076 
1077         sb.setNewLineText((String) null);
1078         assertNull(sb.getNewLineText());
1079     }
1080 
1081     @Test
1082     void testGetSetNullText() {
1083         final TextStringBuilder sb = new TextStringBuilder();
1084         assertNull(sb.getNullText());
1085 
1086         sb.setNullText("null");
1087         assertEquals("null", sb.getNullText());
1088 
1089         sb.setNullText("");
1090         assertNull(sb.getNullText());
1091 
1092         sb.setNullText("NULL");
1093         assertEquals("NULL", sb.getNullText());
1094 
1095         sb.setNullText((String) null);
1096         assertNull(sb.getNullText());
1097     }
1098 
1099     @Test
1100     void testHashCode() {
1101         final TextStringBuilder sb = new TextStringBuilder();
1102         final int hc1a = sb.hashCode();
1103         final int hc1b = sb.hashCode();
1104         assertEquals(hc1a, hc1b);
1105 
1106         // following TEXT-211 : the hash code of the buffer may not be equals to the hash code of the TextStringBuilder itself
1107         final int emptyHc = Arrays.hashCode(sb.getBuffer());
1108         assertNotEquals(emptyHc, hc1a);
1109 
1110         sb.append("abc");
1111         final int hc2a = sb.hashCode();
1112         final int hc2b = sb.hashCode();
1113         assertEquals(hc2a, hc2b);
1114 
1115         final TextStringBuilder sb2 = new TextStringBuilder(100);
1116         final TextStringBuilder sb3 = new TextStringBuilder(10);
1117         final int hc2 = sb2.hashCode();
1118         final int hc3 = sb3.hashCode();
1119         assertEquals(hc2, hc3);
1120 
1121         sb2.append("abc");
1122         sb3.append("abc");
1123         final int hc2b2 = sb2.hashCode();
1124         final int hc3b2 = sb3.hashCode();
1125         assertEquals(hc2b2, hc3b2);
1126     }
1127 
1128     @Test
1129     void testIndexOf_char() {
1130         final TextStringBuilder sb = new TextStringBuilder("abab");
1131         assertEquals(0, sb.indexOf('a'));
1132 
1133         // should work like String#indexOf
1134         assertEquals("abab".indexOf('a'), sb.indexOf('a'));
1135 
1136         assertEquals(1, sb.indexOf('b'));
1137         assertEquals("abab".indexOf('b'), sb.indexOf('b'));
1138 
1139         assertEquals(-1, sb.indexOf('z'));
1140     }
1141 
1142     @Test
1143     void testIndexOf_char_int() {
1144         TextStringBuilder sb = new TextStringBuilder("abab");
1145         assertEquals(0, sb.indexOf('a', -1));
1146         assertEquals(0, sb.indexOf('a', 0));
1147         assertEquals(2, sb.indexOf('a', 1));
1148         assertEquals(-1, sb.indexOf('a', 4));
1149         assertEquals(-1, sb.indexOf('a', 5));
1150 
1151         // should work like String#indexOf
1152         assertEquals("abab".indexOf('a', 1), sb.indexOf('a', 1));
1153 
1154         assertEquals(3, sb.indexOf('b', 2));
1155         assertEquals("abab".indexOf('b', 2), sb.indexOf('b', 2));
1156 
1157         assertEquals(-1, sb.indexOf('z', 2));
1158 
1159         sb = new TextStringBuilder("xyzabc");
1160         assertEquals(2, sb.indexOf('z', 0));
1161         assertEquals(-1, sb.indexOf('z', 3));
1162     }
1163 
1164     @Test
1165     void testIndexOf_String() {
1166         final TextStringBuilder sb = new TextStringBuilder("abab");
1167 
1168         assertEquals(0, sb.indexOf("a"));
1169         // should work like String#indexOf
1170         assertEquals("abab".indexOf("a"), sb.indexOf("a"));
1171 
1172         assertEquals(0, sb.indexOf("ab"));
1173         // should work like String#indexOf
1174         assertEquals("abab".indexOf("ab"), sb.indexOf("ab"));
1175 
1176         assertEquals(1, sb.indexOf("b"));
1177         assertEquals("abab".indexOf("b"), sb.indexOf("b"));
1178 
1179         assertEquals(1, sb.indexOf("ba"));
1180         assertEquals("abab".indexOf("ba"), sb.indexOf("ba"));
1181 
1182         assertEquals(-1, sb.indexOf("z"));
1183 
1184         assertEquals(-1, sb.indexOf((String) null));
1185     }
1186 
1187     @Test
1188     void testIndexOf_String_int() {
1189         TextStringBuilder sb = new TextStringBuilder("abab");
1190         assertEquals(0, sb.indexOf("a", -1));
1191         assertEquals(0, sb.indexOf("a", 0));
1192         assertEquals(2, sb.indexOf("a", 1));
1193         assertEquals(2, sb.indexOf("a", 2));
1194         assertEquals(-1, sb.indexOf("a", 3));
1195         assertEquals(-1, sb.indexOf("a", 4));
1196         assertEquals(-1, sb.indexOf("a", 5));
1197 
1198         assertEquals(-1, sb.indexOf("abcdef", 0));
1199         assertEquals(0, sb.indexOf("", 0));
1200         assertEquals(1, sb.indexOf("", 1));
1201 
1202         // should work like String#indexOf
1203         assertEquals("abab".indexOf("a", 1), sb.indexOf("a", 1));
1204 
1205         assertEquals(2, sb.indexOf("ab", 1));
1206         // should work like String#indexOf
1207         assertEquals("abab".indexOf("ab", 1), sb.indexOf("ab", 1));
1208 
1209         assertEquals(3, sb.indexOf("b", 2));
1210         assertEquals("abab".indexOf("b", 2), sb.indexOf("b", 2));
1211 
1212         assertEquals(1, sb.indexOf("ba", 1));
1213         assertEquals("abab".indexOf("ba", 2), sb.indexOf("ba", 2));
1214 
1215         assertEquals(-1, sb.indexOf("z", 2));
1216 
1217         sb = new TextStringBuilder("xyzabc");
1218         assertEquals(2, sb.indexOf("za", 0));
1219         assertEquals(-1, sb.indexOf("za", 3));
1220 
1221         assertEquals(-1, sb.indexOf((String) null, 2));
1222     }
1223 
1224     @Test
1225     void testIndexOf_StringMatcher() {
1226         final TextStringBuilder sb = new TextStringBuilder();
1227         assertEquals(-1, sb.indexOf((StringMatcher) null));
1228         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a')));
1229 
1230         sb.append("ab bd");
1231         assertEquals(0, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a')));
1232         assertEquals(1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b')));
1233         assertEquals(2, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher()));
1234         assertEquals(4, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('d')));
1235         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.noneMatcher()));
1236         assertEquals(-1, sb.indexOf((StringMatcher) null));
1237 
1238         sb.append(" A1 junction");
1239         assertEquals(6, sb.indexOf(A_NUMBER_MATCHER));
1240     }
1241 
1242     @Test
1243     void testIndexOf_StringMatcher_int() {
1244         final TextStringBuilder sb = new TextStringBuilder();
1245         assertEquals(-1, sb.indexOf((StringMatcher) null, 2));
1246         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 2));
1247         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 0));
1248 
1249         sb.append("ab bd");
1250         assertEquals(0, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), -2));
1251         assertEquals(0, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 0));
1252         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 2));
1253         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 20));
1254 
1255         assertEquals(1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), -1));
1256         assertEquals(1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 0));
1257         assertEquals(1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 1));
1258         assertEquals(3, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 2));
1259         assertEquals(3, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 3));
1260         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 4));
1261         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 5));
1262         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 6));
1263 
1264         assertEquals(2, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), -2));
1265         assertEquals(2, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 0));
1266         assertEquals(2, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 2));
1267         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 4));
1268         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 20));
1269 
1270         assertEquals(-1, sb.indexOf(StringMatcherFactory.INSTANCE.noneMatcher(), 0));
1271         assertEquals(-1, sb.indexOf((StringMatcher) null, 0));
1272 
1273         sb.append(" A1 junction with A2");
1274         assertEquals(6, sb.indexOf(A_NUMBER_MATCHER, 5));
1275         assertEquals(6, sb.indexOf(A_NUMBER_MATCHER, 6));
1276         assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 7));
1277         assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 22));
1278         assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 23));
1279         assertEquals(-1, sb.indexOf(A_NUMBER_MATCHER, 24));
1280     }
1281 
1282     @Test
1283     void testIndexOfLang294() {
1284         final TextStringBuilder sb = new TextStringBuilder("onetwothree");
1285         sb.deleteFirst("three");
1286         assertEquals(-1, sb.indexOf("three"));
1287     }
1288 
1289     @Test
1290     void testIsEmpty() {
1291         final TextStringBuilder sb = new TextStringBuilder();
1292         assertTrue(sb.isEmpty());
1293 
1294         sb.append("Hello");
1295         assertFalse(sb.isEmpty());
1296 
1297         sb.clear();
1298         assertTrue(sb.isEmpty());
1299     }
1300 
1301     @Test
1302     void testIsNotEmpty() {
1303         final TextStringBuilder sb = new TextStringBuilder();
1304         assertFalse(sb.isNotEmpty());
1305 
1306         sb.append("Hello");
1307         assertTrue(sb.isNotEmpty());
1308 
1309         sb.clear();
1310         assertFalse(sb.isNotEmpty());
1311     }
1312 
1313     @Test
1314     void testLang294() {
1315         final TextStringBuilder sb = new TextStringBuilder("\n%BLAH%\nDo more stuff\neven more stuff\n%BLAH%\n");
1316         sb.deleteAll("\n%BLAH%");
1317         assertEquals("\nDo more stuff\neven more stuff\n", sb.toString());
1318     }
1319 
1320     @Test
1321     void testLang295() {
1322         final TextStringBuilder sb = new TextStringBuilder("onetwothree");
1323         sb.deleteFirst("three");
1324         assertFalse(sb.contains('h'), "The contains(char) method is looking beyond the end of the string");
1325         assertEquals(-1, sb.indexOf('h'), "The indexOf(char) method is looking beyond the end of the string");
1326     }
1327 
1328     @Test
1329     void testLang412Left() {
1330         final TextStringBuilder sb = new TextStringBuilder();
1331         sb.appendFixedWidthPadLeft(null, 10, '*');
1332         assertEquals("**********", sb.toString(), "Failed to invoke appendFixedWidthPadLeft correctly");
1333     }
1334 
1335     @Test
1336     void testLang412Right() {
1337         final TextStringBuilder sb = new TextStringBuilder();
1338         sb.appendFixedWidthPadRight(null, 10, '*');
1339         assertEquals("**********", sb.toString(), "Failed to invoke appendFixedWidthPadRight correctly");
1340     }
1341 
1342     @Test
1343     void testLastIndexOf_char() {
1344         final TextStringBuilder sb = new TextStringBuilder("abab");
1345 
1346         assertEquals(2, sb.lastIndexOf('a'));
1347         // should work like String#lastIndexOf
1348         assertEquals("abab".lastIndexOf('a'), sb.lastIndexOf('a'));
1349 
1350         assertEquals(3, sb.lastIndexOf('b'));
1351         assertEquals("abab".lastIndexOf('b'), sb.lastIndexOf('b'));
1352 
1353         assertEquals(-1, sb.lastIndexOf('z'));
1354     }
1355 
1356     @Test
1357     void testLastIndexOf_char_int() {
1358         TextStringBuilder sb = new TextStringBuilder("abab");
1359         assertEquals(-1, sb.lastIndexOf('a', -1));
1360         assertEquals(0, sb.lastIndexOf('a', 0));
1361         assertEquals(0, sb.lastIndexOf('a', 1));
1362 
1363         // should work like String#lastIndexOf
1364         assertEquals("abab".lastIndexOf('a', 1), sb.lastIndexOf('a', 1));
1365 
1366         assertEquals(1, sb.lastIndexOf('b', 2));
1367         assertEquals("abab".lastIndexOf('b', 2), sb.lastIndexOf('b', 2));
1368 
1369         assertEquals(-1, sb.lastIndexOf('z', 2));
1370 
1371         sb = new TextStringBuilder("xyzabc");
1372         assertEquals(2, sb.lastIndexOf('z', sb.length()));
1373         assertEquals(-1, sb.lastIndexOf('z', 1));
1374     }
1375 
1376     @Test
1377     void testLastIndexOf_String() {
1378         final TextStringBuilder sb = new TextStringBuilder("abab");
1379 
1380         assertEquals(2, sb.lastIndexOf("a"));
1381         // should work like String#lastIndexOf
1382         assertEquals("abab".lastIndexOf("a"), sb.lastIndexOf("a"));
1383 
1384         assertEquals(2, sb.lastIndexOf("ab"));
1385         // should work like String#lastIndexOf
1386         assertEquals("abab".lastIndexOf("ab"), sb.lastIndexOf("ab"));
1387 
1388         assertEquals(3, sb.lastIndexOf("b"));
1389         assertEquals("abab".lastIndexOf("b"), sb.lastIndexOf("b"));
1390 
1391         assertEquals(1, sb.lastIndexOf("ba"));
1392         assertEquals("abab".lastIndexOf("ba"), sb.lastIndexOf("ba"));
1393 
1394         assertEquals(-1, sb.lastIndexOf("z"));
1395 
1396         assertEquals(-1, sb.lastIndexOf((String) null));
1397 
1398         assertEquals(4, sb.lastIndexOf(""));
1399         assertEquals("".lastIndexOf(""), new StringBuilder().lastIndexOf(""));
1400     }
1401 
1402     @Test
1403     void testLastIndexOf_String_int() {
1404         TextStringBuilder sb = new TextStringBuilder("abab");
1405         assertEquals(-1, sb.lastIndexOf("a", -1));
1406         assertEquals(0, sb.lastIndexOf("a", 0));
1407         assertEquals(0, sb.lastIndexOf("a", 1));
1408         assertEquals(2, sb.lastIndexOf("a", 2));
1409         assertEquals(2, sb.lastIndexOf("a", 3));
1410         assertEquals(2, sb.lastIndexOf("a", 4));
1411         assertEquals(2, sb.lastIndexOf("a", 5));
1412 
1413         assertEquals(-1, sb.lastIndexOf("abcdef", 3));
1414         assertEquals("abab".lastIndexOf("", 3), sb.lastIndexOf("", 3));
1415         assertEquals("abab".lastIndexOf("", 1), sb.lastIndexOf("", 1));
1416 
1417         // should work like String#lastIndexOf
1418         assertEquals("abab".lastIndexOf("a", 1), sb.lastIndexOf("a", 1));
1419 
1420         assertEquals(0, sb.lastIndexOf("ab", 1));
1421         // should work like String#lastIndexOf
1422         assertEquals("abab".lastIndexOf("ab", 1), sb.lastIndexOf("ab", 1));
1423 
1424         assertEquals(1, sb.lastIndexOf("b", 2));
1425         assertEquals("abab".lastIndexOf("b", 2), sb.lastIndexOf("b", 2));
1426 
1427         assertEquals(1, sb.lastIndexOf("ba", 2));
1428         assertEquals("abab".lastIndexOf("ba", 2), sb.lastIndexOf("ba", 2));
1429 
1430         assertEquals(-1, sb.lastIndexOf("z", 2));
1431 
1432         sb = new TextStringBuilder("xyzabc");
1433         assertEquals(2, sb.lastIndexOf("za", sb.length()));
1434         assertEquals(-1, sb.lastIndexOf("za", 1));
1435 
1436         assertEquals(-1, sb.lastIndexOf((String) null, 2));
1437     }
1438 
1439     @Test
1440     void testLastIndexOf_StringMatcher() {
1441         final TextStringBuilder sb = new TextStringBuilder();
1442         assertEquals(-1, sb.lastIndexOf((StringMatcher) null));
1443         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a')));
1444 
1445         sb.append("ab bd");
1446         assertEquals(0, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a')));
1447         assertEquals(3, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b')));
1448         assertEquals(2, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher()));
1449         assertEquals(4, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('d')));
1450         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.noneMatcher()));
1451         assertEquals(-1, sb.lastIndexOf((StringMatcher) null));
1452 
1453         sb.append(" A1 junction");
1454         assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER));
1455     }
1456 
1457     @Test
1458     void testLastIndexOf_StringMatcher_int() {
1459         final TextStringBuilder sb = new TextStringBuilder();
1460         assertEquals(-1, sb.lastIndexOf((StringMatcher) null, 2));
1461         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 2));
1462         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 0));
1463         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), -1));
1464 
1465         sb.append("ab bd");
1466         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), -2));
1467         assertEquals(0, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 0));
1468         assertEquals(0, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 2));
1469         assertEquals(0, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('a'), 20));
1470 
1471         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), -1));
1472         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 0));
1473         assertEquals(1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 1));
1474         assertEquals(1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 2));
1475         assertEquals(3, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 3));
1476         assertEquals(3, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 4));
1477         assertEquals(3, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 5));
1478         assertEquals(3, sb.lastIndexOf(StringMatcherFactory.INSTANCE.charMatcher('b'), 6));
1479 
1480         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), -2));
1481         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 0));
1482         assertEquals(2, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 2));
1483         assertEquals(2, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 4));
1484         assertEquals(2, sb.lastIndexOf(StringMatcherFactory.INSTANCE.spaceMatcher(), 20));
1485 
1486         assertEquals(-1, sb.lastIndexOf(StringMatcherFactory.INSTANCE.noneMatcher(), 0));
1487         assertEquals(-1, sb.lastIndexOf((StringMatcher) null, 0));
1488 
1489         sb.append(" A1 junction with A2");
1490         assertEquals(-1, sb.lastIndexOf(A_NUMBER_MATCHER, 5));
1491         assertEquals(-1, sb.lastIndexOf(A_NUMBER_MATCHER, 6)); // A matches, 1
1492                                                                // is outside
1493                                                                // bounds
1494         assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 7));
1495         assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 22));
1496         assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 23)); // A matches, 2
1497                                                                // is outside
1498                                                                // bounds
1499         assertEquals(23, sb.lastIndexOf(A_NUMBER_MATCHER, 24));
1500     }
1501 
1502     @Test
1503     void testLeftString() {
1504         final TextStringBuilder sb = new TextStringBuilder("left right");
1505         assertEquals("left", sb.leftString(4));
1506         assertEquals("", sb.leftString(0));
1507         assertEquals("", sb.leftString(-5));
1508         assertEquals("left right", sb.leftString(15));
1509     }
1510 
1511     @Test
1512     void testLength() {
1513         final TextStringBuilder sb = new TextStringBuilder();
1514         assertEquals(0, sb.length());
1515 
1516         sb.append("Hello");
1517         assertEquals(5, sb.length());
1518     }
1519 
1520     @Test
1521     void testMidString() {
1522         final TextStringBuilder sb = new TextStringBuilder("hello goodbye hello");
1523         assertEquals("goodbye", sb.midString(6, 7));
1524         assertEquals("hello", sb.midString(0, 5));
1525         assertEquals("hello", sb.midString(-5, 5));
1526         assertEquals("", sb.midString(0, -1));
1527         assertEquals("", sb.midString(20, 2));
1528         assertEquals("hello", sb.midString(14, 22));
1529     }
1530 
1531     @Test
1532     void testMinimizeCapacity() {
1533         final TextStringBuilder sb = new TextStringBuilder();
1534         sb.minimizeCapacity();
1535         assertEquals(0, sb.capacity());
1536 
1537         sb.append("HelloWorld");
1538         sb.minimizeCapacity();
1539         assertEquals(10, sb.capacity());
1540     }
1541 
1542     @Test
1543     void testOutOfMemoryError() {
1544         // This test is memory hungry requiring at least 7GiB of memory.
1545         // By default expansion will double the buffer size. If we repeat
1546         // add 1GiB of char data then we require at maximum:
1547         // 1GiB char[] data
1548         // 2GiB char[] buffer
1549         // ~4GiB char[] new buffer during reallocation
1550 
1551         // Attempts to guess the amount of free memory available using
1552         // the java.lang.Runtime/java.lang.management objects to
1553         // skip the test often did not work.
1554         // So here we just run the test and return a skip result if the
1555         // OutOfMemoryError occurs too early.
1556 
1557         final TextStringBuilder sb = new TextStringBuilder();
1558         sb.minimizeCapacity();
1559         assertEquals(0, sb.capacity());
1560 
1561         // 1GiB char[] buffer: length is roughly 1/4 the maximum array size
1562         final char[] chars = new char[1 << 29];
1563 
1564         // With infinite memory it should be possible to add this 3 times.
1565         try {
1566             for (int i = 0; i < 3; i++) {
1567                 sb.append(chars);
1568             }
1569         } catch (final OutOfMemoryError ignored) {
1570             Assumptions.abort("Not enough memory for the test");
1571         }
1572 
1573         // Now at 3/4 of the maximum array length.
1574         // Adding is not possible so we expect an OOM error.
1575         assertThrows(OutOfMemoryError.class, () -> sb.append(chars));
1576     }
1577 
1578     @Test
1579     void testOutOfMemoryError2() {
1580         // This test is memory hungry requiring at least 4GiB of memory
1581         // in a single allocation. If not possible then skip the test.
1582 
1583         final TextStringBuilder sb = new TextStringBuilder();
1584         sb.minimizeCapacity();
1585         assertEquals(0, sb.capacity());
1586 
1587         // Allocate a lot
1588         final int small = 10;
1589         final int big = Integer.MAX_VALUE - small;
1590         final char[] extra = new char[small + 1];
1591         try {
1592             sb.ensureCapacity(big);
1593         } catch (final OutOfMemoryError ignored) {
1594             Assumptions.abort("Not enough memory for the test");
1595         }
1596 
1597         fill(sb, big);
1598 
1599         // Adding more than the maximum array size is not possible so we expect an OOM error.
1600         assertThrows(OutOfMemoryError.class, () -> sb.append(extra));
1601     }
1602 
1603     @Test
1604     void testOutOfMemoryError3() {
1605         // This test is memory hungry requiring at least 2GiB of memory
1606         // in a single allocation. If not possible then skip the test.
1607 
1608         final TextStringBuilder sb = new TextStringBuilder();
1609         sb.minimizeCapacity();
1610         assertEquals(0, sb.capacity());
1611 
1612         final int length = 1 << 30;
1613         try {
1614             sb.ensureCapacity(length);
1615         } catch (final OutOfMemoryError ignored) {
1616             Assumptions.abort("Not enough memory for the test");
1617         }
1618 
1619         fill(sb, length);
1620 
1621         // Adding to itself requires a new buffer above the limits of an array
1622         assertThrows(OutOfMemoryError.class, () -> sb.append(sb));
1623     }
1624 
1625     @Test
1626     void testReadFromCharBuffer() throws Exception {
1627         String s = "";
1628         for (int i = 0; i < 100; ++i) {
1629             final TextStringBuilder sb = new TextStringBuilder();
1630             final int len = sb.readFrom(CharBuffer.wrap(s));
1631 
1632             assertEquals(s.length(), len);
1633             assertEquals(s, sb.toString());
1634 
1635             s += Integer.toString(i);
1636         }
1637     }
1638 
1639     @Test
1640     void testReadFromCharBufferAppendsToEnd() throws Exception {
1641         final TextStringBuilder sb = new TextStringBuilder("Test");
1642         sb.readFrom(CharBuffer.wrap(" 123"));
1643         assertEquals("Test 123", sb.toString());
1644     }
1645 
1646     @Test
1647     void testReadFromReadable() throws Exception {
1648         String s = "";
1649         for (int i = 0; i < 100; ++i) {
1650             final TextStringBuilder sb = new TextStringBuilder();
1651             final int len = sb.readFrom(new MockReadable(s));
1652 
1653             assertEquals(s.length(), len);
1654             assertEquals(s, sb.toString());
1655 
1656             s += Integer.toString(i);
1657         }
1658     }
1659 
1660     @Test
1661     void testReadFromReadableAppendsToEnd() throws Exception {
1662         final TextStringBuilder sb = new TextStringBuilder("Test");
1663         sb.readFrom(new MockReadable(" 123"));
1664         assertEquals("Test 123", sb.toString());
1665     }
1666 
1667     @Test
1668     void testReadFromReader() throws Exception {
1669         String s = "1";
1670         for (int i = 0; i < 100; ++i) {
1671             final TextStringBuilder sb = new TextStringBuilder();
1672             final int len = sb.readFrom(new StringReader(s));
1673 
1674             assertEquals(s.length(), len);
1675             assertEquals(s, sb.toString());
1676 
1677             s += Integer.toString(i);
1678         }
1679     }
1680 
1681     @Test
1682     void testReadFromReaderAppendsToEnd() throws Exception {
1683         final TextStringBuilder sb = new TextStringBuilder("Test");
1684         sb.readFrom(new StringReader(" 123"));
1685         assertEquals("Test 123", sb.toString());
1686     }
1687 
1688     @Test
1689     void testReadFromReaderEmpty() throws Exception {
1690         final TextStringBuilder sb = new TextStringBuilder();
1691         final int len = sb.readFrom(new StringReader(StringUtils.EMPTY));
1692         assertEquals(-1, len);
1693         assertEquals(StringUtils.EMPTY, sb.toString());
1694     }
1695 
1696     @Test
1697     void testReadFromReaderInt() throws Exception {
1698         String str = "";
1699         for (int i = 0; i < 100; ++i) {
1700             final TextStringBuilder sb = new TextStringBuilder();
1701             final int len = sb.readFrom(new StringReader(str), str.length());
1702 
1703             assertEquals(str.length(), len);
1704             assertEquals(str, sb.toString());
1705 
1706             str += Integer.toString(i);
1707         }
1708         //
1709         TextStringBuilder sb;
1710         int count;
1711         int target;
1712         final String source = "abc";
1713         final int sourceLen = source.length();
1714         // empty
1715         target = -1;
1716         sb = new TextStringBuilder();
1717         count = sb.readFrom(new StringReader(StringUtils.EMPTY), target);
1718         assertEquals(0, count);
1719         assertEquals(0, sb.size());
1720         assertEquals(source.substring(0, 0), sb.toString());
1721         //
1722         target = -1;
1723         sb = new TextStringBuilder();
1724         count = sb.readFrom(new StringReader(source), target);
1725         assertEquals(0, count);
1726         assertEquals(0, sb.size());
1727         assertEquals(source.substring(0, 0), sb.toString());
1728         //
1729         target = 0;
1730         sb = new TextStringBuilder();
1731         count = sb.readFrom(new StringReader(source), target);
1732         assertEquals(target, count);
1733         assertEquals(target, sb.size());
1734         assertEquals(source.substring(0, target), sb.toString());
1735         //
1736         target = 1;
1737         sb = new TextStringBuilder();
1738         count = sb.readFrom(new StringReader(source), target);
1739         assertEquals(target, count);
1740         assertEquals(target, sb.size());
1741         assertEquals(source.substring(0, target), sb.toString());
1742         //
1743         target = 2;
1744         sb = new TextStringBuilder();
1745         count = sb.readFrom(new StringReader(source), target);
1746         assertEquals(target, count);
1747         assertEquals(target, sb.size());
1748         assertEquals(source.substring(0, target), sb.toString());
1749         //
1750         target = 3;
1751         sb = new TextStringBuilder();
1752         count = sb.readFrom(new StringReader(source), target);
1753         assertEquals(target, count);
1754         assertEquals(target, sb.size());
1755         assertEquals(source.substring(0, target), sb.toString());
1756         //
1757         target = 4;
1758         sb = new TextStringBuilder();
1759         count = sb.readFrom(new StringReader(source), target);
1760         assertEquals(sourceLen, count);
1761         assertEquals(sourceLen, sb.size());
1762         assertEquals(source.substring(0, sourceLen), sb.toString());
1763     }
1764 
1765     @Test
1766     void testReadFromReaderIntEmpty() throws Exception {
1767         final TextStringBuilder sb = new TextStringBuilder();
1768         final int len = sb.readFrom(new StringReader(StringUtils.EMPTY), 1);
1769         assertEquals(-1, len);
1770         assertEquals(StringUtils.EMPTY, sb.toString());
1771     }
1772 
1773     @Test
1774     void testReplace_int_int_String() {
1775         final TextStringBuilder sb = new TextStringBuilder("abc");
1776         sb.replace(0, 1, "d");
1777         assertEquals("dbc", sb.toString());
1778         sb.replace(0, 1, "aaa");
1779         assertEquals("aaabc", sb.toString());
1780         sb.replace(0, 3, "");
1781         assertEquals("bc", sb.toString());
1782         sb.replace(1, 2, (String) null);
1783         assertEquals("b", sb.toString());
1784         sb.replace(1, 1000, "text");
1785         assertEquals("btext", sb.toString());
1786         sb.replace(0, 1000, "text");
1787         assertEquals("text", sb.toString());
1788 
1789         final TextStringBuilder builder = new TextStringBuilder("atext");
1790         builder.replace(1, 1, "ny");
1791         assertEquals("anytext", builder.toString());
1792 
1793         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(2, 1, "anything"));
1794 
1795         builder.clear();
1796         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(1, 2, "anything"));
1797         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(-1, 1, "anything"));
1798     }
1799 
1800     @Test
1801     void testReplace_StringMatcher_String_int_int_int_VaryCount() {
1802         TextStringBuilder sb = new TextStringBuilder("aaxaaaayaa");
1803         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, -1);
1804         assertEquals("-x--y-", sb.toString());
1805 
1806         sb = new TextStringBuilder("aaxaaaayaa");
1807         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 0);
1808         assertEquals("aaxaaaayaa", sb.toString());
1809 
1810         sb = new TextStringBuilder("aaxaaaayaa");
1811         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 1);
1812         assertEquals("-xaaaayaa", sb.toString());
1813 
1814         sb = new TextStringBuilder("aaxaaaayaa");
1815         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 2);
1816         assertEquals("-x-aayaa", sb.toString());
1817 
1818         sb = new TextStringBuilder("aaxaaaayaa");
1819         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 3);
1820         assertEquals("-x--yaa", sb.toString());
1821 
1822         sb = new TextStringBuilder("aaxaaaayaa");
1823         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 4);
1824         assertEquals("-x--y-", sb.toString());
1825 
1826         sb = new TextStringBuilder("aaxaaaayaa");
1827         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, 5);
1828         assertEquals("-x--y-", sb.toString());
1829     }
1830 
1831     @Test
1832     void testReplace_StringMatcher_String_int_int_int_VaryEndIndex() {
1833         TextStringBuilder sb = new TextStringBuilder("aaxaaaayaa");
1834         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 0, -1);
1835         assertEquals("aaxaaaayaa", sb.toString());
1836 
1837         sb = new TextStringBuilder("aaxaaaayaa");
1838         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 2, -1);
1839         assertEquals("-xaaaayaa", sb.toString());
1840 
1841         sb = new TextStringBuilder("aaxaaaayaa");
1842         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 3, -1);
1843         assertEquals("-xaaaayaa", sb.toString());
1844 
1845         sb = new TextStringBuilder("aaxaaaayaa");
1846         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 4, -1);
1847         assertEquals("-xaaaayaa", sb.toString());
1848 
1849         sb = new TextStringBuilder("aaxaaaayaa");
1850         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 5, -1);
1851         assertEquals("-x-aayaa", sb.toString());
1852 
1853         sb = new TextStringBuilder("aaxaaaayaa");
1854         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 6, -1);
1855         assertEquals("-x-aayaa", sb.toString());
1856 
1857         sb = new TextStringBuilder("aaxaaaayaa");
1858         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 7, -1);
1859         assertEquals("-x--yaa", sb.toString());
1860 
1861         sb = new TextStringBuilder("aaxaaaayaa");
1862         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 8, -1);
1863         assertEquals("-x--yaa", sb.toString());
1864 
1865         sb = new TextStringBuilder("aaxaaaayaa");
1866         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 9, -1);
1867         assertEquals("-x--yaa", sb.toString());
1868 
1869         sb = new TextStringBuilder("aaxaaaayaa");
1870         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 10, -1);
1871         assertEquals("-x--y-", sb.toString());
1872 
1873         sb = new TextStringBuilder("aaxaaaayaa");
1874         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, 1000, -1);
1875         assertEquals("-x--y-", sb.toString());
1876 
1877         final TextStringBuilder builder = new TextStringBuilder("aaxaaaayaa");
1878         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 2, 1, -1));
1879         assertEquals("aaxaaaayaa", builder.toString());
1880     }
1881 
1882     @Test
1883     void testReplace_StringMatcher_String_int_int_int_VaryMatcher() {
1884         TextStringBuilder sb = new TextStringBuilder("abcbccba");
1885         sb.replace((StringMatcher) null, "x", 0, sb.length(), -1);
1886         assertEquals("abcbccba", sb.toString());
1887 
1888         sb.replace(StringMatcherFactory.INSTANCE.charMatcher('a'), "x", 0, sb.length(), -1);
1889         assertEquals("xbcbccbx", sb.toString());
1890 
1891         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "x", 0, sb.length(), -1);
1892         assertEquals("xbxcxx", sb.toString());
1893 
1894         sb = new TextStringBuilder("A1-A2A3-A4");
1895         sb.replace(A_NUMBER_MATCHER, "***", 0, sb.length(), -1);
1896         assertEquals("***-******-***", sb.toString());
1897 
1898         sb = new TextStringBuilder();
1899         sb.replace(A_NUMBER_MATCHER, "***", 0, sb.length(), -1);
1900         assertEquals("", sb.toString());
1901     }
1902 
1903     @Test
1904     void testReplace_StringMatcher_String_int_int_int_VaryReplace() {
1905         TextStringBuilder sb = new TextStringBuilder("abcbccba");
1906         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "cb", 0, sb.length(), -1);
1907         assertEquals("abcbccba", sb.toString());
1908 
1909         sb = new TextStringBuilder("abcbccba");
1910         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "-", 0, sb.length(), -1);
1911         assertEquals("ab-c-a", sb.toString());
1912 
1913         sb = new TextStringBuilder("abcbccba");
1914         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "+++", 0, sb.length(), -1);
1915         assertEquals("ab+++c+++a", sb.toString());
1916 
1917         sb = new TextStringBuilder("abcbccba");
1918         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "", 0, sb.length(), -1);
1919         assertEquals("abca", sb.toString());
1920 
1921         sb = new TextStringBuilder("abcbccba");
1922         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("cb"), null, 0, sb.length(), -1);
1923         assertEquals("abca", sb.toString());
1924     }
1925 
1926     @Test
1927     void testReplace_StringMatcher_String_int_int_int_VaryStartIndex() {
1928         TextStringBuilder sb = new TextStringBuilder("aaxaaaayaa");
1929         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 0, sb.length(), -1);
1930         assertEquals("-x--y-", sb.toString());
1931 
1932         sb = new TextStringBuilder("aaxaaaayaa");
1933         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 1, sb.length(), -1);
1934         assertEquals("aax--y-", sb.toString());
1935 
1936         sb = new TextStringBuilder("aaxaaaayaa");
1937         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 2, sb.length(), -1);
1938         assertEquals("aax--y-", sb.toString());
1939 
1940         sb = new TextStringBuilder("aaxaaaayaa");
1941         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 3, sb.length(), -1);
1942         assertEquals("aax--y-", sb.toString());
1943 
1944         sb = new TextStringBuilder("aaxaaaayaa");
1945         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 4, sb.length(), -1);
1946         assertEquals("aaxa-ay-", sb.toString());
1947 
1948         sb = new TextStringBuilder("aaxaaaayaa");
1949         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 5, sb.length(), -1);
1950         assertEquals("aaxaa-y-", sb.toString());
1951 
1952         sb = new TextStringBuilder("aaxaaaayaa");
1953         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 6, sb.length(), -1);
1954         assertEquals("aaxaaaay-", sb.toString());
1955 
1956         sb = new TextStringBuilder("aaxaaaayaa");
1957         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 7, sb.length(), -1);
1958         assertEquals("aaxaaaay-", sb.toString());
1959 
1960         sb = new TextStringBuilder("aaxaaaayaa");
1961         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 8, sb.length(), -1);
1962         assertEquals("aaxaaaay-", sb.toString());
1963 
1964         sb = new TextStringBuilder("aaxaaaayaa");
1965         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 9, sb.length(), -1);
1966         assertEquals("aaxaaaayaa", sb.toString());
1967 
1968         sb = new TextStringBuilder("aaxaaaayaa");
1969         sb.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 10, sb.length(), -1);
1970         assertEquals("aaxaaaayaa", sb.toString());
1971 
1972         final TextStringBuilder builder = new TextStringBuilder("aaxaaaayaa");
1973         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", 11, builder.length(), -1));
1974         assertEquals("aaxaaaayaa", builder.toString());
1975 
1976         builder.set("aaxaaaayaa");
1977         assertThrows(IndexOutOfBoundsException.class, () -> builder.replace(StringMatcherFactory.INSTANCE.stringMatcher("aa"), "-", -1, builder.length(), -1));
1978         assertEquals("aaxaaaayaa", builder.toString());
1979     }
1980 
1981     @Test
1982     void testReplaceAll_char_char() {
1983         final TextStringBuilder sb = new TextStringBuilder("abcbccba");
1984         sb.replaceAll('x', 'y');
1985         assertEquals("abcbccba", sb.toString());
1986         sb.replaceAll('a', 'd');
1987         assertEquals("dbcbccbd", sb.toString());
1988         sb.replaceAll('b', 'e');
1989         assertEquals("dececced", sb.toString());
1990         sb.replaceAll('c', 'f');
1991         assertEquals("defeffed", sb.toString());
1992         sb.replaceAll('d', 'd');
1993         assertEquals("defeffed", sb.toString());
1994     }
1995 
1996     @Test
1997     void testReplaceAll_String_String() {
1998         TextStringBuilder sb = new TextStringBuilder("abcbccba");
1999         sb.replaceAll((String) null, null);
2000         assertEquals("abcbccba", sb.toString());
2001         sb.replaceAll((String) null, "anything");
2002         assertEquals("abcbccba", sb.toString());
2003         sb.replaceAll("", null);
2004         assertEquals("abcbccba", sb.toString());
2005         sb.replaceAll("", "anything");
2006         assertEquals("abcbccba", sb.toString());
2007 
2008         sb.replaceAll("x", "y");
2009         assertEquals("abcbccba", sb.toString());
2010         sb.replaceAll("a", "d");
2011         assertEquals("dbcbccbd", sb.toString());
2012         sb.replaceAll("d", null);
2013         assertEquals("bcbccb", sb.toString());
2014         sb.replaceAll("cb", "-");
2015         assertEquals("b-c-", sb.toString());
2016 
2017         sb = new TextStringBuilder("abcba");
2018         sb.replaceAll("b", "xbx");
2019         assertEquals("axbxcxbxa", sb.toString());
2020 
2021         sb = new TextStringBuilder("bb");
2022         sb.replaceAll("b", "xbx");
2023         assertEquals("xbxxbx", sb.toString());
2024     }
2025 
2026     @Test
2027     void testReplaceAll_StringMatcher_String() {
2028         TextStringBuilder sb = new TextStringBuilder("abcbccba");
2029         sb.replaceAll((StringMatcher) null, null);
2030         assertEquals("abcbccba", sb.toString());
2031         sb.replaceAll((StringMatcher) null, "anything");
2032         assertEquals("abcbccba", sb.toString());
2033         sb.replaceAll(StringMatcherFactory.INSTANCE.noneMatcher(), null);
2034         assertEquals("abcbccba", sb.toString());
2035         sb.replaceAll(StringMatcherFactory.INSTANCE.noneMatcher(), "anything");
2036         assertEquals("abcbccba", sb.toString());
2037 
2038         sb.replaceAll(StringMatcherFactory.INSTANCE.charMatcher('x'), "y");
2039         assertEquals("abcbccba", sb.toString());
2040         sb.replaceAll(StringMatcherFactory.INSTANCE.charMatcher('a'), "d");
2041         assertEquals("dbcbccbd", sb.toString());
2042         sb.replaceAll(StringMatcherFactory.INSTANCE.charMatcher('d'), null);
2043         assertEquals("bcbccb", sb.toString());
2044         sb.replaceAll(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "-");
2045         assertEquals("b-c-", sb.toString());
2046 
2047         sb = new TextStringBuilder("abcba");
2048         sb.replaceAll(StringMatcherFactory.INSTANCE.charMatcher('b'), "xbx");
2049         assertEquals("axbxcxbxa", sb.toString());
2050 
2051         sb = new TextStringBuilder("bb");
2052         sb.replaceAll(StringMatcherFactory.INSTANCE.charMatcher('b'), "xbx");
2053         assertEquals("xbxxbx", sb.toString());
2054 
2055         sb = new TextStringBuilder("A1-A2A3-A4");
2056         sb.replaceAll(A_NUMBER_MATCHER, "***");
2057         assertEquals("***-******-***", sb.toString());
2058 
2059         sb = new TextStringBuilder("Dear X, hello X.");
2060         sb.replaceAll(StringMatcherFactory.INSTANCE.stringMatcher("X"), "012345678901234567");
2061         assertEquals("Dear 012345678901234567, hello 012345678901234567.", sb.toString());
2062     }
2063 
2064     @Test
2065     void testReplaceFirst_char_char() {
2066         final TextStringBuilder sb = new TextStringBuilder("abcbccba");
2067         sb.replaceFirst('x', 'y');
2068         assertEquals("abcbccba", sb.toString());
2069         sb.replaceFirst('a', 'd');
2070         assertEquals("dbcbccba", sb.toString());
2071         sb.replaceFirst('b', 'e');
2072         assertEquals("decbccba", sb.toString());
2073         sb.replaceFirst('c', 'f');
2074         assertEquals("defbccba", sb.toString());
2075         sb.replaceFirst('d', 'd');
2076         assertEquals("defbccba", sb.toString());
2077     }
2078 
2079     @Test
2080     void testReplaceFirst_String_String() {
2081         TextStringBuilder sb = new TextStringBuilder("abcbccba");
2082         sb.replaceFirst((String) null, null);
2083         assertEquals("abcbccba", sb.toString());
2084         sb.replaceFirst((String) null, "anything");
2085         assertEquals("abcbccba", sb.toString());
2086         sb.replaceFirst("", null);
2087         assertEquals("abcbccba", sb.toString());
2088         sb.replaceFirst("", "anything");
2089         assertEquals("abcbccba", sb.toString());
2090 
2091         sb.replaceFirst("x", "y");
2092         assertEquals("abcbccba", sb.toString());
2093         sb.replaceFirst("a", "d");
2094         assertEquals("dbcbccba", sb.toString());
2095         sb.replaceFirst("d", null);
2096         assertEquals("bcbccba", sb.toString());
2097         sb.replaceFirst("cb", "-");
2098         assertEquals("b-ccba", sb.toString());
2099 
2100         sb = new TextStringBuilder("abcba");
2101         sb.replaceFirst("b", "xbx");
2102         assertEquals("axbxcba", sb.toString());
2103 
2104         sb = new TextStringBuilder("bb");
2105         sb.replaceFirst("b", "xbx");
2106         assertEquals("xbxb", sb.toString());
2107     }
2108 
2109     @Test
2110     void testReplaceFirst_StringMatcher_String() {
2111         TextStringBuilder sb = new TextStringBuilder("abcbccba");
2112         sb.replaceFirst((StringMatcher) null, null);
2113         assertEquals("abcbccba", sb.toString());
2114         sb.replaceFirst((StringMatcher) null, "anything");
2115         assertEquals("abcbccba", sb.toString());
2116         sb.replaceFirst(StringMatcherFactory.INSTANCE.noneMatcher(), null);
2117         assertEquals("abcbccba", sb.toString());
2118         sb.replaceFirst(StringMatcherFactory.INSTANCE.noneMatcher(), "anything");
2119         assertEquals("abcbccba", sb.toString());
2120 
2121         sb.replaceFirst(StringMatcherFactory.INSTANCE.charMatcher('x'), "y");
2122         assertEquals("abcbccba", sb.toString());
2123         sb.replaceFirst(StringMatcherFactory.INSTANCE.charMatcher('a'), "d");
2124         assertEquals("dbcbccba", sb.toString());
2125         sb.replaceFirst(StringMatcherFactory.INSTANCE.charMatcher('d'), null);
2126         assertEquals("bcbccba", sb.toString());
2127         sb.replaceFirst(StringMatcherFactory.INSTANCE.stringMatcher("cb"), "-");
2128         assertEquals("b-ccba", sb.toString());
2129 
2130         sb = new TextStringBuilder("abcba");
2131         sb.replaceFirst(StringMatcherFactory.INSTANCE.charMatcher('b'), "xbx");
2132         assertEquals("axbxcba", sb.toString());
2133 
2134         sb = new TextStringBuilder("bb");
2135         sb.replaceFirst(StringMatcherFactory.INSTANCE.charMatcher('b'), "xbx");
2136         assertEquals("xbxb", sb.toString());
2137 
2138         sb = new TextStringBuilder("A1-A2A3-A4");
2139         sb.replaceFirst(A_NUMBER_MATCHER, "***");
2140         assertEquals("***-A2A3-A4", sb.toString());
2141     }
2142 
2143     @Test
2144     void testReverse() {
2145         final TextStringBuilder sb = new TextStringBuilder();
2146         assertEquals("", sb.reverse().toString());
2147 
2148         sb.clear().append(true);
2149         assertEquals("eurt", sb.reverse().toString());
2150         assertEquals("true", sb.reverse().toString());
2151     }
2152 
2153     @Test
2154     void testReverseSurrogatePairs() {
2155         // U+1F600 GRINNING FACE is a supplementary code point encoded as a surrogate pair; reversing
2156         // must keep the pair intact (high before low) like StringBuilder, not split it into garbage.
2157         final String emoji = "😀";
2158         assertEquals(new StringBuilder("a" + emoji + "b").reverse().toString(), new TextStringBuilder("a" + emoji + "b").reverse().toString());
2159         assertEquals("b" + emoji + "a", new TextStringBuilder("a" + emoji + "b").reverse().toString());
2160 
2161         final String emoji2 = "😁";
2162         assertEquals(emoji2 + emoji, new TextStringBuilder(emoji + emoji2).reverse().toString());
2163 
2164         // An unpaired high surrogate has no partner and is left where it lands.
2165         assertEquals("b\uD800a", new TextStringBuilder("a\uD800b").reverse().toString());
2166     }
2167 
2168     @Test
2169     void testRightString() {
2170         final TextStringBuilder sb = new TextStringBuilder("left right");
2171         assertEquals("right", sb.rightString(5));
2172         assertEquals("", sb.rightString(0));
2173         assertEquals("", sb.rightString(-5));
2174         assertEquals("left right", sb.rightString(15));
2175     }
2176 
2177     @Test
2178     void testSetCharAt() {
2179         final TextStringBuilder sb = new TextStringBuilder();
2180         assertThrows(IndexOutOfBoundsException.class, () -> sb.setCharAt(0, 'f'));
2181         assertThrows(IndexOutOfBoundsException.class, () -> sb.setCharAt(-1, 'f'));
2182         sb.append("foo");
2183         sb.setCharAt(0, 'b');
2184         sb.setCharAt(1, 'a');
2185         sb.setCharAt(2, 'r');
2186         assertThrows(IndexOutOfBoundsException.class, () -> sb.setCharAt(3, '!'));
2187         assertEquals("bar", sb.toString());
2188     }
2189 
2190     @Test
2191     void testSetLength() {
2192         final TextStringBuilder sb = new TextStringBuilder();
2193         sb.append("Hello");
2194         sb.setLength(2); // shorten
2195         assertEquals("He", sb.toString());
2196         sb.setLength(2); // no change
2197         assertEquals("He", sb.toString());
2198         sb.setLength(3); // lengthen
2199         assertEquals("He\0", sb.toString());
2200 
2201         assertThrows(IndexOutOfBoundsException.class, () -> sb.setLength(-1));
2202     }
2203 
2204     @Test
2205     void testSize() {
2206         final TextStringBuilder sb = new TextStringBuilder();
2207         assertEquals(0, sb.size());
2208 
2209         sb.append("Hello");
2210         assertEquals(5, sb.size());
2211     }
2212 
2213     @Test
2214     void testStartsWith() {
2215         final TextStringBuilder sb = new TextStringBuilder();
2216         assertFalse(sb.startsWith("a"));
2217         assertFalse(sb.startsWith(null));
2218         assertTrue(sb.startsWith(""));
2219         sb.append("abc");
2220         assertTrue(sb.startsWith("a"));
2221         assertTrue(sb.startsWith("ab"));
2222         assertTrue(sb.startsWith("abc"));
2223         assertFalse(sb.startsWith("cba"));
2224     }
2225 
2226     @Test
2227     void testSubSequenceIntInt() {
2228         final TextStringBuilder sb = new TextStringBuilder("hello goodbye");
2229         // Start index is negative
2230         assertThrows(IndexOutOfBoundsException.class, () -> sb.subSequence(-1, 5));
2231 
2232         // End index is negative
2233         assertThrows(IndexOutOfBoundsException.class, () -> sb.subSequence(2, -1));
2234 
2235         // End index greater than length()
2236         assertThrows(IndexOutOfBoundsException.class, () -> sb.subSequence(2, sb.length() + 1));
2237 
2238         // Start index greater than end index
2239         assertThrows(IndexOutOfBoundsException.class, () -> sb.subSequence(3, 2));
2240 
2241         // Normal cases
2242         assertEquals("hello", sb.subSequence(0, 5));
2243         assertEquals("hello goodbye".subSequence(0, 6), sb.subSequence(0, 6));
2244         assertEquals("goodbye", sb.subSequence(6, 13));
2245         assertEquals("hello goodbye".subSequence(6, 13), sb.subSequence(6, 13));
2246     }
2247 
2248     @Test
2249     void testSubstringInt() {
2250         final TextStringBuilder sb = new TextStringBuilder("hello goodbye");
2251         assertEquals("goodbye", sb.substring(6));
2252         assertEquals("hello goodbye".substring(6), sb.substring(6));
2253         assertEquals("hello goodbye", sb.substring(0));
2254         assertEquals("hello goodbye".substring(0), sb.substring(0));
2255         assertThrows(IndexOutOfBoundsException.class, () -> sb.substring(-1));
2256 
2257         assertThrows(IndexOutOfBoundsException.class, () -> sb.substring(15));
2258 
2259     }
2260 
2261     @Test
2262     void testSubstringIntInt() {
2263         final TextStringBuilder sb = new TextStringBuilder("hello goodbye");
2264         assertEquals("hello", sb.substring(0, 5));
2265         assertEquals("hello goodbye".substring(0, 6), sb.substring(0, 6));
2266 
2267         assertEquals("goodbye", sb.substring(6, 13));
2268         assertEquals("hello goodbye".substring(6, 13), sb.substring(6, 13));
2269 
2270         assertEquals("goodbye", sb.substring(6, 20));
2271 
2272         assertThrows(IndexOutOfBoundsException.class, () -> sb.substring(-1, 5));
2273 
2274         assertThrows(IndexOutOfBoundsException.class, () -> sb.substring(15, 20));
2275     }
2276 
2277     @Test
2278     void testToCharArray() {
2279         final TextStringBuilder sb = new TextStringBuilder();
2280         assertEquals(0, sb.toCharArray().length);
2281 
2282         char[] a = sb.toCharArray();
2283         assertNotNull(a, "toCharArray() result is null");
2284         assertEquals(0, a.length, "toCharArray() result is too large");
2285 
2286         sb.append("junit");
2287         a = sb.toCharArray();
2288         assertEquals(5, a.length, "toCharArray() result incorrect length");
2289         assertArrayEquals("junit".toCharArray(), a, "toCharArray() result does not match");
2290     }
2291 
2292     @Test
2293     void testToCharArrayIntInt() {
2294         final TextStringBuilder sb = new TextStringBuilder();
2295         assertEquals(0, sb.toCharArray(0, 0).length);
2296 
2297         sb.append("junit");
2298         char[] a = sb.toCharArray(0, 20); // too large test
2299         assertEquals(5, a.length, "toCharArray(int,int) result incorrect length");
2300         assertArrayEquals("junit".toCharArray(), a, "toCharArray(int,int) result does not match");
2301 
2302         a = sb.toCharArray(0, 4);
2303         assertEquals(4, a.length, "toCharArray(int,int) result incorrect length");
2304         assertArrayEquals("juni".toCharArray(), a, "toCharArray(int,int) result does not match");
2305 
2306         a = sb.toCharArray(0, 4);
2307         assertEquals(4, a.length, "toCharArray(int,int) result incorrect length");
2308         assertArrayEquals("juni".toCharArray(), a, "toCharArray(int,int) result does not match");
2309 
2310         a = sb.toCharArray(0, 1);
2311         assertNotNull(a, "toCharArray(int,int) result is null");
2312 
2313         assertThrows(IndexOutOfBoundsException.class, () -> sb.toCharArray(-1, 5));
2314 
2315         assertThrows(IndexOutOfBoundsException.class, () -> sb.toCharArray(6, 5));
2316     }
2317 
2318     @Test
2319     void testToString() {
2320         final TextStringBuilder sb = new TextStringBuilder("abc");
2321         assertEquals("abc", sb.toString());
2322     }
2323 
2324     @Test
2325     void testToStringBuffer() {
2326         final TextStringBuilder sb = new TextStringBuilder();
2327         assertEquals(new StringBuffer().toString(), sb.toStringBuffer().toString());
2328 
2329         sb.append("junit");
2330         assertEquals(new StringBuffer("junit").toString(), sb.toStringBuffer().toString());
2331     }
2332 
2333     @Test
2334     void testToStringBuilder() {
2335         final TextStringBuilder sb = new TextStringBuilder();
2336         assertEquals(new StringBuilder().toString(), sb.toStringBuilder().toString());
2337 
2338         sb.append("junit");
2339         assertEquals(new StringBuilder("junit").toString(), sb.toStringBuilder().toString());
2340     }
2341 
2342     @Test
2343     void testTrim() {
2344         final TextStringBuilder sb = new TextStringBuilder();
2345         assertEquals("", sb.reverse().toString());
2346 
2347         sb.set(" \u0000 ");
2348         assertEquals("", sb.trim().toString());
2349 
2350         sb.set(" \u0000 a b c");
2351         assertEquals("a b c", sb.trim().toString());
2352 
2353         sb.set("a b c \u0000 ");
2354         assertEquals("a b c", sb.trim().toString());
2355 
2356         sb.set(" \u0000 a b c \u0000 ");
2357         assertEquals("a b c", sb.trim().toString());
2358 
2359         sb.set("a b c");
2360         assertEquals("a b c", sb.trim().toString());
2361     }
2362 
2363     @Test
2364     void testWrap_CharArray() {
2365         assertThrows(NullPointerException.class, () -> TextStringBuilder.wrap(null));
2366         //
2367         final TextStringBuilder initEmpty = TextStringBuilder.wrap(ArrayUtils.EMPTY_CHAR_ARRAY);
2368         assertEquals(0, initEmpty.size());
2369         assertEquals(0, initEmpty.length());
2370         initEmpty.append('a');
2371         assertEquals(1, initEmpty.size());
2372         assertEquals(1, initEmpty.length());
2373         //
2374         final char[] test = "abc".toCharArray();
2375         final TextStringBuilder builder = TextStringBuilder.wrap(test);
2376         assertArrayEquals(test, builder.getBuffer());
2377         assertEquals(test.length, builder.length());
2378         assertEquals(test.length, builder.size());
2379         assertFalse(builder.isReallocated());
2380         builder.ensureCapacity(builder.capacity() * 2);
2381         assertFalse(Arrays.equals(test, builder.getBuffer()));
2382         assertTrue(builder.isReallocated());
2383     }
2384 
2385     private void testWrap_CharArray_Int(final String test, final int expectedLen) {
2386         final char[] input = "abc".toCharArray();
2387         final TextStringBuilder builder = TextStringBuilder.wrap(input, expectedLen);
2388         assertArrayEquals(input, builder.getBuffer());
2389         assertEquals(expectedLen, builder.length());
2390         assertEquals(expectedLen, builder.size());
2391         assertFalse(builder.isReallocated());
2392         builder.ensureCapacity(builder.capacity() * 2);
2393         assertFalse(Arrays.equals(input, builder.getBuffer()));
2394         assertTrue(builder.isReallocated());
2395     }
2396 
2397     @Test
2398     void testWrap_CharArray_Int_0() {
2399         testWrap_CharArray_Int("abc", 0);
2400     }
2401 
2402     @Test
2403     void testWrap_CharArray_Int_1() {
2404         testWrap_CharArray_Int("abc", 1);
2405     }
2406 
2407     @Test
2408     void testWrap_CharArray_Int_2() {
2409         testWrap_CharArray_Int("abc", 2);
2410     }
2411 
2412     @Test
2413     void testWrap_CharArray_Int_3() {
2414         testWrap_CharArray_Int("abc", 3);
2415     }
2416 
2417     @Test
2418     void testWrap_CharArray_Int_Empty_0() {
2419         final TextStringBuilder initEmpty = TextStringBuilder.wrap(ArrayUtils.EMPTY_CHAR_ARRAY, 0);
2420         assertEquals(0, initEmpty.size());
2421         assertEquals(0, initEmpty.length());
2422         initEmpty.append('a');
2423         assertEquals(1, initEmpty.size());
2424         assertEquals(1, initEmpty.length());
2425     }
2426 
2427     @Test
2428     void testWrap_CharArray_Int_Exceptions() {
2429         assertThrows(NullPointerException.class, () -> TextStringBuilder.wrap(null, 0));
2430         assertThrows(IllegalArgumentException.class, () -> TextStringBuilder.wrap("abc".toCharArray(), -1));
2431         assertThrows(IllegalArgumentException.class, () -> TextStringBuilder.wrap(ArrayUtils.EMPTY_CHAR_ARRAY, 1));
2432     }
2433 }