mirror of
https://github.com/iDvel/rime-ice.git
synced 2026-05-14 00:30:37 +08:00
Compare commits
8 Commits
85c0b0f6c2
...
2026.03.26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07eca7256d | ||
|
|
ff1da70fbd | ||
|
|
17a04afe39 | ||
|
|
3a3e76c82d | ||
|
|
dd67c351d0 | ||
|
|
fbcd597726 | ||
|
|
4e54d7edb1 | ||
|
|
20b21a0ebe |
45
.github/workflows/pages.yml
vendored
45
.github/workflows/pages.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: Deploy rime-ice with fcitx5-rime.js
|
||||
|
||||
on:
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build rime-ice
|
||||
uses: rimeinn/deploy-schema@master
|
||||
with:
|
||||
user-recipe-list: |-
|
||||
iDvel/rime-ice:others/recipes/full
|
||||
shared-recipe-list:
|
||||
package-items: |-
|
||||
build
|
||||
lua
|
||||
opencc
|
||||
custom_phrase.txt
|
||||
|
||||
- name: Download fcitx5-rime.js
|
||||
run: |
|
||||
curl -L -o fcitx5-rime.tgz https://github.com/rimeinn/fcitx5-rime.js/releases/download/latest/fcitx5-rime.tgz
|
||||
mkdir -p fcitx5-rime
|
||||
tar -xzvf fcitx5-rime.tgz -C fcitx5-rime
|
||||
|
||||
- name: Move files to publish directory
|
||||
run: |
|
||||
mkdir -p ./public/dist
|
||||
mv /tmp/deploy-schema/artifact.zip ./public/rime-ice.zip
|
||||
mv fcitx5-rime/package/dist/* ./public/dist
|
||||
cp others/pages/index.html ./public/index.html
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./public
|
||||
48
.github/workflows/release.yml
vendored
48
.github/workflows/release.yml
vendored
@@ -14,13 +14,49 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
Release:
|
||||
runs-on: ubuntu-latest
|
||||
smoke:
|
||||
name: Smoke test before release
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'iDvel/rime-ice'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install lint dependencies
|
||||
run: |
|
||||
python3 -m pip install yamllint
|
||||
sudo apt install -y luarocks
|
||||
sudo luarocks install luacheck
|
||||
|
||||
- name: Cache rime cli bundle
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/rime-cli-cache/rime-cli-linux-1.6.1.zip
|
||||
key: rime-cli-linux-1.6.1
|
||||
|
||||
- name: Run lint
|
||||
run: make -C others/script lint
|
||||
|
||||
- name: Run smoke test suite
|
||||
env:
|
||||
RIME_CLI_URL: https://github.com/mirtlecn/public/releases/download/v1.6.1/rime-cli-linux-1.6.1.zip
|
||||
RIME_CLI_CACHE_PATH: ${{ runner.temp }}/rime-cli-cache/rime-cli-linux-1.6.1.zip
|
||||
run: make -C others/script smoke
|
||||
|
||||
package:
|
||||
name: Package and release
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'iDvel/rime-ice'
|
||||
needs:
|
||||
- smoke
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Go
|
||||
if: ${{ contains(github.event.head_commit.message, ' [build]') || github.event_name == 'workflow_dispatch' }}
|
||||
@@ -56,7 +92,7 @@ jobs:
|
||||
|
||||
- name: Create nightly release
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
uses: "softprops/action-gh-release@v2"
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
## 说明
|
||||
@@ -79,7 +115,7 @@ jobs:
|
||||
|
||||
- name: Create stable release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: "softprops/action-gh-release@v2"
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
body: |
|
||||
## 说明
|
||||
@@ -108,4 +144,4 @@ jobs:
|
||||
git push
|
||||
else
|
||||
echo "No changes to commit."
|
||||
fi
|
||||
fi
|
||||
|
||||
47
.github/workflows/test.yml
vendored
Normal file
47
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Smoke test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- main
|
||||
paths:
|
||||
- "**/**"
|
||||
- "!**.md"
|
||||
- "!**.gitignore"
|
||||
- "!.github/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
smoke-linux:
|
||||
name: Rime smoke test
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'iDvel/rime-ice'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install lint dependencies
|
||||
run: |
|
||||
python3 -m pip install yamllint
|
||||
sudo apt install -y luarocks
|
||||
sudo luarocks install luacheck
|
||||
|
||||
- name: Cache rime cli bundle
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ runner.temp }}/rime-cli-cache/rime-cli-linux-1.6.1.zip
|
||||
key: rime-cli-linux-1.6.1
|
||||
|
||||
- name: Run lint
|
||||
run: make -C others/script lint
|
||||
|
||||
- name: Run smoke test suite
|
||||
env:
|
||||
RIME_CLI_URL: https://github.com/mirtlecn/public/releases/download/v1.6.1/rime-cli-linux-1.6.1.zip
|
||||
RIME_CLI_CACHE_PATH: ${{ runner.temp }}/rime-cli-cache/rime-cli-linux-1.6.1.zip
|
||||
run: make -C others/script smoke
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
为了让这个 Lua 同时适配全拼与双拼,使用 `spelling_hints` 生成的 comment(全拼拼音)作为通用的判断条件。
|
||||
感谢大佬@[Shewer Lu](https://github.com/shewer)提供的思路。
|
||||
|
||||
|
||||
容错词在 cn_dicts/others.dict.yaml 中,有新增建议可以提个 issue
|
||||
--]]
|
||||
|
||||
|
||||
@@ -61,11 +61,11 @@ function M.func(input, seg, env)
|
||||
|
||||
yield_cand(seg, os.date('%H:%M', current_time))
|
||||
yield_cand(seg, os.date('%H:%M:%S', current_time))
|
||||
yield_cand(seg, period_name .. " " .. os.date("%H:%M", current_time))
|
||||
yield_cand(seg, os.date("%H:%M %p", current_time))
|
||||
yield_cand(seg, period_name .. " " .. os.date("%I:%M", current_time))
|
||||
yield_cand(seg, os.date("%I:%M %p", current_time))
|
||||
-- 带上时间划分时,很少有带秒数的,暂时注释掉
|
||||
-- yield_cand(seg, period_name .. " " .. os.date("%H:%M:%S", current_time))
|
||||
-- yield_cand(seg, os.date("%H:%M:%S %p", current_time))
|
||||
-- yield_cand(seg, period_name .. " " .. os.date("%I:%M:%S", current_time))
|
||||
-- yield_cand(seg, os.date("%I:%M:%S %p", current_time))
|
||||
|
||||
-- 星期
|
||||
elseif (input == M.week) then
|
||||
@@ -76,10 +76,21 @@ function M.func(input, seg, env)
|
||||
yield_cand(seg, '礼拜' .. text)
|
||||
yield_cand(seg, '周' .. text)
|
||||
|
||||
-- ISO 8601/RFC 3339 的时间格式 (固定东八区)(示例 2022-01-07T20:42:51+08:00)
|
||||
-- ISO 8601/RFC 3339 的时间格式
|
||||
elseif (input == M.datetime) then
|
||||
local current_time = os.time()
|
||||
yield_cand(seg, os.date('%Y-%m-%dT%H:%M:%S+08:00', current_time))
|
||||
|
||||
-- 获取偏移量,例如 "+0800" 或 "+0000"
|
||||
local tz = os.date("%z", current_time)
|
||||
|
||||
-- 格式化时区
|
||||
-- 如果是零时区 (+0000/-0000),根据规范输出 "Z"
|
||||
-- 否则将 "+0800" 转换为 "+08:00"
|
||||
local iso_tz = (tz == "+0000" or tz == "-0000")
|
||||
and "Z"
|
||||
or tz:gsub("(%d%d)$", ":%1")
|
||||
|
||||
yield_cand(seg, os.date('%Y-%m-%dT%H:%M:%S', current_time) .. iso_tz)
|
||||
yield_cand(seg, os.date('%Y-%m-%d %H:%M:%S', current_time))
|
||||
yield_cand(seg, os.date('%Y%m%d%H%M%S', current_time))
|
||||
|
||||
@@ -116,7 +127,7 @@ function M.func(input, seg, env)
|
||||
-- suffix = "rd"
|
||||
-- end
|
||||
|
||||
yield_cand(seg, string.format("%d %s %s", day, month_names_long[month], year)) -- en_UK
|
||||
yield_cand(seg, string.format("%d %s %s", day, month_names_long[month], year)) -- en_UK
|
||||
yield_cand(seg, string.format("%s %d, %s", month_names_long[month], day, year)) -- en_US
|
||||
end
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
-- 英文词条上屏自动添加空格
|
||||
-- 在 engine/filters 的倒数第二个位置,增加 - lua_filter@*en_spacer
|
||||
--
|
||||
-- 英文后,再输入英文单词(必须为候选项)自动添加空格
|
||||
local F = {}
|
||||
|
||||
|
||||
@@ -63,8 +63,6 @@ end
|
||||
|
||||
-- 数值转换为中文
|
||||
local function number2cnChar(num, flag, digitUnit, wordFigure) --flag=0中文小写反之为大写
|
||||
local result = ""
|
||||
|
||||
if tonumber(flag) < 1 then
|
||||
digitUnit = digitUnit or { [1] = "万", [2] = "亿" }
|
||||
wordFigure = wordFigure or { [1] = "〇", [2] = "一", [3] = "十", [4] = "元" }
|
||||
@@ -73,6 +71,7 @@ local function number2cnChar(num, flag, digitUnit, wordFigure) --flag=0中文小
|
||||
wordFigure = wordFigure or { [1] = "零", [2] = "壹", [3] = "拾", [4] = "元" }
|
||||
end
|
||||
local lens = string.len(num)
|
||||
local result
|
||||
if lens < 5 then
|
||||
result = formatNum(num, flag)
|
||||
elseif lens < 9 then
|
||||
|
||||
@@ -126,9 +126,9 @@ function M.init(env)
|
||||
-- 额外处理包含空格的 preedit,增加最后一个拼音的首字母和 zh, ch, sh 的简码
|
||||
if preedit:find(" ") then
|
||||
local preceding_part, last_part = preedit:match("^(.+)%s(%S+)$")
|
||||
local p1, p2 = "", ""
|
||||
local p2 = ""
|
||||
-- p1 生成最后一个拼音的首字母简码拼写(最后一个空格后的首字母),如 ni hao 生成 nih
|
||||
p1 = preceding_part:gsub(" ", "") .. last_part:sub(1, 1)
|
||||
local p1 = preceding_part:gsub(" ", "") .. last_part:sub(1, 1)
|
||||
-- p2 生成最后一个拼音的 zh, ch, sh 的简码拼写(最后一个空格后以 zh ch sh 开头),如 zhi chi 生成 zhich
|
||||
if last_part:match("^[zcs]h") then
|
||||
p2 = preceding_part:gsub(" ", "") .. last_part:sub(1, 2)
|
||||
|
||||
@@ -45,9 +45,7 @@ local function update_dict_entry( s, code, mem, proj )
|
||||
local code_convert = code:sub( i, i + 1 )
|
||||
local p = proj:apply( code_convert, true )
|
||||
if p and #p > 0 then code_convert = p end
|
||||
if code_convert == 'dian' and pos[loop] then
|
||||
-- Ignored
|
||||
else
|
||||
if code_convert ~= 'dian' or not pos[loop] then
|
||||
table.insert( custom_code, code_convert )
|
||||
end
|
||||
loop = loop + 1
|
||||
@@ -283,12 +281,12 @@ function f.func( input, env )
|
||||
end
|
||||
|
||||
-- 上屏其余的候选
|
||||
for i, cand in ipairs( long_word_cands ) do yield( cand ) end
|
||||
if env.show_other_cands then for i, cand in ipairs( other_cand ) do yield( cand ) end end
|
||||
for _, cand in ipairs( long_word_cands ) do yield( cand ) end
|
||||
if env.show_other_cands then for _, cand in ipairs( other_cand ) do yield( cand ) end end
|
||||
end
|
||||
|
||||
function f.tags_match( seg, env )
|
||||
for i, v in ipairs( env.tag ) do if seg.tags[v] then return true end end
|
||||
for _, v in ipairs( env.tag ) do if seg.tags[v] then return true end end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ function select.func(key, env)
|
||||
local selected_candidate = context:get_selected_candidate()
|
||||
selected_candidate = selected_candidate and selected_candidate.text or input
|
||||
|
||||
local selected_char = ""
|
||||
local selected_char
|
||||
if (key:repr() == env.first_key) then
|
||||
selected_char = selected_candidate:sub(1, utf8.offset(selected_candidate, 2) - 1)
|
||||
elseif (key:repr() == env.last_key) then
|
||||
|
||||
@@ -26,8 +26,8 @@ local function unicode(input, seg, env)
|
||||
yield(Candidate("unicode", seg.start, seg._end, text, string.format("U%x", code)))
|
||||
if code < 0x10000 then
|
||||
for i = 0, 15 do
|
||||
local text = utf8.char(code * 16 + i)
|
||||
yield(Candidate("unicode", seg.start, seg._end, text, string.format("U%x~%x", code, i)))
|
||||
local next_text = utf8.char(code * 16 + i)
|
||||
yield(Candidate("unicode", seg.start, seg._end, next_text, string.format("U%x~%x", code, i)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,35 @@
|
||||
|
||||
除日常更新词库外的一些主要更新 🆕、破坏性变更 ⚠️。
|
||||
|
||||
---
|
||||
|
||||
*2026.03.26 Release*
|
||||
|
||||
## 2026-03-26
|
||||
|
||||
- test: 新增自动化测试和 yaml / lua 语法检查。用例覆盖主流程,在提交时运行。
|
||||
|
||||
## 2026-03-21
|
||||
|
||||
- fix(lua): 修复时区问题(#1501)
|
||||
|
||||
## 2026-03-11
|
||||
|
||||
- dict(radical):拆字词典覆盖拓展区 b - j 汉字
|
||||
|
||||
## 2026-03-08
|
||||
|
||||
- feat: 加入一些时间和日期的自定义展示 [#1485](https://github.com/iDvel/rime-ice/pull/1485)
|
||||
- 中英日期触发关键字,全拼默认为 `rqzh` 和 `rqen`,双拼默认为 `datezh` 和 `dateen`
|
||||
|
||||
## 2026-01-26
|
||||
|
||||
- dict(symbol):删除了 V/v 模式中的 emoji 集合
|
||||
|
||||
## 2026-01-05
|
||||
|
||||
- docs: 添加在线试用链接,由 @Mintimate 友情构建
|
||||
|
||||
---
|
||||
|
||||
*2025.12.08 Release*
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
### 金钱符号
|
||||
¥ 人民币 日元 日币
|
||||
$ 美元 美刀
|
||||
¢ 美分
|
||||
HK$ 港元 港币
|
||||
MOP$ 澳门元 澳门币 葡币
|
||||
S$ 新加坡元 新加坡币
|
||||
@@ -63,7 +64,6 @@ S$ 新加坡元 新加坡币
|
||||
₨ 卢比
|
||||
₹ 印度卢比
|
||||
|
||||
|
||||
### Smileys & Emotion
|
||||
# face-smiling
|
||||
😀 笑脸
|
||||
|
||||
24
others/script/Makefile
Normal file
24
others/script/Makefile
Normal file
@@ -0,0 +1,24 @@
|
||||
.DEFAULT_GOAL := default
|
||||
|
||||
.PHONY: default build lint lint-yaml lint-lua lint-lua-format smoke
|
||||
|
||||
default:
|
||||
@:
|
||||
|
||||
build:
|
||||
go mod tidy && go run . --rime_path "$$(cd ../.. && pwd)" --auto_confirm
|
||||
|
||||
lint:
|
||||
bash lint/run.sh all
|
||||
|
||||
lint-yaml:
|
||||
bash lint/run.sh yaml-lint
|
||||
|
||||
lint-lua:
|
||||
bash lint/run.sh lua-lint
|
||||
|
||||
lint-lua-format:
|
||||
bash lint/run.sh lua-format-check
|
||||
|
||||
smoke:
|
||||
bash ./smoke/run.sh rime_ice
|
||||
37
others/script/lint/.luacheckrc
Normal file
37
others/script/lint/.luacheckrc
Normal file
@@ -0,0 +1,37 @@
|
||||
std = "lua54"
|
||||
|
||||
globals = {
|
||||
"Candidate",
|
||||
"Component",
|
||||
"ConfigValue",
|
||||
"DictEntry",
|
||||
"KeyEvent",
|
||||
"KeySequence",
|
||||
"LevelDb",
|
||||
"Memory",
|
||||
"Opencc",
|
||||
"Projection",
|
||||
"ReverseLookup",
|
||||
"Schema",
|
||||
"Segment",
|
||||
"Set",
|
||||
"ShadowCandidate",
|
||||
"Spans",
|
||||
"Switcher",
|
||||
"TableDb",
|
||||
"UniquifiedCandidate",
|
||||
"UserDb",
|
||||
"yield",
|
||||
"log",
|
||||
"rime_api",
|
||||
}
|
||||
|
||||
ignore = {
|
||||
"111",
|
||||
"121",
|
||||
"142",
|
||||
"143",
|
||||
"211",
|
||||
"212",
|
||||
"631",
|
||||
}
|
||||
20
others/script/lint/.yamllint.yml
Normal file
20
others/script/lint/.yamllint.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
braces: disable
|
||||
brackets: disable
|
||||
colons: disable
|
||||
commas: disable
|
||||
comments: disable
|
||||
comments-indentation: disable
|
||||
document-start: disable
|
||||
empty-lines:
|
||||
max: 2
|
||||
max-end: 1
|
||||
max-start: 0
|
||||
hyphens: disable
|
||||
indentation:
|
||||
indent-sequences: consistent
|
||||
line-length: disable
|
||||
new-line-at-end-of-file: disable
|
||||
truthy: disable
|
||||
44
others/script/lint/README.md
Normal file
44
others/script/lint/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# YAML and Lua lint entrypoint
|
||||
|
||||
This directory contains the lightweight lint entrypoint used by local commands.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Current stage:
|
||||
|
||||
- `yamllint`
|
||||
- `luacheck`
|
||||
|
||||
Example install on macOS:
|
||||
|
||||
```bash
|
||||
brew install yamllint luacheck
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
From the repository root:
|
||||
|
||||
```bash
|
||||
bash others/script/lint/run.sh yaml-lint
|
||||
bash others/script/lint/run.sh lua-lint
|
||||
bash others/script/lint/run.sh all
|
||||
make -C others/script lint-yaml
|
||||
make -C others/script lint-lua
|
||||
make -C others/script lint
|
||||
make -C others/script smoke
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The current implementation enables YAML linting and Lua linting.
|
||||
- The first stage checks repository-root business YAML files and root
|
||||
`*.schema.yaml` files.
|
||||
- `*.schema.yaml` files are preprocessed before linting so the `pin_cand_filter`
|
||||
tab-separated list does not break generic YAML parsing.
|
||||
- `lua-lint` runs `luacheck` with a repository-local configuration derived from
|
||||
the librime-lua globals currently used by this repository.
|
||||
- `lua-format-check` is reserved for a later stage.
|
||||
- `smoke` mirrors the current CI smoke invocation and runs
|
||||
`bash ./others/script/smoke/run.sh rime_ice` through Make.
|
||||
- GitHub Actions are intentionally not changed in this stage.
|
||||
230
others/script/lint/run.sh
Normal file
230
others/script/lint/run.sh
Normal file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
||||
YAMLLINT_CONFIG="${SCRIPT_DIR}/.yamllint.yml"
|
||||
LUACHECK_CONFIG="${SCRIPT_DIR}/.luacheckrc"
|
||||
|
||||
log() {
|
||||
printf '[lint] %s\n' "$*"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
printf '[lint] 🔹 %s\n' "$*"
|
||||
}
|
||||
|
||||
log_pass() {
|
||||
printf '[lint] ✅ %s\n' "$*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
printf '[lint] ⚠️ %s\n' "$*"
|
||||
}
|
||||
|
||||
log_fail() {
|
||||
printf '[lint] 🔴 %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
find_business_yaml_files() {
|
||||
(
|
||||
cd "${REPO_ROOT}"
|
||||
find . \
|
||||
\( -path "./.git" -o -path "./.git/*" -o -path "./.github" -o -path "./.github/*" -o -path "./others" -o -path "./others/*" \) -prune \
|
||||
-o \
|
||||
\( -maxdepth 1 -type f \( -name "*.yaml" -o -name "*.yml" \) ! -name "*.dict.yaml" -print \)
|
||||
) | sort
|
||||
}
|
||||
|
||||
sanitize_schema_yaml() {
|
||||
local source_file="$1"
|
||||
local output_file="$2"
|
||||
|
||||
awk '
|
||||
function indent_width(line) {
|
||||
match(line, /^ */)
|
||||
return RLENGTH
|
||||
}
|
||||
|
||||
BEGIN {
|
||||
in_pin_cand_filter = 0
|
||||
pin_indent = -1
|
||||
}
|
||||
|
||||
{
|
||||
if (!in_pin_cand_filter) {
|
||||
if ($0 ~ /^ *pin_cand_filter:[[:space:]]*$/) {
|
||||
in_pin_cand_filter = 1
|
||||
pin_indent = indent_width($0)
|
||||
}
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*#/) {
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
current_indent = indent_width($0)
|
||||
if (current_indent <= pin_indent) {
|
||||
in_pin_cand_filter = 0
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /^[[:space:]]*-[[:space:]]/) {
|
||||
print substr($0, 1, current_indent) "- __PIN_CAND_FILTER_PLACEHOLDER__"
|
||||
next
|
||||
}
|
||||
|
||||
print
|
||||
}
|
||||
' "${source_file}" > "${output_file}"
|
||||
}
|
||||
|
||||
require_tool() {
|
||||
local tool_name="$1"
|
||||
if ! command -v "${tool_name}" >/dev/null 2>&1; then
|
||||
log_fail "missing required tool: ${tool_name}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_yaml_lint() {
|
||||
require_tool yamllint
|
||||
|
||||
local yaml_files=()
|
||||
local temp_dir=''
|
||||
local lint_status=0
|
||||
local total_start_time
|
||||
total_start_time="$(date +%s)"
|
||||
while IFS= read -r yaml_file; do
|
||||
yaml_files+=("${yaml_file}")
|
||||
done < <(find_business_yaml_files)
|
||||
|
||||
if [[ "${#yaml_files[@]}" -eq 0 ]]; then
|
||||
log_warn 'no business YAML files found'
|
||||
return 0
|
||||
fi
|
||||
|
||||
temp_dir="$(mktemp -d)"
|
||||
trap 'if [[ -n "${temp_dir:-}" ]]; then rm -rf "${temp_dir}"; fi' RETURN
|
||||
|
||||
log_step "yaml-lint checking ${#yaml_files[@]} file(s)"
|
||||
|
||||
for yaml_file in "${yaml_files[@]}"; do
|
||||
local lint_target="${yaml_file}"
|
||||
local lint_output=''
|
||||
local file_start_time
|
||||
local file_elapsed
|
||||
file_start_time="$(date +%s)"
|
||||
|
||||
log_step "yaml-lint checking ${yaml_file}"
|
||||
|
||||
if [[ "${yaml_file}" == *.schema.yaml ]]; then
|
||||
lint_target="${temp_dir}/$(basename "${yaml_file}")"
|
||||
sanitize_schema_yaml "${REPO_ROOT}/${yaml_file#./}" "${lint_target}"
|
||||
fi
|
||||
|
||||
if ! lint_output="$(cd "${REPO_ROOT}" && yamllint -f parsable -c "${YAMLLINT_CONFIG}" "${lint_target}" 2>&1)"; then
|
||||
lint_status=1
|
||||
if [[ "${lint_target}" != "${yaml_file}" ]]; then
|
||||
lint_output="${lint_output//${lint_target}/${yaml_file}}"
|
||||
fi
|
||||
printf '%s\n' "${lint_output}"
|
||||
log_fail "yaml-lint failed for ${yaml_file}"
|
||||
fi
|
||||
|
||||
file_elapsed="$(( $(date +%s) - file_start_time ))"
|
||||
log_pass "yaml-lint finished ${yaml_file} in ${file_elapsed}s"
|
||||
done
|
||||
|
||||
log_pass "yaml-lint completed in $(( $(date +%s) - total_start_time ))s"
|
||||
|
||||
return "${lint_status}"
|
||||
}
|
||||
|
||||
run_lua_lint() {
|
||||
require_tool luacheck
|
||||
local lint_output=''
|
||||
local lint_status=0
|
||||
local last_line=''
|
||||
local normalized_last_line=''
|
||||
log_step 'lua-lint checking lua/**/*.lua'
|
||||
|
||||
set +e
|
||||
lint_output="$(
|
||||
(
|
||||
cd "${REPO_ROOT}"
|
||||
luacheck lua --no-default-config --config "${LUACHECK_CONFIG}" --codes --ranges
|
||||
) 2>&1
|
||||
)"
|
||||
lint_status=$?
|
||||
set -e
|
||||
|
||||
printf '%s\n' "${lint_output}"
|
||||
last_line="$(printf '%s\n' "${lint_output}" | tail -n 1)"
|
||||
normalized_last_line="$(printf '%s\n' "${last_line}" | sed -E $'s/\x1B\\[[0-9;]*[[:alpha:]]//g')"
|
||||
if [[ "${lint_status}" -eq 0 ]]; then
|
||||
log_pass 'lua-lint passed'
|
||||
return 0
|
||||
fi
|
||||
|
||||
if printf '%s\n' "${normalized_last_line}" | grep -Eq '0 errors?'; then
|
||||
log_warn 'lua-lint completed with warnings'
|
||||
return 0
|
||||
fi
|
||||
log_fail 'lua-lint completed with errors'
|
||||
return 1
|
||||
}
|
||||
|
||||
run_lua_format_check() {
|
||||
log_warn 'lua-format-check is not enabled yet'
|
||||
return 1
|
||||
}
|
||||
|
||||
run_all() {
|
||||
run_yaml_lint
|
||||
run_lua_lint
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: bash others/script/lint/run.sh <command>
|
||||
|
||||
Commands:
|
||||
yaml-lint
|
||||
lua-lint
|
||||
lua-format-check
|
||||
all
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
local command="${1:-}"
|
||||
case "${command}" in
|
||||
yaml-lint)
|
||||
run_yaml_lint
|
||||
;;
|
||||
lua-lint)
|
||||
run_lua_lint
|
||||
;;
|
||||
lua-format-check)
|
||||
run_lua_format_check
|
||||
;;
|
||||
all)
|
||||
run_all
|
||||
;;
|
||||
help|-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
usage >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
54
others/script/smoke/README.md
Normal file
54
others/script/smoke/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Smoke Test Framework
|
||||
|
||||
This directory contains the shell-based smoke test framework for the current repository.
|
||||
|
||||
## Layout
|
||||
|
||||
- `run.sh`: suite entrypoint
|
||||
- `lib/common.sh`: shared shell helpers
|
||||
- `suites/config_repo.sh`: generic suite implementation for the current config repository
|
||||
- `cases/rime_ice/input_cases.tsv`: data-driven input cases
|
||||
|
||||
## Current Flow
|
||||
|
||||
- uses local `rime_deployer` and `rime_api_console` from `PATH` when available
|
||||
- otherwise downloads the public Linux CLI bundle when `RIME_CLI_URL` is set
|
||||
- deploys the current repository with `rime_deployer --build`
|
||||
- runs `rime_api_console`
|
||||
- verifies basic pinyin commit and a stable Lua-driven Unicode commit
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `RIME_CLI_URL`: optional public CLI bundle URL
|
||||
- `RIME_CONFIG_ROOT`: optional repository root override
|
||||
- `SMOKE_ALLOW_DESTRUCTIVE=1`: required for local runs because the smoke suite removes `${RIME_CONFIG_ROOT:-repo}/build` and `${RIME_CONFIG_ROOT:-repo}/*.userdb`
|
||||
|
||||
## Destructive Cleanup
|
||||
|
||||
The smoke suite removes the following paths under `RIME_CONFIG_ROOT` before deployment:
|
||||
|
||||
- `build/`
|
||||
- `*.userdb/`
|
||||
|
||||
This cleanup is allowed automatically in CI. Local runs must opt in explicitly:
|
||||
|
||||
```bash
|
||||
SMOKE_ALLOW_DESTRUCTIVE=1 ./others/script/smoke/run.sh
|
||||
```
|
||||
|
||||
## Extending
|
||||
|
||||
Add more rows to `cases/rime_ice/input_cases.tsv`.
|
||||
The current tab-separated case format is:
|
||||
|
||||
- `case_id`
|
||||
- `schema_id`
|
||||
- `key_sequence`
|
||||
- `expected_text`
|
||||
|
||||
`expected_text` also supports:
|
||||
|
||||
- `@today:<date format>`, for example `@today:%Y-%m-%d`
|
||||
- `@regex:<pattern>`, matched against the normalized stdout log
|
||||
|
||||
`rime_api_console` is used as the default runner because it is more reliable than `rime_console` for smoke tests that reuse an already deployed workspace.
|
||||
224
others/script/smoke/cases/rime_ice/input_cases.tsv
Normal file
224
others/script/smoke/cases/rime_ice/input_cases.tsv
Normal file
@@ -0,0 +1,224 @@
|
||||
# ------------------
|
||||
# 方案:rime_ice
|
||||
# ------ 基础 ------
|
||||
# case_id schema_id key_sequence expected_text
|
||||
基础:中文输入 rime_ice wusongpinyin{space} 雾凇拼音
|
||||
基础:英文输入 rime_ice hello{space} hello
|
||||
基础:英文派生 rime_ice PtwoP{space} P2P
|
||||
基础:中英混输 rime_ice Xguang{space} X光
|
||||
基础:自定义短语 rime_ice ig{space} 一个
|
||||
基础:Emoji输入 rime_ice nihao{2} 👋
|
||||
基础:uU拆字反查 rime_ice uUrenrenren{space} 众
|
||||
基础:v键符号输入 rime_ice v1{space} 一
|
||||
插件:英文全大写 rime_ice HELLO{space} HELLO
|
||||
插件:英文首字母大写 rime_ice Hello{space} Hello
|
||||
插件:置顶候选 rime_ice d{space} 的
|
||||
插件:Unicode输入 rime_ice U4f60{space} 你
|
||||
插件:日期输入 rime_ice rq{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 rime_ice uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 rime_ice ranch{space} 染成
|
||||
插件:英文降权 rime_ice ranch{2} ranch
|
||||
插件:数字输入 rime_ice R123{space} 一百二十三
|
||||
插件:计算器 rime_ice cC1+2{space} 3
|
||||
插件:农历输入:农历 rime_ice N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 rime_ice N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 rime_ice nihao{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 rime_ice nihao{bracketright} 好
|
||||
插件:部件辅码 rime_ice ni`ren{space} 你
|
||||
插件:错字错音提示 rime_ice qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:自然码双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin ig{space} 一个
|
||||
基础:中文输入 double_pinyin wusspnyn{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin hello{space} hello
|
||||
基础:英文派生 double_pinyin PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin Xgd{space} X光
|
||||
基础:Emoji输入 double_pinyin nihk{2} 👋
|
||||
基础:uU拆字反查 double_pinyin uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin V1{space} 一
|
||||
插件:英文全大写 double_pinyin HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin d{space} 的
|
||||
插件:Unicode输入 double_pinyin U4f60{space} 你
|
||||
插件:日期输入 double_pinyin date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 double_pinyin bail{space} 把拆
|
||||
插件:英文降权 double_pinyin bail{2} bail
|
||||
插件:数字输入 double_pinyin R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin nihk{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin nihk{bracketright} 好
|
||||
插件:部件辅码 double_pinyin ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:智能 ABC 双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_abc ig{space} 一个
|
||||
# 插件:英文降权 double_pinyin_abc bail{space} 不确定
|
||||
# 插件:英文降权 double_pinyin_abc bail{2} 不确定
|
||||
基础:中文输入 double_pinyin_abc wusspcyc{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_abc hello{space} hello
|
||||
基础:英文派生 double_pinyin_abc PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_abc Xgt{space} X光
|
||||
基础:Emoji输入 double_pinyin_abc nihk{2} 👋
|
||||
基础:uU拆字反查 double_pinyin_abc uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_abc V1{space} 一
|
||||
插件:英文全大写 double_pinyin_abc HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_abc Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_abc d{space} 的
|
||||
插件:Unicode输入 double_pinyin_abc U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_abc date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_abc uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:数字输入 double_pinyin_abc R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_abc cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_abc N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_abc N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_abc nihk{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_abc nihk{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_abc ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_abc qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:微软双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_mspy ig{space} 一个
|
||||
基础:中文输入 double_pinyin_mspy wusspnyn{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_mspy hello{space} hello
|
||||
基础:英文派生 double_pinyin_mspy PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_mspy Xgd{space} X光
|
||||
基础:Emoji输入 double_pinyin_mspy nihk{2} 👋
|
||||
基础:uU拆字反查 double_pinyin_mspy uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_mspy V1{space} 一
|
||||
插件:英文全大写 double_pinyin_mspy HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_mspy Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_mspy d{space} 的
|
||||
插件:Unicode输入 double_pinyin_mspy U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_mspy date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_mspy uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 double_pinyin_mspy bail{space} 把拆
|
||||
插件:英文降权 double_pinyin_mspy bail{2} bail
|
||||
插件:数字输入 double_pinyin_mspy R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_mspy cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_mspy N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_mspy N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_mspy nihk{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_mspy nihk{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_mspy ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_mspy qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:搜狗双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_sogou ig{space} 一个
|
||||
基础:中文输入 double_pinyin_sogou wusspnyn{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_sogou hello{space} hello
|
||||
基础:英文派生 double_pinyin_sogou PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_sogou Xgd{space} X光
|
||||
基础:Emoji输入 double_pinyin_sogou nihk{2} 👋
|
||||
基础:uU拆字反查 double_pinyin_sogou uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_sogou V1{space} 一
|
||||
插件:英文全大写 double_pinyin_sogou HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_sogou Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_sogou d{space} 的
|
||||
插件:Unicode输入 double_pinyin_sogou U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_sogou date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_sogou uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 double_pinyin_sogou bail{space} 把拆
|
||||
插件:英文降权 double_pinyin_sogou bail{2} bail
|
||||
插件:数字输入 double_pinyin_sogou R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_sogou cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_sogou N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_sogou N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_sogou nihk{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_sogou nihk{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_sogou ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_sogou qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:紫光双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_ziguang ig{space} 一个
|
||||
基础:中文输入 double_pinyin_ziguang wushpyyy{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_ziguang hello{space} hello
|
||||
基础:英文派生 double_pinyin_ziguang PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_ziguang Xgg{space} X光
|
||||
基础:Emoji输入 double_pinyin_ziguang nihq{2} 👋
|
||||
基础:uU拆字反查 double_pinyin_ziguang uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_ziguang V1{space} 一
|
||||
插件:英文全大写 double_pinyin_ziguang HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_ziguang Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_ziguang d{space} 的
|
||||
插件:Unicode输入 double_pinyin_ziguang U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_ziguang date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_ziguang uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 double_pinyin_ziguang bail{space} 把栓
|
||||
插件:英文降权 double_pinyin_ziguang bail{2} bail
|
||||
插件:数字输入 double_pinyin_ziguang R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_ziguang cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_ziguang N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_ziguang N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_ziguang nihq{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_ziguang nihq{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_ziguang ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_ziguang qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:拼音加加双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_jiajia ig{space} 一个
|
||||
# 插件:英文降权 double_pinyin_jiajia bail{space} 不确定
|
||||
# 插件:英文降权 double_pinyin_jiajia bail{2} 不确定
|
||||
基础:中文输入 double_pinyin_jiajia wusyplyl{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_jiajia hello{space} hello
|
||||
基础:英文派生 double_pinyin_jiajia PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_jiajia Xgh{space} X光
|
||||
基础:Emoji输入 double_pinyin_jiajia nihd{2} 👋
|
||||
基础:uU拆字反查 double_pinyin_jiajia uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_jiajia V1{space} 一
|
||||
插件:英文全大写 double_pinyin_jiajia HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_jiajia Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_jiajia d{space} 的
|
||||
插件:Unicode输入 double_pinyin_jiajia U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_jiajia date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_jiajia uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:数字输入 double_pinyin_jiajia R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_jiajia cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_jiajia N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_jiajia N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_jiajia nihd{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_jiajia nihd{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_jiajia ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_jiajia qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
||||
# ------------------
|
||||
# 方案:小鹤双拼
|
||||
# ------ 基础 ------
|
||||
# 基础:自定义短语 double_pinyin_flypy ig{space} 一个
|
||||
基础:Emoji输入 double_pinyin_flypy nihc{2} 👋
|
||||
基础:中文输入 double_pinyin_flypy wusspbyb{space} 雾凇拼音
|
||||
基础:英文输入 double_pinyin_flypy hello{space} hello
|
||||
基础:英文派生 double_pinyin_flypy PtwoP{space} P2P
|
||||
基础:中英混输 double_pinyin_flypy Xgl{space} X光
|
||||
基础:uU拆字反查 double_pinyin_flypy uUrenrenren{space} 众
|
||||
基础:v键符号输入 double_pinyin_flypy V1{space} 一
|
||||
插件:英文全大写 double_pinyin_flypy HELLO{space} HELLO
|
||||
插件:英文首字母大写 double_pinyin_flypy Hello{space} Hello
|
||||
插件:置顶候选 double_pinyin_flypy d{space} 的
|
||||
插件:Unicode输入 double_pinyin_flypy U4f60{space} 你
|
||||
插件:日期输入 double_pinyin_flypy date{space} @today:%Y-%m-%d
|
||||
插件:UUID输入 double_pinyin_flypy uuid{space} @regex:^commit: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$
|
||||
插件:英文降权 double_pinyin_flypy bail{space} 把床
|
||||
插件:英文降权 double_pinyin_flypy bail{2} bail
|
||||
插件:数字输入 double_pinyin_flypy R123{space} 一百二十三
|
||||
插件:计算器 double_pinyin_flypy cC1+2{space} 3
|
||||
插件:农历输入:农历 double_pinyin_flypy N19700101{space} 一九六九年冬月廿四
|
||||
插件:农历输入:干支 double_pinyin_flypy N19700101{2} 己酉年(鸡)冬月廿四
|
||||
插件:以词定字:左中括号取首字 double_pinyin_flypy nihc{bracketleft} 你
|
||||
插件:以词定字:右中括号取尾字 double_pinyin_flypy nihc{bracketright} 好
|
||||
插件:部件辅码 double_pinyin_flypy ni`ren{space} 你
|
||||
插件:错字错音提示 double_pinyin_flypy qikefu{Control+Shift+Return} 契诃(hē)夫
|
||||
|
Can't render this file because it has a wrong number of fields in line 4.
|
273
others/script/smoke/lib/common.sh
Normal file
273
others/script/smoke/lib/common.sh
Normal file
@@ -0,0 +1,273 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -z "${SMOKE_ROOT:-}" ]]; then
|
||||
SMOKE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.."; pwd)"
|
||||
fi
|
||||
if [[ -z "${SMOKE_REPO_ROOT:-}" ]]; then
|
||||
SMOKE_REPO_ROOT="$(cd "${SMOKE_ROOT}/../../.."; pwd)"
|
||||
fi
|
||||
if [[ -z "${SMOKE_WORK_ROOT:-}" ]]; then
|
||||
SMOKE_WORK_ROOT="${RUNNER_TEMP:-/tmp}/rime-ice-smoke"
|
||||
fi
|
||||
if [[ -z "${SMOKE_LOG_ROOT:-}" ]]; then
|
||||
SMOKE_LOG_ROOT="${SMOKE_WORK_ROOT}/logs"
|
||||
fi
|
||||
|
||||
SMOKE_CURRENT_LABEL="${SMOKE_CURRENT_LABEL:-}"
|
||||
SMOKE_CURRENT_LOG_FILE="${SMOKE_CURRENT_LOG_FILE:-}"
|
||||
|
||||
log() {
|
||||
printf '[smoke] %s\n' "$*"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
printf '[smoke] 🔹 %s\n' "$*"
|
||||
}
|
||||
|
||||
log_pass() {
|
||||
printf '[smoke] ✅ %s\n' "$*"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
printf '[smoke] ⚠️ %s\n' "$*"
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '[smoke] 🔴 %s\n' "$*" >&2
|
||||
if [[ -n "${SMOKE_CURRENT_LABEL}" ]]; then
|
||||
printf '[smoke][context] %s\n' "${SMOKE_CURRENT_LABEL}" >&2
|
||||
fi
|
||||
if [[ -n "${SMOKE_CURRENT_LOG_FILE}" && -f "${SMOKE_CURRENT_LOG_FILE}" ]]; then
|
||||
printf '[smoke][log] begin %s\n' "${SMOKE_CURRENT_LOG_FILE}" >&2
|
||||
sed -n '1,240p' "${SMOKE_CURRENT_LOG_FILE}" >&2
|
||||
printf '[smoke][log] end %s\n' "${SMOKE_CURRENT_LOG_FILE}" >&2
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
set_failure_context() {
|
||||
local label="$1"
|
||||
local log_file="$2"
|
||||
SMOKE_CURRENT_LABEL="${label}"
|
||||
SMOKE_CURRENT_LOG_FILE="${log_file}"
|
||||
}
|
||||
|
||||
clear_failure_context() {
|
||||
SMOKE_CURRENT_LABEL=""
|
||||
SMOKE_CURRENT_LOG_FILE=""
|
||||
}
|
||||
|
||||
require_command() {
|
||||
local command_name="$1"
|
||||
command -v "${command_name}" >/dev/null 2>&1 ||
|
||||
fail "required command not found: ${command_name}"
|
||||
}
|
||||
|
||||
ensure_clean_dir() {
|
||||
local dir_path="$1"
|
||||
rm -rf "${dir_path}"
|
||||
mkdir -p "${dir_path}"
|
||||
}
|
||||
|
||||
require_destructive_cleanup_approval() {
|
||||
local config_root="$1"
|
||||
local build_dir="${config_root}/build"
|
||||
|
||||
if [[ "${CI:-}" == "true" || "${GITHUB_ACTIONS:-}" == "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "${SMOKE_ALLOW_DESTRUCTIVE:-}" == "1" ]]; then
|
||||
log_warn "destructive cleanup approved by SMOKE_ALLOW_DESTRUCTIVE=1"
|
||||
return 0
|
||||
fi
|
||||
|
||||
fail "smoke test will remove ${build_dir} and ${config_root}/*.userdb; rerun with SMOKE_ALLOW_DESTRUCTIVE=1 for local execution, or run in CI"
|
||||
}
|
||||
|
||||
clean_config_artifacts() {
|
||||
local config_root="$1"
|
||||
local build_dir="${config_root}/build"
|
||||
|
||||
if [[ -d "${build_dir}" ]]; then
|
||||
log_step "removing ${build_dir}"
|
||||
rm -rf "${build_dir}"
|
||||
fi
|
||||
|
||||
find "${config_root}" -mindepth 1 -maxdepth 1 -type d -name '*.userdb' -print0 |
|
||||
while IFS= read -r -d '' userdb_dir; do
|
||||
log_step "removing ${userdb_dir}"
|
||||
rm -rf "${userdb_dir}"
|
||||
done
|
||||
}
|
||||
|
||||
download_file() {
|
||||
local file_url="$1"
|
||||
local output_path="$2"
|
||||
curl -fsSL --retry 3 -o "${output_path}" "${file_url}"
|
||||
}
|
||||
|
||||
prepare_cli_archive() {
|
||||
local cli_url="$1"
|
||||
local output_path="$2"
|
||||
local cache_path="${RIME_CLI_CACHE_PATH:-}"
|
||||
|
||||
if [[ -n "${cache_path}" && -f "${cache_path}" ]]; then
|
||||
log_step "using cached rime cli bundle ${cache_path}" >&2
|
||||
cp "${cache_path}" "${output_path}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_step "downloading rime cli bundle" >&2
|
||||
download_file "${cli_url}" "${output_path}"
|
||||
|
||||
if [[ -n "${cache_path}" ]]; then
|
||||
mkdir -p "$(dirname "${cache_path}")"
|
||||
cp "${output_path}" "${cache_path}"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_zip() {
|
||||
local archive_path="$1"
|
||||
local output_dir="$2"
|
||||
unzip -q "${archive_path}" -d "${output_dir}"
|
||||
}
|
||||
|
||||
prepare_cli_bundle() {
|
||||
local archive_path="$1"
|
||||
local output_dir="$2"
|
||||
local nested_archive_path
|
||||
local bundle_root
|
||||
|
||||
extract_zip "${archive_path}" "${output_dir}"
|
||||
|
||||
nested_archive_path="$(find "${output_dir}" -maxdepth 2 -type f \( -name 'rime_cli-*.zip' -o -name 'rime-cli-*.zip' \) | head -n 1)"
|
||||
if [[ -n "${nested_archive_path}" ]]; then
|
||||
local nested_root="${output_dir}/nested"
|
||||
mkdir -p "${nested_root}"
|
||||
extract_zip "${nested_archive_path}" "${nested_root}"
|
||||
output_dir="${nested_root}"
|
||||
fi
|
||||
|
||||
bundle_root="$(find "${output_dir}" -maxdepth 3 -type f -path '*/bin/rime_deployer' | head -n 1)"
|
||||
[[ -n "${bundle_root}" ]] || fail "rime_deployer not found in bundle"
|
||||
dirname "$(dirname "${bundle_root}")"
|
||||
}
|
||||
|
||||
resolve_cli_commands() {
|
||||
local cli_url="${1:-}"
|
||||
local work_root="$2"
|
||||
local deployer_path
|
||||
local api_console_path
|
||||
local cli_archive
|
||||
local extract_root
|
||||
local bundle_root
|
||||
|
||||
if [[ -n "${cli_url}" ]]; then
|
||||
require_command curl
|
||||
require_command unzip
|
||||
|
||||
cli_archive="${work_root}/rime-cli.zip"
|
||||
extract_root="${work_root}/cli"
|
||||
|
||||
prepare_cli_archive "${cli_url}" "${cli_archive}"
|
||||
log_step "extracting rime cli bundle" >&2
|
||||
bundle_root="$(prepare_cli_bundle "${cli_archive}" "${extract_root}")"
|
||||
deployer_path="${bundle_root}/bin/rime_deployer"
|
||||
api_console_path="${bundle_root}/bin/rime_api_console"
|
||||
else
|
||||
deployer_path="$(command -v rime_deployer || true)"
|
||||
api_console_path="$(command -v rime_api_console || true)"
|
||||
[[ -n "${deployer_path}" ]] || fail "RIME_CLI_URL is not set and local rime_deployer was not found in PATH"
|
||||
[[ -n "${api_console_path}" ]] || fail "RIME_CLI_URL is not set and local rime_api_console was not found in PATH"
|
||||
log_step "using local rime cli commands from PATH" >&2
|
||||
fi
|
||||
|
||||
[[ -x "${deployer_path}" ]] || fail "rime_deployer is not executable: ${deployer_path}"
|
||||
[[ -x "${api_console_path}" ]] || fail "rime_api_console is not executable: ${api_console_path}"
|
||||
printf '%s\n%s\n' "${deployer_path}" "${api_console_path}"
|
||||
}
|
||||
|
||||
combine_logs() {
|
||||
local stdout_path="$1"
|
||||
local stderr_path="$2"
|
||||
local output_path="$3"
|
||||
{
|
||||
printf '== stdout ==\n'
|
||||
cat "${stdout_path}"
|
||||
printf '\n== stderr ==\n'
|
||||
cat "${stderr_path}"
|
||||
} >"${output_path}"
|
||||
}
|
||||
|
||||
normalize_console_output() {
|
||||
local input_path="$1"
|
||||
local output_path="$2"
|
||||
tr -d '\r' <"${input_path}" >"${output_path}"
|
||||
}
|
||||
|
||||
assert_file_exists() {
|
||||
local file_path="$1"
|
||||
[[ -f "${file_path}" ]] || fail "expected file not found: ${file_path}"
|
||||
}
|
||||
|
||||
assert_file_contains() {
|
||||
local file_path="$1"
|
||||
local expected_text="$2"
|
||||
grep -F -- "${expected_text}" "${file_path}" >/dev/null ||
|
||||
fail "expected '${expected_text}' in ${file_path}"
|
||||
}
|
||||
|
||||
assert_file_matches() {
|
||||
local file_path="$1"
|
||||
local expected_pattern="$2"
|
||||
grep -E -- "${expected_pattern}" "${file_path}" >/dev/null ||
|
||||
fail "expected pattern '${expected_pattern}' in ${file_path}"
|
||||
}
|
||||
|
||||
resolve_expected_text() {
|
||||
local expected_text="$1"
|
||||
if [[ "${expected_text}" == @today:* ]]; then
|
||||
local date_format="${expected_text#@today:}"
|
||||
date +"${date_format}"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "${expected_text}"
|
||||
}
|
||||
|
||||
assert_no_error_lines() {
|
||||
local file_path="$1"
|
||||
local match_path="${file_path}.errors"
|
||||
if grep -En '(^E[0-9]{4}[[:space:]])|(^F[0-9]{4}[[:space:]])|(^ERROR[:[:space:]])|(^Error([^[:alpha:]]|$))' "${file_path}" >"${match_path}"; then
|
||||
cat "${match_path}" >&2
|
||||
fail "unexpected error output detected in ${file_path}"
|
||||
fi
|
||||
rm -f "${match_path}"
|
||||
}
|
||||
|
||||
collect_warning_lines() {
|
||||
local file_path="$1"
|
||||
local output_path="$2"
|
||||
grep -En '(^W[0-9]{4}[[:space:]])|(^WARNING[:[:space:]])|([Ww]arning)' "${file_path}" >"${output_path}" || true
|
||||
}
|
||||
|
||||
run_deployer() {
|
||||
local deployer_path="$1"
|
||||
local user_data_dir="$2"
|
||||
local shared_data_dir="$3"
|
||||
local stdout_path="$4"
|
||||
local stderr_path="$5"
|
||||
"${deployer_path}" --build "${user_data_dir}" "${shared_data_dir}" >"${stdout_path}" 2>"${stderr_path}"
|
||||
}
|
||||
|
||||
run_api_console_script() {
|
||||
local api_console_path="$1"
|
||||
local shared_data_dir="$2"
|
||||
local user_data_dir="$3"
|
||||
local input_path="$4"
|
||||
local stdout_path="$5"
|
||||
local stderr_path="$6"
|
||||
RIME_SHARED_DATA_DIR="${shared_data_dir}" \
|
||||
RIME_USER_DATA_DIR="${user_data_dir}" \
|
||||
"${api_console_path}" <"${input_path}" >"${stdout_path}" 2>"${stderr_path}"
|
||||
}
|
||||
23
others/script/smoke/run.sh
Normal file
23
others/script/smoke/run.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)"
|
||||
export SMOKE_ROOT="${script_dir}"
|
||||
export SMOKE_REPO_ROOT="$(cd "${SMOKE_ROOT}/../../.."; pwd)"
|
||||
export SMOKE_WORK_ROOT="${SMOKE_WORK_ROOT:-${RUNNER_TEMP:-/tmp}/rime-ice-smoke}"
|
||||
export SMOKE_LOG_ROOT="${SMOKE_LOG_ROOT:-${SMOKE_WORK_ROOT}/logs}"
|
||||
|
||||
source "${SMOKE_ROOT}/lib/common.sh"
|
||||
|
||||
suite_name="${1:-rime_ice}"
|
||||
|
||||
case "${suite_name}" in
|
||||
rime_ice)
|
||||
source "${SMOKE_ROOT}/suites/config_repo.sh"
|
||||
run_config_repo_suite "${suite_name}"
|
||||
;;
|
||||
*)
|
||||
fail "unknown smoke suite: ${suite_name}"
|
||||
;;
|
||||
esac
|
||||
231
others/script/smoke/suites/config_repo.sh
Normal file
231
others/script/smoke/suites/config_repo.sh
Normal file
@@ -0,0 +1,231 @@
|
||||
#!/bin/bash
|
||||
|
||||
run_config_repo_suite() {
|
||||
local suite_name="$1"
|
||||
local suite_root="${SMOKE_ROOT}/cases/${suite_name}"
|
||||
local work_root="${SMOKE_WORK_ROOT}/${suite_name}"
|
||||
local log_root="${SMOKE_LOG_ROOT}/${suite_name}"
|
||||
local cli_url="${RIME_CLI_URL:-}"
|
||||
local config_root="${RIME_CONFIG_ROOT:-${SMOKE_REPO_ROOT}}"
|
||||
local cli_paths_file="${work_root}/cli-paths.txt"
|
||||
local deployer_path
|
||||
local api_console_path
|
||||
local user_data_dir="${work_root}/user-data"
|
||||
local deploy_stdout="${log_root}/deploy.stdout.log"
|
||||
local deploy_stderr="${log_root}/deploy.stderr.log"
|
||||
local deploy_combined="${log_root}/deploy.combined.log"
|
||||
local deploy_warnings="${log_root}/deploy.warnings.log"
|
||||
|
||||
ensure_clean_dir "${work_root}"
|
||||
ensure_clean_dir "${log_root}"
|
||||
mkdir -p "${user_data_dir}"
|
||||
|
||||
assert_file_exists "${config_root}/default.yaml"
|
||||
assert_file_exists "${config_root}/${suite_name}.schema.yaml"
|
||||
require_destructive_cleanup_approval "${config_root}"
|
||||
clean_config_artifacts "${config_root}"
|
||||
|
||||
resolve_cli_commands "${cli_url}" "${work_root}" >"${cli_paths_file}"
|
||||
deployer_path="$(sed -n '1p' "${cli_paths_file}")"
|
||||
api_console_path="$(sed -n '2p' "${cli_paths_file}")"
|
||||
|
||||
log_step "using config root ${config_root}"
|
||||
log_step "running deployer"
|
||||
if ! run_deployer "${deployer_path}" "${user_data_dir}" "${config_root}" "${deploy_stdout}" "${deploy_stderr}"; then
|
||||
combine_logs "${deploy_stdout}" "${deploy_stderr}" "${deploy_combined}"
|
||||
set_failure_context "deployment" "${deploy_combined}"
|
||||
fail "rime_deployer exited with non-zero status"
|
||||
fi
|
||||
|
||||
combine_logs "${deploy_stdout}" "${deploy_stderr}" "${deploy_combined}"
|
||||
set_failure_context "deployment" "${deploy_combined}"
|
||||
assert_no_error_lines "${deploy_combined}"
|
||||
collect_warning_lines "${deploy_combined}" "${deploy_warnings}"
|
||||
assert_file_exists "${user_data_dir}/build/default.yaml"
|
||||
assert_file_exists "${user_data_dir}/build/${suite_name}.schema.yaml"
|
||||
log_pass "deployment passed"
|
||||
|
||||
if [[ -s "${deploy_warnings}" ]]; then
|
||||
log_warn "deployment warnings detected"
|
||||
cat "${deploy_warnings}"
|
||||
fi
|
||||
clear_failure_context
|
||||
|
||||
run_config_input_cases "${suite_root}/input_cases.tsv" "${api_console_path}" "${config_root}" "${user_data_dir}" "${log_root}"
|
||||
}
|
||||
|
||||
run_config_input_cases() {
|
||||
local case_file="$1"
|
||||
local api_console_path="$2"
|
||||
local shared_data_dir="$3"
|
||||
local user_data_dir="$4"
|
||||
local log_root="$5"
|
||||
local raw_line
|
||||
local pending_schema_id=""
|
||||
local pending_case_file="${log_root}/pending_cases.tsv"
|
||||
|
||||
[[ -f "${case_file}" ]] || fail "case file not found: ${case_file}"
|
||||
: >"${pending_case_file}"
|
||||
|
||||
while IFS= read -r raw_line || [[ -n "${raw_line}" ]]; do
|
||||
if [[ -z "${raw_line}" ]]; then
|
||||
flush_schema_case_group \
|
||||
"${pending_schema_id}" \
|
||||
"${pending_case_file}" \
|
||||
"${api_console_path}" \
|
||||
"${shared_data_dir}" \
|
||||
"${user_data_dir}" \
|
||||
"${log_root}"
|
||||
pending_schema_id=""
|
||||
: >"${pending_case_file}"
|
||||
continue
|
||||
fi
|
||||
if [[ "${raw_line}" == \#* ]]; then
|
||||
flush_schema_case_group \
|
||||
"${pending_schema_id}" \
|
||||
"${pending_case_file}" \
|
||||
"${api_console_path}" \
|
||||
"${shared_data_dir}" \
|
||||
"${user_data_dir}" \
|
||||
"${log_root}"
|
||||
pending_schema_id=""
|
||||
: >"${pending_case_file}"
|
||||
printf '%s\n' "${raw_line}"
|
||||
continue
|
||||
fi
|
||||
|
||||
local case_id
|
||||
local schema_id
|
||||
local key_sequence
|
||||
local expected_text
|
||||
IFS=$'\t' read -r case_id schema_id key_sequence expected_text <<<"${raw_line}"
|
||||
if [[ -z "${case_id}" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -n "${pending_schema_id}" && "${schema_id}" != "${pending_schema_id}" ]]; then
|
||||
flush_schema_case_group \
|
||||
"${pending_schema_id}" \
|
||||
"${pending_case_file}" \
|
||||
"${api_console_path}" \
|
||||
"${shared_data_dir}" \
|
||||
"${user_data_dir}" \
|
||||
"${log_root}"
|
||||
: >"${pending_case_file}"
|
||||
fi
|
||||
|
||||
pending_schema_id="${schema_id}"
|
||||
printf '%s\t%s\t%s\n' "${case_id}" "${key_sequence}" "${expected_text}" >>"${pending_case_file}"
|
||||
done <"${case_file}"
|
||||
|
||||
flush_schema_case_group \
|
||||
"${pending_schema_id}" \
|
||||
"${pending_case_file}" \
|
||||
"${api_console_path}" \
|
||||
"${shared_data_dir}" \
|
||||
"${user_data_dir}" \
|
||||
"${log_root}"
|
||||
}
|
||||
|
||||
flush_schema_case_group() {
|
||||
local schema_id="$1"
|
||||
local case_group_file="$2"
|
||||
local api_console_path="$3"
|
||||
local shared_data_dir="$4"
|
||||
local user_data_dir="$5"
|
||||
local log_root="$6"
|
||||
|
||||
[[ -n "${schema_id}" ]] || return 0
|
||||
[[ -s "${case_group_file}" ]] || return 0
|
||||
|
||||
local group_name="${schema_id}"
|
||||
local group_input_path="${log_root}/${group_name}.group.input.txt"
|
||||
local group_stdout_path="${log_root}/${group_name}.group.stdout.log"
|
||||
local group_stderr_path="${log_root}/${group_name}.group.stderr.log"
|
||||
local group_combined_path="${log_root}/${group_name}.group.combined.log"
|
||||
local group_normalized_path="${log_root}/${group_name}.group.normalized.log"
|
||||
|
||||
build_schema_group_input "${schema_id}" "${case_group_file}" "${group_input_path}"
|
||||
log_step "running schema group ${schema_id}"
|
||||
if ! run_api_console_script "${api_console_path}" "${shared_data_dir}" "${user_data_dir}" "${group_input_path}" "${group_stdout_path}" "${group_stderr_path}"; then
|
||||
combine_logs "${group_stdout_path}" "${group_stderr_path}" "${group_combined_path}"
|
||||
set_failure_context "schema group ${schema_id}" "${group_combined_path}"
|
||||
fail "rime_api_console exited with non-zero status"
|
||||
fi
|
||||
|
||||
combine_logs "${group_stdout_path}" "${group_stderr_path}" "${group_combined_path}"
|
||||
set_failure_context "schema group ${schema_id}" "${group_combined_path}"
|
||||
assert_no_error_lines "${group_combined_path}"
|
||||
normalize_console_output "${group_stdout_path}" "${group_normalized_path}"
|
||||
clear_failure_context
|
||||
|
||||
assert_schema_group_cases "${schema_id}" "${case_group_file}" "${group_normalized_path}" "${group_combined_path}" "${log_root}"
|
||||
}
|
||||
|
||||
build_schema_group_input() {
|
||||
local schema_id="$1"
|
||||
local case_group_file="$2"
|
||||
local output_path="$3"
|
||||
local case_id
|
||||
local key_sequence
|
||||
local expected_text
|
||||
|
||||
: >"${output_path}"
|
||||
while IFS=$'\t' read -r case_id key_sequence expected_text; do
|
||||
[[ -n "${case_id}" ]] || continue
|
||||
printf 'select schema %s\n%s\n' "${schema_id}" "${key_sequence}" >>"${output_path}"
|
||||
done <"${case_group_file}"
|
||||
printf 'exit\n' >>"${output_path}"
|
||||
}
|
||||
|
||||
extract_case_output_block() {
|
||||
local normalized_path="$1"
|
||||
local schema_id="$2"
|
||||
local occurrence="$3"
|
||||
local output_path="$4"
|
||||
awk -v marker="selected schema: [${schema_id}]" -v target="${occurrence}" '
|
||||
$0 == marker {
|
||||
count++
|
||||
if (count == target) {
|
||||
printing = 1
|
||||
} else if (count > target && printing) {
|
||||
exit
|
||||
}
|
||||
}
|
||||
printing {
|
||||
print
|
||||
}
|
||||
' "${normalized_path}" >"${output_path}"
|
||||
}
|
||||
|
||||
assert_schema_group_cases() {
|
||||
local schema_id="$1"
|
||||
local case_group_file="$2"
|
||||
local group_normalized_path="$3"
|
||||
local group_combined_path="$4"
|
||||
local log_root="$5"
|
||||
local case_index=0
|
||||
local case_id
|
||||
local key_sequence
|
||||
local expected_text
|
||||
local resolved_expected_text
|
||||
|
||||
while IFS=$'\t' read -r case_id key_sequence expected_text; do
|
||||
[[ -n "${case_id}" ]] || continue
|
||||
((case_index += 1))
|
||||
resolved_expected_text="$(resolve_expected_text "${expected_text}")"
|
||||
|
||||
local case_stdout_path="${log_root}/${case_id}.stdout.log"
|
||||
extract_case_output_block "${group_normalized_path}" "${schema_id}" "${case_index}" "${case_stdout_path}"
|
||||
|
||||
set_failure_context "case ${case_id}" "${group_combined_path}"
|
||||
assert_file_contains "${case_stdout_path}" "selected schema: [${schema_id}]"
|
||||
if [[ "${expected_text}" == @regex:* ]]; then
|
||||
assert_file_matches "${case_stdout_path}" "${expected_text#@regex:}"
|
||||
else
|
||||
assert_file_contains "${case_stdout_path}" "commit: ${resolved_expected_text}"
|
||||
fi
|
||||
log_pass "input case passed: ${case_id}"
|
||||
clear_failure_context
|
||||
done <"${case_group_file}"
|
||||
}
|
||||
@@ -8,6 +8,14 @@
|
||||
# 鼠须管界面配置指南: https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/鼠鬚管介面配置指南.md
|
||||
# 鼠须管作者写的图形化的皮肤设计器: https://github.com/LEOYoon-Tsaw/Squirrel-Designer
|
||||
|
||||
# Squirrel 部分命令行选项:
|
||||
# squirrel="/Library/Input Methods/Squirrel.app/Contents/MacOS/Squirrel"
|
||||
# --quit:退出所有 Squirrel 进程
|
||||
# --reload:重新部署
|
||||
# --sync:触发同步用户数据
|
||||
# --build:构建
|
||||
# --help:帮助输出帮助文本
|
||||
|
||||
|
||||
# 要比共享目录的同名文件的 config_version 大才可以生效
|
||||
config_version: '2026-01-31' # 对应的鼠须管版本: 1.1.2
|
||||
|
||||
Reference in New Issue
Block a user