
package org.onosproject.pcelabelstore;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import org.onosproject.incubator.net.resource.label.DefaultLabelResource;
import org.onosproject.incubator.net.resource.label.LabelResource;
import org.onosproject.incubator.net.resource.label.LabelResourceId;
import org.onosproject.incubator.net.tunnel.TunnelId;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.pcelabelstore.api.LspLocalLabelInfo;
import org.onosproject.pcelabelstore.util.TestStorageService;

/**
 * Unit tests for DistributedPceStore class.
 */
public class DistributedPceLabelStoreTest {

    private DistributedPceLabelStore distrPceStore;
    private DeviceId deviceId1 = DeviceId.deviceId("foo");
    private DeviceId deviceId2 = DeviceId.deviceId("goo");
    private DeviceId deviceId3 = DeviceId.deviceId("yaa");
    private DeviceId deviceId4 = DeviceId.deviceId("zoo");
    private LabelResourceId labelId1 = LabelResourceId.labelResourceId(1);
    private LabelResourceId labelId2 = LabelResourceId.labelResourceId(2);
    private LabelResourceId labelId3 = LabelResourceId.labelResourceId(3);
    private LabelResourceId labelId4 = LabelResourceId.labelResourceId(4);
    private PortNumber portNumber1 = PortNumber.portNumber(1);
    private PortNumber portNumber2 = PortNumber.portNumber(2);
    private PortNumber portNumber3 = PortNumber.portNumber(3);
    private PortNumber portNumber4 = PortNumber.portNumber(4);
    private ConnectPoint srcConnectionPoint1 = new ConnectPoint(deviceId1, portNumber1);
    private ConnectPoint dstConnectionPoint2 = new ConnectPoint(deviceId2, portNumber2);
    private ConnectPoint srcConnectionPoint3 = new ConnectPoint(deviceId3, portNumber3);
    private ConnectPoint dstConnectionPoint4 = new ConnectPoint(deviceId4, portNumber4);
    private LabelResource labelResource1 = new DefaultLabelResource(deviceId1, labelId1);
    private LabelResource labelResource2 = new DefaultLabelResource(deviceId2, labelId2);
    private LabelResource labelResource3 = new DefaultLabelResource(deviceId3, labelId3);
    private LabelResource labelResource4 = new DefaultLabelResource(deviceId4, labelId4);
    private Link link1;
    private Link link2;
    private List<LabelResource> labelList1 = new LinkedList<>();
    private List<LabelResource> labelList2 = new LinkedList<>();
    private TunnelId tunnelId1 = TunnelId.valueOf("1");
    private TunnelId tunnelId2 = TunnelId.valueOf("2");
    private TunnelId tunnelId3 = TunnelId.valueOf("3");
    private TunnelId tunnelId4 = TunnelId.valueOf("4");

    List<LspLocalLabelInfo> lspLocalLabelInfoList1 = new LinkedList<>();
    List<LspLocalLabelInfo> lspLocalLabelInfoList2 = new LinkedList<>();

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
       distrPceStore = new DistributedPceLabelStore();
       // initialization
       distrPceStore.storageService = new TestStorageService();
       distrPceStore.activate();

       // Initialization of member variables
       link1 = DefaultLink.builder()
                          .providerId(new ProviderId("eth", "1"))
                          .annotations(DefaultAnnotations.builder().set("key1", "yahoo").build())
                          .src(srcConnectionPoint1)
                          .dst(dstConnectionPoint2)
                          .type(Link.Type.DIRECT)
                          .state(Link.State.ACTIVE)
                          .build();
       link2 = DefaultLink.builder()
                          .providerId(new ProviderId("mac", "2"))
                          .annotations(DefaultAnnotations.builder().set("key2", "google").build())
                          .src(srcConnectionPoint3)
                          .dst(dstConnectionPoint4)
                          .type(Link.Type.DIRECT)
                          .state(Link.State.ACTIVE)
                          .build();
       labelList1.add(labelResource1);
       labelList1.add(labelResource2);
       labelList2.add(labelResource3);
       labelList2.add(labelResource4);

       // Create pceccTunnelInfo1
       DeviceId deviceId1 = DeviceId.deviceId("foo");
       LabelResourceId inLabelId1 = LabelResourceId.labelResourceId(1);
       LabelResourceId outLabelId1 = LabelResourceId.labelResourceId(2);

