diff --git a/hw_diag/tests/test_get_serial_number.py b/hw_diag/tests/test_get_serial_number.py index 03f24b93..571e4aad 100644 --- a/hw_diag/tests/test_get_serial_number.py +++ b/hw_diag/tests/test_get_serial_number.py @@ -3,9 +3,17 @@ from unittest.mock import mock_open, patch from hw_diag.utilities.hardware import get_serial_number, load_serial_number, \ - load_cpu_info + load_cpu_info, has_valid_serial TEST_SERIAL = "00000000a3e7kg80" +TEST_SERIAL_ALL_ZERO = "000000000000000000000000000000" +TEST_SERIAL_ROCKPI = "d18dbe5c2a58cc61" +TEST_SERIAL_BOBCAT = "ba033cbdca6d626f" +TEST_SERIAL_RASPI = "000000009e3cb787" +TEST_SERIAL_ROCKPI_WRONG = "W1EP3DN9PU" +TEST_SERIAL_BOBCAT_WRONG = "c3d9b8674f4b94f6" +TEST_SERIAL_SHORT = "123ABC" +TEST_SERIAL_EMPTY = "" TEST_CPU_INFO = """ processor : 0 @@ -22,9 +30,18 @@ TEST_SERIAL_NUMBER_RESULT = {'serial': '00000000a3e7kg80'} TEST_CPU_INFO_RESULT = {'serial': '912558f1a3ae877d'} +SERIAL_VALID = {'serial': '912558f1a3ae877dabcdef1234567890ABCDEF'} +SERIAL_ALL_ZERO = {'serial': '000000000000000'} +SERIAL_WRONG_ROCKPI = {'serial': 'CKHZ4CHI1P'} +SERIAL_WRONG_BOBCAT = {'serial': 'c3d9b8674f4b94f6'} +SERIAL_NON_HEX_TEN_DIGITS = {'serial': 'XXXYYYZZZZ'} +SERIAL_SHORT = {'serial': 'ABC123abc'} +SERIAL_BLANK = {'serial': ''} +SERIAL_MISSING = {} FAILED_CPU_INFO_RESULT = {} FAILED_SERIAL_NUMBER_RESULT = {} + class TestGetSerialNumber(unittest.TestCase): right_value = {'serial_number': '00000000a3e7kg80'} @@ -89,3 +106,221 @@ def test_load_cpuinfo_fail(self): captured = self.caplog self.assertTrue('failed to load /proc/cpuinfo' in str(captured.text)) self.assertEqual(cpuinfo, FAILED_CPU_INFO_RESULT) + + def test_has_valid_serial_all_zeros(self): + self.assertFalse(has_valid_serial(SERIAL_ALL_ZERO)) + + def test_has_valid_serial_non_hex(self): + self.assertFalse(has_valid_serial(SERIAL_NON_HEX_TEN_DIGITS)) + + def test_has_valid_serial_knonwn_wrong(self): + self.assertFalse(has_valid_serial(SERIAL_WRONG_ROCKPI)) + + def test_has_valid_serial_knonwn_wrong(self): + self.assertFalse(has_valid_serial(SERIAL_WRONG_BOBCAT)) + + def test_has_valid_serial_mising(self): + self.assertFalse(has_valid_serial(SERIAL_MISSING)) + + def test_has_valid_serial_blank(self): + self.assertFalse(has_valid_serial(SERIAL_BLANK)) + + def test_has_valid_serial_short(self): + self.assertFalse(has_valid_serial(SERIAL_SHORT)) + + def test_has_valid_serial_true(self): + self.assertTrue(has_valid_serial(SERIAL_VALID)) + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_rockpi(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = True + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_ROCKPI} + mock_serial.return_value = {'serial': TEST_SERIAL_ROCKPI_WRONG} + + right_value = {'serial_number': 'd18dbe5c2a58cc61'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_all_zero(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = True + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_ALL_ZERO} + mock_serial.return_value = {'serial': TEST_SERIAL_ALL_ZERO} + + right_value = {'serial_number': 'Serial number not found'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + self.assertEqual(mock_rockpi.call_count, 2) + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_rockpi_raspi(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = True + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_ROCKPI} + mock_serial.return_value = {'serial': TEST_SERIAL} + + right_value = {'serial_number': 'd18dbe5c2a58cc61'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_short_wrong(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_SHORT} + mock_serial.return_value = {'serial': TEST_SERIAL_BOBCAT_WRONG} + + right_value = {'serial_number': 'Serial number not found'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + self.assertEqual(mock_rockpi.call_count, 2) + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_raspi_rockpi(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_ROCKPI} + mock_serial.return_value = {'serial': TEST_SERIAL} + + right_value = {'serial_number': '00000000a3e7kg80'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_raspi(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL} + mock_serial.return_value = {'serial': TEST_SERIAL} + + right_value = {'serial_number': '00000000a3e7kg80'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_bobcat(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_BOBCAT} + mock_serial.return_value = {'serial': TEST_SERIAL_BOBCAT_WRONG} + + right_value = {'serial_number': 'ba033cbdca6d626f'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_rockpi_zero(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = True + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_ALL_ZERO} + mock_serial.return_value = {'serial': TEST_SERIAL_ROCKPI} + + right_value = {'serial_number': 'd18dbe5c2a58cc61'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + self.assertEqual(mock_rockpi.call_count, 2) + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_rockpi_backwards(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = True + mock_cpuinfo.return_value = {'serial': TEST_SERIAL} + mock_serial.return_value = {'serial': TEST_SERIAL_ROCKPI} + + right_value = {'serial_number': '00000000a3e7kg80'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + def test_get_serial_number_raspi_empty(self, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL} + mock_serial.return_value = {'serial': TEST_SERIAL_EMPTY} + + right_value = {'serial_number': '00000000a3e7kg80'} + get_serial_number(self.diag) + self.assertEqual(self.diag["serial_number"], + right_value["serial_number"]) + + mock_rockpi.assert_called_once() + mock_cpuinfo.assert_called_once() + mock_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + @patch('hw_diag.utilities.hardware.has_valid_serial') + def test_has_valid_serial_called(self, mock_valid_serial, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL} + mock_serial.return_value = {'serial': TEST_SERIAL_EMPTY} + + get_serial_number(self.diag) + mock_valid_serial.assert_called_once() + + @patch('hw_diag.utilities.hardware.is_rockpi') + @patch('hw_diag.utilities.hardware.load_cpu_info') + @patch('hw_diag.utilities.hardware.load_serial_number') + @patch('hw_diag.utilities.hardware.has_valid_serial') + def test_has_valid_serial_called_twice(self, mock_valid_serial, mock_serial, mock_cpuinfo, mock_rockpi): + mock_rockpi.return_value = False + mock_cpuinfo.return_value = {'serial': TEST_SERIAL_EMPTY} + mock_serial.return_value = {'serial': TEST_SERIAL_EMPTY} + + get_serial_number(self.diag) + self.assertEqual(mock_valid_serial.call_count, 2) diff --git a/hw_diag/utilities/hardware.py b/hw_diag/utilities/hardware.py index bb696251..476da3d8 100644 --- a/hw_diag/utilities/hardware.py +++ b/hw_diag/utilities/hardware.py @@ -1,6 +1,7 @@ import dbus import os import psutil +import string from typing import Union from urllib.parse import urlparse from hm_pyhelper.logger import get_logger @@ -78,6 +79,20 @@ DTPARAM_CONFIG_VAR_NAMES = ['BALENA_HOST_CONFIG_dtparam', 'RESIN_HOST_CONFIG_dtparam'] EXT_ANT_DTPARAM = '"ant2"' +INCORRECT_BOBCAT_SERIALS = ['c3d9b8674f4b94f6'] +INCORRECT_ROCKPI_SERIALS = [ + 'W1EP3DN9PU', + '0UQMAKIBII', + 'PN06893W5V', + 'KLOFHWLY95', + '3IT1I4E9TG', + 'CKHZ4CHI1P', + 'I4YE1UGF5N', + 'PERTSKMCT0', + 'S63QCF54CJ', + 'YYMSYLJWG8' +] + def should_display_lte(diagnostics): variant = diagnostics.get('VA') @@ -255,11 +270,11 @@ def get_serial_number(diagnostics): cpuinfo = load_cpu_info() serial = load_serial_number() serial_number = "" - if has_valid_serial(serial) and not is_rockpi(): + if not is_rockpi() and has_valid_serial(serial): serial_number = serial[CPUINFO_SERIAL_KEY] elif has_valid_serial(cpuinfo): serial_number = cpuinfo[CPUINFO_SERIAL_KEY] - elif has_valid_serial(serial) and is_rockpi(): + elif is_rockpi() and has_valid_serial(serial): serial_number = serial[CPUINFO_SERIAL_KEY] else: serial_number = "Serial number not found" @@ -275,11 +290,31 @@ def has_valid_serial(cpuinfo: dict) -> bool: if CPUINFO_SERIAL_KEY not in cpuinfo: return False - # Check if serial number is all 0s... serial_number = cpuinfo[CPUINFO_SERIAL_KEY] + + # Check if serial number is all 0s... if all(c in '0' for c in str(serial_number)): return False + # Check if serial number is a known incorrect ROCKPi serial + # in INCORRECT_ROCKPI_SERIALS... + if str(serial_number) in INCORRECT_ROCKPI_SERIALS: + return False + + # Check if serial number is a known incorrect Bobcat serial + # in INCORRECT_BOBCAT_SERIALS... + if str(serial_number) in INCORRECT_BOBCAT_SERIALS: + return False + + # Check if serial number is 10 characters and non-hexadecimal... + if len(str(serial_number)) == 10 and not all( + c in string.hexdigits for c in str(serial_number)): + return False + + # Check if serial number is < 10 characters... + if len(str(serial_number)) < 10: + return False + return True diff --git a/poetry.lock b/poetry.lock index 86f8f8da..07554ef1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -645,13 +645,13 @@ tornado = ["tornado (>=0.2)"] [[package]] name = "hm-pyhelper" -version = "0.14.19" +version = "0.14.20" description = "Helium Python Helper" optional = false python-versions = ">=3.9,<4.0" files = [ - {file = "hm_pyhelper-0.14.19-py3-none-any.whl", hash = "sha256:eacd3835725057ab6cc9646d00f69d4f43da040f9583a6ff3f898baef9ff1d65"}, - {file = "hm_pyhelper-0.14.19.tar.gz", hash = "sha256:3b561371681b01b7352cdd58885abb29e4c987ca3ec1f749b1ca617cf23197e0"}, + {file = "hm_pyhelper-0.14.20-py3-none-any.whl", hash = "sha256:9d36f68cb9ddc8b73ef0e0e90399227d6e3fc4a944c54bdd434394b4ab666bd5"}, + {file = "hm_pyhelper-0.14.20.tar.gz", hash = "sha256:7d2808d5235b859453998aefc64cfc02abc9dfee25ea97762b5da6304ec7fe02"}, ] [package.dependencies] @@ -1547,4 +1547,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.0" python-versions = "~3.11" -content-hash = "678274475484c62acd289376d855ac877229045a5e8c0ab81f880e100c1ae85f" +content-hash = "bd6fb65254c9c974624e5211ff00d142ba0385385d8e7eb0ee4511ef5e0fdb07" diff --git a/pyproject.toml b/pyproject.toml index a0a35fbc..898cac44 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ Flask-APScheduler = "~1.12.4" Flask-Caching = "~2.0.1" grpcio = "~1.53.0" gunicorn = "~20.1.0" -hm-pyhelper = "0.14.19" +hm-pyhelper = "0.14.20" icmplib = "~3.0.3" ipaddress = "~1.0.23" password-strength = "~0.0.3.post2"