Merge pull request #397 from n-shiota/release

Modified LinkStorageImplTest to use EasyMock instead of mocking GraphDBOperation class
diff --git a/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java b/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
index 57e77b2..5c42452 100644
--- a/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
+++ b/src/test/java/net/onrc/onos/ofcontroller/core/internal/LinkStorageImplTest.java
@@ -5,17 +5,21 @@
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 
 import net.floodlightcontroller.routing.Link;
 import net.onrc.onos.graph.GraphDBConnection;
 import net.onrc.onos.graph.GraphDBOperation;
 import net.onrc.onos.ofcontroller.core.ILinkStorage;
 import net.onrc.onos.ofcontroller.core.INetMapStorage.DM_OPERATION;
-import net.onrc.onos.ofcontroller.core.internal.TestableGraphDBOperation.TestPortObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.IPortObject;
+import net.onrc.onos.ofcontroller.core.INetMapTopologyObjects.ISwitchObject;
 import net.onrc.onos.ofcontroller.linkdiscovery.LinkInfo;
 
-import org.easymock.EasyMock;
+import org.easymock.*;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -40,7 +44,54 @@
 	private static GraphDBConnection conn;
 	
 	// Mock GraphDBOperation (mocks port-related methods only)
-	private static TestableGraphDBOperation ope;
+	private static GraphDBOperation ope;
+	
+	// Uncommitted actions executed in LinkStorageImpl
+	private static ArrayList<LinkEvent> actions;
+	
+	// Dictionary of mock IPortObject to information of port
+	// -> Used to refer DPID from IPortObject
+	private static Map<IPortObject,PortInfo> mockToPortInfoMap;
+	
+	
+	// Links existing in virtual graph
+	private List<Link> links;
+	
+	//================ Utility classes for logging actions in LinkStorageImpl ===========
+	private enum LinkEventType {
+		ADD, DELETE
+	}
+	
+	private class LinkEvent {
+		private Long src_dpid = null;
+		private Long dst_dpid = null;
+		private Short src_port = null;
+		private Short dst_port = null;
+		
+		public LinkEventType type;
+		
+		public LinkEvent(Link link, LinkEventType type) {
+			this.src_dpid = link.getSrc();
+			this.src_port = link.getSrcPort();
+			this.dst_dpid = link.getDst();
+			this.dst_port = link.getDstPort();
+			
+			this.type = type;
+		}
+
+		public Long getSrcDpid() { return src_dpid; }
+		public Short getSrcPort() { return src_port; }
+		public Long getDstDpid() { return dst_dpid; }
+		public Short getDstPort() { return dst_port; }
+		public LinkEventType getType() { return type; }
+	}
+	
+	private class PortInfo {
+		public Long dpid = null;
+		public Short port = null;
+		
+		public PortInfo(Long dpid, Short port) { this.dpid = dpid; this.port = port; }
+	}
 
 	/**
 	 * Setup code called before each tests.
@@ -55,10 +106,13 @@
 		EasyMock.expect(GraphDBConnection.getInstance((String)EasyMock.anyObject())).andReturn(conn).anyTimes();
 		PowerMock.replay(GraphDBConnection.class);
 		
-		ope = new TestableGraphDBOperation();
-		PowerMock.expectNew(GraphDBOperation.class, (GraphDBConnection)EasyMock.anyObject()).andReturn(ope).anyTimes();
+		ope = createMockGraphDBOperation();
+		PowerMock.expectNew(GraphDBOperation.class, new Class<?>[] {GraphDBConnection.class}, EasyMock.anyObject(GraphDBConnection.class)).andReturn(ope).anyTimes();
 		PowerMock.replay(GraphDBOperation.class);
-
+		
+		actions = new ArrayList<LinkEvent>();
+		mockToPortInfoMap = new HashMap<IPortObject,PortInfo>();
+		
 		linkStorage = new LinkStorageImpl();
 		linkStorage.init("/dummy/path/to/conf");
 		
@@ -74,7 +128,6 @@
 	public void tearDown() throws Exception {
 		// finish code
 		linkStorage.close();
-		ope.close();
 	}
 	
 	// TODO: remove @Ignore after UPDATE method is implemented
@@ -104,6 +157,14 @@
 		//Use the link storage API to add the link
 		linkStorage.update(linkToCreate, ILinkStorage.DM_OPERATION.CREATE);
 		doTestLinkExist(linkToVerify);
+
+		// Avoiding duplication is out of scope. DBOperation is responsible for this.
+//		// Add same link
+//		Link linkToCreateTwice = createFeasibleLink();
+//		linkStorage.update(linkToCreateTwice, ILinkStorage.DM_OPERATION.CREATE);
+//		
+//		// this occurs assertion failure if there are two links in titanGraph
+//		doTestLinkIsInGraph(linkToVerify);
 	}
 
 	/**
@@ -145,6 +206,14 @@
 		for(Link l : linksToVerify) {
 			doTestLinkExist(l);
 		}
+	
+		// Out of scope: DBOperation is responsible for avoiding duplication.
+//		// Test creation of existing links
+//		linksToCreate = createFeasibleLinks();
+//		linkStorage.update(linksToCreate, ILinkStorage.DM_OPERATION.CREATE);
+//		for(Link l : linksToVerify) {
+//			doTestLinkIsInGraph(l);
+//		}
 	}
 	
 	/**
@@ -298,7 +367,7 @@
 		
 		List<Link> list = linkStorage.getLinks(dpid, port);
 		
-		assertEquals(list.size(), 1);
+		assertEquals(1, list.size());
 		
 		Link l = list.get(0);
 		assertEquals(l.getSrc(), linkToVerify.getSrc());
@@ -310,7 +379,7 @@
 		
 		List<Link> list2 = linkStorage.getLinks(linkToVerifyNot.getSrc(), (short)linkToVerifyNot.getSrcPort());
 		
-		assertEquals(list2.size(), 0);
+		assertEquals(0, list2.size());
 	}
 	
 	/**
@@ -398,25 +467,26 @@
 	}
 
 	/**
-	 * Test if specific link is existing
+	 * Test if specific link exists
 	 * @param link 
 	 */
 	private void doTestLinkExist(Link link) {
-		assertTrue(ope.hasLinkBetween(HexString.toHexString(link.getSrc()),
-				link.getSrcPort(),
-				HexString.toHexString(link.getDst()),
-				link.getDstPort()));
+		int count = 0;
+		for(Link lt : links) {
+			if(lt.equals(link)) {
+				++count;
+			}
+		}
+		
+		assertTrue(count == 1);
 	}
 	
 	/**
-	 * Test if titanGraph doesn't have specific link
+	 * Test if specific link doesn't exist
 	 * @param link
 	 */
 	private void doTestLinkNotExist(Link link) {
-		assertFalse(ope.hasLinkBetween(HexString.toHexString(link.getSrc()),
-				link.getSrcPort(),
-				HexString.toHexString(link.getDst()),
-				link.getDstPort()));
+		assertFalse(links.contains(link));
 	}
 	
 	/**
@@ -426,6 +496,252 @@
 	private void doTestLinkHasStateOf(Link link, LinkInfo info) {
 	}
 	
+	/**
+	 * Class defines a function called back when IPortObject#removeLink is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class RemoveLinkCallback implements IAnswer<Object> {
+		private long dpid;
+		private short port;
+		public RemoveLinkCallback(long dpid, short port) {
+			this.dpid = dpid; this.port = port;
+		}
+		
+		@Override
+		public Object answer() throws Throwable {
+			IPortObject dstPort = (IPortObject) EasyMock.getCurrentArguments()[0];
+			PortInfo dst = mockToPortInfoMap.get(dstPort);
+
+			Link linkToRemove = new Link(this.dpid,this.port,dst.dpid,dst.port);
+			actions.add(new LinkEvent(linkToRemove,LinkEventType.DELETE));
+			
+			return null;
+		}
+	}
+	
+	/**
+	 * Class defines a function called back when IPortObject#setLinkPort is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class SetLinkPortCallback implements IAnswer<Object> {
+		private long dpid;
+		private short port;
+		public SetLinkPortCallback(long dpid, short port) {
+			this.dpid = dpid; this.port = port;
+		}
+
+		@Override
+		public Object answer() throws Throwable {
+			IPortObject dstPort = (IPortObject) EasyMock.getCurrentArguments()[0];
+			PortInfo dst = mockToPortInfoMap.get(dstPort);
+
+			Link linkToAdd = new Link(this.dpid,this.port,dst.dpid,dst.port);
+			actions.add(new LinkEvent(linkToAdd,LinkEventType.ADD));
+
+			return null;
+		}
+		
+	}
+	
+	/**
+	 * Class defines a function called back when IPortObject#getSwitch is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class GetSwitchCallback implements IAnswer<ISwitchObject> {
+		private long dpid;
+		
+		public GetSwitchCallback(long dpid) {
+			this.dpid = dpid;
+		}
+
+		@Override
+		public ISwitchObject answer() throws Throwable {
+			ISwitchObject sw = createMockSwitch(dpid);
+			return sw;
+		}
+	}
+	
+	/**
+	 * Class defines a function called back when IPortObject#getLinkedPorts is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class GetLinkedPortsCallback implements IAnswer< Iterable<IPortObject> > {
+		private long dpid;
+		private short port;
+		
+		public GetLinkedPortsCallback(long dpid, short port) {
+			this.dpid = dpid;
+			this.port = port;
+		}
+
+		@Override
+		public Iterable<IPortObject> answer() throws Throwable {
+			List<IPortObject> ports = new ArrayList<IPortObject>();
+
+			for(Link lk : links) {
+				if(lk.getSrc() == dpid && lk.getSrcPort() == port) {
+					ports.add(createMockPort(lk.getDst(), lk.getDstPort()));
+				}
+			}
+
+			return ports;
+		}
+		
+	}
+
+	/**
+	 * Class defines a function called back when ISwitchObject#getPorts is called.
+	 * @author Naoki Shiota
+	 *
+	 */
+	private class GetPortsCallback implements IAnswer< Iterable <IPortObject> > {
+		private long dpid;
+		
+		public GetPortsCallback(long dpid) {
+			this.dpid = dpid;
+		}
+		
+		@Override
+		public Iterable<IPortObject> answer() throws Throwable {
+			List<IPortObject> ports = new ArrayList<IPortObject>();
+			
+			for(Short number : getPorts(dpid)) {
+				ports.add(createMockPort(dpid, number));
+			}
+
+			return ports;
+		}
+	}
+
+	// ------------------------Creation of Mock-----------------------------
+	/**
+	 * Create a mock GraphDBOperation which hooks port-related methods.
+	 * @return EasyMock-wrapped GraphDBOperation object.
+	 */
+	@SuppressWarnings("serial")
+	private GraphDBOperation createMockGraphDBOperation() {
+		GraphDBOperation mockDBOpe = EasyMock.createNiceMock(GraphDBOperation.class);
+		
+		// Mock searchPort() method to create new mock IPortObject.
+		EasyMock.expect(mockDBOpe.searchPort((String)EasyMock.anyObject(), EasyMock.anyShort())).
+			andAnswer(new IAnswer<IPortObject>() {
+			@Override
+			public IPortObject answer() throws Throwable {
+				long dpid = HexString.toLong((String)EasyMock.getCurrentArguments()[0]);
+				short port = (Short) EasyMock.getCurrentArguments()[1];
+				IPortObject ret = createMockPort(dpid,port);
+				
+				return ret;
+			}
+		}).anyTimes();
+		
+		// Mock searchSwitch() method to create new mock ISwitchObject.
+		EasyMock.expect(mockDBOpe.searchSwitch((String)EasyMock.anyObject())).
+			andAnswer(new IAnswer<ISwitchObject>() {
+			@Override
+			public ISwitchObject answer() throws Throwable {
+				long dpid = HexString.toLong((String)EasyMock.getCurrentArguments()[0]);
+				ISwitchObject ret = createMockSwitch(dpid);
+				
+				return ret;
+			}
+		}).anyTimes();
+		
+		// Mock getActiveSwitches() method to create list of mock ISwitchObject.
+		EasyMock.expect(mockDBOpe.getActiveSwitches()).andReturn(new ArrayList<ISwitchObject> () {{
+			for(Long dpid : getDpids()) {
+				add(createMockSwitch(dpid));
+			}
+		}}).anyTimes();
+
+		// Mock commit() method to commit change of link information
+		mockDBOpe.commit();
+		EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+			@Override
+			public Object answer() throws Throwable {
+				for(LinkEvent action : actions) {
+					if(action.getType().equals(LinkEventType.ADD)) {
+						Link linkToAdd = new Link(
+								action.getSrcDpid(),
+								action.getSrcPort(),
+								action.getDstDpid(),
+								action.getDstPort());
+						links.add(linkToAdd);
+					} else if(action.getType().equals(LinkEventType.DELETE)) {
+						Link linkToRemove = new Link(
+								action.getSrcDpid(),
+								action.getSrcPort(),
+								action.getDstDpid(),
+								action.getDstPort());
+						links.remove(linkToRemove);
+					} else {
+						log.error("mock commit(): unexpected action {}", new Object[]{action.getType()});
+					}
+				}
+				actions.clear();
+				return null;
+			}
+		}).atLeastOnce();
+		
+		EasyMock.replay(mockDBOpe);
+		return mockDBOpe;
+	}
+	
+	/**
+	 * Create a mock IPortObject using given DPID and port number.
+	 * IPortObject can't store DPID, so DPID is stored to mockToPortInfoMap for later use.
+	 * Duplication is not checked.
+	 * @param dpid DPID of a port
+	 * @param number Port Number
+	 * @return EasyMock-wrapped IPortObject
+	 */
+	private IPortObject createMockPort(long dpid, short number) {
+		IPortObject mockPort = EasyMock.createNiceMock(IPortObject.class);
+		
+		EasyMock.expect(mockPort.getNumber()).andReturn(number);
+		
+		// Mock removeLink() method
+		mockPort.removeLink((IPortObject) EasyMock.anyObject());
+		EasyMock.expectLastCall().andAnswer(new RemoveLinkCallback(dpid, number)).anyTimes();
+		
+		// Mock setLinkPort() method
+		mockPort.setLinkPort((IPortObject) EasyMock.anyObject());
+		EasyMock.expectLastCall().andAnswer(new SetLinkPortCallback(dpid, number)).anyTimes();
+		
+		// Mock getLinkPorts() method
+		EasyMock.expect(mockPort.getLinkedPorts()).andAnswer(new GetLinkedPortsCallback(dpid, number)).anyTimes();
+		
+		// Mock getSwitch() method
+		EasyMock.expect(mockPort.getSwitch()).andAnswer(new GetSwitchCallback(dpid)).anyTimes();
+		
+		mockToPortInfoMap.put(mockPort, new PortInfo(dpid,number));
+		EasyMock.replay(mockPort);
+		
+		return mockPort;
+	}
+	
+	/**
+	 * Create a mock ISwitchObject using given DPID number.
+	 * Duplication is not checked.
+	 * @param dpid DPID of a switch
+	 * @return EasyMock-wrapped ISwitchObject
+	 */
+	private ISwitchObject createMockSwitch(long dpid) {
+		ISwitchObject mockSw = EasyMock.createNiceMock(ISwitchObject.class);
+		
+		EasyMock.expect(mockSw.getPorts()).andAnswer(new GetPortsCallback(dpid)).anyTimes();
+		EasyMock.expect(mockSw.getDPID()).andReturn(HexString.toHexString(dpid)).anyTimes();
+		EasyMock.expect(mockSw.getState()).andReturn("ACTIVE").anyTimes();
+		
+		EasyMock.replay(mockSw);
+		return mockSw;
+	}
+
+
 	//----------------- Creation of test data -----------------------
 	// Assume a network shown below.
 	//
