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.codec.binary;
19  
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.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  
25  import java.io.UnsupportedEncodingException;
26  import java.nio.ByteBuffer;
27  import java.nio.charset.Charset;
28  import java.nio.charset.StandardCharsets;
29  import java.util.function.Function;
30  import java.util.stream.Stream;
31  
32  import org.junit.jupiter.api.Named;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.params.ParameterizedTest;
35  import org.junit.jupiter.params.provider.Arguments;
36  import org.junit.jupiter.params.provider.MethodSource;
37  
38  /**
39   * Tests {@link StringUtils}
40   */
41  class StringUtilsTest {
42  
43      private static final byte[] BYTES_FIXTURE = { 'a', 'b', 'c' };
44  
45      // This is valid input for UTF-16BE
46      private static final byte[] BYTES_FIXTURE_16BE = { 0, 'a', 0, 'b', 0, 'c' };
47  
48      // This is valid for UTF-16LE
49      private static final byte[] BYTES_FIXTURE_16LE = { 'a', 0, 'b', 0, 'c', 0 };
50  
51      private static final String STRING_FIXTURE = "ABC";
52  
53      static Stream<Arguments> getBytesCases() {
54          return Stream.of(
55                  Arguments.of(StandardCharsets.ISO_8859_1,
56                          Named.<Function<String, byte[]>>of("getBytesIso8859_1", StringUtils::getBytesIso8859_1)),
57                  Arguments.of(StandardCharsets.US_ASCII,
58                          Named.<Function<String, byte[]>>of("getBytesUsAscii", StringUtils::getBytesUsAscii)),
59                  Arguments.of(StandardCharsets.UTF_16,
60                          Named.<Function<String, byte[]>>of("getBytesUtf16", StringUtils::getBytesUtf16)),
61                  Arguments.of(StandardCharsets.UTF_16BE,
62                          Named.<Function<String, byte[]>>of("getBytesUtf16Be", StringUtils::getBytesUtf16Be)),
63                  Arguments.of(StandardCharsets.UTF_16LE,
64                          Named.<Function<String, byte[]>>of("getBytesUtf16Le", StringUtils::getBytesUtf16Le)),
65                  Arguments.of(StandardCharsets.UTF_8,
66                          Named.<Function<String, byte[]>>of("getBytesUtf8", StringUtils::getBytesUtf8)));
67      }
68  
69      static Stream<Arguments> mixedEqualityCases() {
70          return Stream.of(
71                  Arguments.of(Named.of("String(abc)", "abc"), Named.of("StringBuilder(abc)", new StringBuilder("abc")), true),
72                  Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), Named.of("String(abcd)", "abcd"), false),
73                  Arguments.of(Named.of("String(abcd)", "abcd"), Named.of("StringBuilder(abc)", new StringBuilder("abc")), false),
74                  Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), Named.of("String(ABC)", "ABC"), false));
75      }
76  
77      static Stream<Arguments> newStringCases() {
78          return Stream.of(
79                  Arguments.of(StandardCharsets.ISO_8859_1,
80                          Named.<Function<byte[], String>>of("newStringIso8859_1", StringUtils::newStringIso8859_1), BYTES_FIXTURE),
81                  Arguments.of(StandardCharsets.US_ASCII,
82                          Named.<Function<byte[], String>>of("newStringUsAscii", StringUtils::newStringUsAscii), BYTES_FIXTURE),
83                  Arguments.of(StandardCharsets.UTF_16,
84                          Named.<Function<byte[], String>>of("newStringUtf16", StringUtils::newStringUtf16), BYTES_FIXTURE),
85                  Arguments.of(StandardCharsets.UTF_16BE,
86                          Named.<Function<byte[], String>>of("newStringUtf16Be", StringUtils::newStringUtf16Be), BYTES_FIXTURE_16BE),
87                  Arguments.of(StandardCharsets.UTF_16LE,
88                          Named.<Function<byte[], String>>of("newStringUtf16Le", StringUtils::newStringUtf16Le), BYTES_FIXTURE_16LE),
89                  Arguments.of(StandardCharsets.UTF_8,
90                          Named.<Function<byte[], String>>of("newStringUtf8", StringUtils::newStringUtf8), BYTES_FIXTURE));
91      }
92  
93      static Stream<Named<Function<byte[], String>>> newStringFunctions() {
94          return Stream.of(
95                  Named.<Function<byte[], String>>of("newStringIso8859_1", StringUtils::newStringIso8859_1),
96                  Named.<Function<byte[], String>>of("newStringUsAscii", StringUtils::newStringUsAscii),
97                  Named.<Function<byte[], String>>of("newStringUtf16", StringUtils::newStringUtf16),
98                  Named.<Function<byte[], String>>of("newStringUtf16Be", StringUtils::newStringUtf16Be),
99                  Named.<Function<byte[], String>>of("newStringUtf16Le", StringUtils::newStringUtf16Le),
100                 Named.<Function<byte[], String>>of("newStringUtf8", StringUtils::newStringUtf8));
101     }
102 
103     static Stream<Arguments> stringBuilderEqualityCases() {
104         return Stream.of(
105                 Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), null, false),
106                 Arguments.of(null, Named.of("StringBuilder(abc)", new StringBuilder("abc")), false),
107                 Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), Named.of("StringBuilder(abc)", new StringBuilder("abc")), true),
108                 Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), Named.of("StringBuilder(abcd)", new StringBuilder("abcd")), false),
109                 Arguments.of(Named.of("StringBuilder(abcd)", new StringBuilder("abcd")), Named.of("StringBuilder(abc)", new StringBuilder("abc")), false),
110                 Arguments.of(Named.of("StringBuilder(abc)", new StringBuilder("abc")), Named.of("StringBuilder(ABC)", new StringBuilder("ABC")), false));
111     }
112 
113     static Stream<Arguments> stringEqualityCases() {
114         return Stream.of(
115                 Arguments.of(null, null, true),
116                 Arguments.of(Named.of("String(abc)", "abc"), null, false),
117                 Arguments.of(null, Named.of("String(abc)", "abc"), false),
118                 Arguments.of(Named.of("String(abc)", "abc"), Named.of("String(abc)", "abc"), true),
119                 Arguments.of(Named.of("String(abc)", "abc"), Named.of("String(abcd)", "abcd"), false),
120                 Arguments.of(Named.of("String(abcd)", "abcd"), Named.of("String(abc)", "abc"), false),
121                 Arguments.of(Named.of("String(abc)", "abc"), Named.of("String(ABC)", "ABC"), false));
122     }
123 
124     @Test
125     void testByteBufferUtf8() {
126         assertNull(StringUtils.getByteBufferUtf8(null), "Should be null safe");
127         final String text = "asdhjfhsadiogasdjhagsdygfjasfgsdaksjdhfk";
128         final ByteBuffer bb = StringUtils.getByteBufferUtf8(text);
129         assertArrayEquals(text.getBytes(StandardCharsets.UTF_8), bb.array());
130     }
131 
132     /**
133      * We could make the constructor private but there does not seem to be a point to jumping through extra code hoops
134      * to restrict instantiation right now.
135      */
136     @Test
137     void testConstructor() {
138         new StringUtils();
139     }
140 
141     @ParameterizedTest(name = "{0}, {1}: {2}")
142     @MethodSource("mixedEqualityCases")
143     void testEqualsMixedCharSequences(final CharSequence left, final CharSequence right, final boolean expected) {
144         assertEquals(expected, StringUtils.equals(left, right));
145     }
146 
147     @ParameterizedTest(name = "{0}, {1}: {2}")
148     @MethodSource("stringBuilderEqualityCases")
149     void testEqualsStringBuilders(final CharSequence left, final CharSequence right, final boolean expected) {
150         assertEquals(expected, StringUtils.equals(left, right));
151     }
152 
153     @ParameterizedTest(name = "{0}, {1}: {2}")
154     @MethodSource("stringEqualityCases")
155     void testEqualsStrings(final CharSequence left, final CharSequence right, final boolean expected) {
156         assertEquals(expected, StringUtils.equals(left, right));
157     }
158 
159     @ParameterizedTest(name = "{0}: {1}")
160     @MethodSource("getBytesCases")
161     void testGetBytes(final Charset charset, final Function<String, byte[]> encoder) throws UnsupportedEncodingException {
162         final String charsetName = charset.name();
163         final byte[] expected = STRING_FIXTURE.getBytes(charsetName);
164         assertArrayEquals(expected, StringUtils.getBytesUnchecked(STRING_FIXTURE, charsetName));
165         assertArrayEquals(expected, encoder.apply(STRING_FIXTURE));
166     }
167 
168     @Test
169     void testGetBytesUncheckedBadName() {
170         assertThrows(IllegalStateException.class, () -> StringUtils.getBytesUnchecked(STRING_FIXTURE, "UNKNOWN"));
171     }
172 
173     @Test
174     void testGetBytesUncheckedNullInput() {
175         assertNull(StringUtils.getBytesUnchecked(null, "UNKNOWN"));
176     }
177 
178     @ParameterizedTest(name = "{0}: {1}")
179     @MethodSource("newStringCases")
180     void testNewString(final Charset charset, final Function<byte[], String> decoder, final byte[] input) throws UnsupportedEncodingException {
181         final String charsetName = charset.name();
182         assertEquals(new String(BYTES_FIXTURE, charsetName), StringUtils.newString(BYTES_FIXTURE, charsetName));
183         assertEquals(new String(input, charsetName), decoder.apply(input));
184     }
185 
186     @Test
187     void testNewStringBadEnc() {
188         assertThrows(IllegalStateException.class, () -> StringUtils.newString(BYTES_FIXTURE, "UNKNOWN"));
189     }
190 
191     @Test
192     void testNewStringNullInput() {
193         assertNull(StringUtils.newString(null, "UNKNOWN"));
194     }
195 
196     @ParameterizedTest(name = "{0}")
197     @MethodSource("newStringFunctions")
198     void testNewStringNullInput_CODEC229(final Function<byte[], String> decoder) {
199         assertNull(decoder.apply(null));
200     }
201 }