@Test
  public void testBufferCallbackUniqueBuffers() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    final int NUMBER_LINES = 1000;
    final int SIZE = 512;

    controller.open(fileName, 1000);

    controller.fill(0, 1, NUMBER_LINES * SIZE, (byte) 'j');

    final ArrayList<ByteBuffer> buffers = new ArrayList<ByteBuffer>();

    BufferCallback bufferCallback =
        new BufferCallback() {
          public void bufferDone(final ByteBuffer buffer) {
            buffers.add(buffer);
          }
        };

    controller.setBufferCallback(bufferCallback);

    CountDownLatch latch = new CountDownLatch(NUMBER_LINES);
    ArrayList<Integer> result = new ArrayList<Integer>();
    for (int i = 0; i < NUMBER_LINES; i++) {
      ByteBuffer buffer1 = AsynchronousFileImpl.newBuffer(SIZE);
      buffer1.rewind();
      for (int j = 0; j < SIZE; j++) {
        buffer1.put((byte) (j % Byte.MAX_VALUE));
      }
      CountDownCallback aio = new CountDownCallback(latch, null, result, i);
      controller.write(i * SIZE, SIZE, buffer1, aio);
    }

    // The buffer callback is only called after the complete callback was
    // called.
    // Because of that a race could happen on the assertions to
    // buffers.size what would invalidate the test
    // We close the file and that would guarantee the buffer callback was
    // called for all the elements
    controller.close();

    CountDownCallback.checkResults(NUMBER_LINES, result);

    // Make sure all the buffers are unique
    ByteBuffer lineOne = null;
    for (ByteBuffer bufferTmp : buffers) {
      if (lineOne == null) {
        lineOne = bufferTmp;
      } else {
        Assert.assertTrue(lineOne != bufferTmp);
      }
    }

    for (ByteBuffer bufferTmp : buffers) {
      destroy(bufferTmp);
    }

    buffers.clear();
  }
  private void asyncData(final int numberOfLines, final int size, final int aioLimit)
      throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.open(fileName, aioLimit);

    CountDownLatch latchDone = new CountDownLatch(numberOfLines);

    buffer = AsynchronousFileImpl.newBuffer(size);
    encodeBufer(buffer);

    preAlloc(controller, numberOfLines * size);

    ArrayList<CountDownCallback> list = new ArrayList<CountDownCallback>();

    ArrayList<Integer> result = new ArrayList<Integer>();

    for (int i = 0; i < numberOfLines; i++) {
      list.add(new CountDownCallback(latchDone, null, result, i));
    }

    long valueInitial = System.currentTimeMillis();

    long lastTime = System.currentTimeMillis();
    int counter = 0;
    for (CountDownCallback tmp : list) {
      controller.write(counter * size, size, buffer, tmp);
      if (++counter % 20000 == 0) {
        AsynchronousFileTest.debug(
            20000 * 1000 / (System.currentTimeMillis() - lastTime) + " rec/sec (Async)");
        lastTime = System.currentTimeMillis();
      }
    }

    UnitTestCase.waitForLatch(latchDone);

    long timeTotal = System.currentTimeMillis() - valueInitial;

    CountDownCallback.checkResults(numberOfLines, result);

    AsynchronousFileTest.debug(
        "After completions time = "
            + timeTotal
            + " for "
            + numberOfLines
            + " registers "
            + " size each line = "
            + size
            + ", Records/Sec="
            + numberOfLines * 1000 / timeTotal
            + " (Assynchronous)");

    for (CountDownCallback tmp : list) {
      Assert.assertEquals(1, tmp.timesDoneCalled.get());
      Assert.assertTrue(tmp.doneCalled);
      Assert.assertEquals(0, tmp.errorCalled);
    }

    controller.close();
  }
 @Test
 public void testInvalidAlloc() throws Exception {
   try {
     @SuppressWarnings("unused")
     ByteBuffer buffer = AsynchronousFileImpl.newBuffer(300);
     Assert.fail("Exception expected");
   } catch (Exception ignored) {
   }
 }
  @Test
  public void testReleaseBuffers() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    WeakReference<ByteBuffer> bufferCheck = null;

    controller.open(fileName, 10000);
    bufferCheck = new WeakReference<ByteBuffer>(controller.getHandler());
    controller.fill(0, 10, 1024, (byte) 0);

    ByteBuffer write = AsynchronousFileImpl.newBuffer(1024);

    for (int i = 0; i < 1024; i++) {
      write.put(UnitTestCase.getSamplebyte(i));
    }

    final CountDownLatch latch = new CountDownLatch(1);

    controller.write(
        0,
        1024,
        write,
        new AIOCallback() {

          public void onError(final int errorCode, final String errorMessage) {}

          public void done() {
            latch.countDown();
          }
        });

    Assert.assertTrue(latch.await(10, TimeUnit.SECONDS));

    WeakReference<ByteBuffer> bufferCheck2 = new WeakReference<ByteBuffer>(write);

    destroy(write);

    write = null;

    UnitTestCase.forceGC(bufferCheck2, 5000);

    Assert.assertNull(bufferCheck2.get());

    controller.close();

    controller = null;

    UnitTestCase.forceGC(bufferCheck, 5000);

    Assert.assertNull(bufferCheck.get());
  }
  @Test
  public void testDirectSynchronous() throws Exception {

    final int NUMBER_LINES = 3000;
    final int SIZE = 1024;

    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.open(fileName, 2000);

    buffer = AsynchronousFileImpl.newBuffer(SIZE);
    encodeBufer(buffer);

    preAlloc(controller, NUMBER_LINES * SIZE);

    long startTime = System.currentTimeMillis();

    for (int i = 0; i < NUMBER_LINES; i++) {
      CountDownLatch latchDone = new CountDownLatch(1);
      CountDownCallback aioBlock = new CountDownCallback(latchDone, null, null, 0);
      controller.write(i * 512, 512, buffer, aioBlock);
      UnitTestCase.waitForLatch(latchDone);
      Assert.assertTrue(aioBlock.doneCalled);
      Assert.assertEquals(0, aioBlock.errorCalled);
    }

    long timeTotal = System.currentTimeMillis() - startTime;
    AsynchronousFileTest.debug(
        "time = "
            + timeTotal
            + " for "
            + NUMBER_LINES
            + " registers "
            + " size each line = "
            + SIZE
            + " Records/Sec="
            + NUMBER_LINES * 1000 / timeTotal
            + " Synchronous");

    controller.close();
  }
  @Test
  public void testInvalidWrite() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.open(fileName, 2000);

    final int SIZE = 512;

    buffer = AsynchronousFileImpl.newBuffer(SIZE);
    encodeBufer(buffer);

    preAlloc(controller, 10 * 512);

    CountDownLatch latchDone = new CountDownLatch(1);

    CountDownCallback aioBlock = new CountDownCallback(latchDone, null, null, 0);
    controller.write(11, 512, buffer, aioBlock);

    UnitTestCase.waitForLatch(latchDone);

    Assert.assertTrue(aioBlock.errorCalled != 0);
    Assert.assertFalse(aioBlock.doneCalled);
  }
  @Test
  public void testInternalWrite() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.open(fileName, 2000);

    final int SIZE = 10 * 512;

    buffer = AsynchronousFileImpl.newBuffer(SIZE);

    for (int i = 0; i < SIZE; i++) {
      buffer.put(getSamplebyte(i));
    }

    controller.writeInternal(0, SIZE, buffer);

    InputStream fileInput = new BufferedInputStream(new FileInputStream(new File(fileName)));

    for (int i = 0; i < SIZE; i++) {
      assertEquals(getSamplebyte(i), fileInput.read());
    }

    assertEquals(-1, fileInput.read());
  }
    @Override
    public void run() {
      super.run();

      ByteBuffer buffer = null;

      synchronized (MultiThreadAsynchronousFileTest.class) {
        buffer = AsynchronousFileImpl.newBuffer(MultiThreadAsynchronousFileTest.SIZE);
      }

      try {

        // I'm always reusing the same buffer, as I don't want any noise from
        // malloc on the measurement
        // Encoding buffer
        MultiThreadAsynchronousFileTest.addString(
            "Thread name=" + Thread.currentThread().getName() + ";" + "\n", buffer);
        for (int local = buffer.position(); local < buffer.capacity() - 1; local++) {
          buffer.put((byte) ' ');
        }
        buffer.put((byte) '\n');

        latchStart.countDown();
        latchStart.await();

        CountDownLatch latchFinishThread = null;

        if (!sync) {
          latchFinishThread = new CountDownLatch(MultiThreadAsynchronousFileTest.NUMBER_OF_LINES);
        }

        LinkedList<CountDownCallback> list = new LinkedList<CountDownCallback>();

        for (int i = 0; i < MultiThreadAsynchronousFileTest.NUMBER_OF_LINES; i++) {

          if (sync) {
            latchFinishThread = new CountDownLatch(1);
          }
          CountDownCallback callback = new CountDownCallback(latchFinishThread, null, null, 0);
          if (!sync) {
            list.add(callback);
          }
          addData(libaio, buffer, callback);
          if (sync) {
            latchFinishThread.await();
            Assert.assertTrue(callback.doneCalled);
            Assert.assertFalse(callback.errorCalled != 0);
          }
        }
        if (!sync) {
          latchFinishThread.await();
        }

        for (CountDownCallback callback : list) {
          Assert.assertTrue(callback.doneCalled);
          Assert.assertFalse(callback.errorCalled != 0);
        }

        for (CountDownCallback callback : list) {
          Assert.assertTrue(callback.doneCalled);
          Assert.assertFalse(callback.errorCalled != 0);
        }

      } catch (Throwable e) {
        e.printStackTrace();
        failed = e;
      } finally {
        synchronized (MultiThreadAsynchronousFileTest.class) {
          AsynchronousFileImpl.destroyBuffer(buffer);
        }
      }
    }
  /**
   * This test will call file.close() when there are still callbacks being processed. This could
   * cause a crash or callbacks missing and this test is validating both situations. The file is
   * also read after being written to validate its correctness
   */
  @Test
  public void testConcurrentClose() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    final int NUMBER_LINES = 1000;
    CountDownLatch readLatch = new CountDownLatch(NUMBER_LINES);
    final int SIZE = 1024;

    controller.open(fileName, 10000);

    controller.fill(0, 1, NUMBER_LINES * SIZE, (byte) 'j');

    controller.setBufferCallback(bufferCallbackDestroy);

    for (int i = 0; i < NUMBER_LINES; i++) {
      ByteBuffer buffer = AsynchronousFileImpl.newBuffer(SIZE);

      buffer.clear();
      addString("Str value " + i + "\n", buffer);
      for (int j = buffer.position(); j < buffer.capacity() - 1; j++) {
        buffer.put((byte) ' ');
      }
      buffer.put((byte) '\n');

      CountDownCallback aio = new CountDownCallback(readLatch, null, null, 0);
      controller.write(i * SIZE, SIZE, buffer, aio);
    }

    // If you call close you're supposed to wait events to finish before
    // closing it
    controller.close();

    controller.setBufferCallback(null);

    Assert.assertEquals(0, readLatch.getCount());
    waitForLatch(readLatch);
    controller.open(fileName, 10);

    ByteBuffer newBuffer = AsynchronousFileImpl.newBuffer(SIZE);

    ByteBuffer buffer = AsynchronousFileImpl.newBuffer(SIZE);

    for (int i = 0; i < NUMBER_LINES; i++) {
      newBuffer.clear();
      addString("Str value " + i + "\n", newBuffer);
      for (int j = newBuffer.position(); j < newBuffer.capacity() - 1; j++) {
        newBuffer.put((byte) ' ');
      }
      newBuffer.put((byte) '\n');

      CountDownLatch latch = new CountDownLatch(1);
      CountDownCallback aio = new CountDownCallback(latch, null, null, 0);
      controller.read(i * SIZE, SIZE, buffer, aio);
      waitForLatch(latch);
      Assert.assertEquals(0, aio.errorCalled);
      Assert.assertTrue(aio.doneCalled);

      byte bytesRead[] = new byte[SIZE];
      byte bytesCompare[] = new byte[SIZE];

      newBuffer.rewind();
      newBuffer.get(bytesCompare);
      buffer.rewind();
      buffer.get(bytesRead);

      for (int count = 0; count < SIZE; count++) {
        Assert.assertEquals(
            "byte position " + count + " differs on line " + i,
            bytesCompare[count],
            bytesRead[count]);
      }

      Assert.assertTrue(buffer.equals(newBuffer));
    }

    destroy(newBuffer);
  }
  @Test
  public void testRead() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.setBufferCallback(bufferCallbackDestroy);

    final int NUMBER_LINES = 1000;
    final int SIZE = 1024;

    controller.open(fileName, 1000);

    controller.fill(0, 1, NUMBER_LINES * SIZE, (byte) 'j');

    {
      CountDownLatch latch = new CountDownLatch(NUMBER_LINES);
      ArrayList<Integer> result = new ArrayList<Integer>();

      AtomicInteger errors = new AtomicInteger(0);

      for (int i = 0; i < NUMBER_LINES; i++) {
        if (i % 100 == 0) {
          System.out.println("Wrote " + i + " lines");
        }
        final ByteBuffer buffer0 = AsynchronousFileImpl.newBuffer(SIZE);
        for (int j = 0; j < SIZE; j++) {
          buffer0.put(UnitTestCase.getSamplebyte(j));
        }

        CountDownCallback aio = new CountDownCallback(latch, errors, result, i);
        controller.write(i * SIZE, SIZE, buffer0, aio);
      }

      waitForLatch(latch);

      Assert.assertEquals(0, errors.get());

      CountDownCallback.checkResults(NUMBER_LINES, result);
    }

    // If you call close you're supposed to wait events to finish before
    // closing it
    controller.close();
    controller.setBufferCallback(null);

    controller.open(fileName, 10);

    buffer = AsynchronousFileImpl.newBuffer(SIZE);

    for (int i = 0; i < NUMBER_LINES; i++) {
      if (i % 100 == 0) {
        System.out.println("Read " + i + " lines");
      }
      AsynchronousFileImpl.clearBuffer(buffer);

      CountDownLatch latch = new CountDownLatch(1);
      AtomicInteger errors = new AtomicInteger(0);
      CountDownCallback aio = new CountDownCallback(latch, errors, null, 0);

      controller.read(i * SIZE, SIZE, buffer, aio);

      waitForLatch(latch);
      Assert.assertEquals(0, errors.get());
      Assert.assertTrue(aio.doneCalled);

      byte bytesRead[] = new byte[SIZE];
      buffer.get(bytesRead);

      for (int count = 0; count < SIZE; count++) {
        Assert.assertEquals(
            "byte position " + count + " differs on line " + i + " position = " + count,
            UnitTestCase.getSamplebyte(count),
            bytesRead[count]);
      }
    }
  }
  @Test
  public void testInvalidReads() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);

    final int SIZE = 512;

    controller.open(fileName, 10);
    controller.close();

    controller = new AsynchronousFileImpl(executor, pollerExecutor);

    controller.open(fileName, 10);

    controller.fill(0, 1, 512, (byte) 'j');

    buffer = AsynchronousFileImpl.newBuffer(SIZE);

    buffer.clear();

    for (int i = 0; i < SIZE; i++) {
      buffer.put((byte) (i % 100));
    }

    LocalCallback callbackLocal = new LocalCallback();

    controller.write(0, 512, buffer, callbackLocal);

    waitForLatch(callbackLocal.latch);

    {
      ByteBuffer newBuffer = AsynchronousFileImpl.newBuffer(512);

      try {
        callbackLocal = new LocalCallback();

        controller.read(0, 50, newBuffer, callbackLocal);

        waitForLatch(callbackLocal.latch);

        Assert.assertTrue(callbackLocal.error);

      } finally {
        // We have to destroy the native buffer manually as it was created with a malloc like C
        // function
        destroy(newBuffer);
        newBuffer = null;
      }
    }
    callbackLocal = new LocalCallback();

    byte bytes[] = new byte[512];

    {
      try {
        ByteBuffer newBuffer = ByteBuffer.wrap(bytes);

        controller.read(0, 512, newBuffer, callbackLocal);

        Assert.fail("An exception was supposed to be thrown");
      } catch (HornetQException ignored) {
        System.out.println(ignored);
      }
    }

    {
      final ByteBuffer newBuffer = AsynchronousFileImpl.newBuffer(512);
      try {
        callbackLocal = new LocalCallback();
        controller.read(0, 512, newBuffer, callbackLocal);
        waitForLatch(callbackLocal.latch);
        Assert.assertFalse(callbackLocal.error);

        newBuffer.rewind();

        byte[] bytesRead = new byte[SIZE];

        newBuffer.get(bytesRead);

        for (int i = 0; i < SIZE; i++) {
          Assert.assertEquals((byte) (i % 100), bytesRead[i]);
        }
      } finally {
        destroy(newBuffer);
      }
    }
  }
  /**
   * This test is validating if the AIO layer can open two different simultaneous files without
   * loose any callbacks. This test made the native layer to crash at some point during development
   */
  @Test
  public void testTwoFiles() throws Exception {
    controller = new AsynchronousFileImpl(executor, pollerExecutor);
    final AsynchronousFileImpl controller2 = new AsynchronousFileImpl(executor, pollerExecutor);
    controller.open(fileName + ".1", 10000);
    controller2.open(fileName + ".2", 10000);

    int numberOfLines = 1000;
    int size = 1024;

    ArrayList<Integer> listResult1 = new ArrayList<Integer>();
    ArrayList<Integer> listResult2 = new ArrayList<Integer>();

    AtomicInteger errors = new AtomicInteger(0);

    try {
      CountDownLatch latchDone = new CountDownLatch(numberOfLines);
      CountDownLatch latchDone2 = new CountDownLatch(numberOfLines);

      buffer = AsynchronousFileImpl.newBuffer(size);
      encodeBufer(buffer);

      preAlloc(controller, numberOfLines * size);
      preAlloc(controller2, numberOfLines * size);

      ArrayList<CountDownCallback> list = new ArrayList<CountDownCallback>();
      ArrayList<CountDownCallback> list2 = new ArrayList<CountDownCallback>();

      for (int i = 0; i < numberOfLines; i++) {
        list.add(new CountDownCallback(latchDone, errors, listResult1, i));
        list2.add(new CountDownCallback(latchDone2, errors, listResult2, i));
      }

      int counter = 0;

      Iterator<CountDownCallback> iter2 = list2.iterator();

      for (CountDownCallback cb1 : list) {
        CountDownCallback cb2 = iter2.next();

        controller.write(counter * size, size, buffer, cb1);
        controller2.write(counter * size, size, buffer, cb2);
        ++counter;
      }

      UnitTestCase.waitForLatch(latchDone);
      UnitTestCase.waitForLatch(latchDone2);

      CountDownCallback.checkResults(numberOfLines, listResult1);
      CountDownCallback.checkResults(numberOfLines, listResult2);

      for (CountDownCallback callback : list) {
        Assert.assertEquals(1, callback.timesDoneCalled.get());
        Assert.assertTrue(callback.doneCalled);
      }

      for (CountDownCallback callback : list2) {
        Assert.assertEquals(1, callback.timesDoneCalled.get());
        Assert.assertTrue(callback.doneCalled);
      }

      Assert.assertEquals(0, errors.get());

      controller.close();
    } finally {
      try {
        controller2.close();
      } catch (Exception ignored) {
      }
    }
  }