Skip to content

test(ssd1327): Improve mock scenarios beyond smoke tests.#301

Merged
nedseb merged 2 commits intomainfrom
test/ssd1327-improve-mocks
Mar 28, 2026
Merged

test(ssd1327): Improve mock scenarios beyond smoke tests.#301
nedseb merged 2 commits intomainfrom
test/ssd1327-improve-mocks

Conversation

@nedseb
Copy link
Copy Markdown
Contributor

@nedseb nedseb commented Mar 28, 2026

Summary

Replace "does not crash" smoke tests with behavioral assertions that verify actual I2C commands and framebuffer state.

Mock tests: from 6 to 16

Category Test What it verifies
Commands Power off SET_FN_SELECT_A + disable + SET_DISP off (exact sequence)
Commands Power on SET_FN_SELECT_A + enable + SET_DISP on (exact sequence)
Commands Contrast SET_CONTRAST + value byte
Commands Invert on SET_DISP_MODE = 0xA7 (inverted)
Commands Invert off SET_DISP_MODE = 0xA4 (normal)
Commands Rotate Exact 10-command sequence (power_off + offset + remap + power_on)
Commands Show Col/row address prefix + framebuffer payload size (8192 bytes)
Commands Init display Unlock (0xFD, 0x12) → display off (0xAE) → display on (0xAF)
Framebuffer Pixel Read-back verifies pixel value at (10,20)
Framebuffer Fill Verifies 5 corner pixels (0,0), (63,63), (127,0), (0,127), (127,127)
Framebuffer Line Bresenham start pixel verified at (0,0)
Framebuffer Scroll Pixel at (5,0) shifts to (6,0), original position cleared
Smoke Fill black/white Does not crash
Smoke Text rendering Does not crash
Smoke Show Does not crash

Infrastructure improvements

  • FrameBuffer.pixel() — read/write support for GS4_HMSB format with proper 4-bit masking
  • FrameBuffer.line() — Bresenham's algorithm implementation
  • FrameBuffer.scroll() — buffer shift with proper pixel copying

Note on I2C vs SPI

The STeaMi board uses SPI for the display. Mock tests use the I2C variant (WS_OLED_128X128_I2C) because FakeI2C provides command logging. The command sequences are identical between I2C and SPI.

Closes #261

Test plan

  • 16 SSD1327 mock tests pass (was 6)
  • make ci passes (206 mock tests + 271 example validations)

Copilot AI review requested due to automatic review settings March 28, 2026 20:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the SSD1327 scenario tests to assert behavioral correctness by validating the I2C command stream (instead of only “does not crash”), and enhances the FrameBuffer stub to support framebuffer state assertions.

Changes:

  • Replace/augment SSD1327 mock smoke tests with checks against FakeI2C write logs for power_on, power_off, contrast, invert, rotate, and show.
  • Add framebuffer state assertions for pixel() and fill() in SSD1327 scenarios.
  • Implement GS4_HMSB pixel() read/write behavior in tests/fake_machine/framebuf_stub.py.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
tests/scenarios/ssd1327.yaml Adds mock assertions that inspect the I2C command/data stream and framebuffer state for SSD1327 behaviors.
tests/fake_machine/framebuf_stub.py Implements GS4_HMSB pixel() read/write to enable framebuffer-based assertions in mock tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/fake_machine/framebuf_stub.py Outdated
return (byte >> 4) & 0x0F if x % 2 == 0 else byte & 0x0F
byte = self.buffer[idx]
if x % 2 == 0:
self.buffer[idx] = (col << 4) | (byte & 0x0F)
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FrameBuffer.pixel() setter path for even x writes (col << 4) without masking to 4 bits. This is inconsistent with fill() and the odd-x branch (which both mask with & 0x0F) and can raise ValueError when col > 15 because bytearray elements must be 0-255. Mask col (e.g., col &= 0x0F) before shifting/writing so the stub matches GS4 semantics.

Suggested change
self.buffer[idx] = (col << 4) | (byte & 0x0F)
self.buffer[idx] = ((col & 0x0F) << 4) | (byte & 0x0F)

Copilot uses AI. Check for mistakes.
Comment thread tests/scenarios/ssd1327.yaml Outdated
Comment on lines +122 to +123
# power_off (3 cmds) + SET_DISP_OFFSET, 128, SET_SEG_REMAP, 0x42 + power_on (3 cmds)
result = 0xA2 in cmds and 0x42 in cmds and 128 in cmds
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rotate mock assertion is very loose: 0xA2 in cmds and 0x42 in cmds and 128 in cmds can pass even if the commands are in the wrong order, SET_SEG_REMAP (0xA0) is never sent, or the parameters are not associated with the right command. Since this test is meant to validate the exact I2C command sequence, consider asserting the full expected cmds list (power_off + offset + remap + power_on) in order.

Suggested change
# power_off (3 cmds) + SET_DISP_OFFSET, 128, SET_SEG_REMAP, 0x42 + power_on (3 cmds)
result = 0xA2 in cmds and 0x42 in cmds and 128 in cmds
# Expect: power_off (FN_SELECT_A disable + DISP off) +
# SET_DISP_OFFSET, 128, SET_SEG_REMAP, 0x42 +
# power_on (FN_SELECT_A enable + DISP on)
expected_cmds = [0xAB, 0x00, 0xAE, 0xA2, 128, 0xA0, 0x42, 0xAB, 0x01, 0xAF]
result = cmds == expected_cmds

