diff --git a/azure-pipelines/pull-request-validation/compare_coverage.py b/azure-pipelines/pull-request-validation/compare_coverage.py new file mode 100644 index 0000000000..8d8d7349e2 --- /dev/null +++ b/azure-pipelines/pull-request-validation/compare_coverage.py @@ -0,0 +1,22 @@ +import sys +import xml.etree.ElementTree as ET + +def get_coverage(xml_path): + tree = ET.parse(xml_path) + root = tree.getroot() + counter = root.find(".//counter[@type='INSTRUCTION']") + covered = int(counter.attrib['covered']) + missed = int(counter.attrib['missed']) + return covered / (covered + missed) + +pr_cov = get_coverage(sys.argv[1]) +dev_cov = get_coverage(sys.argv[2]) + +print(f"PR branch coverage: {pr_cov:.2%}") +print(f"Dev branch coverage: {dev_cov:.2%}") + +if pr_cov < dev_cov: + print("ERROR: PR branch coverage is lower than dev branch. Failing...") + sys.exit(1) +else: + print("SUCCESS: PR branch coverage is not lower than dev branch, this is acceptable!") diff --git a/azure-pipelines/pull-request-validation/pr-msal.yml b/azure-pipelines/pull-request-validation/pr-msal.yml index b895098448..f2701f904f 100644 --- a/azure-pipelines/pull-request-validation/pr-msal.yml +++ b/azure-pipelines/pull-request-validation/pr-msal.yml @@ -24,83 +24,168 @@ resources: name: AzureAD/microsoft-authentication-library-common-for-android ref: dev endpoint: ANDROID_GITHUB + - repository: msal-dev + type: github + name: AzureAD/microsoft-authentication-library-for-android + ref: dev + endpoint: ANDROID_GITHUB pool: name: MSSecurity-1ES-Build-Agents-Pool image: MSSecurity-1ES-Windows-2022 os: windows -jobs: -- job: build_test - displayName: Build & Test - cancelTimeoutInMinutes: 1 - variables: - Codeql.Enabled: true - steps: - - checkout: self - clean: true - submodules: recursive - persistCredentials: True - - bash: | - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" - displayName: 'Set VSTS Fields in Environment' - - template: azure-pipelines/templates/steps/automation-cert.yml@common - - task: JavaToolInstaller@0 - displayName: Use Java 17 - inputs: - versionSpec: '17' - jdkArchitectureOption: x64 - jdkSourceOption: PreInstalled - - task: CodeQL3000Init@0 - - task: Gradle@2 - name: Gradle1 - displayName: Assemble Local - inputs: - tasks: clean msal:assembleLocal - publishJUnitResults: false - testResultsFiles: '**/build/test-results/TEST-*.xml' - jdkVersion: $(BuildParameters.jdkVersion) - jdkArchitecture: $(BuildParameters.jdkArchitecture) - sqGradlePluginVersion: 2.0.1 - - task: CodeQL3000Finalize@0 - - task: Gradle@2 - displayName: Run Unit tests - inputs: - tasks: msal:testLocalDebugUnitTest -Plabtest -PlabSecret=$(LabVaultAppCert) -ProbolectricSdkVersion=${{variables.robolectricSdkVersion}} -PmockApiUrl=$(MOCK_API_URL) -PnativeAuthConfigString=$(NATIVE_AUTH_CONFIG_STRING) - javaHomeSelection: $(BuildParameters.javaHomeSelection) - jdkVersion: 1.17 -- job: spotbugs - displayName: SpotBugs - cancelTimeoutInMinutes: 1 - steps: - - checkout: self - clean: true - submodules: recursive - persistCredentials: True - - bash: | - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" - displayName: 'Set VSTS Fields in Environment' - - template: azure-pipelines/templates/steps/spotbugs.yml@common - parameters: - project: msal -- job: lint - displayName: Lint - cancelTimeoutInMinutes: 1 - steps: - - checkout: self - clean: true - submodules: recursive - persistCredentials: True - - bash: | - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" - echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" - displayName: 'Set VSTS Fields in Environment' - - task: Gradle@3 - displayName: Lint Local debug - inputs: - tasks: clean msal:lintLocalDebug - publishJUnitResults: false - jdkVersion: 1.17 - +stages: + - stage: build_and_test + displayName: Build and Test + jobs: + - job: build_test + displayName: Build & Test (PR Branch) + cancelTimeoutInMinutes: 1 + variables: + Codeql.Enabled: true + steps: + - checkout: self + clean: true + submodules: recursive + persistCredentials: True + - bash: | + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" + displayName: 'Set VSTS Fields in Environment' + - template: azure-pipelines/templates/steps/automation-cert.yml@common + - task: JavaToolInstaller@0 + displayName: Use Java 17 + inputs: + versionSpec: '17' + jdkArchitectureOption: x64 + jdkSourceOption: PreInstalled + - task: CodeQL3000Init@0 + - task: Gradle@2 + name: Gradle1 + displayName: Assemble Local + inputs: + tasks: clean msal:assembleLocal + publishJUnitResults: false + testResultsFiles: '**/build/test-results/TEST-*.xml' + jdkVersion: $(BuildParameters.jdkVersion) + jdkArchitecture: $(BuildParameters.jdkArchitecture) + sqGradlePluginVersion: 2.0.1 + - task: CodeQL3000Finalize@0 + - task: Gradle@2 + displayName: Run Tests + inputs: + tasks: msal:jacocoTestReport -PcodeCoverageEnabled=true -ProbolectricSdkVersion=${{variables.robolectricSdkVersion}} -PmockApiUrl=$(MOCK_API_URL) -PnativeAuthConfigString=$(NATIVE_AUTH_CONFIG_STRING) + javaHomeSelection: $(BuildParameters.javaHomeSelection) + jdkVersion: 1.17 + - script: tree "$(Build.SourcesDirectory)\msal" /F /A + displayName: 'Print File Structure Tree' + - script: tree "$(Build.SourcesDirectory)\msal\build\intermediates\javac\localDebug\compileLocalDebugJavaWithJavac\classes" /F /A + displayName: 'Print File Structure Tree (build/intermediates/javac/localDebug/compileLocalDebugJavaWithJavac/classes)' + - publish: $(Build.SourcesDirectory)/msal/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml + artifact: jacocoReport + displayName: 'Publish JaCoCo Report Artifact (PR Branch)' + - task: PublishCodeCoverageResults@1 + displayName: Publish Code Coverage Results + condition: always() + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: '$(Build.SourcesDirectory)/msal/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml' + reportDirectory: '$(Build.SourcesDirectory)/msal/build/reports/jacoco/jacocoTestReport/html' + failIfCoverageEmpty: false +# This needs to be commented for now, because dev doesn't have the jacocoTestReport task yet, will uncomment in the next PR. +# - job: build_test_dev +# displayName: Build & Test (Dev) +# cancelTimeoutInMinutes: 1 +# variables: +# Codeql.Enabled: true +# steps: +# - checkout: msal-dev +# clean: true +# submodules: recursive +# persistCredentials: True +# - bash: | +# echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" +# echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" +# displayName: 'Set VSTS Fields in Environment' +# - template: azure-pipelines/templates/steps/automation-cert.yml@common +# - task: JavaToolInstaller@0 +# displayName: Use Java 17 +# inputs: +# versionSpec: '17' +# jdkArchitectureOption: x64 +# jdkSourceOption: PreInstalled +# - task: CodeQL3000Init@0 +# - task: Gradle@2 +# name: Gradle1 +# displayName: Assemble Local +# inputs: +# tasks: clean msal:assembleLocal +# publishJUnitResults: false +# testResultsFiles: '**/build/test-results/TEST-*.xml' +# jdkVersion: $(BuildParameters.jdkVersion) +# jdkArchitecture: $(BuildParameters.jdkArchitecture) +# sqGradlePluginVersion: 2.0.1 +# - task: CodeQL3000Finalize@0 +# - task: Gradle@2 +# displayName: Run Tests +# inputs: +# tasks: msal:jacocoTestReport -PcodeCoverageEnabled=true -ProbolectricSdkVersion=${{variables.robolectricSdkVersion}} -PmockApiUrl=$(MOCK_API_URL) -PnativeAuthConfigString=$(NATIVE_AUTH_CONFIG_STRING) +# javaHomeSelection: $(BuildParameters.javaHomeSelection) +# jdkVersion: 1.17 +# - script: tree "$(Build.SourcesDirectory)\msal" /F /A +# displayName: 'Print File Structure Tree' +# - publish: $(Build.SourcesDirectory)/msal/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml +# artifact: jacocoReportDev +# displayName: 'Publish JaCoCo Report Artifact (Dev Branch)' + - job: spotbugs + displayName: SpotBugs + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + submodules: recursive + persistCredentials: True + - bash: | + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" + displayName: 'Set VSTS Fields in Environment' + - template: azure-pipelines/templates/steps/spotbugs.yml@common + parameters: + project: msal + - job: lint + displayName: Lint + cancelTimeoutInMinutes: 1 + steps: + - checkout: self + clean: true + submodules: recursive + persistCredentials: True + - bash: | + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_USERNAME]VSTS" + echo "##vso[task.setvariable variable=ENV_VSTS_MVN_CRED_ACCESSTOKEN]$(System.AccessToken)" + displayName: 'Set VSTS Fields in Environment' + - task: Gradle@3 + displayName: Lint Local debug + inputs: + tasks: clean msal:lintLocalDebug + publishJUnitResults: false + jdkVersion: 1.17 +# - stage: compare_coverage +# displayName: Compare Code Coverage +# dependsOn: +# - build_and_test +# jobs: +# - job: compare +# displayName: Compare PR and Dev Coverage +# steps: +# - download: current +# artifact: jacocoReport # Adjust artifact name as needed +# - download: current +# artifact: jacocoReportDev # Adjust artifact name as needed +# +# - script: | +# python compare_coverage.py \ +# $(Pipeline.Workspace)/jacocoReport/jacocoTestReport.xml \ +# $(Pipeline.Workspace)/jacocoReportDev/jacocoTestReport.xml +# displayName: Compare Jacoco Coverage ... diff --git a/common b/common index 656da4c4bd..89bc11d34c 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 656da4c4bd27231af87dab79fc87d7e0a4778205 +Subproject commit 89bc11d34c62e1e2c85550339443f4b987047c58 diff --git a/msal/build.gradle b/msal/build.gradle index e19122b293..01328e20dd 100644 --- a/msal/build.gradle +++ b/msal/build.gradle @@ -5,6 +5,7 @@ plugins { id 'checkstyle' id 'maven-publish' id 'kotlin-android' + id 'jacoco' // Test retries id 'org.gradle.test-retry' version '1.5.6' @@ -28,8 +29,15 @@ codeCoverageReport { coverage.enabled = enableCodeCoverage } +jacoco { + toolVersion = "0.8.10" +} + // https://blog.gradle.org/gradle-flaky-test-retry-plugin tasks.withType(Test) { + jacoco { + includeNoLocationClasses = true // Required for Robolectric + } retry { // The maximum number of test failures that are allowed before retrying is disabled. maxRetries = 2 @@ -40,6 +48,44 @@ tasks.withType(Test) { } } +tasks.register("jacocoTestReport", JacocoReport) { + dependsOn "testLocalDebugUnitTest" // Run Robolectric tests first + + reports { + xml.required = true + html.required = true + } + + def fileFilter = [ + '**/R.class', '**/R$*.class', '**/BuildConfig.*', + '**/Manifest*.*', '**/*Test*.*', + 'android/**/*.*' + ] + + def kotlinClasses = fileTree( + dir: "$buildDir/tmp/kotlin-classes/localDebug", + excludes: fileFilter + ) + + def javaClasses = fileTree( + dir: "$buildDir/intermediates/javac/localDebug/compileLocalDebugJavaWithJavac/classes", + excludes: fileFilter + ) + + sourceDirectories.setFrom(files([ + "$projectDir/src/main/java", + "$projectDir/src/main/kotlin" + ])) + classDirectories.setFrom(files([kotlinClasses, javaClasses])) + executionData.setFrom(fileTree( + dir: buildDir, + includes: [ + "jacoco/testLocalDebugUnitTest.exec", + "outputs/unit_test_code_coverage/localDebugUnitTest/testLocalDebugUnitTest.exec" + ] + )) +} + android { namespace "com.microsoft.identity.msal" compileOptions {