@Test
  public void testBatchWithActiveCursor() {
    DBCollection c = _db.getCollection("testBatchWithActiveCursor");
    c.drop();

    for (int i = 0; i < 100; i++) c.save(new BasicDBObject("x", i));

    try {
      DBCursor cursor = c.find().batchSize(2); // setting to 1, actually sets to 2 (why, oh why?)
      cursor.next(); // creates real cursor on server.
      cursor.next();
      assertEquals(0, cursor.numGetMores());
      cursor.next();
      assertEquals(1, cursor.numGetMores());
      cursor.next();
      cursor.next();
      assertEquals(2, cursor.numGetMores());
      cursor.next();
      cursor.next();
      assertEquals(3, cursor.numGetMores());
      cursor.batchSize(20);
      cursor.next();
      cursor.next();
      cursor.next();
      assertEquals(4, cursor.numGetMores());
    } catch (IllegalStateException e) {
      assertNotNull(e); // there must be a better way to detect this.
    }
  }
  @Test
  public void testExplain() {
    DBCollection c = _db.getCollection("explain1");
    c.drop();

    for (int i = 0; i < 100; i++) c.save(new BasicDBObject("x", i));

    DBObject q = BasicDBObjectBuilder.start().push("x").add("$gt", 50).get();

    assertEquals(49, c.find(q).count());
    assertEquals(49, c.find(q).itcount());
    assertEquals(49, c.find(q).toArray().size());
    assertEquals(49, c.find(q).itcount());
    assertEquals(20, c.find(q).limit(20).itcount());
    assertEquals(20, c.find(q).limit(-20).itcount());

    c.ensureIndex(new BasicDBObject("x", 1));

    assertEquals(49, c.find(q).count());
    assertEquals(49, c.find(q).toArray().size());
    assertEquals(49, c.find(q).itcount());
    assertEquals(20, c.find(q).limit(20).itcount());
    assertEquals(20, c.find(q).limit(-20).itcount());

    assertEquals(49, c.find(q).explain().get("n"));

    assertEquals(20, c.find(q).limit(20).explain().get("n"));
    assertEquals(20, c.find(q).limit(-20).explain().get("n"));
  }
  @Test // (enabled = false)
  public void testTailableAwait()
      throws ExecutionException, TimeoutException, InterruptedException {
    DBCollection c = _db.getCollection("tail1");
    c.drop();
    _db.createCollection("tail1", new BasicDBObject("capped", true).append("size", 10000));
    for (int i = 0; i < 10; i++) {
      c.save(new BasicDBObject("x", i), WriteConcern.SAFE);
    }

    final DBCursor cur =
        c.find()
            .sort(new BasicDBObject("$natural", 1))
            .addOption(Bytes.QUERYOPTION_TAILABLE | Bytes.QUERYOPTION_AWAITDATA);
    Callable<Object> callable =
        new Callable<Object>() {
          @Override
          public Object call() throws Exception {
            try {
              // the following call will block on the last hasNext
              int i = 0;
              while (cur.hasNext()) {
                DBObject obj = cur.next();
                i++;
                if (i > 10) return obj.get("x");
              }

              return null;
            } catch (Throwable e) {
              return e;
            }
          }
        };

    ExecutorService es = Executors.newSingleThreadExecutor();
    Future<Object> future = es.submit(callable);

    Thread.sleep(5000);
    assertTrue(!future.isDone());

    // this doc should unblock thread
    c.save(new BasicDBObject("x", 10), WriteConcern.SAFE);
    Object retVal = future.get(5, TimeUnit.SECONDS);
    assertEquals(10, retVal);
  }
  @Test
  public void testBatchWithLimit() {
    DBCollection c = _db.getCollection("batchWithLimit1");
    c.drop();

    for (int i = 0; i < 100; i++) c.save(new BasicDBObject("x", i));

    assertEquals(50, c.find().limit(50).itcount());
    assertEquals(50, c.find().batchSize(5).limit(50).itcount());
  }
 @Test(groups = {"basic"})
 public void testSnapshot() {
   DBCollection c = _db.getCollection("snapshot1");
   c.drop();
   for (int i = 0; i < 100; i++) c.save(new BasicDBObject("x", i));
   assertEquals(100, c.find().count());
   assertEquals(100, c.find().toArray().size());
   assertEquals(100, c.find().snapshot().count());
   assertEquals(100, c.find().snapshot().toArray().size());
   assertEquals(100, c.find().snapshot().limit(50).count());
   assertEquals(50, c.find().snapshot().limit(50).toArray().size());
 }
  @Test
  public void testLargeBatch() {
    DBCollection c = _db.getCollection("largeBatch1");
    c.drop();

    int total = 50000;
    int batch = 10000;
    for (int i = 0; i < total; i++) c.save(new BasicDBObject("x", i));

    DBCursor cursor = c.find().batchSize(batch);
    assertEquals(total, cursor.itcount());
    assertEquals(total / batch + 1, cursor.getSizes().size());
  }
  @Test // (enabled = false)
  public void testTailable() {
    DBCollection c = _db.getCollection("tail1");
    c.drop();
    _db.createCollection("tail1", new BasicDBObject("capped", true).append("size", 10000));
    for (int i = 0; i < 10; i++) {
      c.save(new BasicDBObject("x", i));
    }

    DBCursor cur =
        c.find().sort(new BasicDBObject("$natural", 1)).addOption(Bytes.QUERYOPTION_TAILABLE);

    while (cur.hasNext()) {
      cur.next();
      // do nothing...
    }

    assert (!cur.hasNext());
    c.save(new BasicDBObject("x", 12));
    assert (cur.hasNext());
    assertNotNull(cur.next());
    assert (!cur.hasNext());
  }
  @Test
  public void testBig() {
    DBCollection c = _db.getCollection("big1");
    c.drop();

    String bigString;
    {
      StringBuilder buf = new StringBuilder(16000);
      for (int i = 0; i < 16000; i++) buf.append("x");
      bigString = buf.toString();
    }

    int numToInsert = (15 * 1024 * 1024) / bigString.length();

    for (int i = 0; i < numToInsert; i++)
      c.save(BasicDBObjectBuilder.start().add("x", i).add("s", bigString).get());

    assert (800 < numToInsert);

    assertEquals(numToInsert, c.find().count());
    assertEquals(numToInsert, c.find().toArray().size());
    assertEquals(numToInsert, c.find().limit(800).count());
    assertEquals(800, c.find().limit(800).toArray().size());

    // negative limit works like negative batchsize, for legacy reason
    int x = c.find().limit(-800).toArray().size();
    assertLess(x, 800);

    DBCursor a = c.find();
    assertEquals(numToInsert, a.itcount());

    DBCursor b = c.find().batchSize(10);
    assertEquals(numToInsert, b.itcount());
    assertEquals(10, b.getSizes().get(0).intValue());

    assertLess(a.numGetMores(), b.numGetMores());

    assertEquals(numToInsert, c.find().batchSize(2).itcount());
    assertEquals(numToInsert, c.find().batchSize(1).itcount());

    assertEquals(numToInsert, _count(c.find(null, null).skip(0).batchSize(5)));
    assertEquals(5, _count(c.find(null, null).skip(0).batchSize(-5)));
  }
  @Test
  public void testLimitAndBatchSize() {
    DBCollection c = _db.getCollection("LimitAndBatchSize");
    c.drop();

    for (int i = 0; i < 1000; i++) c.save(new BasicDBObject("x", i));

    DBObject q = BasicDBObjectBuilder.start().push("x").add("$lt", 200).get();

    DBCursor cur = c.find(q);
    assertEquals(0, cur.getCursorId());
    assertEquals(200, cur.itcount());

    cur = c.find(q).limit(50);
    assertEquals(0, cur.getCursorId());
    assertEquals(50, cur.itcount());

    cur = c.find(q).batchSize(50);
    assertEquals(0, cur.getCursorId());
    assertEquals(200, cur.itcount());

    cur = c.find(q).batchSize(100).limit(50);
    assertEquals(0, cur.getCursorId());
    assertEquals(50, cur.itcount());

    cur = c.find(q).batchSize(-40);
    assertEquals(0, cur.getCursorId());
    assertEquals(40, cur.itcount());

    cur = c.find(q).limit(-100);
    assertEquals(0, cur.getCursorId());
    assertEquals(100, cur.itcount());

    cur = c.find(q).batchSize(-40).limit(20);
    assertEquals(0, cur.getCursorId());
    assertEquals(20, cur.itcount());

    cur = c.find(q).batchSize(-20).limit(100);
    assertEquals(0, cur.getCursorId());
    assertEquals(20, cur.itcount());
  }