Trying to finish keyboard

This commit is contained in:
Thomas Farstrike
2025-11-17 09:31:40 +01:00
parent b8a61d13b8
commit 47fda6e69f
5 changed files with 532 additions and 20 deletions
+58
View File
@@ -0,0 +1,58 @@
"""
Manual test for the "abc" button bug with DEBUG OUTPUT.
Run with: ./scripts/run_desktop.sh tests/manual_test_abc_button_debug.py
This will show debug output when you click the "abc" button.
Watch the terminal to see what's happening!
"""
import lvgl as lv
from mpos.ui.keyboard import MposKeyboard
# Get active screen
screen = lv.screen_active()
screen.clean()
# Create title
title = lv.label(screen)
title.set_text("ABC Button Debug Test")
title.align(lv.ALIGN.TOP_MID, 0, 5)
# Create instructions
instructions = lv.label(screen)
instructions.set_text(
"Watch the TERMINAL output!\n"
"\n"
"1. Click '?123' to go to numbers mode\n"
"2. Click 'abc' to go back to lowercase\n"
"3. Check terminal for debug output\n"
"4. Check if comma appears in textarea"
)
instructions.set_style_text_align(lv.TEXT_ALIGN.LEFT, 0)
instructions.align(lv.ALIGN.TOP_LEFT, 10, 30)
# Create textarea
textarea = lv.textarea(screen)
textarea.set_size(280, 30)
textarea.set_one_line(True)
textarea.align(lv.ALIGN.TOP_MID, 0, 120)
textarea.set_placeholder_text("Type here...")
# Create keyboard
keyboard = MposKeyboard(screen)
keyboard.set_textarea(textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
print("\n" + "="*70)
print("ABC BUTTON DEBUG TEST")
print("="*70)
print("Instructions:")
print("1. The keyboard starts in LOWERCASE mode")
print("2. Click the '?123' button (bottom left) to switch to NUMBERS mode")
print("3. Click the 'abc' button (bottom left) to switch back to LOWERCASE")
print("4. Watch this terminal for [KEYBOARD DEBUG] messages")
print("5. Check if a comma appears in the textarea")
print("="*70)
print("\nWaiting for button clicks...")
print()
+107
View File
@@ -0,0 +1,107 @@
"""
Automated test that simulates clicking the abc button and shows debug output.
This will show us exactly what's happening when the abc button is clicked.
Usage:
Desktop: ./tests/unittest.sh tests/test_graphical_abc_button_debug.py
"""
import unittest
import lvgl as lv
from mpos.ui.keyboard import MposKeyboard
from graphical_test_helper import wait_for_render
class TestAbcButtonDebug(unittest.TestCase):
"""Test that shows debug output when clicking abc button."""
def setUp(self):
"""Set up test fixtures."""
self.screen = lv.obj()
self.screen.set_size(320, 240)
# Create textarea
self.textarea = lv.textarea(self.screen)
self.textarea.set_size(280, 40)
self.textarea.align(lv.ALIGN.TOP_MID, 0, 10)
self.textarea.set_one_line(True)
# Load screen
lv.screen_load(self.screen)
wait_for_render(5)
def tearDown(self):
"""Clean up."""
lv.screen_load(lv.obj())
wait_for_render(5)
def test_simulate_abc_button_click(self):
"""
Simulate clicking the abc button and show what happens.
"""
print("\n" + "="*70)
print("SIMULATING ABC BUTTON CLICK - WATCH FOR DEBUG OUTPUT")
print("="*70)
keyboard = MposKeyboard(self.screen)
keyboard.set_textarea(self.textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
wait_for_render(10)
# Start in lowercase, switch to numbers
print("\n>>> Switching to NUMBERS mode...")
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(10)
# Wait for debounce period to expire (150ms + margin)
import time
print(">>> Waiting 200ms for debounce period to expire...")
time.sleep(0.2)
# Clear textarea
self.textarea.set_text("")
print(f">>> Textarea cleared: '{self.textarea.get_text()}'")
# Find the "abc" button
abc_button_index = None
for i in range(100):
try:
text = keyboard.get_button_text(i)
if text == "abc":
abc_button_index = i
print(f">>> Found 'abc' button at index {abc_button_index}")
break
except:
pass
# Now simulate what happens when user TOUCHES the button
# When user touches a button, LVGL's button matrix:
# 1. Sets the button as selected
# 2. Triggers VALUE_CHANGED event
print(f"\n>>> Simulating user clicking button {abc_button_index}...")
print(f">>> Before click: textarea = '{self.textarea.get_text()}'")
print("\n--- DEBUG OUTPUT SHOULD APPEAR BELOW ---\n")
# Trigger the VALUE_CHANGED event which our handler catches
# This simulates a real button press
keyboard._keyboard.send_event(lv.EVENT.VALUE_CHANGED, None)
wait_for_render(5)
print("\n--- END DEBUG OUTPUT ---\n")
textarea_after = self.textarea.get_text()
print(f">>> After click: textarea = '{textarea_after}'")
if textarea_after != "":
print(f"\n❌ BUG CONFIRMED!")
print(f" Expected: '' (empty)")
print(f" Got: '{textarea_after}'")
else:
print(f"\n✓ No text added (but check debug output above)")
print("="*70)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,162 @@
"""
Test for the abc button click bug - comma being added.
This test actually CLICKS the abc button to reproduce the comma bug.
Usage:
Desktop: ./tests/unittest.sh tests/test_graphical_keyboard_abc_click_bug.py
"""
import unittest
import lvgl as lv
from mpos.ui.keyboard import MposKeyboard
from graphical_test_helper import wait_for_render
class TestAbcButtonClickBug(unittest.TestCase):
"""Test that clicking abc button doesn't add comma."""
def setUp(self):
"""Set up test fixtures."""
self.screen = lv.obj()
self.screen.set_size(320, 240)
# Create textarea
self.textarea = lv.textarea(self.screen)
self.textarea.set_size(280, 40)
self.textarea.align(lv.ALIGN.TOP_MID, 0, 10)
self.textarea.set_one_line(True)
# Load screen
lv.screen_load(self.screen)
wait_for_render(5)
def tearDown(self):
"""Clean up."""
lv.screen_load(lv.obj())
wait_for_render(5)
def test_clicking_abc_button_should_not_add_comma(self):
"""
Test that actually CLICKING the abc button doesn't add comma.
This is the REAL test - simulating actual user clicks.
"""
print("\n=== Testing ACTUAL CLICKING of abc button ===")
keyboard = MposKeyboard(self.screen)
keyboard.set_textarea(self.textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
wait_for_render(10)
# Start in lowercase, switch to numbers
print("Step 1: Switch to numbers mode")
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(10)
# Clear textarea
self.textarea.set_text("")
print(f" Textarea cleared: '{self.textarea.get_text()}'")
# Find the "abc" button
abc_button_index = None
for i in range(100):
try:
text = keyboard.get_button_text(i)
if text == "abc":
abc_button_index = i
print(f" Found 'abc' button at index {i}")
break
except:
pass
self.assertIsNotNone(abc_button_index, "Should find 'abc' button in numbers mode")
# ACTUALLY CLICK THE BUTTON
print(f"\nStep 2: ACTUALLY CLICK button index {abc_button_index}")
print(f" Before click: textarea='{self.textarea.get_text()}'")
# Simulate button click by sending CLICKED event to the button matrix
# Get the underlying button matrix object
btnm = keyboard._keyboard
# Method 1: Try to programmatically click the button
# This simulates what happens when user actually touches the button
btnm.set_selected_button(abc_button_index)
wait_for_render(2)
# Send the VALUE_CHANGED event
btnm.send_event(lv.EVENT.VALUE_CHANGED, None)
wait_for_render(5)
textarea_after = self.textarea.get_text()
print(f" After click: textarea='{textarea_after}'")
# Check if comma was added
if "," in textarea_after:
print(f"\n ❌ BUG CONFIRMED: Comma was added!")
print(f" Textarea contains: '{textarea_after}'")
self.fail(f"BUG: Clicking 'abc' button added comma! Textarea: '{textarea_after}'")
# Also check if anything else was added
if textarea_after != "":
print(f"\n ❌ BUG CONFIRMED: Something was added!")
print(f" Expected: ''")
print(f" Got: '{textarea_after}'")
self.fail(f"BUG: Clicking 'abc' button added text! Textarea: '{textarea_after}'")
print(f"\n ✓ SUCCESS: No text added, textarea is still empty")
def test_clicking_abc_multiple_times(self):
"""
Test clicking abc button multiple times in a row.
"""
print("\n=== Testing MULTIPLE clicks of abc button ===")
keyboard = MposKeyboard(self.screen)
keyboard.set_textarea(self.textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
wait_for_render(10)
for attempt in range(5):
print(f"\n--- Attempt {attempt + 1} ---")
# Go to numbers mode
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(10)
# Clear textarea
self.textarea.set_text("")
# Find abc button
abc_button_index = None
for i in range(100):
try:
text = keyboard.get_button_text(i)
if text == "abc":
abc_button_index = i
break
except:
pass
# Click it
print(f"Clicking 'abc' at index {abc_button_index}")
keyboard._keyboard.set_selected_button(abc_button_index)
wait_for_render(2)
keyboard._keyboard.send_event(lv.EVENT.VALUE_CHANGED, None)
wait_for_render(5)
textarea_text = self.textarea.get_text()
print(f" Result: textarea='{textarea_text}'")
if textarea_text != "":
print(f" ❌ FAIL on attempt {attempt + 1}: Got '{textarea_text}'")
self.fail(f"Attempt {attempt + 1}: Clicking 'abc' added '{textarea_text}'")
else:
print(f" ✓ OK")
print("\n✓ SUCCESS: All 5 attempts worked correctly")
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,167 @@
"""
Test for rapid mode switching bug (clicking ?123/abc rapidly).
This test reproduces:
1. Comma being added when clicking "abc" button
2. Intermittent crashes when rapidly clicking mode switch buttons
Usage:
Desktop: ./tests/unittest.sh tests/test_graphical_keyboard_rapid_mode_switch.py
"""
import unittest
import lvgl as lv
from mpos.ui.keyboard import MposKeyboard
from graphical_test_helper import wait_for_render
class TestRapidModeSwitching(unittest.TestCase):
"""Test rapid mode switching between lowercase and numbers."""
def setUp(self):
"""Set up test fixtures."""
self.screen = lv.obj()
self.screen.set_size(320, 240)
# Create textarea
self.textarea = lv.textarea(self.screen)
self.textarea.set_size(280, 40)
self.textarea.align(lv.ALIGN.TOP_MID, 0, 10)
self.textarea.set_one_line(True)
# Load screen
lv.screen_load(self.screen)
wait_for_render(5)
def tearDown(self):
"""Clean up."""
lv.screen_load(lv.obj())
wait_for_render(5)
def test_rapid_clicking_abc_button(self):
"""
Rapidly click the "abc" button to reproduce the comma bug and crash.
Expected: Clicking "abc" should NOT add comma to textarea
Bug: Comma is being added, suggesting button index confusion
"""
print("\n=== Testing rapid clicking of abc button ===")
keyboard = MposKeyboard(self.screen)
keyboard.set_textarea(self.textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
wait_for_render(10)
# Start in lowercase, switch to numbers
print("Step 1: Switch to numbers mode")
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(10) # Give time to settle
# Clear textarea
self.textarea.set_text("")
# Now find the "abc" button
abc_button_index = None
for i in range(100):
try:
text = keyboard.get_button_text(i)
if text == "abc":
abc_button_index = i
print(f" Found 'abc' button at index {i}")
break
except:
pass
self.assertIsNotNone(abc_button_index, "Should find 'abc' button in numbers mode")
# Simulate rapid clicking by alternating modes
print("\nStep 2: Rapidly switch modes by simulating abc/?123 clicks")
for i in range(10):
# Get current mode
current_mode = keyboard._keyboard.get_mode()
# Clear text before click
textarea_before = self.textarea.get_text()
print(f" Click {i+1}: mode={current_mode}, textarea='{textarea_before}'")
if current_mode == MposKeyboard.MODE_NUMBERS or current_mode == lv.keyboard.MODE.NUMBER:
# Click "abc" to go to lowercase
keyboard.set_mode(MposKeyboard.MODE_LOWERCASE)
else:
# Click "?123" to go to numbers
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(2)
# Check if text changed (BUG: should not change!)
textarea_after = self.textarea.get_text()
if textarea_after != textarea_before:
print(f" ERROR: Text changed from '{textarea_before}' to '{textarea_after}'")
self.fail(f"BUG: Clicking mode switch button added '{textarea_after}' to textarea")
# Verify textarea is still empty
final_text = self.textarea.get_text()
print(f"\nFinal textarea text: '{final_text}'")
self.assertEqual(final_text, "",
f"Textarea should be empty after mode switches, but contains: '{final_text}'")
print("SUCCESS: No spurious characters added during rapid mode switching")
def test_button_indices_after_mode_switch(self):
"""
Test that button indices remain consistent after mode switches.
This helps identify if the comma bug is due to button index confusion.
"""
print("\n=== Testing button indices after mode switch ===")
keyboard = MposKeyboard(self.screen)
keyboard.set_textarea(self.textarea)
keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
wait_for_render(10)
# Map button indices in lowercase mode
print("\nButton indices in LOWERCASE mode:")
keyboard.set_mode(MposKeyboard.MODE_LOWERCASE)
wait_for_render(10)
lowercase_buttons = {}
for i in range(40):
try:
text = keyboard.get_button_text(i)
if text in ["?123", ",", "abc", lv.SYMBOL.UP]:
lowercase_buttons[text] = i
print(f" '{text}' at index {i}")
except:
pass
# Map button indices in numbers mode
print("\nButton indices in NUMBERS mode:")
keyboard.set_mode(MposKeyboard.MODE_NUMBERS)
wait_for_render(10)
numbers_buttons = {}
for i in range(40):
try:
text = keyboard.get_button_text(i)
if text in ["?123", ",", "abc", "=\\<"]:
numbers_buttons[text] = i
print(f" '{text}' at index {i}")
except:
pass
# Check if comma and abc are at same index
if "," in lowercase_buttons and "abc" in numbers_buttons:
comma_idx = lowercase_buttons[","]
abc_idx = numbers_buttons["abc"]
print(f"\nComparison:")
print(f" Comma in lowercase: index {comma_idx}")
print(f" 'abc' in numbers: index {abc_idx}")
if comma_idx == abc_idx:
print(" WARNING: Comma and 'abc' share the same index!")
print(" This could explain why comma appears when clicking 'abc'")
if __name__ == "__main__":
unittest.main()