// RUN THIS TEST LAST!!
  // TODO move this test case to OnDemandUnicasterTest, sounds like a pure unit test
  // proabably doesn't make sense anymore since it doesn't have any data from the
  // previous tests to clear
  public void testUnicasterClearingCode() throws Exception {

    BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
    // clear up any messages before we begin the test.
    drainAll();

    { // clear all the unicaster data structures
      Long[] longs = new Long[] {new Long(0), new Long(1)};
      Class[] classTypes = new Class[] {Long.TYPE, Long.TYPE};
      // now confirm that clearing code works
      Object ret =
          PrivilegedAccessor.invokeMethod(
              onDemandUnicaster, "clearDataStructures", longs, classTypes);
      assertTrue(ret instanceof Boolean);
      assertTrue(((Boolean) ret).booleanValue());
    }

    DatagramPacket pack = null;

    Message m = null;

    byte[] guid = searchServices.newQueryGUID();
    searchServices.query(guid, "whatever");
    // i need to pretend that the UI is showing the user the query still
    callback.setGUID(new GUID(guid));

    QueryRequest qr =
        BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0], QueryRequest.class);
    assertNotNull(qr);
    assertTrue(qr.desiresOutOfBandReplies());

    // ok, the leaf is sending OOB queries - good stuff, now we should send
    // a lot of results back and make sure it buffers the bypassed OOB ones
    for (int i = 0; i < testUP.length; i++) {
      Response[] res = new Response[200];
      for (int j = 0; j < res.length; j++)
        res[j] =
            responseFactory.createResponse(
                10 + j + i, 10 + j + i, "whatever " + j + i, UrnHelper.SHA1);
      m =
          queryReplyFactory.createQueryReply(
              qr.getGUID(),
              (byte) 1,
              6355,
              myIP(),
              0,
              res,
              GUID.makeGuid(),
              new byte[0],
              false,
              false,
              true,
              true,
              false,
              false,
              null);
      testUP[i].send(m);
      testUP[i].flush();
    }

    // create a test uploader and send back that response
    TestUploader uploader = new TestUploader(networkManagerStub);
    uploader.start("whatever", UPLOADER_PORT, false);
    uploader.setBusy(true);
    RemoteFileDesc rfd = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");

    // wait for processing
    Thread.sleep(1500);

    for (int i = 0; i < UDP_ACCESS.length; i++) {
      ReplyNumberVendorMessage vm =
          replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(
              new GUID(qr.getGUID()), i + 1);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      vm.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[i].send(pack);
    }

    // wait for processing
    Thread.sleep(500);

    {
      // all the UDP ReplyNumberVMs should have been bypassed
      assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
    }

    Downloader downloader =
        downloadServices.download(new RemoteFileDesc[] {rfd}, false, new GUID(guid));

    final int MAX_TRIES = 60;
    for (int i = 0; i <= MAX_TRIES; i++) {
      Thread.sleep(500);
      if (downloader.getState() == DownloadState.ITERATIVE_GUESSING) break;
      if (i == MAX_TRIES) fail("didn't GUESS!!");
    }

    // we should start getting guess queries on all UDP ports, actually
    // querykey requests
    for (int i = 0; i < UDP_ACCESS.length; i++) {
      boolean gotPing = false;
      while (!gotPing) {
        try {
          byte[] datagramBytes = new byte[1000];
          pack = new DatagramPacket(datagramBytes, 1000);
          UDP_ACCESS[i].setSoTimeout(10000); // may need to wait
          UDP_ACCESS[i].receive(pack);
          InputStream in = new ByteArrayInputStream(pack.getData());
          m = messageFactory.read(in, Network.TCP);
          m.hop();
          if (m instanceof PingRequest) gotPing = ((PingRequest) m).isQueryKeyRequest();
        } catch (InterruptedIOException iioe) {
          assertTrue("was successful for " + i, false);
        }
      }
    }

    // Prepopulate Query Keys
    AddressSecurityToken qk =
        new AddressSecurityToken(InetAddress.getLocalHost(), SERVER_PORT, macManager);
    for (int i = 0; i < (UDP_ACCESS.length / 2); i++) {
      byte[] ip = new byte[] {(byte) 127, (byte) 0, (byte) 0, (byte) 1};
      PingReply pr =
          pingReplyFactory.createQueryKeyReply(
              GUID.makeGuid(), (byte) 1, UDP_ACCESS[i].getLocalPort(), ip, 10, 10, false, qk);
      pr.hop();
      onDemandUnicaster.handleQueryKeyPong(pr);
    }

    // ensure that it gets into the OnDemandUnicaster
    {
      // now we should make sure MessageRouter retains the key
      Map _queryKeys = (Map) PrivilegedAccessor.getValue(onDemandUnicaster, "_queryKeys");
      assertNotNull(_queryKeys);
      assertEquals((UDP_ACCESS.length / 2), _queryKeys.size());

      // now make sure some URNs are still buffered
      Map _bufferedURNs = (Map) PrivilegedAccessor.getValue(onDemandUnicaster, "_bufferedURNs");
      assertNotNull(_bufferedURNs);
      assertEquals((UDP_ACCESS.length / 2), _bufferedURNs.size());
    }

    // now until those guys get expired
    Thread.sleep(60 * 1000);

    {
      Long[] longs = new Long[] {new Long(0), new Long(1)};
      Class[] classTypes = new Class[] {Long.TYPE, Long.TYPE};
      // now confirm that clearing code works
      Object ret =
          PrivilegedAccessor.invokeMethod(
              onDemandUnicaster, "clearDataStructures", longs, classTypes);
      assertTrue(ret instanceof Boolean);
      assertTrue(((Boolean) ret).booleanValue());
    }

    // ensure that clearing worked
    {
      // now we should make sure MessageRouter retains the key
      Map _queryKeys = (Map) PrivilegedAccessor.getValue(onDemandUnicaster, "_queryKeys");
      assertNotNull(_queryKeys);
      assertEquals(0, _queryKeys.size());

      // now make sure some URNs are still buffered
      Map _bufferedURNs = (Map) PrivilegedAccessor.getValue(onDemandUnicaster, "_bufferedURNs");
      assertNotNull(_bufferedURNs);
      assertEquals(0, _bufferedURNs.size());
    }

    assertEquals(DownloadState.BUSY, downloader.getState());

    callback.clearGUID();
    downloader.stop();

    Thread.sleep(1000);

    {
      // now we should make sure MessageRouter clears the map
      assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
    }
  }
  public void testUsesCachedQueryKeys() throws Exception {

    BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
    // clear up any messages before we begin the test.
    drainAll();

    DatagramPacket pack = null;

    Message m = null;

    byte[] guid = searchServices.newQueryGUID();
    searchServices.query(guid, "whatever");
    // i need to pretend that the UI is showing the user the query still
    callback.setGUID(new GUID(guid));

    QueryRequest qr =
        BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0], QueryRequest.class);
    assertNotNull(qr);
    assertTrue(qr.desiresOutOfBandReplies());

    // ok, the leaf is sending OOB queries - good stuff, now we should send
    // a lot of results back and make sure it buffers the bypassed OOB ones
    for (int i = 0; i < testUP.length; i++) {
      Response[] res = new Response[200];
      for (int j = 0; j < res.length; j++)
        res[j] =
            responseFactory.createResponse(
                10 + j + i, 10 + j + i, "whatever " + j + i, UrnHelper.SHA1);
      m =
          queryReplyFactory.createQueryReply(
              qr.getGUID(),
              (byte) 1,
              6355,
              myIP(),
              0,
              res,
              GUID.makeGuid(),
              new byte[0],
              false,
              false,
              true,
              true,
              false,
              false,
              null);
      testUP[i].send(m);
      testUP[i].flush();
    }

    // create a test uploader and send back that response
    TestUploader uploader = new TestUploader(networkManagerStub);
    uploader.start("whatever", UPLOADER_PORT, false);
    uploader.setBusy(true);
    URN urn = URN.createSHA1Urn("urn:sha1:GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");
    RemoteFileDesc rfd = makeRFD(urn);

    // wait for processing
    Thread.sleep(1500);

    // send back ReplyNumberVMs that should be bypassed
    for (int i = 0; i < UDP_ACCESS.length; i++) {
      ReplyNumberVendorMessage vm =
          replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(
              new GUID(qr.getGUID()), i + 1);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      vm.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[i].send(pack);
    }

    // wait for processing
    Thread.sleep(500);

    {
      // all the UDP ReplyNumberVMs should have been bypassed
      assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
    }

    // Prepopulate Query Keys
    AddressSecurityToken qk =
        new AddressSecurityToken(InetAddress.getLocalHost(), SERVER_PORT, macManager);
    for (int i = 0; i < UDP_ACCESS.length; i++) {
      byte[] ip = new byte[] {(byte) 127, (byte) 0, (byte) 0, (byte) 1};
      PingReply pr =
          pingReplyFactory.createQueryKeyReply(
              GUID.makeGuid(), (byte) 1, UDP_ACCESS[i].getLocalPort(), ip, 10, 10, false, qk);
      pr.hop();
      onDemandUnicaster.handleQueryKeyPong(pr);
    }

    // confirm download will try to GUESS
    long currTime = System.currentTimeMillis();
    Downloader downloader =
        downloadServices.download(new RemoteFileDesc[] {rfd}, false, new GUID(guid));

    final int MAX_TRIES = 60;
    for (int i = 0; i <= MAX_TRIES; i++) {
      Thread.sleep(500);
      if (downloader.getState() == DownloadState.ITERATIVE_GUESSING) break;
      if (i == MAX_TRIES) fail("didn't GUESS!!");
    }

    // we should start getting guess queries on all UDP ports
    for (int i = 0; i < UDP_ACCESS.length; i++) {
      boolean gotQuery = false;
      while (!gotQuery) {
        try {
          byte[] datagramBytes = new byte[1000];
          pack = new DatagramPacket(datagramBytes, 1000);
          UDP_ACCESS[i].setSoTimeout(10000); // may need to wait
          UDP_ACCESS[i].receive(pack);
          InputStream in = new ByteArrayInputStream(pack.getData());
          m = messageFactory.read(in, Network.TCP);
          if (m instanceof QueryRequest) {
            QueryRequest qReq = (QueryRequest) m;
            Set queryURNs = qReq.getQueryUrns();
            gotQuery = queryURNs.contains(urn);
            if (gotQuery)
              gotQuery = qReq.getQueryKey().isFor(InetAddress.getLocalHost(), SERVER_PORT);
          }
        } catch (InterruptedIOException iioe) {
          assertTrue("was successful for " + i, false);
        }
      }
    }

    Thread.sleep((UDP_ACCESS.length * 1500) - (System.currentTimeMillis() - currTime));

    assertEquals(DownloadState.BUSY, downloader.getState());

    callback.clearGUID();
    downloader.stop();

    Thread.sleep(1000);

    {
      // now we should make sure MessageRouter clears the map
      assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
    }

    uploader.stopThread();
  }