Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e446b6b
Restructure modules for the PubSub package split: java/android -> cor…
umair-ably Sep 1, 2026
8a3cd77
Bump the hardcoded agent-version canary in RealtimeHttpHeaderTest to …
umair-ably Sep 2, 2026
055e698
Add the device and server door packages with side-declaring agents
ttypic Sep 16, 2026
a129d9f
Add lockstep release pre-flight and a release dry-run CI job
umair-ably Sep 1, 2026
78b4860
Run the UTS through the server door's builders
umair-ably Sep 2, 2026
58ac97c
Skip token-auth conformance on the server UTS leg (realtime 40167)
umair-ably Sep 2, 2026
86b8c82
Run AuthReauthTest on the server UTS leg via a claim-bearing JWT
umair-ably Sep 2, 2026
3e73212
Adapt the UTS side-mode assertions to the ably-pubsub-java family ide…
umair-ably Sep 2, 2026
74cf820
Run native-token conformance on every UTS leg by splitting the clients
umair-ably Sep 2, 2026
6b5866c
Give the door builders an options-accepting entry point
ttypic Sep 17, 2026
548a7ad
Rename the REST naming to HTTP across the project
ttypic Sep 17, 2026
35f3cee
Remove the deprecated API across the SDK
ttypic Sep 17, 2026
2fbaab4
Drop unused imports in the tests and network-client-okhttp
ttypic Sep 18, 2026
8321b91
Replace environment, restHost and realtimeHost with endpoint
ttypic Oct 8, 2026
ce61a1b
Make releasing a realtime channel that isn't detached an error (RTS4c-e)
SimonWoolf Oct 9, 2026
33aef1b
Rename ClientOptions.idempotentRestPublishing to idempotentHttpPublis…
sacOO7 Oct 29, 2026
3e030d1
Persist the push registration token type by name
sacOO7 Oct 29, 2026
f04783f
Complete the 2.0 migration guide
sacOO7 Oct 29, 2026
54ee94d
Align the push token note in the migration guide with the migration b…
sacOO7 Oct 29, 2026
16fc065
Point README, CHANGELOG and CONTRIBUTING at 2.0
sacOO7 Oct 29, 2026
f95c15b
Fix stale :java references in UTS docs and tooling
sacOO7 Oct 29, 2026
8852d21
Emit additional agent entries in alphabetical order
sacOO7 Oct 29, 2026
412b114
Snapshot options in the door builders and strip the opposite side
sacOO7 Oct 29, 2026
0b82c58
Test the door side flags on the wire and the device error path
sacOO7 Oct 29, 2026
786cc9d
Forward uts.side to the :core and :liveobjects UTS tasks
sacOO7 Oct 29, 2026
107ab17
Clean up build and CI leftovers
sacOO7 Oct 29, 2026
17d9fda
Cover release() in the remaining channel states (RTS4c-e)
sacOO7 Oct 29, 2026
a33b1f6
Derive the agent version in RealtimeHttpHeaderTest
sacOO7 Oct 29, 2026
b9d81bc
Point the uts-to-kotlin skill at the 2.0 packages and modules
sacOO7 Oct 29, 2026
041e037
Correct details in the migration guide and UTS docs
sacOO7 Oct 29, 2026
58ae236
Describe the Ably-Agent entry order accurately in the migration guide
sacOO7 Oct 29, 2026
29dd929
Correct the wrapper-proxy and endpoint Javadoc
sacOO7 Oct 29, 2026
aca0297
Keep the idempotentRestPublishing option name
sacOO7 Oct 29, 2026
0fa250a
Restructure the README for the 2.0 device and server packages
sacOO7 Oct 30, 2026
25a3f80
Drop the version-placeholder note from the README install section
sacOO7 Oct 30, 2026
b874a86
Merge pull request #1256 from ably/integration/v2-review-fixes
sacOO7 Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
105 changes: 52 additions & 53 deletions .claude/skills/uts-to-kotlin/SKILL.md

Large diffs are not rendered by default.

