Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Agent 的配置入口由同一个目录补齐,移动到任何已有停靠区

## 验证

- `WorkbenchContributionHostTests.chineseActionSearchFindsAndRestoresHiddenTerminal` 使用实际中英资源验证中文标题、中文说明和英文别名,执行后恢复终端入口,切换回英文后不再沿用旧语言;修复前中文查询确认为空。Core 的 `shared/fixtures/search/actions-v1.json` 验证排序、Unicode、插件原文和输入预算。
- `WorkbenchContributionHostTests.chineseActionSearchFindsAndRestoresHiddenTerminal` 使用实际中英资源验证中文标题、中文说明和英文别名,执行后恢复终端入口,切换回英文后不再沿用旧语言;修复前中文查询确认为空。Core 的 `shared/fixtures/search/actions-v1.json` 验证排序、Unicode、插件原文和输入预算。 动作匹配实现位于 `rust/lithe-core/src/action_search/mod.rs`,遵循 Core 根目录仅保留 `lib.rs` 的既有布局规则;模块路径和 `search.actions` 协议不变。

- `WorkbenchContributionHostTests.toolWindowsMenuRestoresHiddenTerminalAtItsSavedDockWithoutToggling` 覆盖隐藏入口恢复、原停靠组、重复 Show 不关闭、模块禁用及旧菜单动作;已安装声明式插件流程补充从同一菜单恢复。`ContextMenuCoverageTests.rightClickOnMenuItemReplacesThePopupWithoutRunningItsPrimaryAction` 用原生右键事件验证菜单交接与执行,不运行主动作;模型回归确认恢复图标时没有模块激活或终端会话。实际产品还需在深浅主题检查左侧“…”、菜单打开/关闭、快捷键与终端已有会话。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,19 @@ Lithe 为标题预留至少六列;更密集的行可以继续扩宽。这是 L
对齐可能让斜向箭头尖端跨过逻辑行边界半个点,因此命中检查覆盖相邻行及尖端
附近一个物理像素;屏幕 backing scale 变化只重绘,不重新建图。

2026-11-03 CI 提速验证暴露了测试夹具的事件上下文问题:直接调用
`NSWindow.sendEvent` 不会更新 `NSApp.currentEvent`。旧 mouse-move 事件会让原生
箭头表面按设计放弃命中,回到 SwiftUI 行按钮;1× 下向下斜箭头尖端越过行边界,
出现目标 `40` 未跳转、反而选中相邻行 `2` 的四处断言失败。2× 下按钮几何仍能
接住点击,因而本地 Retina 验证会掩盖它。固定 1× 与旧事件上下文后,本地重现了
CI 的同一组失败;这不是缩小尖端断言范围的理由。

`swiftUIDiagonalArrowTips` 现同时覆盖 1×/2× 和尖端/内侧半点。窗口测试替身与
SwiftUI 环境使用同一缩放系数;同步派发每次合成点击时,测试内临时提供该事件
作为 AppKit 的公开 `currentEvent`。替换仅存在于 MainActor 的同步调用范围,
通过 `defer` 恢复 getter 并释放 block,嵌套前后也断言旧上下文恢复。仍调用真实
窗口事件、命中和回调路径,不改变生产事件过滤、几何、断言或一秒等待上限。

Git Log 时间列保留 Inter 13 regular,启用字体的等宽数字特性,避免 `11`、`55`
等数字组合改变文本总宽度。按应用语言而非系统语言选择格式:英文为
`yyyy/MM/dd hh:mm AM/PM`,中文为 `yyyy/MM/dd HH:mm`。英文 AM/PM 使用固定
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node

import { createHash } from "node:crypto";
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
Expand Down Expand Up @@ -29,6 +30,9 @@ export function parseArguments(arguments_) {
suiteTimeoutMs: 1200000,
report: null,
keepGoing: false,
shardIndex: 1,
shardCount: 1,
testPrefix: null,
};
for (let index = 0; index < arguments_.length; index += 1) {
const argument = arguments_[index];
Expand All @@ -51,6 +55,12 @@ export function parseArguments(arguments_) {
} else if (argument === "--suite-timeout-ms") {
options.suiteTimeoutMs = positiveInteger(arguments_[++index], "--suite-timeout-ms");
} else if (argument === "--report") options.report = path.resolve(arguments_[++index]);
else if (argument === "--shard-index") options.shardIndex = positiveInteger(arguments_[++index], "--shard-index");
else if (argument === "--shard-count") options.shardCount = positiveInteger(arguments_[++index], "--shard-count");
else if (argument === "--test-prefix") {
options.testPrefix = arguments_[++index];
if (!options.testPrefix || options.testPrefix.startsWith("--")) throw new Error("--test-prefix requires a nonempty prefix.");
}
else if (argument === "--keep-going") options.keepGoing = true;
else throw new Error(`Unknown argument: ${argument}`);
}
Expand All @@ -61,9 +71,16 @@ export function parseArguments(arguments_) {
if (options.testBudgets.some((budget) => budget.maxMs <= options.warnMs)) {
throw new Error("--test-budget must be higher than --warn-ms.");
}
if (options.shardIndex > options.shardCount) throw new Error("--shard-index must not exceed --shard-count.");
return options;
}

