Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e121ca0a authored by Tobias Thierer's avatar Tobias Thierer
Browse files

HexDumpTest: provide test coverage

While debugging bug 160801497, I discovered that HexDump would throw
unusual exception types (RuntimeException, StringIndexOutOfBoundsException)
when given a String with non-hex characters or odd length.

Further investigation found that HexDumpTest provided minimal coverage,
and one of its test methods no longer passes.

This CL fixes the broken test, and adds more coverage. The unusual
exception types thrown on invalid hex strings are now formalized in the
test.

Test: atest FrameworksCoreTests:com.android.internal.util.HexDumpTest
Change-Id: I1dc24f803c303e3457b856d62088e09c046e31e3
parent 056630f1
Loading
Loading
Loading
Loading
+145 −2
Original line number Diff line number Diff line
@@ -16,8 +16,16 @@

package com.android.internal.util;

import static com.android.internal.util.HexDump.hexStringToByteArray;
import static com.android.internal.util.HexDump.toHexString;

import junit.framework.TestCase;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Random;

public final class HexDumpTest extends TestCase {
    public void testBytesToHexString() {
        assertEquals("abcdef", HexDump.toHexString(
@@ -25,7 +33,142 @@ public final class HexDumpTest extends TestCase {
        assertEquals("ABCDEF", HexDump.toHexString(
                new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
    }
    public void testNullArray() {
        assertEquals("(null)", HexDump.toHexString(null));

    public void testNullByteArray() {
        assertThrows(
                NullPointerException.class,
                () -> HexDump.toHexString(null));
    }

    public void testBytesToHexString_allByteValues() {
        byte[] bytes = new byte[256];
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (i % 256);
        }

        StringBuilder sb = new StringBuilder();
        for (char firstChar : "0123456789ABCDEF".toCharArray()) {
            for (char secondChar : "0123456789ABCDEF".toCharArray()) {
                sb.append(firstChar).append(secondChar);
            }
        }
        String expected = sb.toString();

        assertEquals(expected, HexDump.toHexString(bytes));
    }

    public void testRoundTrip_fromBytes() {
        Random deterministicRandom = new Random(31337); // arbitrary but deterministic
        for (int length = 0; length < 100; length++) {
            byte[] bytes = new byte[length];
            deterministicRandom.nextBytes(bytes);
            byte[] reconstruction = hexStringToByteArray(toHexString(bytes));

            assertBytesEqual(bytes, reconstruction);
        }
    }

    public void testRoundTrip_fromString() {
        String hexString = "0123456789ABCDEF72f9a3438934c378d34f32a8b932";
        for (int length = 0; length < hexString.length(); length += 2) {
            String original = hexString.substring(0, length);
            String reconstruction = toHexString(hexStringToByteArray(original));
            assertEquals(original.toUpperCase(), reconstruction);
        }
    }

    public void testToHexString_offsetLength() {
        byte[] bytes = new byte[32];
        for (int i = 0; i < 16; i++) {
            bytes[i] = (byte) i;
            bytes[16 + i] = (byte) (16 * i);
        }
        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";
        for (int offset = 0; offset < bytes.length; offset++) {
            for (int len = 0; len < (bytes.length - offset); len++) {

                byte[] subBytes = new byte[len];
                System.arraycopy(bytes, offset, subBytes, 0, len);

                String actual = toHexString(bytes, offset, len);
                assertEquals(expected.substring(2 * offset, 2 * offset + 2 * len), actual);
                assertEquals(toHexString(subBytes), actual);
            }
        }
    }

    public void testToHexString_case() {
        byte[] bytes = new byte[32];
        for (int i = 0; i < 16; i++) {
            bytes[i] = (byte) i;
            bytes[16 + i] = (byte) (16 * i);
        }

        String expected = "000102030405060708090A0B0C0D0E0F00102030405060708090A0B0C0D0E0F0";

        assertEquals(expected.toUpperCase(), toHexString(bytes, true));
        assertEquals(expected.toLowerCase(), toHexString(bytes, false));

        // default is uppercase
        assertEquals(expected.toUpperCase(), toHexString(bytes));
    }

    public void testHexStringToByteArray_empty() {
        assertBytesEqual(new byte[0], HexDump.hexStringToByteArray(""));
    }

    public void testHexStringToByteArray_null() {
        assertThrows(
                NullPointerException.class,
                () -> HexDump.hexStringToByteArray((String) null));
    }

    public void testHexStringToByteArray_invalidCharacters() {
        // IllegalArgumentException would probably have been better than RuntimeException, but it
        // might be too late to change now.
        assertThrows(
                RuntimeException.class,
                () -> HexDump.hexStringToByteArray("GG"));
        assertThrows(
                RuntimeException.class,
                () -> HexDump.hexStringToByteArray("\0\0"));
        assertThrows(
                RuntimeException.class,
                () -> HexDump.hexStringToByteArray("abcdefgh"));
    }

    public void testHexStringToByteArray_oddLength() {
        // IllegalArgumentException would probably have been better than
        // StringIndexOutOfBoundsException, but it might be too late to change now.
        assertThrows(
                StringIndexOutOfBoundsException.class,
                () -> HexDump.hexStringToByteArray("A"));
        assertThrows(
                StringIndexOutOfBoundsException.class,
                () -> HexDump.hexStringToByteArray("123"));
        assertThrows(
                StringIndexOutOfBoundsException.class,
                () -> HexDump.hexStringToByteArray("ABCDE"));
    }

    private static void assertBytesEqual(byte[] expected, byte[] actual) {
        if (!Arrays.equals(expected, actual)) {
            fail("Expected " + Arrays.toString(expected) + ", got " + Arrays.toString(actual));
        }
    }

    private static void assertThrows(Class<? extends RuntimeException> clazz, Runnable runnable) {
        try {
            runnable.run();
        } catch (RuntimeException exception) {
            assertEquals(toStrackTrace(exception), clazz, exception.getClass());
        }
    }

    private static String toStrackTrace(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        throwable.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }

}