Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Numpy for all revisions #641

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add tests
  • Loading branch information
hchargois committed Jan 6, 2025
commit 3537e324972bb7999e36414f0c55b7d1ab2bcca4
Empty file added tests/__init__.py
Empty file.
Empty file added tests/library/__init__.py
Empty file.
Empty file added tests/library/lcd/__init__.py
Empty file.
82 changes: 82 additions & 0 deletions tests/library/lcd/golden/rev_a_display_pil_image_landscape.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions tests/library/lcd/golden/rev_a_display_pil_image_portrait.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions tests/library/lcd/golden/rev_a_display_pil_image_reverse_portrait.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/library/lcd/golden/rev_a_set_brightness.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
write 2fc00000006e
82 changes: 82 additions & 0 deletions tests/library/lcd/golden/rev_b_display_pil_image_landscape.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions tests/library/lcd/golden/rev_b_display_pil_image_portrait.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions tests/library/lcd/golden/rev_b_display_pil_image_reverse_portrait.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/library/lcd/golden/rev_b_set_brightness.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
write ce0000000000000000ce

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions tests/library/lcd/golden/rev_c_display_pil_image_portrait.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/library/lcd/golden/rev_c_set_brightness.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
write 7bef69000000010000003f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
4,881 changes: 4,881 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_landscape.txt

Large diffs are not rendered by default.

639 changes: 639 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_patch_landscape.txt

Large diffs are not rendered by default.

639 changes: 639 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_patch_portrait.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

4,881 changes: 4,881 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_portrait.txt

Large diffs are not rendered by default.

4,881 changes: 4,881 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_reverse_landscape.txt

Large diffs are not rendered by default.

4,881 changes: 4,881 additions & 0 deletions tests/library/lcd/golden/rev_d_display_pil_image_reverse_portrait.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions tests/library/lcd/golden/rev_d_set_brightness.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
write 4343007d
write 4343007d
25 changes: 25 additions & 0 deletions tests/library/lcd/sample_image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from PIL import Image


def generate_sample_image(width, height):
"""Generates a sample image that has red, green, blue and black in each
corner and the other pixels interpolated linearly."""
img = Image.new("RGB", (width, height))
pixels = img.load()
assert pixels is not None

for x in range(width):
for y in range(height):
# Calculate interpolation weights based on position
wx = x / (width - 1) # Horizontal weight (0 to 1)
wy = y / (height - 1) # Vertical weight (0 to 1)

# Linear interpolation for each color channel
r = int((1 - wx) * (1 - wy) * 255) # Top-left (Red)
g = int(wx * (1 - wy) * 255) # Top-right (Green)
b = int(wy * (1 - wx) * 255) # Bottom-left (Blue)

# Set the pixel value
pixels[x, y] = (r, g, b)

return img
72 changes: 72 additions & 0 deletions tests/library/lcd/serial_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import os
from unittest.mock import Mock
import unittest

# Set the environment variable to any non-empty string when running the tests
# to record golden files instead of asserting their expectations
RECORD_GOLDEN = bool(os.getenv("RECORD_GOLDEN"))

# Set the environment variable to any non-empty string when running the tests
# to enable benchmarking, i.e. make the serial reads/writes a no-op; and don't
# assert the test expectations. That way, the time to run the tests should be
# mostly representative of the serialization time.
BENCHMARK = bool(os.getenv("BENCHMARK"))

class MockSerial(Mock):
def expect_golden(self, tc: unittest.TestCase, fn: str):
golden_dir = os.path.join(os.path.dirname(__file__), "golden")
full_path = os.path.join(golden_dir, fn + ".txt")

if RECORD_GOLDEN:
with open(full_path, "w+", encoding="ascii") as f:
for method, args, _ in self.mock_calls:
if method == "write":
assert len(args) == 1
f.write(f"write {args[0].hex()}\n")
elif method == "read":
assert len(args) == 1
f.write(f"read {args[0]}\n")
# don't record the other methods

else:
with open(full_path, "r", encoding="ascii") as f:
expected = list(filter(lambda l: l.strip() != "", f.readlines()))

mock_calls = []
for method, args, _ in self.mock_calls:
if method not in ["write", "read"]:
continue
mock_calls.append((method, args))

tc.assertEqual(len(mock_calls), len(expected))
for call, exp in zip(mock_calls, expected):
exp_name, exp_arg = exp.split()
call_name, call_args = call
tc.assertEqual(call_name, exp_name)
if call_name == "write":
tc.assertEqual(call_args, (bytes.fromhex(exp_arg),))
elif call_name == "read":
tc.assertEqual(call_args, (int(exp_arg),))

class NoopSerial:
def write(self, data):
pass

def read(self, size):
pass

def close(self):
pass

def reset_input_buffer(self):
pass

def expect_golden(self, tc: unittest.TestCase, fn: str):
pass


def new_testing_serial():
if BENCHMARK:
return NoopSerial()
else:
return MockSerial()
12 changes: 12 additions & 0 deletions tests/library/lcd/test_color.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import unittest

from library.lcd.color import parse_color


class TestColor(unittest.TestCase):
def test_parse_color(self):
self.assertEqual(parse_color("255, 0, 0"), (255, 0, 0))
self.assertEqual(parse_color("red"), (255, 0, 0))
self.assertEqual(parse_color("#f00"), (255, 0, 0))
self.assertEqual(parse_color("#ff0000"), (255, 0, 0))
self.assertEqual(parse_color((255, 0, 0)), (255, 0, 0))
84 changes: 84 additions & 0 deletions tests/library/lcd/test_lcd_comm_rev_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import unittest

