Skip to content

Commit 420b3bb

Browse files
author
Axel Dahlberg
committed
Added test framework with mocked socket connection
Added test framework with mocked socket connection
1 parent 2c207fe commit 420b3bb

File tree

6 files changed

+192
-3
lines changed

6 files changed

+192
-3
lines changed

.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ install:
2323
- pip3 install dist/*.whl
2424
script:
2525
- make lint
26+
- make tests
2627
deploy:
2728
provider: pypi
2829
user: adahlberg

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ python-deps:
1717
_verified:
1818
@echo "CQC-Python is verified!"
1919

20+
tests:
21+
@${PYTHON} -m pytest tests
22+
2023
verify: clean python-deps lint _verified
2124

2225
_remove_build:
@@ -35,4 +38,4 @@ _build:
3538

3639
build: _clear_build _build
3740

38-
.PHONY: clean lint python-deps verify build
41+
.PHONY: clean lint python-deps verify build tests

cqc/pythonLib.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ def allocate_qubits(self, num_qubits, notify=True, block=True):
674674

675675
return qubits
676676

677-
def release_qubits(self, qubits, notify=True, block=False, action=False):
677+
def release_qubits(self, qubits, notify=True, block=True, action=False):
678678
"""
679679
Release qubits so backend can free them up for other uses
680680
:param qubits: a list of qubits to be released
@@ -2339,7 +2339,7 @@ def reset(self, notify=True, block=True):
23392339
message = self._cqc.readMessage()
23402340
self._cqc.print_CQC_msg(message)
23412341

2342-
def release(self, notify=True, block=False):
2342+
def release(self, notify=True, block=True):
23432343
"""
23442344
Release the current qubit
23452345
:param notify: Do we wish to be notified when done

tests/conftest.py

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import pytest
2+
import inspect
3+
import socket
4+
from collections import namedtuple
5+
6+
from cqc.pythonLib import CQCConnection
7+
8+
Call = namedtuple("Call", ["name", "args", "kwargs"])
9+
10+
11+
def _spy_wrapper(method):
12+
"""Wraps a method to be able to spy on it"""
13+
def new_method(self, *args, **kwargs):
14+
if method.__name__ == '__init__':
15+
self.calls = []
16+
call = Call(method.__name__, args, kwargs)
17+
self.calls.append(call)
18+
return method(self, *args, **kwargs)
19+
20+
return new_method
21+
22+
23+
def spy_on_class(cls):
24+
"""Spies on all calls to the methods of a class"""
25+
for method_name, method in inspect.getmembers(cls, predicate=inspect.isfunction):
26+
setattr(cls, method_name, _spy_wrapper(method))
27+
return cls
28+
29+
30+
@spy_on_class
31+
class MockSocket:
32+
def __init__(self, *args, **kwargs):
33+
pass
34+
35+
def connect(self, *args, **kwargs):
36+
pass
37+
38+
def send(self, *args, **kwargs):
39+
pass
40+
41+
def recv(self, *args, **kwargs):
42+
pass
43+
44+
def close(self, *args, **kwargs):
45+
pass
46+
47+
48+
@pytest.fixture
49+
def mock_socket(monkeypatch):
50+
def get_mocked_socket(*args, **kwargs):
51+
mock_socket = MockSocket(*args, **kwargs)
52+
return mock_socket
53+
54+
monkeypatch.setattr(socket, "socket", get_mocked_socket)
55+
56+
57+
class MockedFirstMessage:
58+
"""Mocks the second header returned by CQCConnection.readMessage"""
59+
class MockedTypeEntry:
60+
def __eq__(self, other):
61+
"""This type will be equal to any integer."""
62+
return isinstance(other, int)
63+
64+
@property
65+
def tp(self):
66+
return self.MockedTypeEntry()
67+
68+
69+
class MockedOtherMessage:
70+
"""Mocks the second header returned by CQCConnection.readMessage"""
71+
next_qubit_id = 0
72+
73+
@property
74+
def qubit_id(self):
75+
qid = self.next_qubit_id
76+
self.next_qubit_id += 1
77+
return qid
78+
79+
@property
80+
def outcome(self):
81+
return 0
82+
83+
@property
84+
def datetime(self):
85+
return 0
86+
87+
88+
@pytest.fixture
89+
def mock_read_message(monkeypatch):
90+
"""Mock the readMessage, check_error and print_CQC_msg from CQCConnection when testing."""
91+
def mocked_readMessage(self):
92+
return [MockedFirstMessage(), MockedOtherMessage()]
93+
94+
def mocked_print_CQC_msg(self, message):
95+
pass
96+
97+
def mocked_check_error(self, hdr):
98+
pass
99+
100+
monkeypatch.setattr(CQCConnection, "readMessage", mocked_readMessage)
101+
monkeypatch.setattr(CQCConnection, "print_CQC_msg", mocked_print_CQC_msg)
102+
monkeypatch.setattr(CQCConnection, "check_error", mocked_check_error)

tests/test_cqcconnection.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import pytest
2+
3+
from cqc.pythonLib import CQCConnection, qubit
4+
from cqc.cqcHeader import CQCHeader, CQCCmdHeader, CQC_TP_COMMAND,\
5+
CQC_CMD_HDR_LENGTH, CQC_CMD_H, CQC_CMD_NEW, CQC_CMD_RELEASE
6+
7+
from utilities import get_header
8+
9+
10+
def get_expected_headers_simple_h():
11+
"""What headers we expect"""
12+
hdr_tp_cmd = get_header(
13+
CQCHeader,
14+
version=2,
15+
tp=CQC_TP_COMMAND,
16+
app_id=0,
17+
length=CQC_CMD_HDR_LENGTH,
18+
)
19+
hdr_cmd_new = get_header(
20+
CQCCmdHeader,
21+
qubit_id=0,
22+
instr=CQC_CMD_NEW,
23+
notify=True,
24+
action=False,
25+
block=True,
26+
)
27+
hdr_cmd_h = get_header(
28+
CQCCmdHeader,
29+
qubit_id=0,
30+
instr=CQC_CMD_H,
31+
notify=True,
32+
action=False,
33+
block=True,
34+
)
35+
hdr_cmd_release = get_header(
36+
CQCCmdHeader,
37+
qubit_id=0,
38+
instr=CQC_CMD_RELEASE,
39+
notify=True,
40+
action=False,
41+
block=True,
42+
)
43+
44+
expected_headers = [
45+
hdr_tp_cmd,
46+
hdr_cmd_new,
47+
hdr_tp_cmd,
48+
hdr_cmd_h,
49+
hdr_tp_cmd + hdr_cmd_release,
50+
]
51+
52+
return expected_headers
53+
54+
55+
def commands_to_apply_simple_h(cqc):
56+
"""What to do with the CQCConnection"""
57+
q = qubit(cqc)
58+
q.H()
59+
60+
61+
@pytest.mark.parametrize("commands_to_apply, get_expected_headers", [
62+
(commands_to_apply_simple_h, get_expected_headers_simple_h),
63+
])
64+
def test_commands(commands_to_apply, get_expected_headers, monkeypatch, mock_socket, mock_read_message):
65+
66+
with CQCConnection("Test", socket_address=('localhost', 8000), use_classical_communication=False) as cqc:
67+
commands_to_apply(cqc)
68+
69+
expected_headers = get_expected_headers()
70+
71+
commands_send = list(filter(lambda call: call.name == 'send', cqc._s.calls))
72+
assert len(expected_headers) == len(commands_send)
73+
for command, expected in zip(commands_send, expected_headers):
74+
print(command.args[0])
75+
print(expected)
76+
print()
77+
if expected is not None:
78+
assert command.args[0] == expected

tests/utilities.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
def get_header(header_class, *args, **kwargs):
2+
"""Construct and packs a given header"""
3+
hdr = header_class()
4+
hdr.setVals(*args, **kwargs)
5+
return hdr.pack()

0 commit comments

Comments
 (0)