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