Repository: pymeasure/pymeasure (language: python)

Issue:
Race condition in test suite involving HP8116A tests
The pass/fail outcome of the test suite is currently dependent on execution order. On my machine, I'm seeing the following failure:

```
FAILED tests/instruments/test_all_instruments.py::test_property_docstrings[HP8116A.status] - AttributeError: 'NoneType' object has no attribute 'split'
```

At first glance, this seems to indicate that the `status` property of the `HP8116A` instrument class does not have a docstring but, looking at the source code in `pymeasure/instruments/hp/hp8116a.py`, it clearly does:

```python
    @property
    def status(self):
        """Get the status byte of the 8116A as a :class:`Status` IntFlag-type enum."""
        return Status(self.adapter.connection.read_stb())
```

Upon closer inspection, it turns out that the tests for the HP8116A instrument (in `tests/instruments/hp/test_hp8116a.py`) modify the `status` property of the instrument object to mock the value of `status`:

```python
HP8116A.status = property(fget=lambda self: Status(5))
```

Depending on whether these tests run before or after the docstring test in `tests/instruments/test_all_instruments.py`, the test suite either passes or fails.

I have a plausible-looking fix for this issue that I'll propose as a PR.



Discussion / hints:

Fix patch:
diff --git a/AUTHORS.txt b/AUTHORS.txt
index df1b1a7287..1e2fa3323b 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -85,3 +85,4 @@ Nick James Kirkby
 Konrad Gralher
 David Ziliak
 David Sun
+Kévin Petit


Test patch:
diff --git a/tests/instruments/hp/test_hp8116a.py b/tests/instruments/hp/test_hp8116a.py
index 3cdf8642b8..e4c7d18aca 100644
--- a/tests/instruments/hp/test_hp8116a.py
+++ b/tests/instruments/hp/test_hp8116a.py
@@ -28,7 +28,11 @@
 from pymeasure.instruments.hp import HP8116A
 from pymeasure.instruments.hp.hp8116a import Status
 
-HP8116A.status = property(fget=lambda self: Status(5))
+
+class HP8116AWithMockStatus(HP8116A):
+    @property
+    def status(self):
+        return Status(5)
 
 
 init_comm = [(b"CST", b"x" * 87 + b' ,\r\n')]  # communication during init
@@ -36,7 +40,7 @@
 
 def test_init():
     with expected_protocol(
-            HP8116A,
+            HP8116AWithMockStatus,
             init_comm,
     ):
         pass  # Verify the expected communication.
@@ -44,7 +48,7 @@ def test_init():
 
 def test_duty_cycle():
     with expected_protocol(
-            HP8116A,
+            HP8116AWithMockStatus,
             init_comm + [(b"IDTY", b"00000035")],
     ) as instr:
         assert instr.duty_cycle == 35
@@ -52,18 +56,18 @@ def test_duty_cycle():
 
 def test_duty_cycle_setter():
     with expected_protocol(
-            HP8116A,
+            HP8116AWithMockStatus,
             init_comm + [(b"DTY 34.5 %", None)],
     ) as instr:
         instr.duty_cycle = 34.5
 
 
 def test_sweep_time():
-    with expected_protocol(HP8116A, init_comm + [("SWT 5 S", None)]) as inst:
+    with expected_protocol(HP8116AWithMockStatus, init_comm + [("SWT 5 S", None)]) as inst:
         # This test tests also the generate_1_2_5_sequence method and truncation.
         inst.sweep_time = 3
 
 
 def test_limit_enabled():
-    with expected_protocol(HP8116A, init_comm + [("L1", None)]) as inst:
+    with expected_protocol(HP8116AWithMockStatus, init_comm + [("L1", None)]) as inst:
         inst.limit_enabled = True