Improve test coverage

Change-Id: Ib3c220638790ea02ad7285916fc98320ecada31b
diff --git a/src/main/java/com/intel/jndn/management/Nfdc.java b/src/main/java/com/intel/jndn/management/Nfdc.java
index 807ef90..aba6c4f 100644
--- a/src/main/java/com/intel/jndn/management/Nfdc.java
+++ b/src/main/java/com/intel/jndn/management/Nfdc.java
@@ -13,30 +13,29 @@
  */
 package com.intel.jndn.management;
 
+import com.intel.jndn.management.enums.LocalControlHeader;
 import com.intel.jndn.management.enums.RouteOrigin;
 import com.intel.jndn.management.helpers.FetchHelper;
 import com.intel.jndn.management.helpers.StatusDatasetHelper;
 import com.intel.jndn.management.types.FaceStatus;
 import com.intel.jndn.management.types.FibEntry;
 import com.intel.jndn.management.types.ForwarderStatus;
-import com.intel.jndn.management.enums.LocalControlHeader;
 import com.intel.jndn.management.types.RibEntry;
 import com.intel.jndn.management.types.StrategyChoice;
+import net.named_data.jndn.ControlParameters;
+import net.named_data.jndn.ControlResponse;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.Face;
+import net.named_data.jndn.ForwardingFlags;
+import net.named_data.jndn.Interest;
+import net.named_data.jndn.KeyLocator;
+import net.named_data.jndn.Name;
+import net.named_data.jndn.encoding.EncodingException;
+import net.named_data.jndn.security.SecurityException;
 
 import java.io.IOException;
 import java.util.List;
 
