1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3;
18
19 import static org.apache.commons.lang3.LangAssertions.assertIndexOutOfBoundsException;
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.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27 import static org.junit.jupiter.params.provider.Arguments.arguments;
28
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.Modifier;
31 import java.util.Random;
32 import java.util.stream.IntStream;
33 import java.util.stream.Stream;
34
35 import org.junit.jupiter.api.Test;
36 import org.junit.jupiter.params.ParameterizedTest;
37 import org.junit.jupiter.params.provider.Arguments;
38 import org.junit.jupiter.params.provider.MethodSource;
39
40
41
42
43 class CharSequenceUtilsTest extends AbstractLangTest {
44
45 private abstract static class RunTest {
46
47 abstract boolean invoke();
48
49 void run(final TestData data, final String id) {
50 if (data.throwable != null) {
51 assertThrows(data.throwable, this::invoke, id + " Expected " + data.throwable);
52 } else {
53 final boolean stringCheck = invoke();
54 assertEquals(data.expected, stringCheck, id + " Failed test " + data);
55 }
56 }
57
58 }
59
60 static class TestData {
61 final String source;
62 final boolean ignoreCase;
63 final int toffset;
64 final String other;
65 final int ooffset;
66 final int len;
67 final boolean expected;
68 final Class<? extends Throwable> throwable;
69
70 TestData(final String source, final boolean ignoreCase, final int toffset, final String other, final int ooffset, final int len,
71 final boolean expected) {
72 this.source = source;
73 this.ignoreCase = ignoreCase;
74 this.toffset = toffset;
75 this.other = other;
76 this.ooffset = ooffset;
77 this.len = len;
78 this.expected = expected;
79 this.throwable = null;
80 }
81
82 TestData(final String source, final boolean ignoreCase, final int toffset, final String other, final int ooffset, final int len,
83 final Class<? extends Throwable> throwable) {
84 this.source = source;
85 this.ignoreCase = ignoreCase;
86 this.toffset = toffset;
87 this.other = other;
88 this.ooffset = ooffset;
89 this.len = len;
90 this.expected = false;
91 this.throwable = throwable;
92 }
93
94 @Override
95 public String toString() {
96 final StringBuilder sb = new StringBuilder();
97 sb.append(source).append("[").append(toffset).append("]");
98 sb.append(ignoreCase ? " caseblind " : " samecase ");
99 sb.append(other).append("[").append(ooffset).append("]");
100 sb.append(" ").append(len).append(" => ");
101 if (throwable != null) {
102 sb.append(throwable);
103 } else {
104 sb.append(expected);
105 }
106 return sb.toString();
107 }
108 }
109
110 static class WrapperString implements CharSequence {
111 private final CharSequence inner;
112
113 WrapperString(final CharSequence inner) {
114 this.inner = inner;
115 }
116
117 @Override
118 public char charAt(final int index) {
119 return inner.charAt(index);
120 }
121
122 @Override
123 public IntStream chars() {
124 return inner.chars();
125 }
126
127 @Override
128 public IntStream codePoints() {
129 return inner.codePoints();
130 }
131
132 @Override
133 public int length() {
134 return inner.length();
135 }
136
137 @Override
138 public CharSequence subSequence(final int start, final int end) {
139 return inner.subSequence(start, end);
140 }
141
142 @Override
143 public String toString() {
144 return inner.toString();
145 }
146 }
147
148 private static final TestData[] TEST_DATA = {
149
150
151 new TestData("", true, -1, "", -1, -1, false),
152 new TestData("", true, 0, "", 0, 1, false),
153 new TestData("a", true, 0, "abc", 0, 0, true),
154 new TestData("a", true, 0, "abc", 0, 1, true),
155 new TestData("a", true, 0, null, 0, 0, NullPointerException.class),
156 new TestData(null, true, 0, null, 0, 0, NullPointerException.class),
157 new TestData(null, true, 0, "", 0, 0, NullPointerException.class),
158 new TestData("Abc", true, 0, "abc", 0, 3, true),
159 new TestData("Abc", false, 0, "abc", 0, 3, false),
160 new TestData("Abc", true, 1, "abc", 1, 2, true),
161 new TestData("Abc", false, 1, "abc", 1, 2, true),
162 new TestData("Abcd", true, 1, "abcD", 1, 2, true),
163 new TestData("Abcd", false, 1, "abcD", 1, 2, true),
164
165 };
166
167 static Stream<Arguments> lastIndexWithStandardCharSequence() {
168
169 return Stream.of(
170 arguments("abc", "b", 2, 1),
171 arguments(new StringBuilder("abc"), "b", 2, 1),
172 arguments(new StringBuffer("abc"), "b", 2, 1),
173 arguments("abc", new StringBuilder("b"), 2, 1),
174 arguments(new StringBuilder("abc"), new StringBuilder("b"), 2, 1),
175 arguments(new StringBuffer("abc"), new StringBuffer("b"), 2, 1),
176 arguments(new StringBuilder("abc"), new StringBuffer("b"), 2, 1)
177 );
178
179 }
180
181 @Test
182 void testConstructor() {
183 assertNotNull(new CharSequenceUtils());
184 final Constructor<?>[] cons = CharSequenceUtils.class.getDeclaredConstructors();
185 assertEquals(1, cons.length);
186 assertTrue(Modifier.isPublic(cons[0].getModifiers()));
187 assertTrue(Modifier.isPublic(CharSequenceUtils.class.getModifiers()));
188 assertFalse(Modifier.isFinal(CharSequenceUtils.class.getModifiers()));
189 }
190
191 @ParameterizedTest
192 @MethodSource("lastIndexWithStandardCharSequence")
193 void testLastIndexOfWithDifferentCharSequences(final CharSequence cs, final CharSequence search, final int start, final int expected) {
194 assertEquals(expected, CharSequenceUtils.lastIndexOf(cs, search, start));
195 }
196
197 @Test
198 void testNewLastIndexOf() {
199 testNewLastIndexOfSingle("808087847-1321060740-635567660180086727-925755305", "-1321060740-635567660", 21);
200 testNewLastIndexOfSingle("", "");
201 testNewLastIndexOfSingle("1", "");
202 testNewLastIndexOfSingle("", "1");
203 testNewLastIndexOfSingle("1", "1");
204 testNewLastIndexOfSingle("11", "1");
205 testNewLastIndexOfSingle("1", "11");
206
207 testNewLastIndexOfSingle("apache", "a");
208 testNewLastIndexOfSingle("apache", "p");
209 testNewLastIndexOfSingle("apache", "e");
210 testNewLastIndexOfSingle("apache", "x");
211 testNewLastIndexOfSingle("oraoraoraora", "r");
212 testNewLastIndexOfSingle("mudamudamudamuda", "d");
213
214
215
216 testNewLastIndexOfSingle("junk-ststarting", "starting");
217
218 final Random random = new Random();
219 final StringBuilder seg = new StringBuilder();
220 while (seg.length() <= CharSequenceUtils.TO_STRING_LIMIT) {
221 seg.append(random.nextInt());
222 }
223 StringBuilder original = new StringBuilder(seg);
224 testNewLastIndexOfSingle(original, seg);
225 for (int i = 0; i < 100; i++) {
226 if (random.nextDouble() < 0.5) {
227 original.append(random.nextInt() % 10);
228 } else {
229 original = new StringBuilder().append(random.nextInt() % 100).append(original);
230 }
231 testNewLastIndexOfSingle(original, seg);
232 }
233 }
234
235 private void testNewLastIndexOfSingle(final CharSequence a, final CharSequence b) {
236 final int maxa = Math.max(a.length(), b.length());
237 for (int i = -maxa - 10; i <= maxa + 10; i++) {
238 testNewLastIndexOfSingle(a, b, i);
239 }
240 testNewLastIndexOfSingle(a, b, Integer.MIN_VALUE);
241 testNewLastIndexOfSingle(a, b, Integer.MAX_VALUE);
242 }
243
244 private void testNewLastIndexOfSingle(final CharSequence a, final CharSequence b, final int start) {
245 testNewLastIndexOfSingleSingle(a, b, start);
246 testNewLastIndexOfSingleSingle(b, a, start);
247 }
248
249 private void testNewLastIndexOfSingleSingle(final CharSequence a, final CharSequence b, final int start) {
250 assertEquals(a.toString().lastIndexOf(b.toString(), start),
251 CharSequenceUtils.lastIndexOf(new WrapperString(a.toString()), new WrapperString(b.toString()), start),
252 "testNewLastIndexOf fails! original : " + a + " seg : " + b + " start : " + start);
253 }
254
255 @Test
256 void testRegionMatches() {
257 for (final TestData data : TEST_DATA) {
258 new RunTest() {
259 @Override
260 boolean invoke() {
261 return data.source.regionMatches(data.ignoreCase, data.toffset, data.other, data.ooffset, data.len);
262 }
263 }.run(data, "String");
264 new RunTest() {
265 @Override
266 boolean invoke() {
267 return CharSequenceUtils.regionMatches(data.source, data.ignoreCase, data.toffset, data.other, data.ooffset, data.len);
268 }
269 }.run(data, "CSString");
270 new RunTest() {
271 @Override
272 boolean invoke() {
273 return CharSequenceUtils.regionMatches(new StringBuilder(data.source), data.ignoreCase, data.toffset, data.other, data.ooffset, data.len);
274 }
275 }.run(data, "CSNonString");
276 }
277 }
278
279 @Test
280 void testSubSequence() {
281
282
283
284 assertNull(CharSequenceUtils.subSequence(null, -1));
285 assertNull(CharSequenceUtils.subSequence(null, 0));
286 assertNull(CharSequenceUtils.subSequence(null, 1));
287
288
289
290 assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence(StringUtils.EMPTY, 0));
291 assertEquals("012", CharSequenceUtils.subSequence("012", 0));
292 assertEquals("12", CharSequenceUtils.subSequence("012", 1));
293 assertEquals("2", CharSequenceUtils.subSequence("012", 2));
294 assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence("012", 3));
295 }
296
297 @Test
298 void testSubSequenceNegativeStart() {
299 assertIndexOutOfBoundsException(() -> CharSequenceUtils.subSequence(StringUtils.EMPTY, -1));
300 }
301
302 @Test
303 void testSubSequenceTooLong() {
304 assertIndexOutOfBoundsException(() -> CharSequenceUtils.subSequence(StringUtils.EMPTY, 1));
305 }
306
307 @Test
308 void testToCharArray() {
309 final StringBuilder builder = new StringBuilder("abcdefg");
310 final char[] expected = builder.toString().toCharArray();
311 assertArrayEquals(expected, CharSequenceUtils.toCharArray(builder));
312 assertArrayEquals(expected, CharSequenceUtils.toCharArray(builder.toString()));
313 assertArrayEquals(ArrayUtils.EMPTY_CHAR_ARRAY, CharSequenceUtils.toCharArray(null));
314 }
315 }