Skip to content

Commit

Permalink
looking to see if the yml file was written correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
mark11778 committed Dec 2, 2024
1 parent 505ffa2 commit 0cb6dac
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 0 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/python_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Python Tests

on:
push:
branches: [main, develop, feature/citesting] # or your default branch name
pull_request:
branches: [main, develop, feature/citesting] # or your default branch name

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.12.4' # Specify your Python version if needed

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest # Add any other dependencies your tests need
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Run tests
run: |
python -m pytest citestfiles/ -v # This will run all test files in citestfiles directory
200 changes: 200 additions & 0 deletions citestfiles/G9Driver/g9driver_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

import unittest
from unittest.mock import MagicMock, patch
from instrumentctl.G9SP_interlock.g9_driver import G9Driver

class TestG9Driver(unittest.TestCase):
# Sample response data - modified versions for different test cases
BASE_RESPONSE = bytearray([
0x40, 0x00, 0x00, 0xC3, # Header
# Bytes 4-10: Initial padding
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
# SITDF (Safety Input Terminal Data Flags) - 6 bytes
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
# SOTDF (Safety Output Terminal Data Flags) - 4 bytes
0xFF, 0xFF, 0xFF, 0xFF,
# SITSF (Safety Input Terminal Status Flags) - 6 bytes
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
# SOTSF (Safety Output Terminal Status Flags) - 4 bytes
0xFF, 0xFF, 0xFF, 0xFF
] + [0x00] * 145 + [0x2A, 0x0D]) # Padding + Footer

def setUp(self):
self.driver = G9Driver()
self.driver.ser = MagicMock()
# Set normal operation status in BASE_RESPONSE
self.BASE_RESPONSE[self.driver.US_OFFSET:self.driver.US_OFFSET + 2] = b'\x00\x01'

def create_response_with_checksum(self, base_message):
"""Helper to create a response with valid checksum"""
message_without_checksum = base_message[:-4]
checksum = self.driver._calculate_checksum(message_without_checksum, 194)
return message_without_checksum + checksum + self.driver.FOOTER

def test_normal_response_processing(self):
"""Test processing of a normal response with all systems operational"""
msg = bytearray(self.BASE_RESPONSE)
msg[self.driver.US_OFFSET:self.driver.US_OFFSET + 2] = b'\x00\x01' # Normal unit status
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

self.driver.ser.read_until.return_value = msg_with_checksum

try:
sitsf, sitdf = self.driver._process_response(msg_with_checksum)
self.assertEqual(len(sitsf), self.driver.NUMIN)
self.assertEqual(len(sitdf), self.driver.NUMIN)
self.assertTrue(all(bit == 1 for bit in sitsf))
self.assertTrue(all(bit == 1 for bit in sitdf))
except ValueError as e:
self.fail(f"process_response() raised ValueError unexpectedly: {str(e)}")

def test_unit_status_error(self):
"""Test detection of unit status errors"""
pass
# # Ensure msg is a bytearray to allow modification
# msg = bytearray(self.BASE_RESPONSE)
# # Set Output Power Supply Error Flag (bit 9)
# msg[self.driver.US_OFFSET:self.driver.US_OFFSET + 2] = b'\xFF\xFF'

# # Convert to bytes only when needed for checksum creation
# msg_with_checksum = self.create_response_with_checksum(bytes(msg))

# with self.assertRaises(ValueError) as context:
# self.driver._process_response(msg_with_checksum)
# self.assertIn("Output Power Supply Error Flag", str(context.exception))



def test_safety_input_error(self):
"""Test detection of safety input terminal errors"""
msg = bytearray(self.BASE_RESPONSE)
# Fill input error section with zeros first
msg[self.driver.SITEC_OFFSET:self.driver.SITEC_OFFSET + 24] = bytes([0] * 24)
# Set error in the last 10 bytes of the input section
error_section = msg[self.driver.SITEC_OFFSET:self.driver.SITEC_OFFSET + 24]
# Put error code 3 (Internal circuit error) at start of last 10 bytes
msg[self.driver.SITEC_OFFSET + 14] = 0x30 # Position error at start of last 10 bytes
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

with self.assertRaises(ValueError) as context:
self.driver._process_response(msg_with_checksum)
self.assertIn("Internal circuit error", str(context.exception))

def test_safety_output_error(self):
"""Test detection of safety output terminal errors"""
msg = bytearray(self.BASE_RESPONSE)
# Fill output error section with zeros first
msg[self.driver.SOTEC_OFFSET:self.driver.SOTEC_OFFSET + 16] = bytes([0] * 16)
# Set error code 2 (Overcurrent detection) at start of last 10 bytes
msg[self.driver.SOTEC_OFFSET + 6] = 0x20
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

with self.assertRaises(ValueError) as context:
self.driver._process_response(msg_with_checksum)
self.assertIn("Overcurrent detection", str(context.exception))

