@Test
public void testControllerNodeIPChanges() throws Exception {

    class DummyHAListener implements IHAListener {
        public Map<String, String> curControllerNodeIPs;
        public Map<String, String> addedControllerNodeIPs;
        public Map<String, String> removedControllerNodeIPs;
        public int nCalled;
        
        public DummyHAListener() {
            this.nCalled = 0;
        }
            
        @Override
        public void roleChanged(Role oldRole, Role newRole) {
            // ignore
        }

        @Override
        public synchronized void controllerNodeIPsChanged(
                Map<String, String> curControllerNodeIPs,
                Map<String, String> addedControllerNodeIPs,
                Map<String, String> removedControllerNodeIPs) {
            this.curControllerNodeIPs = curControllerNodeIPs;
            this.addedControllerNodeIPs = addedControllerNodeIPs;
            this.removedControllerNodeIPs = removedControllerNodeIPs;
            this.nCalled++;
            notifyAll();
        }
        
        public void do_assert(int nCalled,
                Map<String, String> curControllerNodeIPs,
                Map<String, String> addedControllerNodeIPs,
                Map<String, String> removedControllerNodeIPs) {
            assertEquals("nCalled is not as expected", nCalled, this.nCalled);
            assertEquals("curControllerNodeIPs is not as expected", 
                    curControllerNodeIPs, this.curControllerNodeIPs);
            assertEquals("addedControllerNodeIPs is not as expected", 
                    addedControllerNodeIPs, this.addedControllerNodeIPs);
            assertEquals("removedControllerNodeIPs is not as expected", 
                    removedControllerNodeIPs, this.removedControllerNodeIPs);
            
        }
    }
    long waitTimeout = 250; // ms
    DummyHAListener listener  = new DummyHAListener();
    HashMap<String,String> expectedCurMap = new HashMap<String, String>();
    HashMap<String,String> expectedAddedMap = new HashMap<String, String>();
    HashMap<String,String> expectedRemovedMap = new HashMap<String, String>();
    
    controller.addHAListener(listener);
    ControllerRunThread t = new ControllerRunThread();
    t.start();
    
    synchronized(listener) {
        // Insert a first entry
        controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1"));
        expectedCurMap.clear();
        expectedAddedMap.clear();
        expectedRemovedMap.clear();
        expectedCurMap.put("c1", "1.1.1.1");
        expectedAddedMap.put("c1", "1.1.1.1");
        listener.wait(waitTimeout);
        listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap);
        
        // Add an interface that we want to ignore. 
        controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2"));
        listener.wait(waitTimeout); // TODO: do a different check. This call will have to wait for the timeout
        assertTrue("controllerNodeIPsChanged() should not have been called here", 
                listener.nCalled == 1);
        // Add another entry
        controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2"));
        expectedCurMap.clear();
        expectedAddedMap.clear();
        expectedRemovedMap.clear();
        expectedCurMap.put("c1", "1.1.1.1");
        expectedCurMap.put("c2", "2.2.2.2");
        expectedAddedMap.put("c2", "2.2.2.2");
        listener.wait(waitTimeout);
        listener.do_assert(2, expectedCurMap, expectedAddedMap, expectedRemovedMap);
        // Update an entry
        controller.storageSource.updateRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME,
                "row3", getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.3"));
        expectedCurMap.clear();
        expectedAddedMap.clear();
        expectedRemovedMap.clear();
        expectedCurMap.put("c1", "1.1.1.1");
        expectedCurMap.put("c2", "2.2.2.3");
        expectedAddedMap.put("c2", "2.2.2.3");
        expectedRemovedMap.put("c2", "2.2.2.2");
        listener.wait(waitTimeout);
        listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap);
        // Delete an entry
        controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, 
                "row3");
        expectedCurMap.clear();
        expectedAddedMap.clear();
        expectedRemovedMap.clear();
        expectedCurMap.put("c1", "1.1.1.1");
        expectedRemovedMap.put("c2", "2.2.2.3");
        listener.wait(waitTimeout);
        listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap);
    }
}