public void testBusyDownloadLocatesSources() 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);
    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(1000);
      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) {
          fail("was successful for " + i, iioe);
        }
      }
    }

    // Thread.sleep((UDP_ACCESS.length * 1000) -
    // (System.currentTimeMillis() - currTime));
    int guessWaitTime = 5000;
    Thread.sleep(guessWaitTime + 2000);
    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();
  }
  public void testDownloadFinishes() 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 = TestFile.hash();
    RemoteFileDesc rfd = makeRFD(urn);

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

    // just do it for 1 UDP guy
    {
      ReplyNumberVendorMessage vm =
          replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(
              new GUID(qr.getGUID()), 1);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      vm.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[0].send(pack);
    }

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

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

    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 get a query key request
    {
      boolean gotPing = false;
      while (!gotPing) {
        byte[] datagramBytes = new byte[1000];
        pack = new DatagramPacket(datagramBytes, 1000);
        UDP_ACCESS[0].setSoTimeout(10000); // may need to wait
        UDP_ACCESS[0].receive(pack);
        InputStream in = new ByteArrayInputStream(pack.getData());
        m = messageFactory.read(in, Network.TCP);
        m.hop();
        if (m instanceof PingRequest) gotPing = ((PingRequest) m).isQueryKeyRequest();
      }
    }

    // send back a query key
    AddressSecurityToken qk =
        new AddressSecurityToken(InetAddress.getLocalHost(), SERVER_PORT, macManager);
    {
      byte[] ip = new byte[] {(byte) 127, (byte) 0, (byte) 0, (byte) 1};
      PingReply pr =
          pingReplyFactory.createQueryKeyReply(
              GUID.makeGuid(), (byte) 1, UDP_ACCESS[0].getLocalPort(), ip, 10, 10, false, qk);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      pr.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[0].send(pack);
    }

    Thread.sleep(500);

    // 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(1, _queryKeys.size());
    }

    byte[] urnQueryGUID = null;
    { // confirm a URN query
      boolean gotQuery = false;
      while (!gotQuery) {
        byte[] datagramBytes = new byte[1000];
        pack = new DatagramPacket(datagramBytes, 1000);
        UDP_ACCESS[0].setSoTimeout(10000); // may need to wait
        UDP_ACCESS[0].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);
            if (gotQuery) urnQueryGUID = qReq.getGUID();
          }
        }
      }
    }

    assertNotNull(urnQueryGUID);

    long timeoutVal = 8000 - (System.currentTimeMillis() - currTime);
    Thread.sleep(timeoutVal > 0 ? timeoutVal : 0);
    assertEquals(DownloadState.BUSY, downloader.getState());
    // purge front end of query
    callback.clearGUID();

    // create a new Uploader to service the download
    TestUploader uploader2 = new TestUploader(networkManagerStub);
    uploader2.start("whatever", UPLOADER_PORT + 1, false);
    uploader2.setRate(100);

    { // send back a query request, the TestUploader should service upload
      rfd = makeRFD(urn, UPLOADER_PORT + 1);
      Response[] res = new Response[] {responseFactory.createResponse(10, 10, "whatever", urn)};
      m =
          queryReplyFactory.createQueryReply(
              urnQueryGUID,
              (byte) 1,
              UPLOADER_PORT + 1,
              myIP(),
              0,
              res,
              GUID.makeGuid(),
              new byte[0],
              false,
              false,
              true,
              true,
              false,
              false,
              null);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      m.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[0].send(pack);
    }

    // after a while, the download should finish, the bypassed results
    // should be discarded
    Thread.sleep(10000);
    assertEquals(DownloadState.COMPLETE, downloader.getState());

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

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

    // luckily there is hacky little way to go through the download paces -
    // download from yourself :) .

    Message m = null;

    byte[] guid = searchServices.newQueryGUID();
    searchServices.query(guid, "metadata");
    callback.setGUID(new GUID(guid));

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

    // just return ONE real result and the rest junk
    Response resp = null;
    QueryReply reply = null;
    {
      // get a correct response object
      QueryRequest qrTemp = queryRequestFactory.createQuery("metadata");
      testUP[0].send(qrTemp);
      testUP[0].flush();

      reply = BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0], QueryReply.class);
      assertNotNull(reply);
      resp = (reply.getResultsAsList()).get(0);
    }
    assertNotNull(reply);
    assertNotNull(resp);
    Response[] res = new Response[] {resp};

    // this isn't really needed but just for completeness send it back to
    // the test Leaf
    m =
        queryReplyFactory.createQueryReply(
            guid,
            (byte) 1,
            SERVER_PORT,
            myIP(),
            0,
            res,
            GUID.makeGuid(),
            new byte[0],
            false,
            false,
            true,
            true,
            false,
            false,
            null);
    testUP[0].send(m);
    testUP[0].flush();

    // send back a lot of results via TCP so you konw the UDP one will be
    // bypassed
    for (int i = 0; i < testUP.length; i++) {
      res = new Response[75];
      for (int j = 0; j < res.length; j++)
        res[j] =
            responseFactory.createResponse(
                10 + j + i, 10 + j + i, "metadata " + j + i, UrnHelper.SHA1);
      m =
          queryReplyFactory.createQueryReply(
              guid,
              (byte) 1,
              testUP[0].getPort(),
              myIP(),
              0,
              res,
              GUID.makeGuid(),
              new byte[0],
              false,
              false,
              true,
              true,
              false,
              false,
              null);
      testUP[i].send(m);
      testUP[i].flush();
    }

    // allow for processing
    Thread.sleep(3000);

    {
      // now we should make sure MessageRouter has not bypassed anything
      // yet
      assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
    }

    // send back a UDP response and make sure it was saved in bypassed...
    {
      ReplyNumberVendorMessage vm =
          replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(guid), 1);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      vm.write(baos);
      DatagramPacket pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[0].send(pack);
    }

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

    {
      // all the UDP ReplyNumberVMs should have been bypassed
      assertByPassedResultsCacheHasSize(guid, 1);
    }

    // now do the download, wait for it to finish, and then bypassed results
    // should be empty again
    RemoteFileDesc rfd =
        resp.toRemoteFileDesc(reply, null, remoteFileDescFactory, pushEndpointFactory);

    assertFalse("file should not be saved yet", new File(_savedDir, "metadata.mp3").exists());

    downloadServices.download(new RemoteFileDesc[] {rfd}, false, new GUID(guid));
    UploadSettings.UPLOAD_SPEED.setValue(5);

    searchServices.stopQuery(new GUID(guid));
    callback.clearGUID();

    // download still in progress, don't purge
    assertByPassedResultsCacheHasSize(guid, 1);

    UploadSettings.UPLOAD_SPEED.setValue(100);

    // sleep to make sure the download starts
    Thread.sleep(20000);

    assertTrue("file should saved", new File(_savedDir, "metadata.mp3").exists());

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

    // set smaller clear times so we can test in a timely fashion

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

    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();
    }

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

    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);
      DatagramPacket pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[i].send(pack);
    }

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

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

    {
      // now we should make sure MessageRouter clears the map
      searchServices.stopQuery(new GUID(qr.getGUID()));
      assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
    }
    callback.clearGUID();
  }
  // 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 testMultipleDownloadsNoPurge() 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);
    RemoteFileDesc rfd = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");

    TestUploader uploader2 = new TestUploader(networkManagerStub);
    uploader2.start("whatever", UPLOADER_PORT * 2, false);
    uploader2.setBusy(true);
    RemoteFileDesc rfd2 = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSASUSH");

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

    { // bypass 1 result only
      ReplyNumberVendorMessage vm =
          replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(
              new GUID(qr.getGUID()), 1);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      vm.write(baos);
      pack =
          new DatagramPacket(
              baos.toByteArray(),
              baos.toByteArray().length,
              testUP[0].getInetAddress(),
              SERVER_PORT);
      UDP_ACCESS[0].send(pack);
    }

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

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

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

    //  Don't try using the same default file
    Downloader downloader2 =
        downloadServices.download(
            new RemoteFileDesc[] {rfd2}, new GUID(guid), false, null, "anotherFile");

    // let downloaders do stuff
    final int MAX_TRIES = 60;
    boolean oneGood = false, twoGood = false;
    for (int i = 0; i <= MAX_TRIES; i++) {
      Thread.sleep(500);
      if (downloader.getState() == DownloadState.BUSY) oneGood = true;
      if (downloader2.getState() == DownloadState.BUSY) twoGood = true;
      if (oneGood && twoGood) break;
      if (i == MAX_TRIES) fail("didn't GUESS!!");
    }

    callback.clearGUID(); // isQueryAlive == false
    downloader.stop();

    Thread.sleep(500);

    {
      // we should still have bypassed results since downloader2 alive
      assertByPassedResultsCacheHasSize(qr.getGUID(), 1);
    }

    downloader2.stop();
    Thread.sleep(1000);

    {
      // now we should make sure MessageRouter clears the map
      assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
    }
    uploader.stopThread();
    uploader2.stopThread();
  }
  // MUST RUN THIS TEST FIRST
  public void testMixedProtocol() throws Exception {
    DatagramPacket pack = null;
    UDP_ACCESS = new DatagramSocket();

    for (int i = 0; i < testUP.length; i++) {
      assertTrue("not open", testUP[i].isOpen());
      assertTrue("not up->leaf", testUP[i].isSupernodeClientConnection());
      drain(testUP[i], 500);
      if ((i == 2)) { // i'll send 0 later....
        testUP[i].send(MessagesSupportedVendorMessage.instance());
        testUP[i].flush();
      }
    }

    testUP[0].send(MessagesSupportedVendorMessage.instance());
    testUP[0].flush();

    // first we need to set up GUESS capability
    UDP_ACCESS.setSoTimeout(TIMEOUT * 2);
    // ----------------------------------------
    // set up solicited UDP support
    PrivilegedAccessor.setValue(rs.getUdpService(), "_acceptedSolicitedIncoming", Boolean.TRUE);

    // set up unsolicited UDP support
    PrivilegedAccessor.setValue(rs.getUdpService(), "_acceptedUnsolicitedIncoming", Boolean.TRUE);

    // you also have to set up TCP incoming....
    {
      Socket sock = null;
      OutputStream os = null;
      try {
        sock = Sockets.connect(InetAddress.getLocalHost().getHostAddress(), SERVER_PORT, 12);
        os = sock.getOutputStream();
        os.write("\n\n".getBytes());
      } catch (IOException ignored) {
      } catch (SecurityException ignored) {
      } catch (Throwable t) {
        ErrorService.error(t);
      } finally {
        if (sock != null)
          try {
            sock.close();
          } catch (IOException ignored) {
          }
        if (os != null)
          try {
            os.close();
          } catch (IOException ignored) {
          }
      }
    }

    // ----------------------------------------

    Thread.sleep(250);
    // we should now be guess capable and tcp incoming capable....
    assertTrue(RouterService.isGUESSCapable());
    assertTrue(RouterService.acceptedIncomingConnection());

    // get rid of any messages that are stored up.
    drainAll();

    // first of all, we should confirm that we are sending out a OOB query.
    GUID queryGuid = new GUID(RouterService.newQueryGUID());
    assertTrue(
        GUID.addressesMatch(
            queryGuid.bytes(), RouterService.getAddress(), RouterService.getPort()));
    RouterService.query(queryGuid.bytes(), "susheel");
    Thread.sleep(250);

    // some connected UPs should get a OOB query
    for (int i = 0; i < testUP.length; i++) {
      QueryRequest qr = getFirstQueryRequest(testUP[i], TIMEOUT);
      assertNotNull("up " + i + " didn't get query", qr);
      assertEquals(new GUID(qr.getGUID()), queryGuid);
      if ((i == 0) || (i == 2)) assertTrue(qr.desiresOutOfBandReplies());
      else assertTrue(!qr.desiresOutOfBandReplies());
    }

    // now confirm that we leaf guide the 'even' guys but not the others.
    Message m = null;
    // ensure that we'll get a QueryStatusResponse from the Responses
    // we're sending.
    for (int i = 0; i < testUP.length; i++) {
      Response[] res = new Response[7];
      res[0] = new Response(10, 10, "susheel" + i);
      res[1] = new Response(10, 10, "susheel smells good" + i);
      res[2] = new Response(10, 10, "anita is sweet" + i);
      res[3] = new Response(10, 10, "anita is prety" + i);
      res[4] = new Response(10, 10, "susheel smells bad" + i);
      res[5] = new Response(10, 10, "renu is sweet " + i);
      res[6] = new Response(10, 10, "prety is spelled pretty " + i);
      m =
          new QueryReply(
              queryGuid.bytes(),
              (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();
    }

    // all UPs should get a QueryStatusResponse
    for (int i = 0; i < testUP.length; i++) {
      QueryStatusResponse stat = getFirstQueryStatus(testUP[i]);
      if ((i == 0) || (i == 2)) {
        assertNotNull(stat);
        assertEquals(new GUID(stat.getGUID()), queryGuid);
        assertEquals(5, stat.getNumResults());
      } else assertNull(stat);
    }

    // shut off the query....
    RouterService.stopQuery(queryGuid);

    // all UPs should get a QueryStatusResponse with 65535
    for (int i = 0; i < testUP.length; i++) {
      QueryStatusResponse stat = getFirstQueryStatus(testUP[i]);
      if ((i == 0) || (i == 2)) {
        assertNotNull(stat);
        assertEquals(new GUID(stat.getGUID()), queryGuid);
        assertEquals(65535, stat.getNumResults());
      } else assertNull(stat);
    }
  }