def test_response_format_validation(self):
"""Test validation of response format"""
# Test invalid start byte
msg = bytearray(self.BASE_RESPONSE)
msg[0] = 0x41 # Wrong start byte
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

with self.assertRaises(ValueError) as context:
self.driver._validate_response_format(msg_with_checksum)
self.assertIn("Invalid start byte", str(context.exception))

def test_checksum_validation(self):
"""Test checksum validation"""
msg = bytes(self.BASE_RESPONSE)
# Corrupt the message after calculating valid checksum
msg_with_checksum = self.create_response_with_checksum(msg)
corrupted_msg = bytearray(msg_with_checksum)
corrupted_msg[10] = 0xFF # Change a byte in the message

with self.assertRaises(ValueError) as context:
self.driver._validate_checksum(corrupted_msg)
self.assertIn("Checksum failed", str(context.exception))

#TODO: change this test since the unit status now does not raise an error
# def test_complex_error_combinations(self):
# """Test multiple simultaneous error conditions"""
# msg = bytearray(self.BASE_RESPONSE)
# # Set multiple errors
# # msg[self.driver.US_OFFSET:self.driver.US_OFFSET + 2] = b'\x02\x00' # Unit status error
# msg[self.driver.SITEC_OFFSET] = 0x30 # Input error
# msg[self.driver.SOTEC_OFFSET] = 0x20 # Output error
# msg_with_checksum = self.create_response_with_checksum(bytes(msg))

# with self.assertRaises(ValueError) as context:
# self.driver._process_response(msg_with_checksum)
# # Should raise the first error it encounters
# self.assertIn("Output Power Supply Error Flag", str(context.exception))

def test_input_terminal_status_checker(self):
"""Test the input terminal status checker with various error codes"""
test_cases = [
(0x10, "Invalid configuration"),
(0x20, "External test signal failure"),
(0x30, "Internal circuit error"),
(0x40, "Discrepancy error"),
(0x50, "Failure of the associated dual-channel input")
]

for error_code, expected_message in test_cases:
msg = bytearray(self.BASE_RESPONSE)
# Fill with zeros first
msg[self.driver.SITEC_OFFSET:self.driver.SITEC_OFFSET + 24] = bytes([0] * 24)
# Place error code in last byte
msg[self.driver.SITEC_OFFSET + 23] = error_code
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

with self.assertRaises(ValueError) as context:
self.driver._process_response(msg_with_checksum)
self.assertIn(expected_message, str(context.exception))

def test_output_terminal_status_checker(self):
"""Test the output terminal status checker with various error codes"""
test_cases = [
(0x10, "Invalid configuration"),
(0x20, "Overcurrent detection"),
(0x30, "Short circuit detection"),
(0x40, "Stuck-at-high detection"),
(0x50, "Failure of the associated dual-channel output"),
(0x60, "Internal circuit error"),
(0x80, "Dual channel violation")
]

for error_code, expected_message in test_cases:
msg = bytearray(self.BASE_RESPONSE)
# Fill with zeros first
msg[self.driver.SOTEC_OFFSET:self.driver.SOTEC_OFFSET + 16] = bytes([0] * 16)
# Place error code in last byte
msg[self.driver.SOTEC_OFFSET + 15] = error_code
msg_with_checksum = self.create_response_with_checksum(bytes(msg))
msg_with_checksum = self.create_response_with_checksum(bytes(msg))

with self.assertRaises(ValueError) as context:
self.driver._process_response(msg_with_checksum)
self.assertIn(expected_message, str(context.exception))

def test_calculate_checksum(self):
"""Test the checksum calculation for a known data message."""
# Create a sample message with known bytes
test_data = b'@\x00\x00\xc3\x00\x00\xcb\x00\x00\x00\x00\xfc\x0f\x00\x00\x00\x00E\x00\x00\x00\xff\xff\x0f\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x12\x00\x9a\x08~\x15\x00\x0020000012X17M\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\n\n?\x00\x15~?\x00\x15l?\x00\x15f?\x00\x15`?\x00\x15`?\x00\x15`?\x00\x15H?\x00\x15H?\x00\x15H?\x00\x15B\x06\x00\x15f\x01\x00\x15f\x06\x00\x15`\x01\x00\x15`\x06\x00\x15B\x01\x00\x15B\x06\x00\x15B\x01\x00\x15B\x06\x00\x15\x1e\x01\x00\x15\x1e\x14\xf6*\r'

# Verify that the calculated checksum is correct
cal = self.driver._calculate_checksum(test_data, 194)
self.assertEqual(test_data[-4:-2], cal,
f"""
Checksum calculation did not match expected value: calculated {self.driver._calculate_checksum(test_data, 194)};
expected {test_data[-4:-2]}
""")



if __name__ == '__main__':
unittest.main()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ pyserial==3.5
scipy==1.14.1
tkdial==0.0.7
pandas==2.1.3
pytest
unittest

0 comments on commit 0cb6dac

Please sign in to comment.