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.lang3;
19  
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.assertTrue;
24  
25  import java.util.stream.Stream;
26  
27  import org.junit.jupiter.api.Test;
28  import org.junit.jupiter.params.ParameterizedTest;
29  import org.junit.jupiter.params.provider.MethodSource;
30  import org.junitpioneer.jupiter.DefaultLocale;
31  
32  /**
33   * Tests {@link Strings}.
34   */
35  class StringsTest extends AbstractLangTest {
36  
37      public static Stream<Strings> stringsFactory() {
38          return Stream.of(Strings.CS, Strings.CI);
39      }
40  
41      @Test
42      void testBuilder() {
43          assertTrue(Strings.builder().setIgnoreCase(false).get().isCaseSensitive());
44          assertFalse(Strings.builder().setIgnoreCase(true).get().isCaseSensitive());
45          //
46          assertTrue(Strings.builder().setNullIsLess(false).get().isCaseSensitive());
47          assertTrue(Strings.builder().setNullIsLess(true).get().isCaseSensitive());
48      }
49  
50      @Test
51      void testBuilderDefaults() {
52          final Strings strings = Strings.builder().get();
53          assertTrue(strings.isCaseSensitive());
54      }
55  
56      @Test
57      void testCaseInsensitiveConstant() {
58          assertNotNull(Strings.CI);
59          assertFalse(Strings.CI.isCaseSensitive());
60      }
61  
62      /**
63       * For an empty search the case-insensitive {@code indexOf} returned {@code startPos} unchanged once it reached
64       * {@code str.length() + 1}, so a start position one past the end yielded an index beyond the string instead of
65       * {@code -1}.
66       */
67      @Test
68      void testCaseInsensitiveIndexOfEmptyOutOfRange() {
69          // repro: returned 4 (past the end of a length-3 string) before the fix
70          final String emptySearch = StringUtils.EMPTY;
71          assertEquals(-1, Strings.CI.indexOf("abc", emptySearch, 4));
72          // documented out-of-range example, also -1
73          assertEquals(-1, Strings.CI.indexOf("abc", emptySearch, 9));
74          // the end position is still a valid empty match
75          assertEquals(3, Strings.CI.indexOf("abc", emptySearch, 3));
76          assertEquals(2, Strings.CI.indexOf("aabaabaa", emptySearch, 2));
77          assertEquals(0, Strings.CI.indexOf(emptySearch, emptySearch, 0));
78          assertEquals(0, Strings.CI.indexOf(emptySearch, emptySearch, -1));
79          assertEquals(0, Strings.CI.indexOf("a", emptySearch, -1));
80      }
81  
82      /**
83       * {@code U+0130} lower-cases to the two-char sequence {@code "i̇"} outside Turkish locales, so pre-lower-casing the
84       * search argument made the case-insensitive replace look for a two-char needle that no longer matches the single source
85       * character.
86       */
87      @Test
88      @DefaultLocale("en")
89      void testCaseInsensitiveReplaceLengthChangingLowerCase() {
90          assertEquals("aXb", Strings.CI.replace("aİb", "İ", "X", -1));
91          assertEquals("x_y_z", Strings.CI.replace("xİyİz", "İ", "_", -1));
92          assertEquals("X", Strings.CI.replaceOnce("İ", "İ", "X"));
93      }
94  
95      /**
96       * Expanding the existing test group {@link StringUtilsStartsEndsWithTest#testStartsWithAny()} to include case-insensitive cases
97       */
98      @Test
99      void testCaseInsensitiveStartsWithAny() {
100         // LANG-1682
101         assertFalse(Strings.CI.startsWithAny(null, (String[]) null));
102         assertFalse(Strings.CI.startsWithAny(null, "aBc"));
103         assertFalse(Strings.CI.startsWithAny("AbCxYz", (String[]) null));
104         assertFalse(Strings.CI.startsWithAny("AbCxYz"));
105         assertTrue(Strings.CI.startsWithAny("AbCxYz", "aBc"));
106         assertTrue(Strings.CI.startsWithAny("AbCxYz", null, "XyZ", "aBc"));
107         assertFalse(Strings.CI.startsWithAny("AbCxYz", null, "XyZ", "aBcD"));
108         assertTrue(Strings.CI.startsWithAny("AbCxYz", ""));
109         assertTrue(Strings.CI.startsWithAny("abcxyz", null, "XyZ", "ABCX"));
110         assertTrue(Strings.CI.startsWithAny("ABCXYZ", null, "XyZ", "abc"));
111 
112         assertTrue(Strings.CI.startsWithAny("AbCxYz", new StringBuilder("XyZ"), new StringBuffer("aBc")));
113         assertTrue(Strings.CI.startsWithAny(new StringBuffer("AbCxYz"), new StringBuilder("XyZ"), new StringBuffer("abc")));
114     }
115 
116     @Test
117     void testCaseSensitiveConstant() {
118         assertNotNull(Strings.CS);
119         assertTrue(Strings.CS.isCaseSensitive());
120     }
121 
122     @Test
123     void testComputeInitialCapacityDoesNotReturnSafeMaxDueToOverflow() {
124         final int textLength = 100;
125         final int searchLength = 0;
126         final int replacementLength = Integer.MAX_VALUE;
127         final int max = 1;
128         // Expected mathematical result:
129         // 100 + (2147483647 - 0) * 1 = 2147483747
130         // which exceeds SAFE_MAX_ARRAY_LENGTH (2147483639)
131         final int expected = Integer.MAX_VALUE - 8;
132         assertEquals(expected, Strings.initialCapacity(textLength, searchLength, replacementLength, max));
133     }
134 
135     @Test
136     void testComputeInitialCapacityLargeInputsDoNotIncorrectlyClampToSafeMax() {
137         final int result = Strings.initialCapacity(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE - 500, Integer.MAX_VALUE, 1);
138         // Growth = 500
139         // Expected = Integer.MAX_VALUE - 500
140         assertEquals(Integer.MAX_VALUE - 500, result);
141     }
142 
143     @Test
144     void testComputeInitialCapacityNeverOverflowsForMaxValueInputs() {
145         final int capacity = Strings.initialCapacity(Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 64);
146         assertEquals(ArrayUtils.SAFE_MAX_ARRAY_LENGTH, capacity);
147     }
148 
149     @Test
150     void testComputeInitialCapacityReplacementGrowthDoesNotOverflowInt() {
151         final int capacity = Strings.initialCapacity(0, 0, 50_000_000, 64);
152         assertEquals(ArrayUtils.SAFE_MAX_ARRAY_LENGTH, capacity);
153     }
154 
155     @Test
156     void testComputeInitialCapacityReturnsSmallerValueWhenResultIsBelowSafeMax() {
157         final int textLength = 100;
158         final int searchLength = 0;
159         final int replacementLength = 1000;
160         final int max = 1;
161         final int expected = 1100;
162         assertEquals(expected, Strings.initialCapacity(textLength, searchLength, replacementLength, max));
163     }
164 
165     @ParameterizedTest
166     @MethodSource("stringsFactory")
167     void testEqualsCharSequence(final Strings strings) {
168         final CharSequence nullCharSequence = null;
169         assertTrue(strings.equals(nullCharSequence, nullCharSequence));
170         assertFalse(strings.equals(nullCharSequence, ""));
171         assertFalse(strings.equals("", nullCharSequence));
172     }
173 
174     @ParameterizedTest
175     @MethodSource("stringsFactory")
176     void testEqualsStrings(final Strings strings) {
177         final String nullStr = null;
178         assertTrue(strings.equals(nullStr, nullStr));
179         assertFalse(strings.equals(nullStr, ""));
180         assertFalse(strings.equals("", nullStr));
181     }
182 }