@Test
public void testInstructionOffsetMap() {

    ImmutableList<ImmutableInstruction> instructions = ImmutableList.of(
            /*00: 0x00*/ new ImmutableInstruction10t(Opcode.GOTO, 1),
            /*01: 0x01*/ new ImmutableInstruction10x(Opcode.NOP),
            /*02: 0x02*/ new ImmutableInstruction11n(Opcode.CONST_4, 2, 3),
            /*03: 0x03*/ new ImmutableInstruction11x(Opcode.RETURN, 4),
            /*04: 0x04*/ new ImmutableInstruction12x(Opcode.ARRAY_LENGTH, 5, 6),
            /*05: 0x05*/ new ImmutableInstruction20t(Opcode.GOTO_16, 7),
            /*06: 0x07*/ new ImmutableInstruction21c(Opcode.CONST_STRING, 8, new ImmutableStringReference("blah")),
            /*07: 0x09*/ new ImmutableInstruction21ih(Opcode.CONST_HIGH16, 9, 0x10000),
            /*08: 0x0b*/ new ImmutableInstruction21lh(Opcode.CONST_WIDE_HIGH16, 10, 0x1000000000000L),
            /*09: 0x0d*/ new ImmutableInstruction21s(Opcode.CONST_16, 11, 12),
            /*10: 0x0f*/ new ImmutableInstruction21t(Opcode.IF_EQZ, 12, 13),
            /*11: 0x11*/ new ImmutableInstruction22b(Opcode.ADD_INT_LIT8, 14, 15, 16),
            /*12: 0x13*/ new ImmutableInstruction22c(Opcode.INSTANCE_OF, 0, 1,
                                                     new ImmutableTypeReference("Ltype;")),
            /*13: 0x15*/ new ImmutableInstruction22s(Opcode.ADD_INT_LIT16, 2, 3, 17),
            /*14: 0x17*/ new ImmutableInstruction22t(Opcode.IF_EQ, 4, 5, 18),
            /*15: 0x19*/ new ImmutableInstruction22x(Opcode.MOVE_FROM16, 19, 20),
            /*16: 0x1b*/ new ImmutableInstruction23x(Opcode.AGET, 21, 22, 23),
            /*17: 0x1d*/ new ImmutableInstruction30t(Opcode.GOTO_32, 24),
            /*18: 0x20*/ new ImmutableInstruction31c(Opcode.CONST_STRING_JUMBO, 25,
                                                     new ImmutableStringReference("this is a string")),
            /*19: 0x23*/ new ImmutableInstruction31i(Opcode.CONST, 26, 27),
            /*20: 0x26*/ new ImmutableInstruction31t(Opcode.FILL_ARRAY_DATA, 28, 29),
            /*21: 0x29*/ new ImmutableInstruction32x(Opcode.MOVE_16, 30, 31),
            /*22: 0x2c*/ new ImmutableInstruction35c(Opcode.FILLED_NEW_ARRAY, 0, 0, 0, 0, 0, 0,
                                                     new ImmutableTypeReference("Ltype;")),
            /*23: 0x2f*/ new ImmutableInstruction3rc(Opcode.FILLED_NEW_ARRAY_RANGE, 0, 0,
                                                     new ImmutableTypeReference("Ltype;")),
            /*24: 0x32*/ new ImmutableInstruction51l(Opcode.CONST_WIDE, 32, 33),
            /*25: 0x37*/ new ImmutableInstruction10t(Opcode.GOTO, 1)
    );
    ImmutableMethodImplementation impl = new ImmutableMethodImplementation(33, instructions, null, null);
    InstructionOffsetMap instructionOffsetMap = new InstructionOffsetMap(instructions);
    int[] expectedOffsets = new int[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11,
            0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x20, 0x23, 0x26, 0x29, 0x2c, 0x2f, 0x32, 0x37};
    for (int i=0; i<instructions.size(); i++) {
        Assert.assertEquals(expectedOffsets[i], instructionOffsetMap.getInstructionCodeOffset(i));
        Assert.assertEquals(i, instructionOffsetMap.getInstructionIndexAtCodeOffset(expectedOffsets[i], true));
        Assert.assertEquals(i, instructionOffsetMap.getInstructionIndexAtCodeOffset(expectedOffsets[i], false));
    }
    int instructionIndex = -1;
    for (int codeOffset=0; codeOffset<=expectedOffsets[expectedOffsets.length-1]; codeOffset++) {
        if (codeOffset == expectedOffsets[instructionIndex+1]) {
            // this offset is at the beginning of an instruction
            instructionIndex++;
        } else {
            // this offset is in the middle of an instruction
            Assert.assertEquals(instructionIndex,
                    instructionOffsetMap.getInstructionIndexAtCodeOffset(codeOffset, false));
            try {
                instructionOffsetMap.getInstructionIndexAtCodeOffset(codeOffset, true);
                Assert.fail(String.format("Exception exception didn't occur for code offset 0x%x", codeOffset));
            } catch (ExceptionWithContext ex) {
                // expected exception
            }
        }
    }
    Assert.assertEquals(expectedOffsets.length-1,
            instructionOffsetMap.getInstructionIndexAtCodeOffset(expectedOffsets[expectedOffsets.length-1]+1, false));
    Assert.assertEquals(expectedOffsets.length-1,
            instructionOffsetMap.getInstructionIndexAtCodeOffset(expectedOffsets[expectedOffsets.length-1]+10, false));
}