org.opendaylight.vtn.manager.internal.util.vnode.VTNMacMapStatusTest.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.vtn.manager.internal.util.vnode.VTNMacMapStatusTest.java

Source

/*
 * Copyright (c) 2015, 2016 NEC Corporation. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.vtn.manager.internal.util.vnode;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.tuple.Triple;

import org.junit.Test;

import org.opendaylight.vtn.manager.util.EtherAddress;

import org.opendaylight.vtn.manager.internal.util.inventory.MacVlan;
import org.opendaylight.vtn.manager.internal.util.inventory.NodePortFilter;
import org.opendaylight.vtn.manager.internal.util.inventory.PortFilter;
import org.opendaylight.vtn.manager.internal.util.inventory.PortVlan;
import org.opendaylight.vtn.manager.internal.util.inventory.SalNode;
import org.opendaylight.vtn.manager.internal.util.inventory.SalPort;
import org.opendaylight.vtn.manager.internal.util.inventory.SpecificPortFilter;

import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;

import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;

import org.opendaylight.vtn.manager.internal.TestBase;

import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.mapping.mac.rev150907.vtn.mac.map.info.MacMapStatus;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.mapping.mac.rev150907.vtn.mac.map.info.MacMapStatusBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.mapping.mac.rev150907.vtn.mac.map.status.MappedHost;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.mapping.mac.rev150907.vtn.mac.map.status.MappedHostBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.mapping.mac.rev150907.vtn.mac.mappable.MacMap;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.rev150328.Vtns;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.rev150328.vtns.Vtn;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.rev150328.vtns.VtnKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.types.rev150209.VnodeName;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.vbridge.rev150907.vtn.vbridge.list.Vbridge;
import org.opendaylight.yang.gen.v1.urn.opendaylight.vtn.vbridge.rev150907.vtn.vbridge.list.VbridgeKey;

import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;

/**
 * JUnit test for {@link VTNMacMapStatus}.
 */
public class VTNMacMapStatusTest extends TestBase {
    /**
     * A list of {@link SalPort} instances.
     */
    private final List<SalPort> portList = new ArrayList<>();

    /**
     * Cursor into {@link #portList} array.
     */
    private int portListIndex;

    /**
     * A list of {@link SalPort} instances which are never used for test.
     */
    private final List<SalPort> unusedPortList = new ArrayList<>();

    /**
     * A class to keep hosts mapped by MAC mapping.
     */
    private static final class HostMap {
        /**
         * A map to keep hosts mapped by MAC mapping.
         */
        private final Map<MacVlan, SalPort> hostMap;

        /**
         * Construct an empty instance.
         */
        private HostMap() {
            hostMap = new HashMap<>();
        }

        /**
         * Copy constructor.
         *
         * @param map  A {@link HostMap} instance to be copied.
         */
        private HostMap(HostMap map) {
            hostMap = new HashMap<>(map.hostMap);
        }

        /**
         * Return the switch port associated with the given host.
         *
         * @param mvlan  A {@link MacVlan} instance.
         * @return  A {@link SalPort} instance if found.
         *          {@code null} if not found.
         */
        private SalPort get(MacVlan mvlan) {
            return hostMap.get(mvlan);
        }

        /**
         * Add the specified host.
         *
         * @param mvlan  A {@link MacVlan} instance.
         * @param sport  A {@link SalPort} instance.
         */
        private void put(MacVlan mvlan, SalPort sport) {
            hostMap.put(mvlan, sport);
        }

        /**
         * Remove the specified host.
         *
         * @param mvlan  A {@link MacVlan} instance.
         * @return  A {@link SalPort} instance previously associated with
         *          {@code mvlan}.
         */
        private SalPort remove(MacVlan mvlan) {
            return hostMap.remove(mvlan);
        }

        /**
         * Return a set of hosts.
         *
         * @return  A set of host.
         */
        private Set<MacVlan> keySet() {
            return hostMap.keySet();
        }

        /**
         * Return a set of host entries.
         *
         * @return  A set of host entries.
         */
        private Set<Entry<MacVlan, SalPort>> entrySet() {
            return hostMap.entrySet();
        }

        /**
         * Remove all the host entries in this map.
         */
        private void clear() {
            hostMap.clear();
        }

        /**
         * Ensure that the given map keeps the same hosts as this instance.
         *
         * @param map  A map that keeps mapped hosts.
         */
        private void verify(Map<MacVlan, SalPort> map) {
            assertEquals(hostMap, map);
        }

