Skip to content
Closed
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
58 changes: 54 additions & 4 deletions .github/workflows/run-tests-compatibility.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,34 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-environment
- name: Setup Node with pnpm cache
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Enable Corepack
run: corepack enable
- name: Install JS deps
run: pnpm install --prefer-offline --frozen-lockfile=false

- name: Install Playwright (Chrome for Testing)
if: ${{ matrix.target == 'js' }}
run: pnpm exec playwright install --with-deps chrome

- name: Run tests (js: Node + browser)
if: ${{ matrix.target == 'js' }}
env:
WTR_CONCURRENCY: '2'
run: >
./gradlew
jsProviderTest jsBrowserTest
--continue
-Pckbuild.providerTests.step=compatibility.generate
-Pckbuild.testtool.instanceId=${{ matrix.os }}-${{ matrix.target }}
-Pckbuild.wtrConcurrency=${{ env.WTR_CONCURRENCY }}

- name: Run tests
if: ${{ matrix.target != 'connectedAndroid' }}
- name: Run tests (non-js)
if: ${{ matrix.target != 'connectedAndroid' && matrix.target != 'js' }}
run: >
./gradlew
${{ matrix.target }}ProviderTest
Expand Down Expand Up @@ -74,6 +99,15 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-environment
- name: Setup Node with pnpm cache
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Enable Corepack
run: corepack enable
- name: Install JS deps
run: pnpm install --prefer-offline --frozen-lockfile=false

- name: Download testtool server-storage
uses: actions/download-artifact@v4
Expand All @@ -85,8 +119,24 @@ jobs:
- name: Delete downloaded testtool server-storage
run: rm *.tar

- name: Run tests
if: ${{ matrix.target != 'connectedAndroid' }}
- name: Install Playwright (Chrome for Testing)
if: ${{ matrix.target == 'js' }}
run: pnpm exec playwright install --with-deps chrome

- name: Run tests (js: Node + browser)
if: ${{ matrix.target == 'js' }}
env:
WTR_CONCURRENCY: '2'
run: >
./gradlew
jsProviderTest jsBrowserTest
--continue
-Pckbuild.providerTests.step=compatibility.validate
-Pckbuild.testtool.instanceId=${{ matrix.os }}-${{ matrix.target }}
-Pckbuild.wtrConcurrency=${{ env.WTR_CONCURRENCY }}

- name: Run tests (non-js)
if: ${{ matrix.target != 'connectedAndroid' && matrix.target != 'js' }}
run: >
./gradlew
${{ matrix.target }}ProviderTest
Expand Down
24 changes: 22 additions & 2 deletions .github/workflows/run-tests-default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,31 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-environment
- name: Setup Node with pnpm cache
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Enable Corepack
run: corepack enable
- name: Install JS deps
run: pnpm install --prefer-offline --frozen-lockfile=false

- name: Install Playwright (Chrome for Testing)
if: ${{ matrix.target == 'js' }}
run: pnpm exec playwright install --with-deps chrome

- name: Run tests
if: ${{ matrix.target != 'connectedAndroid' }}
- name: Run tests (non-JS or JS Node-only)
if: ${{ matrix.target != 'connectedAndroid' && matrix.target != 'js' }}
run: ./gradlew ${{ matrix.target }}Test --continue

- name: Run JS tests (Node + WebCrypto browser)
if: ${{ matrix.target == 'js' }}
env:
# Tune WTR parallelism across test files
WTR_CONCURRENCY: '2'
run: ./gradlew jsAllTest --continue -Pckbuild.wtrConcurrency=${{ env.WTR_CONCURRENCY }}

- name: Run tests (android)
if: ${{ matrix.target == 'connectedAndroid' }}
uses: ./.github/actions/run-android-emulator-tests
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ build/
/docs/README.md
/docs/CHANGELOG.md
/site
# pnpm / node
node_modules/
.pnpm-debug.log
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,30 @@ Additionally, it's possible to use [BOM][BOM] or [Gradle version catalog][Gradle
[BOM]: https://whyoleg.github.io/cryptography-kotlin/bom/

[Gradle version catalog]: https://whyoleg.github.io/cryptography-kotlin/gradle-version-catalog/

## JS Tests (Web Test Runner + Playwright)

- Install browsers once: `./gradlew installBrowsers` (or `npm run browsers:install`)
- Run JS Node tests: `./gradlew jsNodeTest`
- Run WebCrypto browser tests (all groups): `./gradlew jsBrowserTest`
- Run both (Node + browser): `./gradlew jsAllTest`

Per‑group browser tasks:
- `./gradlew jsBrowserTestCore` (KDF + digests)
- `./gradlew jsBrowserTestAes` (AES modes)
- `./gradlew jsBrowserTestMac` (HMAC)
- `./gradlew jsBrowserTestEc` (ECDSA/ECDH)
- `./gradlew jsBrowserTestRsa` (RSA)
- `./gradlew jsBrowserTestCompat` (compatibility suites)

