Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ci/changed-file-filters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ ciFilters:
# decide which macOS jobs run, so a change to one has to rerun them all, while a Maestro
# flow edit under e2e/ must not.
iosCiConfig:
- 'scripts/report_native_coverage'
- 'scripts/lib/native_coverage_report.rb'
- 'e2e/bitrise.yml'
- 'e2e/config/ios_ci.yml'
- 'e2e/lib/ios_ci_run_plan.rb'
Expand Down
27 changes: 25 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,31 @@ pnpm --dir protocol test --coverage
A failing threshold fails the test command and its CI job. Add tests for the
uncovered behavior instead of lowering the threshold. The thresholds live in
`platforms/web/vite.config.ts`, `platforms/react-native/jest.config.js`, and
`protocol/vitest.config.ts`. Swift, Android, and standalone telemetry tests do
not yet enforce coverage thresholds.
`protocol/vitest.config.ts`.

Swift and Android generate coverage reports without enforcing a minimum yet:

| Platform | Local command | Reports | CI artifact |
| --- | --- | --- | --- |
| Android | `dev android test coverage` | `platforms/android/lib/build/reports/coverage/test/debug/` (HTML and XML) | `android-coverage` in the GitHub Actions Android test job |
| Swift | `dev swift test coverage` | `.xcresults/coverage/` (JSON and text) | Coverage reports in the Bitrise Swift package test job |

CI updates a coverage comment per native platform on same-repository pull requests,
using the same headings, line-coverage badges, and metric formatting as the
JavaScript reports, plus a link to the full reports. Android also writes its
coverage table to the GitHub Actions job summary.
Fork builds and builds without a write token keep the artifacts and log summaries
without posting a comment. These summaries report coverage without enforcing 85%.

Android reports cover the library's debug unit tests, including Robolectric
tests and the library's telemetry code. Swift reports contain per-target and
per-file line coverage from the package tests; inspect `ShopifyCheckoutKit` and
`ShopifyAcceleratedCheckouts` for the SDK coverage. Xcode also lists test and
dependency targets, sometimes with no executable lines; compare the named
production targets rather than the overall total. The full Swift result bundle
is available locally at `.xcresults/ShopifyCheckoutKit-Package-coverage.xcresult`.
Neither report includes sample app or end-to-end tests. Standalone TypeScript
telemetry tests do not yet collect coverage.

### Testing PR builds with Tophat

Expand Down
51 changes: 47 additions & 4 deletions .github/workflows/android-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
pull-requests: write
defaults:
run:
working-directory: platforms/android
Expand All @@ -28,12 +31,52 @@ jobs:
java-version: 17
cache: 'gradle'

- name: Run Tests
run: ./gradlew test --console=plain
- name: Run tests and generate library coverage
run: ./gradlew test :lib:createDebugUnitTestCoverageReport --console=plain

- name: Run Protocol Unit Tests
- name: Upload library coverage reports
id: coverage_artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: android-coverage
path: platforms/android/lib/build/reports/coverage/test/debug/
if-no-files-found: error
retention-days: 7

- name: Summarize library coverage
working-directory: ${{ github.workspace }}
env:
COVERAGE_REPOSITORY: ${{ github.repository }}
COVERAGE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
COVERAGE_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }}
# Fork and Dependabot tokens cannot write PR comments. The job summary
# and downloadable reports remain available for those builds.
COVERAGE_TOKEN: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.token || '' }}
COVERAGE_REPORT_URL: ${{ steps.coverage_artifact.outputs.artifact-url }}
run: ruby scripts/report_native_coverage android platforms/android/lib/build/reports/coverage/test/debug/report.xml

- name: Run Embedded Checkout Protocol tests with coverage
working-directory: protocol/languages/kotlin
run: ./gradlew test --console=plain
run: ./gradlew test jacocoTestReport --console=plain

- name: Upload Embedded Checkout Protocol coverage
id: protocol_coverage_artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: protocol-kotlin-coverage
path: protocol/languages/kotlin/embedded-checkout-protocol/build/reports/jacoco/test/
if-no-files-found: error
retention-days: 7

- name: Summarize Embedded Checkout Protocol coverage
working-directory: ${{ github.workspace }}
env:
COVERAGE_REPOSITORY: ${{ github.repository }}
COVERAGE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
COVERAGE_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.number || '' }}
COVERAGE_TOKEN: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'dependabot[bot]' && github.token || '' }}
COVERAGE_REPORT_URL: ${{ steps.protocol_coverage_artifact.outputs.artifact-url }}
run: ruby scripts/report_native_coverage protocol-kotlin protocol/languages/kotlin/embedded-checkout-protocol/build/reports/jacoco/test/jacocoTestReport.xml