        /**
         * Ensure that the given {@link MacMapStatus} instance keeps the same
         * hosts as this instance.
         *
         * @param mst  A {@link MacMapStatus} instance.
         */
        private void verify(MacMapStatus mst) {
            List<MappedHost> hosts = mst.getMappedHost();
            if (hostMap.isEmpty()) {
                assertEquals(null, hosts);
                return;
            }

            assertNotNull(hosts);
            assertEquals(hostMap.size(), hosts.size());

            for (MappedHost mhost : hosts) {
                EtherAddress eaddr = new EtherAddress(mhost.getMacAddress());
                int vid = mhost.getVlanId().getValue().intValue();
                MacVlan mv = new MacVlan(eaddr.getAddress(), vid);
                SalPort sport = SalPort.create(mhost.getPortId());
                assertEquals(hostMap.get(mv), sport);
            }
        }

        // Object

        /**
         * Determine whether the given object is identical to this object.
         *
         * @param o  An object to be compared.
         * @return   {@code true} if identical. Otherwise {@code false}.
         */
        @Override
        public boolean equals(Object o) {
            boolean ret = (o == this);
            if (!ret && o != null && getClass().equals(o.getClass())) {
                HostMap hmap = (HostMap) o;
                ret = hostMap.equals(hmap.hostMap);
            }

            return ret;
        }

        /**
         * Return the hash code of this object.
         *
         * @return  The hash code.
         */
        @Override
        public int hashCode() {
            return Objects.hash(getClass(), hostMap);
        }
    }

    /**
     * A class to keep networks mapped by MAC mapping.
     */
    private static final class NetworkMap {
        /**
         * Pairs of {@link PortVlan} instance and its reference counter.
         */
        private final Map<PortVlan, Integer> networkMap = new HashMap<>();

        /**
         * Append the specified network.
         *
         * @param pvlan  A {@link PortVlan} instance to be added.
         */
        private void add(PortVlan pvlan) {
            Integer count = networkMap.get(pvlan);
            int cnt = (count == null) ? 1 : count.intValue() + 1;
            networkMap.put(pvlan, Integer.valueOf(cnt));
        }

        /**
         * Remove the specified network.
         *
         * @param pvlan  A {@link PortVlan} instance to be removed.
         * @return  {@code true} is returned if the specified {@link PortVlan}
         *          was removed from this instance.
         *          {@code false} is returned if it is still kept by this
         *          instance.
         */
        private boolean remove(PortVlan pvlan) {
            Integer count = networkMap.remove(pvlan);
            assertNotNull(count);

            int cnt = count.intValue();
            boolean removed;
            if (cnt > 1) {
                networkMap.put(pvlan, Integer.valueOf(cnt - 1));
                removed = false;
            } else {
                removed = true;
            }

            return removed;
        }

        /**
         * Force to remove the specified network.
         *
         * @param pvlan  A {@link PortVlan} instance to be removed.
         */
        private void removeForce(PortVlan pvlan) {
            networkMap.remove(pvlan);
        }

        /**
         * Force to remove all networks on the specified port.
         *
         * @param filter  A {@link PortFilter} instance which determines
         *                ports to be removed.
         * @return  A set of {@link PortVlan} instances which were actually
         *          removed is returned.
         * @throws Exception  An error occurred.
         */
        private Set<PortVlan> removeForce(PortFilter filter) throws Exception {
            Set<PortVlan> removed = new HashSet<PortVlan>();
            for (Iterator<PortVlan> it = networkMap.keySet().iterator(); it.hasNext();) {
                PortVlan pvlan = it.next();
                SalPort sport = pvlan.getPort();
                if (filter.accept(sport, null)) {
                    removed.add(pvlan);
                    it.remove();
                }
            }

            return removed;
        }

        /**
         * Return a set of {@link PortVlan} instances.
         *
         * @return  A set of {@link PortVlan} instances.
         */
        private Set<PortVlan> getNetworks() {
            return networkMap.keySet();
        }
    }

    /**
     * Construct a new instance.
     */
    public VTNMacMapStatusTest() {
        // Create node connectors for test.
        for (long dpid = 0; dpid < 2L; dpid++) {
            for (long id = 1L; id <= 5L; id++) {
                portList.add(new SalPort(dpid, id));
            }

            for (long id = 100L; id <= 102L; id++) {
                unusedPortList.add(new SalPort(dpid, id));
            }
        }
    }