47 changes: 23 additions & 24 deletions .claude/skills/uts-to-kotlin/references/objects-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ different things**, and a third *internal* layer underneath. Keep them straight:

| Layer | Spec name | ably-js | ably-java | Package |
|---|---|---|---|---|
| **Creation value type** — immutable blueprint you pass *into* `set` | `LiveMap` / `LiveCounter` (the `RTLMV*` / `RTLCV*` classes) | `LiveMap.create()` / `LiveCounter.create()` | `LiveMap` / `LiveCounter` | `io.ably.lib.liveobjects.value` |
| **Public read/write view** — what you navigate & subscribe on | `PathObject`, `Instance` | `PathObject`, `Instance` | typed hierarchy (§4, §5) | base in `io.ably.lib.liveobjects.path` / `.instance`; **typed subtypes in `.path.types` / `.instance.types`** |
| **Internal graph object** — the live CRDT node | `InternalLiveMap` / `InternalLiveCounter` (`RTLM*` / `RTLC*`), `ObjectsPool` | internal | `InternalLiveMap` / `InternalLiveCounter` / `ObjectsPool` — **same names as the spec** (`internal` classes, `:liveobjects` module) | `io.ably.lib.liveobjects[.value.livemap/.value.livecounter]` — not public; access rules §13, full symbol map §17 |
| **Creation value type** — immutable blueprint you pass *into* `set` | `LiveMap` / `LiveCounter` (the `RTLMV*` / `RTLCV*` classes) | `LiveMap.create()` / `LiveCounter.create()` | `LiveMap` / `LiveCounter` | `io.ably.pubsub.liveobjects.value` |
| **Public read/write view** — what you navigate & subscribe on | `PathObject`, `Instance` | `PathObject`, `Instance` | typed hierarchy (§4, §5) | base in `io.ably.pubsub.liveobjects.path` / `.instance`; **typed subtypes in `.path.types` / `.instance.types`** |
| **Internal graph object** — the live CRDT node | `InternalLiveMap` / `InternalLiveCounter` (`RTLM*` / `RTLC*`), `ObjectsPool` | internal | `InternalLiveMap` / `InternalLiveCounter` / `ObjectsPool` — **same names as the spec** (`internal` classes, `:liveobjects` module) | `io.ably.pubsub.liveobjects[.value.livemap/.value.livecounter]` — not public; access rules §13, full symbol map §17 |

So when a spec says `counter = LiveCounter.create(5)` and passes it to `set`, that's the **value type**
(`io.ably.lib.liveobjects.value.LiveCounter`). When a spec says "the resolved value is an
(`io.ably.pubsub.liveobjects.value.LiveCounter`). When a spec says "the resolved value is an
`InternalLiveCounter` with `.data == 5`", that's the **internal graph node** (§13). When a spec navigates
`root.get("counter").value()`, that's the **public view** (`PathObject`).

Expand Down Expand Up @@ -97,10 +97,10 @@ In the spec/ably-js a `PathObject` is polymorphic: `get`, `at`, `value`, `set`,
all hang off the one object. In ably-java the base `PathObject` exposes **only** the type-agnostic methods;
everything type-specific is moved onto a sub-interface you reach via an `as*` cast.

**Base `PathObject`** (`io.ably.lib.liveobjects.path.PathObject`) — always available. The typed sub-types
**Base `PathObject`** (`io.ably.pubsub.liveobjects.path.PathObject`) — always available. The typed sub-types
returned by the `as*` casts (`LiveMapPathObject`, `LiveCounterPathObject`, `NumberPathObject`,
`StringPathObject`, `BooleanPathObject`, `BinaryPathObject`, `JsonObjectPathObject`, `JsonArrayPathObject`)
live in **`io.ably.lib.liveobjects.path.types`** (not `.path`) — import them from there.
live in **`io.ably.pubsub.liveobjects.path.types`** (not `.path`) — import them from there.

| Spec / ably-js | ably-java |
|---|---|
Expand Down Expand Up @@ -207,7 +207,7 @@ Same partition as `PathObject`, with two differences: the base is **abstract / n
directly (`RTTS7e`), and the casts **throw** on mismatch instead of degrading (`RTTS9d`) — because an
`Instance` wraps an already-resolved value of known type.