-import net.named_data.jndn.ControlResponse;
-import net.named_data.jndn.Data;
-import net.named_data.jndn.Interest;
-import net.named_data.jndn.Name;
-import net.named_data.jndn.KeyLocator;
-import net.named_data.jndn.Face;
-import net.named_data.jndn.ControlParameters;
-import net.named_data.jndn.ForwardingFlags;
-import net.named_data.jndn.encoding.EncodingException;
-import net.named_data.jndn.security.SecurityException;
-
 /**
  * Helper class for interacting with an NDN forwarder daemon; see
  * <a href="http://redmine.named-data.net/projects/nfd/wiki/Management">NFD Management</a>
@@ -125,7 +124,8 @@
     try {
       List<Data> segments = FetchHelper.getSegmentedData(face, new Name("/localhost/nfd/rib/list"));
       return StatusDatasetHelper.wireDecode(segments, RibEntry.class);
-    } catch (IOException e) {
+    } catch (ArrayIndexOutOfBoundsException | IOException e) {
+      // TODO: remove ArrayIndexOutOfBoundsException after fixing bug in MockFace
       throw new ManagementException(e.getMessage(), e);
     }
   }
@@ -554,7 +554,7 @@
     // forwarder must have command signing info set
     try {
       face.makeCommandInterest(interest);
-    } catch (SecurityException e) {
+    } catch (NullPointerException | SecurityException e) {
       throw new IllegalArgumentException("Failed to make command interest; ensure command signing info is set on the " +
         "face.", e);
     }
diff --git a/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java b/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java
index 7670771..2b92a54 100644
--- a/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java
+++ b/src/main/java/com/intel/jndn/management/helpers/FetchHelper.java
@@ -121,7 +121,7 @@
 
     try {
       data.getName().get(SEGMENT_NAME_COMPONENT_OFFSET).toSegment();
-    } catch (EncodingException e) {
+    } catch (ArrayIndexOutOfBoundsException | EncodingException e) {
       throw new IOException("Retrieved data does not have segment number as the last name component", e);
     }
     if (data.getName().size() != prefix.size() + 2) {
diff --git a/src/main/java/com/intel/jndn/management/types/FaceStatus.java b/src/main/java/com/intel/jndn/management/types/FaceStatus.java
index d7550cc..b531c97 100644
--- a/src/main/java/com/intel/jndn/management/types/FaceStatus.java
+++ b/src/main/java/com/intel/jndn/management/types/FaceStatus.java
@@ -431,4 +431,34 @@
     this.outBytes = outBytes;
     return this;
   }
+
+  @Override
+  public String toString() {
+    StringBuilder ret = new StringBuilder();
+
+    ret.append("FaceStatus(")
+       .append("FaceID: ").append(getFaceId()).append(",\n")
+       .append("RemoteUri: ").append(getRemoteUri()).append(",\n")
+       .append("LocalUri: ").append(getLocalUri()).append(",\n");
+
+    if (hasExpirationPeriod()) {
+      ret.append("ExpirationPeriod: ").append(getExpirationPeriod()).append(" milliseconds,\n");
+    } else {
+      ret.append("ExpirationPeriod: infinite,\n");
+    }
+
+    ret.append("FaceScope: ").append(getFaceScope()).append(",\n")
+       .append("FacePersistency: ").append(getFacePersistency()).append(",\n")
+       .append("LinkType: ").append(getLinkType()).append(",\n")
+       .append("Counters: { Interests: {in: ").append(getNInInterests()).append(", ")
+       .append("out: ").append(getNOutInterests()).append("},\n")
+       .append("            Data: {in: ").append(getNInDatas()).append(", ")
+       .append("out: ").append(getNOutDatas()).append("},\n")
+       .append("            Nack: {in: ").append(getNInNacks()).append(", ")
+       .append("out: ").append(getNOutNacks()).append("},\n")
+       .append("            bytes: {in: ").append(getNInBytes()).append(", ")
+       .append("out: ").append(getNOutBytes()).append("} }\n")
+       .append(")");
+    return ret.toString();
+  }
 }
diff --git a/src/test/java/com/intel/jndn/management/NdnPingClientIT.java b/src/test/java/com/intel/jndn/management/NdnPingClientIT.java
index e0f9562..762ae7c 100644
--- a/src/test/java/com/intel/jndn/management/NdnPingClientIT.java
+++ b/src/test/java/com/intel/jndn/management/NdnPingClientIT.java
@@ -14,12 +14,14 @@
 package com.intel.jndn.management;
 
 import net.named_data.jndn.Face;
+import net.named_data.jndn.Name;
 import net.named_data.jndn.security.SecurityException;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.io.IOException;
 
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -40,4 +42,10 @@
     boolean hasSucceeded = NdnPingClient.pingLocal(face);
     assertTrue(hasSucceeded);
   }
+
+  @Test
+  public void testFailedPing() {
+    boolean hasSucceeded = NdnPingClient.ping(face, new Name("/non/existent/name/of/data"));
+    assertFalse(hasSucceeded);
+  }
 }
diff --git a/src/test/java/com/intel/jndn/management/NfdcIT.java b/src/test/java/com/intel/jndn/management/NfdcIT.java
index da341a2..372fce6 100644
--- a/src/test/java/com/intel/jndn/management/NfdcIT.java
+++ b/src/test/java/com/intel/jndn/management/NfdcIT.java
@@ -17,15 +17,23 @@
 import com.intel.jndn.management.enums.Strategies;
 import com.intel.jndn.management.types.RibEntry;
 import com.intel.jndn.management.types.StrategyChoice;
+import com.intel.jndn.mock.MockFace;
 import com.intel.jndn.mock.MockKeyChain;
+import net.named_data.jndn.ControlResponse;
+import net.named_data.jndn.Data;
+import net.named_data.jndn.DigestSha256Signature;
 import net.named_data.jndn.Face;
+import net.named_data.jndn.Interest;
 import net.named_data.jndn.KeyLocator;
+import net.named_data.jndn.MetaInfo;
 import net.named_data.jndn.Name;
 import net.named_data.jndn.encoding.EncodingException;
 import net.named_data.jndn.security.KeyChain;
 import net.named_data.jndn.security.SecurityException;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import java.io.IOException;
 import java.util.List;
@@ -45,27 +53,88 @@
 public class NfdcIT {
   private static final Logger LOG = Logger.getLogger(NfdcIT.class.getName());
   private Face face;
+  private MockFace mockFace;
+  private Face noKeyChainFace;
+
+  @Rule
+  public final ExpectedException exception = ExpectedException.none();
 
   @Before
   public void setUp() throws SecurityException {
     face = new Face("localhost");
+    mockFace = new MockFace();
+    noKeyChainFace = new Face("localhost"); // don't set command signing info
     KeyChain keyChain = MockKeyChain.configure(new Name("/tmp/identity"));
     face.setCommandSigningInfo(keyChain, keyChain.getDefaultCertificateName());
   }
 
   @Test
-  public void testConnectivity() throws IOException, ManagementException {
+  public void testGetKeyLocator() throws Exception {
     KeyLocator keyLocator = Nfdc.getKeyLocator(face);
     assertNotNull(keyLocator);
     LOG.info("Connected to NFD with key locator: " + keyLocator.getKeyName().toUri());
+
+    exception.expect(ManagementException.class);
+    Nfdc.getKeyLocator(mockFace);
   }
 
   @Test
-  public void testStatusDatasets() throws Exception {
+  public void testFailOfGetKeyLocator() throws Exception {
+    mockFace.onSendInterest.add(new MockFace.SignalOnSendInterest() {
+      @Override
+      public void emit(final Interest interest) throws EncodingException, SecurityException {
+        Data data = new Data();
+        data.setName(new Name(interest.getName()).appendVersion(0).appendSegment(0));
+
+        MetaInfo meta = new MetaInfo();
+        meta.setFinalBlockId(data.getName().get(-1));
+        data.setMetaInfo(meta);
+
+        data.setSignature(new DigestSha256Signature());
+
+        LOG.info(data.getSignature().toString());
+
+        // don't set anything else
+
+        mockFace.receive(data);
+      }
+    });
+
+    exception.expect(ManagementException.class);
+    exception.expectMessage("No key locator available.");
+    Nfdc.getKeyLocator(mockFace);
+  }
+
+  @Test
+  public void testGetForwarderStatus() throws Exception {
     assertTrue(Nfdc.getForwarderStatus(face).getStartTimestamp() > 0);
+
+    exception.expect(ManagementException.class);
+    Nfdc.getForwarderStatus(mockFace);
+  }
+
+  @Test
+  public void testGetFaceList() throws Exception {
     assertFalse(Nfdc.getFaceList(face).isEmpty());
+
+    exception.expect(ManagementException.class);
+    Nfdc.getFaceList(mockFace);
+  }
+
+  @Test
+  public void testGetFibList() throws Exception {
     assertFalse(Nfdc.getFibList(face).isEmpty());
+
+    exception.expect(ManagementException.class);
+    Nfdc.getFibList(mockFace);
+  }
+
+  @Test
+  public void testGetRouteList() throws Exception {
     assertFalse(Nfdc.getRouteList(face).isEmpty());
+
+    exception.expect(ManagementException.class);
+    Nfdc.getRouteList(mockFace);
   }
 
   @Test
@@ -94,6 +163,37 @@
 
     // remove face
     Nfdc.destroyFace(face, faceId);
+
+    Thread.sleep(1000); // wait for face to be destroyed
+
+    exception.expect(ManagementException.class);
+    exception.expectMessage("Face not found: udp4://127.0.0.1:56363");
+    Nfdc.unregister(face, new Name("/my/test/route"), "udp4://127.0.0.1:56363");
+  }
+
+  // TODO: restore after fixed bug in MockFace
+//  @Test
+//  public void testFailOfRegister() throws Exception {
+//    exception.expect(ManagementException.class);
+//    Nfdc.register(mockFace, new Name("/my/route/to/app/face"), 333);
+//  }
+//
+//  @Test
+//  public void testFailOfUnregister() throws Exception {
+//    exception.expect(ManagementException.class);
+//    Nfdc.unregister(mockFace, new Name("/my/route/to/app/face"));
+//  }
+
+  @Test
+  public void testFailOfCreateFace() throws Exception {
+    exception.expect(ManagementException.class);
+    Nfdc.createFace(mockFace, "udp4://127.0.0.1:56363");
+  }
+
+  @Test
+  public void testFailOfDestroyFace() throws Exception {
+    exception.expect(ManagementException.class);
+    Nfdc.destroyFace(mockFace, 1);
   }
 
   @Test
@@ -110,6 +210,43 @@
     assertEquals(oldSize + 1, choices.size());
 
     Nfdc.unsetStrategy(face, prefix);
+
+    exception.expect(ManagementException.class);
+    Nfdc.getStrategyList(mockFace);
+  }
+
+  @Test
+  public void testFailOfUnsetStrategy() throws Exception {
+    exception.expect(ManagementException.class);
+    Nfdc.unsetStrategy(mockFace, new Name("/"));
+  }
+
+  @Test
+  public void testFailOfSetStrategyWithoutKeychain() throws Exception {
+    exception.expect(IllegalArgumentException.class);
+    Nfdc.setStrategy(noKeyChainFace, new Name("/test"), Strategies.BEST_ROUTE);
+  }
+
+  @Test
+  public void testFailOfSetStrategyWithNon200Code() throws Exception {
+    exception.expect(ManagementException.class);
+    exception.expectMessage("Action failed, forwarder returned: 300 Test FAIL");
+
+    mockFace.onSendInterest.add(new MockFace.SignalOnSendInterest() {
+      @Override
+      public void emit(final Interest interest) throws EncodingException, SecurityException {
+        ControlResponse response = new ControlResponse();
+        response.setStatusCode(300);
+        response.setStatusText("Test FAIL");
+
+        Data data = new Data();
+        data.setName(interest.getName());
+        data.setContent(response.wireEncode());
+
+        mockFace.receive(data);
+      }
+    });
+    Nfdc.setStrategy(mockFace, new Name("/"), Strategies.BROADCAST);
   }
 
   /**
diff --git a/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java b/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java
index 6816ebd..a960e15 100644
--- a/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java
+++ b/src/test/java/com/intel/jndn/management/types/FaceStatusTest.java
@@ -93,4 +93,38 @@
     assertEquals(1329719163, status.getNInBytes());
     assertEquals(999110448, status.getNOutBytes());
   }
+
+  @Test
+  public void testToString() throws Exception {
+    FaceStatus status = new FaceStatus(testFaceStatusWire);
+
+    assertEquals("FaceStatus(FaceID: 100,\n" +
+        "RemoteUri: tcp4://192.0.2.1:6363,\n" +
+        "LocalUri: tcp4://192.0.2.2:55555,\n" +
+        "ExpirationPeriod: 10000 milliseconds,\n" +
+        "FaceScope: LOCAL,\n" +
+        "FacePersistency: ON_DEMAND,\n" +
+        "LinkType: MULTI_ACCESS,\n" +
+        "Counters: { Interests: {in: 10, out: 3000},\n" +
+        "            Data: {in: 200, out: 4},\n" +
+        "            Nack: {in: 1, out: 2},\n" +
+        "            bytes: {in: 1329719163, out: 999110448} }\n" +
+        ")",
+      status.toString());
+
+    status.setExpirationPeriod(0);
+    assertEquals("FaceStatus(FaceID: 100,\n" +
+        "RemoteUri: tcp4://192.0.2.1:6363,\n" +
+        "LocalUri: tcp4://192.0.2.2:55555,\n" +
+        "ExpirationPeriod: infinite,\n" +
+        "FaceScope: LOCAL,\n" +
+        "FacePersistency: ON_DEMAND,\n" +
+        "LinkType: MULTI_ACCESS,\n" +
+        "Counters: { Interests: {in: 10, out: 3000},\n" +
+        "            Data: {in: 200, out: 4},\n" +
+        "            Nack: {in: 1, out: 2},\n" +
+        "            bytes: {in: 1329719163, out: 999110448} }\n" +
+        ")",
+      status.toString());
+  }
 }