From 4afe3f14dfb0b275494cf4875635ab9e07ca0a1e Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 20:23:06 -0500 Subject: [PATCH 01/76] feat: attempt scrolling --- .../ui/tests/test_ui/raylib_screenshots.py | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index fb42b94d6d1940..55d3826d4f5207 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -5,9 +5,11 @@ import time import pathlib from collections import namedtuple +import logging import pyautogui import pywinctl +from PIL import ImageChops from cereal import log from cereal import messaging @@ -18,12 +20,16 @@ from openpilot.selfdrive.test.helpers import with_processes from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.system.updated.updated import parse_release_notes +from pathlib import Path TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 +logger = logging.getLogger("raylib_screenshots") + + # Offroad alerts to test OFFROAD_ALERTS = ['Offroad_IsTakingSnapshot'] @@ -147,7 +153,7 @@ def setup(self): try: self.ui = pywinctl.getWindowsWithTitle("UI")[0] except Exception as e: - print(f"failed to find ui window, assuming that it's in the top left (for Xvfb) {e}") + logger.warning(f"failed to find ui window, assuming that it's in the top left (for Xvfb) {e}") self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080) def screenshot(self, name: str): @@ -155,6 +161,41 @@ def screenshot(self, name: str): cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) cropped.save(SCREENSHOTS_DIR / f"{name}.png") + def screenshot_with_suffix(self, name: str, suffix: str): + """Save a screenshot with an added suffix before the extension.""" + full_screenshot = pyautogui.screenshot() + cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) + cropped.save(SCREENSHOTS_DIR / f"{name}_{suffix}.png") + + def capture_scrollable(self, name: str, max_pages: int = 8): + """Capture a scrollable page by taking screenshots and scrolling until content stops changing.""" + # center point inside UI where content is likely present + center_x = int(self.ui.width * 0.5) + center_y = int(self.ui.height * 0.5) + + # take first screenshot + full_screenshot = pyautogui.screenshot() + prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) + prev.save(SCREENSHOTS_DIR / f"{name}_0.png") + + for i in range(1, max_pages): + pyautogui.scroll(-15, x=self.ui.left + center_x, y=self.ui.top + center_y) + time.sleep(2) + full_screenshot = pyautogui.screenshot() + curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) + + # check for difference + try: + diff = ImageChops.difference(prev.convert('RGBA'), curr.convert('RGBA')) + if diff.getbbox() is None: + # no changes -> reached end + break + except Exception as e: + print(f"error comparing screenshots: {e}") + + curr.save(SCREENSHOTS_DIR / f"{name}_{i}.png") + prev = curr + def click(self, x: int, y: int, *args, **kwargs): pyautogui.mouseDown(self.ui.left + x, self.ui.top + y, *args, **kwargs) time.sleep(0.01) @@ -165,7 +206,15 @@ def test_ui(self, name, setup_case): self.setup() time.sleep(UI_DELAY) # wait for UI to start setup_case(self.click, self.pm) - self.screenshot(name) + # For pages that can scroll (toggles) capture multiple pages + # if name == "settings_toggles": + try: + self.capture_scrollable(name) + except Exception: + logger.exception("failed capturing scrollable page, falling back to single screenshot") + self.screenshot(name) + # else: + # self.screenshot(name) def create_screenshots(): From c74aea9399f8f14a2f5ebe64bcb5b4be32f055e0 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 20:25:54 -0500 Subject: [PATCH 02/76] fix: update scroll amount --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 55d3826d4f5207..902af229453a3e 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -179,7 +179,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-15, x=self.ui.left + center_x, y=self.ui.top + center_y) + pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(2) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From 793cd57065792fc8d5c4dcc94d111a2bd43631ab Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 20:30:08 -0500 Subject: [PATCH 03/76] remove unused import --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 1 - 1 file changed, 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 902af229453a3e..156f3049307a10 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -20,7 +20,6 @@ from openpilot.selfdrive.test.helpers import with_processes from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.system.updated.updated import parse_release_notes -from pathlib import Path TEST_DIR = pathlib.Path(__file__).parent TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" From 6b9607eba0a27d14c8081ed73566acbd5fa60239 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 20:37:47 -0500 Subject: [PATCH 04/76] test ui change --- selfdrive/ui/layouts/settings/device.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/layouts/settings/device.py b/selfdrive/ui/layouts/settings/device.py index 758200c0ad10ab..99e681814ddd15 100644 --- a/selfdrive/ui/layouts/settings/device.py +++ b/selfdrive/ui/layouts/settings/device.py @@ -61,7 +61,7 @@ def _initialize_items(self): button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), - dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), + dual_button_item("REBOOT", "Power Off!", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt), ] regulatory_btn.set_visible(TICI) return items @@ -156,8 +156,7 @@ def _update_calib_description(self): cloudlog.exception("invalid LiveTorqueParameters") desc += "