Copilot uses AI. Check for mistakes.
Comment thread tests/scenarios/ssd1327.yaml Outdated
Comment on lines +135 to +141
cmds = [data[1] for reg, data in log if reg is None and len(data) == 2 and data[0] == 0x80]
# SET_COL_ADDR (0x15), start, end, SET_ROW_ADDR (0x75), start, end
has_col = 0x15 in cmds
has_row = 0x75 in cmds
# Check data was sent (writevto with 0x40 prefix)
has_data = any(reg is None and len(data) > 2 and data[0] == 0x40 for reg, data in log)
result = has_col and has_row and has_data
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The show mock test only checks that SET_COL_ADDR/SET_ROW_ADDR appear somewhere in cmds, but does not verify the start/end values or that they are sent before the data burst. This can produce false positives if the driver changes behavior but still emits those bytes. Consider asserting the exact first 6 command bytes/params (0x15, col_start, col_end, 0x75, row_start, row_end) and then validating the subsequent data write begins with 0x40 and has the expected payload length.

Suggested change
cmds = [data[1] for reg, data in log if reg is None and len(data) == 2 and data[0] == 0x80]
# SET_COL_ADDR (0x15), start, end, SET_ROW_ADDR (0x75), start, end
has_col = 0x15 in cmds
has_row = 0x75 in cmds
# Check data was sent (writevto with 0x40 prefix)
has_data = any(reg is None and len(data) > 2 and data[0] == 0x40 for reg, data in log)
result = has_col and has_row and has_data
# Collect command bytes (0x80 prefix, 1-byte payload) in order
cmd_bytes = [data[1] for reg, data in log if reg is None and len(data) == 2 and data[0] == 0x80]
# Expected: SET_COL_ADDR (0x15), col_start, col_end, SET_ROW_ADDR (0x75), row_start, row_end
# For a 128x128 SSD1327 panel, columns 0-63 (0x00-0x3F) and rows 0-127 (0x00-0x7F)
expected_prefix = [0x15, 0x00, 0x3F, 0x75, 0x00, 0x7F]
# Find first data burst (writevto with 0x40 prefix and payload > 0)
data_writes = [data for reg, data in log if reg is None and len(data) > 2 and data[0] == 0x40]
if not data_writes:
result = False
else:
first_data = data_writes[0]
# Determine expected framebuffer payload length: width * height / 2 (4-bit pixels)
width = getattr(dev, "width", 128)
height = getattr(dev, "height", 128)
expected_payload_len = (width * height) // 2
actual_payload_len = len(first_data) - 1 # exclude 0x40 prefix
result = (cmd_bytes[:6] == expected_prefix) and (actual_payload_len == expected_payload_len)

Copilot uses AI. Check for mistakes.
Comment thread tests/scenarios/ssd1327.yaml Outdated
action: script
script: |
dev.fill(7)
result = dev.framebuf.pixel(0, 0) == 7 and dev.framebuf.pixel(63, 63) == 7
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name says "Fill sets all pixels", but the assertion only samples (0,0) and (63,63) on a 128x128 buffer. This won't catch issues in the second half of rows/columns (or the last byte in the buffer). To better match the intent, also sample an edge pixel like (127,127) (and/or (127,0)) after fill(7).

Suggested change
result = dev.framebuf.pixel(0, 0) == 7 and dev.framebuf.pixel(63, 63) == 7
result = (
dev.framebuf.pixel(0, 0) == 7 and
dev.framebuf.pixel(63, 63) == 7 and
dev.framebuf.pixel(127, 0) == 7 and
dev.framebuf.pixel(0, 127) == 7 and
dev.framebuf.pixel(127, 127) == 7
)

Copilot uses AI. Check for mistakes.
@nedseb nedseb force-pushed the test/ssd1327-improve-mocks branch from aa79a97 to 1a99e7b Compare March 28, 2026 20:55
@nedseb
Copy link
Copy Markdown
Contributor Author

nedseb commented Mar 28, 2026

Les 4 commentaires Copilot ont été corrigés dans 024057a :

  1. pixel() masquage col — ajouté col & 0x0F avant le shift pour éviter un dépassement sur col > 15.
  2. rotate assertion trop lâche — remplacé par une assertion stricte de la séquence complète de 10 commandes dans l'ordre exact.
  3. show assertion insuffisante — vérifie maintenant les 6 premiers bytes de commande (col_start, col_end, row_start, row_end) et la taille exacte du payload de données (8192 bytes pour 128×128 en GS4).
  4. fill ne teste que 2 pixels — ajouté 3 pixels supplémentaires aux coins (127,0), (0,127), (127,127).

@nedseb nedseb merged commit 8ad7a4e into main Mar 28, 2026
10 checks passed
@nedseb nedseb deleted the test/ssd1327-improve-mocks branch March 28, 2026 21:00
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test(ssd1327): Improve mock scenarios beyond smoke tests.

2 participants