Skip to content

Commit

Permalink
Refactor devicebus device
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoklosowski committed Oct 4, 2024
1 parent 217b8bb commit a8e95b2
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 100 deletions.
6 changes: 1 addition & 5 deletions src/pychip8/devices/devicebus.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ def __repr__(self) -> str:
return f'DeviceBus(devices={len(self._devices)})'

def __len__(self) -> int:
size = 0
for _, end, _ in self._devices:
if end >= size:
size = end + 1
return size
return max((end + 1 for _, end, _ in self._devices), default=0)

def __getitem__(self, address: int, /) -> int:
for start, end, device in self._devices:
Expand Down
172 changes: 77 additions & 95 deletions tests/pychip8/devices/test_devicebus.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from pychip8.devices.devicebus import DeviceBus
from pychip8.devices.devicebus import Device, DeviceBus


class TestDeviceBus:
Expand All @@ -14,7 +14,8 @@ def test_repr(self) -> None:

sut = DeviceBus()
for _ in range(number_devices):
sut.map(0, MagicMock())
mock_device = MagicMock(spec_set=Device)
sut.map(0, mock_device)

assert repr(sut) == f'DeviceBus(devices={number_devices})'

Expand All @@ -31,35 +32,34 @@ def test_length_with_devices_in_order(self) -> None:
for _ in range(10):
size = randint(1, 1024)

device = MagicMock()
device.__len__.return_value = size
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = size

sut.map(address, device)
sut.map(address, mock_device)

address += size
assert len(sut) == address

def test_length_with_devices_in_diferent_order(self) -> None:
for _ in range(10):
device1 = MagicMock()
device1.__len__.return_value = randint(1, 1024)
device2 = MagicMock()
device2.__len__.return_value = randint(1, 1024)
mock_device1 = MagicMock(spec_set=Device)
mock_device1.__len__.return_value = randint(1, 1024)

mock_device2 = MagicMock(spec_set=Device)
mock_device2.__len__.return_value = randint(1, 1024)

sut = DeviceBus()
sut.map(5000, device1)
sut.map(0, device2)
sut.map(5000, mock_device1)
sut.map(0, mock_device2)

assert len(sut) == 5000 + len(device1)
assert len(sut) == 5000 + len(mock_device1)

def test_read_address_without_devices(self) -> None:
sut = DeviceBus()

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(0, 1024)]

assert exc_info.value.args[0] == 'Device not found for this address'

def test_read_address(self) -> None:
for i in range(10):
size1 = randint(1, 1024)
Expand All @@ -72,52 +72,46 @@ def test_read_address(self) -> None:
address2 = randint(start2, start2 + size2 - 1)
value2 = object()

device1 = MagicMock()
device1.__len__.return_value = size1
device1.__getitem__.return_value = value1
mock_device1 = MagicMock(spec_set=Device)
mock_device1.__len__.return_value = size1
mock_device1.__getitem__.return_value = value1

device2 = MagicMock()
device2.__len__.return_value = size2
device2.__getitem__.return_value = value2
mock_device2 = MagicMock(spec_set=Device)
mock_device2.__len__.return_value = size2
mock_device2.__getitem__.return_value = value2

sut = DeviceBus()
sut.map(start1, device1)
sut.map(start2, device2)
sut.map(start1, mock_device1)
sut.map(start2, mock_device2)

assert sut[address1] is value1
device1.__getitem__.assert_called_once_with(address1 - start1)
mock_device1.__getitem__.assert_called_once_with(address1 - start1)
assert sut[address2] is value2
device2.__getitem__.assert_called_once_with(address2 - start2)
mock_device2.__getitem__.assert_called_once_with(address2 - start2)

def test_read_address_not_found_device(self) -> None:
for _ in range(10):
size = randint(1, 1024)
address = randint(100, 1024)

device = MagicMock()
device.__len__.return_value = size
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = size

sut = DeviceBus()
sut.map(address, device)
sut.map(address, mock_device)

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(0, address - 1)]

assert exc_info.value.args[0] == 'Device not found for this address'

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(address + size, (address + size) * 2)]

assert exc_info.value.args[0] == 'Device not found for this address'

def test_write_address_without_devices(self) -> None:
sut = DeviceBus()

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(0, 1024)] = 0

assert exc_info.value.args[0] == 'Device not found for this address'

def test_write_address(self) -> None:
for i in range(10):
size1 = randint(1, 1024)
Expand All @@ -130,89 +124,83 @@ def test_write_address(self) -> None:
address2 = randint(start2, start2 + size2 - 1)
value2 = randint(0, 255)

device1 = MagicMock()
device1.__len__.return_value = size1
mock_device1 = MagicMock(spec_set=Device)
mock_device1.__len__.return_value = size1

device2 = MagicMock()
device2.__len__.return_value = size2
mock_device2 = MagicMock(spec_set=Device)
mock_device2.__len__.return_value = size2

sut = DeviceBus()
sut.map(start1, device1)
sut.map(start2, device2)
sut.map(start1, mock_device1)
sut.map(start2, mock_device2)

sut[address1] = value1
sut[address2] = value2