       LspLocalLabelInfo lspLocalLabel1 = DefaultLspLocalLabelInfo.builder()
               .deviceId(deviceId1)
               .inLabelId(inLabelId1)
               .outLabelId(outLabelId1)
               .build();
       lspLocalLabelInfoList1.add(lspLocalLabel1);
       distrPceStore.addTunnelInfo(tunnelId1, lspLocalLabelInfoList1);

       // Create pceccTunnelInfo2
       DeviceId deviceId2 = DeviceId.deviceId("foo");
       LabelResourceId inLabelId2 = LabelResourceId.labelResourceId(3);
       LabelResourceId outLabelId2 = LabelResourceId.labelResourceId(4);

       LspLocalLabelInfo lspLocalLabel2 = DefaultLspLocalLabelInfo.builder()
               .deviceId(deviceId2)
               .inLabelId(inLabelId2)
               .outLabelId(outLabelId2)
               .build();
       lspLocalLabelInfoList2.add(lspLocalLabel2);
       distrPceStore.addTunnelInfo(tunnelId2, lspLocalLabelInfoList2);
    }

    @After
    public void tearDown() throws Exception {
    }

    /**
     * Checks the operation of addGlobalNodeLabel() method.
     */
    @Test
    public void testAddGlobalNodeLabel() {
        // add device with label
        distrPceStore.addGlobalNodeLabel(deviceId1, labelId1);
        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId1), is(true));
        assertThat(distrPceStore.getGlobalNodeLabel(deviceId1), is(labelId1));
        distrPceStore.addGlobalNodeLabel(deviceId2, labelId2);
        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId2), is(true));
        assertThat(distrPceStore.getGlobalNodeLabel(deviceId2), is(labelId2));
    }

    /**
     * Checks the operation of addAdjLabel() method.
     */
    @Test
    public void testAddAdjLabel() {
        // link with list of labels
        distrPceStore.addAdjLabel(link1, labelId1);
        assertThat(distrPceStore.existsAdjLabel(link1), is(true));
        assertThat(distrPceStore.getAdjLabel(link1), is(labelId1));
        distrPceStore.addAdjLabel(link2, labelId2);
        assertThat(distrPceStore.existsAdjLabel(link2), is(true));
        assertThat(distrPceStore.getAdjLabel(link2), is(labelId2));
    }

    /**
     * Checks the operation of addTunnelInfo() method.
     */
    @Test
    public void testAddTunnelInfo() {
        // TunnelId with device label store information
        distrPceStore.addTunnelInfo(tunnelId1, lspLocalLabelInfoList1);
        assertThat(distrPceStore.existsTunnelInfo(tunnelId1), is(true));
        assertThat(distrPceStore.getTunnelInfo(tunnelId1), is(lspLocalLabelInfoList1));
        distrPceStore.addTunnelInfo(tunnelId2, lspLocalLabelInfoList2);
        assertThat(distrPceStore.existsTunnelInfo(tunnelId2), is(true));
        assertThat(distrPceStore.getTunnelInfo(tunnelId2), is(lspLocalLabelInfoList2));
    }

    /**
     * Checks the operation of existsGlobalNodeLabel() method.
     */
    @Test
    public void testExistsGlobalNodeLabel() {
        testAddGlobalNodeLabel();

        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId1), is(true));
        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId2), is(true));
        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId3), is(false));
        assertThat(distrPceStore.existsGlobalNodeLabel(deviceId4), is(false));
    }

    /**
     * Checks the operation of existsAdjLabel() method.
     */
    @Test
    public void testExistsAdjLabel() {
        testAddAdjLabel();

        assertThat(distrPceStore.existsAdjLabel(link1), is(true));
        assertThat(distrPceStore.existsAdjLabel(link2), is(true));
    }

    /**
     * Checks the operation of existsTunnelInfo() method.
     */
    @Test
    public void testExistsTunnelInfo() {
        testAddTunnelInfo();

        assertThat(distrPceStore.existsTunnelInfo(tunnelId1), is(true));
        assertThat(distrPceStore.existsTunnelInfo(tunnelId2), is(true));
        assertThat(distrPceStore.existsTunnelInfo(tunnelId3), is(false));
        assertThat(distrPceStore.existsTunnelInfo(tunnelId4), is(false));
    }

    /**
     * Checks the operation of getGlobalNodeLabelCount() method.
     */
    @Test
    public void testGetGlobalNodeLabelCount() {
        testAddGlobalNodeLabel();

        assertThat(distrPceStore.getGlobalNodeLabelCount(), is(2));
    }

    /**
     * Checks the operation of getAdjLabelCount() method.
     */
    @Test
    public void testGetAdjLabelCount() {
        testAddAdjLabel();

        assertThat(distrPceStore.getAdjLabelCount(), is(2));
    }

    /**
     * Checks the operation of getTunnelInfoCount() method.
     */
    @Test
    public void testGetTunnelInfoCount() {
        testAddTunnelInfo();

        assertThat(distrPceStore.getTunnelInfoCount(), is(2));
    }

    /**
     * Checks the operation of getGlobalNodeLabels() method.
     */
    @Test
    public void testGetGlobalNodeLabels() {
        testAddGlobalNodeLabel();

        Map<DeviceId, LabelResourceId> nodeLabelMap = distrPceStore.getGlobalNodeLabels();
        assertThat(nodeLabelMap, is(notNullValue()));
        assertThat(nodeLabelMap.isEmpty(), is(false));
        assertThat(nodeLabelMap.size(), is(2));
    }

    /**
     * Checks the operation of getAdjLabels() method.
     */
    @Test
    public void testGetAdjLabels() {
        testAddAdjLabel();

        Map<Link, LabelResourceId> adjLabelMap = distrPceStore.getAdjLabels();
        assertThat(adjLabelMap, is(notNullValue()));
        assertThat(adjLabelMap.isEmpty(), is(false));
        assertThat(adjLabelMap.size(), is(2));
    }

    /**
     * Checks the operation of getTunnelInfos() method.
     */
    @Test
    public void testGetTunnelInfos() {
        testAddTunnelInfo();

        Map<TunnelId, List<LspLocalLabelInfo>> tunnelInfoMap = distrPceStore.getTunnelInfos();
        assertThat(tunnelInfoMap, is(notNullValue()));
        assertThat(tunnelInfoMap.isEmpty(), is(false));
        assertThat(tunnelInfoMap.size(), is(2));
    }

    /**
     * Checks the operation of getGlobalNodeLabel() method.
     */
    @Test
    public void testGetGlobalNodeLabel() {
        testAddGlobalNodeLabel();

        // deviceId1 with labelId1
        assertThat(deviceId1, is(notNullValue()));
        assertThat(distrPceStore.getGlobalNodeLabel(deviceId1), is(labelId1));

        // deviceId2 with labelId2
        assertThat(deviceId2, is(notNullValue()));
        assertThat(distrPceStore.getGlobalNodeLabel(deviceId2), is(labelId2));
    }

    /**
     * Checks the operation of getAdjLabel() method.
     */
    @Test
    public void testGetAdjLabel() {
        testAddAdjLabel();

        // link1 with labels
        assertThat(link1, is(notNullValue()));
        assertThat(distrPceStore.getAdjLabel(link1), is(labelId1));

        // link2 with labels
        assertThat(link2, is(notNullValue()));
        assertThat(distrPceStore.getAdjLabel(link2), is(labelId2));
    }

    /**
     * Checks the operation of getTunnelInfo() method.
     */
    @Test
    public void testGetTunnelInfo() {
        testAddTunnelInfo();

        // tunnelId1 with device label store info
        assertThat(tunnelId1, is(notNullValue()));
        assertThat(distrPceStore.getTunnelInfo(tunnelId1), is(lspLocalLabelInfoList1));

        // tunnelId2 with device label store info
        assertThat(tunnelId2, is(notNullValue()));
        assertThat(distrPceStore.getTunnelInfo(tunnelId2), is(lspLocalLabelInfoList2));
    }

    /**
     * Checks the operation of removeGlobalNodeLabel() method.
     */
    @Test
    public void testRemoveGlobalNodeLabel() {
        testAddGlobalNodeLabel();

        assertThat(distrPceStore.removeGlobalNodeLabel(deviceId1), is(true));
        assertThat(distrPceStore.removeGlobalNodeLabel(deviceId2), is(true));
    }

    /**
     * Checks the operation of removeAdjLabel() method.
     */
    @Test
    public void testRemoveAdjLabel() {
        testAddAdjLabel();

        assertThat(distrPceStore.removeAdjLabel(link1), is(true));
        assertThat(distrPceStore.removeAdjLabel(link2), is(true));
    }

    /**
     * Checks the operation of removeTunnelInfo() method.
     */
    @Test
    public void testRemoveTunnelInfo() {
        testAddTunnelInfo();

        assertThat(distrPceStore.removeTunnelInfo(tunnelId1), is(true));
        assertThat(distrPceStore.removeTunnelInfo(tunnelId2), is(true));
    }
}