    /**
     * Test case for {@link VTNMacMapStatus#VTNMacMapStatus()} and
     * {@link VTNMacMapStatus#VTNMacMapStatus(MacMapStatus)}.
     */
    @Test
    public void testConstructor() {
        // Test case for an empty instance.
        VTNMacMapStatus vmst = new VTNMacMapStatus();
        MacMapStatus empty = new MacMapStatusBuilder().build();
        assertEquals(empty, vmst.toMacMapStatus());
        assertFalse(vmst.isDirty());

        vmst = new VTNMacMapStatus(null);
        assertEquals(empty, vmst.toMacMapStatus());
        assertFalse(vmst.isDirty());

        vmst = new VTNMacMapStatus(empty);
        assertEquals(empty, vmst.toMacMapStatus());
        assertFalse(vmst.isDirty());

        HostMap hostMap = new HostMap();
        MacVlan[] hosts = { new MacVlan(0x001122334455L, 0), new MacVlan(0xfeffabcdef00L, 0),
                new MacVlan(0xfeffabcdef05L, 1), new MacVlan(0xfeffabcdef0aL, 100),
                new MacVlan(0xfeffabcdefffL, 123), new MacVlan(0x000000000001L, 4094),
                new MacVlan(0x00000000000aL, 4094), new MacVlan(0xfc927ace41d7L, 4095), };

        List<MappedHost> mhosts = new ArrayList<>();
        for (MacVlan mv : hosts) {
            SalPort sport = getPort();
            hostMap.put(mv, sport);

            MappedHost mhost = new MappedHostBuilder().setMacAddress(mv.getMacAddress())
                    .setPortId(sport.getNodeConnectorId()).setVlanId(new VlanId(mv.getVlanId())).build();
            mhosts.add(mhost);
            MacMapStatus mst = new MacMapStatusBuilder().setMappedHost(mhosts).build();
            vmst = new VTNMacMapStatus(mst);
            hostMap.verify(vmst.toMacMapStatus());

            // Add one more host at the same port.
            long mac = mv.getAddress();
            MacVlan newMv = new MacVlan(mac + 1L, mv.getVlanId());
            hostMap.put(newMv, sport);

            mhost = new MappedHostBuilder().setMacAddress(newMv.getMacAddress())
                    .setPortId(sport.getNodeConnectorId()).setVlanId(new VlanId(newMv.getVlanId())).build();
            mhosts.add(mhost);
            mst = new MacMapStatusBuilder().setMappedHost(mhosts).build();
            vmst = new VTNMacMapStatus(mst);
            hostMap.verify(vmst.toMacMapStatus());
        }

        // Test case for invalid mac-map-status.
        MappedHost mhost = new MappedHostBuilder().build();
        mhosts = Collections.singletonList(mhost);
        MacMapStatus mst = new MacMapStatusBuilder().setMappedHost(mhosts).build();
        try {
            new VTNMacMapStatus(mst);
            unexpected();
        } catch (IllegalStateException e) {
            Throwable cause = e.getCause();
            assertEquals(NullPointerException.class, cause.getClass());
            String msg = "Unable to cache mac-map-status: " + cause.getMessage();
            assertEquals(msg, e.getMessage());
        }
    }

    /**
     * Test case for
     * {@link VTNMacMapStatus#submit(ReadWriteTransaction, MacMapIdentifier)}.
     *
     * @throws Exception  An error occurred.
     */
    @Test
    public void testSubmit() throws Exception {
        // Empty instance.
        VTNMacMapStatus vmst = new VTNMacMapStatus();
        ReadWriteTransaction tx = mock(ReadWriteTransaction.class);
        VnodeName vtnName = new VnodeName("tenant");
        VnodeName vbrName = new VnodeName("bridge");
        MacMapIdentifier mapId = new MacMapIdentifier(vtnName, vbrName);
        vmst.submit(tx, mapId);
        verifyZeroInteractions(tx);

        // Activate a mapping.
        MacVlan mv = new MacVlan(0x001122334455L, 1234);
        SalPort sport = new SalPort(1234L, 5678L);
        MacAddress mac = mv.getMacAddress();
        MappedHost mhost = new MappedHostBuilder().setMacAddress(mac).setPortId(sport.getNodeConnectorId())
                .setVlanId(new VlanId(mv.getVlanId())).build();
        MacMapStatus mst = new MacMapStatusBuilder().setMappedHost(Collections.singletonList(mhost)).build();
        LogicalDatastoreType oper = LogicalDatastoreType.OPERATIONAL;
        InstanceIdentifier<MacMapStatus> path = InstanceIdentifier.builder(Vtns.class)
                .child(Vtn.class, new VtnKey(vtnName)).child(Vbridge.class, new VbridgeKey(vbrName))
                .child(MacMap.class).child(MacMapStatus.class).build();

        vmst.activate(mapId, mv, sport);
        vmst.submit(tx, mapId);
        verify(tx).put(oper, path, mst, false);
        verifyNoMoreInteractions(tx);

        // Submit again.
        tx = mock(ReadWriteTransaction.class);
        vmst.submit(tx, mapId);
        verifyZeroInteractions(tx);

        // Try to activate the same host.
        vmst.activate(mapId, mv, sport);
        vmst.submit(tx, mapId);
        verifyZeroInteractions(tx);
    }