**Base `Instance`** (`io.ably.lib.liveobjects.instance.Instance`):
**Base `Instance`** (`io.ably.pubsub.liveobjects.instance.Instance`):

| Spec / ably-js | ably-java |
|---|---|
Expand Down Expand Up @@ -254,7 +254,7 @@ immutable creation value types.

| Spec / ably-js | ably-java |
|---|---|
| `LiveCounter.create()` | `LiveCounter.create(): LiveCounter` (`io.ably.lib.liveobjects.value`) |
| `LiveCounter.create()` | `LiveCounter.create(): LiveCounter` (`io.ably.pubsub.liveobjects.value`) |
| `LiveCounter.create(5)` | `LiveCounter.create(5)` (arg is `Number`) |
| `LiveMap.create()` | `LiveMap.create(): LiveMap` |
| `LiveMap.create({ a: 1, b: "x" })` | `LiveMap.create(mapOf("a" to LiveMapValue.of(1), "b" to LiveMapValue.of("x")))` — entries are `Map<String, LiveMapValue>` |
Expand Down Expand Up @@ -370,13 +370,13 @@ depth (`RTPO19c1`).
| remove all | `` channel.`object`.offAll() `` |

Listener: `ObjectStateChange.Listener.onStateChanged(ObjectStateEvent)`. Enum `ObjectStateEvent { SYNCING,
SYNCED }` (`io.ably.lib.liveobjects.state`).
SYNCED }` (`io.ably.pubsub.liveobjects.state`).

---

## 10. `ValueType` & type discrimination <a id="10-valuetype"></a>

ably-js uses string-literal type tags; ably-java has an enum `io.ably.lib.liveobjects.ValueType`:
ably-js uses string-literal type tags; ably-java has an enum `io.ably.pubsub.liveobjects.ValueType`:

| Spec value category | `ValueType` |
|---|---|
Expand All @@ -394,7 +394,7 @@ non-null. Use `getType()` for "what is it" assertions and the matching `as*` cas

The spec's `PublicAPI::ObjectMessage` / `PublicAPI::ObjectOperation` (the `PAOM*` / `PAOOP*` types,
delivered to subscription listeners) map to ably-java interfaces with getters (package
`io.ably.lib.liveobjects.message`). The `PublicAPI::` prefix is dropped — ably-java exposes them as
`io.ably.pubsub.liveobjects.message`). The `PublicAPI::` prefix is dropped — ably-java exposes them as
`ObjectMessage` / `ObjectOperation`.

> **Getter-only, no *public* constructor — use the `buildPublicObjectMessage` helper.** In normal use you
Expand Down Expand Up @@ -520,10 +520,10 @@ Several **unit** specs assert on the **internal CRDT graph**, not the public API
that is otherwise `internal`. Build the source with the op builders and assert the public getters (§11).

In ably-java these are **not public**. They live in the `:liveobjects` module as `Internal*` / `Default*` / `Wire*` /
`ResolvedValue` / `Leaf` / `MapRef` / `CounterRef` classes (package `io.ably.lib.liveobjects.*`,
`ResolvedValue` / `Leaf` / `MapRef` / `CounterRef` classes (package `io.ably.pubsub.liveobjects.*`,
implementation source set), visible only to that module's own tests. Because of this, **all objects tiers
translate into `:liveobjects`'s own test source set** —
`liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/{unit,integration,proxy}/` — so the unit tier
`liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/{unit,integration,proxy}/` — so the unit tier
sees module internals directly, and the integration/proxy tiers reach the plugin through the same module.
Consequences when translating:

Expand All @@ -548,8 +548,8 @@ Consequences when translating:

Every objects unit spec opens with `setup_synced_channel` and constructs protocol/object messages with the
`build_*` helpers. These are implemented in
`liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/unit/Helpers.kt` (package
`io.ably.lib.liveobjects.uts.unit`; transport bootstrap via the shared `io.ably.lib.uts.infra.*` infra helpers,
`liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/unit/Helpers.kt` (package
`io.ably.pubsub.liveobjects.uts.unit`; transport bootstrap via the shared `io.ably.pubsub.uts.infra.*` infra helpers,
message construction via the typed `Wire*` DTOs — §17.10) — **call them; don't hand-roll the mock setup or
message wire forms.**

Expand Down Expand Up @@ -599,8 +599,8 @@ wire `action` / `semantics` are integer enum codes — the builders emit the cod
Some objects **integration** specs (tier `integration/standard`) seed object state over REST *before* the
realtime client connects, via the spec's `## REST Fixture Provisioning` helper `provision_objects_via_rest`.
Its ably-java translation lives in
`liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/integration/Helpers.kt` (package
`io.ably.lib.liveobjects.uts.integration`) — **call it; don't hand-roll the REST request or payload
`liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/integration/Helpers.kt` (package
`io.ably.pubsub.liveobjects.uts.integration`) — **call it; don't hand-roll the REST request or payload
JSON.** (Currently only `objects/integration/RTPO15` uses it.) It lives inside `:liveobjects`'s own tests
now (like the whole objects suite), but still uses only the public `AblyRest` — the "public API only for
REST provisioning" convention survives even though the module's internals are visible.
Expand All @@ -623,10 +623,9 @@ REST provisioning" convention survives even though the module's internals are vi
> envelope on the legacy `sandbox-rest.ably.io` host; both were aligned upstream — to this V2 shape and to
> the canonical nonprod sandbox host `sandbox.realtime.ably-nonprod.net` — in ably/specification#497.
>
> **Sandbox host.** `provisionObjectsViaRest` sets `restHost = SandboxApp.sandboxHost`
> (`sandbox.realtime.ably-nonprod.net`) — the same nonprod host `SandboxApp` and the realtime clients use,
> **not** `environment="sandbox"` (which resolves to the legacy `sandbox-rest.ably.io`, and
> can't be combined with `restHost` per `Hosts.java` TO3k2/TO3k3). The REST call hits the live sandbox
> **Sandbox host.** `provisionObjectsViaRest` sets `endpoint = SandboxApp.sandboxEndpoint`
> (`nonprod:sandbox`, resolving to `sandbox.realtime.ably-nonprod.net`) — the same endpoint `SandboxApp`
> and the realtime clients use. The REST call hits the live sandbox
> today, and the realtime client observes the provisioned data through the SDK's OBJECT_SYNC +
> `RealtimeObject.get()` (both implemented).

Expand Down Expand Up @@ -712,8 +711,8 @@ This section maps the **5 internal-graph unit specs** — `internal_live_counter
internal CRDT state, so they cannot translate against the public API — background in
`JAVA_LIVEOBJECTS_INTERNAL_METHODS_ACCESS_REPORT.md` at the repo root) — onto the internal engine.
Like the rest of the objects suite, they go into
**`liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/unit/`** (package
`io.ably.lib.liveobjects.uts.unit`, task `:liveobjects:runLiveObjectsUnitTests`), where the test
**`liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/unit/`** (package
`io.ably.pubsub.liveobjects.uts.unit`, task `:liveobjects:runLiveObjectsUnitTests`), where the test
compilation is associated with `main`, so every `internal` declaration is directly visible — no
reflection.

Expand All @@ -727,7 +726,7 @@ reflection.

The internal classes have `private constructor`s; each pairs with an `internal` companion factory that
needs a `DefaultRealtimeObject`. Build one from the mocked adapter (helper already exists in
`liveobjects/src/test/kotlin/io/ably/lib/liveobjects/unit/TestHelpers.kt`):
`liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/unit/TestHelpers.kt`):

```kotlin
val ro = DefaultRealtimeObject("test", getMockAblyClientAdapter())
Expand Down
14 changes: 7 additions & 7 deletions .claude/skills/uts-to-kotlin/scripts/resolve_uts.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@
SKILL_DIR = Path(__file__).resolve().parent.parent
MAPPING = SKILL_DIR / "uts-package-mapping.json"
TIERS = ("unit", "integration", "proxy")
# Owning Gradle module, keyed by a target dir's first path segment. The `lib` -> `:java`
# pair is the load-bearing non-obvious mapping (the `:java` module's build file wires
# Owning Gradle module, keyed by a target dir's first path segment. The `lib` -> `:core`
# pair is the load-bearing non-obvious mapping (the `:core` module's build file wires
# `../lib/src/...` srcDirs).
MODULE_BY_PREFIX = {"lib": ":java", "liveobjects": ":liveobjects", "uts": ":uts"}
MODULE_BY_PREFIX = {"lib": ":core", "liveobjects": ":liveobjects", "uts": ":uts"}


def fail(code, message):
Expand Down Expand Up @@ -95,7 +95,7 @@ def main():
"--create",
metavar="NAME",
help="add a mapping for this source module using NAME as the ably-java "
"module base name, then resolve. Scaffolds full lib/-rooted (:java) paths "
"module base name, then resolve. Scaffolds full lib/-rooted (:core) paths "
"only — a module whose tiers live in another Gradle module (like objects "
"-> :liveobjects) still needs a hand-edit afterwards.",
)
Expand Down Expand Up @@ -131,10 +131,10 @@ def main():
f"--create target {target!r} must be a simple module base name "
f"(letters/digits/underscore, e.g. 'liveobjects') so it forms a "
f"valid path and Kotlin package.")
# Full repo-root-relative paths using the realtime/`lib` (:java) template.
# Full repo-root-relative paths using the realtime/`lib` (:core) template.
# A module whose tiers live in another Gradle module (like objects ->
# :liveobjects) still needs a hand-edit — --create only scaffolds :java-hosted modules.
base = "lib/src/test/kotlin/io/ably/lib/uts"
# :liveobjects) still needs a hand-edit — --create only scaffolds :core-hosted modules.
base = "lib/src/test/kotlin/io/ably/pubsub/uts"
new_entry = {
"unit": f"{base}/unit/{target}",
"integration": f"{base}/integration/standard/{target}",
Expand Down
20 changes: 10 additions & 10 deletions .claude/skills/uts-to-kotlin/uts-package-mapping.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"_comment": "Maps each UTS spec module (a dir under specification/uts/) to its target test directory per tier. Each tier value is ONE repo-root-relative path (never machine-absolute); the Kotlin package is the path after 'src/test/kotlin/' with '/' -> '.'; the owning Gradle module is the path's first segment (lib/ -> :java, liveobjects/ -> :liveobjects, uts/ -> :uts). Every tier path MUST keep a module segment after the tier (e.g. 'unit/realtime', never bare 'unit') so a derived package can never collide with a :uts smoke package (io.ably.lib.uts.unit / .integration.standard / .integration.proxy) — an invariant currently held only by construction. An optional 'notes' field points (relative to this skill dir) to a per-module ably-js -> ably-java translation reference, read before translating that module. Used by the uts-to-kotlin skill (scripts/resolve_uts.py).",
"_comment": "Maps each UTS spec module (a dir under specification/uts/) to its target test directory per tier. Each tier value is ONE repo-root-relative path (never machine-absolute); the Kotlin package is the path after 'src/test/kotlin/' with '/' -> '.'; the owning Gradle module is the path's first segment (lib/ -> :core, liveobjects/ -> :liveobjects, uts/ -> :uts). Every tier path MUST keep a module segment after the tier (e.g. 'unit/realtime', never bare 'unit') so a derived package can never collide with a :uts smoke package (io.ably.pubsub.uts.unit / .integration.standard / .integration.proxy) — an invariant currently held only by construction. An optional 'notes' field points (relative to this skill dir) to a per-module ably-js -> ably-java translation reference, read before translating that module. Used by the uts-to-kotlin skill (scripts/resolve_uts.py).",
"packages": {
"realtime": {
"unit": "lib/src/test/kotlin/io/ably/lib/uts/unit/realtime",
"integration": "lib/src/test/kotlin/io/ably/lib/uts/integration/standard/realtime",
"proxy": "lib/src/test/kotlin/io/ably/lib/uts/integration/proxy/realtime"
"unit": "lib/src/test/kotlin/io/ably/pubsub/uts/unit/realtime",
"integration": "lib/src/test/kotlin/io/ably/pubsub/uts/integration/standard/realtime",
"proxy": "lib/src/test/kotlin/io/ably/pubsub/uts/integration/proxy/realtime"
},
"objects": {
"unit": "liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/unit",
"integration": "liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/integration",
"proxy": "liveobjects/src/test/kotlin/io/ably/lib/liveobjects/uts/proxy",
"unit": "liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/unit",
"integration": "liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/integration",
"proxy": "liveobjects/src/test/kotlin/io/ably/pubsub/liveobjects/uts/proxy",
"notes": "references/objects-mapping.md"
},
"rest": {
"unit": "lib/src/test/kotlin/io/ably/lib/uts/unit/rest",
"integration": "lib/src/test/kotlin/io/ably/lib/uts/integration/standard/rest",
"proxy": "lib/src/test/kotlin/io/ably/lib/uts/integration/proxy/rest"
"unit": "lib/src/test/kotlin/io/ably/pubsub/uts/unit/rest",
"integration": "lib/src/test/kotlin/io/ably/pubsub/uts/integration/standard/rest",
"proxy": "lib/src/test/kotlin/io/ably/pubsub/uts/integration/proxy/rest"
}
}
}
32 changes: 31 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,34 @@ jobs:
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :java:runUtsUnitTests :uts:runUtsUnitTests
- run: ./gradlew checkWithCodenarc checkstyleMain checkstyleTest runUnitTests runLiveObjectsUnitTests :core:runUtsUnitTests :uts:runUtsUnitTests