" - desc += ("openpilot is continuously calibrating, resetting is rarely required. " + - "Resetting calibration will restart openpilot if the car is powered on.") + desc += "openpilot is continuously calibrating, resetting is rarely required. " + "Resetting calibration will restart openpilot if the car is powered on." self._reset_calib_btn.set_description(desc) @@ -197,7 +196,9 @@ def _on_regulatory(self): def _on_review_training_guide(self): if not self._training_guide: + def completed_callback(): gui_app.set_modal_overlay(None) + self._training_guide = TrainingGuide(completed_callback=completed_callback) gui_app.set_modal_overlay(self._training_guide) From fc15da803cbdc1b802c9a1558564bdd96bad3a62 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:10:24 -0500 Subject: [PATCH 05/76] use our own repo for workflows --- .github/workflows/raylib_ui_preview.yaml | 105 ++++++++++++----------- .github/workflows/selfdrive_tests.yaml | 61 +++---------- 2 files changed, 63 insertions(+), 103 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index ff9655d155f827..554d770d09f813 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -16,55 +16,56 @@ env: UI_JOB_NAME: "Create raylib UI Report" REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} - BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui" + BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib" + CI_ARTIFACTS_OWNER: ${{ github.repository_owner }} + CI_ARTIFACTS_REPO: openpilot-ci-artifacts jobs: preview: - if: github.repository == 'commaai/openpilot' - name: preview - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - pull-requests: write - actions: read - steps: - - name: Waiting for ui generation to start - run: sleep 30 - - - name: Waiting for ui generation to end - uses: lewagon/wait-on-check-action@v1.3.4 - with: - ref: ${{ env.SHA }} - check-name: ${{ env.UI_JOB_NAME }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - allowed-conclusions: success - wait-interval: 20 - - - name: Getting workflow run ID - id: get_run_id - run: | - echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT - - - name: Getting proposed ui - id: download-artifact - uses: dawidd6/action-download-artifact@v6 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - run_id: ${{ steps.get_run_id.outputs.run_id }} - search_artifacts: true - name: raylib-report-1-${{ env.REPORT_NAME }} - path: ${{ github.workspace }}/pr_ui - - - name: Getting master ui - uses: actions/checkout@v4 - with: - repository: commaai/ci-artifacts - ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} - path: ${{ github.workspace }}/master_ui_raylib - ref: openpilot_master_ui_raylib - - - name: Saving new master ui + name: preview + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + pull-requests: write + actions: read + steps: + - name: Waiting for ui generation to start + run: sleep 30 + + - name: Waiting for ui generation to end + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ env.SHA }} + check-name: ${{ env.UI_JOB_NAME }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + allowed-conclusions: success + wait-interval: 20 + + - name: Getting workflow run ID + id: get_run_id + run: | + echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT + + - name: Getting proposed ui + id: download-artifact + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + run_id: ${{ steps.get_run_id.outputs.run_id }} + search_artifacts: true + name: raylib-report-1-${{ env.REPORT_NAME }} + path: ${{ github.workspace }}/pr_ui + + - name: Getting master ui + uses: actions/checkout@v4 + with: + repository: ${{ env.CI_ARTIFACTS_OWNER }}/${{ env.CI_ARTIFACTS_REPO }} + ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} + path: ${{ github.workspace }}/master_ui_raylib + ref: openpilot_master_ui_raylib + + - name: Saving new master ui if: github.ref == 'refs/heads/master' && github.event_name == 'push' working-directory: ${{ github.workspace }}/master_ui_raylib run: | @@ -102,7 +103,7 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
" @@ -119,13 +120,13 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
master proposed master proposed
diff composite diff diff composite diff
" @@ -138,7 +139,7 @@ jobs: if [[ $INDEX -eq 0 ]]; then TABLE="${TABLE}" fi - TABLE="${TABLE} " + TABLE="${TABLE} " if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then TABLE="${TABLE}" fi diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 35ced1e38b4e7c..baa5aa434d271d 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -32,12 +32,7 @@ env: jobs: build_release: name: build release - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 env: STRIPPED_DIR: /tmp/releasepilot steps: @@ -65,23 +60,17 @@ jobs: cd $STRIPPED_DIR ${{ env.RUN }} "release/check-dirty.sh" - name: Check submodules - if: github.repository == 'commaai/openpilot' timeout-minutes: 3 run: release/check-submodules.sh build: - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: submodules: true - name: Setup docker push - if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot' + if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' run: | echo "PUSH_IMAGE=true" >> "$GITHUB_ENV" $DOCKER_LOGIN @@ -91,7 +80,7 @@ jobs: build_mac: name: build macOS - runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }} + runs-on: macos-latest steps: - uses: actions/checkout@v4 with: @@ -124,12 +113,7 @@ jobs: static_analysis: name: static analysis - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 env: PYTHONWARNINGS: default steps: @@ -143,12 +127,7 @@ jobs: unit_tests: name: unit tests - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -170,12 +149,7 @@ jobs: process_replay: name: process replay - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -219,12 +193,7 @@ jobs: simulator_driving: name: simulator driving - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 if: false # FIXME: Started to timeout recently steps: - uses: actions/checkout@v4 @@ -245,12 +214,7 @@ jobs: create_ui_report: # This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml name: Create UI Report - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 if: false # FIXME: FrameReader is broken on CI runners steps: - uses: actions/checkout@v4 @@ -280,12 +244,7 @@ jobs: create_raylib_ui_report: name: Create raylib UI Report - runs-on: ${{ - (github.repository == 'commaai/openpilot') && - ((github.event_name != 'pull_request') || - (github.event.pull_request.head.repo.full_name == 'commaai/openpilot')) - && fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]') - || fromJSON('["ubuntu-24.04"]') }} + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: From 6317ab37ae713859a0655997bdf727d97b8a2d17 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:14:19 -0500 Subject: [PATCH 06/76] fix: indention --- .github/workflows/raylib_ui_preview.yaml | 182 +++++++++++------------ 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 554d770d09f813..da948a3cb314b5 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -66,93 +66,93 @@ jobs: ref: openpilot_master_ui_raylib - name: Saving new master ui - if: github.ref == 'refs/heads/master' && github.event_name == 'push' - working-directory: ${{ github.workspace }}/master_ui_raylib - run: | - git checkout --orphan=new_master_ui_raylib - git rm -rf * - git branch -D openpilot_master_ui_raylib - git branch -m openpilot_master_ui_raylib - git config user.name "GitHub Actions Bot" - git config user.email "<>" - mv ${{ github.workspace }}/pr_ui/*.png . - git add . - git commit -m "raylib screenshots for commit ${{ env.SHA }}" - git push origin openpilot_master_ui_raylib --force - - - name: Finding diff - if: github.event_name == 'pull_request_target' - id: find_diff - run: >- - sudo apt-get update && sudo apt-get install -y imagemagick - - scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') - A=($scenes) - - DIFF="" - TABLE="
All Screenshots" - TABLE="${TABLE}" - - for ((i=0; i<${#A[*]}; i=i+1)); - do - # Check if the master file exists - if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then - # This is a new file in PR UI that doesn't exist in master - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" - DIFF="${DIFF}
" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
" - DIFF="${DIFF}
" - elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then - convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png - composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png - convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif - - mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png - - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
master proposed
diff composite diff
" - DIFF="${DIFF}
" - else - rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png - fi - - INDEX=$(($i % 2)) - if [[ $INDEX -eq 0 ]]; then - TABLE="${TABLE}" - fi - TABLE="${TABLE} " - if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then - TABLE="${TABLE}" - fi - done - - TABLE="${TABLE}" - - echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - - - name: Saving proposed ui - if: github.event_name == 'pull_request_target' - working-directory: ${{ github.workspace }}/master_ui_raylib - run: | + if: github.ref == 'refs/heads/master' && github.event_name == 'push' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | + git checkout --orphan=new_master_ui_raylib + git rm -rf * + git branch -D openpilot_master_ui_raylib + git branch -m openpilot_master_ui_raylib + git config user.name "GitHub Actions Bot" + git config user.email "<>" + mv ${{ github.workspace }}/pr_ui/*.png . + git add . + git commit -m "raylib screenshots for commit ${{ env.SHA }}" + git push origin openpilot_master_ui_raylib --force + + - name: Finding diff + if: github.event_name == 'pull_request_target' + id: find_diff + run: >- + sudo apt-get update && sudo apt-get install -y imagemagick + + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') + A=($scenes) + + DIFF="" + TABLE="
All Screenshots" + TABLE="${TABLE}" + + for ((i=0; i<${#A[*]}; i=i+1)); + do + # Check if the master file exists + if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then + # This is a new file in PR UI that doesn't exist in master + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" + DIFF="${DIFF}
" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
" + DIFF="${DIFF}
" + elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then + convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png + composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png + convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif + + mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png + + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
master proposed
diff composite diff
" + DIFF="${DIFF}
" + else + rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png + fi + + INDEX=$(($i % 2)) + if [[ $INDEX -eq 0 ]]; then + TABLE="${TABLE}" + fi + TABLE="${TABLE} " + if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then + TABLE="${TABLE}" + fi + done + + TABLE="${TABLE}" + + echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" + + - name: Saving proposed ui + if: github.event_name == 'pull_request_target' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | git config user.name "GitHub Actions Bot" git config user.email "<>" git checkout --orphan=${{ env.BRANCH_NAME }} @@ -162,10 +162,10 @@ jobs: git commit -m "raylib screenshots for PR #${{ github.event.number }}" git push origin ${{ env.BRANCH_NAME }} --force - - name: Comment Screenshots on PR - if: github.event_name == 'pull_request_target' - uses: thollander/actions-comment-pull-request@v2 - with: + - name: Comment Screenshots on PR + if: github.event_name == 'pull_request_target' + uses: thollander/actions-comment-pull-request@v2 + with: message: | ## raylib UI Preview From 9bc566c0be1a078de1e66fdfe5f410f9b1b01b6a Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:22:06 -0500 Subject: [PATCH 07/76] temp disable path checks --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index da948a3cb314b5..b1d89ca46d5a46 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -7,9 +7,9 @@ on: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - paths: - - 'selfdrive/ui/**' - - 'system/ui/**' + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' workflow_dispatch: env: From f2d2b7d458d277e756e68b9049c5b4ccba18f6a7 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:25:33 -0500 Subject: [PATCH 08/76] Revert "temp disable path checks" This reverts commit 9bc566c0be1a078de1e66fdfe5f410f9b1b01b6a. --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index b1d89ca46d5a46..da948a3cb314b5 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -7,9 +7,9 @@ on: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - # paths: - # - 'selfdrive/ui/**' - # - 'system/ui/**' + paths: + - 'selfdrive/ui/**' + - 'system/ui/**' workflow_dispatch: env: From 19f7d8cb0af69a9a8e344b27b871104edbe216ef Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:26:10 -0500 Subject: [PATCH 09/76] update branch name --- .github/workflows/raylib_ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index da948a3cb314b5..6f08e0d6c64b7e 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -16,7 +16,7 @@ env: UI_JOB_NAME: "Create raylib UI Report" REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} - BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib" + BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui" CI_ARTIFACTS_OWNER: ${{ github.repository_owner }} CI_ARTIFACTS_REPO: openpilot-ci-artifacts From bfa0e7d0d47fc8612f807a73b3201ddd7fb5e5f6 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:38:32 -0500 Subject: [PATCH 10/76] test change ui --- selfdrive/ui/layouts/settings/software.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/layouts/settings/software.py b/selfdrive/ui/layouts/settings/software.py index 7aebc609f87ce7..e5a671e27a99a6 100644 --- a/selfdrive/ui/layouts/settings/software.py +++ b/selfdrive/ui/layouts/settings/software.py @@ -45,7 +45,7 @@ def __init__(self): self._onroad_label = ListItem(title="Updates are only downloaded while the car is off.") self._version_item = text_item("Current Version", ui_state.params.get("UpdaterCurrentDescription") or "") - self._download_btn = button_item("Download", "CHECK", callback=self._on_download_update) + self._download_btn = button_item("DOWNLOAD", "CHECK", callback=self._on_download_update) # Install button is initially hidden self._install_btn = button_item("Install Update", "INSTALL", callback=self._on_install_update) From 85950008479f6a7af833c68bbdebe04cba019acb Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:41:07 -0500 Subject: [PATCH 11/76] adjust inention --- .github/workflows/raylib_ui_preview.yaml | 296 +++++++++++------------ 1 file changed, 148 insertions(+), 148 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 6f08e0d6c64b7e..b93a7ea8cf2b0e 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -22,154 +22,154 @@ env: jobs: preview: - name: preview - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - pull-requests: write - actions: read - steps: - - name: Waiting for ui generation to start - run: sleep 30 - - - name: Waiting for ui generation to end - uses: lewagon/wait-on-check-action@v1.3.4 - with: - ref: ${{ env.SHA }} - check-name: ${{ env.UI_JOB_NAME }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - allowed-conclusions: success - wait-interval: 20 - - - name: Getting workflow run ID - id: get_run_id - run: | - echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT - - - name: Getting proposed ui - id: download-artifact - uses: dawidd6/action-download-artifact@v6 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - run_id: ${{ steps.get_run_id.outputs.run_id }} - search_artifacts: true - name: raylib-report-1-${{ env.REPORT_NAME }} - path: ${{ github.workspace }}/pr_ui - - - name: Getting master ui - uses: actions/checkout@v4 - with: - repository: ${{ env.CI_ARTIFACTS_OWNER }}/${{ env.CI_ARTIFACTS_REPO }} - ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} - path: ${{ github.workspace }}/master_ui_raylib - ref: openpilot_master_ui_raylib - - - name: Saving new master ui - if: github.ref == 'refs/heads/master' && github.event_name == 'push' - working-directory: ${{ github.workspace }}/master_ui_raylib - run: | - git checkout --orphan=new_master_ui_raylib - git rm -rf * - git branch -D openpilot_master_ui_raylib - git branch -m openpilot_master_ui_raylib - git config user.name "GitHub Actions Bot" - git config user.email "<>" - mv ${{ github.workspace }}/pr_ui/*.png . - git add . - git commit -m "raylib screenshots for commit ${{ env.SHA }}" - git push origin openpilot_master_ui_raylib --force - - - name: Finding diff - if: github.event_name == 'pull_request_target' - id: find_diff - run: >- - sudo apt-get update && sudo apt-get install -y imagemagick - - scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') - A=($scenes) - - DIFF="" - TABLE="
All Screenshots" - TABLE="${TABLE}" - - for ((i=0; i<${#A[*]}; i=i+1)); - do - # Check if the master file exists - if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then - # This is a new file in PR UI that doesn't exist in master - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" - DIFF="${DIFF}
" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
" - DIFF="${DIFF}
" - elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then - convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png - composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png - convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif - - mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png - - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
master proposed
diff composite diff
" - DIFF="${DIFF}
" - else - rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png - fi - - INDEX=$(($i % 2)) - if [[ $INDEX -eq 0 ]]; then - TABLE="${TABLE}" - fi - TABLE="${TABLE} " - if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then - TABLE="${TABLE}" - fi - done - - TABLE="${TABLE}" - - echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - - - name: Saving proposed ui - if: github.event_name == 'pull_request_target' - working-directory: ${{ github.workspace }}/master_ui_raylib - run: | + name: preview + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + pull-requests: write + actions: read + steps: + - name: Waiting for ui generation to start + run: sleep 30 + + - name: Waiting for ui generation to end + uses: lewagon/wait-on-check-action@v1.3.4 + with: + ref: ${{ env.SHA }} + check-name: ${{ env.UI_JOB_NAME }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + allowed-conclusions: success + wait-interval: 20 + + - name: Getting workflow run ID + id: get_run_id + run: | + echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?[0-9]+)") | .number')" >> $GITHUB_OUTPUT + + - name: Getting proposed ui + id: download-artifact + uses: dawidd6/action-download-artifact@v6 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + run_id: ${{ steps.get_run_id.outputs.run_id }} + search_artifacts: true + name: raylib-report-1-${{ env.REPORT_NAME }} + path: ${{ github.workspace }}/pr_ui + + - name: Getting master ui + uses: actions/checkout@v4 + with: + repository: ${{ env.CI_ARTIFACTS_OWNER }}/${{ env.CI_ARTIFACTS_REPO }} + ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} + path: ${{ github.workspace }}/master_ui_raylib + ref: openpilot_master_ui_raylib + + - name: Saving new master ui + if: github.ref == 'refs/heads/master' && github.event_name == 'push' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | + git checkout --orphan=new_master_ui_raylib + git rm -rf * + git branch -D openpilot_master_ui_raylib + git branch -m openpilot_master_ui_raylib git config user.name "GitHub Actions Bot" git config user.email "<>" - git checkout --orphan=${{ env.BRANCH_NAME }} - git rm -rf * - mv ${{ github.workspace }}/pr_ui/* . + mv ${{ github.workspace }}/pr_ui/*.png . git add . - git commit -m "raylib screenshots for PR #${{ github.event.number }}" - git push origin ${{ env.BRANCH_NAME }} --force - - - name: Comment Screenshots on PR - if: github.event_name == 'pull_request_target' - uses: thollander/actions-comment-pull-request@v2 - with: - message: | - - ## raylib UI Preview - ${{ steps.find_diff.outputs.DIFF }} - comment_tag: run_id_screenshots_raylib - pr_number: ${{ github.event.number }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + git commit -m "raylib screenshots for commit ${{ env.SHA }}" + git push origin openpilot_master_ui_raylib --force + + - name: Finding diff + if: github.event_name == 'pull_request_target' + id: find_diff + run: >- + sudo apt-get update && sudo apt-get install -y imagemagick + + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') + A=($scenes) + + DIFF="" + TABLE="
All Screenshots" + TABLE="${TABLE}" + + for ((i=0; i<${#A[*]}; i=i+1)); + do + # Check if the master file exists + if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then + # This is a new file in PR UI that doesn't exist in master + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" + DIFF="${DIFF}
" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
" + DIFF="${DIFF}
" + elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then + convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png + composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png + convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif + + mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png + + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
master proposed
diff composite diff
" + DIFF="${DIFF}
" + else + rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png + fi + + INDEX=$(($i % 2)) + if [[ $INDEX -eq 0 ]]; then + TABLE="${TABLE}" + fi + TABLE="${TABLE} " + if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then + TABLE="${TABLE}" + fi + done + + TABLE="${TABLE}" + + echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" + + - name: Saving proposed ui + if: github.event_name == 'pull_request_target' + working-directory: ${{ github.workspace }}/master_ui_raylib + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<>" + git checkout --orphan=${{ env.BRANCH_NAME }} + git rm -rf * + mv ${{ github.workspace }}/pr_ui/* . + git add . + git commit -m "raylib screenshots for PR #${{ github.event.number }}" + git push origin ${{ env.BRANCH_NAME }} --force + + - name: Comment Screenshots on PR + if: github.event_name == 'pull_request_target' + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + + ## raylib UI Preview + ${{ steps.find_diff.outputs.DIFF }} + comment_tag: run_id_screenshots_raylib + pr_number: ${{ github.event.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a5df256807df807b2cdbe3aa74c897e5ddf9fd66 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:41:17 -0500 Subject: [PATCH 12/76] always run --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index b93a7ea8cf2b0e..23e7afb6a7f787 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -7,9 +7,9 @@ on: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - paths: - - 'selfdrive/ui/**' - - 'system/ui/**' + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' workflow_dispatch: env: From 58651096a22565de69f00cee07eb528f6b160285 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:41:45 -0500 Subject: [PATCH 13/76] all branches and types --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 23e7afb6a7f787..2b49a7359a15ae 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -4,9 +4,9 @@ on: branches: - master pull_request_target: - types: [assigned, opened, synchronize, reopened, edited] - branches: - - 'master' + # types: [assigned, opened, synchronize, reopened, edited] + # branches: + # - 'master' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' From 9a01578cad1d046ffff60771899194ce5f5348a1 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:43:53 -0500 Subject: [PATCH 14/76] add pr types back --- .github/workflows/raylib_ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 2b49a7359a15ae..dbbabc8ae8911e 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -4,7 +4,7 @@ on: branches: - master pull_request_target: - # types: [assigned, opened, synchronize, reopened, edited] + types: [assigned, opened, synchronize, reopened, edited] # branches: # - 'master' # paths: From 56c6f9312adfdc35cfa27ac14f04e78e661f0383 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:45:18 -0500 Subject: [PATCH 15/76] add branch back --- .github/workflows/raylib_ui_preview.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index dbbabc8ae8911e..23e7afb6a7f787 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -5,8 +5,8 @@ on: - master pull_request_target: types: [assigned, opened, synchronize, reopened, edited] - # branches: - # - 'master' + branches: + - 'master' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' From c76dbf1fbf336ac089772d5e6f3dad7ba177c5db Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:49:43 -0500 Subject: [PATCH 16/76] don't skip --- .github/workflows/raylib_ui_preview.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 23e7afb6a7f787..c67a462f02a46f 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -22,6 +22,7 @@ env: jobs: preview: + if: true name: preview runs-on: ubuntu-latest timeout-minutes: 20 From a7414d777526023bb0e43e494071cd147102edbb Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:51:39 -0500 Subject: [PATCH 17/76] fix --- .github/workflows/raylib_ui_preview.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index c67a462f02a46f..a8798feea08ea7 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -3,13 +3,13 @@ on: push: branches: - master - pull_request_target: + pull_request: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - # paths: - # - 'selfdrive/ui/**' - # - 'system/ui/**' + paths: + - 'selfdrive/ui/**' + - 'system/ui/**' workflow_dispatch: env: @@ -22,7 +22,6 @@ env: jobs: preview: - if: true name: preview runs-on: ubuntu-latest timeout-minutes: 20 From 26988b955e1c09a8210600565414504263583db3 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 21:55:44 -0500 Subject: [PATCH 18/76] add both --- .github/workflows/raylib_ui_preview.yaml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index a8798feea08ea7..c8a0967a47f37d 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -3,13 +3,20 @@ on: push: branches: - master - pull_request: + pull_request_target: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - paths: - - 'selfdrive/ui/**' - - 'system/ui/**' + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' + pull_request: + types: [opened, synchronize, reopened, edited] + branches: + - master + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' workflow_dispatch: env: From 4f8bc6ea9691a452261f3d77692af76c2e55658d Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:00:01 -0500 Subject: [PATCH 19/76] disable macos build --- .github/workflows/selfdrive_tests.yaml | 64 +++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index baa5aa434d271d..99528c96a439d7 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -78,38 +78,38 @@ jobs: - uses: ./.github/workflows/compile-openpilot timeout-minutes: 30 - build_mac: - name: build macOS - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV - - name: Homebrew cache - uses: ./.github/workflows/auto-cache - with: - path: ~/Library/Caches/Homebrew - key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} - restore-keys: | - brew-macos-${{ env.CACHE_COMMIT_DATE }} - brew-macos - - name: Install dependencies - run: ./tools/mac_setup.sh - env: - PYTHONWARNINGS: default # package install has DeprecationWarnings - HOMEBREW_DISPLAY_INSTALL_TIMES: 1 - - run: git lfs pull - - name: Getting scons cache - uses: ./.github/workflows/auto-cache - with: - path: /tmp/scons_cache - key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} - restore-keys: | - scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }} - scons-${{ runner.arch }}-macos - - name: Building openpilot - run: . .venv/bin/activate && scons -j$(nproc) + # build_mac: + # name: build macOS + # runs-on: macos-latest + # steps: + # - uses: actions/checkout@v4 + # with: + # submodules: true + # - run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV + # - name: Homebrew cache + # uses: ./.github/workflows/auto-cache + # with: + # path: ~/Library/Caches/Homebrew + # key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} + # restore-keys: | + # brew-macos-${{ env.CACHE_COMMIT_DATE }} + # brew-macos + # - name: Install dependencies + # run: ./tools/mac_setup.sh + # env: + # PYTHONWARNINGS: default # package install has DeprecationWarnings + # HOMEBREW_DISPLAY_INSTALL_TIMES: 1 + # - run: git lfs pull + # - name: Getting scons cache + # uses: ./.github/workflows/auto-cache + # with: + # path: /tmp/scons_cache + # key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} + # restore-keys: | + # scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }} + # scons-${{ runner.arch }}-macos + # - name: Building openpilot + # run: . .venv/bin/activate && scons -j$(nproc) static_analysis: name: static analysis From 2154955d242442d7b26b0d63ba8bd2e2e5f46059 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:02:00 -0500 Subject: [PATCH 20/76] fix workflow --- .github/workflows/raylib_ui_preview.yaml | 60 ++++++++++-------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index c8a0967a47f37d..998b3fa287bf34 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -7,16 +7,9 @@ on: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - # paths: - # - 'selfdrive/ui/**' - # - 'system/ui/**' - pull_request: - types: [opened, synchronize, reopened, edited] - branches: - - master - # paths: - # - 'selfdrive/ui/**' - # - 'system/ui/**' + paths: + - 'selfdrive/ui/**' + - 'system/ui/**' workflow_dispatch: env: @@ -24,11 +17,10 @@ env: REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui" - CI_ARTIFACTS_OWNER: ${{ github.repository_owner }} - CI_ARTIFACTS_REPO: openpilot-ci-artifacts jobs: preview: + if: github.repository == 'commaai/openpilot' name: preview runs-on: ubuntu-latest timeout-minutes: 20 @@ -67,7 +59,7 @@ jobs: - name: Getting master ui uses: actions/checkout@v4 with: - repository: ${{ env.CI_ARTIFACTS_OWNER }}/${{ env.CI_ARTIFACTS_REPO }} + repository: commaai/ci-artifacts ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} path: ${{ github.workspace }}/master_ui_raylib ref: openpilot_master_ui_raylib @@ -110,7 +102,7 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
" @@ -127,13 +119,13 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
master proposed master proposed
diff composite diff diff composite diff
" @@ -146,7 +138,7 @@ jobs: if [[ $INDEX -eq 0 ]]; then TABLE="${TABLE}" fi - TABLE="${TABLE} " + TABLE="${TABLE} " if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then TABLE="${TABLE}" fi @@ -160,23 +152,23 @@ jobs: if: github.event_name == 'pull_request_target' working-directory: ${{ github.workspace }}/master_ui_raylib run: | - git config user.name "GitHub Actions Bot" - git config user.email "<>" - git checkout --orphan=${{ env.BRANCH_NAME }} - git rm -rf * - mv ${{ github.workspace }}/pr_ui/* . - git add . - git commit -m "raylib screenshots for PR #${{ github.event.number }}" - git push origin ${{ env.BRANCH_NAME }} --force + git config user.name "GitHub Actions Bot" + git config user.email "<>" + git checkout --orphan=${{ env.BRANCH_NAME }} + git rm -rf * + mv ${{ github.workspace }}/pr_ui/* . + git add . + git commit -m "raylib screenshots for PR #${{ github.event.number }}" + git push origin ${{ env.BRANCH_NAME }} --force - name: Comment Screenshots on PR if: github.event_name == 'pull_request_target' uses: thollander/actions-comment-pull-request@v2 with: - message: | - - ## raylib UI Preview - ${{ steps.find_diff.outputs.DIFF }} - comment_tag: run_id_screenshots_raylib - pr_number: ${{ github.event.number }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + message: | + + ## raylib UI Preview + ${{ steps.find_diff.outputs.DIFF }} + comment_tag: run_id_screenshots_raylib + pr_number: ${{ github.event.number }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 4977b342e83349a220877ea2a8507e445fe857e9 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:02:20 -0500 Subject: [PATCH 21/76] fix workflow again --- .github/workflows/raylib_ui_preview.yaml | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 998b3fa287bf34..d17daaf1b1ec95 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -7,9 +7,16 @@ on: types: [assigned, opened, synchronize, reopened, edited] branches: - 'master' - paths: - - 'selfdrive/ui/**' - - 'system/ui/**' + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' + pull_request: + types: [opened, synchronize, reopened, edited] + branches: + - master + # paths: + # - 'selfdrive/ui/**' + # - 'system/ui/**' workflow_dispatch: env: @@ -17,10 +24,11 @@ env: REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }} SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }} BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui" + CI_ARTIFACTS_OWNER: ${{ github.repository_owner }} + CI_ARTIFACTS_REPO: openpilot-ci-artifacts jobs: preview: - if: github.repository == 'commaai/openpilot' name: preview runs-on: ubuntu-latest timeout-minutes: 20 @@ -59,7 +67,7 @@ jobs: - name: Getting master ui uses: actions/checkout@v4 with: - repository: commaai/ci-artifacts + repository: ${{ env.CI_ARTIFACTS_OWNER }}/${{ env.CI_ARTIFACTS_REPO }} ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }} path: ${{ github.workspace }}/master_ui_raylib ref: openpilot_master_ui_raylib @@ -102,7 +110,7 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
" @@ -119,13 +127,13 @@ jobs: DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
master proposed master proposed
diff composite diff diff composite diff
" @@ -138,7 +146,7 @@ jobs: if [[ $INDEX -eq 0 ]]; then TABLE="${TABLE}" fi - TABLE="${TABLE} " + TABLE="${TABLE} " if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then TABLE="${TABLE}" fi From f2ec54b19564d9a3cdaac8513db7c0adcd76cadc Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:08:36 -0500 Subject: [PATCH 22/76] all branches --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index d17daaf1b1ec95..f5b92170ae2865 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -2,18 +2,18 @@ name: "raylib ui preview" on: push: branches: - - master + - '**' pull_request_target: types: [assigned, opened, synchronize, reopened, edited] branches: - - 'master' + - '**' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' pull_request: types: [opened, synchronize, reopened, edited] branches: - - master + - '**' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' From 2ec67effaf2596d7b1acc4103a6f67a3513a51b6 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:09:32 -0500 Subject: [PATCH 23/76] disable pull_request_target --- .github/workflows/raylib_ui_preview.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index f5b92170ae2865..c743b4b591d610 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -3,10 +3,10 @@ on: push: branches: - '**' - pull_request_target: - types: [assigned, opened, synchronize, reopened, edited] - branches: - - '**' + # pull_request_target: + # types: [assigned, opened, synchronize, reopened, edited] + # branches: + # - '**' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' From 7caec7e668c65b085ecb00a91db42074988b52c3 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:10:13 -0500 Subject: [PATCH 24/76] only pr --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index c743b4b591d610..b502f4abf6d795 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -1,8 +1,8 @@ name: "raylib ui preview" on: - push: - branches: - - '**' + # push: + # branches: + # - '**' # pull_request_target: # types: [assigned, opened, synchronize, reopened, edited] # branches: From 8e4bd43ce5a552650d2096f9fec187e151383d02 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:12:46 -0500 Subject: [PATCH 25/76] test --- .github/workflows/raylib_ui_preview.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index b502f4abf6d795..d741f8ac82446a 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -1,8 +1,8 @@ name: "raylib ui preview" on: - # push: - # branches: - # - '**' + push: + branches: + - 'master' # pull_request_target: # types: [assigned, opened, synchronize, reopened, edited] # branches: @@ -13,7 +13,7 @@ on: pull_request: types: [opened, synchronize, reopened, edited] branches: - - '**' + - 'master' # paths: # - 'selfdrive/ui/**' # - 'system/ui/**' From 2b816f32ad3aa0805da4ad8b55c781070891a90e Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:19:57 -0500 Subject: [PATCH 26/76] only on ui change --- .github/workflows/raylib_ui_preview.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index d741f8ac82446a..1518701dbbb345 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -14,9 +14,9 @@ on: types: [opened, synchronize, reopened, edited] branches: - 'master' - # paths: - # - 'selfdrive/ui/**' - # - 'system/ui/**' + paths: + - 'selfdrive/ui/**' + - 'system/ui/**' workflow_dispatch: env: From 5278ce8bcd532373166cac5892459a0daa2e6fe6 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:27:02 -0500 Subject: [PATCH 27/76] fix uploading preview --- .github/workflows/raylib_ui_preview.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 1518701dbbb345..daf8cc2a13fb61 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -73,7 +73,7 @@ jobs: ref: openpilot_master_ui_raylib - name: Saving new master ui - if: github.ref == 'refs/heads/master' && github.event_name == 'push' + if: github.event_name == 'push' working-directory: ${{ github.workspace }}/master_ui_raylib run: | git checkout --orphan=new_master_ui_raylib @@ -88,7 +88,7 @@ jobs: git push origin openpilot_master_ui_raylib --force - name: Finding diff - if: github.event_name == 'pull_request_target' + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' id: find_diff run: >- sudo apt-get update && sudo apt-get install -y imagemagick @@ -157,7 +157,7 @@ jobs: echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - name: Saving proposed ui - if: github.event_name == 'pull_request_target' + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' working-directory: ${{ github.workspace }}/master_ui_raylib run: | git config user.name "GitHub Actions Bot" @@ -170,7 +170,7 @@ jobs: git push origin ${{ env.BRANCH_NAME }} --force - name: Comment Screenshots on PR - if: github.event_name == 'pull_request_target' + if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' uses: thollander/actions-comment-pull-request@v2 with: message: | From 6a423ce99db55cf841d78550f7106bb2520bbb77 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:44:21 -0500 Subject: [PATCH 28/76] reduce wait after scroll --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 156f3049307a10..f3f0f22e9e5912 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -179,7 +179,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): for i in range(1, max_pages): pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) - time.sleep(2) + time.sleep(1) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From b39554fa219130a27debda80f23f39da32f29b09 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 22:57:37 -0500 Subject: [PATCH 29/76] large scroll --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index f3f0f22e9e5912..29d9b859ef7af2 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -178,7 +178,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) + pyautogui.scroll(-5000, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From 63d1b0fcfb7fcb43fcab525d3aec2857d9afc3e2 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 23:44:20 -0500 Subject: [PATCH 30/76] drag to scroll instead --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 29d9b859ef7af2..442c50437bd7b2 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -178,7 +178,19 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-5000, x=self.ui.left + center_x, y=self.ui.top + center_y) + # perform a click-and-drag to simulate scrolling (drag upwards to move content down) + try: + start_x = self.ui.left + center_x + start_y = self.ui.top + center_y + # move to start, press, drag up and release + pyautogui.moveTo(start_x, start_y) + pyautogui.mouseDown() + drag_amount = -int(self.ui.height * 0.5) # drag up by ~50% of UI height + pyautogui.dragRel(0, drag_amount, duration=0.25) + pyautogui.mouseUp() + except Exception as e: + logger.exception(f"failed to perform drag scroll: {e}") + time.sleep(1) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From e8b8982bfb3a3b245d1598a8691c2eb92156d009 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 23:50:34 -0500 Subject: [PATCH 31/76] Revert "drag to scroll instead" This reverts commit 63d1b0fcfb7fcb43fcab525d3aec2857d9afc3e2. --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 442c50437bd7b2..29d9b859ef7af2 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -178,19 +178,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - # perform a click-and-drag to simulate scrolling (drag upwards to move content down) - try: - start_x = self.ui.left + center_x - start_y = self.ui.top + center_y - # move to start, press, drag up and release - pyautogui.moveTo(start_x, start_y) - pyautogui.mouseDown() - drag_amount = -int(self.ui.height * 0.5) # drag up by ~50% of UI height - pyautogui.dragRel(0, drag_amount, duration=0.25) - pyautogui.mouseUp() - except Exception as e: - logger.exception(f"failed to perform drag scroll: {e}") - + pyautogui.scroll(-5000, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From 5673654f56bfaf723895c12e91358b815d8243e7 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 23:50:46 -0500 Subject: [PATCH 32/76] Revert "large scroll" This reverts commit b39554fa219130a27debda80f23f39da32f29b09. --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 29d9b859ef7af2..f3f0f22e9e5912 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -178,7 +178,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-5000, x=self.ui.left + center_x, y=self.ui.top + center_y) + pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) From d73efb7132e776fd708a9f2d100676c5d7e624fa Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 23:56:50 -0500 Subject: [PATCH 33/76] remove unused function --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index f3f0f22e9e5912..12cc7c0af0eb01 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -160,12 +160,6 @@ def screenshot(self, name: str): cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) cropped.save(SCREENSHOTS_DIR / f"{name}.png") - def screenshot_with_suffix(self, name: str, suffix: str): - """Save a screenshot with an added suffix before the extension.""" - full_screenshot = pyautogui.screenshot() - cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) - cropped.save(SCREENSHOTS_DIR / f"{name}_{suffix}.png") - def capture_scrollable(self, name: str, max_pages: int = 8): """Capture a scrollable page by taking screenshots and scrolling until content stops changing.""" # center point inside UI where content is likely present From f87ce50d132a25b646de2e81f0e7a60cb174d017 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Oct 2025 23:58:45 -0500 Subject: [PATCH 34/76] remove failing window check; add screenshot error handling --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 12cc7c0af0eb01..207fe5c3baf073 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -149,11 +149,7 @@ def setup(self): ds.clear_write_flag() time.sleep(0.05) time.sleep(0.5) - try: - self.ui = pywinctl.getWindowsWithTitle("UI")[0] - except Exception as e: - logger.warning(f"failed to find ui window, assuming that it's in the top left (for Xvfb) {e}") - self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080) + self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080) def screenshot(self, name: str): full_screenshot = pyautogui.screenshot() @@ -168,6 +164,8 @@ def capture_scrollable(self, name: str, max_pages: int = 8): # take first screenshot full_screenshot = pyautogui.screenshot() + if not full_screenshot: + raise Exception("failed to capture screenshot") prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) prev.save(SCREENSHOTS_DIR / f"{name}_0.png") @@ -175,6 +173,8 @@ def capture_scrollable(self, name: str, max_pages: int = 8): pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() + if not full_screenshot: + raise Exception(f"failed to capture screenshot on page {i}") curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) # check for difference From 9b72903526437ab135a4567d4cbe1de3645a5426 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:10:29 -0500 Subject: [PATCH 35/76] test clicking center instead of scrolling --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 207fe5c3baf073..c239be6b3d7a07 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -8,7 +8,6 @@ import logging import pyautogui -import pywinctl from PIL import ImageChops from cereal import log @@ -170,7 +169,8 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) + # pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) + self.click(center_x, center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() if not full_screenshot: @@ -179,7 +179,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): # check for difference try: - diff = ImageChops.difference(prev.convert('RGBA'), curr.convert('RGBA')) + diff = ImageChops.difference(prev.convert('RGB'), curr.convert('RGB')) if diff.getbbox() is None: # no changes -> reached end break From 6467c2b58cac376f358715256d58b9d3ee8f131c Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:17:05 -0500 Subject: [PATCH 36/76] revert to scroll --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index c239be6b3d7a07..3250449f2bf72d 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -169,8 +169,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - # pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) - self.click(center_x, center_y) + pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) time.sleep(1) full_screenshot = pyautogui.screenshot() if not full_screenshot: From 9c847eca678c7f9b949097c458c89013dc78efd0 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:17:14 -0500 Subject: [PATCH 37/76] skip apt-get update --- .github/workflows/raylib_ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index daf8cc2a13fb61..71e746a8c00274 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -91,7 +91,7 @@ jobs: if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get update && sudo apt-get install -y imagemagick + sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) From c2b3aa2a549af904515bbbc6f9b1452ec98ed8a4 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:28:55 -0500 Subject: [PATCH 38/76] use mouse down/drag instead of scroll --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 3250449f2bf72d..9f92cffa09087f 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -169,7 +169,13 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}_0.png") for i in range(1, max_pages): - pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) + # pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) + start_x = self.ui.left + center_x + start_y = self.ui.top + center_y + int(self.ui.height * 0.5) + end_y = self.ui.top + center_y - int(self.ui.height) + pyautogui.mouseDown(start_x, start_y) + pyautogui.moveTo(start_x, end_y, duration=0.35) + pyautogui.mouseUp(start_x, end_y) time.sleep(1) full_screenshot = pyautogui.screenshot() if not full_screenshot: From 9b61e7bdfb3314e7836331175e2ced79559712f4 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:35:58 -0500 Subject: [PATCH 39/76] Revert "skip apt-get update" This reverts commit 9c847eca678c7f9b949097c458c89013dc78efd0. --- .github/workflows/raylib_ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 71e746a8c00274..daf8cc2a13fb61 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -91,7 +91,7 @@ jobs: if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' id: find_diff run: >- - sudo apt-get install -y imagemagick + sudo apt-get update && sudo apt-get install -y imagemagick scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) From 3b1d0da84b6224124851966576e53a3f818e2cd4 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:41:49 -0500 Subject: [PATCH 40/76] longer delay --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 9f92cffa09087f..2eda3ad05cc280 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -176,7 +176,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): pyautogui.mouseDown(start_x, start_y) pyautogui.moveTo(start_x, end_y, duration=0.35) pyautogui.mouseUp(start_x, end_y) - time.sleep(1) + time.sleep(1.5) full_screenshot = pyautogui.screenshot() if not full_screenshot: raise Exception(f"failed to capture screenshot on page {i}") From e4c2965d9240fc45c76079904ef80d38990808ff Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 00:57:37 -0500 Subject: [PATCH 41/76] add tiny delays between mouse events --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 2eda3ad05cc280..d979f16b166337 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -174,7 +174,9 @@ def capture_scrollable(self, name: str, max_pages: int = 8): start_y = self.ui.top + center_y + int(self.ui.height * 0.5) end_y = self.ui.top + center_y - int(self.ui.height) pyautogui.mouseDown(start_x, start_y) + time.sleep(0.01) pyautogui.moveTo(start_x, end_y, duration=0.35) + time.sleep(0.01) pyautogui.mouseUp(start_x, end_y) time.sleep(1.5) full_screenshot = pyautogui.screenshot() From 9749f3495c7c8ed5eecf33109bfbb2bd75dc5f21 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 01:05:46 -0500 Subject: [PATCH 42/76] enhance screenshot capture with progression diffs and raw diff saving --- .../ui/tests/test_ui/raylib_screenshots.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index d979f16b166337..96eb95bfe4535e 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -8,7 +8,7 @@ import logging import pyautogui -from PIL import ImageChops +from PIL import Image, ImageChops from cereal import log from cereal import messaging @@ -156,7 +156,7 @@ def screenshot(self, name: str): cropped.save(SCREENSHOTS_DIR / f"{name}.png") def capture_scrollable(self, name: str, max_pages: int = 8): - """Capture a scrollable page by taking screenshots and scrolling until content stops changing.""" + """Capture a scrollable page by taking screenshots and saving progression diffs between pages.""" # center point inside UI where content is likely present center_x = int(self.ui.width * 0.5) center_y = int(self.ui.height * 0.5) @@ -192,8 +192,25 @@ def capture_scrollable(self, name: str, max_pages: int = 8): break except Exception as e: print(f"error comparing screenshots: {e}") + break + # save the current page curr.save(SCREENSHOTS_DIR / f"{name}_{i}.png") + + # create a visual progression diff: red overlay where pixels changed + try: + # threshold the diff to create a mask + mask = diff.convert('L').point(lambda p: 255 if p > 20 else 0) + overlay = Image.new('RGBA', curr.size, (255, 0, 0, 120)) + curr_rgba = curr.convert('RGBA') + progression = curr_rgba.copy() + progression.paste(overlay, (0, 0), mask) + progression.save(SCREENSHOTS_DIR / f"{name}_{i}_progression.png") + # also save the raw diff for debugging if desired + diff.save(SCREENSHOTS_DIR / f"{name}_{i}_rawdiff.png") + except Exception as e: + print(f"failed to build progression diff for {name} page {i}: {e}") + prev = curr def click(self, x: int, y: int, *args, **kwargs): From 0a414325b548b8211031a4ce6f9c0a4c6b90bfcb Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 11:16:40 -0500 Subject: [PATCH 43/76] don't save diff --- .../ui/tests/test_ui/raylib_screenshots.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 1ca1da0e7ca745..5d486fa972b60e 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -197,19 +197,19 @@ def capture_scrollable(self, name: str, max_pages: int = 8): # save the current page curr.save(SCREENSHOTS_DIR / f"{name}_{i}.png") - # create a visual progression diff: red overlay where pixels changed - try: - # threshold the diff to create a mask - mask = diff.convert('L').point(lambda p: 255 if p > 20 else 0) - overlay = Image.new('RGBA', curr.size, (255, 0, 0, 120)) - curr_rgba = curr.convert('RGBA') - progression = curr_rgba.copy() - progression.paste(overlay, (0, 0), mask) - progression.save(SCREENSHOTS_DIR / f"{name}_{i}_progression.png") - # also save the raw diff for debugging if desired - diff.save(SCREENSHOTS_DIR / f"{name}_{i}_rawdiff.png") - except Exception as e: - print(f"failed to build progression diff for {name} page {i}: {e}") + # # create a visual progression diff: red overlay where pixels changed + # try: + # # threshold the diff to create a mask + # mask = diff.convert('L').point(lambda p: 255 if p > 20 else 0) + # overlay = Image.new('RGBA', curr.size, (255, 0, 0, 120)) + # curr_rgba = curr.convert('RGBA') + # progression = curr_rgba.copy() + # progression.paste(overlay, (0, 0), mask) + # progression.save(SCREENSHOTS_DIR / f"{name}_{i}_progression.png") + # # also save the raw diff for debugging if desired + # diff.save(SCREENSHOTS_DIR / f"{name}_{i}_rawdiff.png") + # except Exception as e: + # print(f"failed to build progression diff for {name} page {i}: {e}") prev = curr From e88590d361a3166405d4100962200398df6aca7b Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 11:17:09 -0500 Subject: [PATCH 44/76] use original file name for first one --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 5d486fa972b60e..026e3fd1f02254 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -166,7 +166,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): if not full_screenshot: raise Exception("failed to capture screenshot") prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) - prev.save(SCREENSHOTS_DIR / f"{name}_0.png") + prev.save(SCREENSHOTS_DIR / f"{name}.png") for i in range(1, max_pages): # pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) From 2d5153fd4a03b0e1325ab6b133e6abdcc21e8c5b Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 11:21:26 -0500 Subject: [PATCH 45/76] fix import --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 026e3fd1f02254..555bc3d53f4877 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -8,7 +8,7 @@ import logging import pyautogui -from PIL import Image, ImageChops +from PIL import ImageChops from cereal import log from cereal import messaging @@ -197,20 +197,6 @@ def capture_scrollable(self, name: str, max_pages: int = 8): # save the current page curr.save(SCREENSHOTS_DIR / f"{name}_{i}.png") - # # create a visual progression diff: red overlay where pixels changed - # try: - # # threshold the diff to create a mask - # mask = diff.convert('L').point(lambda p: 255 if p > 20 else 0) - # overlay = Image.new('RGBA', curr.size, (255, 0, 0, 120)) - # curr_rgba = curr.convert('RGBA') - # progression = curr_rgba.copy() - # progression.paste(overlay, (0, 0), mask) - # progression.save(SCREENSHOTS_DIR / f"{name}_{i}_progression.png") - # # also save the raw diff for debugging if desired - # diff.save(SCREENSHOTS_DIR / f"{name}_{i}_rawdiff.png") - # except Exception as e: - # print(f"failed to build progression diff for {name} page {i}: {e}") - prev = curr def click(self, x: int, y: int, *args, **kwargs): From 138dbf9357c454057c29fb8392d914310c9930ce Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 11:29:41 -0500 Subject: [PATCH 46/76] update comments --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 555bc3d53f4877..951855ef02862a 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -156,7 +156,6 @@ def screenshot(self, name: str): cropped.save(SCREENSHOTS_DIR / f"{name}.png") def capture_scrollable(self, name: str, max_pages: int = 8): - """Capture a scrollable page by taking screenshots and saving progression diffs between pages.""" # center point inside UI where content is likely present center_x = int(self.ui.width * 0.5) center_y = int(self.ui.height * 0.5) @@ -178,7 +177,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): pyautogui.moveTo(start_x, end_y, duration=0.35) time.sleep(0.01) pyautogui.mouseUp(start_x, end_y) - time.sleep(1.5) + time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this full_screenshot = pyautogui.screenshot() if not full_screenshot: raise Exception(f"failed to capture screenshot on page {i}") @@ -186,6 +185,7 @@ def capture_scrollable(self, name: str, max_pages: int = 8): # check for difference try: + # This might need to be more robust to allow for small pixel diffs in case scrolling isn't consistent, but so far it seems to work diff = ImageChops.difference(prev.convert('RGB'), curr.convert('RGB')) if diff.getbbox() is None: # no changes -> reached end From 81cd6db0d87761b77774ad346c2947e7f84de094 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 12:31:43 -0500 Subject: [PATCH 47/76] use xdotool command for scrolling to fix clicking issue --- .../ui/tests/test_ui/raylib_screenshots.py | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 951855ef02862a..2a64d1182e0967 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -168,16 +168,11 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev.save(SCREENSHOTS_DIR / f"{name}.png") for i in range(1, max_pages): - # pyautogui.scroll(-300, x=self.ui.left + center_x, y=self.ui.top + center_y) - start_x = self.ui.left + center_x - start_y = self.ui.top + center_y + int(self.ui.height * 0.5) - end_y = self.ui.top + center_y - int(self.ui.height) - pyautogui.mouseDown(start_x, start_y) - time.sleep(0.01) - pyautogui.moveTo(start_x, end_y, duration=0.35) - time.sleep(0.01) - pyautogui.mouseUp(start_x, end_y) - time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this + # pyautogui.moveTo(self.ui.left + center_x, self.ui.top + center_y) + # time.sleep(0.01) + # 20 clicks is about a full page, but for smaller scroll panels we should use less + self.vscroll(-15, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine + time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this full_screenshot = pyautogui.screenshot() if not full_screenshot: raise Exception(f"failed to capture screenshot on page {i}") @@ -204,6 +199,24 @@ def click(self, x: int, y: int, *args, **kwargs): time.sleep(0.01) pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs) + def vscroll(self, clicks: int, delay=100): + """Perform vertical scroll using xdotool. + + clicks > 0: scroll up + clicks < 0: scroll down + + delay: delay between clicks in milliseconds + """ + clicks = int(clicks) + if clicks == 0: + return + elif clicks > 0: + button = 4 # scroll up + else: + button = 5 # scroll down + # send xdotool events + os.system(f"xdotool click --repeat {abs(clicks)} --delay {delay} {button}") + @with_processes(["ui"]) def test_ui(self, name, setup_case): self.setup() From dfc02f3e92131ddde94aa38f2f35705558aae9b0 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 12:32:07 -0500 Subject: [PATCH 48/76] disable unused code --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 2a64d1182e0967..65921ccd26343a 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -156,9 +156,9 @@ def screenshot(self, name: str): cropped.save(SCREENSHOTS_DIR / f"{name}.png") def capture_scrollable(self, name: str, max_pages: int = 8): - # center point inside UI where content is likely present - center_x = int(self.ui.width * 0.5) - center_y = int(self.ui.height * 0.5) + # # center point inside UI where content is likely present + # center_x = int(self.ui.width * 0.5) + # center_y = int(self.ui.height * 0.5) # take first screenshot full_screenshot = pyautogui.screenshot() From a442ccc851ac129cd83ec43de40ab37a7f5ada2a Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 12:48:32 -0500 Subject: [PATCH 49/76] refactor scrolling logic to support different scroll amounts per case --- .../ui/tests/test_ui/raylib_screenshots.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 65921ccd26343a..dcc577a21f9fb1 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -24,6 +24,7 @@ TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 +DEFAULT_SCROLL_AMOUNT = -20 # Perfect for most full screen scrollers logger = logging.getLogger("raylib_screenshots") @@ -132,6 +133,12 @@ def setup_software_release_notes(click, pm: PubMaster): "software_release_notes": setup_software_release_notes, } +# per-case scroll amount overrides (negative -> scroll down, positive -> scroll up) +SCROLL_AMOUNT_OVERRIDES = { + "offroad_alert": -12, + "homescreen_update_available": -12, +} + class TestUI: def __init__(self): @@ -155,7 +162,7 @@ def screenshot(self, name: str): cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) cropped.save(SCREENSHOTS_DIR / f"{name}.png") - def capture_scrollable(self, name: str, max_pages: int = 8): + def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=8): # # center point inside UI where content is likely present # center_x = int(self.ui.width * 0.5) # center_y = int(self.ui.height * 0.5) @@ -167,11 +174,11 @@ def capture_scrollable(self, name: str, max_pages: int = 8): prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) prev.save(SCREENSHOTS_DIR / f"{name}.png") - for i in range(1, max_pages): + for i in range(1, max_screenshots): # pyautogui.moveTo(self.ui.left + center_x, self.ui.top + center_y) # time.sleep(0.01) # 20 clicks is about a full page, but for smaller scroll panels we should use less - self.vscroll(-15, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine + self.vscroll(scroll_clicks, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this full_screenshot = pyautogui.screenshot() if not full_screenshot: @@ -222,15 +229,12 @@ def test_ui(self, name, setup_case): self.setup() time.sleep(UI_DELAY) # wait for UI to start setup_case(self.click, self.pm) - # For pages that can scroll (toggles) capture multiple pages - # if name == "settings_toggles": try: - self.capture_scrollable(name) + scroll_clicks = SCROLL_AMOUNT_OVERRIDES.get(name, DEFAULT_SCROLL_AMOUNT) + self.capture_scrollable(name, scroll_clicks=scroll_clicks) except Exception: logger.exception("failed capturing scrollable page, falling back to single screenshot") self.screenshot(name) - # else: - # self.screenshot(name) def create_screenshots(): From a38295ba47421cc120f4a768852238d17600f218 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 12:53:00 -0500 Subject: [PATCH 50/76] install xdotool in ui test --- .github/workflows/selfdrive_tests.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 99528c96a439d7..a7e9d69b01a92f 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -253,7 +253,9 @@ jobs: - name: Build openpilot run: ${{ env.RUN }} "scons -j$(nproc)" - name: Create raylib UI Report - run: > + run: | + # install xdotool on the runner so xdotool is available inside the test runtime when possible + sudo apt-get update && sudo apt-get install -y xdotool ${{ env.RUN }} "PYTHONWARNINGS=ignore && source selfdrive/test/setup_xvfb.sh && python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py" From 8a6eba24557b3f37922d962bed9d88e3200e1656 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 12:53:38 -0500 Subject: [PATCH 51/76] add error handling for xdotool command in UI test --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index dcc577a21f9fb1..b51643841c737f 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -222,7 +222,9 @@ def vscroll(self, clicks: int, delay=100): else: button = 5 # scroll down # send xdotool events - os.system(f"xdotool click --repeat {abs(clicks)} --delay {delay} {button}") + result = os.system(f"xdotool click --repeat {abs(clicks)} --delay {delay} {button}") + if result != 0: + raise Exception("xdotool command failed (ensure xdotool is installed)") @with_processes(["ui"]) def test_ui(self, name, setup_case): From 2793eea1e41dc9954712feaf59fa5a19644966bb Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 13:03:45 -0500 Subject: [PATCH 52/76] install xdotool in the test runtime setup for UI report generation --- .github/workflows/selfdrive_tests.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index a7e9d69b01a92f..f659e4ab813b66 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -254,9 +254,8 @@ jobs: run: ${{ env.RUN }} "scons -j$(nproc)" - name: Create raylib UI Report run: | - # install xdotool on the runner so xdotool is available inside the test runtime when possible - sudo apt-get update && sudo apt-get install -y xdotool - ${{ env.RUN }} "PYTHONWARNINGS=ignore && + ${{ env.RUN }} "apt-get update && apt-get install -y xdotool && + PYTHONWARNINGS=ignore && source selfdrive/test/setup_xvfb.sh && python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py" - name: Upload Raylib UI Report From b1bec5e64d8be74bebb95483204ff0e8b3feb9a3 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 13:57:20 -0500 Subject: [PATCH 53/76] add case config types --- .../ui/tests/test_ui/raylib_screenshots.py | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index b51643841c737f..9c8a96594442f5 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -5,6 +5,8 @@ import time import pathlib from collections import namedtuple +from collections.abc import Callable +from typing import NotRequired, TypedDict import logging import pyautogui @@ -117,7 +119,14 @@ def setup_software_release_notes(click, pm: PubMaster): click(588, 110) # expand description for current version -CASES = { +SetupFunction = Callable[[Callable[..., None], PubMaster], None] +class CaseConfig(TypedDict): + scroll_amount: NotRequired[int] + scroll_enabled: NotRequired[bool] +CaseValue = SetupFunction | tuple[SetupFunction, CaseConfig | None] + +# Value can be the setup function, or tuple of (setup func, config) +CASES: dict[str, CaseValue] = { "homescreen": setup_homescreen, "settings_device": setup_settings, "settings_network": setup_settings_network, @@ -127,19 +136,12 @@ def setup_software_release_notes(click, pm: PubMaster): "settings_developer": setup_settings_developer, "keyboard": setup_keyboard, "pair_device": setup_pair_device, - "offroad_alert": setup_offroad_alert, - "homescreen_update_available": setup_homescreen_update_available, + "offroad_alert": (setup_offroad_alert, {"scroll_amount": -12}), + "homescreen_update_available": (setup_homescreen_update_available, {"scroll_amount": -12}), "confirmation_dialog": setup_confirmation_dialog, "software_release_notes": setup_software_release_notes, } -# per-case scroll amount overrides (negative -> scroll down, positive -> scroll up) -SCROLL_AMOUNT_OVERRIDES = { - "offroad_alert": -12, - "homescreen_update_available": -12, -} - - class TestUI: def __init__(self): os.environ["SCALE"] = os.getenv("SCALE", "1") @@ -227,12 +229,20 @@ def vscroll(self, clicks: int, delay=100): raise Exception("xdotool command failed (ensure xdotool is installed)") @with_processes(["ui"]) - def test_ui(self, name, setup_case): + def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | None = None): self.setup() time.sleep(UI_DELAY) # wait for UI to start setup_case(self.click, self.pm) + config = config or {} + + # If scrolling disabled for this case, just take a screenshot + scroll_enabled = config.get("scroll_enabled", True) + if not scroll_enabled: + self.screenshot(name) + return + try: - scroll_clicks = SCROLL_AMOUNT_OVERRIDES.get(name, DEFAULT_SCROLL_AMOUNT) + scroll_clicks = config.get("scroll_amount", DEFAULT_SCROLL_AMOUNT) self.capture_scrollable(name, scroll_clicks=scroll_clicks) except Exception: logger.exception("failed capturing scrollable page, falling back to single screenshot") @@ -249,7 +259,11 @@ def create_screenshots(): params = Params() params.put("DongleId", "123456789012345") for name, setup in CASES.items(): - t.test_ui(name, setup) + if isinstance(setup, tuple): + setup_fn, cfg = setup + else: + setup_fn, cfg = setup, None + t.test_ui(name, setup_fn, cfg) if __name__ == "__main__": From 519f742c3da092c48ac1d8cd4e6411bb77764101 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 14:13:33 -0500 Subject: [PATCH 54/76] modify keyboard case to disable scrolling --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 9c8a96594442f5..8f2a51cc8909b8 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -26,7 +26,7 @@ TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 -DEFAULT_SCROLL_AMOUNT = -20 # Perfect for most full screen scrollers +DEFAULT_SCROLL_AMOUNT = -20 # Good for most full screen scrollers logger = logging.getLogger("raylib_screenshots") @@ -134,7 +134,7 @@ class CaseConfig(TypedDict): "settings_software": setup_settings_software, "settings_firehose": setup_settings_firehose, "settings_developer": setup_settings_developer, - "keyboard": setup_keyboard, + "keyboard": (setup_keyboard, {"scroll_enabled": False}), # The blinking cursor makes it think there was a change when scrolling "pair_device": setup_pair_device, "offroad_alert": (setup_offroad_alert, {"scroll_amount": -12}), "homescreen_update_available": (setup_homescreen_update_available, {"scroll_amount": -12}), From 34712d7821021a808926aa36b0c1f8b7842082cb Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 14:15:00 -0500 Subject: [PATCH 55/76] simplify unpacking --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 8f2a51cc8909b8..95d8aa6a9bdaa5 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -259,10 +259,7 @@ def create_screenshots(): params = Params() params.put("DongleId", "123456789012345") for name, setup in CASES.items(): - if isinstance(setup, tuple): - setup_fn, cfg = setup - else: - setup_fn, cfg = setup, None + setup_fn, cfg = setup if isinstance(setup, tuple) else (setup, None) t.test_ui(name, setup_fn, cfg) From e0137f9856de109f7f0806e84a8e339cb6309829 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 14:22:45 -0500 Subject: [PATCH 56/76] clean up --- .../ui/tests/test_ui/raylib_screenshots.py | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 95d8aa6a9bdaa5..fa9d070152769e 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -7,7 +7,6 @@ from collections import namedtuple from collections.abc import Callable from typing import NotRequired, TypedDict -import logging import pyautogui from PIL import ImageChops @@ -27,8 +26,7 @@ SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 DEFAULT_SCROLL_AMOUNT = -20 # Good for most full screen scrollers - -logger = logging.getLogger("raylib_screenshots") +MAX_SCREENSHOTS_PER_CASE = 8 # Maximum screenshots to generate while scrolling # Offroad alerts to test @@ -164,30 +162,20 @@ def screenshot(self, name: str): cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) cropped.save(SCREENSHOTS_DIR / f"{name}.png") - def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=8): - # # center point inside UI where content is likely present - # center_x = int(self.ui.width * 0.5) - # center_y = int(self.ui.height * 0.5) - - # take first screenshot + def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=MAX_SCREENSHOTS_PER_CASE): + # Take first screenshot full_screenshot = pyautogui.screenshot() - if not full_screenshot: - raise Exception("failed to capture screenshot") prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) prev.save(SCREENSHOTS_DIR / f"{name}.png") + # Scroll until there are no more changes or we reach the limit for i in range(1, max_screenshots): - # pyautogui.moveTo(self.ui.left + center_x, self.ui.top + center_y) - # time.sleep(0.01) - # 20 clicks is about a full page, but for smaller scroll panels we should use less self.vscroll(scroll_clicks, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this full_screenshot = pyautogui.screenshot() - if not full_screenshot: - raise Exception(f"failed to capture screenshot on page {i}") curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) - # check for difference + # Check for difference try: # This might need to be more robust to allow for small pixel diffs in case scrolling isn't consistent, but so far it seems to work diff = ImageChops.difference(prev.convert('RGB'), curr.convert('RGB')) @@ -198,7 +186,7 @@ def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=8): print(f"error comparing screenshots: {e}") break - # save the current page + # Save the current page curr.save(SCREENSHOTS_DIR / f"{name}_{i}.png") prev = curr @@ -211,19 +199,15 @@ def click(self, x: int, y: int, *args, **kwargs): def vscroll(self, clicks: int, delay=100): """Perform vertical scroll using xdotool. - clicks > 0: scroll up - clicks < 0: scroll down + clicks: number of scroll clicks to perform (negative means scroll down) - delay: delay between clicks in milliseconds + delay: delay between scroll clicks in milliseconds """ clicks = int(clicks) if clicks == 0: return - elif clicks > 0: - button = 4 # scroll up - else: - button = 5 # scroll down - # send xdotool events + button = 4 if clicks > 0 else 5 # 4=up, 5=down + # Run xdotool command to scroll result = os.system(f"xdotool click --repeat {abs(clicks)} --delay {delay} {button}") if result != 0: raise Exception("xdotool command failed (ensure xdotool is installed)") @@ -231,11 +215,11 @@ def vscroll(self, clicks: int, delay=100): @with_processes(["ui"]) def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | None = None): self.setup() - time.sleep(UI_DELAY) # wait for UI to start + time.sleep(UI_DELAY) # Wait for UI to start setup_case(self.click, self.pm) config = config or {} - # If scrolling disabled for this case, just take a screenshot + # Just take a screenshot if scrolling is disabled scroll_enabled = config.get("scroll_enabled", True) if not scroll_enabled: self.screenshot(name) @@ -244,8 +228,8 @@ def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | Non try: scroll_clicks = config.get("scroll_amount", DEFAULT_SCROLL_AMOUNT) self.capture_scrollable(name, scroll_clicks=scroll_clicks) - except Exception: - logger.exception("failed capturing scrollable page, falling back to single screenshot") + except Exception as e: + print(f"failed capturing scrollable page, falling back to single screenshot: {e}") self.screenshot(name) From ff92b96a223e393510c98cef083abc05d082ecef Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 17:27:02 -0500 Subject: [PATCH 57/76] more refactors --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index fa9d070152769e..d111fbda5ebfcb 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -117,10 +117,12 @@ def setup_software_release_notes(click, pm: PubMaster): click(588, 110) # expand description for current version -SetupFunction = Callable[[Callable[..., None], PubMaster], None] class CaseConfig(TypedDict): scroll_amount: NotRequired[int] scroll_enabled: NotRequired[bool] + + +SetupFunction = Callable[[Callable[..., None], PubMaster], None] CaseValue = SetupFunction | tuple[SetupFunction, CaseConfig | None] # Value can be the setup function, or tuple of (setup func, config) @@ -134,12 +136,13 @@ class CaseConfig(TypedDict): "settings_developer": setup_settings_developer, "keyboard": (setup_keyboard, {"scroll_enabled": False}), # The blinking cursor makes it think there was a change when scrolling "pair_device": setup_pair_device, - "offroad_alert": (setup_offroad_alert, {"scroll_amount": -12}), - "homescreen_update_available": (setup_homescreen_update_available, {"scroll_amount": -12}), + "offroad_alert": (setup_offroad_alert, {"scroll_amount": -12}), # smaller scrollable area + "homescreen_update_available": (setup_homescreen_update_available, {"scroll_amount": -12}), # smaller scrollable area "confirmation_dialog": setup_confirmation_dialog, "software_release_notes": setup_software_release_notes, } + class TestUI: def __init__(self): os.environ["SCALE"] = os.getenv("SCALE", "1") @@ -171,13 +174,13 @@ def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=MAX_ # Scroll until there are no more changes or we reach the limit for i in range(1, max_screenshots): self.vscroll(scroll_clicks, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine - time.sleep(1.5) # 1.0 didn't seem to be enough (caused small font pixel differences); if that happens again, try increasing this + time.sleep(1.5) # 1.0 didn't seem to be enough (font edge pixel diffs) full_screenshot = pyautogui.screenshot() curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) # Check for difference try: - # This might need to be more robust to allow for small pixel diffs in case scrolling isn't consistent, but so far it seems to work + # TODO: This might need to be more robust to allow for small pixel diffs in case scrolling isn't consistent, but so far it seems to work diff = ImageChops.difference(prev.convert('RGB'), curr.convert('RGB')) if diff.getbbox() is None: # no changes -> reached end From 79d435d2fff3e13aabf8e75e3e1c697929a72ca0 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 17:30:35 -0500 Subject: [PATCH 58/76] refactor screenshot methods to improve usability and save functionality --- .../ui/tests/test_ui/raylib_screenshots.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index d111fbda5ebfcb..b72aaf71cbb4cc 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -160,23 +160,25 @@ def setup(self): time.sleep(0.5) self.ui = namedtuple("bb", ["left", "top", "width", "height"])(0, 0, 2160, 1080) - def screenshot(self, name: str): + def screenshot(self): full_screenshot = pyautogui.screenshot() cropped = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) - cropped.save(SCREENSHOTS_DIR / f"{name}.png") + return cropped + + def screenshot_and_save(self, name: str): + screenshot = self.screenshot() + screenshot.save(SCREENSHOTS_DIR / f"{name}.png") + return screenshot def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=MAX_SCREENSHOTS_PER_CASE): # Take first screenshot - full_screenshot = pyautogui.screenshot() - prev = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) - prev.save(SCREENSHOTS_DIR / f"{name}.png") + prev = self.screenshot_and_save(name) # Scroll until there are no more changes or we reach the limit for i in range(1, max_screenshots): self.vscroll(scroll_clicks, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine time.sleep(1.5) # 1.0 didn't seem to be enough (font edge pixel diffs) - full_screenshot = pyautogui.screenshot() - curr = full_screenshot.crop((self.ui.left, self.ui.top, self.ui.left + self.ui.width, self.ui.top + self.ui.height)) + curr = self.screenshot() # Check for difference try: @@ -225,7 +227,7 @@ def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | Non # Just take a screenshot if scrolling is disabled scroll_enabled = config.get("scroll_enabled", True) if not scroll_enabled: - self.screenshot(name) + self.screenshot_and_save(name) return try: @@ -233,7 +235,7 @@ def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | Non self.capture_scrollable(name, scroll_clicks=scroll_clicks) except Exception as e: print(f"failed capturing scrollable page, falling back to single screenshot: {e}") - self.screenshot(name) + self.screenshot_and_save(name) def create_screenshots(): From d8dc6feb096812762062870b51cf4cf357353be2 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 17:49:19 -0500 Subject: [PATCH 59/76] create SCROLL_DELAY constant --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index b72aaf71cbb4cc..fc66b840de1e57 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -25,6 +25,7 @@ TEST_OUTPUT_DIR = TEST_DIR / "raylib_report" SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 +SCROLL_DELAY = 1.5 # Delay screenshot by this many seconds after scrolling (to allow scroll to settle) DEFAULT_SCROLL_AMOUNT = -20 # Good for most full screen scrollers MAX_SCREENSHOTS_PER_CASE = 8 # Maximum screenshots to generate while scrolling @@ -176,8 +177,8 @@ def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=MAX_ # Scroll until there are no more changes or we reach the limit for i in range(1, max_screenshots): - self.vscroll(scroll_clicks, delay=50) # 20ms didn't work well for larger scrolls; 50 seems fine - time.sleep(1.5) # 1.0 didn't seem to be enough (font edge pixel diffs) + self.vscroll(scroll_clicks, delay=50) # 50ms between scroll clicks; 20ms didn't work well for larger scrolls + time.sleep(SCROLL_DELAY) curr = self.screenshot() # Check for difference From 71c97f15f3108d04be50a67233f6320032e146c9 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 19:13:35 -0500 Subject: [PATCH 60/76] test smaller step amount --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index fc66b840de1e57..86552aecb3fb4c 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -26,7 +26,7 @@ SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 SCROLL_DELAY = 1.5 # Delay screenshot by this many seconds after scrolling (to allow scroll to settle) -DEFAULT_SCROLL_AMOUNT = -20 # Good for most full screen scrollers +DEFAULT_SCROLL_AMOUNT = -10 # Good for most full screen scrollers MAX_SCREENSHOTS_PER_CASE = 8 # Maximum screenshots to generate while scrolling From 6e7354dfc5028d768c58d351f56538bfd0309d99 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 19:23:54 -0500 Subject: [PATCH 61/76] Revert "test smaller step amount" This reverts commit 71c97f15f3108d04be50a67233f6320032e146c9. --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 86552aecb3fb4c..fc66b840de1e57 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -26,7 +26,7 @@ SCREENSHOTS_DIR = TEST_OUTPUT_DIR / "screenshots" UI_DELAY = 0.2 SCROLL_DELAY = 1.5 # Delay screenshot by this many seconds after scrolling (to allow scroll to settle) -DEFAULT_SCROLL_AMOUNT = -10 # Good for most full screen scrollers +DEFAULT_SCROLL_AMOUNT = -20 # Good for most full screen scrollers MAX_SCREENSHOTS_PER_CASE = 8 # Maximum screenshots to generate while scrolling From 4e78fffe920cb4cf419493e7e6e2b6add0f4c875 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 19:24:46 -0500 Subject: [PATCH 62/76] re-enable macOS test --- .github/workflows/selfdrive_tests.yaml | 64 +++++++++++++------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index f659e4ab813b66..8d7fc5a006100f 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -78,38 +78,38 @@ jobs: - uses: ./.github/workflows/compile-openpilot timeout-minutes: 30 - # build_mac: - # name: build macOS - # runs-on: macos-latest - # steps: - # - uses: actions/checkout@v4 - # with: - # submodules: true - # - run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV - # - name: Homebrew cache - # uses: ./.github/workflows/auto-cache - # with: - # path: ~/Library/Caches/Homebrew - # key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} - # restore-keys: | - # brew-macos-${{ env.CACHE_COMMIT_DATE }} - # brew-macos - # - name: Install dependencies - # run: ./tools/mac_setup.sh - # env: - # PYTHONWARNINGS: default # package install has DeprecationWarnings - # HOMEBREW_DISPLAY_INSTALL_TIMES: 1 - # - run: git lfs pull - # - name: Getting scons cache - # uses: ./.github/workflows/auto-cache - # with: - # path: /tmp/scons_cache - # key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} - # restore-keys: | - # scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }} - # scons-${{ runner.arch }}-macos - # - name: Building openpilot - # run: . .venv/bin/activate && scons -j$(nproc) + build_mac: + name: build macOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV + - name: Homebrew cache + uses: ./.github/workflows/auto-cache + with: + path: ~/Library/Caches/Homebrew + key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} + restore-keys: | + brew-macos-${{ env.CACHE_COMMIT_DATE }} + brew-macos + - name: Install dependencies + run: ./tools/mac_setup.sh + env: + PYTHONWARNINGS: default # package install has DeprecationWarnings + HOMEBREW_DISPLAY_INSTALL_TIMES: 1 + - run: git lfs pull + - name: Getting scons cache + uses: ./.github/workflows/auto-cache + with: + path: /tmp/scons_cache + key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }} + restore-keys: | + scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }} + scons-${{ runner.arch }}-macos + - name: Building openpilot + run: . .venv/bin/activate && scons -j$(nproc) static_analysis: name: static analysis From adace1daa84cf982138ab2f13a11e2100020f913 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 19:42:51 -0500 Subject: [PATCH 63/76] fix macOS --- .github/workflows/selfdrive_tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 8d7fc5a006100f..feab1e5674bc53 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -94,6 +94,8 @@ jobs: restore-keys: | brew-macos-${{ env.CACHE_COMMIT_DATE }} brew-macos + - name: Force link openssl@3 + run: brew link --overwrite openssl@3 - name: Install dependencies run: ./tools/mac_setup.sh env: From 323da41e71d4f1ae6166cb8e75fb719719a875c3 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 19:48:16 -0500 Subject: [PATCH 64/76] unlink first --- .github/workflows/selfdrive_tests.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index feab1e5674bc53..67b5c280262d97 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -94,6 +94,8 @@ jobs: restore-keys: | brew-macos-${{ env.CACHE_COMMIT_DATE }} brew-macos + - name: Unlink openssl@1.1 + run: brew unlink openssl@1.1 || true - name: Force link openssl@3 run: brew link --overwrite openssl@3 - name: Install dependencies From c0bf240322ea89102662916519e51065e119fb83 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 20:00:28 -0500 Subject: [PATCH 65/76] attempt fix again --- .github/workflows/selfdrive_tests.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 67b5c280262d97..c08fd83994e3bf 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -94,10 +94,10 @@ jobs: restore-keys: | brew-macos-${{ env.CACHE_COMMIT_DATE }} brew-macos - - name: Unlink openssl@1.1 - run: brew unlink openssl@1.1 || true - - name: Force link openssl@3 - run: brew link --overwrite openssl@3 + - name: Force relink openssl@3 + run: | + brew unlink openssl@3 || true + brew link --overwrite openssl@3 - name: Install dependencies run: ./tools/mac_setup.sh env: From da08749847da70c311b63f338446e18cc1735010 Mon Sep 17 00:00:00 2001 From: David Date: Sat, 11 Oct 2025 21:27:12 -0500 Subject: [PATCH 66/76] remove macOS fix --- .github/workflows/selfdrive_tests.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index c08fd83994e3bf..5af02fef7f4fd6 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -94,10 +94,10 @@ jobs: restore-keys: | brew-macos-${{ env.CACHE_COMMIT_DATE }} brew-macos - - name: Force relink openssl@3 - run: | - brew unlink openssl@3 || true - brew link --overwrite openssl@3 + # - name: Force relink openssl@3 + # run: | + # brew unlink openssl@3 || true + # brew link --overwrite openssl@3 - name: Install dependencies run: ./tools/mac_setup.sh env: From 51ccfa54c46e571c51ca0c5c3a4a538f21ab107b Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 13:56:35 -0500 Subject: [PATCH 67/76] add experimental mode description case and new scroll function that doesn't use xdotool --- .../ui/tests/test_ui/raylib_screenshots.py | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index fc66b840de1e57..a7404cf8ca46bd 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -42,88 +42,96 @@ def put_update_params(params: Params): params.put("UpdaterNewDescription", description) -def setup_homescreen(click, pm: PubMaster): +def setup_homescreen(click, scroll, pm: PubMaster): pass -def setup_settings(click, pm: PubMaster): +def setup_settings(click, scroll, pm: PubMaster): click(100, 100) -def close_settings(click, pm: PubMaster): +def close_settings(click, scroll, pm: PubMaster): click(240, 216) -def setup_settings_network(click, pm: PubMaster): - setup_settings(click, pm) +def setup_settings_network(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) click(278, 450) -def setup_settings_toggles(click, pm: PubMaster): - setup_settings(click, pm) +def setup_settings_toggles(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) click(278, 600) -def setup_settings_software(click, pm: PubMaster): +def setup_settings_software(click, scroll, pm: PubMaster): put_update_params(Params()) - setup_settings(click, pm) + setup_settings(click, scroll, pm) click(278, 720) -def setup_settings_firehose(click, pm: PubMaster): - setup_settings(click, pm) +def setup_settings_firehose(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) click(278, 845) -def setup_settings_developer(click, pm: PubMaster): - setup_settings(click, pm) +def setup_settings_developer(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) click(278, 950) -def setup_keyboard(click, pm: PubMaster): - setup_settings_developer(click, pm) +def setup_keyboard(click, scroll, pm: PubMaster): + setup_settings_developer(click, scroll, pm) click(1930, 470) -def setup_pair_device(click, pm: PubMaster): +def setup_pair_device(click, scroll, pm: PubMaster): click(1950, 800) -def setup_offroad_alert(click, pm: PubMaster): +def setup_offroad_alert(click, scroll, pm: PubMaster): set_offroad_alert("Offroad_TemperatureTooHigh", True, extra_text='99C') set_offroad_alert("Offroad_ExcessiveActuation", True, extra_text='longitudinal') for alert in OFFROAD_ALERTS: set_offroad_alert(alert, True) - setup_settings(click, pm) - close_settings(click, pm) + setup_settings(click, scroll, pm) + close_settings(click, scroll, pm) -def setup_confirmation_dialog(click, pm: PubMaster): - setup_settings(click, pm) +def setup_confirmation_dialog(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) click(1985, 791) # reset calibration -def setup_homescreen_update_available(click, pm: PubMaster): +def setup_homescreen_update_available(click, scroll, pm: PubMaster): params = Params() params.put_bool("UpdateAvailable", True) put_update_params(params) - setup_settings(click, pm) - close_settings(click, pm) + setup_settings(click, scroll, pm) + close_settings(click, scroll, pm) -def setup_software_release_notes(click, pm: PubMaster): - setup_settings(click, pm) - setup_settings_software(click, pm) +def setup_software_release_notes(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) + setup_settings_software(click, scroll, pm) click(588, 110) # expand description for current version +def setup_experimental_mode_description(click, scroll, pm: PubMaster): + setup_settings(click, scroll, pm) + setup_settings_toggles(click, scroll, pm) + click(1200, 280) # expand description for experimental mode + scroll(-4) # scroll down to show more of the description + time.sleep(1) + + class CaseConfig(TypedDict): scroll_amount: NotRequired[int] scroll_enabled: NotRequired[bool] -SetupFunction = Callable[[Callable[..., None], PubMaster], None] +SetupFunction = Callable[[Callable[..., None], Callable[..., None], PubMaster], None] CaseValue = SetupFunction | tuple[SetupFunction, CaseConfig | None] # Value can be the setup function, or tuple of (setup func, config) @@ -141,6 +149,10 @@ class CaseConfig(TypedDict): "homescreen_update_available": (setup_homescreen_update_available, {"scroll_amount": -12}), # smaller scrollable area "confirmation_dialog": setup_confirmation_dialog, "software_release_notes": setup_software_release_notes, + "experimental_mode_description": ( + setup_experimental_mode_description, + {"scroll_enabled": False}, + ), } @@ -218,11 +230,20 @@ def vscroll(self, clicks: int, delay=100): if result != 0: raise Exception("xdotool command failed (ensure xdotool is installed)") + def scroll(self, clicks: int, *args, **kwargs): + if clicks == 0: + return + click = -1 if clicks < 0 else 1 # -1 = down, 1 = up + clicks = abs(clicks) + for i in range(clicks): + pyautogui.scroll(click, *args, **kwargs) # call scroll for individual clicks since it doesn't use delay between clicks internally + time.sleep(0.01) # small delay between clicks otherwise it doesn't work + @with_processes(["ui"]) def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | None = None): self.setup() time.sleep(UI_DELAY) # Wait for UI to start - setup_case(self.click, self.pm) + setup_case(self.click, self.scroll, self.pm) config = config or {} # Just take a screenshot if scrolling is disabled From d6c188e31a8d509c6ec8f79013da28cf209ef150 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 14:16:41 -0500 Subject: [PATCH 68/76] improve scroll function --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index a7404cf8ca46bd..33850309e0e7a3 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -234,10 +234,9 @@ def scroll(self, clicks: int, *args, **kwargs): if clicks == 0: return click = -1 if clicks < 0 else 1 # -1 = down, 1 = up - clicks = abs(clicks) - for i in range(clicks): - pyautogui.scroll(click, *args, **kwargs) # call scroll for individual clicks since it doesn't use delay between clicks internally - time.sleep(0.01) # small delay between clicks otherwise it doesn't work + for _ in range(abs(clicks)): + pyautogui.scroll(click, *args, **kwargs) # scroll for individual clicks since we need to delay between clicks + time.sleep(0.01) # small delay between scroll clicks to work properly in xvfb @with_processes(["ui"]) def test_ui(self, name: str, setup_case: SetupFunction, config: CaseConfig | None = None): From 4b600582f48fb04cb3526e93e45ca560697c9dc0 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 14:20:29 -0500 Subject: [PATCH 69/76] remove xdotool scroll --- .github/workflows/selfdrive_tests.yaml | 3 +-- .../ui/tests/test_ui/raylib_screenshots.py | 18 +----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index 5af02fef7f4fd6..35c03f4957a0e6 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -258,8 +258,7 @@ jobs: run: ${{ env.RUN }} "scons -j$(nproc)" - name: Create raylib UI Report run: | - ${{ env.RUN }} "apt-get update && apt-get install -y xdotool && - PYTHONWARNINGS=ignore && + ${{ env.RUN }} "PYTHONWARNINGS=ignore && source selfdrive/test/setup_xvfb.sh && python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py" - name: Upload Raylib UI Report diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 33850309e0e7a3..a02a5cfe21bbf2 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -189,7 +189,7 @@ def capture_scrollable(self, name: str, scroll_clicks: int, max_screenshots=MAX_ # Scroll until there are no more changes or we reach the limit for i in range(1, max_screenshots): - self.vscroll(scroll_clicks, delay=50) # 50ms between scroll clicks; 20ms didn't work well for larger scrolls + self.scroll(scroll_clicks) time.sleep(SCROLL_DELAY) curr = self.screenshot() @@ -214,22 +214,6 @@ def click(self, x: int, y: int, *args, **kwargs): time.sleep(0.01) pyautogui.mouseUp(self.ui.left + x, self.ui.top + y, *args, **kwargs) - def vscroll(self, clicks: int, delay=100): - """Perform vertical scroll using xdotool. - - clicks: number of scroll clicks to perform (negative means scroll down) - - delay: delay between scroll clicks in milliseconds - """ - clicks = int(clicks) - if clicks == 0: - return - button = 4 if clicks > 0 else 5 # 4=up, 5=down - # Run xdotool command to scroll - result = os.system(f"xdotool click --repeat {abs(clicks)} --delay {delay} {button}") - if result != 0: - raise Exception("xdotool command failed (ensure xdotool is installed)") - def scroll(self, clicks: int, *args, **kwargs): if clicks == 0: return From 6cdc12d9198d867983a46629f4bfa5ca7e9b1d4d Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 16:23:13 -0500 Subject: [PATCH 70/76] openpilot long confirmation dialog --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index a02a5cfe21bbf2..0b9c6ccf2abf79 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -85,6 +85,11 @@ def setup_keyboard(click, scroll, pm: PubMaster): click(1930, 470) +def setup_openpilot_long_control_confirmation_dialog(click, scroll, pm: PubMaster): + setup_settings_developer(click, scroll, pm) + click(2000, 1000) # toggle openpilot longitudinal control + + def setup_pair_device(click, scroll, pm: PubMaster): click(1950, 800) @@ -153,6 +158,7 @@ class CaseConfig(TypedDict): setup_experimental_mode_description, {"scroll_enabled": False}, ), + "openpilot_long_control_confirmation_dialog": setup_openpilot_long_control_confirmation_dialog, } From d2d9e95c79b0ad1b15ff92e0453a976a2f3b5991 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 17:00:54 -0500 Subject: [PATCH 71/76] set alphaLongitudinalAvailable --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 0b9c6ccf2abf79..173c658f2821aa 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -11,7 +11,7 @@ import pyautogui from PIL import ImageChops -from cereal import log +from cereal import car, log from cereal import messaging from cereal.messaging import PubMaster from openpilot.common.basedir import BASEDIR @@ -76,6 +76,10 @@ def setup_settings_firehose(click, scroll, pm: PubMaster): def setup_settings_developer(click, scroll, pm: PubMaster): + CP = car.CarParams() + CP.alphaLongitudinalAvailable = True + Params().put("CarParamsPersistent", CP.to_bytes()) + setup_settings(click, scroll, pm) click(278, 950) @@ -87,7 +91,7 @@ def setup_keyboard(click, scroll, pm: PubMaster): def setup_openpilot_long_control_confirmation_dialog(click, scroll, pm: PubMaster): setup_settings_developer(click, scroll, pm) - click(2000, 1000) # toggle openpilot longitudinal control + click(2000, 960) # toggle openpilot longitudinal control def setup_pair_device(click, scroll, pm: PubMaster): From 713ce24ded5b81423861e6bb9b9f49699d8507b3 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 17:23:44 -0500 Subject: [PATCH 72/76] add comment --- selfdrive/ui/tests/test_ui/raylib_screenshots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/tests/test_ui/raylib_screenshots.py b/selfdrive/ui/tests/test_ui/raylib_screenshots.py index 173c658f2821aa..137b90991d86e9 100755 --- a/selfdrive/ui/tests/test_ui/raylib_screenshots.py +++ b/selfdrive/ui/tests/test_ui/raylib_screenshots.py @@ -77,7 +77,7 @@ def setup_settings_firehose(click, scroll, pm: PubMaster): def setup_settings_developer(click, scroll, pm: PubMaster): CP = car.CarParams() - CP.alphaLongitudinalAvailable = True + CP.alphaLongitudinalAvailable = True # show alpha long control toggle Params().put("CarParamsPersistent", CP.to_bytes()) setup_settings(click, scroll, pm) From 41bba83297eb604d827e569cef26d7b115547682 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 20:40:10 -0500 Subject: [PATCH 73/76] show removed images --- .github/workflows/raylib_ui_preview.yaml | 110 +++++++++++++---------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index daf8cc2a13fb61..68b6a7c6fd1a78 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -93,7 +93,13 @@ jobs: run: >- sudo apt-get update && sudo apt-get install -y imagemagick - scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') + PR_DIR="${{ github.workspace }}/pr_ui" + MASTER_DIR="${{ github.workspace }}/master_ui_raylib" + + scenes_pr=$(find "$PR_DIR" -maxdepth 1 -name '*.png' -printf "%f\n" | cut -d '.' -f 1 || true) + scenes_master=$(find "$MASTER_DIR" -maxdepth 1 -name '*.png' -printf "%f\n" | cut -d '.' -f 1 || true) + # combine and uniq, exclude pair_device + scenes=$(printf "%s\n%s\n" "$scenes_pr" "$scenes_master" | sort -u | grep -v 'pair_device' || true) A=($scenes) DIFF="" @@ -102,59 +108,71 @@ jobs: for ((i=0; i<${#A[*]}; i=i+1)); do - # Check if the master file exists - if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then - # This is a new file in PR UI that doesn't exist in master - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
" - DIFF="${DIFF}
" - elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then - convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png - composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png - convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif - - mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png - + # New in PR (exists in PR, not in master) + if [ ! -f "${MASTER_DIR}/${A[$i]}.png" ] && [ -f "${PR_DIR}/${A[$i]}.png" ]; then + # This is a new file in PR UI that doesn't exist in master + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
" + DIFF="${DIFF}
" + # Removed in PR (exists in master, missing in PR) + elif [ -f "${MASTER_DIR}/${A[$i]}.png" ] && [ ! -f "${PR_DIR}/${A[$i]}.png" ]; then DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" + DIFF="${DIFF}${A[$i]} : \$\${\\color{magenta}\\text{REMOVED}}\$\$" DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
master proposed
diff composite diff master
" DIFF="${DIFF}
" - else - rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png - fi - - INDEX=$(($i % 2)) - if [[ $INDEX -eq 0 ]]; then - TABLE="${TABLE}" - fi - TABLE="${TABLE} " - if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then - TABLE="${TABLE}" - fi - done - - TABLE="${TABLE}" - - echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" + elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then + convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png + composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png + convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif + + mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png + + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
master proposed
diff composite diff
" + DIFF="${DIFF}
" + else + rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png + fi + + INDEX=$(($i % 2)) + if [[ $INDEX -eq 0 ]]; then + TABLE="${TABLE}" + fi + TABLE="${TABLE} " + if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then + TABLE="${TABLE}" + fi + done + + TABLE="${TABLE}" + + echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - name: Saving proposed ui if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' From 46fc5870d543d1ceaed372d9c22041346cec79d8 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 20:58:01 -0500 Subject: [PATCH 74/76] Revert "show removed images" This reverts commit 41bba83297eb604d827e569cef26d7b115547682. --- .github/workflows/raylib_ui_preview.yaml | 110 ++++++++++------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index 68b6a7c6fd1a78..daf8cc2a13fb61 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -93,13 +93,7 @@ jobs: run: >- sudo apt-get update && sudo apt-get install -y imagemagick - PR_DIR="${{ github.workspace }}/pr_ui" - MASTER_DIR="${{ github.workspace }}/master_ui_raylib" - - scenes_pr=$(find "$PR_DIR" -maxdepth 1 -name '*.png' -printf "%f\n" | cut -d '.' -f 1 || true) - scenes_master=$(find "$MASTER_DIR" -maxdepth 1 -name '*.png' -printf "%f\n" | cut -d '.' -f 1 || true) - # combine and uniq, exclude pair_device - scenes=$(printf "%s\n%s\n" "$scenes_pr" "$scenes_master" | sort -u | grep -v 'pair_device' || true) + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') A=($scenes) DIFF="" @@ -108,71 +102,59 @@ jobs: for ((i=0; i<${#A[*]}; i=i+1)); do - # New in PR (exists in PR, not in master) - if [ ! -f "${MASTER_DIR}/${A[$i]}.png" ] && [ -f "${PR_DIR}/${A[$i]}.png" ]; then - # This is a new file in PR UI that doesn't exist in master - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
" - DIFF="${DIFF}
" - # Removed in PR (exists in master, missing in PR) - elif [ -f "${MASTER_DIR}/${A[$i]}.png" ] && [ ! -f "${PR_DIR}/${A[$i]}.png" ]; then + # Check if the master file exists + if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then + # This is a new file in PR UI that doesn't exist in master + DIFF="${DIFF}
" + DIFF="${DIFF}${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$" + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}
" + DIFF="${DIFF}
" + elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then + convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png + composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png + convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif + + mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png + DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{magenta}\\text{REMOVED}}\$\$" + DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" DIFF="${DIFF}" DIFF="${DIFF}" - DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF} " + DIFF="${DIFF}" + + DIFF="${DIFF}" + DIFF="${DIFF} " + DIFF="${DIFF} " DIFF="${DIFF}" DIFF="${DIFF}
master master proposed
diff composite diff
" DIFF="${DIFF}
" - elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then - convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png - composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png - convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif - - mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png - - DIFF="${DIFF}
" - DIFF="${DIFF}${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$" - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}" - DIFF="${DIFF} " - DIFF="${DIFF} " - DIFF="${DIFF}" - - DIFF="${DIFF}
master proposed
diff composite diff
" - DIFF="${DIFF}
" - else - rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png - fi - - INDEX=$(($i % 2)) - if [[ $INDEX -eq 0 ]]; then - TABLE="${TABLE}" - fi - TABLE="${TABLE} " - if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then - TABLE="${TABLE}" - fi - done - - TABLE="${TABLE}" - - echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" + else + rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png + fi + + INDEX=$(($i % 2)) + if [[ $INDEX -eq 0 ]]; then + TABLE="${TABLE}" + fi + TABLE="${TABLE} " + if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then + TABLE="${TABLE}" + fi + done + + TABLE="${TABLE}" + + echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT" - name: Saving proposed ui if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' From c9668a740d6bf2a654e3e962183109dcdd006a56 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 13 Oct 2025 21:07:47 -0500 Subject: [PATCH 75/76] don't exclude pair_device --- .github/workflows/raylib_ui_preview.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/raylib_ui_preview.yaml b/.github/workflows/raylib_ui_preview.yaml index daf8cc2a13fb61..40f01072791280 100644 --- a/.github/workflows/raylib_ui_preview.yaml +++ b/.github/workflows/raylib_ui_preview.yaml @@ -93,7 +93,7 @@ jobs: run: >- sudo apt-get update && sudo apt-get install -y imagemagick - scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device') + scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1) A=($scenes) DIFF="" From e01f87cab78cff1f0a1fe3e6942b177a79a26f3f Mon Sep 17 00:00:00 2001 From: David Date: Wed, 15 Oct 2025 14:15:20 -0500 Subject: [PATCH 76/76] relink openssl --- tools/mac_setup.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/mac_setup.sh b/tools/mac_setup.sh index 0ae0b35359e6e8..f6bc2be3e5b851 100755 --- a/tools/mac_setup.sh +++ b/tools/mac_setup.sh @@ -32,6 +32,10 @@ else brew up fi +# Relink openssl +brew unlink openssl@3 || true +brew link overwrite openssl@3 + brew bundle --file=- <<-EOS brew "git-lfs" brew "capnp"