#!/usr/bin/env python3
#   Copyright (C) 2026 John Kacur
# SPDX-License-Identifier: GPL-2.0-only
"""
Unit tests for EPERM error handling in isolate_cpus function.

Tests verify that tuna properly handles Permission Denied errors when
attempting to set CPU affinity on processes like PID 1 (systemd/init).
"""

import unittest
import os
import sys


class TestEPERMHandling(unittest.TestCase):
    """Test cases for EPERM error handling in isolate_cpus"""

    @classmethod
    def setUpClass(cls):
        """Set up test fixtures - read tuna.py once for all tests"""
        tuna_path = os.path.join(os.path.dirname(__file__), '..', 'tuna', 'tuna.py')
        with open(tuna_path, 'r') as f:
            cls.tuna_content = f.read()

        # Find isolate_cpus function bounds
        cls.isolate_cpus_start = cls.tuna_content.find('def isolate_cpus(')
        cls.assertNotEqual(cls.isolate_cpus_start, -1, "Could not find isolate_cpus function")

        # Find next function to get the bounds
        next_def = cls.tuna_content.find('\ndef ', cls.isolate_cpus_start + 1)
        if next_def == -1:
            next_def = len(cls.tuna_content)

        cls.isolate_cpus_code = cls.tuna_content[cls.isolate_cpus_start:next_def]

    def test_eperm_constant_exists(self):
        """Test that errno.EPERM is referenced in the code"""
        self.assertIn('errno.EPERM', self.tuna_content,
                      "errno.EPERM constant not found in tuna.py")

    def test_warning_message_exists(self):
        """Test that warning message exists for unable to isolate"""
        self.assertIn('Warning: Unable to isolate pid', self.tuna_content,
                      "Warning message not found in tuna.py")

    def test_eperm_in_isolate_cpus_function(self):
        """Test that EPERM handling is in the isolate_cpus function"""
        self.assertIn('errno.EPERM', self.isolate_cpus_code,
                      "EPERM handling not found in isolate_cpus function")

    def test_both_ebusy_and_eperm_handled(self):
        """Test that both EBUSY and EPERM errors are handled"""
        self.assertIn('errno.EBUSY', self.isolate_cpus_code,
                      "EBUSY handling not found in isolate_cpus function")
        self.assertIn('errno.EPERM', self.isolate_cpus_code,
                      "EPERM handling not found in isolate_cpus function")

    def test_eperm_handler_calls_continue(self):
        """Test that EPERM handler calls continue to keep processing"""
        eperm_block_start = self.isolate_cpus_code.find('if err.args[0] == errno.EPERM')
        self.assertNotEqual(eperm_block_start, -1,
                           "EPERM handling block not found")

        # Check that continue is called within the next ~200 chars
        eperm_block = self.isolate_cpus_code[eperm_block_start:eperm_block_start + 200]
        self.assertIn('continue', eperm_block,
                     "EPERM handler should call 'continue' to avoid crash")

    def test_eperm_handler_structure(self):
        """Test that EPERM handler has correct structure with comm extraction"""
        eperm_block_start = self.isolate_cpus_code.find('if err.args[0] == errno.EPERM')
        self.assertNotEqual(eperm_block_start, -1, "EPERM handling block not found")

        eperm_block = self.isolate_cpus_code[eperm_block_start:eperm_block_start + 250]

        # Should extract comm for the warning message
        self.assertIn('comm = ps[pid].stat["comm"]', eperm_block,
                     "EPERM handler should extract process name")

        # Should print warning with pid and comm
        self.assertIn('print(f\'Warning: Unable to isolate pid {pid} [{comm}]\')', eperm_block,
                     "EPERM handler should print warning with pid and comm")


if __name__ == '__main__':
    unittest.main()
