79677985

Date: 2025-06-24 17:26:30
Score: 1
Natty:
Report link

Does this help you?

import unittest
from unittest.mock import MagicMock, patch
class TestGatttoolInteraction(unittest.TestCase):
    """
    Test suite for the gatttool interaction functions.
    Mocks pexpect.spawn to avoid actual external process calls.
    """

    @patch('pexpect.spawn') # Patch pexpect.spawn globally for all tests in this class
    def test_connect_success(self, mock_spawn):
        """
        Tests that the connect function correctly spawns gatttool,
        sends the connect command, and expects success.
        """
        # Configure the mock pexpect.spawn object.
        # When mock_spawn() is called, it returns a mock object (mock_gatt).
        mock_gatt = MagicMock()
        mock_spawn.return_value = mock_gatt

        # Simulate gatttool's response to the connect command.
        # When mock_gatt.expect is called, we want it to succeed without error.
        # We don't need to specify return value for expect if it just needs to not raise.
        mock_gatt.expect.return_value = 0 # A common return for success in pexpect

        # Call the function under test
        connect()

        # Assertions to verify correct behavior:

        # 1. Verify pexpect.spawn was called with the correct command
        mock_spawn.assert_called_once_with("sudo gatttool -I")

        # 2. Verify the correct connect command was sent
        mock_gatt.sendline.assert_any_call("connect " + MAC)

        # 3. Verify 'Connection successful' was expected
        mock_gatt.expect.assert_called_once_with("Connection successful")

        # 4. Verify the global 'gatt' variable was set to the mock object
        self.assertIs(gatt, mock_gatt)


    @patch('pexpect.spawn')
    def test_connect_failure(self, mock_spawn):
        """
        Tests that connect raises an exception if 'Connection successful'
        is not found, simulating a connection failure.
        """
        mock_gatt = MagicMock()
        mock_spawn.return_value = mock_gatt

        # Configure expect to raise an exception, simulating failure to find expected text
        mock_gatt.expect.side_effect = pexpect.exceptions.TIMEOUT('Timeout waiting for "Connection successful"')

        # Assert that the function raises the expected pexpect exception
        with self.assertRaises(pexpect.exceptions.TIMEOUT):
            connect()

        mock_spawn.assert_called_once_with("sudo gatttool -I")
        mock_gatt.sendline.assert_called_once_with("connect " + MAC)
        mock_gatt.expect.assert_called_once_with("Connection successful")


    @patch('pexpect.spawn') # Patch pexpect.spawn for this test
    def test_send_success(self, mock_spawn):
        """
        Tests that the send function correctly calls connect(),
        sends the char-write-req command, and expects success.
        """
        mock_gatt_instance = MagicMock()
        # Ensure that each call to pexpect.spawn() (e.g., from connect())
        # returns the same mock object in this test context.
        mock_spawn.return_value = mock_gatt_instance

        # Simulate successful responses for both connect() and send() operations
        # expect() should not raise an error
        mock_gatt_instance.expect.return_value = 0

        test_value = "ab"
        send(test_value)

        # Assertions:

        # 1. Verify pexpect.spawn was called twice (once by connect() inside send(), then again by connect() in the second call)
        # However, because send() calls connect() which *re-spawns* and overwrites 'gatt',
        # we only care about the state after the final connect().
        # The important thing is that gatt.sendline and gatt.expect are called correctly on the *final* gatt object.
        # Since connect() is called, pexpect.spawn will be called once.
        mock_spawn.assert_called_once_with("sudo gatttool -I")

        # 2. Verify the connect command was sent by connect()
        mock_gatt_instance.sendline.assert_any_call("connect " + MAC)

        # 3. Verify 'Connection successful' was expected by connect()
        mock_gatt_instance.expect.assert_any_call("Connection successful")

        # 4. Verify the characteristic write command was sent
        expected_write_command = f"char-write-req 0x000c {test_value}0"
        mock_gatt_instance.sendline.assert_any_call(expected_write_command)

        # 5. Verify 'Characteristic value was written successfully' was expected
        mock_gatt_instance.expect.assert_any_call("Characteristic value was written successfully")

        # Ensure gatt.sendline and gatt.expect were called for both operations
        # We use call_count to ensure both connect and send operations occurred on the mock
        self.assertEqual(mock_gatt_instance.sendline.call_count, 2)
        self.assertEqual(mock_gatt_instance.expect.call_count, 2)

    @patch('pexpect.spawn')
    def test_send_write_failure(self, mock_spawn):
        """
        Tests that send raises an exception if 'Characteristic value was written successfully'
        is not found, simulating a write failure.
        """
        mock_gatt_instance = MagicMock()
        mock_spawn.return_value = mock_gatt_instance

        # Set up expect to succeed for the 'connect' call
        mock_gatt_instance.expect.side_effect = [
            0, # Success for "Connection successful"
            pexpect.exceptions.TIMEOUT('Timeout waiting for "Characteristic value was written successfully"') # Failure for the write
        ]

        test_value = "1a"
        with self.assertRaises(pexpect.exceptions.TIMEOUT):
            send(test_value)

        mock_spawn.assert_called_once_with("sudo gatttool -I")
        mock_gatt_instance.sendline.assert_any_call("connect " + MAC)
        mock_gatt_instance.expect.assert_any_call("Connection successful")
        mock_gatt_instance.sendline.assert_any_call(f"char-write-req 0x000c {test_value}0")
        mock_gatt_instance.expect.assert_any_call("Characteristic value was written successfully")
        self.assertEqual(mock_gatt_instance.sendline.call_count, 2)
        self.assertEqual(mock_gatt_instance.expect.call_count, 2)


if __name__ == '__main__':
    unittest.main(argv=['first-arg-is-ignored'], exit=False) # exit=False prevents sys.exit()
Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • Low reputation (0.5):
Posted by: Mickle-The-Pickle