Filtering and flags:
- Filter to suite/test (regex): `-Pckbuild.wtrGrep="..."`
- Compatibility sub‑steps: `-Pckbuild.providerTests.step=compatibility.generate|compatibility.generateStress|compatibility.validate`
- Browser logs: `-Pckbuild.wtrLogs=true` (local default: on)
- Live progress (less buffered): `-Pckbuild.wtrLive=true` (local default: on)
- Fast mode (fewer iterations/smaller sizes): `-Pckbuild.fast=true` (local default: on)
- Concurrency across files: `-Pckbuild.wtrConcurrency=4` (local default: 4)

Defaults by environment:
- Local runs (no `CI`/`GITHUB_ACTIONS`): logs/live/fast are enabled and concurrency=4 unless you override.
- CI runs: no local defaults are applied; set flags explicitly in workflows.
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,21 @@ registerTestAggregationTask(

registerTestAggregationTask(
name = "jsProviderTest",
taskDependencies = { tasks.withType<KotlinJsTest>().matching { it.compilation.platformType == KotlinPlatformType.js } },
taskDependencies = {
// Only Node-based JS tests here; browser tests are handled by :test
tasks.withType<KotlinJsTest>().matching {
it.compilation.platformType == KotlinPlatformType.js && it.name.contains("Node", ignoreCase = true)
}
},
targetFilter = { it.platformType == KotlinPlatformType.js }
)

// Ensure provider JS browser tests can run via Web Test Runner when available
tasks.matching { it.name == "jsProviderTest" }.configureEach {
// If this project defines :test (for JS browser targets), include it
dependsOn(tasks.matching { it.name == "test" })
}

registerTestAggregationTask(
name = "jvmAllProviderTest",
taskDependencies = { tasks.withType<KotlinJvmTest>() },
Expand Down
128 changes: 124 additions & 4 deletions build-logic/src/main/kotlin/ckbuild.multiplatform-tests.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import org.jetbrains.kotlin.gradle.targets.js.ir.*
import org.jetbrains.kotlin.gradle.targets.js.testing.*
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.*
import org.jetbrains.kotlin.gradle.targets.native.tasks.*
import org.gradle.kotlin.dsl.*
import java.io.File
import dev.whyoleg.cryptography.testtool.plugin.TesttoolServerConfiguration
import dev.whyoleg.cryptography.testtool.plugin.TesttoolServerService

plugins {
id("ckbuild.multiplatform-base")
Expand Down Expand Up @@ -40,11 +44,127 @@ kotlin {
}

targets.withType<KotlinJsIrTarget>().configureEach {
// Browser tests run via Web Test Runner (Playwright) using :test.
// We no longer configure Karma for browser tests.
// Emit ESM for the browser test development executable so Web Test Runner can import it
project.tasks.matching { it.name == "compileTestDevelopmentExecutableKotlinJs" }
.withType(org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink::class.java)
.configureEach {
compilerOptions {
moduleKind.set(org.jetbrains.kotlin.gradle.dsl.JsModuleKind.MODULE_ES)
}
}

// Provide a generic :test task to run browser tests via Web Test Runner (Playwright)
whenBrowserConfigured {
testTask {
useKarma {
useConfigDirectory(rootDir.resolve("karma.config.d"))
useChromeHeadless()
if (project.tasks.findByName("test") == null) {
// Placeholder for potential Testtool server wiring if needed
val testtoolConfig = TesttoolServerConfiguration(rootProject)
// Keep server alive while WTR runs by using the BuildService directly
val wtrServerService = gradle.sharedServices.registerIfAbsent(
"wtr-testtool-server-service",
TesttoolServerService::class.java
) {
parameters.instanceId.set(providers.gradleProperty("ckbuild.testtool.instanceId").orElse("wtr"))
parameters.storage.set(testtoolConfig.serverStorageDir)
}

project.tasks.register<org.gradle.api.tasks.Exec>("test") {
// This Exec task wires a BuildService and streams output; mark as CC-incompatible
notCompatibleWithConfigurationCache("Exec task streams output and uses a BuildService")
// Allow skipping per-module WTR when using aggregated browser run
onlyIf {
!providers.gradleProperty("ckbuild.aggregateJsBrowser")
.map(String::toBoolean)
.getOrElse(false)
}
group = org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP
description = "Run JS browser tests via Web Test Runner (Playwright Chrome)"
dependsOn(project.tasks.matching { it.name == "jsTestTestDevelopmentExecutableCompileSync" })
// Also tie the server BuildService lifecycle to this Exec task
usesService(wtrServerService)
// Force service realization so the server actually starts before WTR
doFirst {
wtrServerService.get()
}
// Assume browsers are pre-installed (installBrowsers) in CI/local; no hard dependency here
workingDir = rootProject.projectDir

// Restrict WTR to this module's browser test dev executable
val relModuleDir = project.projectDir.relativeTo(rootProject.projectDir)
.path.replace(File.separatorChar, '/')
val testGlob = "$relModuleDir/build/compileSync/js/test/testDevelopmentExecutable/kotlin/*-test.mjs"

// Note: Testtool server is not required for default browser tests; run WTR directly

val cmd = listOf(
"bash", "-lc",
"set -o pipefail; " +
"WTR_FILES='$testGlob' pnpm exec web-test-runner --config web-test-runner.config.mjs | tee 'build/wtr-${project.name}.log'; " +
"status=${'$'}{PIPESTATUS[0]}; exit ${'$'}status"
)
commandLine = cmd

// If a Testtool server is required, it is started via BuildService before running :test

// Optional grep via -Pckbuild.wtrGrep="pattern"
val wtrGrep = providers.gradleProperty("ckbuild.wtrGrep").orNull
var grepApplied = false
wtrGrep?.let { grep ->
if (grep.isNotBlank()) {
environment("WTR_GREP", grep)
grepApplied = true
}
}
// Map providerTests.step to grep if not explicitly set
if (!grepApplied) {
val step = providers.gradleProperty("ckbuild.providerTests.step").orNull
val mapped = when (step) {
null -> "^(?:(?!CompatibilityTest.*(generateStep|generateStressStep|validateStep)$).)*$"
"compatibility.generate" -> "CompatibilityTest.*generateStep$"
"compatibility.generateStress" -> "CompatibilityTest.*generateStressStep$"
"compatibility.validate" -> "CompatibilityTest.*validateStep$"
else -> null
}
if (mapped != null) environment("WTR_GREP", mapped)
}
// Optional live browser logs: -Pckbuild.wtrLogs=true
val isCi = System.getenv("CI") != null || System.getenv("GITHUB_ACTIONS") != null
val wtrLogs = providers.gradleProperty("ckbuild.wtrLogs").map(String::toBoolean).getOrElse(false)
var logsApplied = false
if (wtrLogs) { environment("WTR_BROWSER_LOGS", "1"); logsApplied = true }
// Optional live progress (non-static logging): -Pckbuild.wtrLive=true
val wtrLive = providers.gradleProperty("ckbuild.wtrLive").map(String::toBoolean).getOrElse(false)
var liveApplied = false
if (wtrLive) { environment("WTR_LIVE", "1"); liveApplied = true }
// Optional fast mode to reduce iterations: -Pckbuild.fast=true
val fast = providers.gradleProperty("ckbuild.fast").map(String::toBoolean).getOrElse(false)
var fastApplied = false
if (fast) { environment("WTR_FAST", "1"); fastApplied = true }
// Optional headful/devtools
val headful = providers.gradleProperty("ckbuild.wtrHeadful").map(String::toBoolean).getOrElse(false)
if (headful) environment("HEADFUL", "1")
val devtools = providers.gradleProperty("ckbuild.wtrDevtools").map(String::toBoolean).getOrElse(false)
if (devtools) environment("WTR_DEVTOOLS", "1")
// Optional concurrency via -Pckbuild.wtrConcurrency=N
var concApplied = false
providers.gradleProperty("ckbuild.wtrConcurrency").orNull?.let { conc ->
if (conc.isNotBlank()) { environment("WTR_CONCURRENCY", conc); concApplied = true }
}
// Apply environment-based defaults when not explicitly set
if (!isCi && !logsApplied) environment("WTR_BROWSER_LOGS", "1")
if (!isCi && !liveApplied) environment("WTR_LIVE", "1")
if (!isCi && !fastApplied) environment("WTR_FAST", "1")
if (!concApplied && !isCi) environment("WTR_CONCURRENCY", "4")

// Optional concurrency via -Pckbuild.wtrConcurrency=N
providers.gradleProperty("ckbuild.wtrConcurrency").orNull?.let { conc ->
if (conc.isNotBlank()) environment("WTR_CONCURRENCY", conc)
}

// Stream sub-process output to Gradle console
standardOutput = System.out
errorOutput = System.err
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions build-logic/src/main/kotlin/ckbuild/Projects.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ object Projects {
"cryptography-provider-jdk-android-tests" to setOf(),
"cryptography-provider-openssl3-test" to setOf(),
"cryptography-provider-tests" to setOf(),
// WebCrypto split browser test modules (not published)
"cryptography-provider-webcrypto-tests-core" to setOf(),
"cryptography-provider-webcrypto-tests-aes" to setOf(),
"cryptography-provider-webcrypto-tests-mac" to setOf(),
"cryptography-provider-webcrypto-tests-ec" to setOf(),
"cryptography-provider-webcrypto-tests-rsa" to setOf(),
"cryptography-provider-webcrypto-tests-compat" to setOf(),
)

val published: Set<String> = projectTags.filter { Tag.PUBLISHED in it.value }.keys
Expand Down
Loading