public static Builder<ByteBuffer> create() {
  return new Builder<>(PROXY_OPTIMAL);
} public static <T> KeyRange<T> closed(final T start, final T stop) {
  return new KeyRange<>(KeyRangeType.FORWARD_CLOSED, start, stop);
} public static final String DB_1 = "test-db-1"; @SuppressWarnings("PMD.AvoidUsingOctalValues")
public static final int POSIX_MODE = 0664; static ByteBuffer bb(final int value) {
  final ByteBuffer bb = allocateDirect(BYTES);
  bb.putInt(value).flip();
  return bb;
} public static final class BadValueSizeException extends LmdbNativeException {

  static final int MDB_BAD_VALSIZE = -30_781;
  private static final long serialVersionUID = 1L;

  BadValueSizeException() {
    super(MDB_BAD_VALSIZE,
          "Unsupported size of key/DB name/data, or wrong DUPFIXED size");
  }
} public static final class AlreadyClosedException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public AlreadyClosedException() {
    super("Environment has already been closed");
  }
} public static class EnvIsReadOnly extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public EnvIsReadOnly() {
    super("Read-write Txn incompatible with read-only Env");
  }
} public static class IncompatibleParent extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public IncompatibleParent() {
    super("Transaction incompatible with its parent transaction");
  }
} public static final class NotReadyException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public NotReadyException() {
    super("Transaction is not in ready state");
  }
} public static class NotResetException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public NotResetException() {
    super("Transaction has not been reset");
  }
} public static class ReadOnlyRequiredException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public ReadOnlyRequiredException() {
    super("Not a read-only transaction");
  }
} public static class ReadWriteRequiredException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public ReadWriteRequiredException() {
    super("Not a read-write transaction");
  }
} public static class ResetException extends LmdbException {

  private static final long serialVersionUID = 1L;

  /**
   * Creates a new instance.
   */
  public ResetException() {
    super("Transaction has already been reset");
  }
}
import static com.jakewharton.byteunits.BinaryByteUnit.KIBIBYTES;
import static java.nio.ByteBuffer.allocateDirect;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.lmdbjava.DbiFlags.MDB_CREATE;
import static org.lmdbjava.Env.create;
import static org.lmdbjava.EnvFlags.MDB_NOSUBDIR;
import static org.lmdbjava.EnvFlags.MDB_RDONLY_ENV;
import static org.lmdbjava.KeyRange.closed;
import static org.lmdbjava.TestUtils.DB_1;
import static org.lmdbjava.TestUtils.POSIX_MODE;
import static org.lmdbjava.TestUtils.bb;
import static org.lmdbjava.Txn.State.DONE;
import static org.lmdbjava.Txn.State.READY;
import static org.lmdbjava.Txn.State.RELEASED;
import static org.lmdbjava.Txn.State.RESET;
import static org.lmdbjava.TxnFlags.MDB_RDONLY_TXN;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.lmdbjava.Dbi.BadValueSizeException;
import org.lmdbjava.Env.AlreadyClosedException;
import org.lmdbjava.Txn.EnvIsReadOnly;
import org.lmdbjava.Txn.IncompatibleParent;
import org.lmdbjava.Txn.NotReadyException;
import org.lmdbjava.Txn.NotResetException;
import org.lmdbjava.Txn.ReadOnlyRequiredException;
import org.lmdbjava.Txn.ReadWriteRequiredException;
import org.lmdbjava.Txn.ResetException;
    }
  }

  @Test
  public void testGetId() {
    final Dbi<ByteBuffer> db = env.openDbi(DB_1, MDB_CREATE);

    final AtomicLong txId1 = new AtomicLong();
    final AtomicLong txId2 = new AtomicLong();

    try (Txn<ByteBuffer> tx1 = env.txnRead()) {
      txId1.set(tx1.getId());
    }

    db.put(bb(1), bb(2));

    try (Txn<ByteBuffer> tx2 = env.txnRead()) {
      txId2.set(tx2.getId());
    }
    // should not see the same snapshot
    assertThat(txId1.get(), is(not(txId2.get())));
  }

  @Test
  public void txCanCommitThenCloseWithoutError() {
    try (Txn<ByteBuffer> txn = env.txnRead()) {
      assertThat(txn.getState(), is(READY));
      txn.commit();
      assertThat(txn.getState(), is(DONE));
    }
  }

  @Test(expected = NotReadyException.class)
  public void txCannotAbortIfAlreadyCommitted() {
    try (Txn<ByteBuffer> txn = env.txnRead()) {
      assertThat(txn.getState(), is(READY));
      txn.commit();
      assertThat(txn.getState(), is(DONE));
      txn.abort();
    }
  }

  @Test(expected = NotReadyException.class)
  public void txCannotCommitTwice() {
    try (Txn<ByteBuffer> txn = env.txnRead()) {
      txn.commit();
      txn.commit(); // error
    }
  }

  @Test(expected = AlreadyClosedException.class)
  @SuppressWarnings("ResultOfObjectAllocationIgnored")
  public void txConstructionDeniedIfEnvClosed() {
    env.close();
    env.txnRead();
  }

  @Test
  public void txParent() {
    try (Txn<ByteBuffer> txRoot = env.txnWrite();
         Txn<ByteBuffer> txChild = env.txn(txRoot)) {
      assertThat(txRoot.getParent(), is(nullValue()));
      assertThat(txChild.getParent(), is(txRoot));
    }
  }

  @Test(expected = IncompatibleParent.class)
  public void txParentROChildRWIncompatible() {
    try (Txn<ByteBuffer> txRoot = env.txnRead()) {
      env.txn(txRoot); // error
    }
  }

  @Test(expected = IncompatibleParent.class)
  public void txParentRWChildROIncompatible() {
    try (Txn<ByteBuffer> txRoot = env.txnWrite()) {
      env.txn(txRoot, MDB_RDONLY_TXN); // error
    }
  }

  @Test
  public void txReadOnly() {
    try (Txn<ByteBuffer> txn = env.txnRead()) {
      assertThat(txn.getParent(), is(nullValue()));
      assertThat(txn.getState(), is(READY));
      assertThat(txn.isReadOnly(), is(true));
      txn.checkReady();
      txn.checkReadOnly();
      txn.reset();
      assertThat(txn.getState(), is(RESET));
      txn.renew();
      assertThat(txn.getState(), is(READY));
      txn.commit();
      assertThat(txn.getState(), is(DONE));
      txn.close();
      assertThat(txn.getState(), is(RELEASED));
    }
  }

  @Test
  public void txReadWrite() {
    final Txn<ByteBuffer> txn = env.txnWrite();
    assertThat(txn.getParent(), is(nullValue()));
    assertThat(txn.getState(), is(READY));
    assertThat(txn.isReadOnly(), is(false));
    txn.checkReady();
    txn.checkWritesAllowed();
    txn.commit();
    assertThat(txn.getState(), is(DONE));
    txn.close();
    assertThat(txn.getState(), is(RELEASED));
  }

  @Test(expected = NotResetException.class)
  public void txRenewDeniedWithoutPriorReset() {
    try (Txn<ByteBuffer> txn = env.txnRead()) {
      txn.renew();
    }
  }

  @Test(expected = ResetException.class)