- name: Setup sample app environment
id: sample_env
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
with:
filters: |
android:
- 'scripts/report_native_coverage'
- 'scripts/lib/native_coverage_report.rb'
- '.github/workflows/android-publish.yml'
- '.github/workflows/android-protocol-publish.yml'
- '.github/workflows/android-test.yml'
Expand Down Expand Up @@ -102,6 +104,7 @@ jobs:
- '.ci/changed-file-filters.yml'
- '.github/workflows/ci.yml'
scripts:
- 'scripts/report_native_coverage'
- '.github/scripts/measure-package-size'
- '.github/scripts/*bundle-size*'
- '.github/workflows/package-size.yml'
Expand All @@ -127,6 +130,9 @@ jobs:
name: Android
needs: changes
if: needs.changes.outputs.android == 'true'
permissions:
contents: read
pull-requests: write
uses: ./.github/workflows/android-test.yml

swift-lint:
Expand Down
21 changes: 21 additions & 0 deletions dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,16 @@ commands:
*) echo "Usage: dev protocol test [kotlin|swift|typescript] [-- <extra args>]"; exit 1 ;;
esac

coverage:
desc: "Run native protocol tests with coverage. Usage: dev protocol coverage <swift|kotlin>"
syntax: "<swift|kotlin>"
run: |
case "$1" in
swift) ./protocol/scripts/test_swift_coverage ;;
kotlin) protocol/languages/kotlin/gradlew -p protocol/languages/kotlin :embedded-checkout-protocol:jacocoTestReport --console=plain ;;
*) echo "Usage: dev protocol coverage <swift|kotlin>"; exit 1 ;;
esac

lint:
desc: "Lint Kotlin protocol source and TypeScript test files"
run: |
Expand Down Expand Up @@ -377,6 +387,14 @@ commands:
platforms/android/gradlew -p platforms/android clean test --console=plain
platforms/android/samples/CheckoutKitAndroidDemo/gradlew -p platforms/android/samples/CheckoutKitAndroidDemo :app:testDebugUnitTest --console=plain
subcommands:
coverage:
desc: Run Android library unit tests and generate HTML/XML coverage reports
syntax:
optional: --local
run: |
source platforms/android/scripts/parse_local_protocol_flag
parse_local_protocol_flag "$@"
platforms/android/gradlew -p platforms/android :lib:createDebugUnitTestCoverageReport --console=plain
samples:
desc: Run JVM tests for the Checkout Kit Android sample app
syntax:
Expand Down Expand Up @@ -654,6 +672,9 @@ commands:
./Scripts/test_samples
fi
subcommands:
coverage:
desc: Run Swift package tests and export Xcode coverage reports
run: cd platforms/swift && ./Scripts/test_coverage
sample:
desc: Run the sample app test target
syntax: "[test_class_name]"
Expand Down
41 changes: 39 additions & 2 deletions e2e/bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,51 @@ workflows:
- git-clone@8: {}
- bundle::create-ios-simulator: {}
- script@1:
title: Run Swift package tests
title: Run Swift package tests with coverage
timeout: 3600
no_output_timeout: 1800
inputs:
- content: |-
set -euo pipefail
cd platforms/swift
./Scripts/xcode_run test ShopifyCheckoutKit-Package
./Scripts/test_coverage
- deploy-to-bitrise-io@2:
is_always_run: false
inputs:
- deploy_path: "$BITRISE_SOURCE_DIR/.xcresults/coverage"
- script@1:
title: Report Swift coverage on the pull request
inputs:
- content: |-
set -euo pipefail
export COVERAGE_REPOSITORY="Shopify/checkout-kit"
export COVERAGE_PR="${BITRISE_PULL_REQUEST:-}"
export COVERAGE_SHA="${BITRISE_GIT_COMMIT:-}"
export COVERAGE_REPORT_URL="${BITRISE_BUILD_URL:-}"
export COVERAGE_TOKEN="${OVERRIDE_GITHUB_TOKEN:-${GIT_HTTP_PASSWORD:-}}"
ruby scripts/report_native_coverage swift .xcresults/coverage/coverage.json

- script@1:
title: Run Embedded Checkout Protocol Swift coverage
inputs:
- content: |-
set -euo pipefail
./protocol/scripts/test_swift_coverage
- deploy-to-bitrise-io@2:
is_always_run: false
inputs:
- deploy_path: "$BITRISE_SOURCE_DIR/protocol/languages/swift/.build/coverage"
- script@1:
title: Report Embedded Checkout Protocol Swift coverage
inputs:
- content: |-
set -euo pipefail
export COVERAGE_REPOSITORY="Shopify/checkout-kit"
export COVERAGE_PR="${BITRISE_PULL_REQUEST:-}"
export COVERAGE_SHA="${BITRISE_GIT_COMMIT:-}"
export COVERAGE_REPORT_URL="${BITRISE_BUILD_URL:-}"
export COVERAGE_TOKEN="${OVERRIDE_GITHUB_TOKEN:-${GIT_HTTP_PASSWORD:-}}"
ruby scripts/report_native_coverage protocol-swift protocol/languages/swift/.build/coverage/coverage.json

