public void validate(ByteBuffer[] inputs, int[] erasedIndexes,
      ByteBuffer[] outputs) throws IOException {
    markBuffers(outputs);

    try {
      ByteBuffer validInput = CoderUtil.findFirstValidInput(inputs);
      boolean isDirect = validInput.isDirect();
      int capacity = validInput.capacity();
      int remaining = validInput.remaining();

      // Init buffer
      if (buffer == null || buffer.isDirect() != isDirect
          || buffer.capacity() < remaining) {
        buffer = allocateBuffer(isDirect, capacity);
      }
      buffer.clear().limit(remaining);

      // Create newInputs and newErasedIndex for validation
      ByteBuffer[] newInputs = new ByteBuffer[inputs.length];
      int count = 0;
      for (int i = 0; i < erasedIndexes.length; i++) {
        newInputs[erasedIndexes[i]] = outputs[i];
        count++;
      }
      newErasedIndex = -1;
      boolean selected = false;
      int numValidIndexes = CoderUtil.getValidIndexes(inputs).length;
      for (int i = 0; i < newInputs.length; i++) {
        if (count == numValidIndexes) {
          break;
        } else if (!selected && inputs[i] != null) {
          newErasedIndex = i;
          newInputs[i] = null;
          selected = true;
        } else if (newInputs[i] == null) {
          newInputs[i] = inputs[i];
          if (inputs[i] != null) {
            count++;
          }
        }
      }

      // Keep it for testing
      newValidIndexes = CoderUtil.getValidIndexes(newInputs);

      decoder.decode(newInputs, new int[]{newErasedIndex},
          new ByteBuffer[]{buffer});

      if (!buffer.equals(inputs[newErasedIndex])) {
        throw new InvalidDecodingException("Failed to validate decoding");
      }
    } finally {
      toLimits(inputs);
      resetBuffers(outputs);
    }
  }
@Test
  public void testValidateWithBadDecoding() throws IOException {
    prepare(null, numDataUnits, numParityUnits, erasedDataIndexes,
        erasedParityIndexes);
    this.usingDirectBuffer = true;
    prepareCoders(true);
    prepareValidator(true);
    prepareBufferAllocator(false);

    // encode
    ECChunk[] dataChunks = prepareDataChunksForEncoding();
    ECChunk[] parityChunks = prepareParityChunksForEncoding();
    ECChunk[] clonedDataChunks = cloneChunksWithData(dataChunks);
    try {
      encoder.encode(dataChunks, parityChunks);
    } catch (Exception e) {
      Assert.fail("Should not get Exception: " + e.getMessage());
    }

    // decode
    backupAndEraseChunks(clonedDataChunks, parityChunks);
    ECChunk[] inputChunks =
        prepareInputChunksForDecoding(clonedDataChunks, parityChunks);
    markChunks(inputChunks);
    ensureOnlyLeastRequiredChunks(inputChunks);
    ECChunk[] recoveredChunks = prepareOutputChunksForDecoding();
    int[] erasedIndexes = getErasedIndexesForDecoding();
    try {
      decoder.decode(inputChunks, erasedIndexes, recoveredChunks);
    } catch (Exception e) {
      Assert.fail("Should not get Exception: " + e.getMessage());
    }

    // validate
    restoreChunksFromMark(inputChunks);
    polluteSomeChunk(recoveredChunks);
    try {
      validator.validate(inputChunks, erasedIndexes, recoveredChunks);
      Assert.fail("Validation should fail due to bad decoding");
    } catch (InvalidDecodingException e) {
      String expected = "Failed to validate decoding";
      GenericTestUtils.assertExceptionContains(expected, e);
    }
  }