@Test
  public void testUpdateDoesNotExist() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testUpdateDoesNotExist");

    NDNHandle thandle = NDNHandle.open();
    NDNHandle rhandle = NDNHandle.open();
    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testUpdateDoesNotExist"), collectionObjName);
      NDNStringObject so = new NDNStringObject(testName, rhandle);

      // so should catch exception thrown by underlying stream when it times out.
      Assert.assertFalse(so.available());

      // ask for it in background
      so.updateInBackground();

      NDNStringObject sowrite =
          new NDNStringObject(testName, "Now we write something.", SaveType.REPOSITORY, thandle);
      setupNamespace(testName);
      saveAndLog("Delayed write", sowrite, null, "Now we write something.");
      TestUtils.checkObject(thandle, sowrite);
      so.waitForData();
      Assert.assertTrue(so.available());
      Assert.assertEquals(so.string(), sowrite.string());
      Assert.assertEquals(so.getVersionedName(), sowrite.getVersionedName());
    } finally {
      thandle.close();
      rhandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testUpdateDoesNotExist");
  }
  @Test
  public void testEmptySave() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testEmptySave");

    boolean caught = false;
    NDNHandle handle = NDNHandle.open();
    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testEmptySave"), collectionObjName);
      CollectionObject emptycoll =
          new CollectionObject(testName, (Collection) null, SaveType.REPOSITORY, handle);
      setupNamespace(testName);
      try {
        emptycoll.setData(small1); // set temporarily to non-null
        saveAndLog("Empty", emptycoll, null, null);
        TestUtils.checkObject(handle, emptycoll);
      } catch (InvalidObjectException iox) {
        // this is what we expect to happen
        caught = true;
      }
      Assert.assertTrue("Failed to produce expected exception.", caught);
    } finally {
      handle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testEmptySave");
  }
  @BeforeClass
  public static void setUpBeforeClass() throws Exception {
    Log.info(
        Log.FAC_TEST,
        "Setting up NDNNetworkObjectTestRepo, prefix {0}",
        testHelper.getClassNamespace());

    NDNHandle myhandle = NDNHandle.open();
    try {
      ns = new ContentName[NUM_LINKS];
      for (int i = 0; i < NUM_LINKS; ++i) {
        ns[i] =
            new ContentName(testHelper.getClassNamespace(), "Links", prefix + Integer.toString(i));
      }
      Arrays.fill(publisherid1, (byte) 6);
      Arrays.fill(publisherid2, (byte) 3);

      pubID1 = new PublisherID(publisherid1, PublisherType.KEY);
      pubID2 = new PublisherID(publisherid2, PublisherType.ISSUER_KEY);

      las[0] = new LinkAuthenticator(pubID1);
      las[1] = null;
      las[2] = new LinkAuthenticator(pubID2, null, null, SignedInfo.ContentType.DATA, contenthash1);
      las[3] = new LinkAuthenticator(pubID1, null, NDNTime.now(), null, contenthash1);

      for (int j = 4; j < NUM_LINKS; ++j) {
        las[j] = new LinkAuthenticator(pubID2, null, NDNTime.now(), null, null);
      }

      lrs = new Link[NUM_LINKS];
      for (int i = 0; i < lrs.length; ++i) {
        lrs[i] = new Link(ns[i], las[i]);
      }

      empty = new Collection();
      small1 = new Collection();
      small2 = new Collection();
      for (int i = 0; i < 5; ++i) {
        small1.add(lrs[i]);
        small2.add(lrs[i + 5]);
      }
      big = new Collection();
      for (int i = 0; i < NUM_LINKS; ++i) {
        big.add(lrs[i]);
      }
      Log.info(
          Log.FAC_TEST,
          "Finihed setting up NDNNetworkObjectTestRepo, prefix {0}",
          testHelper.getClassNamespace());
    } finally {
      myhandle.close();
      KeyManager.closeDefaultKeyManager();
    }
  }
  @Test
  public void testSaveAsGone() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testSaveAsGone");

    NDNHandle thandle = NDNHandle.open();
    NDNHandle rhandle = NDNHandle.open();
    NDNHandle shandle = NDNHandle.open();

    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testSaveAsGone"), collectionObjName);
      CollectionObject c0 = new CollectionObject(testName, empty, SaveType.REPOSITORY, thandle);
      setupNamespace(testName);
      NDNTime t0 = saveAsGoneAndLog("Gone", c0);
      Assert.assertTrue("Should be gone", c0.isGone());
      ContentName goneVersionName = c0.getVersionedName();

      NDNTime t1 = saveAndLog("NotGone", c0, null, small1);
      TestUtils.checkObject(thandle, c0);
      Assert.assertFalse("Should not be gone", c0.isGone());
      Assert.assertTrue(t1.after(t0));

      CollectionObject c1 = new CollectionObject(testName, rhandle);
      NDNTime t2 = waitForDataAndLog(testName.toString(), c1);
      Assert.assertFalse("Read back should not be gone", c1.isGone());
      Assert.assertEquals(t2, t1);

      NDNTime t3 = updateAndLog(goneVersionName.toString(), c1, goneVersionName);
      Assert.assertTrue(VersioningProfile.isVersionOf(c1.getVersionedName(), testName));
      Assert.assertEquals(t3, t0);
      Assert.assertTrue("Read back should be gone.", c1.isGone());

      t0 = saveAsGoneAndLog("GoneAgain", c0);
      Assert.assertTrue("Should be gone", c0.isGone());

      CollectionObject c2 = new CollectionObject(testName, shandle);
      NDNTime t4 = waitForDataAndLog(testName.toString(), c2);
      Assert.assertTrue(
          "Read back of " + c0.getVersionedName() + " should be gone, got " + c2.getVersionedName(),
          c2.isGone());
      Assert.assertEquals(t4, t0);
    } finally {
      thandle.close();
      rhandle.close();
      shandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testSaveAsGone");
  }
  @Test
  public void testUpdateOtherName() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testUpdateOtherName");

    NDNHandle thandle = NDNHandle.open();
    NDNHandle rhandle = NDNHandle.open();
    try {
      ContentName testName =
          new ContentName(
              testHelper.getTestNamespace("testUpdateOtherName"), collectionObjName, "name1");
      ContentName testName2 =
          new ContentName(
              testHelper.getTestNamespace("testUpdateOtherName"), collectionObjName, "name2");

      CollectionObject c0 = new CollectionObject(testName, empty, SaveType.REPOSITORY, rhandle);
      setupNamespace(testName);
      NDNTime t0 = saveAndLog("Empty", c0, null, empty);
      TestUtils.checkObject(rhandle, c0);

      CollectionObject c1 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, thandle);
      // Cheat a little, make this one before the setupNamespace...
      CollectionObject c2 = new CollectionObject(testName2, small1, SaveType.REPOSITORY, null);
      setupNamespace(testName2);
      NDNTime t1 = saveAndLog("Small", c1, null, small1);
      TestUtils.checkObject(thandle, c1);
      Assert.assertTrue("First version should come before second", t0.before(t1));

      NDNTime t2 = saveAndLog("Small2ndWrite", c2, null, small1);
      TestUtils.checkObject(c2.getHandle(), c2);
      Assert.assertTrue("Third version should come after second", t1.before(t2));
      Assert.assertTrue(c2.contentEquals(c1));
      Assert.assertFalse(c2.equals(c1));

      NDNTime t3 = updateAndLog(c0.getVersionedName().toString(), c0, testName2);
      Assert.assertTrue(VersioningProfile.isVersionOf(c0.getVersionedName(), testName2));
      Assert.assertEquals(t3, t2);
      Assert.assertTrue(c0.contentEquals(c2));

      t3 = updateAndLog(c0.getVersionedName().toString(), c0, c1.getVersionedName());
      Assert.assertTrue(VersioningProfile.isVersionOf(c0.getVersionedName(), testName2));
      Assert.assertEquals(t3, t1);
      Assert.assertTrue(c0.contentEquals(c1));
    } finally {
      thandle.close();
      rhandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testUpdateOtherName");
  }
 /**
  * Get the version of the stored node key name
  *
  * @return the version
  */
 public NDNTime nodeKeyVersion() {
   try {
     return VersioningProfile.getLastVersionAsTimestamp(storedNodeKeyName());
   } catch (VersionMissingException e) {
     if (Log.isLoggable(Log.FAC_ACCESSCONTROL, Level.WARNING)) {
       Log.warning(
           Log.FAC_ACCESSCONTROL,
           "Unexpected: name that was confirmed to have a version on construction throws a VersionMissingException: "
               + storedNodeKeyName());
     }
     throw new IllegalStateException(
         "Unexpected: name that was confirmed to have a version on construction throws a VersionMissingException: "
             + storedNodeKeyName());
   }
 }
  /**
   * Initialize internal policy state, from file if policyFile != null This method is intended to be
   * called at the beginning of a subclass initialize() method to handle the generic policy setup,
   * after which the subclass initialize() should adjust policy (including calling readPolicy) as
   * appropriate. If both "policy file" and "initial namespace" are non-null the policy file takes
   * precedence
   *
   * @param policyFile policy file
   * @param initial namespace
   * @throws RepositoryException
   * @throws FileNotFoundException
   * @throws ContentDecodingException
   * @throws MalformedContentNameStringException
   */
  public PolicyXML startInitPolicy(File policyFile, String nameSpace) throws RepositoryException {
    BasicPolicy policy = new BasicPolicy(null);
    policy.setVersion(getVersion());

    if (null != policyFile) {
      try {
        FileInputStream fis = new FileInputStream(policyFile);
        try {
          policy.updateFromInputStream(fis);
        } finally {
          try {
            fis.close();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      } catch (FileNotFoundException e) {
        throw new RepositoryException(e.getMessage());
      }
    } else if (null != nameSpace) { // Try setting an initial namespace from the namespace parameter
      ArrayList<ContentName> nameSpaceAL = new ArrayList<ContentName>(1);
      try {
        nameSpaceAL.add(ContentName.fromNative(nameSpace));
      } catch (MalformedContentNameStringException e) {
        Log.warning(Log.FAC_REPO, "Invalid namespace specified: {0}", nameSpace);
      }
      policy.setNamespace(nameSpaceAL);
    }
    return policy.getPolicyXML();
  }
 @AfterClass
 public static void tearDownAfterClass() throws Exception {
   Log.info(
       Log.FAC_TEST,
       "Tearing down NDNNetworkObjectTestRepo, prefix {0}",
       testHelper.getClassNamespace());
 }
  @Test
  public void testSaveToVersion() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testSaveToVersion");

    // Testing problem of disappearing versions, inability to get latest. Use simpler
    // object than a collection.
    NDNHandle lput = NDNHandle.open();
    NDNHandle lget = NDNHandle.open();
    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testSaveToVersion"), stringObjName);

      NDNTime desiredVersion = NDNTime.now();

      NDNStringObject so = new NDNStringObject(testName, "First value", SaveType.REPOSITORY, lput);
      setupNamespace(testName);
      saveAndLog("SpecifiedVersion", so, desiredVersion, "Time: " + desiredVersion);
      TestUtils.checkObject(lput, so);
      Assert.assertEquals("Didn't write correct version", desiredVersion, so.getVersion());

      NDNStringObject ro = new NDNStringObject(testName, lget);
      ro.waitForData();
      Assert.assertEquals("Didn't read correct version", desiredVersion, ro.getVersion());
      ContentName versionName = ro.getVersionedName();

      saveAndLog("UpdatedVersion", so, null, "ReplacementData");
      TestUtils.checkObject(lput, so);
      updateAndLog("UpdatedData", ro, null);
      Assert.assertTrue(
          "New version " + so.getVersion() + " should be later than old version " + desiredVersion,
          (desiredVersion.before(so.getVersion())));
      Assert.assertEquals("Didn't read correct version", so.getVersion(), ro.getVersion());

      NDNStringObject ro2 = new NDNStringObject(versionName, null);
      ro2.waitForData();
      Assert.assertEquals("Didn't read correct version", desiredVersion, ro2.getVersion());
    } finally {
      lput.close();
      lget.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testSaveToVersion");
  }
 /**
  * Computes the descendant node key for a specified descendant node using the key derivation
  * function.
  *
  * @param descendantNodeName the name of the descendant node
  * @param keyLabel the label of the key
  * @return the node key
  * @throws InvalidKeyException
  * @throws ContentEncodingException
  */
 public NodeKey computeDescendantNodeKey(ContentName descendantNodeName, String keyLabel)
     throws InvalidKeyException, ContentEncodingException {
   if (nodeName().equals(descendantNodeName)) {
     if (Log.isLoggable(Log.FAC_ACCESSCONTROL, Level.INFO)) {
       Log.info(
           Log.FAC_ACCESSCONTROL,
           "Asked to compute ourselves as our own descendant (node key "
               + nodeName()
               + "), returning this.");
     }
     return this;
   }
   if (!nodeName().isPrefixOf(descendantNodeName)) {
     throw new IllegalArgumentException(
         "Node " + descendantNodeName + " is not a child of this node " + nodeName());
   }
   byte[] derivedKey =
       KeyDerivationFunction.DeriveKeyForNode(
           nodeName(), nodeKey().getEncoded(), keyLabel, descendantNodeName);
   return new NodeKey(descendantNodeName, derivedKey, storedNodeKeyName(), storedNodeKeyID());
 }
  @Test
  public void testUpdateListener() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testUpdateListener");

    SaveType saveType = SaveType.REPOSITORY;
    NDNHandle writeHandle = NDNHandle.open();
    NDNHandle readHandle = NDNHandle.open();

    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testUpdateListener"), stringObjName);

      NDNStringObject writeObject =
          new NDNStringObject(testName, "Something to listen to.", saveType, writeHandle);
      writeObject.save();

      CounterListener ourListener = new CounterListener();
      NDNStringObject readObject = new NDNStringObject(testName, null, null, readHandle);
      readObject.addListener(ourListener);
      boolean result = readObject.update();

      Assert.assertTrue(result);
      Assert.assertTrue(ourListener.getCounter() == 1);

      readObject.updateInBackground();

      writeObject.save("New stuff! New stuff!");
      synchronized (readObject) {
        while (ourListener.getCounter() == 1) readObject.wait();
      }
      // For some reason, we're getting two updates on our updateInBackground...
      Assert.assertTrue(ourListener.getCounter() > 1);
    } finally {
      writeHandle.close();
      readHandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testUpdateListener");
  }
  /**
   * Read policy from persistent storage under standard naming convention. This method may be called
   * optionally during initialization by a subclass after it is initialized enough to process
   * getContent() calls
   *
   * @param globalPrefix - used to find our policy file
   * @return XML for the current policy or null if no current policy
   * @throws MalformedContentNameStringException
   * @throws IOException
   */
  public PolicyXML readPolicy(ContentName globalPrefix)
      throws MalformedContentNameStringException, IOException {
    if (Log.isLoggable(Log.FAC_REPO, Level.INFO))
      Log.info(
          Log.FAC_REPO,
          "REPO: reading policy from network: {0}/{1}/{2}",
          REPO_NAMESPACE,
          globalPrefix,
          REPO_POLICY);
    ContentName policyName = BasicPolicy.getPolicyName(globalPrefix);

    // We can't use the regular repo handle for this because we need ndnd to communicate across the
    // faces
    NDNHandle readHandle;
    readHandle = NDNHandle.open(_km);
    PolicyObject policyObject = new PolicyObject(policyName, readHandle);
    try {
      return policyObject.policyInfo();
    } catch (ContentNotReadyException cge) {
    } finally {
      readHandle.close();
    }
    return null;
  }
 /**
  * Gets current repository information to be used as content in a ContentObject
  *
  * @param names intended for nonimplemented repository ACK protocol - currently unused
  */
 public RepositoryInfoObject getRepoInfo(
     ContentName name, String info, ArrayList<ContentName> names) {
   try {
     RepositoryInfo rri = _info;
     if (names != null || info != null) {
       if (names != null)
         rri =
             new RepositoryInfo(
                 getVersion(), _info.getGlobalPrefix(), _info.getLocalName(), names);
       else
         rri =
             new RepositoryInfo(getVersion(), _info.getGlobalPrefix(), _info.getLocalName(), info);
     }
     RepositoryInfoObject rio = new RepositoryInfoObject(name, rri, SaveType.RAW, _handle);
     rio.setFreshnessSeconds(12); // Same time as repo will express interest
     return rio;
   } catch (Exception e) {
     Log.logStackTrace(Level.WARNING, e);
     e.printStackTrace();
   }
   return null;
 }
  @Test
  public void testLocalCopyListener() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testLocalCopyListener");

    NDNHandle rhandle = NDNHandle.open();
    try {
      // The local copy wrapper API isn't applicable in very many situations. Try this instead.
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testLocalCopyListener"), collectionObjName);

      LocalCopyListener copyListener = new LocalCopyListener();

      // Would like to test functionality that arranges for existing version to get written
      // to the repo, but can't do that without a flosser or using a flow server.
      NDNStringObject so = new NDNStringObject(testName, rhandle);
      so.addListener(copyListener);

      Assert.assertFalse(so.available());

      class Record {
        boolean callback = false;
      }
      Record record = new Record();

      class Listener implements UpdateListener {
        Record _rec;

        public Listener(Record r) {
          _rec = r;
        }

        public void newVersionAvailable(NDNNetworkObject<?> newVersion, boolean wasSave) {
          synchronized (_rec) {
            _rec.callback = true;
            _rec.notifyAll();
          }
        }
      };

      // ask for it in background
      so.updateInBackground(false, new Listener(record));

      NDNHandle thandle = NDNHandle.open();
      try {
        NDNStringObject sowrite =
            new NDNStringObject(testName, "Now we write", SaveType.RAW, thandle);
        setupNamespace(testName);
        saveAndLog("Delayed write", sowrite, null, "Now we write");
        TestUtils.checkObject(thandle, sowrite);
        so.waitForData();
        Assert.assertTrue(so.available());
        Assert.assertEquals(so.string(), sowrite.string());
        Assert.assertEquals(so.getVersionedName(), sowrite.getVersionedName());

        new Waiter(UPDATE_TIMEOUT) {
          @Override
          protected boolean check(Object o, Object check) throws Exception {
            return ((Record) o).callback;
          }
        }.wait(record, record);
        Assert.assertEquals(true, record.callback);
        if (!RepositoryControl.localRepoSync(rhandle, so)) {
          Thread.sleep(SystemConfiguration.MEDIUM_TIMEOUT);
          // Should be in the repo by now
          Assert.assertTrue(RepositoryControl.localRepoSync(rhandle, so));
        }
      } finally {
        thandle.close();
      }
    } finally {
      rhandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testLocalCopyListener");
  }
  @Test
  public void testLocalCopyWrapper() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testLocalCopyWrapper");

    NDNHandle thandle = NDNHandle.open();
    NDNHandle rhandle = NDNHandle.open();

    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testLocalCopyWrapper"), collectionObjName);
      NDNStringObject so = new NDNStringObject(testName, rhandle);
      LocalCopyWrapper wo = new LocalCopyWrapper(so);
      Assert.assertFalse(wo.available());
      class Record {
        boolean callback = false;
      }
      Record record = new Record();

      class Listener implements UpdateListener {
        Record _rec;

        public Listener(Record r) {
          _rec = r;
        }

        public void newVersionAvailable(NDNNetworkObject<?> newVersion, boolean wasSave) {
          synchronized (_rec) {
            _rec.callback = true;
            _rec.notifyAll();
          }
        }
      };

      // ask for it in background
      wo.updateInBackground(false, new Listener(record));

      NDNStringObject sowrite =
          new NDNStringObject(testName, "Now we write", SaveType.RAW, thandle);
      setupNamespace(testName);
      saveAndLog("Delayed write", sowrite, null, "Now we write");
      TestUtils.checkObject(thandle, sowrite);
      wo.waitForData();
      Assert.assertTrue(wo.available());
      Assert.assertEquals(((NDNStringObject) wo.object()).string(), sowrite.string());
      Assert.assertEquals(wo.getVersionedName(), sowrite.getVersionedName());

      new Waiter(UPDATE_TIMEOUT) {
        @Override
        protected boolean check(Object o, Object check) throws Exception {
          return ((Record) o).callback;
        }
      }.wait(record, record);
      Assert.assertEquals(true, record.callback);

      if (!RepositoryControl.localRepoSync(rhandle, so)) {
        Thread.sleep(SystemConfiguration.MEDIUM_TIMEOUT);
        // Should be in the repo by now
        Assert.assertTrue(RepositoryControl.localRepoSync(rhandle, so));
      }
    } finally {
      thandle.close();
      rhandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testLocalCopyWrapper");
  }
 public void shutDown() {
   Log.info(Log.FAC_REPO, "RespositoryStoreBase.shutdown()");
   if (null != _handle) _handle.close();
 }
  @Test
  public void testVersioning() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testVersioning");

    // Testing problem of disappearing versions, inability to get latest. Use simpler
    // object than a collection.
    NDNHandle lput = NDNHandle.open();
    NDNHandle lget = NDNHandle.open();

    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testVersioning"), stringObjName);

      NDNStringObject so = new NDNStringObject(testName, "First value", SaveType.REPOSITORY, lput);
      setupNamespace(testName);
      NDNStringObject ro = null;
      NDNStringObject ro2 = null;
      NDNStringObject ro3, ro4; // make each time, to get a new handle.
      NDNTime soTime, srTime, sr2Time, sr3Time, sr4Time, so2Time;
      for (int i = 0; i < numbers.length; ++i) {
        soTime = saveAndLog(numbers[i], so, null, numbers[i]);
        TestUtils.checkObject(lput, so);
        ro = new NDNStringObject(testName, lget);
        srTime = waitForDataAndLog(numbers[i], ro);
        ro2 = new NDNStringObject(testName, null);
        sr2Time = waitForDataAndLog(numbers[i], ro2);
        ro3 = new NDNStringObject(ro.getVersionedName(), null); // read specific version
        sr3Time = waitForDataAndLog("UpdateToROVersion", ro3);
        // Save a new version and pull old
        so2Time = saveAndLog(numbers[i] + "-Update", so, null, numbers[i] + "-Update");
        TestUtils.checkObject(lput, so);
        ro4 = new NDNStringObject(ro.getVersionedName(), null); // read specific version
        sr4Time = waitForDataAndLog("UpdateAnotherToROVersion", ro4);
        Log.info(
            Log.FAC_TEST,
            "Update "
                + i
                + ": Times: "
                + soTime
                + " "
                + srTime
                + " "
                + sr2Time
                + " "
                + sr3Time
                + " different: "
                + so2Time);
        Assert.assertEquals("SaveTime doesn't match first read", soTime, srTime);
        Assert.assertEquals("SaveTime doesn't match second read", soTime, sr2Time);
        Assert.assertEquals("SaveTime doesn't match specific version read", soTime, sr3Time);
        Assert.assertFalse("UpdateTime isn't newer than read time", soTime.equals(so2Time));
        Assert.assertEquals("SaveTime doesn't match specific version read", soTime, sr4Time);
      }
    } finally {
      lput.close();
      lget.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testVersioning");
  }
  @Test
  public void testUpdateInBackground() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testUpdateInBackground");

    NDNHandle thandle = NDNHandle.open();
    NDNHandle thandle2 = NDNHandle.open();
    NDNHandle thandle3 = NDNHandle.open();

    try {
      ContentName testName =
          new ContentName(
              testHelper.getTestNamespace("testUpdateInBackground"), stringObjName, "name1");
      // Make writer first, so it picks up readers first interests even before it writes.
      NDNStringObject c2 =
          new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle);

      NDNStringObject c0 =
          new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle2);
      c0.updateInBackground();

      NDNStringObject c1 =
          new NDNStringObject(testName, (String) null, SaveType.REPOSITORY, thandle3);
      c1.updateInBackground(true);

      Assert.assertFalse(c0.available());
      Assert.assertFalse(c0.isSaved());
      Assert.assertFalse(c1.available());
      Assert.assertFalse(c1.isSaved());

      NDNTime t1 = saveAndLog("First string", c2, null, "Here is the first string.");
      TestUtils.checkObject(thandle, c2);
      Log.info(
          Log.FAC_TEST,
          "Saved c2: "
              + c2.getVersionedName()
              + " c0 available? "
              + c0.available()
              + " c1 available? "
              + c1.available());
      c0.waitForData();
      Assert.assertEquals("c0 update", c0.getVersion(), c2.getVersion());
      c1.waitForData();
      Assert.assertEquals("c1 update", c1.getVersion(), c2.getVersion());

      NDNTime t2 = saveAndLog("Second string", c2, null, "Here is the second string.");
      TestUtils.checkObject(thandle, c2);
      doWait(c1, t2);
      Assert.assertEquals("c1 update 2", c1.getVersion(), c2.getVersion());
      Assert.assertEquals("c0 unchanged", c0.getVersion(), t1);

      // Sleep for a while and see how fast the interests come. Should be only 2 interests.
      Log.info(Log.FAC_TEST, "Sleeping, count background interests.");
      long time = System.currentTimeMillis();
      Thread.sleep(2000);
      long elapsed = System.currentTimeMillis() - time;
      long count = (elapsed / 4000) + 1;
      Log.info(
          Log.FAC_TEST,
          "Slept " + elapsed / 1000.0 + " seconds, should have been " + count + " interests.");

      NDNTime t3 = saveAndLog("Third string", c2, null, "Here is the third string.");
      TestUtils.checkObject(thandle, c2);
      doWait(c1, t3);
      Assert.assertEquals("c1 update 3", c1.getVersion(), c2.getVersion());
      Assert.assertEquals("c0 unchanged", c0.getVersion(), t1);

      c1.cancelInterest();
    } finally {
      thandle.close();
      thandle2.close();
      thandle3.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testUpdateInBackground");
  }
  @Test
  public void testStreamUpdate() throws Exception {
    Log.info(Log.FAC_TEST, "Starting testStreamUpdate");

    NDNHandle tHandle = NDNHandle.open();
    try {
      ContentName testName =
          new ContentName(testHelper.getTestNamespace("testStreamUpdate"), collectionObjName);
      CollectionObject testCollectionObject =
          new CollectionObject(testName, small1, SaveType.REPOSITORY, tHandle);
      setupNamespace(testName);

      saveAndLog("testStreamUpdate", testCollectionObject, null, small1);
      TestUtils.checkObject(tHandle, testCollectionObject);
      Log.info(
          Log.FAC_TEST, "testCollectionObject name: " + testCollectionObject.getVersionedName());

      NDNVersionedInputStream vis =
          new NDNVersionedInputStream(testCollectionObject.getVersionedName());
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] buf = new byte[128];
      // Will incur a timeout
      while (!vis.eof()) {
        int read = vis.read(buf);
        if (read > 0) baos.write(buf, 0, read);
      }
      Log.info(
          Log.FAC_TEST,
          "Read "
              + baos.toByteArray().length
              + " bytes, digest: "
              + DigestHelper.printBytes(DigestHelper.digest(baos.toByteArray()), 16));

      Collection decodedData = new Collection();
      decodedData.decode(baos.toByteArray());
      Log.info(Log.FAC_TEST, "Decoded collection data: " + decodedData);
      Assert.assertEquals(
          "Decoding via stream fails to give expected result!", decodedData, small1);

      NDNVersionedInputStream vis2 =
          new NDNVersionedInputStream(testCollectionObject.getVersionedName());
      ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
      // Will incur a timeout
      while (!vis2.eof()) {
        int val = vis2.read();
        if (val < 0) break;
        baos2.write((byte) val);
      }
      Log.info(
          Log.FAC_TEST,
          "Read "
              + baos2.toByteArray().length
              + " bytes, digest: "
              + DigestHelper.printBytes(DigestHelper.digest(baos2.toByteArray()), 16));
      Assert.assertArrayEquals(
          "Reading same object twice gets different results!",
          baos.toByteArray(),
          baos2.toByteArray());

      Collection decodedData2 = new Collection();
      decodedData2.decode(baos2.toByteArray());
      Assert.assertEquals(
          "Decoding via stream byte read fails to give expected result!", decodedData2, small1);

      NDNVersionedInputStream vis3 =
          new NDNVersionedInputStream(testCollectionObject.getVersionedName());
      Collection decodedData3 = new Collection();
      decodedData3.decode(vis3);
      Assert.assertEquals(
          "Decoding via stream full read fails to give expected result!", decodedData3, small1);
    } finally {
      tHandle.close();
      KeyManager.closeDefaultKeyManager();
    }

    Log.info(Log.FAC_TEST, "Completed testStreamUpdate");
  }