@Test
public void testTwoSubsequentIcmpRequests() throws Exception {

	testCreateVip();
	testCreatePool();
	testCreateMember();
	IOFSwitch sw1;
	IPacket arpRequest1, arpReply1, icmpPacket1, icmpPacket2;
	byte[] arpRequest1Serialized;
	byte[] arpReply1Serialized;
	byte[] icmpPacket1Serialized, icmpPacket2Serialized;
	OFPacketIn arpRequestPacketIn1;
	OFPacketIn icmpPacketIn1, icmpPacketIn2;
	OFPacketOut arpReplyPacketOut1;
	Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL);
	sw1 = EasyMock.createNiceMock(IOFSwitch.class);
	expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes();
	expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes();
	expect(sw1.getOFFactory()).andReturn(factory).anyTimes();
	sw1.write(capture(wc1));
	expectLastCall().anyTimes();
	sw1.flush();
	expectLastCall().anyTimes();
	
	replay(sw1);
	sfp.switchAdded(DatapathId.of(1L));
	
	verify(sw1);
	/* Test plan:
	 * - two clients and two servers on sw1 port 1, 2, 3, 4
	 * - mock arp request received towards vip1 from (1L, 1)
	 * - proxy arp got pushed out to (1L, 1)- check sw1 getting the packetout
	 * - mock icmp request received towards vip1 from (1L, 1)
	 * - device manager list of devices queried to identify source and dest devices
	 * - routing engine queried to get inbound and outbound routes
	 * - check getRoute calls and responses
	 * - sfp called to install flows
	 * - check sfp calls
	 */
	// Build topology
	reset(topology);
	expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes();
	expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes();
	expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes();
	expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(2))).andReturn(true).anyTimes();
	expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes();
	expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(4))).andReturn(true).anyTimes();
	replay(topology);
	// Build arp packets
	arpRequest1 = new Ethernet()
	.setSourceMACAddress("00:00:00:00:00:01")
	.setDestinationMACAddress("ff:ff:ff:ff:ff:ff")
	.setEtherType(EthType.ARP)
	.setVlanID((short) 0)
	.setPriorityCode((byte) 0)
	.setPayload(
			new ARP()
			.setHardwareType(ARP.HW_TYPE_ETHERNET)
			.setProtocolType(ARP.PROTO_TYPE_IP)
			.setHardwareAddressLength((byte) 6)
			.setProtocolAddressLength((byte) 4)
			.setOpCode(ARP.OP_REQUEST)
			.setSenderHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
			.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1"))
			.setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:00"))
			.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100")));
	arpRequest1Serialized = arpRequest1.serialize();
	arpRequestPacketIn1 = factory.buildPacketIn()
			.setMatch(factory.buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build())
			.setBufferId(OFBufferId.NO_BUFFER)
			.setData(arpRequest1Serialized)
			.setReason(OFPacketInReason.NO_MATCH)
			.build();
	IFloodlightProviderService.bcStore.put(cntx,
			IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
			(Ethernet) arpRequest1);
	// Mock proxy arp packet-out
	arpReply1 = new Ethernet()
	.setSourceMACAddress(LBVip.LB_PROXY_MAC)
	.setDestinationMACAddress(HexString.fromHexString("00:00:00:00:00:01"))
	.setEtherType(EthType.ARP)
	.setVlanID((short) 0)
	.setPriorityCode((byte) 0)
	.setPayload(
			new ARP()
			.setHardwareType(ARP.HW_TYPE_ETHERNET)
			.setProtocolType(ARP.PROTO_TYPE_IP)
			.setHardwareAddressLength((byte) 6)
			.setProtocolAddressLength((byte) 4)
			.setOpCode(ARP.OP_REPLY)
			.setSenderHardwareAddress(HexString.fromHexString(LBVip.LB_PROXY_MAC))
			.setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100"))
			.setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:01"))
			.setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1")));
	arpReply1Serialized = arpReply1.serialize();
	List<OFAction> poactions = new ArrayList<OFAction>();
	poactions.add(factory.actions().output(arpRequestPacketIn1.getMatch().get(MatchField.IN_PORT), Integer.MAX_VALUE));
	arpReplyPacketOut1 = factory.buildPacketOut()
			.setBufferId(OFBufferId.NO_BUFFER)
			.setInPort(OFPort.ANY)
			.setActions(poactions)
			.setData(arpReply1Serialized)
			.setXid(22)
			.build();
	sw1.write(arpReplyPacketOut1);
	
	lb.receive(sw1, arpRequestPacketIn1, cntx);
	verify(sw1, topology);
	assertTrue(wc1.hasCaptured());  // wc1 should get packetout
	List<OFMessage> msglist1 = wc1.getValues();
	for (OFMessage m: msglist1) {
		if (m instanceof OFPacketOut)
			assertTrue(OFMessageUtils.equalsIgnoreXid(arpReplyPacketOut1, m));
		else
			assertTrue(false); // unexpected message
	}
	//
	// Skip arpRequest2 test - in reality this will happen, but for unit test the same logic
	// is already validated with arpRequest1 test above
	//
	
	// Keep the StaticFlowEntryPusher happy with a switch in the switch service
	Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>(1);
	switches.put(DatapathId.of(1), sw1);
	getMockSwitchService().setSwitches(switches);
	// Build icmp packets
	icmpPacket1 = new Ethernet()
	.setSourceMACAddress("00:00:00:00:00:01")
	.setDestinationMACAddress(LBVip.LB_PROXY_MAC)
	.setEtherType(EthType.IPv4)
	.setVlanID((short) 0)
	.setPriorityCode((byte) 0)
	.setPayload(
			new IPv4()
			.setSourceAddress("10.0.0.1")
			.setDestinationAddress("10.0.0.100")
			.setProtocol(IpProtocol.ICMP)
			.setPayload(new ICMP()
			.setIcmpCode((byte) 0)
			.setIcmpType((byte) 0)));
	icmpPacket1Serialized = icmpPacket1.serialize();
	icmpPacketIn1 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn()
			.setBufferId(OFBufferId.NO_BUFFER)
			.setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build())
			.setData(icmpPacket1Serialized)
			.setReason(OFPacketInReason.NO_MATCH)
			.build();
	icmpPacket2 = new Ethernet()
	.setSourceMACAddress("00:00:00:00:00:02")
	.setDestinationMACAddress(LBVip.LB_PROXY_MAC)
	.setEtherType(EthType.IPv4)
	.setVlanID((short) 0)
	.setPriorityCode((byte) 0)
	.setPayload(
			new IPv4()
			.setSourceAddress("10.0.0.2")
			.setDestinationAddress("10.0.0.100")
			.setProtocol(IpProtocol.ICMP)
			.setPayload(new ICMP()
			.setIcmpCode((byte) 0)
			.setIcmpType((byte) 0)));
	icmpPacket2Serialized = icmpPacket2.serialize();
	icmpPacketIn2 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn()
			.setBufferId(OFBufferId.NO_BUFFER)
			.setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(2)).build())
			.setData(icmpPacket2Serialized)
			.setReason(OFPacketInReason.NO_MATCH)
			.build();
	MacAddress dataLayerSource1 = ((Ethernet)icmpPacket1).getSourceMACAddress();
	IPv4Address networkSource1 = ((IPv4)((Ethernet)icmpPacket1).getPayload()).getSourceAddress();
	MacAddress dataLayerDest1 = MacAddress.of("00:00:00:00:00:03");
	IPv4Address networkDest1 = IPv4Address.of("10.0.0.3");
	MacAddress dataLayerSource2 = ((Ethernet)icmpPacket2).getSourceMACAddress();
	IPv4Address networkSource2 = ((IPv4)((Ethernet)icmpPacket2).getPayload()).getSourceAddress();
	MacAddress dataLayerDest2 = MacAddress.of("00:00:00:00:00:04");
	IPv4Address networkDest2 = IPv4Address.of("10.0.0.4");
	deviceManager.learnEntity(dataLayerSource1.getLong(),
			null, networkSource1.getInt(),
			1L, 1);
	deviceManager.learnEntity(dataLayerSource2.getLong(),
			null, networkSource2.getInt(),
			1L, 2);
	deviceManager.learnEntity(dataLayerDest1.getLong(),
			null, networkDest1.getInt(),
			1L, 3);
	deviceManager.learnEntity(dataLayerDest2.getLong(),
			null, networkDest2.getInt(),
			1L, 4);
	// in bound #1
	Route route1 = new Route(DatapathId.of(1L), DatapathId.of(1L));
	List<NodePortTuple> nptList1 = new ArrayList<NodePortTuple>();
	nptList1.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
	nptList1.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
	route1.setPath(nptList1);
	expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.of(0))).andReturn(route1).atLeastOnce();
	// outbound #1
	Route route2 = new Route(DatapathId.of(1L), DatapathId.of(1L));
	List<NodePortTuple> nptList2 = new ArrayList<NodePortTuple>();
	nptList2.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3)));
	nptList2.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1)));
	route2.setPath(nptList2);
	expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(3), DatapathId.of(1L), OFPort.of(1), U64.of(0))).andReturn(route2).atLeastOnce();
	// inbound #2
	Route route3 = new Route(DatapathId.of(1L), DatapathId.of(1L));
	List<NodePortTuple> nptList3 = new ArrayList<NodePortTuple>();
	nptList3.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(2)));
	nptList3.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(4)));
	route3.setPath(nptList3);
	expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(2), DatapathId.of(1L), OFPort.of(4), U64.of(0))).andReturn(route3).atLeastOnce();
	// outbound #2
	Route route4 = new Route(DatapathId.of(1L), DatapathId.of(1L));
	List<NodePortTuple> nptList4 = new ArrayList<NodePortTuple>();
	nptList4.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(4)));
	nptList4.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(2)));
	route4.setPath(nptList3);
	expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(4), DatapathId.of(1L), OFPort.of(2), U64.of(0))).andReturn(route4).atLeastOnce();
	replay(routingEngine);
	wc1.reset();
	IFloodlightProviderService.bcStore.put(cntx,
			IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
			(Ethernet) icmpPacket1);
	lb.receive(sw1, icmpPacketIn1, cntx);
	IFloodlightProviderService.bcStore.put(cntx,
			IFloodlightProviderService.CONTEXT_PI_PAYLOAD,
			(Ethernet) icmpPacket2);
	lb.receive(sw1, icmpPacketIn2, cntx);
	assertTrue(wc1.hasCaptured());  // wc1 should get packetout
	List<OFMessage> msglist2 = wc1.getValues();
	assertTrue(msglist2.size()==2); // has inbound and outbound packetouts
	// TODO: not seeing flowmods yet ...
	Map<String, OFFlowMod> map = sfp.getFlows(DatapathId.of(1L));
	assertTrue(map.size()==4);
}