    /**
     * Ensure that {@link VTNMacMapStatus} maintains mapped host information
     * correctly.
     *
     * <ul>
     *   <li>{@link VTNMacMapStatus#activate(MacMapIdentifier, MacVlan, SalPort)}</li>
     *   <li>{@link VTNMacMapStatus#getPort(MacVlan)}</li>
     *   <li>{@link VTNMacMapStatus#getPortVlan(long)}</li>
     *   <li>{@link VTNMacMapStatus#hasMapping(PortVlan)}</li>
     *   <li>{@link VTNMacMapStatus#hasMapping()}</li>
     *   <li>{@link VTNMacMapStatus#getNetworks()}</li>
     *   <li>{@link VTNMacMapStatus#getDuplicate(MacVlan)}</li>
     *   <li>{@link VTNMacMapStatus#isDirty()}</li>
     *   <li>{@link VTNMacMapStatus#inactivate(MacVlan, Set)}</li>
     *   <li>{@link VTNMacMapStatus#inactivate(PortVlan)}</li>
     *   <li>{@link VTNMacMapStatus#inactivate(PortFilter, Set)}</li>
     *   <li>{@link VTNMacMapStatus#inactivate(MacMapIdentifier, Set, Set, Set)}</li>
     * </ul>
     *
     * @throws Exception  An error occurred.
     */
    @Test
    public void testStatus() throws Exception {
        // Test case for an empty instance.
        VTNMacMapStatus vmst = new VTNMacMapStatus();
        assertFalse(vmst.hasMapping());
        assertNull(vmst.getNetworks());
        assertFalse(vmst.isDirty());

        NetworkMap nwMap = new NetworkMap();
        HostMap hostMap = new HostMap();

        // Activate MAC mappings with specifying MAC addresses in which the
        // MSB is not set.
        VnodeName vtnName = new VnodeName("tenant");
        VnodeName vbrName = new VnodeName("bridge");
        MacMapIdentifier mapId = new MacMapIdentifier(vtnName, vbrName);
        int[] vlans = { 0, 1, 4094, 4095 };
        long mac = 0x1000L;
        for (int i = 0; i < 10; i++) {
            for (int vid : vlans) {
                checkActivate(vmst, mapId, mac, vid, null, nwMap, hostMap);
                mac++;
            }
        }

        // Activate MAC mappings with specifying MAC addresses in which the
        // MSB is set.
        mac = 0xfeffffffff00L;
        for (int i = 0; i < 10; i++) {
            for (int vid : vlans) {
                checkActivate(vmst, mapId, mac, vid, null, nwMap, hostMap);
                mac++;
            }
        }

        // Ensure that duplicate MAC addresses are rejected.
        for (Entry<MacVlan, SalPort> entry : hostMap.entrySet()) {
            MacVlan mapped = entry.getKey();
            SalPort sport = entry.getValue();
            mac = mapped.getAddress();
            MacVlan mvlan = new MacVlan(mac, 2000);

            assertEquals(mapped, vmst.getDuplicate(mvlan));
            try {
                vmst.activate(mapId, mvlan, sport);
                fail("An exception must be thrown.");
            } catch (MacMapDuplicateException e) {
                assertEquals(mvlan, e.getHost());
                assertEquals(mapId, e.getIdentifier());
                assertEquals(mapped, e.getDuplicate());
            }

            assertFalse(vmst.isDirty());
            hostMap.verify(vmst.toMacMapStatus());
        }

        Set<PortVlan> nw = vmst.getNetworks();
        assertEquals(nwMap.getNetworks(), nw);

        // Move all hosts to one port.
        SalPort newPort = unusedPortList.get(0);
        HostMap hmap = new HostMap(hostMap);
        for (Entry<MacVlan, SalPort> entry : hmap.entrySet()) {
            MacVlan mvlan = entry.getKey();
            mac = mvlan.getAddress();
            int vid = mvlan.getVlanId();
            SalPort sport = entry.getValue();
            PortVlan pvlan = new PortVlan(sport, vid);
            assertNull(vmst.getDuplicate(mvlan));
            assertEquals(pvlan, vmst.getPortVlan(mac));
            assertEquals(sport, vmst.getPort(mvlan));
            assertFalse(vmst.isDirty());

            Triple<Boolean, SalPort, PortVlan> result = vmst.activate(mapId, mvlan, newPort);
            assertTrue(vmst.isDirty());
            assertFalse(vmst.isDirty());
            assertFalse(result.getLeft().booleanValue());
            assertEquals(sport, result.getMiddle());
            PortVlan released = (nwMap.remove(pvlan)) ? pvlan : null;
            assertEquals(released, result.getRight());
            hostMap.put(mvlan, newPort);
            hostMap.verify(vmst.toMacMapStatus());

            assertNull(vmst.activate(mapId, mvlan, newPort));
            assertFalse(vmst.isDirty());
            hostMap.verify(vmst.toMacMapStatus());

            pvlan = new PortVlan(newPort, mvlan.getVlanId());
            assertNull(vmst.getDuplicate(mvlan));
            assertEquals(pvlan, vmst.getPortVlan(mac));
            assertEquals(newPort, vmst.getPort(mvlan));

            nwMap.add(pvlan);
            assertEquals(nwMap.getNetworks(), vmst.getNetworks());
            assertTrue(vmst.hasMapping());
        }

        // Restore mappings.
        checkActivate(vmst, mapId, hmap, nwMap, hostMap);
        assertEquals(hmap, hostMap);

        // Inactivate all hosts by specifying MacVlan instance.
        for (Entry<MacVlan, SalPort> entry : hmap.entrySet()) {
            assertTrue(vmst.hasMapping());
            MacVlan mvlan = entry.getKey();
            mac = mvlan.getAddress();
            SalPort sport = entry.getValue();
            PortVlan pvlan = new PortVlan(sport, mvlan.getVlanId());
            assertTrue(vmst.hasMapping(pvlan));

            Set<PortVlan> released = new HashSet<PortVlan>();
            assertEquals(sport, vmst.inactivate(mvlan, released));
            assertTrue(vmst.isDirty());
            assertFalse(vmst.isDirty());
            assertEquals(sport, hostMap.remove(mvlan));
            hostMap.verify(vmst.toMacMapStatus());

            boolean removed = nwMap.remove(pvlan);
            if (removed) {
                assertEquals(1, released.size());
                assertTrue(released.contains(pvlan));
            } else {
                assertTrue(released.isEmpty());
            }
            assertEquals(!removed, vmst.hasMapping(pvlan));

            assertNull(vmst.getDuplicate(mvlan));
            assertNull(vmst.getPortVlan(mac));
            assertNull(vmst.getPort(mvlan));

            // Try to inactivate the same host.
            released.clear();
            assertNull(vmst.inactivate(mvlan, released));
            assertFalse(vmst.isDirty());
            assertTrue(released.isEmpty());
            hostMap.verify(vmst.toMacMapStatus());
        }
        assertFalse(vmst.hasMapping());

        // Restore mappings again.
        checkActivate(vmst, mapId, hmap, nwMap, hostMap);
        assertEquals(hmap, hostMap);

        // Inactivate all hosts by specifying PortVlan instance.
        Set<PortVlan> nwSet = new HashSet<>(nwMap.getNetworks());
        for (PortVlan pvlan : nwSet) {
            assertTrue(vmst.hasMapping());
            assertTrue(vmst.hasMapping(pvlan));
            Set<MacVlan> removed = vmst.inactivate(pvlan);
            assertTrue(vmst.isDirty());
            assertFalse(vmst.isDirty());
            assertEquals(removeHosts(hostMap, pvlan).keySet(), removed);
            nwMap.removeForce(pvlan);
            hostMap.verify(vmst.toMacMapStatus());

            assertFalse(vmst.hasMapping(pvlan));
            Set<PortVlan> nw1 = nwMap.getNetworks();
            if (nw1.isEmpty()) {
                nw1 = null;
            }
            assertEquals(nw1, vmst.getNetworks());

            for (MacVlan mvlan : removed) {
                assertNull(vmst.getDuplicate(mvlan));
                assertNull(vmst.getPortVlan(mvlan.getAddress()));
                assertNull(vmst.getPort(mvlan));
            }

            // Try to inactivate the same network.
            assertNull(vmst.inactivate(pvlan));
            assertFalse(vmst.isDirty());
            hostMap.verify(vmst.toMacMapStatus());
        }
        assertFalse(vmst.hasMapping());

        // Restore mappings again.
        checkActivate(vmst, mapId, hmap, nwMap, hostMap);
        assertEquals(hmap, hostMap);

        // Inactivate all hosts on the specified switch.
        SalNode removedNode = portList.get(0).getSalNode();
        PortFilter filter = new NodePortFilter(removedNode);
        Set<PortVlan> released = new HashSet<>();
        Map<MacVlan, SalPort> removedMap = vmst.inactivate(filter, released);
        assertTrue(vmst.isDirty());
        assertFalse(vmst.isDirty());
        assertFalse(removedMap.isEmpty());
        assertFalse(released.isEmpty());
        assertEquals(removedMap, removeHosts(hostMap, filter));
        assertEquals(released, nwMap.removeForce(filter));
        assertEquals(nwMap.getNetworks(), vmst.getNetworks());
        hostMap.verify(vmst.toMacMapStatus());

        for (Entry<MacVlan, SalPort> entry : removedMap.entrySet()) {
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            assertEquals(removedNode, sport.getSalNode());
            assertNull(vmst.getDuplicate(mvlan));
            assertNull(vmst.getPortVlan(mvlan.getAddress()));
            assertNull(vmst.getPort(mvlan));
            PortVlan pvlan = new PortVlan(sport, mvlan.getVlanId());
            assertFalse(vmst.hasMapping(pvlan));
        }
        assertTrue(vmst.hasMapping());

        // Try to inactivate again.
        released.clear();
        assertTrue(vmst.inactivate(filter, released).isEmpty());
        assertFalse(vmst.isDirty());
        assertTrue(released.isEmpty());
        hostMap.verify(vmst.toMacMapStatus());

        // Inactivate all hosts by specifying switch port.
        for (SalPort sport : portList) {
            SalNode snode = sport.getSalNode();
            boolean inactivated = snode.equals(removedNode);
            filter = new SpecificPortFilter(sport);
            released.clear();
            removedMap = vmst.inactivate(filter, released);
            assertEquals(!inactivated, vmst.isDirty());
            assertFalse(vmst.isDirty());
            assertEquals(inactivated, removedMap.isEmpty());
            assertEquals(inactivated, released.isEmpty());
            assertEquals(removedMap, removeHosts(hostMap, filter));
            assertEquals(released, nwMap.removeForce(filter));
            hostMap.verify(vmst.toMacMapStatus());

            Set<PortVlan> nw1 = nwMap.getNetworks();
            if (nw1.isEmpty()) {
                nw1 = null;
            }
            assertEquals(nw1, vmst.getNetworks());

            for (Entry<MacVlan, SalPort> entry : removedMap.entrySet()) {
                MacVlan mvlan = entry.getKey();
                assertEquals(sport, entry.getValue());
                assertFalse(removedNode.equals(sport.getSalNode()));
                assertNull(vmst.getDuplicate(mvlan));
                assertNull(vmst.getPortVlan(mvlan.getAddress()));
                assertNull(vmst.getPort(mvlan));
                PortVlan pvlan = new PortVlan(sport, mvlan.getVlanId());
                assertFalse(vmst.hasMapping(pvlan));
            }

            // Try to inactivate again.
            released.clear();
            assertTrue(vmst.inactivate(filter, released).isEmpty());
            assertFalse(vmst.isDirty());
            assertTrue(released.isEmpty());
            hostMap.verify(vmst.toMacMapStatus());
        }
        assertFalse(vmst.hasMapping());

        // Restore mappings again.
        checkActivate(vmst, mapId, hmap, nwMap, hostMap);
        assertEquals(hmap, hostMap);

        // Test case for inactivate(MacMapIdentifier, Set, Set, Set).
        // In order to simplify the test code, hosts to be inactivated are
        // determined by PortVlan instances.
        // At first, choose 2 arbitrary VLAN networks.
        Set<PortVlan> expectedNw = new HashSet<>();
        Map<MacVlan, SalPort> expectedHosts = new HashMap<>();
        Set<Integer> unmappedVlans = new HashSet<>();
        for (int i = 1; i <= 2; i++) {
            int idx = (i * i) + 2;
            int vid = vlans[i];
            SalPort sport = portList.get(idx);
            PortVlan pvlan = new PortVlan(sport, vid);
            expectedNw.add(pvlan);
            nwMap.removeForce(pvlan);
            Map<MacVlan, SalPort> m = removeHosts(hostMap, pvlan);
            assertFalse(m.isEmpty());
            expectedHosts.putAll(m);

            // This test case assumes that this host is mapped by MAC mapping
            // with wildcard MAC address. So this VLAN ID needs to be added
            // to unmappedVlans.
            unmappedVlans.add(vid);
        }
        assertFalse(expectedNw.isEmpty());
        assertFalse(expectedHosts.isEmpty());
        assertFalse(unmappedVlans.isEmpty());

        // Construct an allowed host set.
        Set<MacVlan> allowed = new HashSet<>();
        for (MacVlan mvlan : hmap.keySet()) {
            int vid = mvlan.getVlanId();
            if (unmappedVlans.contains(vid) && !expectedHosts.containsKey(mvlan)) {
                // This host should be retained.
                allowed.add(mvlan);
            }
        }
        assertFalse(allowed.isEmpty());

        // Inactivate all hosts on the specified VLAN except for hosts
        // which are mapped explicitly.
        released.clear();
        removedMap = vmst.inactivate(mapId, allowed, unmappedVlans, released);
        assertTrue(vmst.isDirty());
        assertFalse(vmst.isDirty());
        assertFalse(removedMap.isEmpty());
        assertFalse(released.isEmpty());
        assertEquals(removedMap, expectedHosts);
        assertEquals(released, expectedNw);
        assertEquals(nwMap.getNetworks(), vmst.getNetworks());
        hostMap.verify(vmst.toMacMapStatus());

        for (Entry<MacVlan, SalPort> entry : removedMap.entrySet()) {
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            assertNull(vmst.getDuplicate(mvlan));
            assertNull(vmst.getPortVlan(mvlan.getAddress()));
            assertNull(vmst.getPort(mvlan));
            PortVlan pvlan = new PortVlan(sport, mvlan.getVlanId());
            assertFalse(vmst.hasMapping(pvlan));
        }
        assertTrue(vmst.hasMapping());

        // Try to inactivate again.
        released.clear();
        assertTrue(vmst.inactivate(mapId, allowed, unmappedVlans, released).isEmpty());
        assertFalse(vmst.isDirty());
        assertTrue(released.isEmpty());
        assertTrue(vmst.hasMapping());
        hostMap.verify(vmst.toMacMapStatus());

        // No host should be inactivated if unmappedVlans is empty.
        allowed.clear();
        unmappedVlans.clear();
        assertTrue(vmst.inactivate(mapId, allowed, unmappedVlans, released).isEmpty());
        assertFalse(vmst.isDirty());
        assertTrue(released.isEmpty());
        assertTrue(vmst.hasMapping());
        hostMap.verify(vmst.toMacMapStatus());

        // No host should be inactivated if all hosts are explicitly mapped.
        for (int vid : vlans) {
            unmappedVlans.add(vid);
        }
        allowed.clear();
        allowed.addAll(hostMap.keySet());
        assertTrue(vmst.inactivate(mapId, allowed, unmappedVlans, released).isEmpty());
        assertFalse(vmst.isDirty());
        assertTrue(released.isEmpty());
        assertTrue(vmst.hasMapping());
        hostMap.verify(vmst.toMacMapStatus());

        // All hosts should be inactivated if no host is explicitly mapped
        // and all VLANs are unmapped.
        allowed.clear();
        hostMap.verify(vmst.inactivate(mapId, allowed, unmappedVlans, released));
        assertTrue(vmst.isDirty());
        assertFalse(vmst.isDirty());
        assertEquals(nwMap.getNetworks(), released);
        assertFalse(vmst.hasMapping());
        assertNull(vmst.getNetworks());

        for (Entry<MacVlan, SalPort> entry : hostMap.entrySet()) {
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            assertNull(vmst.getDuplicate(mvlan));
            assertNull(vmst.getPortVlan(mvlan.getAddress()));
            assertNull(vmst.getPort(mvlan));
            PortVlan pvlan = new PortVlan(sport, mvlan.getVlanId());
            assertFalse(vmst.hasMapping(pvlan));
        }

        hostMap.clear();
        hostMap.verify(vmst.toMacMapStatus());

        // Try to inactivate again.
        released.clear();
        assertTrue(vmst.inactivate(mapId, allowed, unmappedVlans, released).isEmpty());
        assertFalse(vmst.isDirty());
        assertTrue(released.isEmpty());
        assertFalse(vmst.hasMapping());
        hostMap.verify(vmst.toMacMapStatus());
    }

