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 junit.framework.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(String name) {
049            final String source = "the quick brown dog jumped over the lazy fox";
050            try {
051                byte[] bytes = source.getBytes(name);
052                String str = new String(bytes, name);
053                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 (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 (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(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 (DecoderException e) {
106                // Expected exception
107            }
108        }
109    
110        private void log(String s) {
111            if (LOG) {
112                System.out.println(s);
113                System.out.flush();
114            }
115        }
116    
117        private void log(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 (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(String name, String parent) throws UnsupportedEncodingException, DecoderException {
139            if (charsetSanityCheck(name) == false) {
140                return;
141            }
142            log(parent + "=" + name);
143            Hex customCodec = new Hex(name);
144            // source data
145            String sourceString = "Hello World";
146            byte[] sourceBytes = sourceString.getBytes(name);
147            // test 1
148            // encode source to hex string to bytes with charset
149            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            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            Hex utf8Codec = new Hex();
161            expectedHexString = "48656c6c6f20576f726c64";
162            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            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 (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 (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 (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 (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 (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 (EncoderException e) {
260                // Expected exception
261            }
262        }
263    
264        @Test
265        public void testEncodeDecodeRandom() throws DecoderException, EncoderException {
266            Random random = new Random();
267    
268            Hex hex = new Hex();
269            for (int i = 5; i > 0; i--) {
270                byte[] data = new byte[random.nextInt(10000) + 1];
271                random.nextBytes(data);
272    
273                // static API
274                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                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            char[] c = Hex.encodeHex(new byte[36]);
307            assertEquals("000000000000000000000000000000000000000000000000000000000000000000000000", new String(c));
308        }
309    
310        @Test
311        public void testHelloWorldLowerCaseHex() {
312            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            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    }