New package handling MC-capabilities 07/114907/55
authorJoakim Törnqvist <joakim.tornqvist@smartoptics.com>
Wed, 15 Jan 2025 07:29:32 +0000 (08:29 +0100)
committerJoakim Törnqvist <joakim.tornqvist@smartoptics.com>
Fri, 7 Mar 2025 13:41:35 +0000 (14:41 +0100)
OVERVIEW

The class McCapabilityCollection simplifies finding out if a service
slot width is compatible with all nodes in the path.
MC capabilities such as slot width granularity and min/max slots are
taken into account.

The class NodeMcCapability is intended to simplify handling API input by
removing the need of extra null checks and implements yang default
values.

Example #1
----------

/**
 * Determine if a service with a slot width of 50GHz (i.e. 8 slots
 * of 6.25GHz) is compatible with the default MC capabilites of slot
 * width granularity 50GHz and min and max slots set to 1.
 */
boolean compatible() {

    CapabilityCollection collection = new McCapabilityCollection();
    collection.add(new InterfaceMcCapability("ROADM-A-SRG1", 50, 1, 1));

    //true
    return collection.isCompatibleService(6.25, 8);
}

Example #2 - Observer
---------------------

/**
 * There is an optional Observer interface. The implementing class
 * is notified about any errors when verifying if a MC interface
 * is not compatible with a service.
 * The class VoidObserver may be used if one whishes to ignore all
 * error messages.
 */
public boolean compatible() {
    CapabilityCollection collection =
            new McCapabilityCollection(new Observer() {
                @Override
                public void error(String message) {
                    System.out.println(message);
                }
            });
    collection.add(new InterfaceMcCapability("ROADM-A-SRG1", 50, 1, 1));

    return collection.isCompatibleService(6.25, 6);
}

JIRA: TRNSPRTPCE-849
Change-Id: Ie770ef84337733c15e5cf10b0ad1cca2a45d2019
Signed-off-by: Joakim Törnqvist <joakim.tornqvist@smartoptics.com>
pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/McCapability.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/NodeMcCapability.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/Observer.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/VoidObserver.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/CapabilityCollection.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapability.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapability.java [new file with mode: 0644]
pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollection.java [new file with mode: 0644]
pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapabilityTest.java [new file with mode: 0644]
pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollectionTest.java [new file with mode: 0644]

diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/McCapability.java b/pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/McCapability.java
new file mode 100644 (file)
index 0000000..1243f71
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2025 Smartoptics and others.  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.transportpce.pce.node.mccapabilities;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.math.BigDecimal;
+
+/**
+ * This interface is intended to specify methods from
+ * which the client may retrieve MC Capabilities.
+ */
+public interface McCapability {
+
+    /**
+     * Width of a slot measured in GHz.
+     */
+    @NonNull BigDecimal slotWidthGranularity();
+
+    /**
+     * Granularity of allowed center frequencies.
+     * The base frequency for this computation is 193.1 THz (G.694.1)
+     */
+    @NonNull BigDecimal centerFrequencyGranularity();
+
+    /**
+     * Minimum number of slots permitted to be joined together to form a media channel.
+     * Must be less than or equal to the max-slots.
+     */
+    int minSlots();
+
+    /**
+     * Maximum number of slots permitted to be joined together to form a media channel.
+     * Must be greater than or equal to the min-slots.
+     */
+    int maxSlots();
+
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/NodeMcCapability.java b/pce/src/main/java/org/opendaylight/transportpce/pce/node/mccapabilities/NodeMcCapability.java
new file mode 100644 (file)
index 0000000..195bebf
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.node.mccapabilities;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import java.math.BigDecimal;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev250115.mc.capabilities.McCapabilities;
+
+/**
+ * The primary purpose of this POJO is to specify default values in regard to MC-capabilities
+ * and thus saving the client from the hassle of implementing null-checks.
+ */
+public class NodeMcCapability implements McCapability {
+
+    private BigDecimal slotWidthGranularityGHz = BigDecimal.valueOf(50);
+
+    private BigDecimal centerFrequencyGranularityGHz = BigDecimal.valueOf(50);
+
+    private int minSlots = 1;
+
+    private int maxSlots = 1;
+
+    /**
+     * Create a NodeMcCapability object with default values defined in the yang model:
+     * - CenterFrequencyGranularity = 50(GHz).
+     * - SlotWidthFrequencyGranularity = 50(GHz).
+     * - min and max slots set to 1.
+     */
+    public NodeMcCapability() {
+    }
+
+    public NodeMcCapability(BigDecimal slotWidthGranularityGHz, BigDecimal centerFrequencyGranularityGHz, int minSlots,
+            int maxSlots) {
+
+        if (slotWidthGranularityGHz != null) {
+            this.slotWidthGranularityGHz = slotWidthGranularityGHz;
+        }
+        if (centerFrequencyGranularityGHz != null) {
+            this.centerFrequencyGranularityGHz = centerFrequencyGranularityGHz;
+        }
+
+        this.minSlots = minSlots;
+        this.maxSlots = maxSlots;
+    }
+
+    /**
+     * Create an object using the data in mcCapabilities. If a piece of data is null
+     * in mcCapabilities, the default values for this class is used.
+     *
+     * @see NodeMcCapability#NodeMcCapability()
+     */
+    public NodeMcCapability(McCapabilities mcCapabilities) {
+        this();
+
+        if (mcCapabilities != null) {
+            if (mcCapabilities.getSlotWidthGranularity() != null) {
+                this.slotWidthGranularityGHz = mcCapabilities.getSlotWidthGranularity().getValue().decimalValue();
+            }
+
+            if (mcCapabilities.getCenterFreqGranularity() != null) {
+                this.centerFrequencyGranularityGHz = mcCapabilities
+                        .getCenterFreqGranularity().getValue().decimalValue();
+            }
+
+            if (mcCapabilities.getMinSlots() != null) {
+                this.minSlots = mcCapabilities.getMinSlots().intValue();
+            }
+
+            if (mcCapabilities.getMaxSlots() != null) {
+                this.maxSlots = mcCapabilities.getMaxSlots().intValue();
+            }
+        }
+    }
+
+    @Override
+    public @NonNull BigDecimal slotWidthGranularity() {
+        return slotWidthGranularityGHz;
+    }
+
+    @Override
+    public @NonNull BigDecimal centerFrequencyGranularity() {
+        return centerFrequencyGranularityGHz;
+    }
+
+    @Override
+    public int minSlots() {
+        return minSlots;
+    }
+
+    @Override
+    public int maxSlots() {
+        return maxSlots;
+    }
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/Observer.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/Observer.java
new file mode 100644 (file)
index 0000000..59414ef
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.observer;
+
+public interface Observer {
+
+    /**
+     * Send an error message to the observer.
+     */
+    void error(String message);
+
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/VoidObserver.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/observer/VoidObserver.java
new file mode 100644 (file)
index 0000000..1cc403b
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.observer;
+
+public class VoidObserver implements Observer {
+
+    @Override
+    public void error(String message) {
+        //Don't do anything.
+    }
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/CapabilityCollection.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/CapabilityCollection.java
new file mode 100644 (file)
index 0000000..a8002d3
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+public interface CapabilityCollection {
+
+    /**
+     * Add a MCCapability to this collection.
+     */
+    boolean add(McCapability mcCapability);
+
+    /**
+     * Determine if this MC interface is compatible with the required
+     * service frequency width (i.e. slotWidthGranularityGHz x slotCount).
+     *
+     * @param slotWidthGranularityGHz Typically the frequency width of each slot in a 768 grid.
+     * @param slotCount Typically the nr of slots the service requires on a 768 slot grid.
+     */
+    boolean isCompatibleService(double slotWidthGranularityGHz, int slotCount);
+
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapability.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapability.java
new file mode 100644 (file)
index 0000000..313e162
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+import java.math.BigDecimal;
+import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.transportpce.pce.spectrum.observer.Observer;
+import org.opendaylight.transportpce.pce.spectrum.observer.VoidObserver;
+
+public class InterfaceMcCapability implements McCapability {
+
+    private final String node;
+
+    private final BigDecimal slotWidthGranularity;
+
+    private final int minSlots;
+
+    private final int maxSlots;
+
+    public InterfaceMcCapability(BigDecimal slotWidthGranularity, int minSlots, int maxSlots) {
+        this("Unknown node", slotWidthGranularity, minSlots, maxSlots);
+    }
+
+    public InterfaceMcCapability(@NonNull String node, BigDecimal slotWidthGranularity, int minSlots, int maxSlots) {
+        this.node = node;
+        this.slotWidthGranularity = slotWidthGranularity;
+        this.minSlots = minSlots;
+        this.maxSlots = maxSlots;
+    }
+
+    public InterfaceMcCapability(double slotWidthGranularity, int minSlots, int maxSlots) {
+        this(BigDecimal.valueOf(slotWidthGranularity), minSlots, maxSlots);
+    }
+
+    public InterfaceMcCapability(@NonNull String node, double slotWidthGranularity, int minSlots, int maxSlots) {
+        this(node, BigDecimal.valueOf(slotWidthGranularity), minSlots, maxSlots);
+    }
+
+    @Override
+    public boolean isCompatibleWithServiceFrequency(BigDecimal requiredFrequencyWidthGHz) {
+        return isCompatibleWithServiceFrequency(requiredFrequencyWidthGHz, new VoidObserver());
+    }
+
+    @Override
+    public boolean isCompatibleWithServiceFrequency(BigDecimal requiredFrequencyWidthGHz, Observer observer) {
+
+        BigDecimal quotient;
+        try {
+            quotient = requiredFrequencyWidthGHz.divide(slotWidthGranularity);
+        } catch (ArithmeticException e) {
+            return false;
+        }
+
+        BigDecimal remainder = requiredFrequencyWidthGHz.remainder(slotWidthGranularity);
+
+        if (remainder.compareTo(BigDecimal.ZERO) == 0
+                && quotient.compareTo(BigDecimal.valueOf(minSlots)) >= 0
+                && quotient.compareTo(BigDecimal.valueOf(maxSlots)) <= 0) {
+
+            return true;
+        }
+
+        observer.error(String.format("Cannot fit a service with %sGHz on node %s, "
+                        + "with slot width granularity %s min slots %s and max slots %s",
+                requiredFrequencyWidthGHz,
+                node,
+                slotWidthGranularity,
+                minSlots,
+                maxSlots));
+
+        return false;
+    }
+
+    @Override
+    public boolean isCompatibleWithServiceFrequency(double requiredFrequencyWidthGHz) {
+        return isCompatibleWithServiceFrequency(BigDecimal.valueOf(requiredFrequencyWidthGHz), new VoidObserver());
+    }
+
+    @Override
+    public boolean isCompatibleWithServiceFrequency(double requiredFrequencyWidthGHz, Observer observer) {
+        return isCompatibleWithServiceFrequency(BigDecimal.valueOf(requiredFrequencyWidthGHz), observer);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (!(object instanceof InterfaceMcCapability interfaceMcCapability)) {
+            return false;
+        }
+        return node.equals(interfaceMcCapability.node)
+                && minSlots == interfaceMcCapability.minSlots
+                && maxSlots == interfaceMcCapability.maxSlots
+                && Objects.equals(slotWidthGranularity, interfaceMcCapability.slotWidthGranularity);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(slotWidthGranularity, minSlots, maxSlots);
+    }
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapability.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapability.java
new file mode 100644 (file)
index 0000000..17dacb4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+import java.math.BigDecimal;
+import org.opendaylight.transportpce.pce.spectrum.observer.Observer;
+
+public interface McCapability {
+
+    /**
+     * Determine if this MC interface is compatible with the required
+     * service frequency width.
+     */
+    boolean isCompatibleWithServiceFrequency(BigDecimal requiredFrequencyWidthGHz);
+
+    /**
+     * Determine if this MC interface is compatible with the required
+     * service frequency width.
+     * The observer is notified about errors.
+     */
+    boolean isCompatibleWithServiceFrequency(BigDecimal requiredFrequencyWidthGHz, Observer observer);
+
+    /**
+     * Determine if this MC interface is compatible with the required
+     * service frequency width.
+     *
+     * @see McCapability#isCompatibleWithServiceFrequency(BigDecimal)
+     */
+    boolean isCompatibleWithServiceFrequency(double requiredFrequencyWidthGHz);
+
+    /**
+     * Determine if this MC interface is compatible with the required
+     * service frequency width.
+     * The observer is notified about errors.
+     *
+     * @see McCapability#isCompatibleWithServiceFrequency(BigDecimal)
+     * @see McCapability#isCompatibleWithServiceFrequency(BigDecimal, Observer)
+     */
+    boolean isCompatibleWithServiceFrequency(double requiredFrequencyWidthGHz, Observer observer);
+}
diff --git a/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollection.java b/pce/src/main/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollection.java
new file mode 100644 (file)
index 0000000..2e9acde
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+import java.math.BigDecimal;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import org.opendaylight.transportpce.pce.spectrum.observer.Observer;
+import org.opendaylight.transportpce.pce.spectrum.observer.VoidObserver;
+
+public class McCapabilityCollection implements CapabilityCollection {
+
+    private final Set<McCapability> slots = new LinkedHashSet<>();
+
+    private final Observer observer;
+
+    public McCapabilityCollection() {
+        this(new VoidObserver());
+    }
+
+    public McCapabilityCollection(Observer observer) {
+        this.observer = observer;
+    }
+
+    @Override
+    public boolean add(McCapability mcCapability) {
+        return slots.add(mcCapability);
+    }
+
+    @Override
+    public boolean isCompatibleService(double slotWidthGranularityGHz, int slotCount) {
+        BigDecimal widthGHz = BigDecimal.valueOf(slotWidthGranularityGHz).multiply(BigDecimal.valueOf(slotCount));
+
+        for (McCapability mcCapability : slots) {
+            if (!mcCapability.isCompatibleWithServiceFrequency(widthGHz, observer)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapabilityTest.java b/pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/InterfaceMcCapabilityTest.java
new file mode 100644 (file)
index 0000000..324fef0
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.math.BigDecimal;
+import org.junit.Test;
+import org.opendaylight.transportpce.pce.spectrum.observer.Observer;
+
+public class InterfaceMcCapabilityTest {
+
+    Observer observer = mock(Observer.class);
+
+    @Test
+    public void slotWidthEqualToServiceWidth() {
+        McCapability slot = new InterfaceMcCapability(50, 1, 1);
+
+        assertTrue(slot.isCompatibleWithServiceFrequency(50));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertTrue(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void slotWidthIsLessThanServiceWidth() {
+        McCapability slot = new InterfaceMcCapability(6.25, 1, 8);
+
+        assertTrue(slot.isCompatibleWithServiceFrequency(50));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertTrue(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void slotWidthIsLessThanServiceWidthTwo() {
+        McCapability slot = new InterfaceMcCapability(3.125, 1, 16);
+
+        assertTrue(slot.isCompatibleWithServiceFrequency(50));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertTrue(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void slotWidthIsLessThanServiceWidthThree() {
+        McCapability slot = new InterfaceMcCapability(4.6875, 1, 16);
+
+        assertTrue(slot.isCompatibleWithServiceFrequency(37.5));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(37.5)));
+        assertTrue(slot.isCompatibleWithServiceFrequency(37.5, observer));
+        assertTrue(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(37.5), observer));
+    }
+
+    @Test
+    public void incompatibleGranularityIsFalse() {
+        McCapability slot = new InterfaceMcCapability(4.6875, 1, 16);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(50));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertFalse(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void incompatibleGranularityIsFalseTwo() {
+        McCapability slot = new InterfaceMcCapability(6.30, 1, 8);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(50));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertFalse(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void incompatibleGranularityIsFalseThree() {
+        McCapability slot = new InterfaceMcCapability(6.25, 1, 6);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(50));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertFalse(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void minSlotIsTooHigh() {
+        McCapability slot = new InterfaceMcCapability(6.25, 8, 16);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(37.5));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(37.5)));
+        assertFalse(slot.isCompatibleWithServiceFrequency(37.5, observer));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(37.5), observer));
+    }
+
+    @Test
+    public void slotWidthGranularityIsTooHigh() {
+        McCapability slot = new InterfaceMcCapability(100, 1, 1);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(50));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50)));
+        assertFalse(slot.isCompatibleWithServiceFrequency(50, observer));
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observer));
+    }
+
+    @Test
+    public void testUnknownNodesEquals() {
+        McCapability slotOne = new InterfaceMcCapability(50, 1, 1);
+        McCapability slotTwo = new InterfaceMcCapability(50, 1, 1);
+
+        assertTrue(slotOne.equals(slotTwo));
+    }
+
+    @Test
+    public void testKnownNodesEquals() {
+        McCapability slotOne = new InterfaceMcCapability("A", 50, 1, 1);
+        McCapability slotTwo = new InterfaceMcCapability("A", 50, 1, 1);
+
+        assertTrue(slotOne.equals(slotTwo));
+    }
+
+    @Test
+    public void testKnownNodesNotEquals() {
+        McCapability slotOne = new InterfaceMcCapability("A", 50, 1, 1);
+        McCapability slotTwo = new InterfaceMcCapability("B", 50, 1, 1);
+
+        assertFalse(slotOne.equals(slotTwo));
+    }
+
+    @Test
+    public void observerIsNotifiedFrequencyAsDoubleValue() {
+        McCapability slot = new InterfaceMcCapability(100, 1, 1);
+
+        Observer observerMock = mock(Observer.class);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(50, observerMock));
+
+        verify(observerMock, times(1)).error(anyString());
+    }
+
+
+    @Test
+    public void observerIsNotifiedFrequencyAsBigDecimalValue() {
+        McCapability slot = new InterfaceMcCapability(100, 1, 1);
+
+        Observer observerMock = mock(Observer.class);
+
+        assertFalse(slot.isCompatibleWithServiceFrequency(BigDecimal.valueOf(50), observerMock));
+
+        verify(observerMock, times(1)).error(anyString());
+    }
+}
diff --git a/pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollectionTest.java b/pce/src/test/java/org/opendaylight/transportpce/pce/spectrum/slot/McCapabilityCollectionTest.java
new file mode 100644 (file)
index 0000000..a601de7
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  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.transportpce.pce.spectrum.slot;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.jupiter.api.Test;
+
+class McCapabilityCollectionTest {
+
+    @Test
+    void add() {
+
+        McCapability mcCapability = mock(McCapability.class);
+
+        CapabilityCollection slotCollection = new McCapabilityCollection();
+        assertTrue(slotCollection.add(mcCapability));
+    }
+
+    @Test
+    void emptyCollectionIsCompatible() {
+
+        CapabilityCollection slotCollection = new McCapabilityCollection();
+        assertTrue(slotCollection.isCompatibleService(6.25, 8));
+
+    }
+
+    @Test
+    void isCompatibleService() {
+
+        McCapability mcCapability = mock(McCapability.class);
+        when(mcCapability.isCompatibleWithServiceFrequency(any(), any())).thenReturn(true);
+
+        CapabilityCollection slotCollection = new McCapabilityCollection();
+        slotCollection.add(mcCapability);
+        assertTrue(slotCollection.isCompatibleService(6.25, 8));
+
+    }
+
+    @Test
+    void isNotCompatibleService() {
+
+        McCapability mcCapability = mock(McCapability.class);
+        when(mcCapability.isCompatibleWithServiceFrequency(any())).thenReturn(false);
+
+        CapabilityCollection slotCollection = new McCapabilityCollection();
+        slotCollection.add(mcCapability);
+        assertFalse(slotCollection.isCompatibleService(6.25, 8));
+
+    }
+
+    @Test
+    void multipleWhereOneIsFalseReturnsFalse() {
+
+        CapabilityCollection slotCollection = new McCapabilityCollection();
+
+        McCapability mcCapabilityOne = mock(McCapability.class);
+        when(mcCapabilityOne.isCompatibleWithServiceFrequency(any(), any())).thenReturn(true);
+        assertTrue(slotCollection.add(mcCapabilityOne));
+
+        McCapability mcCapabilityTwo = mock(McCapability.class);
+        when(mcCapabilityTwo.isCompatibleWithServiceFrequency(any(), any())).thenReturn(true);
+        assertTrue(slotCollection.add(mcCapabilityTwo));
+
+        assertTrue(slotCollection.isCompatibleService(6.25, 8));
+
+        McCapability mcCapabilityThree = mock(McCapability.class);
+        when(mcCapabilityThree.isCompatibleWithServiceFrequency(any())).thenReturn(false);
+        assertTrue(slotCollection.add(mcCapabilityThree));
+
+        assertFalse(slotCollection.isCompatibleService(6.25, 8));
+
+    }
+
+}