From 24386bb56f5f625e5b0b646eacc8ccc246f8afb0 Mon Sep 17 00:00:00 2001 From: eric8810 Date: Mon, 19 Oct 2026 18:53:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(kotlin):=20=E6=8F=92=E4=BB=B6=20API?= =?UTF-8?q?=E3=80=81=E7=BB=9F=E4=B8=80=E4=BA=8B=E4=BB=B6=E3=80=81=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=B7=A5=E5=85=B7=E4=B8=8E=E8=BF=90=E8=A1=8C=E6=97=B6?= =?UTF-8?q?=E5=A5=91=E7=BA=A6=EF=BC=88#390=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kotlin SDK K-P1 的第二部分: - 插件 API 3:Rutis.define(name, inject, provides?, schema?) { ctx, config -> }; 配置是 @Serializable 类型,经 RutisFormat 解码,schema 由 SerialDescriptor 推出 (可覆盖);service(sync = …) 声明方法种类(默认 async);Ctx 是行的协程 作用域(卸载时取消并等待),use(@RutisClient 代理;同进程提供者实现 I 时 得到对象本身)、provide(没声明的服务以 rows.announce 通告,方法全为 async; 宿主比 0.12 旧时按契约提示一次)、effect(卸载时逆序)。 - 运行时:mount(features rows.v2、hosts、leaf、scopes、rows.announce; plugins、implementation、engine)、rows.schema / load / update / unload、 hosts.provide / withdraw、release、get、dispose;调用方的行(每行按 isolate 计算自己的服务 id);插件 API 版本检查。 - 统一事件:on(prepend、global,监听器一律 kind sync)、emit / parallel / serial / waterfall / bail / filter;快速路径与经宿主的 events.dispatch(filter 为选择器 函数引用、terminal);events.on / off / elsewhere / call 在读线程上按帧序处理; emit 按监听器串行;作用域;卸载时拒绝该行的 events.call、撤下服务、取消并 等待正在运行的监听器(RUTIS_SHUTDOWN_TIMEOUT);rutis/* 只可监听; 报告行原文;对没有 events-v2 的宿主握手后按原文拒绝。 - jvm 测试入口 Rutis.serve(args, plugins…)(unix: / 裸路径 / tcp: 加令牌): 会话结束卸载全部行,清理超过时限或旧宿主时以 1 退出。 - 测试工具 arcships.rutis.testing(RutisTest.load):内存通道上的真实会话, 测试一方是宿主(服务、总线、行与作用域)。 - rutis-conformance:weather 与 conformance-events 插件、Main(Rutis.serve); 测试工具对 weather.expected.json 与 events.expected.json 的检查(51 个用例)。 - 期望文件:weather 加 kotlin/* 样例值;events 的语言加 kotlin(Kotlin 监听器 是同步的,两个 async-listener 用例除外),toolCases 加 kotlin 51; events.rs 记下 Kotlin 的取消(CancellationException)。 - Rust:session_matrix 加 Kotlin(回拨 socket 路径、loopback);loader 的测试 feature kotlin,events_conformance 以 Kotlin 为 X,并作其他语言的 Y。 - CI:runtimes-kotlin 加 session_matrix 与 events_conformance。 --- .github/workflows/ci.yml | 22 +- .../src/runtime/testing/events.expected.json | 14 +- .../src/runtime/testing/events.rs | 6 +- .../src/runtime/testing/weather.expected.json | 15 +- crates/rutis-bridge/tests/events_expected.rs | 2 +- crates/rutis-bridge/tests/session_matrix.rs | 30 + crates/rutis-loader/Cargo.toml | 3 + crates/rutis-loader/tests/common/runtimes.rs | 41 ++ .../rutis-loader/tests/events_conformance.rs | 32 +- docs/ci.en.md | 4 + docs/ci.md | 4 + .../rutis/rutis-conformance/build.gradle.kts | 21 +- .../kotlin/arcships/rutis/conformance/Main.kt | 12 + .../conformance/plugins/ConformanceEvents.kt | 311 +++++++++ .../rutis/conformance/plugins/Weather.kt | 119 ++++ .../conformance/EventsConformanceTest.kt | 334 +++++++++ .../conformance/WeatherConformanceTest.kt | 116 ++++ .../arcships/rutis/conformance/Written.kt | 112 +++ .../jvmMain/kotlin/arcships/rutis/Serve.kt | 95 +++ .../kotlin/arcships/rutis/GeneratedTest.kt | 2 + .../kotlin/arcships/rutis/RuntimeTest.kt | 143 ++++ .../src/shared/kotlin/arcships/rutis/Ctx.kt | 147 ++++ .../shared/kotlin/arcships/rutis/Events.kt | 84 +++ .../src/shared/kotlin/arcships/rutis/Rutis.kt | 131 ++++ .../kotlin/arcships/rutis/internal/Bus.kt | 252 +++++++ .../kotlin/arcships/rutis/internal/Control.kt | 102 +++ .../arcships/rutis/internal/EventsRuntime.kt | 453 ++++++++++++ .../kotlin/arcships/rutis/internal/Runtime.kt | 457 +++++++++++++ .../kotlin/arcships/rutis/internal/Schema.kt | 96 +++ .../arcships/rutis/internal/Sessions.kt | 68 ++ .../arcships/rutis/testing/RutisTest.kt | 645 ++++++++++++++++++ 31 files changed, 3852 insertions(+), 21 deletions(-) create mode 100644 kotlin/rutis/rutis-conformance/src/main/kotlin/arcships/rutis/conformance/Main.kt create mode 100644 kotlin/rutis/rutis-conformance/src/main/kotlin/arcships/rutis/conformance/plugins/ConformanceEvents.kt create mode 100644 kotlin/rutis/rutis-conformance/src/main/kotlin/arcships/rutis/conformance/plugins/Weather.kt create mode 100644 kotlin/rutis/rutis-conformance/src/test/kotlin/arcships/rutis/conformance/EventsConformanceTest.kt create mode 100644 kotlin/rutis/rutis-conformance/src/test/kotlin/arcships/rutis/conformance/WeatherConformanceTest.kt create mode 100644 kotlin/rutis/rutis-conformance/src/test/kotlin/arcships/rutis/conformance/Written.kt create mode 100644 kotlin/rutis/rutis/src/jvmMain/kotlin/arcships/rutis/Serve.kt create mode 100644 kotlin/rutis/rutis/src/jvmTest/kotlin/arcships/rutis/RuntimeTest.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/Ctx.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/Events.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/Rutis.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/Bus.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/Control.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/EventsRuntime.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/Runtime.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/Schema.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/internal/Sessions.kt create mode 100644 kotlin/rutis/rutis/src/shared/kotlin/arcships/rutis/testing/RutisTest.kt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a75197e18..3b84a31a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -850,7 +850,7 @@ jobs: java-version: '17' cache: ${{ runner.environment == 'github-hosted' && 'gradle' || '' }} cache-dependency-path: kotlin/rutis/**/*.gradle.kts - - name: gradle build (the SDK, the processor, the conformance programs) + - name: gradle build (the SDK, the processor, the testing tool, the conformance programs) working-directory: kotlin/rutis run: | # The downloads first, retried (the runners' networks); then the @@ -862,10 +862,28 @@ jobs: done ./gradlew --no-daemon build echo "RUTIS_KOTLIN_JAR=$PWD/rutis-conformance/build/libs/rutis-conformance-all.jar" >> "$GITHUB_ENV" + # The other languages, as the far ends of the events conformance. + - uses: actions/setup-node@v4 + with: + node-version: '22' + - run: npm --prefix node/rutis-runtime ci + - uses: actions/setup-python@v5 + if: runner.environment == 'github-hosted' || runner.os != 'macOS' + with: + python-version: '3.10' + - run: python3 -m pip install "websockets==15.*" # Only the Kotlin tests of the contract suites: the other runtimes run # in their own jobs. - name: cargo test (the contract suites with Kotlin) - run: cargo test -p rutis-bridge --features testing,kotlin --test runtime_conformance --test value_roundtrip kotlin_ + run: cargo test -p rutis-bridge --features testing,websocket,kotlin --test runtime_conformance --test value_roundtrip --test session_matrix kotlin_ + # The events conformance with X Kotlin, and Kotlin as Y of the others + # (the written placements on pull requests, a sample in full runs). + - name: cargo test (events conformance with Kotlin) + env: + RUTIS_EVENTS_PLACEMENTS: ${{ needs.changes.outputs.all == 'true' && 'sampled' || 'written' }} + RUTIS_EVENTS_REQUIRE: node,python,kotlin + RUTIS_EVENTS_SETTLE_SCALE: '2' + run: cargo test -p rutis-loader --features node,python,kotlin --test events_conformance - name: self-hosted runner, end if: always() && runner.environment == 'self-hosted' env: diff --git a/crates/rutis-bridge/src/runtime/testing/events.expected.json b/crates/rutis-bridge/src/runtime/testing/events.expected.json index c3d5abd01..5144ebe5a 100644 --- a/crates/rutis-bridge/src/runtime/testing/events.expected.json +++ b/crates/rutis-bridge/src/runtime/testing/events.expected.json @@ -1,18 +1,18 @@ { "about": [ "Unified events conformance (#464): one list both the real runtimes and the SDK testing tools are checked against. Format: docs/design-events-protocol-2026-10-18.en.md §13; the cases of §13.3 come first, then the ones added to cover every rule.", - "Real runtimes: crates/rutis-loader/tests/events_conformance.rs (rows of one rutis host, its runtimes, and a second node). Testing tools, the cases without `tool: false`: node/rutis/test/events-conformance.test.mjs, python/rutis/tests/test_events_conformance.py, go/rutis/rutistest/events_conformance_test.go, bun/rutis-bun/test/events-conformance.test.ts, swift/rutis/Tests/RutisTests/EventsConformanceTests.swift (RutisTest). The file is checked for shape by crates/rutis-bridge/tests/events_expected.rs.", - "Languages (`only` / `except`): rust (the host's Rust plugin), node (a Cordis plugin in the Node runtime), node-leaf (a leaf plugin in the Node runtime), bun, python, go, swift. Fixtures: node/rutis-runtime/test/fixtures/conformance-events{,-e,-f}/ (Cordis) and conformance-events-leaf{,-e,-f}.mjs, bun/rutis-bun/test/fixtures/conformance-events{,-e,-f}.ts, python/rutis/tests/fixtures/conformance_events{,_e,_f}.py, go/rutis/internal/fixtures/plugins/conformance (served by go/rutis/internal/fixtures/runtime; one plugin provides every emitter undeclared, its sync method on the wire `dispatchSync`), swift/rutis/Sources/RutisFixturePlugins/ConformanceEvents.swift (served by RutisConformance; as Go), and the Rust plugin of crates/rutis-loader/tests/events_conformance.", + "Real runtimes: crates/rutis-loader/tests/events_conformance.rs (rows of one rutis host, its runtimes, and a second node). Testing tools, the cases without `tool: false`: node/rutis/test/events-conformance.test.mjs, python/rutis/tests/test_events_conformance.py, go/rutis/rutistest/events_conformance_test.go, bun/rutis-bun/test/events-conformance.test.ts, swift/rutis/Tests/RutisTests/EventsConformanceTests.swift (RutisTest), kotlin/rutis/rutis-conformance/src/test/kotlin/arcships/rutis/conformance/EventsConformanceTest.kt. The file is checked for shape by crates/rutis-bridge/tests/events_expected.rs.", + "Languages (`only` / `except`): rust (the host's Rust plugin), node (a Cordis plugin in the Node runtime), node-leaf (a leaf plugin in the Node runtime), bun, python, go, swift, kotlin (the Kotlin SDK's jvm target). Fixtures: node/rutis-runtime/test/fixtures/conformance-events{,-e,-f}/ (Cordis) and conformance-events-leaf{,-e,-f}.mjs, bun/rutis-bun/test/fixtures/conformance-events{,-e,-f}.ts, python/rutis/tests/fixtures/conformance_events{,_e,_f}.py, go/rutis/internal/fixtures/plugins/conformance (served by go/rutis/internal/fixtures/runtime; one plugin provides every emitter undeclared, its sync method on the wire `dispatchSync`), swift/rutis/Sources/RutisFixturePlugins/ConformanceEvents.swift (served by RutisConformance; as Go), kotlin/rutis/rutis-conformance/src/main/kotlin/arcships/rutis/conformance/plugins/ConformanceEvents.kt (likewise: one plugin provides every emitter undeclared, `dispatchSync`), and the Rust plugin of crates/rutis-loader/tests/events_conformance.", "Scopes: each scope path is one instance of its group, created once; t2's group is `team`, not `tenant`, so that rows placed in t1 are not copied into t2 (a loader instance copies its group's rows). The rule compares ids, so the group names do not matter to it.", "Additions to the format of §13.2: a listener's `fails` ({name, message}: its row's apply throws that error, after registering nothing); `typed: \"unit\"` (a Rust unit struct event); `observesDispatch` (a Cordis listener row that also logs `