parameters:
  - name: Artifacts
    type: object
    default: []
  - name: ServiceDirectory
    type: string
    default: not-specified
  - name: TestPipeline
    type: boolean
    default: false
  - name: CtestRegex
    type: string
    default: .*
  - name: CtestExcludeRegex
    type: string
    default: ''
  - name: CoverageReportPath
    type: string
    default: 'sdk/*/*/*cov_xml.xml'
  - name: CoverageEnabled
    type: boolean
    default: true
    # Matrix generation:
    # https://github.com/Azure/azure-sdk-tools/blob/main/eng/common/scripts/job-matrix/README.md
  - name: Matrix
    type: string
  - name: LineCoverageTarget
    type: number
    default: 95
  - name: BranchCoverageTarget
    type: number
    default: 70
  - name: TestEnv
    type: object
    default: []    
  # Supplied by archetype-sdk-tests-generate.yml dynamic matrix generator
  - name: CloudConfig
    type: object
    default: {}
  # Supplied by archetype-sdk-tests-generate.yml dynamic matrix generator.
  # Must be wired up to ensure population of parameters.Matrix
  - name: DependsOn
    type: string
    default: ''
  # Supplied by archetype-sdk-tests-generate.yml dynamic matrix generator
  - name: UsePlatformContainer
    type: boolean
    default: false
  - name: PreTestSteps
    type: stepList
    default: []
  - name: PostTestSteps
    type: stepList
    default: []
  - name: DisplayName
    type: string
    default: Validate
  - name: OSName
    type: string
    default: ''

jobs:
  - job: ${{ parameters.DisplayName }}_${{ parameters.OSName }}
    displayName: ${{ parameters.DisplayName }}
    dependsOn: ${{ parameters.DependsOn }}
    condition: and(succeededOrFailed(), ne(variables['Skip.Test'], 'true'), ne(${{ parameters.Matrix }}, '{}'))
    strategy:
      matrix: $[ ${{ parameters.Matrix }} ]
      maxParallel: 12
    pool:
      name: $(Pool)
      # 1es pipeline templates converts `image` to demands: ImageOverride under the hood
      # which is incompatible with image selection in the default non-1es hosted pools
      ${{ if eq(parameters.OSName, 'macOS') }}:
        vmImage: $(OSVmImage)
      ${{ else }}:
        image: $(OSVmImage)
      os: ${{ parameters.OSName }}
    variables:
    - name: CMOCKA_XML_FILE
      value: "%g-test-results.xml"
    - name: CMOCKA_MESSAGE_OUTPUT
      value: "xml"
    - name: BuildArgs
      value: ""
    - name: CmakeEnvArg
      value: ""
    - name: CmakeArgs
      value: ""
    - name: VcpkgArgs
      value: ""
