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