name: Core

on:
  pull_request:
    types:
      - opened
      - reopened
      - synchronize  # branch head update
      - labeled
  workflow_dispatch:
  schedule:
    - cron: '15 23 * * *'  # every day @ 23:15
  push:
    branches:
      - 'release/**'

# cancel any previous runs on the same PR
concurrency:
  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

permissions:
  id-token: write       # for fetching the OIDC token
  contents: read        # for actions/checkout
  pull-requests: write  # for dflook comments on PR

env:
  SDL_VIDEODRIVER: dummy
  PULL_COMMENT_PATH: ${{ github.workspace }}/tests/core-pull-comment.md
  TREZOR_PYTEST_LOGS_DIR: ${{ github.workspace }}/tests/

jobs:
  param:
    name: Determine pipeline parameters
    runs-on: ubuntu-latest
    outputs:
      test_lang: ${{ steps.set_vars.outputs.test_lang }}
      asan: ${{ steps.set_vars.outputs.asan }}
    steps:
      - id: set_vars
        name: Set variables
        run: |
          echo test_lang=${{ (github.event_name == 'schedule' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'translations'))) && '[\"en\", \"cs\", \"fr\", \"de\", \"es\", \"pt\"]' || '[\"en\"]' }} >> $GITHUB_OUTPUT
          # TODO: fix ASAN tests on nightly CI runs (#5143)
          echo asan=${{ '[\"noasan\"]' }} >> $GITHUB_OUTPUT
          cat $GITHUB_OUTPUT

  core_firmware:
    name: Build firmware (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        model: ${{ fromJSON(startsWith(github.ref, 'refs/heads/release/') && '["T2B1", "T2T1", "T3B1", "T3T1", "T3W1"]' || '["T2T1", "T3B1", "T3T1", "T3W1"]') }}
        coins: [universal, btconly]
        type: ${{ fromJSON(github.event_name == 'schedule' && '["normal", "debuglink", "production"]' || '["normal", "debuglink"]') }}
        include:
          - model: D001
            coins: universal
            type: normal
          - model: T2B1
            coins: universal
            type: normal
        exclude:
          - model: T3W1
            type: production
    env:
      TREZOR_MODEL: ${{ matrix.model }}
      BITCOIN_ONLY: ${{ matrix.coins == 'universal' && '0' || '1' }}
      PYOPT: ${{ matrix.type == 'debuglink' && '0' || '1' }}
      PRODUCTION: ${{ matrix.type == 'production' && '1' || '0' }}
      BOOTLOADER_DEVEL: ${{ matrix.model == 'T3W1' && '1' || '0' }}
      QUIET_MODE: 1
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_boardloader"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink'
      - run: nix-shell --run "uv run make -C core build_bootloader"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink'
      - run: nix-shell --run "uv run make -C core build_bootloader_ci"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink' && matrix.model == 'T2T1'
      - run: nix-shell --run "uv run make -C core build_prodtest"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink'
      - run: nix-shell --run "uv run make -C core build_prodtest_emu"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink' && matrix.model == 'T3W1'
      - run: nix-shell --run "uv run make -C core build_firmware"
      - run: nix-shell --run "uv run ./tools/print-rust-stack-sizes.py | sort -k1 -n | tail -n 50"
      - run: nix-shell --run "uv run ./tools/print-rust-type-sizes.sh core/build/firmware/rust-type-sizes.log" | awk '$1 >= 1000' | sort -k1 -n
      - run: nix-shell --run "uv run make -C core sizecheck"
        if: matrix.coins == 'universal' && matrix.type != 'debuglink'
      - run: nix-shell --run "uv run ./tools/check-bitcoin-only core/build/firmware/firmware.bin"
        if: matrix.coins == 'btconly' && matrix.type != 'debuglink'
      - uses: actions/upload-artifact@v4
        with:
          name: core-firmware-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}
          path: |
            core/build/boardloader/*.bin
            core/build/bootloader/*.bin
            core/build/bootloader_ci/*.bin
            core/build/prodtest/*.bin
            core/build/prodtest_emu/*.bin
            core/build/firmware/firmware.elf
            core/build/firmware/firmware-*.bin
            core/build/firmware/rust-type-sizes.log
          retention-days: 7

  core_emu:
    name: Build emu (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.type }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs: param
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        coins: [universal, btconly]
        # type: [normal, debuglink]
        type: ${{ fromJSON(github.event_name == 'schedule' && '["normal", "debuglink"]' || '["debuglink"]') }}
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
        exclude:
          - type: normal
            asan: asan
    env:
      TREZOR_MODEL: ${{ matrix.model }}
      BITCOIN_ONLY: ${{ matrix.coins == 'universal' && '0' || '1' }}
      PYOPT: ${{ matrix.type == 'debuglink' && '0' || '1' }}
      ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
      LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt"
      QUIET_MODE: 1
      DISABLE_TROPIC: 0
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_bootloader_emu"
        if: matrix.coins == 'universal' && matrix.asan == 'noasan'
      - run: nix-shell --run "uv run make -C core build_unix_frozen"
      - run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C core test_emu_sanity"
      - run: cp core/build/unix/trezor-emu-core core/build/unix/trezor-emu-core-${{ matrix.model }}-${{ matrix.coins }}
      - uses: actions/upload-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}-${{ matrix.asan }}
          path: |
            core/build/unix/trezor-emu-core*
            core/build/bootloader_emu/bootloader.elf
          retention-days: 7

  core_emu_arm:
    if: github.event_name == 'schedule'
    name: Build emu arm
    runs-on: ubuntu-latest-arm64
    needs: param
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        coins: [universal]
        type: [debuglink]
        asan: [noasan]
        exclude:
          - type: normal
            asan: asan
    env:
      TREZOR_MODEL: ${{ matrix.model }}
      BITCOIN_ONLY: ${{ matrix.coins == 'universal' && '0' || '1' }}
      PYOPT: ${{ matrix.type == 'debuglink' && '0' || '1' }}
      ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
      LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt"
      QUIET_MODE: 1
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_bootloader_emu"
        if: matrix.coins == 'universal'
      - run: nix-shell --run "uv run make -C core build_unix_frozen"
      - run: mv core/build/unix/trezor-emu-core core/build/unix/trezor-emu-arm-core-${{ matrix.model }}-${{ matrix.coins }}
      - uses: actions/upload-artifact@v4
        with:
          name: core-emu-arm-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.type }}-${{ matrix.asan }}
          path: |
            core/build/unix/trezor-emu-*
            core/build/bootloader_emu/bootloader.elf
          retention-days: 2

  core_unit_python_test:
    name: Python unit tests (${{ matrix.model }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs: param
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_MODEL: ${{ matrix.model }}
      ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
      LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt"
      DISABLE_TROPIC: 0
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_unix"
      - run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C core test"
      - run: nix-shell --run "uv run make -C core test_emu_sanity"  # sanity check non-frozen emulator

  core_unit_rust_test:
    name: Rust unit tests (${{ matrix.model }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_MODEL: ${{ matrix.model }}
      ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
      RUSTC_BOOTSTRAP: ${{ matrix.asan == 'asan' && '1' || '0' }}
      RUSTFLAGS: ${{ matrix.asan == 'asan' && '-Z sanitizer=address' || '' }}
      LSAN_OPTIONS: "suppressions=../../asan_suppressions.txt"
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_unix"
      # Ensure that "cargo build" works when NOT executed through our makefiles,
      # indicating that it does not rely on particular envvars or other flags.
      # This makes sure that rust-analyzer will work on our codebase.
      - run: nix-shell --run "cd core/embed/rust && cargo build"
        if: matrix.asan == 'noasan'
      - run: nix-shell --run "uv run make -C core clippy"
      - run: nix-shell --run "uv run make -C core test_rust"

  core_rust_client_test:
    name: Rust trezor-client tests
    runs-on: ubuntu-latest
    needs: core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1]
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-noasan
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run core/emu.py --headless -q --temporary-profile --slip0014 --command cargo test --manifest-path rust/trezor-client/Cargo.toml"

  # Device tests for Core. Running device tests and also comparing screens
  # with the expected UI result.
  # See artifacts for a comprehensive report of UI.
  # See [docs/tests/ui-tests](../tests/ui-tests.md) for more info.
  core_device_test:
    name: Device tests (${{ matrix.model }}, ${{ matrix.coins }}, ${{ matrix.asan }}, ${{ matrix.lang }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        coins: [universal, btconly]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
        lang: ${{ fromJSON(needs.param.outputs.test_lang) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      TREZOR_MODEL: ${{ matrix.model }}
      ADDRESS_SANITIZER: ${{ matrix.asan == 'asan' && '1' || '0' }}
      PYTEST_TIMEOUT: ${{ matrix.asan == 'asan' && 600 || 400 }}
      ACTIONS_DO_UI_TEST: ${{ matrix.coins == 'universal' && matrix.asan == 'noasan' }}
      TEST_LANG: ${{ matrix.lang }}
      TESTOPTS: "--durations 10 --session-timeout 1800"  # 30m pytest global timeout
    timeout-minutes: 40
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-${{ matrix.coins }}-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - name: Start Tropic model
        if: ${{ env.TREZOR_MODEL == 'T3W1' && env.ACTIONS_DO_UI_TEST != 'true' }}  # ACTIONS_DO_UI_TEST refers to the test_emu_ui_multicore below which uses --control-emulators and starts tvl internally
        run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C core ${{ env.ACTIONS_DO_UI_TEST == 'true' && 'test_emu_ui_multicore' || 'test_emu' }}"
      - run: tail -v -n50 tests/trezor*.log || true
        if: failure()
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-device-${{ matrix.model }}-${{ matrix.coins }}-${{ matrix.lang }}-${{ matrix.asan }}
          path: tests/trezor*.log
          retention-days: 7
        if: always()
      - uses: ./.github/actions/ui-report
        with:
          model: ${{ matrix.model }}
          lang: ${{ matrix.lang }}
          status: ${{ job.status }}
        if: ${{ always() && env.ACTIONS_DO_UI_TEST == 'true' }}
        continue-on-error: true
      - uses: ./.github/actions/upload-coverage

  # Click tests - UI.
  # See [docs/tests/click-tests](../tests/click-tests.md) for more info.
  core_click_test:
    name: Click tests (${{ matrix.model }}, ${{ matrix.asan }}, ${{ matrix.lang }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    timeout-minutes: 40
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
        lang: ${{ fromJSON(needs.param.outputs.test_lang) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      # MULTICORE: 4  # more could interfere with other jobs
      PYTEST_TIMEOUT: 400
      TEST_LANG: ${{ matrix.lang }}
      TESTOPTS: "--durations 10"
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core test_emu_click_ui_multicore"
        if: ${{ matrix.asan == 'noasan' }}
      - run: nix-shell --run "uv run make -C core test_emu_click"
        if: ${{ matrix.asan == 'asan' }}
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-click-${{ matrix.model }}-${{ matrix.lang }}-${{ matrix.asan }}
          path: tests/trezor*.log
          retention-days: 7
        if: always()
      - uses: ./.github/actions/ui-report
        with:
          model: ${{ matrix.model }}
          lang: ${{ matrix.lang }}
          status: ${{ job.status }}
        if: always()
        continue-on-error: true
      - uses: ./.github/actions/upload-coverage

  # Upgrade tests.
  # See [docs/tests/upgrade-tests](../tests/upgrade-tests.md) for more info.
  core_upgrade_test:
    name: Upgrade tests (${{ matrix.model }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        # FIXME: T3B1 https://github.com/trezor/trezor-firmware/issues/2724
        # FIXME: T3T1 https://github.com/trezor/trezor-firmware/issues/3595
        model: [T2T1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_UPGRADE_TEST: core
      PYTEST_TIMEOUT: 20
      TESTOPTS: "--durations 10"
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - run: nix-shell --run "tests/download_emulators.sh ${{ matrix.model }}"
      - run: nix-shell --run "uv run pytest tests/upgrade_tests"
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-upgrade-${{ matrix.model }}-${{ matrix.asan }}
          path: |
            tests/trezor*.log
          retention-days: 7
        if: always()

  # Persistence tests - UI.
  core_persistence_test:
    name: Persistence tests (${{ matrix.model }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      PYTEST_TIMEOUT: 400
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - name: Start Tropic model
        if: ${{ matrix.model == 'T3W1' }}
        run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C core test_emu_persistence_ui"
        if: ${{ matrix.asan == 'noasan' }}
      - run: nix-shell --run "uv run make -C core test_emu_persistence"
        if: ${{ matrix.asan == 'asan' }}
      - uses: ./.github/actions/ui-report
        with:
          model: ${{ matrix.model }}
          lang: en
          status: ${{ job.status }}
        if: always()
        continue-on-error: true
      - uses: ./.github/actions/upload-coverage
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-persistence-${{ matrix.model }}-${{ matrix.asan }}
          path: |
            tests/trezor*.log
          retention-days: 7
        if: always()

  core_hwi_test:
    name: HWI tests
    if: false  # XXX currently failing
    continue-on-error: true
    runs-on: ubuntu-latest
    needs: core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-noasan
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment  # XXX poetry maybe not needed
      - run: nix-shell --run "git clone --depth=1 https://github.com/bitcoin-core/HWI.git"
      - run: nix-shell --arg fullDeps true --run "cd HWI && poetry install && poetry run ./test/test_trezor.py --model_t ../core/build/unix/trezor-emu-core bitcoind"
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-hwi-${{ matrix.model }}
          path: HWI/trezor-t-emulator.stdout
          retention-days: 7

  core_memory_profile:
    name: Memory allocation report
    if: false  # NOTE manual job, comment out to run
    runs-on: ubuntu-latest
    env:
      TREZOR_MODEL: T2T1
      TREZOR_MEMPERF: 1
      PYOPT: 0
      PYTEST_TIMEOUT: 900
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core build_unix_frozen"
      - run: nix-shell --run "uv run make -C core test_emu"
      - run: nix-shell --run "mkdir core/prof/memperf-html"
      - run: nix-shell --run "uv run core/tools/alloc.py --alloc-data=core/src/alloc_data.txt html core/prof/memperf-html"
      - uses: actions/upload-artifact@v4
        with:
          name: core-memperf-${{ matrix.model }}
          path: |
            tests/trezor*.log
            core/prof/memperf-html
          retention-days: 7
        if: always()

  # Flash size profiling

  # Finds out how much flash space we have left in the firmware build
  # Fails if the free space is less than certain threshold
  core_flash_size_check:
    name: Flash size check
    runs-on: ubuntu-latest
    needs: core_firmware
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1]  # FIXME: checker.py lacks awareness of U5 flash layout
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-firmware-${{ matrix.model }}-universal-normal  # FIXME: s/normal/debuglink/
          path: core/build
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run core/tools/size/checker.py core/build/firmware/firmware.elf"

  # Monero tests.
  core_monero_test:
    name: Monero test (${{ matrix.model }}, ${{ matrix.asan }})
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      PYTEST_TIMEOUT: 400
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
        with:
          full-deps: true
      - run: nix-shell --arg fullDeps true --run "uv run make -C core test_emu_monero"
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-monero-${{ matrix.model }}-${{ matrix.asan }}
          path: |
            tests/trezor*.log
            core/tests/trezor_monero_tests.log
          retention-days: 7
        if: always()
      - uses: ./.github/actions/upload-coverage


  # Tests for U2F and HID.
  core_u2f_test:
    name: U2F test (${{ matrix.model }}, ${{ matrix.asan }}
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      PYTEST_TIMEOUT: 400
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - name: Start Tropic model
        if: ${{ matrix.model == 'T3W1' }}
        run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C tests/fido_tests/u2f-tests-hid"
      - run: nix-shell --run "uv run make -C core test_emu_u2f"
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-u2f-${{ matrix.model }}-${{ matrix.asan }}
          path: tests/trezor*.log
          retention-days: 7
        if: always()
      - uses: ./.github/actions/upload-coverage

  # FIDO2 device tests.
  core_fido2_test:
    name: FIDO2 test (${{ matrix.model }}, ${{ matrix.asan }}
    runs-on: ubuntu-latest
    needs:
      - param
      - core_emu
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3T1, T3W1]  # XXX T3B1 https://github.com/trezor/trezor-firmware/issues/2724
        asan: ${{ fromJSON(needs.param.outputs.asan) }}
    env:
      TREZOR_PROFILING: ${{ matrix.asan == 'noasan' && '1' || '0' }}
      PYTEST_TIMEOUT: 400
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          name: core-emu-${{ matrix.model }}-universal-debuglink-${{ matrix.asan }}
          path: core/build
      - run: chmod +x core/build/unix/trezor-emu-core*
      - uses: ./.github/actions/environment
      - name: Start Tropic model
        if: ${{ matrix.model == 'T3W1' }}
        run: nix-shell --arg fullDeps true --run "cd vendor/ts-tvl && poetry env use 3.12 && poetry install && poetry run model_server tcp -c ../../tests/tropic_model/config.yml > ../../tests/trezor-tropic-model.log 2>&1 &"
      - run: nix-shell --run "uv run make -C core test_emu_fido2"
      - uses: actions/upload-artifact@v4
        with:
          name: core-test-fido2-${{ matrix.model }}-${{ matrix.asan }}
          path: |
            tests/trezor*.log
          retention-days: 7
        if: always()
      - uses: ./.github/actions/upload-coverage

  core_coverage_report:
    name: Coverage report
    runs-on: ubuntu-latest
    needs:
      - core_click_test
      - core_persistence_test
      - core_device_test
      - core_monero_test
      - core_u2f_test
      - core_fido2_test
    strategy:
      fail-fast: false
      matrix:
        model: [T2T1, T3B1, T3T1, T3W1]
    env:
      # TODO: https://github.com/trezor/trezor-firmware/issues/5957
      COVERAGE_THRESHOLD: ${{ matrix.model == 'T3B1' && 84 || 85 }}
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive
      - uses: actions/download-artifact@v4
        with:
          pattern: core-coverage-${{ matrix.model }}-*
          path: core
          merge-multiple: true
      - uses: ./.github/actions/environment
      - run: nix-shell --run "uv run make -C core coverage"
      - uses: actions/upload-artifact@v4
        with:
          name: core-coverage-report-${{ matrix.model }}
          path: core/htmlcov
          retention-days: 7
          include-hidden-files: true
        if: always()

  core_ui_comment:
    name: Post comment with UI diff URLs
    # skip UI comment job on external PRs (see #5381)
    if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    needs:
      - param
    steps:
      - uses: actions/checkout@v4
      - run: sleep 1m  # try avoiding github api rate limit
      - run: |
          python ci/make_pull_comment.py "${{ github.run_id }}" '${{ needs.param.outputs.test_lang }}' > ${{ env.PULL_COMMENT_PATH }}
          cat ${{ env.PULL_COMMENT_PATH }} >> $GITHUB_STEP_SUMMARY
      - uses: ./.github/actions/ui-comment
        if: github.event_name == 'pull_request'
      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::538326561891:role/gh_actions_deploy_dev_firmware_data
          aws-region: eu-west-1

      - run: python ci/make_summary_htmls.py "index" '${{ needs.param.outputs.test_lang }}' "tests/ui_tests/reporting/"
      - run: python ci/make_summary_htmls.py "diff" '${{ needs.param.outputs.test_lang }}' "tests/ui_tests/reporting/"
      - name: Upload per-language HTML summaries to S3
        run: |
          cd tests/ui_tests/reporting/
          for F in *.html
          do
            aws s3 cp $F s3://data.trezor.io/dev/firmware/ui_report/${{ github.run_id }}/$F
          done

  core_upload_emu:
    name: Upload emulator binaries
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    needs:
      - core_emu
      - core_emu_arm
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: core-emu*debuglink-noasan
          merge-multiple: true
      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::538326561891:role/gh_actions_deploy_dev_firmware_data
          aws-region: eu-west-1
        continue-on-error: true
      - run: |
          rm unix/trezor-emu-core
          aws s3 sync --only-show-errors unix s3://data.trezor.io/dev/firmware/emu-nightly

  core_upload_emu_branch:
    name: Upload emulator binaries for the current branch
    # Not building it for nightly CI
    if: github.event_name != 'schedule'
    runs-on: ubuntu-latest
    needs:
      # Do not include ARM, they are only built on nightly
      - core_emu
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: core-emu*debuglink-noasan
          merge-multiple: true
      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::538326561891:role/gh_actions_deploy_dev_firmware_data
          aws-region: eu-west-1
        continue-on-error: true
      - name: Determine branch name
        run: |
          if [ "${{ github.event_name }}" == "pull_request" ]; then
            branch_name=${{ github.head_ref }}
          else
            branch_name=${{ github.ref_name }}
          fi
          echo "branch_name=$branch_name" >> $GITHUB_ENV
      - name: Upload artifacts to branch directory
        run: |
          rm unix/trezor-emu-core
          aws s3 sync --only-show-errors unix s3://data.trezor.io/dev/firmware/emu-branches/$branch_name

  # Connect
  # TODO: core_connect_test
