/* * Copyright 2005-2015 by BerryWorks Software, LLC. All rights reserved. */ package com.berryworks.edireader.util.base64; import com.berryworks.edireader.util.Conversion; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import static org.junit.Assert.assertEquals; public class TestDecoding { private byte[] bytes; private AbstractDecoder decoder; private DecoderFrontEnd frontEnd; private DecoderBackEnd backEnd; private List<Integer> emission; @Before public void setUp() { decoder = new AbstractDecoder() { protected void emit(byte b) { emission.add((int) b); } }; frontEnd = new DecoderFrontEnd() { protected void emit(byte b) { emission.add((int) b); } protected void endOfData() { } }; backEnd = new DecoderBackEnd() { protected void emit(byte b) { emission.add((int) b); } }; emission = new ArrayList<>(); } @Test public void testCase1() throws IOException { /** * Original bytes: 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 * Emitted by front end: 101010 | 10 | 1010 | 1010 | 10 | 101010 | 101010 | 10 | 1010 | 1010 | 10 | 101010 * assembled by back end: 101010 | 101010 | 101010 | 101010 | 101010 | 101010 | 101010 | 101010 * encoding: q | q | q | q | q | q | q | q * decoding first step: 101010 | 101010 | 101010 | 101010 | 101010 | 101010 | 101010 | 101010 * decoding front end: 101010 | 10 | 1010 | 1010 | 10 | 101010 | 101010 | 10 | 1010 | 1010 | 10 | 101010 * Emitted by back end: 10101010 | 10101010 | 10101010 | 10101010 | 10101010 | 10101010 */ bytes = Conversion.toByteArray("qqqqqqqq".toCharArray()); for (byte aByte : bytes) frontEnd.consume(aByte); bytes = toByteArray(new int[]{42, 2, 10, 10, 2, 42, 42, 2, 10, 10, 2, 42}); assertEqualsByteArray(bytes, resetEmission()); for (byte aByte : bytes) backEnd.consume(aByte); assertEqualsByteArray(new int[]{170, 170, 170, 170, 170, 170}, resetEmission()); } @Test public void testCase1b() throws IOException { decoder.decode("qqqqqqqq"); assertEqualsByteArray(new int[]{170, 170, 170, 170, 170, 170}, resetEmission()); } @Test public void testCase2() throws IOException { /** * Original bytes: 00000000 | 00000001 | 0000010 | 00000011 * Emitted by front end: 000000 | 00 | 0000 | 0001 | 00 | 000010 | 000000 | 11 * assembled by back end: 000000 | 00 0000 | 0001 00 | 000010 | 000000 | 11 0000 | = | = * encoding: A | A | E | C | A | w | = | = * decoding first step: 000000 | 00 0000 | 0001 00 | 000010 | 000000 | 11 0000 * decoding front end: 000000 | 00 | 0000 | 0001 | 00 | 000010 | 000000 | 11 | 0000 * Emitted by back end: 00000000 | 00000001 | 0000010 | 00000011 */ bytes = Conversion.toByteArray("AAECAw==".toCharArray()); for (byte aByte : bytes) frontEnd.consume(aByte); bytes = toByteArray(new int[]{0, 0, 0, 1, 0, 2, 0, 3, 0}); assertEqualsByteArray(bytes, resetEmission()); for (byte aByte : bytes) backEnd.consume(aByte); bytes = toByteArray(new int[]{0, 1, 2, 3}); assertEqualsByteArray(bytes, resetEmission()); } @Test public void testCase2b() throws IOException { decoder.decode("AAECAw=="); assertEqualsByteArray(new byte[]{0, 1, 2, 3}, resetEmission()); } @Test public void testCase3() throws IOException { /** * Original bytes: 11111101 | 11111110 | 11111111 | 11111110 | 11111101 * Emitted by front end: 111111 | 01 | 1111 | 1110 | 11 | 111111 | 111111 | 10 | 1111 | 1101 * assembled by back end: 111111 | 01 1111 | 1110 11 | 111111 | 111111 | 10 1111 | 1101 00 | = * encoding: / | f | 7 | / | / | v | 0 | = * decoding first step: 111111 | 01 1111 | 1110 11 | 111111 | 111111 | 10 1111 | 1101 00 * decoding front end: 111111 | 01 | 1111 | 1110 | 11 | 111111 | 111111 | 10 | 1111 | 1101 | 00 * Emitted by back end: 11111101 | 11111110 | 11111111 | 11111110 | 11111101 */ bytes = Conversion.toByteArray("/f7//v0=".toCharArray()); for (byte aByte : bytes) frontEnd.consume(aByte); bytes = new byte[]{63, 1, 15, 14, 3, 63, 63, 2, 15, 13, 0}; assertEqualsByteArray(bytes, resetEmission()); for (byte aByte : bytes) backEnd.consume(aByte); assertEqualsByteArray(new int[]{253, 254, 255, 254, 253}, resetEmission()); } @Test public void testCase3b() throws IOException { decoder.decode("/f7//v0="); assertEqualsByteArray(new int[]{253, 254, 255, 254, 253}, resetEmission()); } @Test public void testEachEncodeVariation() throws IOException { bytes = Conversion.toByteArray("/f7//v0=".toCharArray()); decoder.decode(new ByteArrayInputStream(bytes)); assertEqualsByteArray(new int[]{253, 254, 255, 254, 253}, resetEmission()); decoder.decode(bytes); assertEqualsByteArray(new int[]{253, 254, 255, 254, 253}, resetEmission()); decoder.decode("MDEyMzQ1Njc4OUFiQ2RFZgoNCQ=="); assertEqualsByteArray( Conversion.toByteArray("0123456789AbCdEf\n\r\t".toCharArray()), resetEmission()); } public void assertEqualsByteArray(String string, List<Integer> list) { assertEqualsByteArray(toByteArray(string), list); } public void assertEqualsByteArray(byte[] bytes, List list) { assertEquals("array length does not match List size: ", bytes.length, list.size()); Iterator it = list.iterator(); for (int i = 0; i < bytes.length; i++) { Object o = it.next(); if (o instanceof Integer) { Integer iValue = (int) bytes[i]; char c[] = Conversion.toCharArray(new byte[]{(byte) iValue.intValue()}); char d[] = Conversion.toCharArray(new byte[]{((Integer) o).byteValue()}); assertEquals("mismatched values at index " + i + ": (expecting char " + d[0] + " instead of " + c[0] + "): ", iValue, o); } else { throw new RuntimeException("Type " + o.getClass().getName() + " not supported in assertEqualsByteArra()"); } } } public void assertEqualsByteArray(int[] ints, List list) { assertEqualsByteArray(toByteArray(ints), list); } private byte[] toByteArray(int[] ints) { byte[] result = new byte[ints.length]; for (int i = 0; i < ints.length; i++) result[i] = (byte) ints[i]; return result; } private byte[] toByteArray(String s) { return Conversion.toByteArray(s.toCharArray()); } private List resetEmission() { List result = emission; emission = new ArrayList<>(); return result; } }