@@ -441,38 +757,61 @@
 	// dpid2 : 00:00:00:00:0a:02
 	// dpid3 : 00:00:00:00:0a:03
 	
+	/**
+	 * Initialize links member to represent test topology above.
+	 */
 	private void initLinks() {
-		final String dpid1 = "00:00:00:00:0a:01";
-		final String dpid2 = "00:00:00:00:0a:02";
-		final String dpid3 = "00:00:00:00:0a:03";
+		links = new ArrayList<Link>();
 		
-		ope.createNewSwitchForTest(dpid1).setStateForTest("ACTIVE");
-		ope.createNewSwitchForTest(dpid2).setStateForTest("ACTIVE");
-		ope.createNewSwitchForTest(dpid3).setStateForTest("ACTIVE");
-
-		TestPortObject ports1 [] = {
-				ope.createNewPortForTest(dpid1, (short)1),
-				ope.createNewPortForTest(dpid1, (short)2),
-				ope.createNewPortForTest(dpid1, (short)3),
-				ope.createNewPortForTest(dpid1, (short)4),
-		};
-
-		TestPortObject ports2 [] = {
-				ope.createNewPortForTest(dpid2, (short)1),
-				ope.createNewPortForTest(dpid2, (short)2),
-		};
-
-		TestPortObject ports3 [] = {
-				ope.createNewPortForTest(dpid3, (short)1),
-				ope.createNewPortForTest(dpid3, (short)2),
-		};
-		
-		ope.setLinkBetweenPortsForTest(ports1[0], ports2[0]);
-		ope.setLinkBetweenPortsForTest(ports1[3], ports3[1]);
+		links.add(new Link(Long.decode("0x0000000000000a01"), 1, Long.decode("0x0000000000000a02"), 1));
+		links.add(new Link(Long.decode("0x0000000000000a01"), 4, Long.decode("0x0000000000000a03"), 2));
 	}
 	
 	/**
-	 * Returns new Link object of existing link
+	 * Returns list of port number attached to the switch specified by given DPID.
+	 * @param dpid DPID of the switch
+	 * @return List of port number
+	 */
+	@SuppressWarnings("serial")
+	private List<Short> getPorts(long dpid) {
+		List<Short> ports;
+		
+		if(dpid == Long.decode("0x0000000000000a01")) {
+			ports = new ArrayList<Short>() {{
+				add((short)1);
+				add((short)2);
+				add((short)3);
+				add((short)4);
+			}};
+		} else if(dpid == Long.decode("0x0000000000000a02") || dpid == Long.decode("0x0000000000000a03")) {
+			ports = new ArrayList<Short>() {{
+				add((short)1);
+				add((short)2);
+			}};
+		} else {
+			ports = new ArrayList<Short>();
+		}
+		
+		return ports;
+	}
+	
+	/**
+	 * Returns list of DPIDs in test topology.
+	 * @return List of DPIDs
+	 */
+	@SuppressWarnings("serial")
+	private List<Long> getDpids() {
+		List<Long> dpids = new ArrayList<Long>() {{
+			add(Long.decode("0x0000000000000a01"));
+			add(Long.decode("0x0000000000000a02"));
+			add(Long.decode("0x0000000000000a03"));
+		}};
+		
+		return dpids;
+	}
+	
+	/**
+	 * Returns new Link object of an existing link
 	 * @return new Link object
 	 */
 	private Link createExistingLink() {
@@ -480,7 +819,7 @@
 	}
 	
 	/**
-	 * Returns new Link object of not-existing but feasible link
+	 * Returns new Link object of a not-existing but feasible link
 	 * @return new Link object
 	 */
 	private Link createFeasibleLink() {