ci-ios-swift-samples:
meta:
Expand Down
14 changes: 14 additions & 0 deletions platforms/android/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ android {
}

buildTypes {
debug {
enableUnitTestCoverage true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
Expand Down Expand Up @@ -180,6 +183,17 @@ tasks.withType(Test).configureEach {
dependsOn verifyPublishedProtocol
}

plugins.withId('jacoco') {
tasks.withType(Test).configureEach {
jacoco {
// Robolectric loads SDK classes without a source location. Include
// those classes, but avoid instrumenting its runtime or JDK internals.
includeNoLocationClasses = true
includes = ['com.shopify.checkoutkit.*']
}
}
}

// Local protocol sources are for joint development only. Maven Local remains
// available for React Native's explicit --local builds.
tasks.withType(PublishToMavenRepository).configureEach {
Expand Down
18 changes: 18 additions & 0 deletions platforms/swift/Scripts/test_coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
repo_root="$(cd "$script_dir/../../.." && pwd)"
report_dir="$repo_root/.xcresults/coverage"
result_bundle="$repo_root/.xcresults/ShopifyCheckoutKit-Package-coverage.xcresult"

# Remove old exports before running so a failed test run cannot publish stale coverage.
mkdir -p "$report_dir"
rm -f "$report_dir/coverage.json" "$report_dir/coverage.txt"

ENABLE_CODE_COVERAGE=1 "$script_dir/xcode_run" test ShopifyCheckoutKit-Package

xcrun xccov view --report --json "$result_bundle" > "$report_dir/coverage.json"
xcrun xccov view --report "$result_bundle" > "$report_dir/coverage.txt"
xcrun xccov view --report --only-targets "$result_bundle"
printf '\nCoverage reports: %s\n' "$report_dir"
8 changes: 7 additions & 1 deletion platforms/swift/Scripts/xcode_run
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,21 @@ xcodebuild_cmd="xcodebuild $ACTION -scheme $SCHEME -sdk iphonesimulator -destina
# Retry failed tests up to three total attempts, including the initial run.
if [[ "$ACTION" == *"test"* ]]; then
xcodebuild_cmd="$xcodebuild_cmd -retry-tests-on-failure -test-iterations 3"
if [[ "${ENABLE_CODE_COVERAGE:-0}" == "1" ]]; then
xcodebuild_cmd="$xcodebuild_cmd -enableCodeCoverage YES"
fi
fi

if [[ "$SCHEME" == "ShopifyCheckoutKit-Package" && "$ACTION" == *"build"* && "$ACTION" != *"test"* ]]; then
xcodebuild_cmd="$xcodebuild_cmd SWIFT_TREAT_WARNINGS_AS_ERRORS=YES"
fi

if [[ "$CI" == "true" && ( "$ACTION" == *"build"* || "$ACTION" == *"test"* ) ]]; then
if [[ ( "$CI" == "true" || "${ENABLE_CODE_COVERAGE:-0}" == "1" ) && ( "$ACTION" == *"build"* || "$ACTION" == *"test"* ) ]]; then
result_bundle_dir="$(cd "$SCRIPT_DIR/../../.." && pwd)/.xcresults"
result_bundle_path="$result_bundle_dir/$SCHEME.xcresult"
if [[ "${ENABLE_CODE_COVERAGE:-0}" == "1" ]]; then
result_bundle_path="$result_bundle_dir/$SCHEME-coverage.xcresult"
fi
rm -rf "$result_bundle_path"
mkdir -p "$result_bundle_dir"
xcodebuild_cmd="$xcodebuild_cmd -resultBundlePath \"$result_bundle_path\""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
id 'signing'
id 'maven-publish'
id 'java-library'
id 'jacoco'
id 'org.jetbrains.kotlin.jvm'
id 'org.jetbrains.kotlin.plugin.serialization'
id 'io.gitlab.arturbosch.detekt'
Expand Down Expand Up @@ -121,3 +122,13 @@ publishing {
}
}
}

// The standalone protocol suite measures its own artifact, including generated
// wire models, independently of the Android SDK's published dependency.
jacocoTestReport {
dependsOn test
reports {
xml.required = true
html.required = true
}
}
11 changes: 11 additions & 0 deletions protocol/scripts/test_swift_coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
package_path="$repo_root/protocol/languages/swift"
report_dir="$package_path/.build/coverage"
mkdir -p "$report_dir"
rm -f "$report_dir/coverage.json"
swift test --package-path "$package_path" --enable-code-coverage
coverage_path="$(swift test --package-path "$package_path" --show-codecov-path)"
cp "$coverage_path" "$report_dir/coverage.json"
Loading
Loading