feat(dashboard): info button opens in-app modal instead of external b… #368
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Example Smoke Tests | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| - cron: '0 7 * * 1' # Weekly Monday 07:00 UTC — MATLAB examples | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-mex: | |
| name: Build MEX | |
| uses: ./.github/workflows/_build-mex-octave.yml | |
| with: | |
| artifact-name: mex-linux-examples | |
| smoke-test: | |
| name: Example Smoke Tests | |
| timeout-minutes: 45 | |
| needs: build-mex | |
| runs-on: ubuntu-latest | |
| container: gnuoctave/octave:11.1.0 | |
| env: | |
| FASTSENSE_SKIP_BUILD: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Download MEX binaries | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: mex-linux-examples | |
| - name: Run example smoke tests | |
| shell: bash | |
| run: | | |
| # Each example runs in its own Octave subprocess so that a segfault | |
| # in one example (e.g. during close-all cleanup) does not kill the | |
| # entire test suite. | |
| EXAMPLES=( | |
| # Core | |
| example_basic | |
| example_multi | |
| example_datetime | |
| example_nan_gaps | |
| example_vibration | |
| example_ecg | |
| example_linked | |
| example_uneven_sampling | |
| example_alarm_bands | |
| example_lttb_vs_minmax | |
| example_multi_sensor_linked | |
| example_navigator_overlay | |
| example_visual_features | |
| example_toolbar | |
| example_100M | |
| # Dashboard (basic grid — no DashboardEngine) | |
| example_dashboard | |
| example_dashboard_9tile | |
| # Sensor | |
| example_sensor_static | |
| example_sensor_multi_state | |
| example_sensor_threshold | |
| example_sensor_registry | |
| example_sensor_dashboard | |
| # Heavy | |
| example_dynamic_thresholds_100M | |
| # ----------------------------------------------------------------- | |
| # Skipped — Octave-incompatible (MATLAB-only features): | |
| # example_themes — segfault in Octave figure cleanup | |
| # example_mixed_tiles — uses 'categorical' (MATLAB-only) | |
| # example_dock — uses 'datetime' (MATLAB-only) | |
| # example_dock_many_tabs — uses 'datetime' | |
| # example_dock_disk — uses 'datetime' | |
| # example_disk_storage — MATLAB-only API | |
| # example_sensor_detail_* — 'disableDefaultInteractivity' / datetime | |
| # example_sensor_todisk — MATLAB-only API | |
| # example_stress_test — interactive / long-running | |
| # Skipped — DashboardWidget uses abstract methods, Octave requires @-folders: | |
| # example_dashboard_engine example_dashboard_all_widgets | |
| # example_dashboard_groups example_dashboard_info | |
| # example_dashboard_advanced example_widget_* | |
| # Skipped — requires interaction / runs indefinitely: | |
| # demo_all — input() call | |
| # example_dashboard_live — live timer | |
| # example_event_detection_live — live timer | |
| # example_event_viewer_from_file — background timer | |
| # example_live_pipeline — starts 15s timer | |
| # example_sensor_detail — bare pause; calls | |
| ) | |
| # Start a persistent Xvfb so individual runs don't race for displays | |
| Xvfb :99 -screen 0 1280x1024x24 & | |
| XVFB_PID=$! | |
| export DISPLAY=:99 | |
| sleep 1 | |
| PASSED=0 | |
| FAILED=0 | |
| FAIL_LIST="" | |
| TOTAL=${#EXAMPLES[@]} | |
| for name in "${EXAMPLES[@]}"; do | |
| printf "Running %s... " "$name" | |
| if octave --eval " | |
| addpath(pwd); install(); | |
| cd('examples'); | |
| $name(); | |
| close all force; | |
| " > /tmp/example_out.log 2>&1; then | |
| echo "OK" | |
| PASSED=$((PASSED + 1)) | |
| else | |
| EC=$? | |
| echo "FAILED (exit code $EC)" | |
| tail -40 /tmp/example_out.log | grep -v '^\s*$' | tail -15 | sed 's/^/ /' | |
| FAILED=$((FAILED + 1)) | |
| FAIL_LIST="${FAIL_LIST}\n - ${name}" | |
| fi | |
| done | |
| kill $XVFB_PID 2>/dev/null || true | |
| echo "" | |
| echo "=== ${PASSED}/${TOTAL} examples passed ===" | |
| # --- GitHub Step Summary --- | |
| if [ -n "$GITHUB_STEP_SUMMARY" ]; then | |
| { | |
| echo "### Octave Example Smoke Tests" | |
| echo "" | |
| echo "- ${PASSED:-0}/${TOTAL:-0} passed" | |
| if [ -n "$FAIL_LIST" ]; then | |
| echo "" | |
| echo "**Failures:**" | |
| echo "" | |
| printf "%b" "$FAIL_LIST" | sed 's/^/- /' | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| if [ "$FAILED" -gt 0 ]; then | |
| echo "" | |
| printf "Failed:%b\n" "$FAIL_LIST" | |
| exit 1 | |
| fi | |
| matlab-examples: | |
| name: MATLAB Example Smoke Tests | |
| timeout-minutes: 60 | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup MATLAB | |
| uses: matlab-actions/setup-matlab@v3 | |
| with: | |
| # Matches tests.yml (PR #109): R2020b libmex segfaults on the | |
| # GitHub xvfb runner; codebase targets R2021a+ uifigure features. | |
| release: R2021b | |
| cache: true | |
| - name: Run MATLAB-only examples | |
| uses: matlab-actions/run-command@v3 | |
| with: | |
| command: | | |
| install(); | |
| cd('examples'); | |
| examples = { | |
| % DashboardEngine examples (require abstract classdef — MATLAB only) | |
| 'example_dashboard_engine' | |
| 'example_dashboard_all_widgets' | |
| 'example_dashboard_groups' | |
| 'example_dashboard_info' | |
| 'example_dashboard_advanced' | |
| % Individual widget examples | |
| 'example_widget_fastsense' | |
| 'example_widget_number' | |
| 'example_widget_status' | |
| 'example_widget_text' | |
| 'example_widget_gauge' | |
| 'example_widget_table' | |
| 'example_widget_rawaxes' | |
| 'example_widget_timeline' | |
| 'example_widget_group' | |
| 'example_widget_barchart' | |
| 'example_widget_heatmap' | |
| 'example_widget_histogram' | |
| 'example_widget_scatter' | |
| 'example_widget_image' | |
| 'example_widget_multistatus' | |
| % Other MATLAB-only examples | |
| 'example_themes' | |
| 'example_mixed_tiles' | |
| 'example_dock' | |
| 'example_dock_many_tabs' | |
| 'example_sensor_detail_basic' | |
| 'example_sensor_detail_dashboard' | |
| }; | |
| passed = 0; | |
| failed = 0; | |
| failList = {}; | |
| for i = 1:numel(examples) | |
| name = examples{i}; | |
| fprintf('Running %s... ', name); | |
| try | |
| feval(name); | |
| close all force; | |
| fprintf('OK\n'); | |
| passed = passed + 1; | |
| catch e | |
| fprintf('FAILED\n %s\n', e.message); | |
| failed = failed + 1; | |
| failList{end+1} = name; | |
| end | |
| end | |
| fprintf('\n=== %d/%d examples passed ===\n', passed, numel(examples)); | |
| nTotal = numel(examples); | |
| nPassed = passed; | |
| try | |
| summaryFile = getenv('GITHUB_STEP_SUMMARY'); | |
| if ~isempty(summaryFile) | |
| fid = fopen(summaryFile, 'a'); | |
| if fid > 0 | |
| fprintf(fid, '### MATLAB Examples\n\n'); | |
| fprintf(fid, '- %d/%d passed\n', nPassed, nTotal); | |
| if nTotal - nPassed > 0 && ~isempty(failList) | |
| fprintf(fid, '\n**Failures:**\n\n'); | |
| for k = 1:numel(failList) | |
| fprintf(fid, '- %s\n', failList{k}); | |
| end | |
| end | |
| fclose(fid); | |
| end | |
| end | |
| catch | |
| % never fail the job because of a step-summary write | |
| end | |
| if failed > 0 | |
| fprintf('\nFailed:\n'); | |
| for i = 1:numel(failList) | |
| fprintf(' - %s\n', failList{i}); | |
| end | |
| error('ExampleSmokeTests:failed', '%d example(s) failed', failed); | |
| end |