# Apply to all services running public pipeline
    - name: AZURE_TEST_MODE
      value: "PLAYBACK"
    - name: AZURE_LOG_LEVEL
      value: "verbose"
    - name: Codeql.Enabled 
      value: true
    - name: Codeql.BuildIdentifier 
      value: ${{ parameters.ServiceDirectory }}
    - name: Codeql.SkipTaskAutoInjection
      value: false
    - ${{ each testEnvVar in parameters.TestEnv }}:
      - name: ${{ testEnvVar.Name }}
        value: ${{ testEnvVar.Value }}

    steps:
      - template: /eng/common/pipelines/templates/steps/verify-agent-os.yml
        parameters:
          AgentImage: ${{ parameters.OSName }}

      - template: /eng/pipelines/templates/steps/fix-1es-image-apt-azure-sources.yml

      - pwsh: sudo apt update && sudo apt install -y $(AptDependencies)
        retryCountOnTaskFailure: 10
        condition: and(succeeded(), ne(variables['AptDependencies'], ''))
        displayName: Install dependencies from apt

      - pwsh: sudo xcode-select -s /Applications/Xcode_$(XCODE_VERSION).app/Contents/Developer
        condition: >-
          and(
            succeeded(),
            contains(variables['OSVmImage'], 'macOS'),
            ne(variables['XCODE_VERSION'], '')
          )
        displayName: Set Xcode version

      - template: /eng/pipelines/templates/steps/vcpkg.yml

      # Validate all the files are formatted correctly according to the
      # .clang-format file. This step runs on linux only and assumes that
      # clang-format-11 is installed.
      - bash: |
          # Run clang-format recursively on each source and header file within the repo sdk folder.
          echo "Check clang-formatting"
          clang-format --version
          find ./sdk \( -iname '*.hpp' -o -iname '*.cpp' \) ! -iname 'json.hpp' -exec clang-format -i {} \;

          if [[ `git status | grep modified | awk '{print $2}'` ]]; then
            echo Some files were not formatted correctly according to the .clang-format file.
            echo Please run clang-format version 10 or greater to fix the issue by using this bash command at the root of the repo:
            echo "find ./sdk \( -iname '*.hpp' -o -iname '*.cpp' \) ! -iname 'json.hpp' -exec clang-format -i {} \;"
            echo ""
            echo "List of files not formatted correctly:"
            git status | grep modified | awk '{print $2}'
            echo ""
            echo ""
            echo "--- Differences (patch file): ---"
            git diff
            echo "--- (You can apply the diff above locally using the 'git apply --ignore-space-change <patch_file>' command) ---"
            exit 1
          fi

          echo Success, all files are formatted correctly according to the .clang-format file.
          exit 0

        displayName: Validate Clang Format
        condition: and(succeededOrFailed(), eq(variables['CHECK_CLANG_FORMAT'], 1))

      # Validate all the files are saved as ASCII only without a UTF-8 BOM.
      - bash: |
          echo Validate that the files in the repo contain only ASCII characters, saved as UTF-8, without a BOM at the start.

          # Run grep recursive excluding git folder and known expected files and save a file with results.
          grep -I -P -n "[^\x00-\x7F]" -r --exclude-dir ".git" --exclude-dir ".github" --exclude-dir "eng" --exclude-dir "vcpkg_installed" --exclude-dir "_deps" --exclude-dir "vendor" --exclude-dir "nlohmann-json-test" --exclude "grepResults" . > grepResults
          
          # Display results to console.
          cat grepResults
          
          # Each result will produce one line, count how many lines were found.
          files_with_non_ascii=($(wc -l < grepResults))
          
          # Show info about the total files that needs attention.
          echo Files found with non-ASCII characters: $files_with_non_ascii
          
          # Remove the grepResults file.
          rm grepResults
          
          # Return the count. When greater than 0, the step will fail.
          exit $files_with_non_ascii
        
        displayName: Validate Characters are ASCII
        condition: and(succeededOrFailed(), eq(variables['CHECK_ASCII_CHARACTERS'], 1))

      - ${{ each artifact in parameters.Artifacts }}:
        - template: /eng/common/pipelines/templates/steps/set-test-pipeline-version.yml
          parameters:
            PackageName: ${{ artifact.Name }}
            ServiceDirectory: ${{ parameters.ServiceDirectory }}
            TestPipeline: ${{ parameters.TestPipeline }}

      - pwsh: |
          Write-Host "##vso[task.setvariable variable=DOTNET_ROLL_FORWARD]Major"
        displayName: Set DOTNET_ROLL_FORWARD to Major globally

      - template: /eng/pipelines/templates/steps/cmake-build.yml
        parameters:
          ServiceDirectory: ${{ parameters.ServiceDirectory }}
          GenerateArgs: "$(CmakeArgs)"
          BuildArgs: "$(BuildArgs)"
          VcpkgArgs: "$(VcpkgArgs)"
          Env: "$(CmakeEnvArg)"

      - template: /eng/pipelines/templates/steps/show-failure-logs.yml

      - template: /eng/common/testproxy/test-proxy-tool.yml
        parameters:
          runProxy: true
          rootFolder: '$(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}}'
          templateFolder: '$(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}}'
          condition: and(succeeded(), contains(variables.CmakeArgs, 'BUILD_TESTING=ON'),ne('${{parameters.ServiceDirectory}}', 'template'))
        
      - ${{ parameters.PreTestSteps }}
      
      - pwsh: |
          test-proxy restore -a $(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}}
        workingDirectory: '$(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}}'
        displayName: Restore Recordings
        condition: and(succeeded(), contains(variables.CmakeArgs, 'BUILD_TESTING=ON'), ne('${{parameters.ServiceDirectory}}', 'core'), ne('${{parameters.ServiceDirectory}}', 'template'), ne('${{parameters.ServiceDirectory}}', 'appconfiguration'))
        name: RestoreRecordings

      - pwsh: |
          ctest `
            -C Debug `
            -V `
            --tests-regex '${{ parameters.CtestRegex }}' `
            --exclude-regex '${{ parameters.CtestExcludeRegex }}' `
            --no-compress-output `
            -T Test
        workingDirectory: build
        displayName: Test
        
      - ${{ parameters.PostTestSteps }}
            
     
      - pwsh: |
          $ErrorActionPreference = 'SilentlyContinue'
          get-content test-proxy.log
        displayName: TestProxy Log
        condition: and(succeededOrFailed(), contains(variables.CmakeArgs, 'BUILD_TESTING=ON'),ne('${{parameters.ServiceDirectory}}', 'template'))
        workingDirectory: $(Build.SourcesDirectory)/sdk/${{parameters.ServiceDirectory}}
        continueOnError: true

      - task: PublishTestResults@2
        inputs:
          testResultsFormat: cTest
          testResultsFiles: Testing/*/Test.xml
          testRunTitle: $(Agent.JobName)
          searchFolder: build
          mergeTestResults: true
          publishRunAttachments: true
        displayName: Publish test results
        condition: succeededOrFailed()

      - ${{ if eq(parameters.CoverageEnabled, true) }}:
        - pwsh: |
            $toolsDirectory = "$(Agent.TempDirectory)/coveragetools"
            dotnet tool install -g dotnet-reportgenerator-globaltool
            dotnet tool install dotnet-reportgenerator-globaltool --tool-path $toolsDirectory
            Write-Host "##vso[task.setvariable variable=ToolsDirectory]$toolsDirectory"
          displayName: Install coverage tools
          condition: and(succeeded(), eq(variables['CODE_COVERAGE'], 'enabled'))

        # Make coverage targets (specified in coverage_targets.txt) and assemble
        # coverage report
        - bash: |
            make VERBOSE=1 `cat ${{ parameters.ServiceDirectory }}-targets-coverage.txt`
            $(ToolsDirectory)/reportgenerator "-reports:${{ parameters.CoverageReportPath }}" "-targetdir:." "-reporttypes:Cobertura"
          workingDirectory: build
          displayName: Generate Code Coverage Data
          condition: and(succeededOrFailed(), eq(variables['CODE_COVERAGE'], 'enabled'))

        - task: PublishCodeCoverageResults@1
          inputs:
            codeCoverageTool: Cobertura
            summaryFileLocation: '$(Build.SourcesDirectory)/**/Cobertura.xml'
          displayName: Publish Code Coverage to DevOps
          condition: and(succeededOrFailed(), eq(variables['CODE_COVERAGE'], 'enabled'))

        - task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@8
          displayName: Check line coverage
          inputs:
            checkCoverage: true
            coverageFailOption: fixed
            coverageType: line
            # Minimum baseline for line coverage
            coverageThreshold: ${{ parameters.LineCoverageTarget }}
          condition: and(succeededOrFailed(), eq(variables['CODE_COVERAGE'], 'enabled'), eq(variables['Skip.LineCoverageEnforcement'], ''))

        - task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@8
          displayName: Check branch coverage
          inputs:
            checkCoverage: true
            coverageFailOption: fixed
            coverageType: branches
            # Minimum baseline for branch coverage
            coverageThreshold: ${{ parameters.BranchCoverageTarget }}
          condition: and(succeededOrFailed(), eq(variables['CODE_COVERAGE'], 'enabled'), eq(variables['Skip.BranchCoverageEnforcement'], ''))

      - task: Powershell@2
        inputs:
          filePath: $(Build.SourcesDirectory)/eng/scripts/Get-BinarySizes.ps1
          arguments: >-
            -ServiceDirectory ${{parameters.ServiceDirectory}}
            -OsVMImage '$(OSVmImage)'
            -CmakeEnvArg '$(CmakeEnvArg)'
            -BuildArgs '$(BuildArgs)'
            -VcpkgArgs '$(VcpkgArgs)'
            -Job '$(Agent.JobName)'
            -BuildReason '$(Build.Reason)'
            -SourceBranch '$(Build.SourceBranch)'
            -ExtraLabels @{
              MetricVersion = 1;
            }
          pwsh: true
          workingDirectory: $(Pipeline.Workspace)
        displayName: Report Binary Sizes

      # Use the job name to create the artifact name for MAP file publishing.
      # Attempts are also noted starting with 1
      # "Validate windows2022_UWP_debug_x86" -> "windows2022_UWP_debug_x86_attempt_1"
      - pwsh: |
          $artifactName = "$(Agent.JobName)_attempt_$(System.JobAttempt)"
          $parts = $artifactName -split ' '
          if ($parts[1]) {
            $artifactName = $parts[1]
          }
          Write-Host "##vso[task.setvariable variable=MapFileArtifactName;]$artifactName"
        displayName: Set map file artifact name
        condition: eq(variables['PublishMapFiles'], 'true')

      - task: CopyFiles@2
        inputs:
          contents: "**/*.map"
          targetFolder: $(Build.ArtifactStagingDirectory)
          flattenFolders: true
        condition: eq(variables['PublishMapFiles'], 'true')
        displayName: Stage map files

      - template: /eng/common/pipelines/templates/steps/publish-1es-artifact.yml
        parameters:
          ArtifactPath: $(Build.ArtifactStagingDirectory)
          ArtifactName: map-files-$(MapFileArtifactName)
          CustomCondition: eq(variables['PublishMapFiles'], 'true')
          SbomEnabled: false

