/**
   * Add two, check that only one comes out Mark first as done, second comes out
   *
   * @throws InterruptedException
   */
  @Test
  public void testTwoTransactions() throws InterruptedException {
    final long txn_id0 = 1000;
    final long txn_id1 = 2000;
    Collection<Integer> partitions0 = CatalogUtil.getAllPartitionIds(catalog_db);
    Collection<Integer> partitions1 = CatalogUtil.getAllPartitionIds(catalog_db);

    final MockCallback inner_callback0 = new MockCallback();
    TransactionInitWrapperCallback outer_callback0 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback0.init(txn_id0, partitions0, inner_callback0);

    final MockCallback inner_callback1 = new MockCallback();
    TransactionInitWrapperCallback outer_callback1 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback1.init(txn_id1, partitions1, inner_callback1);

    // insert the higher ID first but make sure it comes out second
    this.queue.insert(txn_id1, partitions1, outer_callback1);
    this.queue.insert(txn_id0, partitions0, outer_callback0);

    // create another thread to get the locks in order
    Thread t =
        new Thread() {
          public void run() {
            try {
              inner_callback0.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id0, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
            try {
              inner_callback1.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id1, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
          }
        };
    t.start();

    while (queue.isEmpty() == false) {
      queue.checkQueues();
      ThreadUtil.sleep(10);
    }

    // wait for all the locks to be acquired
    t.join();
  }
  /**
   * Insert the txn into our queue and then call check This should immediately release our
   * transaction and invoke the inner_callback
   *
   * @throws InterruptedException
   */
  @Test
  public void testSingleTransaction() throws InterruptedException {
    long txn_id = 1000;
    Collection<Integer> partitions = CatalogUtil.getAllPartitionIds(catalog_db);

    MockCallback inner_callback = new MockCallback();
    TransactionInitWrapperCallback outer_callback = new TransactionInitWrapperCallback(hstore_site);
    outer_callback.init(txn_id, partitions, inner_callback);

    // Insert the txn into our queue and then call check
    // This should immediately release our transaction and invoke the inner_callback
    boolean ret = this.queue.insert(txn_id, partitions, outer_callback);
    assert (ret);

    int tries = 10;
    while (queue.isEmpty() == false && tries-- > 0) {
      queue.checkQueues();
      ThreadUtil.sleep(100);
    }
    assert (inner_callback.lock.availablePermits() > 0);
    // Block on the MockCallback's lock until our thread above is able to release everybody.
    // inner_callback.lock.acquire();
  }
  /**
   * Add two overlapping partitions, lowest id comes out Mark first as done, second comes out
   *
   * @throws InterruptedException
   */
  @Test
  public void testOverlappingTransactions() throws InterruptedException {
    final long txn_id0 = 1000;
    final long txn_id1 = 2000;
    Collection<Integer> partitions0 = new HashSet<Integer>();
    partitions0.add(0);
    partitions0.add(1);
    partitions0.add(2);
    Collection<Integer> partitions1 = new HashSet<Integer>();
    partitions1.add(2);
    partitions1.add(3);

    final MockCallback inner_callback0 = new MockCallback();
    TransactionInitWrapperCallback outer_callback0 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback0.init(txn_id0, partitions0, inner_callback0);

    final MockCallback inner_callback1 = new MockCallback();
    TransactionInitWrapperCallback outer_callback1 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback1.init(txn_id1, partitions1, inner_callback1);

    this.queue.insert(txn_id0, partitions0, outer_callback0);
    this.queue.insert(txn_id1, partitions1, outer_callback1);

    // create another thread to get the locks in order
    Thread t =
        new Thread() {
          public void run() {
            try {
              inner_callback0.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id0, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
            try {
              inner_callback1.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id1, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
          }
        };
    t.start();

    // only the first txn should be released because they are not disjoint
    while (queue.checkQueues() == false) {
      ThreadUtil.sleep(10);
    }
    assertFalse(queue.isEmpty());

    while (queue.isEmpty() == false) {
      queue.checkQueues();
      ThreadUtil.sleep(10);
    }

    // wait for all the locks to be acquired
    t.join();
  }
  /**
   * Add two disjoint partitions and third that touches all partitions Two come out right away and
   * get marked as done Third doesn't come out until everyone else is done
   *
   * @throws InterruptedException
   */
  @Test
  public void testDisjointTransactions() throws InterruptedException {
    final long txn_id0 = 1000;
    final long txn_id1 = 2000;
    final long txn_id2 = 3000;
    Collection<Integer> partitions0 = new HashSet<Integer>();
    partitions0.add(0);
    partitions0.add(2);
    Collection<Integer> partitions1 = new HashSet<Integer>();
    partitions1.add(1);
    partitions1.add(3);
    Collection<Integer> partitions2 = CatalogUtil.getAllPartitionIds(catalog_db);

    final MockCallback inner_callback0 = new MockCallback();
    TransactionInitWrapperCallback outer_callback0 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback0.init(txn_id0, partitions0, inner_callback0);

    final MockCallback inner_callback1 = new MockCallback();
    TransactionInitWrapperCallback outer_callback1 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback1.init(txn_id1, partitions1, inner_callback1);

    final MockCallback inner_callback2 = new MockCallback();
    TransactionInitWrapperCallback outer_callback2 =
        new TransactionInitWrapperCallback(hstore_site);
    outer_callback2.init(txn_id2, partitions2, inner_callback2);

    this.queue.insert(txn_id0, partitions0, outer_callback0);
    this.queue.insert(txn_id1, partitions1, outer_callback1);

    // create another thread to get the locks in order
    Thread t =
        new Thread() {
          public void run() {
            try {
              inner_callback0.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id0, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
            try {
              inner_callback1.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id1, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
            try {
              inner_callback2.lock.acquire();
              for (int partition = 0; partition < NUM_PARTITONS; ++partition) {
                queue.finished(txn_id2, Status.OK, partition);
              }
            } catch (InterruptedException e) {
            }
          }
        };
    t.start();

    // both of the first two disjoint txns should be released on the same call to checkQueues()
    while (queue.checkQueues() == false) {
      ThreadUtil.sleep(10);
    }
    assertTrue(queue.isEmpty());

    // add the third txn and wait for it
    this.queue.insert(txn_id2, partitions2, outer_callback2);
    while (queue.isEmpty() == false) {
      queue.checkQueues();
      ThreadUtil.sleep(10);
    }

    // wait for all the locks to be acquired
    t.join();
  }