from library.lcd.lcd_comm_rev_a import LcdCommRevA, Orientation

from .serial_mock import new_testing_serial
from .sample_image import generate_sample_image


class MockedLcdCommRevA(LcdCommRevA):
def openSerial(self):
self.lcd_serial = new_testing_serial()

def expect_golden(self, tc: unittest.TestCase, fn: str):
self.lcd_serial.expect_golden(tc, fn)

sample_img_portrait = generate_sample_image(320, 480)
sample_img_landscape = generate_sample_image(480, 320)

class TestLcdCommRevA(unittest.TestCase):
def test_set_brightness(self):
lcd = MockedLcdCommRevA()
lcd.SetBrightness()

lcd.expect_golden(self, "rev_a_set_brightness")

# display_pil_image_<orientation> : display a full-screen image

def test_display_pil_image_portrait(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_a_display_pil_image_portrait")

def test_display_pil_image_landscape(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_a_display_pil_image_landscape")

def test_display_pil_image_reverse_portrait(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_a_display_pil_image_reverse_portrait")

def test_display_pil_image_reverse_landscape(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_a_display_pil_image_reverse_landscape")

# display_pil_image_patch_<orientation> : display a less-than-full-screen image at a given location

def test_display_pil_image_patch_portrait(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_a_display_pil_image_patch_portrait")

def test_display_pil_image_patch_landscape(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_a_display_pil_image_patch_landscape")

def test_display_pil_image_patch_reverse_portrait(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_a_display_pil_image_patch_reverse_portrait")

def test_display_pil_image_patch_reverse_landscape(self):
lcd = MockedLcdCommRevA()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_a_display_pil_image_patch_reverse_landscape")
84 changes: 84 additions & 0 deletions tests/library/lcd/test_lcd_comm_rev_b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import unittest

from library.lcd.lcd_comm_rev_b import LcdCommRevB, Orientation

from .serial_mock import new_testing_serial
from .sample_image import generate_sample_image


class MockedLcdCommRevB(LcdCommRevB):
def openSerial(self):
self.lcd_serial = new_testing_serial()

def expect_golden(self, tc: unittest.TestCase, fn: str):
self.lcd_serial.expect_golden(tc, fn)

sample_img_portrait = generate_sample_image(320, 480)
sample_img_landscape = generate_sample_image(480, 320)

class TestLcdCommRevB(unittest.TestCase):
def test_set_brightness(self):
lcd = MockedLcdCommRevB()
lcd.SetBrightness()

lcd.expect_golden(self, "rev_b_set_brightness")

# display_pil_image_<orientation> : display a full-screen image

def test_display_pil_image_portrait(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_b_display_pil_image_portrait")

def test_display_pil_image_landscape(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_b_display_pil_image_landscape")

def test_display_pil_image_reverse_portrait(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_b_display_pil_image_reverse_portrait")

def test_display_pil_image_reverse_landscape(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_b_display_pil_image_reverse_landscape")

# display_pil_image_patch_<orientation> : display a less-than-full-screen image at a given location

def test_display_pil_image_patch_portrait(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_b_display_pil_image_patch_portrait")

def test_display_pil_image_patch_landscape(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_b_display_pil_image_patch_landscape")

def test_display_pil_image_patch_reverse_portrait(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_b_display_pil_image_patch_reverse_portrait")

def test_display_pil_image_patch_reverse_landscape(self):
lcd = MockedLcdCommRevB()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_b_display_pil_image_patch_reverse_landscape")
84 changes: 84 additions & 0 deletions tests/library/lcd/test_lcd_comm_rev_c.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import unittest

from library.lcd.lcd_comm_rev_c import LcdCommRevC, Orientation

from .serial_mock import new_testing_serial
from .sample_image import generate_sample_image


class MockedLcdCommRevC(LcdCommRevC):
def openSerial(self):
self.lcd_serial = new_testing_serial()

def expect_golden(self, tc: unittest.TestCase, fn: str):
self.lcd_serial.expect_golden(tc, fn)

sample_img_portrait = generate_sample_image(480, 800)
sample_img_landscape = generate_sample_image(800, 480)

class TestLcdCommRevC(unittest.TestCase):
def test_set_brightness(self):
lcd = MockedLcdCommRevC()
lcd.SetBrightness()

lcd.expect_golden(self, "rev_c_set_brightness")

# display_pil_image_<orientation> : display a full-screen image

def test_display_pil_image_portrait(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_c_display_pil_image_portrait")

def test_display_pil_image_landscape(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_c_display_pil_image_landscape")

def test_display_pil_image_reverse_portrait(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait)

lcd.expect_golden(self, "rev_c_display_pil_image_reverse_portrait")

def test_display_pil_image_reverse_landscape(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape)

lcd.expect_golden(self, "rev_c_display_pil_image_reverse_landscape")

# display_pil_image_patch_<orientation> : display a less-than-full-screen image at a given location

def test_display_pil_image_patch_portrait(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_c_display_pil_image_patch_portrait")

def test_display_pil_image_patch_landscape(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_c_display_pil_image_patch_landscape")

def test_display_pil_image_patch_reverse_portrait(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.REVERSE_PORTRAIT)
lcd.DisplayPILImage(sample_img_portrait, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_c_display_pil_image_patch_reverse_portrait")

def test_display_pil_image_patch_reverse_landscape(self):
lcd = MockedLcdCommRevC()
lcd.SetOrientation(orientation=Orientation.REVERSE_LANDSCAPE)
lcd.DisplayPILImage(sample_img_landscape, x=10, y=20, image_width=100, image_height=200)

lcd.expect_golden(self, "rev_c_display_pil_image_patch_reverse_landscape")
Loading