From 4d8a61e9ac4a45d469ce4c441f6fe058f98e44be Mon Sep 17 00:00:00 2001
From: Andriy Kokhan <andriy.kokhan@plvision.eu>
Date: Sun, 17 Sep 2023 16:25:45 +0300
Subject: [PATCH] Added FDB basic UTs (#199)

Signed-off-by: Andriy Kokhan <andriy.kokhan@plvision.eu>
Signed-off-by: selldinesh <dinesh.sellappan@keysight.com>
---
 .github/workflows/sc-client-server-deb10.yml |  4 +-
 .github/workflows/sc-client-server-deb11.yml |  4 +-
 .github/workflows/sc-standalone-deb10.yml    |  7 +-
 .github/workflows/sc-standalone-deb11.yml    |  7 +-
 common/sai_npu.py                            | 12 +--
 tests/ut/test_fdb_ut.py                      | 91 ++++++++++++++++++++
 6 files changed, 112 insertions(+), 13 deletions(-)
 create mode 100644 tests/ut/test_fdb_ut.py

diff --git a/.github/workflows/sc-client-server-deb10.yml b/.github/workflows/sc-client-server-deb10.yml
index 53bfa247..0f04fe09 100644
--- a/.github/workflows/sc-client-server-deb10.yml
+++ b/.github/workflows/sc-client-server-deb10.yml
@@ -156,8 +156,10 @@ jobs:
 
     - name: Run functional test cases
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v -k "test_l2_basic"
+    - name: Run unit tests
+      run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v ut/test_acl_ut.py ut/test_bridge_ut.py ut/test_vrf_ut.py ut/test_port_ut.py ut/test_fdb_ut.py
     - name: Run unit tests
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v -k \
-           "test_acl_ut or test_bridge_ut or (test_switch_ut and not sai_map_list_t) or test_vrf_ut or test_port_ut.py"
+           "test_switch_ut and not sai_map_list_t"
     - name: Run thift data-driven tests
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v test_l2_basic_dd.py
diff --git a/.github/workflows/sc-client-server-deb11.yml b/.github/workflows/sc-client-server-deb11.yml
index 389b3342..5246b189 100644
--- a/.github/workflows/sc-client-server-deb11.yml
+++ b/.github/workflows/sc-client-server-deb11.yml
@@ -155,8 +155,10 @@ jobs:
 
     - name: Run functional test cases
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v -k "test_l2_basic"
+    - name: Run unit tests
+      run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v ut/test_acl_ut.py ut/test_bridge_ut.py ut/test_vrf_ut.py ut/test_port_ut.py ut/test_fdb_ut.py
     - name: Run unit tests
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v -k \
-           "test_acl_ut or test_bridge_ut or (test_switch_ut and not sai_map_list_t) or test_vrf_ut or test_port_ut.py"
+           "test_switch_ut and not sai_map_list_t"
     - name: Run thift data-driven tests
       run: ./exec.sh --no-tty -i client pytest --testbed=saivs_client_server -v test_l2_basic_dd.py
diff --git a/.github/workflows/sc-standalone-deb10.yml b/.github/workflows/sc-standalone-deb10.yml
index 0e533969..93e86819 100644
--- a/.github/workflows/sc-standalone-deb10.yml
+++ b/.github/workflows/sc-standalone-deb10.yml
@@ -72,8 +72,9 @@ jobs:
     - name: Run sairedis tests
       run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k "test_sairec"
     - name: Run unit tests
-      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k \
-           "test_acl_ut or test_bridge_ut or (test_switch_ut and not sai_map_list_t) or test_vrf_ut or test_port_ut.py"
+      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v ut/test_acl_ut.py ut/test_bridge_ut.py ut/test_vrf_ut.py ut/test_port_ut.py ut/test_fdb_ut.py
+    - name: Run unit tests
+      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k "test_switch_ut and not sai_map_list_t"
     - name: Run data-driven tests
       run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v test_l2_basic_dd.py
     - name: Run API tests
@@ -94,7 +95,7 @@ jobs:
     - name: Run thift data-driven tests
       run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v test_l2_basic_dd.py
     - name: Run thrift unit tests
-      run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v ut/test_vrf_ut.py ut/test_bridge_ut.py ut/test_acl_ut.py
+      run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v ut/test_vrf_ut.py ut/test_bridge_ut.py ut/test_acl_ut.py ut/test_fdb_ut.py
     - name: Run thrift unit tests
       run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v -k \
            "(test_switch_ut and not sai_map_list_t and not sai_system_port_config_list_t) or (test_port_ut and not sai_map_list_t)"
diff --git a/.github/workflows/sc-standalone-deb11.yml b/.github/workflows/sc-standalone-deb11.yml
index a8c1945d..6e0b38fa 100644
--- a/.github/workflows/sc-standalone-deb11.yml
+++ b/.github/workflows/sc-standalone-deb11.yml
@@ -72,8 +72,9 @@ jobs:
     - name: Run sairedis tests
       run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k "test_sairec"
     - name: Run unit tests
-      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k \
-           "test_acl_ut or test_bridge_ut or (test_switch_ut and not sai_map_list_t) or test_vrf_ut or test_port_ut.py"
+      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v ut/test_acl_ut.py ut/test_bridge_ut.py ut/test_vrf_ut.py ut/test_port_ut.py ut/test_fdb_ut.py
+    - name: Run unit tests
+      run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v -k "test_switch_ut and not sai_map_list_t"
     - name: Run data-driven tests
       run: ./exec.sh --no-tty pytest --testbed=saivs_standalone -v test_l2_basic_dd.py
     - name: Run API tests
@@ -94,7 +95,7 @@ jobs:
     - name: Run thift data-driven tests
       run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v test_l2_basic_dd.py
     - name: Run thrift unit tests
-      run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v ut/test_vrf_ut.py ut/test_bridge_ut.py ut/test_acl_ut.py
+      run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v ut/test_vrf_ut.py ut/test_bridge_ut.py ut/test_acl_ut.py ut/test_fdb_ut.py
     - name: Run thrift unit tests
       run: ./exec.sh --no-tty -s thrift pytest --testbed=saivs_thrift_standalone -v -k \
            "(test_switch_ut and not sai_map_list_t and not sai_system_port_config_list_t) or (test_port_ut and not sai_map_list_t)"
diff --git a/common/sai_npu.py b/common/sai_npu.py
index cf364876..65805dd8 100644
--- a/common/sai_npu.py
+++ b/common/sai_npu.py
@@ -100,8 +100,9 @@ def reset(self):
         attr = []
         self.init(attr)
 
-    def create_fdb(self, vlan_oid, mac, bp_oid, action="SAI_PACKET_ACTION_FORWARD"):
-        self.create('SAI_OBJECT_TYPE_FDB_ENTRY:' + json.dumps(
+    def create_fdb(self, vlan_oid, mac, bp_oid, entry_type="SAI_FDB_ENTRY_TYPE_STATIC", action="SAI_PACKET_ACTION_FORWARD", do_assert=True):
+        return self.create(
+                   'SAI_OBJECT_TYPE_FDB_ENTRY:' + json.dumps(
                        {
                            "bvid"      : vlan_oid,
                            "mac"       : mac,
@@ -109,13 +110,14 @@ def create_fdb(self, vlan_oid, mac, bp_oid, action="SAI_PACKET_ACTION_FORWARD"):
                        }
                    ),
                    [
-                       "SAI_FDB_ENTRY_ATTR_TYPE",           "SAI_FDB_ENTRY_TYPE_STATIC",
+                       "SAI_FDB_ENTRY_ATTR_TYPE",           entry_type,
                        "SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_oid,
                        "SAI_FDB_ENTRY_ATTR_PACKET_ACTION",  action
-                   ])
+                   ],
+                   do_assert)
 
     def remove_fdb(self, vlan_oid, mac, do_assert=True):
-        self.remove('SAI_OBJECT_TYPE_FDB_ENTRY:' + json.dumps(
+        return self.remove('SAI_OBJECT_TYPE_FDB_ENTRY:' + json.dumps(
                        {
                            "bvid"      : vlan_oid,
                            "mac"       : mac,
diff --git a/tests/ut/test_fdb_ut.py b/tests/ut/test_fdb_ut.py
new file mode 100644
index 00000000..46b4ecd3
--- /dev/null
+++ b/tests/ut/test_fdb_ut.py
@@ -0,0 +1,91 @@
+import pytest
+import json
+from saichallenger.common.sai_data import SaiObjType
+
+
+@pytest.fixture(scope="module", autouse=True)
+def skip_all(testbed_instance):
+    testbed = testbed_instance
+    if testbed is not None and len(testbed.npu) != 1:
+        pytest.skip("invalid for \"{}\" testbed".format(testbed.name))
+
+
+class TestFdbEntry:
+    state = dict()
+    mac = "00:00:11:22:33:44"
+
+    @classmethod
+    def key(cls, npu, bvid, mac=None):
+        key = 'SAI_OBJECT_TYPE_FDB_ENTRY:' + json.dumps(
+                       {
+                           "bvid"      : bvid,
+                           "mac"       : mac if mac else cls.mac,
+                           "switch_id" : npu.switch_oid
+                       }
+                   )
+        return key
+
+    @pytest.mark.dependency()
+    def test_create_dynamic(self, npu):
+        npu.create_fdb(npu.default_vlan_oid, TestFdbEntry.mac, npu.dot1q_bp_oids[0], "SAI_FDB_ENTRY_TYPE_DYNAMIC")
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_create_duplicated_dynamic(self, npu):
+        status, _ = npu.create_fdb(npu.default_vlan_oid, TestFdbEntry.mac, npu.dot1q_bp_oids[0], "SAI_FDB_ENTRY_TYPE_DYNAMIC", do_assert=False)
+        assert status == "SAI_STATUS_ITEM_ALREADY_EXISTS"
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_create_duplicated_static(self, npu):
+        status, _ = npu.create_fdb(npu.default_vlan_oid, TestFdbEntry.mac, npu.dot1q_bp_oids[0], "SAI_FDB_ENTRY_TYPE_STATIC", do_assert=False)
+        assert status == "SAI_STATUS_ITEM_ALREADY_EXISTS"
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_change_to_static(self, npu):
+        npu.set(TestFdbEntry.key(npu, npu.default_vlan_oid), ["SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"])
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_change_to_static'])
+    def test_change_to_dynamic(self, npu):
+        npu.set(TestFdbEntry.key(npu, npu.default_vlan_oid), ["SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"])
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_default_action(self, npu):
+        data = npu.get(TestFdbEntry.key(npu, npu.default_vlan_oid), ["SAI_FDB_ENTRY_ATTR_PACKET_ACTION", ""])
+        assert data.value() == "SAI_PACKET_ACTION_FORWARD"
+        self.state["SAI_FDB_ENTRY_ATTR_PACKET_ACTION"] = data.value()
+
+    @pytest.mark.parametrize(
+        "action",
+        [
+            ("SAI_PACKET_ACTION_DROP"),
+            ("SAI_PACKET_ACTION_DONOTDROP"),
+            ("SAI_PACKET_ACTION_COPY"),
+            ("SAI_PACKET_ACTION_COPY_CANCEL"),
+            ("SAI_PACKET_ACTION_TRAP"),
+            ("SAI_PACKET_ACTION_LOG"),
+            ("SAI_PACKET_ACTION_DENY"),
+            ("SAI_PACKET_ACTION_TRANSIT"),
+            ("SAI_PACKET_ACTION_FORWARD")
+        ]
+    )
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_set_action(self, npu, action):
+        attr = "SAI_FDB_ENTRY_ATTR_PACKET_ACTION"
+        status = npu.set(TestFdbEntry.key(npu, npu.default_vlan_oid),
+                         [attr, action], do_assert=False)
+        npu.assert_status_success(status)
+        data = npu.get(TestFdbEntry.key(npu, npu.default_vlan_oid), [attr, ""])
+        assert data.value() == action
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_no_bridge_port(self, npu):
+        npu.set(TestFdbEntry.key(npu, npu.default_vlan_oid), ["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", "oid:0x0"])
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_remove_dynamic(self, npu):
+        npu.remove_fdb(npu.default_vlan_oid, TestFdbEntry.mac)
+
+    @pytest.mark.dependency(depends=['TestFdbEntry::test_create_dynamic'])
+    def test_duplicated_remove(self, npu):
+        status = npu.remove_fdb(npu.default_vlan_oid, TestFdbEntry.mac, do_assert=False)
+        assert status == "SAI_STATUS_ITEM_NOT_FOUND"
+