private void assertBlobReadSuccess(GetBlobOptions options, Future<Long> readIntoFuture,
      ByteBufferAsyncWritableChannel asyncWritableChannel, ReadableStreamChannel readableStreamChannel,
      CountDownLatch readCompleteLatch, AtomicLong readCompleteResult,
      AtomicReference<Throwable> readCompleteThrowable) {
    try {
      ByteBuffer putContentBuf;
      if (options != null && options.isRawMode()) {
        putContentBuf = getBlobBuffer();
        Assert.assertNotNull("Did not find server with blob: " + blobIdStr, putContentBuf);
      } else {
        // If a range is set, compare the result against the specified byte range.
        if (options != null && options.getRange() != null) {
          ByteRange range = options.getRange().toResolvedByteRange(blobSize, options.resolveRangeOnEmptyBlob());
          putContentBuf = ByteBuffer.wrap(putContent, (int) range.getStartOffset(), (int) range.getRangeSize());
        } else {
          putContentBuf = ByteBuffer.wrap(putContent);
        }
      }

      long written;
      Assert.assertTrue("ReadyForPollCallback should have been invoked as readInto() was called",
          mockNetworkClient.getAndClearWokenUpStatus());
      // Compare byte by byte.
      final int bytesToRead = putContentBuf.remaining();
      int readBytes = 0;
      do {
        ByteBuffer buf = asyncWritableChannel.getNextChunk();
        int bufLength = buf.remaining();
        Assert.assertTrue("total content read should not be greater than length of put content",
            readBytes + bufLength <= bytesToRead);
        while (buf.hasRemaining()) {
          Assert.assertEquals("Get and Put blob content should match", putContentBuf.get(), buf.get());
          readBytes++;
        }
        asyncWritableChannel.resolveOldestChunk(null);
        Assert.assertTrue("ReadyForPollCallback should have been invoked as writable channel callback was called",
            mockNetworkClient.getAndClearWokenUpStatus());
      } while (readBytes < bytesToRead);
      written = readIntoFuture.get();
      Assert.assertEquals("the returned length in the future should be the length of data written", (long) readBytes,
          written);
      Assert.assertNull("There should be no more data in the channel", asyncWritableChannel.getNextChunk(0));
      readableStreamChannel.close();
      readCompleteResult.set(written);
    } catch (Throwable e) {
      readCompleteThrowable.set(e);
    } finally {
      readCompleteLatch.countDown();
    }
  }
Assert that the operation is complete and successful. Note that the future completion and callback invocation
happens outside of the GetOperation, so those are not checked here. But at this point, the operation result should
be ready.
@param options The {@link GetBlobOptions} for the operation to check.
@param readIntoFuture The future associated with the read on the {@link ReadableStreamChannel} result of the
                      operation.
@param asyncWritableChannel The {@link ByteBufferAsyncWritableChannel} to which bytes will be written by the
                            operation.
@param readableStreamChannel The {@link ReadableStreamChannel} that bytes are read from in the operation.
@param readCompleteLatch The latch to count down once the read is completed.
@param readCompleteResult This will contain the bytes written on return.
@param readCompleteThrowable This will contain any exceptions encountered during the read.
ambry-router/src/test/java/com/github/ambry/router/GetBlobOperationTest.java