name: Run XUnit tests

on:
  workflow_dispatch:
    inputs:
      soak_test:
        required: false
        type: boolean
        default: false
  workflow_call:
    inputs:
      soak_test:
        required: false
        type: boolean
        default: false

## In the matrix:
##  os - name of the Github actions runner
##  suffix - name used in the description of the job
##  z3 - name of the Z3 asset at the 'z3BaseUri' URL
##  chmod - whether the Z3 download needs execute permissions added

defaults:
  run:
    working-directory: dafny

jobs:
  populate-matrix-dimensions:
    runs-on: ubuntu-latest
    steps:
      - name: Populate iterations (normal mode)
        id: populate-iterations-normal
        if: "!inputs.soak_test"
        working-directory: .
        run: echo "iterations=[1]" >> $GITHUB_OUTPUT
      - name: Populate iterations (soak test mode)
        id: populate-iterations-soak
        if: inputs.soak_test
        working-directory: .
        run: echo "iterations=[`seq -s , 1 5`]" >> $GITHUB_OUTPUT
    outputs:
      iterations: ${{ steps.populate-iterations-normal.outputs.iterations }} ${{ steps.populate-iterations-soak.outputs.iterations }}
  
  build:
    needs: populate-matrix-dimensions
    runs-on: ${{matrix.os}}
    timeout-minutes: 60
    name: ${{matrix.suffix}} (${{matrix.iteration}})
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-20.04, windows-2019, macos-13]
        iteration: ${{ fromJson(needs.populate-matrix-dimensions.outputs.iterations) }}
        include:
          - os: macos-13
            suffix: osx
            chmod: true
          - os: windows-2019
            suffix: win
            chmod: false
          - os: ubuntu-20.04
            suffix: ubuntu-20.04
            chmod: true
    env:
      solutionPath: Source/Dafny.sln
      z3BaseUri: https://github.com/dafny-lang/solver-builds/releases/download/snapshot-2024-04-10
      Logging__LogLevel__Microsoft: Debug
    steps:
    - uses: actions/checkout@v4
      with:
        path: dafny
        submodules: recursive
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: 6.0.x
    - name: Install dependencies
      run: |
        dotnet restore ${{env.solutionPath}}
        dotnet tool install dotnet-coverage
    - name: Load Z3
      shell: pwsh
      run: |
        Invoke-WebRequest ${{env.z3BaseUri}}/z3-4.12.1-x64-${{matrix.os}}-bin.zip -OutFile z3.zip
        Expand-Archive z3.zip .
        Remove-Item z3.zip
    - name: Move Z3
      run: |
        mkdir -p Binaries/z3/bin
        mv z3-* Binaries/z3/bin/
    - name: Set Z3 Permissions
      if: ${{matrix.chmod}}
      run: |
        chmod +x Binaries/z3/bin/z3*
    - name: Build
      run: dotnet build --no-restore ${{env.solutionPath}}
    - name: Run DafnyCore Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyCore.Test/coverlet.runsettings Source/DafnyCore.Test
    - name: Run DafnyLanguageServer Tests (soak test - iteration ${{matrix.iteration}})
      if: inputs.soak_test
      run: |
        dotnet test --no-restore --blame-crash --blame-hang-timeout 360s --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyLanguageServer.Test/coverlet.runsettings Source/DafnyLanguageServer.Test
    - name: Run DafnyLanguageServer Tests
      if: "!inputs.soak_test"
      run: |
        ##  Note that, for some mysterious reason, --collect doesn't work with the DafnyLanguageServer.Test package
        dotnet coverage collect -o DafnyLanguageServer.Test.coverage dotnet test --no-restore --blame-hang-timeout 360s --logger "console;verbosity=normal" --logger trx Source/DafnyLanguageServer.Test
    - name: Run DafnyDriver Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyDriver.Test/coverlet.runsettings Source/DafnyDriver.Test
    - name: Run DafnyPipeline Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyPipeline.Test/coverlet.runsettings Source/DafnyPipeline.Test
    - name: Run DafnyTestGeneration Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --blame-hang-timeout 5m --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyTestGeneration.Test/coverlet.runsettings Source/DafnyTestGeneration.Test
    - name: Run AutoExtern Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/AutoExtern.Test/coverlet.runsettings Source/AutoExtern.Test
    - name: Run DafnyRuntime Tests
      if: "!inputs.soak_test"
      run: dotnet test --no-restore --logger "console;verbosity=normal" --logger trx --collect:"XPlat Code Coverage" --settings Source/DafnyRuntime.Tests/coverlet.runsettings Source/DafnyRuntime.Tests
    - uses: actions/upload-artifact@v4
      if: always() && !inputs.soak_test
      with:
        name: unit-test-results-${{ matrix.os }}
        path: |
          dafny/Source/*/TestResults/*.trx
          dafny/Source/*/TestResults/*/coverage.cobertura.xml
          dafny/DafnyLanguageServer.Test.coverage