// Hash the stable target/name identity rather than enumeration order. Adding a
// test cannot move every later case, and every case belongs to exactly one shard.
export function shardForTest(target, name, count) {
return createHash("sha256").update(`${target}::${name}`).digest().readUInt32BE(0) % count + 1;
}

function artifactFromLine(line) {
try {
const value = JSON.parse(line);
Expand Down Expand Up @@ -106,6 +123,7 @@ export async function run(
writeFileSync(logPath, "");
const artifacts = new Map();
const records = [];
const inventory = [];
const compilerMessages = [];
const suiteStartedAt = now();
const suiteDeadline = suiteStartedAt + options.suiteTimeoutMs;
Expand Down Expand Up @@ -146,6 +164,12 @@ export async function run(
testBudgets: options.testBudgets ?? [],
suiteTimeoutMs: options.suiteTimeoutMs,
buildDurationMs,
inventory,
selection: {
shardIndex: options.shardIndex ?? 1,
shardCount: options.shardCount ?? 1,
testPrefix: options.testPrefix ?? null,
},
suite: {
timedOut: suiteTimedOutStage !== null,
stage: suiteTimedOutStage,
Expand Down Expand Up @@ -249,7 +273,10 @@ export async function run(
.map((line) => line.match(/^(.*): test$/)?.[1])
.filter(Boolean);

inventory.push(...tests.map(name => `${artifact.target}::${name}`));
for (const testName of tests) {
if (options.testPrefix && !testName.startsWith(options.testPrefix)) continue;
if (shardForTest(artifact.target, testName, options.shardCount ?? 1) !== (options.shardIndex ?? 1)) continue;
console.log(`RUN ${artifact.target}::${testName}`);
// The most specific integration-test prefix wins. The shared suite
// deadline still caps every subprocess, including longer native tests.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
[CmdletBinding()]
param(
[ValidateSet("All", "Frontend", "WindowsRust", "SharedRust")]
[ValidateSet("All", "Frontend", "WindowsRust", "SharedRust", "SharedRustCore", "SharedRustHosts")]
[string]$Scope = "All",
[ValidateRange(1, 3600)]
[int]$WarnSeconds = 1,
[ValidateRange(1, 3600)]
[int]$MaxSeconds = 15,
[ValidateRange(1, 7200)]
[int]$SuiteTimeoutSeconds = 1200,
[string[]]$FrontendTestPath = @()
[string[]]$FrontendTestPath = @(),
[ValidateRange(1, 16)]
[int]$CoreShardIndex = 1,
[ValidateRange(1, 16)]
[int]$CoreShardCount = 1
)

$ErrorActionPreference = "Stop"
if ($CoreShardIndex -gt $CoreShardCount) { throw "CoreShardIndex must not exceed CoreShardCount." }
if ($WarnSeconds -ge $MaxSeconds) { throw "WarnSeconds must be lower than MaxSeconds." }

if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Expand Down Expand Up @@ -69,6 +74,7 @@ function Invoke-TimedRustTests {
$arguments += @("--package", $Package)
}
if ($Package -eq "lithe-core") {
$arguments += @("--shard-index", $CoreShardIndex, "--shard-count", $CoreShardCount)
# These cases start real Git and the production TypeScript module
# in Node. One Node startup hit its old 10s local deadline on CI.
$arguments += @("--test-budget", "git_paths_roundtrip_=60000")
Expand Down Expand Up @@ -193,7 +199,7 @@ if ($Scope -in @("All", "WindowsRust")) {
-Report (Join-Path $reportRoot "windows-rust.json")
}

if ($Scope -in @("All", "SharedRust")) {
if ($Scope -in @("All", "SharedRust", "SharedRustHosts")) {
# Dependency tests are not selected by -p lithe-core. Exercise the native
# process and AskPass adapter on Windows as a separate timed package.
Invoke-TimedRustTests `
Expand All @@ -214,6 +220,9 @@ if ($Scope -in @("All", "SharedRust")) {
-TargetDirectory "rust/target" `
-Report (Join-Path $reportRoot "ide-host-rust.json")

}

if ($Scope -in @("All", "SharedRust", "SharedRustCore")) {
Invoke-TimedRustTests `
-Manifest "rust/Cargo.toml" `
-Package "lithe-core" `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ try {
assert.ok(readFileSync(path.join(repositoryRoot, ".agents/skills/write-stable-tests/scripts/test-stability-windows.ps1"), "utf8")
.includes(`"--test-budget", "${linkedBudget}"`));
assert.ok(readFileSync(path.join(repositoryRoot, ".github/workflows/ci-windows.yml"), "utf8")
.includes(`--test-budget ${linkedBudget}`));
.includes("--test-prefix lsp::interface::engine::real_jdt_tests::"));
const report = path.join(budgetFixtureRoot, "budget.json");
const options = parseRustTimingArguments([
"--manifest", path.join(budgetFixtureRoot, "Cargo.toml"), "--report", report,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ inputs:
restore-only:
description: Restore caches without saving them at the end of the job. Cargo compilation outputs are always restore-only here; save them with save-macos-cargo-build-cache.
required: false
default: "false"
default: ${{ github.event_name == 'pull_request' }}

outputs:
cargo-build-key:
Expand Down
Loading
Loading