    /**
     * Return a {@link SalPort} for test.
     *
     * @return  A {@link SalPort} instance.
     */
    private SalPort getPort() {
        int idx = portListIndex;
        SalPort sport = portList.get(idx);
        idx++;
        portListIndex = (idx >= portList.size()) ? 0 : idx;

        return sport;
    }

    /**
     * Activate hosts specified by the map.
     *
     * @param vmst     A {@link VTNMacMapStatus} instance to be tested.
     * @param mapId    A {@link MacMapIdentifier} instance.
     * @param hosts    A map which contains hosts to be activated.
     * @param nwMap    A {@link NetworkMap} instance to store mapped
     *                 networks.
     * @param hostMap  A {@link HostMap} instance to store mapped hosts.
     * @throws Exception  An error occurred.
     */
    private void checkActivate(VTNMacMapStatus vmst, MacMapIdentifier mapId, HostMap hosts, NetworkMap nwMap,
            HostMap hostMap) throws Exception {
        for (Entry<MacVlan, SalPort> entry : hosts.entrySet()) {
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            long mac = mvlan.getAddress();
            int vid = mvlan.getVlanId();
            checkActivate(vmst, mapId, mac, vid, sport, nwMap, hostMap);
        }
    }

    /**
     * Activate the given host and verify results.
     *
     * @param vmst     A {@link VTNMacMapStatus} instance to be tested.
     * @param mapId    A {@link MacMapIdentifier} instance.
     * @param mac      A long value which represents the MAC address.
     * @param vid      A VLAN ID.
     * @param sport    A {@link SalPort} instance.
     *                 An arbitrary port is chosen if {@code null} is
     *                 specified.
     * @param nwMap    A {@link NetworkMap} instance to store mapped
     *                 networks.
     * @param hostMap  A map to store activated hosts.
     * @throws Exception  An error occurred.
     */
    private void checkActivate(VTNMacMapStatus vmst, MacMapIdentifier mapId, long mac, int vid, SalPort sport,
            NetworkMap nwMap, HostMap hostMap) throws Exception {
        boolean active = !vmst.hasMapping();
        MacVlan mvlan = new MacVlan(mac, vid);
        SalPort port = sport;
        if (port == null) {
            port = getPort();
        }
        PortVlan pvlan = new PortVlan(port, vid);
        assertEquals(nwMap.getNetworks().contains(pvlan), vmst.hasMapping(pvlan));
        assertNull(vmst.getDuplicate(mvlan));

        SalPort oldPort = hostMap.get(mvlan);
        PortVlan oldPv;
        if (oldPort == null) {
            oldPv = null;
            assertNull(vmst.getPortVlan(mac));
            assertNull(vmst.getPort(mvlan));
        } else {
            oldPv = new PortVlan(oldPort, vid);
            assertEquals(oldPv, vmst.getPortVlan(mac));
            assertEquals(oldPort, vmst.getPort(mvlan));
        }

        Triple<Boolean, SalPort, PortVlan> result = vmst.activate(mapId, mvlan, port);
        assertTrue(vmst.isDirty());
        assertFalse(vmst.isDirty());
        assertEquals(active, result.getLeft().booleanValue());
        assertEquals(oldPort, result.getMiddle());
        PortVlan released;
        if (oldPv == null) {
            released = null;
        } else {
            released = (nwMap.remove(oldPv)) ? oldPv : null;
        }
        assertEquals(released, result.getRight());
        hostMap.put(mvlan, port);
        hostMap.verify(vmst.toMacMapStatus());

        // null should be returned if the host is already
        // activated.
        assertNull(vmst.activate(mapId, mvlan, port));
        assertFalse(vmst.isDirty());
        hostMap.verify(vmst.toMacMapStatus());

        assertTrue(vmst.hasMapping(pvlan));
        assertNull(vmst.getDuplicate(mvlan));
        assertEquals(pvlan, vmst.getPortVlan(mac));
        assertEquals(port, vmst.getPort(mvlan));

        nwMap.add(pvlan);

        assertEquals(nwMap.getNetworks(), vmst.getNetworks());
        assertTrue(vmst.hasMapping());
    }

