name: C/C++ CI

on:
  push:
    branches: [ master, dev ]
  pull_request:
    branches: [ master, dev ]

jobs:
  build:
    name: Build and test (Firebird ${{ matrix.firebird_version }})
    strategy:
      fail-fast: false
      matrix:
        include:
          - firebird_version: "3"
            firebird_image_tag: "3"
            allow_failure: false
          - firebird_version: "4"
            firebird_image_tag: "4"
            allow_failure: false
          - firebird_version: "5"
            firebird_image_tag: "5"
            allow_failure: false
          - firebird_version: "6"
            # Firebird 6 Docker tags are not yet available in the official image.
            # Keep this matrix entry in place and run it against latest for now.
            firebird_image_tag: "latest"
            allow_failure: true
    continue-on-error: ${{ matrix.allow_failure }}

    #runs-on: ubuntu-latest
    runs-on: ubuntu-24.04
    services:
      firebird:
        image: firebirdsql/firebird:${{ matrix.firebird_image_tag }}
        ports:
          - 3050:3050
        env:
          FIREBIRD_USER: SYSDBA
          FIREBIRD_PASSWORD: masterkey
          FIREBIRD_ROOT_PASSWORD: masterkey

    permissions:
      contents: read

    steps:
    - uses: actions/checkout@v4
    - name: apt update
      run: sudo apt-get update
    - name: Install tools and libs
      run: sudo apt-get install build-essential git cmake libnotify-dev libwxgtk3.2-dev xvfb xdotool -y
    - name: Install Firebird 5 client
      run: |
        wget -nv -O Firebird-5.0.4.1812-0-linux-x64.tar.gz \
            "https://github.com/FirebirdSQL/firebird/releases/download/v5.0.4/Firebird-5.0.4.1812-0-linux-x64.tar.gz"
        tar xzf Firebird-5.0.4.1812-0-linux-x64.tar.gz
        # The outer tarball contains a nested buildroot.tar.gz; extract it.
        # Do NOT run install.sh which aborts when it detects the Docker Firebird service running on port 3050.
        tar xzf Firebird-5.0.4.1812-0-linux-x64/buildroot.tar.gz -C Firebird-5.0.4.1812-0-linux-x64
        sudo mkdir -p /opt/firebird/lib /opt/firebird/include
        sudo cp -P Firebird-5.0.4.1812-0-linux-x64/opt/firebird/lib/libfbclient.so* /opt/firebird/lib/
        sudo cp -r Firebird-5.0.4.1812-0-linux-x64/opt/firebird/include/. /opt/firebird/include/
        echo "/opt/firebird/lib" | sudo tee /etc/ld.so.conf.d/firebird.conf
        sudo ldconfig
    - name: CMake
      run: |
          mkdir build
          cd build
          cmake .. -DCMAKE_EXE_LINKER_FLAGS="-L/opt/firebird/lib"
    - name: Build
      run: make
      working-directory: ./build
    - name: Wait for Firebird service
      run: |
        for i in {1..60}; do
          if (echo > /dev/tcp/127.0.0.1/3050) >/dev/null 2>&1; then
            exit 0
          fi
          sleep 1
        done
        echo "Firebird service did not start in time."
        exit 1
    - name: Test
      env:
        IBPP_TEST_SERVER: localhost/3050
        LD_LIBRARY_PATH: /opt/firebird/lib
      run: ctest --output-on-failure --output-junit ctest-results.xml
      working-directory: ./build
    - name: Desktop smoke test (issue #436 - flamerobin starts without crash)
      # Run flamerobin under a virtual X display. The process is killed after
      # a few seconds via timeout; exit code 124 (SIGTERM from timeout/SIGALRM)
      # and 143 (128+15, SIGTERM forwarded by timeout on some systems) are both
      # expected. Any other non-zero code indicates a crash on startup.
      env:
        LD_LIBRARY_PATH: /opt/firebird/lib
      run: |
        EXIT=0
        xvfb-run --auto-servernum timeout 5 ./flamerobin || EXIT=$?
        if [ "$EXIT" -ne 0 ] && [ "$EXIT" -ne 124 ] && [ "$EXIT" -ne 143 ]; then
          echo "flamerobin exited unexpectedly (code $EXIT) - possible startup crash"
          exit 1
        fi
        echo "Desktop smoke test passed (exit code $EXIT)"
      working-directory: ./build
    - name: UI automation test (issue #436 - trigger drop does not crash)
      # Start flamerobin under a virtual display, wait for the window to
      # appear, then terminate it with SIGTERM.  xdotool is used to detect
      # the window.  The test verifies that the application starts and shows a
      # window, catching regressions that crash on startup.
      # Xvfb has no window manager so Alt+F4 is never dispatched; SIGTERM is
      # used instead to shut the application down cleanly.
      # xvfb-run --auto-servernum is used so a fresh display is always chosen,
      # avoiding conflicts with any display left active by the preceding smoke
      # test step.
      env:
        LD_LIBRARY_PATH: /opt/firebird/lib
      run: |
        xvfb-run --auto-servernum bash -c '
          ./flamerobin &
          APP_PID=$!

          # Wait up to 30 s for the flamerobin window to appear.
          # Try matching by window name first, then by WM_CLASS, so the test
          # is not sensitive to the exact title string ("FlameRobin - …" etc.)
          # or to the window not yet being mapped as "visible".
          FOUND=0
          WID=""
          for i in $(seq 1 30); do
            WID="$(xdotool search --name "FlameRobin" 2>/dev/null | head -n1 || true)"
            if [ -n "$WID" ]; then
              FOUND=1
              break
            fi

            WID="$(xdotool search --class "flamerobin" 2>/dev/null | head -n1 || true)"
            if [ -n "$WID" ]; then
              FOUND=1
              break
            fi

            sleep 1
          done

          if [ "$FOUND" -ne 1 ]; then
            echo "UI test: flamerobin window not found within 30 s"
            echo "--- xdotool search --name . (debug) ---"
            xdotool search --name "." 2>/dev/null | head -n 50 || true
            kill "$APP_PID" 2>/dev/null || true
            exit 1
          fi

          echo "UI test: window id $WID found"

          # Xvfb has no window manager, so Alt+F4 is never dispatched.
          # Shut the application down with SIGTERM instead.
          kill -TERM "$APP_PID" 2>/dev/null || true

          # Wait up to 10 s for the process to exit cleanly.
          for i in $(seq 1 10); do
            if ! kill -0 "$APP_PID" 2>/dev/null; then
              break
            fi
            sleep 1
          done

          # Force-kill if it is still running, then reap it.
          kill -9 "$APP_PID" 2>/dev/null || true
          wait "$APP_PID" 2>/dev/null || true
          APP_EXIT=$?

          # Any exit code is acceptable here (graceful close or SIGTERM).
          echo "UI automation test passed (flamerobin exit code $APP_EXIT)"
        '
      working-directory: ./build
    - name: Publish test report
      # Skip on fork pull requests: the read-only token cannot create check
      # runs, which would produce noisy "Resource not accessible by
      # integration" errors without affecting the actual test outcome.
      if: always() && github.event_name != 'pull_request'
      uses: mikepenz/action-junit-report@v5
      with:
        report_paths: ./build/ctest-results.xml
        check_name: Google Test Results
