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