@@ -72,6 +72,32 @@ def tearDown(self):
7272 except :
7373 pass # Already on launcher or error
7474
75+ def _find_and_click_settings_button (self , screen , use_send_event = True ):
76+ """Find and click the settings button using lv.SYMBOL.SETTINGS.
77+
78+ Args:
79+ screen: LVGL screen object to search
80+ use_send_event: If True (default), use send_event() which is more reliable.
81+ If False, use simulate_click() with coordinates.
82+
83+ Returns True if button was found and clicked, False otherwise.
84+ """
85+ settings_button = find_button_with_text (screen , lv .SYMBOL .SETTINGS )
86+ if settings_button :
87+ coords = get_widget_coords (settings_button )
88+ print (f"Found settings button at ({ coords ['center_x' ]} , { coords ['center_y' ]} )" )
89+ if use_send_event :
90+ # Use send_event for more reliable button triggering
91+ settings_button .send_event (lv .EVENT .CLICKED , None )
92+ print ("Clicked settings button using send_event()" )
93+ else :
94+ simulate_click (coords ['center_x' ], coords ['center_y' ], press_duration_ms = 100 )
95+ print ("Clicked settings button using simulate_click()" )
96+ return True
97+ else :
98+ print ("Settings button not found via lv.SYMBOL.SETTINGS" )
99+ return False
100+
75101 def test_settings_button_click_no_crash (self ):
76102 """
77103 Test that clicking the settings button doesn't cause a segfault.
@@ -83,7 +109,7 @@ def test_settings_button_click_no_crash(self):
83109 1. Start camera app
84110 2. Wait for camera to initialize
85111 3. Capture initial screenshot
86- 4. Click settings button (top-right corner )
112+ 4. Click settings button (found dynamically by lv.SYMBOL.SETTINGS )
87113 5. Verify settings dialog opened
88114 6. If we get here without crash, test passes
89115 """
@@ -108,18 +134,12 @@ def test_settings_button_click_no_crash(self):
108134 print (f"\n Capturing initial screenshot: { screenshot_path } " )
109135 capture_screenshot (screenshot_path , width = 320 , height = 240 )
110136
111- # Find and click settings button
112- # The settings button is positioned at TOP_RIGHT with offset (0, 60)
113- # On a 320x240 screen, this is approximately x=260, y=90
114- # We'll click slightly inside the button to ensure we hit it
115- settings_x = 300 # Right side of screen, inside the 60px button
116- settings_y = 100 # 60px down from top, center of 60px button
137+ # Find and click settings button dynamically
138+ found = self ._find_and_click_settings_button (screen )
139+ self .assertTrue (found , "Settings button with lv.SYMBOL.SETTINGS not found on screen" )
117140
118- print (f"\n Clicking settings button at ({ settings_x } , { settings_y } )" )
119- simulate_click (settings_x , settings_y , press_duration_ms = 100 )
120-
121- # Wait for settings dialog to appear
122- wait_for_render (iterations = 20 )
141+ # Wait for settings dialog to appear - needs more time for Activity transition
142+ wait_for_render (iterations = 50 )
123143
124144 # Get screen again (might have changed after navigation)
125145 screen = lv .screen_active ()
@@ -128,19 +148,26 @@ def test_settings_button_click_no_crash(self):
128148 print ("\n Screen labels after clicking settings:" )
129149 print_screen_labels (screen )
130150
131- # Verify settings screen opened
132- # Look for "Camera Settings" or "resolution" text
133- has_settings_ui = (
134- verify_text_present (screen , "Camera Settings" ) or
135- verify_text_present (screen , "Resolution" ) or
136- verify_text_present (screen , "resolution" ) or
137- verify_text_present (screen , "Save" ) or
138- verify_text_present (screen , "Cancel" )
139- )
151+ # Verify settings screen opened by looking for the Save button
152+ # This is more reliable than text search since buttons are always present
153+ save_button = find_button_with_text (screen , "Save" )
154+ cancel_button = find_button_with_text (screen , "Cancel" )
155+
156+ has_settings_ui = save_button is not None or cancel_button is not None
157+
158+ # Also try text-based verification as fallback
159+ if not has_settings_ui :
160+ has_settings_ui = (
161+ verify_text_present (screen , "Camera Settings" ) or
162+ verify_text_present (screen , "Resolution" ) or
163+ verify_text_present (screen , "resolution" ) or
164+ verify_text_present (screen , "Basic" ) or # Tab name
165+ verify_text_present (screen , "Color Mode" ) # Setting name
166+ )
140167
141168 self .assertTrue (
142169 has_settings_ui ,
143- "Settings screen did not open (no expected UI elements found)"
170+ "Settings screen did not open (no Save/Cancel buttons or expected UI elements found)"
144171 )
145172
146173 # Capture screenshot of settings dialog
@@ -151,15 +178,68 @@ def test_settings_button_click_no_crash(self):
151178 # If we got here without segfault, the test passes!
152179 print ("\n ✓ Settings button clicked successfully without crash!" )
153180
181+ def _find_and_click_button (self , screen , text , use_send_event = True ):
182+ """Find and click a button by its text label.
183+
184+ Args:
185+ screen: LVGL screen object to search
186+ text: Text to search for in button labels
187+ use_send_event: If True (default), use send_event() which is more reliable.
188+ If False, use simulate_click() with coordinates.
189+
190+ Returns True if button was found and clicked, False otherwise.
191+ """
192+ button = find_button_with_text (screen , text )
193+ if button :
194+ coords = get_widget_coords (button )
195+ print (f"Found '{ text } ' button at ({ coords ['center_x' ]} , { coords ['center_y' ]} )" )
196+ if use_send_event :
197+ # Use send_event for more reliable button triggering
198+ button .send_event (lv .EVENT .CLICKED , None )
199+ print (f"Clicked '{ text } ' button using send_event()" )
200+ else :
201+ simulate_click (coords ['center_x' ], coords ['center_y' ], press_duration_ms = 100 )
202+ print (f"Clicked '{ text } ' button using simulate_click()" )
203+ return True
204+ else :
205+ print (f"Button with text '{ text } ' not found" )
206+ return False
207+
208+ def _find_dropdown (self , screen ):
209+ """Find a dropdown widget on the screen.
210+
211+ Returns the dropdown widget or None if not found.
212+ """
213+ def find_dropdown_recursive (obj ):
214+ # Check if this object is a dropdown
215+ try :
216+ if obj .__class__ .__name__ == 'dropdown' or hasattr (obj , 'get_selected' ):
217+ # Verify it's actually a dropdown by checking for dropdown-specific method
218+ if hasattr (obj , 'get_options' ):
219+ return obj
220+ except :
221+ pass
222+
223+ # Check children
224+ child_count = obj .get_child_count ()
225+ for i in range (child_count ):
226+ child = obj .get_child (i )
227+ result = find_dropdown_recursive (child )
228+ if result :
229+ return result
230+ return None
231+
232+ return find_dropdown_recursive (screen )
233+
154234 def test_resolution_change_no_crash (self ):
155235 """
156236 Test that changing resolution doesn't cause a crash.
157237
158238 This tests the full resolution change workflow:
159239 1. Start camera app
160- 2. Open settings
161- 3. Change resolution
162- 4. Save settings
240+ 2. Open settings (found dynamically by lv.SYMBOL.SETTINGS)
241+ 3. Change resolution via dropdown
242+ 4. Save settings (found dynamically by "Save" text)
163243 5. Verify camera continues working
164244
165245 This verifies fixes for:
@@ -176,61 +256,63 @@ def test_resolution_change_no_crash(self):
176256 # Wait for camera to initialize
177257 wait_for_render (iterations = 30 )
178258
179- # Click settings button
259+ # Click settings button dynamically
260+ screen = lv .screen_active ()
180261 print ("\n Opening settings..." )
181- simulate_click (290 , 90 , press_duration_ms = 100 )
262+ found = self ._find_and_click_settings_button (screen )
263+ self .assertTrue (found , "Settings button with lv.SYMBOL.SETTINGS not found on screen" )
182264 wait_for_render (iterations = 20 )
183265
184266 screen = lv .screen_active ()
185267
186- # Try to find the dropdown/resolution selector
187- # The CameraSettingsActivity creates a dropdown widget
188- # Let's look for any dropdown on screen
268+ # Try to find the dropdown/resolution selector dynamically
189269 print ("\n Looking for resolution dropdown..." )
270+ dropdown = self ._find_dropdown (screen )
271+
272+ if dropdown :
273+ # Click the dropdown to open it
274+ coords = get_widget_coords (dropdown )
275+ print (f"Found dropdown at ({ coords ['center_x' ]} , { coords ['center_y' ]} )" )
276+ simulate_click (coords ['center_x' ], coords ['center_y' ], press_duration_ms = 100 )
277+ wait_for_render (iterations = 15 )
278+
279+ # Get current selection and try to change it
280+ try :
281+ current = dropdown .get_selected ()
282+ option_count = dropdown .get_option_count ()
283+ print (f"Dropdown has { option_count } options, current selection: { current } " )
284+
285+ # Select a different option (next one, or first if at end)
286+ new_selection = (current + 1 ) % option_count
287+ dropdown .set_selected (new_selection )
288+ print (f"Changed selection to: { new_selection } " )
289+ except Exception as e :
290+ print (f"Could not change dropdown selection: { e } " )
291+ # Fallback: click below current position to select different option
292+ simulate_click (coords ['center_x' ], coords ['center_y' ] + 30 , press_duration_ms = 100 )
293+ else :
294+ print ("Dropdown not found, test may not fully exercise resolution change" )
190295
191- # Find all clickable objects (dropdowns are clickable)
192- # We'll try clicking in the middle area where the dropdown should be
193- # Dropdown is typically centered, so around x=160, y=120
194- dropdown_x = 160
195- dropdown_y = 120
196-
197- print (f"Clicking dropdown area at ({ dropdown_x } , { dropdown_y } )" )
198- simulate_click (dropdown_x , dropdown_y , press_duration_ms = 100 )
199296 wait_for_render (iterations = 15 )
200297
201- # The dropdown should now be open showing resolution options
202- # Let's capture what we see
298+ # Capture screenshot
203299 screenshot_path = f"{ self .screenshot_dir } /camera_dropdown_open.raw"
204300 print (f"Capturing dropdown screenshot: { screenshot_path } " )
205301 capture_screenshot (screenshot_path , width = 320 , height = 240 )
206302
207303 screen = lv .screen_active ()
208- print ("\n Screen after opening dropdown:" )
304+ print ("\n Screen after dropdown interaction :" )
209305 print_screen_labels (screen )
210306
211- # Try to select a different resolution
212- # Options are typically stacked vertically
213- # Let's click a bit lower to select a different option
214- option_x = 160
215- option_y = 150 # Below the current selection
216-
217- print (f"\n Selecting different resolution at ({ option_x } , { option_y } )" )
218- simulate_click (option_x , option_y , press_duration_ms = 100 )
219- wait_for_render (iterations = 15 )
220-
221- # Now find and click the Save button
307+ # Find and click the Save button dynamically
222308 print ("\n Looking for Save button..." )
223- save_button = find_button_with_text (lv .screen_active (), "Save" )
224-
225- if save_button :
226- coords = get_widget_coords (save_button )
227- print (f"Found Save button at { coords } " )
228- simulate_click (coords ['center_x' ], coords ['center_y' ], press_duration_ms = 100 )
229- else :
230- # Fallback: Save button is typically at bottom-left
231- # Based on CameraSettingsActivity code: ALIGN.BOTTOM_LEFT
232- print ("Save button not found via text, trying bottom-left corner" )
233- simulate_click (80 , 220 , press_duration_ms = 100 )
309+ save_found = self ._find_and_click_button (lv .screen_active (), "Save" )
310+
311+ if not save_found :
312+ # Try "OK" as alternative
313+ save_found = self ._find_and_click_button (lv .screen_active (), "OK" )
314+
315+ self .assertTrue (save_found , "Save/OK button not found on settings screen" )
234316
235317 # Wait for reconfiguration to complete
236318 print ("\n Waiting for reconfiguration..." )
@@ -244,12 +326,18 @@ def test_resolution_change_no_crash(self):
244326 # If we got here without segfault, the test passes!
245327 print ("\n ✓ Resolution changed successfully without crash!" )
246328
247- # Verify camera is still showing something
329+ # Verify camera is still showing something by checking for camera UI elements
248330 screen = lv .screen_active ()
249331 # The camera app should still be active (not crashed back to launcher)
250- # We can check this by looking for camera-specific UI elements
251- # or just the fact that we haven't crashed
252-
332+ # Check for camera-specific buttons (close, settings, snap, qr)
333+ has_camera_ui = (
334+ find_button_with_text (screen , lv .SYMBOL .CLOSE ) or
335+ find_button_with_text (screen , lv .SYMBOL .SETTINGS ) or
336+ find_button_with_text (screen , lv .SYMBOL .OK ) or
337+ find_button_with_text (screen , lv .SYMBOL .EYE_OPEN )
338+ )
339+
340+ self .assertTrue (has_camera_ui , "Camera app UI not found after resolution change - app may have crashed" )
253341 print ("\n ✓ Camera app still running after resolution change!" )
254342
255343
0 commit comments