From 2a575bf9b5a4942f60e979161764b9e942699e1e Mon Sep 17 00:00:00 2001
From: Lars Amsel <lars.amsel@ni.com>
Date: Fri, 4 Jun 2021 08:27:50 +0200
Subject: uhd: Add support for the USRP X410
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Lars Amsel <lars.amsel@ni.com>
Co-authored-by: Michael Auchter <michael.auchter@ni.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Cristina Fuentes <cristina.fuentes-curiel@ni.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Virendra Kakade <virendra.kakade@ni.com>
Co-authored-by: Lane Kolbly <lane.kolbly@ni.com>
Co-authored-by: Max Köhler <max.koehler@ni.com>
Co-authored-by: Andrew Lynch <andrew.lynch@ni.com>
Co-authored-by: Grant Meyerhoff <grant.meyerhoff@ni.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Thomas Vogel <thomas.vogel@ni.com>
---
 mpm/python/tests/components_tests.py | 114 +++++++++++++++++++++++++++++++++++
 mpm/python/tests/run_unit_tests.py   |   8 +++
 mpm/python/tests/sys_utils_tests.py  |   5 ++
 mpm/python/tests/test_utilities.py   |  28 +++++++++
 4 files changed, 155 insertions(+)
 create mode 100644 mpm/python/tests/components_tests.py

(limited to 'mpm/python/tests')

diff --git a/mpm/python/tests/components_tests.py b/mpm/python/tests/components_tests.py
new file mode 100644
index 000000000..120b89121
--- /dev/null
+++ b/mpm/python/tests/components_tests.py
@@ -0,0 +1,114 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Tests the components classes (currently ZynqComponents)
+"""
+
+from usrp_mpm.components import ZynqComponents
+from base_tests import TestBase
+
+import copy
+import os.path
+import tempfile
+import unittest
+
+class TestZynqComponents(TestBase):
+    """
+    Test functions of the ZynqComponents class
+    """
+
+    _testcase_input = '// mpm_version foo_current_version 1.10\n' \
+                      '// mpm_version foo_oldest_compatible_version 1.5\n' \
+                      '// mpm_version bar_current_version 1.2\n' \
+                      '// mpm_version bar_oldest_compatible_version 1.0\n' \
+                      '// mpm_version baz_current_version 1.2.3\n' \
+                      '// mpm_version baz_oldest_compatible_version 1.0.0\n' \
+                      '// mpm_version zack 2.0\n' \
+                      '// mpm_version zack_oldest_compatible_version 1.0\n' \
+                      '// mpm_other_tag noname_current_version 1.2.3\n' \
+                      '// other comment\n'
+
+    _testcase_result = {
+        'bar': {'current': (1, 2), 'oldest': (1,0)},
+        'baz': {'current': (1, 2, 3), 'oldest': (1,0,0)},
+        'foo': {'current': (1, 10), 'oldest': (1, 5)},
+        'zack': {'current': (2, 0), 'oldest': (1,0)},
+    }
+
+    def _write_dts_file_from_test_cases(self, content):
+        """ Write content to a temporary .dts file """
+        f = tempfile.NamedTemporaryFile(mode="w+", suffix=".dts")
+        expected = {}
+        f.write(content)
+        f.flush()
+        return f
+
+    def test_parse_dts_version_info_from_file(self):
+        """ Test function ZynqComponents._parse_dts_version_info_from_file """
+        f = self._write_dts_file_from_test_cases(self._testcase_input)
+        expected = self._testcase_result
+        result = ZynqComponents._parse_dts_version_info_from_file(f.name)
+        self.assertEqual(result, expected)
+
+    def test_verify_compatibility(self):
+        """ Test function ZynqComponents._verify_compatibility """
+        class _log_dummy():
+            def _dummy(self, *args):
+                pass
+            trace = _dummy
+            info = _dummy
+            warning = _dummy
+            error = _dummy
+
+        f = self._write_dts_file_from_test_cases(self._testcase_input)
+        compatibility = self._testcase_result
+        for version_type in ['current', 'oldest']:
+            for case in ['normal', 'smaller_mpm_minor', 'bigger_mpm_minor',
+                'smaller_mpm_major', 'bigger_mpm_major', 'component_missing',
+                'additional_component']:
+                compatibility_testcase = copy.deepcopy(compatibility)
+                foo_major, foo_minor = compatibility['foo'][version_type]
+                if case == 'normal':
+                    compatibility_testcase['foo'][version_type] = (foo_major, foo_minor)
+                    error_expected = None
+                elif case == 'smaller_mpm_minor':
+                    compatibility_testcase['foo'][version_type] = (foo_major, foo_minor-1)
+                    error_expected = None
+                elif case == 'bigger_mpm_minor':
+                    compatibility_testcase['foo'][version_type] = (foo_major, foo_minor+1)
+                    error_expected = None
+                elif case == 'smaller_mpm_major':
+                    compatibility_testcase['foo'][version_type] = (foo_major-1, foo_minor)
+                    if version_type == 'oldest':
+                        error_expected = None
+                    else:
+                        error_expected = RuntimeError()
+                elif case == 'bigger_mpm_major':
+                    compatibility_testcase['foo'][version_type] = (foo_major+1, foo_minor)
+                    if version_type == 'oldest':
+                        error_expected = RuntimeError()
+                    else:
+                        error_expected = None
+                elif case == 'component_missing':
+                    del compatibility_testcase['foo']
+                    error_expected = None
+                elif case == 'additional_component':
+                    compatibility_testcase['newcomp'] = {version_type: (2, 10)}
+                    error_expected = None
+                update_dict = {
+                    'compatibility': compatibility_testcase,
+                    'check_dts_for_compatibility': True,
+                }
+                filebasename, _ = os.path.splitext(f.name)
+                try:
+                    self._zynqcomponents = ZynqComponents()
+                    self._zynqcomponents.log = _log_dummy()
+                    self._zynqcomponents._verify_compatibility(filebasename, update_dict)
+                    error = None
+                except RuntimeError as r:
+                    error = r
+                self.assertEqual(error.__class__, error_expected.__class__,
+                                 f"Unexpected result for test case {case} (version type: {version_type})")
diff --git a/mpm/python/tests/run_unit_tests.py b/mpm/python/tests/run_unit_tests.py
index c563804ae..a26fa1d22 100755
--- a/mpm/python/tests/run_unit_tests.py
+++ b/mpm/python/tests/run_unit_tests.py
@@ -13,6 +13,7 @@ import argparse
 from sys_utils_tests import TestNet
 from mpm_utils_tests import TestMpmUtils
 from eeprom_tests import TestEeprom
+from usrp_mpm import __simulated__
 
 import importlib.util
 if importlib.util.find_spec("xmlrunner"):
@@ -25,8 +26,15 @@ TESTS = {
         TestEeprom,
     },
     'n3xx': set(),
+    'x4xx': set()
 }
 
+if not __simulated__:
+    from components_tests import TestZynqComponents
+    TESTS['x4xx'].update({
+        TestZynqComponents
+    })
+
 def parse_args():
     """Parse arguments when running this as a script"""
     parser_help = 'Run MPM Python unittests'
diff --git a/mpm/python/tests/sys_utils_tests.py b/mpm/python/tests/sys_utils_tests.py
index 50a10e1a1..c189257c2 100755
--- a/mpm/python/tests/sys_utils_tests.py
+++ b/mpm/python/tests/sys_utils_tests.py
@@ -62,6 +62,11 @@ class TestNet(TestBase):
         """
         if self.device_name == 'n3xx':
             possible_ifaces = ['eth0', 'sfp0', 'sfp1']
