Skip to content

Commit b8d57f5

Browse files
committed
✅ Add unit test for LtAmp class
1 parent 23b29ef commit b8d57f5

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Compatibility with LtAmp protocol:
6363
- [ ] Audio Status
6464
- [x] Audition Preset (status, start, exit)
6565
- [ ] Clear/rename/shift/swap/save (to/as) preset
66+
- [x] Retrieve preset
6667
- [x] Connection Status
6768
- [x] Current Preset (request/load)
6869
- [ ] DSP Unit Parameter
@@ -105,6 +106,14 @@ Fully-tested LT-series amps:
105106
106107
LtAmp.py is licensed under the permissive MIT license, which means that you may fork, modify, adapt, and redistribute with few restrictions. If you wish to contribute your changes back to the base module, please open a [pull request](/pulls). To report bugs, request features, or discuss the project, open an [issue](/issues) or [discussion](/discussions).
107108

109+
### 🧪 Testing
110+
111+
To run unit tests, use the command:
112+
113+
```bash
114+
python -m unittest discover tests
115+
```
116+
108117
## 🙌 Acknowledgements
109118

110119
- Brent Maxwell ([@brentmaxwell](https://github.com/brentmaxwell)) and his LtAmp .NET libray: his published reverse engineering docs, schemas, proto files, etc. were instrumental in the creation of this Python module.

tests/test_ltamp.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import unittest
2+
import time
3+
from ltamp import LtAmp
4+
import threading
5+
6+
class TestLtAmp(unittest.TestCase):
7+
@classmethod
8+
def setUpClass(cls):
9+
cls.amp = LtAmp()
10+
try:
11+
cls.amp.connect()
12+
cls.amp.send_sync_begin()
13+
time.sleep(1)
14+
cls.amp.send_heartbeat()
15+
cls.amp.send_sync_end()
16+
cls._heartbeat_running = True
17+
def heartbeat():
18+
while cls._heartbeat_running:
19+
cls.amp.send_heartbeat()
20+
time.sleep(0.1)
21+
cls._heartbeat_thread = threading.Thread(target=heartbeat, daemon=True)
22+
cls._heartbeat_thread.start()
23+
except Exception as e:
24+
cls.amp = None
25+
print(f"SKIPPING tests: Could not connect to amp: {e}")
26+
27+
@classmethod
28+
def tearDownClass(cls):
29+
if getattr(cls, "amp", None):
30+
cls._heartbeat_running = False
31+
cls._heartbeat_thread.join(timeout=2)
32+
cls.amp.disconnect()
33+
34+
def setUp(self):
35+
if self.amp is None:
36+
self.skipTest("Amp not connected, skipping hardware integration tests.")
37+
38+
def test_firmware_version(self):
39+
version = self.amp.request_firmware_version()
40+
self.assertIsInstance(version, str)
41+
self.assertRegex(version, r"^\d+\.\d+\.\d+$")
42+
43+
def test_product_id(self):
44+
pid = self.amp.request_product_id()
45+
self.assertIsInstance(pid, str)
46+
47+
def test_qa_slots(self):
48+
slots = self.amp.request_qa_slots()
49+
self.assertIsInstance(slots, list)
50+
self.assertEqual(len(slots), 2)
51+
self.assertTrue(all(isinstance(x, int) for x in slots))
52+
53+
def test_set_and_get_preset(self):
54+
orig_preset = self.amp.request_current_preset()
55+
idx = orig_preset["index"]
56+
self.amp.set_preset(idx)
57+
time.sleep(1)
58+
curr = self.amp.request_current_preset()
59+
self.assertEqual(curr["index"], idx)
60+
61+
def test_usb_gain(self):
62+
orig_gain = self.amp.request_usb_gain()
63+
self.amp.set_usb_gain(orig_gain)
64+
time.sleep(0.5)
65+
gain = self.amp.request_usb_gain()
66+
self.assertEqual(gain, orig_gain)
67+
68+
69+
def test_memory_usage(self):
70+
mem = self.amp.request_memory_usage()
71+
self.assertIsInstance(mem, dict)
72+
self.assertIn("stack", mem)
73+
self.assertIn("heap", mem)
74+
self.assertIsInstance(mem["stack"], int)
75+
self.assertIsInstance(mem["heap"], int)
76+
77+
def test_processor_utilization(self):
78+
util = self.amp.request_processor_utilization()
79+
self.assertIsInstance(util, dict)
80+
for key in ("percent", "minPercent", "maxPercent"):
81+
self.assertIn(key, util)
82+
self.assertIsInstance(util[key], float)
83+
self.assertGreaterEqual(util[key], 0)
84+
85+
def test_send_sync_begin_end(self):
86+
# Just ensure both calls do not raise
87+
try:
88+
self.amp.send_sync_begin()
89+
time.sleep(1)
90+
self.amp.send_sync_end()
91+
except Exception as e:
92+
self.fail(f"send_sync_begin() or send_sync_end() raised an exception: {e}")
93+
94+
def test_disconnect(self):
95+
try:
96+
self.amp.disconnect()
97+
except Exception as e:
98+
self.fail(f"disconnect() raised an exception: {e}")
99+
# Reconnect for further tests (if needed)
100+
try:
101+
self.amp.connect()
102+
self.amp.send_sync_begin()
103+
time.sleep(1)
104+
self.amp.send_sync_end()
105+
except Exception as e:
106+
self.skipTest(f"Could not reconnect to amp after disconnect: {e}")
107+
108+
if __name__ == "__main__":
109+
unittest.main()

0 commit comments

Comments
 (0)