    /**
     * Remove all hosts on the specified network from the host map.
     *
     * @param hostMap  A map to store activated hosts.
     * @param pvlan    A {@link PortVlan} instance.
     * @return  A map which contains removed entries is returned.
     */
    private Map<MacVlan, SalPort> removeHosts(HostMap hostMap, PortVlan pvlan) {
        Map<MacVlan, SalPort> removed = new HashMap<>();
        SalPort targetPort = pvlan.getPort();
        int targetVlan = pvlan.getVlanId();
        for (Iterator<Entry<MacVlan, SalPort>> it = hostMap.entrySet().iterator(); it.hasNext();) {
            Entry<MacVlan, SalPort> entry = it.next();
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            if (mvlan.getVlanId() == targetVlan && sport.equals(targetPort)) {
                removed.put(mvlan, sport);
                it.remove();
            }
        }

        return removed;
    }

    /**
     * Remove all hosts on switch ports selected by the specified filter.
     *
     * @param hostMap  A map to store activated hosts.
     * @param filter   A {@link PortFilter} instance which determines switch
     *                 ports.
     * @return  A map which contains removed entries is returned.
     * @throws Exception  An error occurred.
     */
    private Map<MacVlan, SalPort> removeHosts(HostMap hostMap, PortFilter filter) throws Exception {
        Map<MacVlan, SalPort> removed = new HashMap<>();
        for (Iterator<Entry<MacVlan, SalPort>> it = hostMap.entrySet().iterator(); it.hasNext();) {
            Entry<MacVlan, SalPort> entry = it.next();
            MacVlan mvlan = entry.getKey();
            SalPort sport = entry.getValue();
            if (filter.accept(sport, null)) {
                removed.put(mvlan, sport);
                it.remove();
            }
        }

        return removed;
    }
}