001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.codec.binary;
019
020 import static org.junit.Assert.assertEquals;
021 import static org.junit.Assert.assertFalse;
022 import static org.junit.Assert.assertTrue;
023 import static org.junit.Assert.fail;
024
025 import java.io.UnsupportedEncodingException;
026 import java.nio.charset.Charset;
027 import java.nio.charset.UnsupportedCharsetException;
028 import java.util.Arrays;
029 import java.util.Random;
030
031 import org.junit.Assert;
032
033 import org.apache.commons.codec.DecoderException;
034 import org.apache.commons.codec.EncoderException;
035 import org.junit.Test;
036
037 /**
038 * Tests {@link org.apache.commons.codec.binary.Hex}.
039 *
040 * @version $Id: HexTest.html 889935 2013-12-11 05:05:13Z ggregory $
041 */
042 public class HexTest {
043
044 private static final String BAD_ENCODING_NAME = "UNKNOWN";
045
046 private final static boolean LOG = false;
047
048 private boolean charsetSanityCheck(final String name) {
049 final String source = "the quick brown dog jumped over the lazy fox";
050 try {
051 final byte[] bytes = source.getBytes(name);
052 final String str = new String(bytes, name);
053 final boolean equals = source.equals(str);
054 if (equals == false) {
055 // Here with:
056 //
057 // Java Sun 1.4.2_19 x86 32-bits on Windows XP
058 // JIS_X0212-1990
059 // x-JIS0208
060 //
061 // Java Sun 1.5.0_17 x86 32-bits on Windows XP
062 // JIS_X0212-1990
063 // x-IBM834
064 // x-JIS0208
065 // x-MacDingbat
066 // x-MacSymbol
067 //
068 // Java Sun 1.6.0_14 x86 32-bits
069 // JIS_X0212-1990
070 // x-IBM834
071 // x-JIS0208
072 // x-MacDingbat
073 // x-MacSymbol
074 //
075 log("FAILED charsetSanityCheck=Interesting Java charset oddity: Roundtrip failed for " + name);
076 }
077 return equals;
078 } catch (final UnsupportedEncodingException e) {
079 // Should NEVER happen since we are getting the name from the Charset class.
080 if (LOG) {
081 log("FAILED charsetSanityCheck=" + name + ", e=" + e);
082 log(e);
083 }
084 return false;
085 } catch (final UnsupportedOperationException e) {
086 // Caught here with:
087 // x-JISAutoDetect on Windows XP and Java Sun 1.4.2_19 x86 32-bits
088 // x-JISAutoDetect on Windows XP and Java Sun 1.5.0_17 x86 32-bits
089 // x-JISAutoDetect on Windows XP and Java Sun 1.6.0_14 x86 32-bits
090 if (LOG) {
091 log("FAILED charsetSanityCheck=" + name + ", e=" + e);
092 log(e);
093 }
094 return false;
095 }
096 }
097
098 /**
099 * @param data
100 */
101 private void checkDecodeHexOddCharacters(final char[] data) {
102 try {
103 Hex.decodeHex(data);
104 fail("An exception wasn't thrown when trying to decode an odd number of characters");
105 } catch (final DecoderException e) {
106 // Expected exception
107 }
108 }
109
110 private void log(final String s) {
111 if (LOG) {
112 System.out.println(s);
113 System.out.flush();
114 }
115 }
116
117 private void log(final Throwable t) {
118 if (LOG) {
119 t.printStackTrace(System.out);
120 System.out.flush();
121 }
122 }
123
124 @Test
125 public void testCustomCharset() throws UnsupportedEncodingException, DecoderException {
126 for (final String name : Charset.availableCharsets().keySet()) {
127 testCustomCharset(name, "testCustomCharset");
128 }
129 }
130
131 /**
132 * @param name
133 * @param parent
134 * TODO
135 * @throws UnsupportedEncodingException
136 * @throws DecoderException
137 */
138 private void testCustomCharset(final String name, final String parent) throws UnsupportedEncodingException, DecoderException {
139 if (charsetSanityCheck(name) == false) {
140 return;
141 }
142 log(parent + "=" + name);
143 final Hex customCodec = new Hex(name);
144 // source data
145 final String sourceString = "Hello World";
146 final byte[] sourceBytes = sourceString.getBytes(name);
147 // test 1
148 // encode source to hex string to bytes with charset
149 final byte[] actualEncodedBytes = customCodec.encode(sourceBytes);
150 // encode source to hex string...
151 String expectedHexString = Hex.encodeHexString(sourceBytes);
152 // ... and get the bytes in the expected charset
153 final byte[] expectedHexStringBytes = expectedHexString.getBytes(name);
154 Assert.assertTrue(Arrays.equals(expectedHexStringBytes, actualEncodedBytes));
155 // test 2
156 String actualStringFromBytes = new String(actualEncodedBytes, name);
157 assertEquals(name + ", expectedHexString=" + expectedHexString + ", actualStringFromBytes=" + actualStringFromBytes,
158 expectedHexString, actualStringFromBytes);
159 // second test:
160 final Hex utf8Codec = new Hex();
161 expectedHexString = "48656c6c6f20576f726c64";
162 final byte[] decodedUtf8Bytes = (byte[]) utf8Codec.decode(expectedHexString);
163 actualStringFromBytes = new String(decodedUtf8Bytes, utf8Codec.getCharset());
164 // sanity check:
165 assertEquals(name, sourceString, actualStringFromBytes);
166 // actual check:
167 final byte[] decodedCustomBytes = customCodec.decode(actualEncodedBytes);
168 actualStringFromBytes = new String(decodedCustomBytes, name);
169 assertEquals(name, sourceString, actualStringFromBytes);
170 }
171
172 @Test(expected=UnsupportedCharsetException.class)
173 public void testCustomCharsetBadName() {
174 new Hex(BAD_ENCODING_NAME);
175 }
176
177 @Test
178 public void testCustomCharsetToString() {
179 assertTrue(new Hex().toString().indexOf(Hex.DEFAULT_CHARSET_NAME) >= 0);
180 }
181
182 @Test
183 public void testDecodeArrayOddCharacters() {
184 try {
185 new Hex().decode(new byte[]{65});
186 fail("An exception wasn't thrown when trying to decode an odd number of characters");
187 } catch (final DecoderException e) {
188 // Expected exception
189 }
190 }
191
192 @Test
193 public void testDecodeBadCharacterPos0() {
194 try {
195 new Hex().decode("q0");
196 fail("An exception wasn't thrown when trying to decode an illegal character");
197 } catch (final DecoderException e) {
198 // Expected exception
199 }
200 }
201
202 @Test
203 public void testDecodeBadCharacterPos1() {
204 try {
205 new Hex().decode("0q");
206 fail("An exception wasn't thrown when trying to decode an illegal character");
207 } catch (final DecoderException e) {
208 // Expected exception
209 }
210 }
211
212 @Test
213 public void testDecodeClassCastException() {
214 try {
215 new Hex().decode(new int[]{65});
216 fail("An exception wasn't thrown when trying to decode.");
217 } catch (final DecoderException e) {
218 // Expected exception
219 }
220 }
221
222 @Test
223 public void testDecodeHexOddCharacters1() {
224 checkDecodeHexOddCharacters(new char[]{'A'});
225 }
226
227 @Test
228 public void testDecodeHexOddCharacters3() {
229 checkDecodeHexOddCharacters(new char[]{'A', 'B', 'C'});
230 }
231
232 @Test
233 public void testDecodeHexOddCharacters5() {
234 checkDecodeHexOddCharacters(new char[]{'A', 'B', 'C', 'D', 'E'});
235 }
236
237 @Test
238 public void testDecodeStringOddCharacters() {
239 try {
240 new Hex().decode("6");
241 fail("An exception wasn't thrown when trying to decode an odd number of characters");
242 } catch (final DecoderException e) {
243 // Expected exception
244 }
245 }
246
247 @Test
248 public void testDencodeEmpty() throws DecoderException {
249 assertTrue(Arrays.equals(new byte[0], Hex.decodeHex(new char[0])));
250 assertTrue(Arrays.equals(new byte[0], new Hex().decode(new byte[0])));
251 assertTrue(Arrays.equals(new byte[0], (byte[]) new Hex().decode("")));
252 }
253
254 @Test
255 public void testEncodeClassCastException() {
256 try {
257 new Hex().encode(new int[]{65});
258 fail("An exception wasn't thrown when trying to encode.");
259 } catch (final EncoderException e) {
260 // Expected exception
261 }
262 }
263
264 @Test
265 public void testEncodeDecodeRandom() throws DecoderException, EncoderException {
266 final Random random = new Random();
267
268 final Hex hex = new Hex();
269 for (int i = 5; i > 0; i--) {
270 final byte[] data = new byte[random.nextInt(10000) + 1];
271 random.nextBytes(data);
272
273 // static API
274 final char[] encodedChars = Hex.encodeHex(data);
275 byte[] decodedBytes = Hex.decodeHex(encodedChars);
276 assertTrue(Arrays.equals(data, decodedBytes));
277
278 // instance API with array parameter
279 final byte[] encodedStringBytes = hex.encode(data);
280 decodedBytes = hex.decode(encodedStringBytes);
281 assertTrue(Arrays.equals(data, decodedBytes));
282
283 // instance API with char[] (Object) parameter
284 String dataString = new String(encodedChars);
285 char[] encodedStringChars = (char[]) hex.encode(dataString);
286 decodedBytes = (byte[]) hex.decode(encodedStringChars);
287 assertTrue(Arrays.equals(StringUtils.getBytesUtf8(dataString), decodedBytes));
288
289 // instance API with String (Object) parameter
290 dataString = new String(encodedChars);
291 encodedStringChars = (char[]) hex.encode(dataString);
292 decodedBytes = (byte[]) hex.decode(new String(encodedStringChars));
293 assertTrue(Arrays.equals(StringUtils.getBytesUtf8(dataString), decodedBytes));
294 }
295 }
296
297 @Test
298 public void testEncodeEmpty() throws EncoderException {
299 assertTrue(Arrays.equals(new char[0], Hex.encodeHex(new byte[0])));
300 assertTrue(Arrays.equals(new byte[0], new Hex().encode(new byte[0])));
301 assertTrue(Arrays.equals(new char[0], (char[]) new Hex().encode("")));
302 }
303
304 @Test
305 public void testEncodeZeroes() {
306 final char[] c = Hex.encodeHex(new byte[36]);
307 assertEquals("000000000000000000000000000000000000000000000000000000000000000000000000", new String(c));
308 }
309
310 @Test
311 public void testHelloWorldLowerCaseHex() {
312 final byte[] b = StringUtils.getBytesUtf8("Hello World");
313 final String expected = "48656c6c6f20576f726c64";
314 char[] actual;
315 actual = Hex.encodeHex(b);
316 assertEquals(expected, new String(actual));
317 actual = Hex.encodeHex(b, true);
318 assertEquals(expected, new String(actual));
319 actual = Hex.encodeHex(b, false);
320 assertFalse(expected.equals(new String(actual)));
321 }
322
323 @Test
324 public void testHelloWorldUpperCaseHex() {
325 final byte[] b = StringUtils.getBytesUtf8("Hello World");
326 final String expected = "48656C6C6F20576F726C64";
327 char[] actual;
328 actual = Hex.encodeHex(b);
329 assertFalse(expected.equals(new String(actual)));
330 actual = Hex.encodeHex(b, true);
331 assertFalse(expected.equals(new String(actual)));
332 actual = Hex.encodeHex(b, false);
333 assertTrue(expected.equals(new String(actual)));
334 }
335
336 @Test
337 public void testRequiredCharset() throws UnsupportedEncodingException, DecoderException {
338 testCustomCharset("UTF-8", "testRequiredCharset");
339 testCustomCharset("UTF-16", "testRequiredCharset");
340 testCustomCharset("UTF-16BE", "testRequiredCharset");
341 testCustomCharset("UTF-16LE", "testRequiredCharset");
342 testCustomCharset("US-ASCII", "testRequiredCharset");
343 testCustomCharset("ISO8859_1", "testRequiredCharset");
344 }
345 }