device1.__setitem__.assert_called_once_with(address1 - start1, value1)
device2.__setitem__.assert_called_once_with(address2 - start2, value2)
mock_device1.__setitem__.assert_called_once_with(address1 - start1, value1)
mock_device2.__setitem__.assert_called_once_with(address2 - start2, value2)

def test_write_address_not_found_device(self) -> None:
for _ in range(10):
size = randint(1, 1024)
address = randint(100, 1024)

device = MagicMock()
device.__len__.return_value = size
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = size

sut = DeviceBus()
sut.map(address, device)
sut.map(address, mock_device)

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(0, address - 1)] = 0

assert exc_info.value.args[0] == 'Device not found for this address'

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[randint(address + size, (address + size) * 2)] = 0

assert exc_info.value.args[0] == 'Device not found for this address'

def test_load_program(self) -> None:
for _ in range(10):
address = randint(0, 2048)
content = [randint(0, 255) for _ in range(randint(1, 512))]
program = BytesIO(bytes(content))

device = MagicMock()
device.__len__.return_value = 4096
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = 4096

sut = DeviceBus()
sut.map(0, device)
sut.map(0, mock_device)

sut.load_program(program, address)

assert device.__setitem__.call_count == len(content)
assert mock_device.__setitem__.call_count == len(content)
for i, value in enumerate(content):
device.__setitem__.assert_any_call(address + i, value)
mock_device.__setitem__.assert_any_call(address + i, value)

def test_unmap_without_device_mapped(self) -> None:
device = MagicMock()
mock_device = MagicMock(spec_set=Device)

sut = DeviceBus()

sut.unmap_device(device)
sut.unmap_device(mock_device)

def test_unmap_device(self) -> None:
for _ in range(10):
start = randint(0, 1024)
size = randint(1, 1024)
address = randint(start, start + size - 1)

device = MagicMock()
device.__len__.return_value = size
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = size

sut = DeviceBus()
sut.map(start, device)
sut.map(start, mock_device)

sut[address]

sut.unmap_device(device)
sut.unmap_device(mock_device)

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[address]

assert exc_info.value.args[0] == 'Device not found for this address'

def test_unmap_with_multiple_devices(self) -> None:
for i in range(10):
size1 = randint(1, 1024)
Expand All @@ -223,55 +211,51 @@ def test_unmap_with_multiple_devices(self) -> None:
start2 = randint(1024 if i % 2 else 0, 2048 - size2 if i % 2 else 1024 - size2)
address2 = randint(start2, start2 + size2 - 1)

device1 = MagicMock()
device1.__len__.return_value = size1
mock_device1 = MagicMock(spec_set=Device)
mock_device1.__len__.return_value = size1

device2 = MagicMock()
device2.__len__.return_value = size2
mock_device2 = MagicMock(spec_set=Device)
mock_device2.__len__.return_value = size2

sut = DeviceBus()
sut.map(start1, device1)
sut.map(start2, device2)
sut.map(start1, mock_device1)
sut.map(start2, mock_device2)

sut[address1]
sut[address2]

sut.unmap_device(device2)
sut.unmap_device(mock_device2)

sut[address1]
with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[address2]

assert exc_info.value.args[0] == 'Device not found for this address'

def test_unmap_without_address_mapped(self) -> None:
device = MagicMock()
mock_device = MagicMock(spec_set=Device)

sut = DeviceBus()

sut.unmap_address(device)
sut.unmap_address(mock_device)

def test_unmap_address(self) -> None:
for _ in range(10):
start = randint(0, 1024)
size = randint(1, 1024)
address = randint(start, start + size - 1)

device = MagicMock()
device.__len__.return_value = size
mock_device = MagicMock(spec_set=Device)
mock_device.__len__.return_value = size

sut = DeviceBus()
sut.map(start, device)
sut.map(start, mock_device)

sut[address]

sut.unmap_address(start)

with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[address]

assert exc_info.value.args[0] == 'Device not found for this address'

def test_unmap_with_multiple_addresses(self) -> None:
for i in range(10):
size1 = randint(1, 1024)
Expand All @@ -282,23 +266,21 @@ def test_unmap_with_multiple_addresses(self) -> None:
start2 = randint(1024 if i % 2 else 0, 2048 - size2 if i % 2 else 1024 - size2)
address2 = randint(start2, start2 + size2 - 1)

device1 = MagicMock()
device1.__len__.return_value = size1
mock_device1 = MagicMock(spec_set=Device)
mock_device1.__len__.return_value = size1

device2 = MagicMock()
device2.__len__.return_value = size2
mock_device2 = MagicMock(spec_set=Device)
mock_device2.__len__.return_value = size2

sut = DeviceBus()
sut.map(start1, device1)
sut.map(start2, device2)
sut.map(start1, mock_device1)
sut.map(start2, mock_device2)

sut[address1]
sut[address2]

sut.unmap_address(start2)

sut[address1]
with pytest.raises(RuntimeError) as exc_info:
with pytest.raises(RuntimeError, match='^Device not found for this address$'):
sut[address2]

assert exc_info.value.args[0] == 'Device not found for this address'

0 comments on commit a8e95b2

Please sign in to comment.