Skip to content

Commit 87f91fc

Browse files
author
Axel Dahlberg
authored
Merge pull request #44 from SoftwareQuTech/add-tests
Add tests
2 parents 2c207fe + f7e0fbd commit 87f91fc

File tree

8 files changed

+210
-13
lines changed

8 files changed

+210
-13
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,11 @@ matrix:
1717
os: osx
1818
osx_image: xcode10.2
1919
language: shell
20-
before_install:
21-
- make build
2220
install:
23-
- pip3 install dist/*.whl
21+
- make install
2422
script:
2523
- make lint
24+
- make tests
2625
deploy:
2726
provider: pypi
2827
user: adahlberg

Makefile

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,32 @@ PYTHON = python3
22
PIP = pip3
33
CQC_DIR = cqc
44
EXAMPLES = examples
5+
TESTS = tests
56

67
clean: _clear_pyc _clear_build
78

89
_clear_pyc:
910
@find . -name '*.pyc' -delete
1011

1112
lint:
12-
@${PYTHON} -m flake8 ${CQC_DIR} ${EXAMPLES}
13+
@${PYTHON} -m flake8 ${CQC_DIR} ${EXAMPLES} ${TESTS}
1314

1415
python-deps:
15-
@cat requirements.txt | xargs -n 1 -L 1 $(PIP) install
16+
@${PIP} install -r requirements.txt
17+
18+
test-deps:
19+
@${PIP} install -r test_requirements.txt
1620

1721
_verified:
1822
@echo "CQC-Python is verified!"
1923

20-
verify: clean python-deps lint _verified
24+
tests:
25+
@${PYTHON} -m pytest ${TESTS}
26+
27+
verify: clean python-deps lint tests _verified
28+
29+
install: test-deps build
30+
@${PIP} install dist/*whl
2131

2232
_remove_build:
2333
@rm -f -r build
@@ -35,4 +45,4 @@ _build:
3545

3646
build: _clear_build _build
3747

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

cqc/pythonLib.py

Lines changed: 2 additions & 2 deletions
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

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
numpy>=1.14.0
2-
bitstring>=3.1.5
3-
flake8>=3.6.0
4-
twisted>=18.7.0
1+
numpy>=1.14.0,<1.18.0
2+
bitstring>=3.1.5,<4.0.0
3+
twisted>=19.7.0,<20.0.0
4+
anytree>=2.7.2,<3.0.0

test_requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
flake8>=3.6.0,<4.0.0
2+
pytest>=5.2.1,<6.0.0

tests/conftest.py

Lines changed: 102 additions & 0 deletions
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 first 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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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_sent = list(filter(lambda call: call.name == 'send', cqc._s.calls))
72+
assert len(expected_headers) == len(commands_sent)
73+
for command, expected in zip(commands_sent, expected_headers):
74+
print(command.args[0])
75+
print(expected)
76+
print()
77+
# Excluding None gives the opportunity to not specify all expected headers but still check the number of them
78+
if expected is not None:
79+
assert command.args[0] == expected

tests/utilities.py

Lines changed: 5 additions & 0 deletions
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)