# A second UTS leg through the server door's builders (see the uts.side handling in
# uts/.../ClientFactories.kt): the builders stamp a side-declaring agent entry and pass
# everything else through, so conformance must be identical on both legs; SideModesTest
# fails a leg whose stamp does not match. The leg covers every module whose UTS specs
# build clients through the factories: :core and :liveobjects (plus the :uts self-tests).
# There is no device leg on the JVM — the device door is not wired into the factories' seam;
# its stamping contract is covered by the device module's own tests (jvmTest and the
# instrumentation tests in emulate.yml).
- run: ./gradlew :core:runUtsUnitTests :uts:runUtsUnitTests :liveobjects:runLiveObjectsUnitTests -Duts.side=server

# Continuously proves the release pre-flight and that every published module
# builds a publishable artifact set, so version/coordinate regressions surface
# on PRs rather than on release day.
release-dry-run:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
with:
persist-credentials: false
- name: Set up the JDK
uses: actions/setup-java@17f84c3641ba7b8f6deff6309fc4c864478f5d62 # v3
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@d9c87d481d55275bb5441eef3fe0e46805f9ef70 # v3
- run: ./gradlew verifyReleaseArtifacts publishToMavenLocal
10 changes: 6 additions & 4 deletions .github/workflows/emulate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ jobs:
strategy:
fail-fast: false
matrix:
android-api-level: [ 21, 24, 29, 35 ]
# core-android declares minSdk 19, but API 19-23 (and the HttpURLConnection network engine
# used there, -PhttpURLConnection) are not covered by this emulator matrix.
android-api-level: [ 24, 29, 35 ]

steps:
- name: checkout
Expand Down Expand Up @@ -50,10 +52,10 @@ jobs:
arch: ${{ steps.get-avd-arch.outputs.arch }}
target: default
# Print emulator logs if tests fail
script: ./gradlew :android:connectedAndroidTest ${{ matrix.android-api-level == 19 && '-PhttpURLConnection' || '' }} || (adb logcat -d System.out:I && exit 1)
script: ./gradlew :core-android:connectedAndroidTest :device:connectedAndroidTest || (adb logcat -d System.out:I && exit 1)

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: android-build-reports-${{ matrix.android-api-level }}
path: android/build/reports/
name: core-android-build-reports-${{ matrix.android-api-level }}
path: core-android/build/reports/
Loading
Loading