import abc

import os

from unittest.mock import patch

from library_test.base_test import BaseLibraryTest





class TestProcessMcu(BaseLibraryTest, metaclass=abc.ABCMeta):

    TESTCASE_DIR = os.path.join(os.path.dirname(__file__), "testcase")



    @classmethod

    def get_module_path(cls):

        return "ascend_deployer.library.process_mcu"



    @classmethod

    def setUpClass(cls) -> None:

        super().setUpClass()



    def setUp(self) -> None:

        npu_info = {"card": 'A300i-pro', "model": 'A300i-pro', "scene": 'a300i', "product": 'A300i'}

        with patch('ascend_deployer.library.process_mcu.common_info.get_npu_info', return_value=npu_info):

            from ascend_deployer.library.process_mcu import McuInstallation



            self.utils = McuInstallation()





class TestProcessMcuGeneral(TestProcessMcu):



    @classmethod

    def get_testcase_path(cls):

        return os.path.join(cls.TESTCASE_DIR, "upgrade_mcu.yml")



    def test_find_mcu_file(self):

        exist_file = self.utils._find_mcu_file("/root/resources/run_from_a310p_zip/")

        self.assertTrue("Ascend-hdk-310p-mcu_24.2.1.hpm" in exist_file)

        empty_file = self.utils._find_mcu_file("/root/resources/")

        self.assertFalse(empty_file)



    def test_check_driver_and_npu(self):

        with self.assertRaises(Exception) as context:

            self.utils._check_driver_and_npu()

        self.assertEqual(str(context.exception),

                         "[ASCEND][WARNING] Driver not installed completely, please reinstall driver first")



    def test_run_command(self):

        err_return = 1

        err_command = 'err command'

        command_mock_out = 'out'

        command_mock_err = 'err'

        with patch.object(self.utils.module, 'run_command',

                          return_value=(err_return, command_mock_out, command_mock_err)):

            with self.assertRaises(Exception) as context:

                self.utils._run_command(err_command)

            self.assertEqual(str(context.exception), "calling {} failed on {}: {}".format(err_command, err_return,

                                                                                          command_mock_out + command_mock_err))



        ok_commamd = "ok command"

        command_mock_ok_out = "out"

        command_mock_ok_err = "err"

        ok_return = 0

        with patch.object(self.utils.module, 'run_command',

                          return_value=(ok_return, command_mock_ok_out, command_mock_ok_err)):

            output = self.utils._run_command(ok_commamd)

            self.assertEqual(output, command_mock_ok_out + command_mock_ok_err)



    def test_do_upgrade_mcu(self):

        npu_id_info = """                Total Count                    : 1



                NPU ID                         : 8

                Product Name                   : IT21PDDA01

                Serial Number                  : 033VNY10MB000071

                Chip Count                     : 1"""

        with patch.object(self.utils, '_run_command', return_value=npu_id_info):

            with self.assertRaises(Exception) as context:

                self.utils._do_upgrade_mcu()

        self.assertEqual(str(context.exception), "[ASCEND][WARNING] MCU upgrade failed, exit!")



    def test_process_mcu(self):

        # Test execution user is non-root --upgrade=mcu scenario

        @patch.object(os, 'getuid', return_value=-1)

        def non_root_test(mock_getuid):

            with self.assertRaises(Exception) as context:

                self.utils.auto_mode_flag = False

                self.utils._process_mcu()

            self.assertEqual(str(context.exception), '[ASCEND] None-root user cannot upgrade mcu!')

            with self.assertRaises(Exception) as context:

                self.utils.auto_mode_flag = False

                self.utils._process_mcu()

            self.assertEqual(str(context.exception), '[ASCEND] None-root user cannot upgrade mcu!')

            # Test execution user is non-root auto scenario

            with patch.object(self.utils.module, 'exit_json', return_value="skip"):

                self.utils.auto_mode_flag = True

                re = self.utils._process_mcu()

                self.assertEqual(re, "skip")



        non_root_test()



        # The test did not detect the auto scenario of the npu.

        with patch.object(self.utils.module, 'exit_json', return_value="skip"):

            self.utils.auto_mode_flag = True

            with patch.object(self.utils, 'npu_info', {}):

                result = self.utils._process_mcu()

                self.assertEqual(result, "skip")



        # The test did not detect the --upgrade =mcu scenario of the npu.

        with self.assertRaises(Exception) as context:

            self.utils.auto_mode_flag = False

            with patch.object(self.utils, 'npu_info', {}):

                self.utils._process_mcu()

        self.assertEqual(str(context.exception), "[ASCEND][WARNING] Can not detect npu, exit!")



        # Test the scenario without complete installation of npu

        with self.assertRaises(Exception) as context:

            self.utils.auto_mode_flag = False

            self.utils._process_mcu()

        self.assertEqual(str(context.exception),

                         "[ASCEND][WARNING] Driver not installed completely, please reinstall driver first")



        @patch.object(os.path, 'exists', return_value=None)

        def test_func_with_mock_path(mock_path):

            # The test did not find the auto scene of npu-smi

            with patch.object(self.utils.module, 'exit_json', return_value="skip"):

                self.utils.auto_mode_flag = True

                result = self.utils._process_mcu()

                self.assertEqual(result, "skip")



            # The test did not find the upgrade=mcu scenario of npu-smi

            self.utils.auto_mode_flag = False

            with self.assertRaises(Exception) as context:

                self.utils._process_mcu()

            self.assertEqual(str(context.exception), '[ASCEND][WARNING] Can not find npu-smi bin, exit!')



            # The test did not find the upgrade=mcu scenario for mcu.

            with patch.object(self.utils.module, 'get_bin_path', return_value=True):

                self.utils.auto_mode_flag = False

                with self.assertRaises(Exception) as context:

                    self.utils._process_mcu()

                self.assertEqual(str(context.exception), "[ASCEND][WARNING] Can not find mcu file, exit!")



            # Test the auto scene where the mcu file is not found.

            with patch.object(self.utils.module, 'get_bin_path', return_value=True):

                with patch.object(self.utils.module, 'exit_json', return_value="skip"):

                    self.utils.auto_mode_flag = True

                    result = self.utils._process_mcu()

                    self.assertEqual(result, "skip")



            @patch.object(self.utils.module, 'get_bin_path', return_value=True)

            @patch.object(self.utils, '_find_mcu_file', return_value="mcu.hpm")

            def test_func_with_mock_utils(mock_find, mock_get_bin):

                with self.assertRaises(Exception) as context:

                    self.utils.auto_mode_flag = False

                    self.utils._process_mcu()

                self.assertEqual(str(context.exception),

                                 "calling npu-smi info -l failed on -1: Testcase error, no cmd pattern found. cmd: ['npu-smi', 'info', '-l']")



            test_func_with_mock_utils()



        test_func_with_mock_path()