+        elif self.device_name == 'x4xx':
+            # x4xx devices have an internal network interface
+            # TODO: change this when sfp0 is enabled
+            # possible_ifaces = ['eth0', 'sfp0', 'int0']
+            possible_ifaces = ['eth0', 'int0']
         else:
             possible_ifaces = ['eth0', 'sfp0']
 
diff --git a/mpm/python/tests/test_utilities.py b/mpm/python/tests/test_utilities.py
index 198eda5e8..942ad956a 100755
--- a/mpm/python/tests/test_utilities.py
+++ b/mpm/python/tests/test_utilities.py
@@ -90,6 +90,7 @@ class MockRegsIface(object):
         self.map = register_map
         self.recent_vals = {}
         self.next_vals = {}
+        self.recent_addrs = []
 
     def peek32(self, addr):
         """
@@ -110,12 +111,32 @@ class MockRegsIface(object):
         """
         self.map.set_reg(addr, value)
 
+        self.recent_addrs.append(addr)
+
         # Store written value in a list
         if addr in self.recent_vals:
             self.recent_vals[addr].append(value)
         else:
             self.recent_vals[addr] = [value]
 
+    def peek16(self, addr):
+        """
+        Pass the request to the 32 bit version
+        """
+        return self.peek32(addr) & 0xFFFF
+
+    def poke16(self, addr, value):
+        """
+        Pass the request to the 32 bit version
+        """
+        self.poke32(addr, value)
+
+    def get_recent_addrs(self):
+        return self.recent_addrs
+
+    def clear_recent_addrs(self):
+        self.recent_addrs = []
+
     def get_recent_vals(self, addr):
         """
         Returns the past values written to a given address.
@@ -123,6 +144,13 @@ class MockRegsIface(object):
         """
         return self.recent_vals.get(addr, [])
 
+    def clear_recent_vals(self, addr):
+        """
+        Clears the past values written to a given address.
+        Useful for validating HW interaction
+        """
+        self.recent_vals[addr] = []
+
     def set_next_vals(self, addr, vals):
         """
         Sets a list of the next values to be read from the
-- 
cgit v1.2.3