From b4c9a263bdd7972f10bc3b6f2828632b73953b90 Mon Sep 17 00:00:00 2001 From: Sunrisepeak Date: Tue, 10 Nov 2026 11:00:50 +0900 Subject: [PATCH] 1.4.0: a run of cases through a part names its program test/caseResult named the job's filter as the program and the record's name as the part: cases run through a part (unit/a) were reported as the program unit/a, and CLion and Neovim placed them under a suite of that name. It now names them as the record states them, by the rule of test/result (owner_of). mcpp names a run that selects cases (--case) after the program; such a result carries no part. The design note and a comment named the Neovim command :Mcpp testInsights; it is :Mcpp test.insights. Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com> --- .agents/docs/2026-11-10-tests-from-mcpp.md | 8 +++++--- CHANGELOG.md | 13 +++++++++++++ mcpp.toml | 2 +- mide-plugins/clion/README.md | 2 +- mide-plugins/clion/gradle.properties | 2 +- mide-plugins/nvim/lua/mcpp/session.lua | 2 +- mide-plugins/nvim/lua/mcpp/tests.lua | 2 +- mide-plugins/vscode/package-lock.json | 4 ++-- mide-plugins/vscode/package.json | 2 +- src/server.cppm | 17 ++++++++++++----- tests/e2e/protocol.cpp | 18 +++++++++++++++++- 11 files changed, 55 insertions(+), 17 deletions(-) diff --git a/.agents/docs/2026-11-10-tests-from-mcpp.md b/.agents/docs/2026-11-10-tests-from-mcpp.md index f4eaadc..3782f61 100644 --- a/.agents/docs/2026-11-10-tests-from-mcpp.md +++ b/.agents/docs/2026-11-10-tests-from-mcpp.md @@ -92,7 +92,9 @@ A result names the program it belongs to: A whole-program run therefore marks every case. - `test/caseResult`: gains the fields of a case above. It is sent for every record of the job, not - only the last one; a run of a directory program writes one record per part. + only the last one; a run of a directory program writes one record per part. It names the program + and the part as the record does, by the rule of a result above, whatever the job's filter; mcpp + 2026.11.9.1 names a run that selects cases (`--case`) after the program, and its result has no part. - `test/caseEvent` (new): `{jobId, root, program, part, case, event: "begin" | "end", tMs, ...}` with the fields of a case on `end`. It is sent as mcpp writes a `case` record (`test-case-stream`). - `test/history {root, program?, case?, runs?}` (new): the store's last runs (20 by default) and each @@ -151,7 +153,7 @@ insights are empty and say so. - A failure's `expected` and `actual` appear as two lines under it, and in its quickfix text. - A case's line carries its sign (✓, ✗) and, as virtual text, its duration and its metrics against their bounds. -- `:Mcpp testInsights` opens the insights as text in a floating window. A metric's trend is drawn with +- `:Mcpp test.insights` opens the insights as text in a floating window. A metric's trend is drawn with block characters (▁▂▃▅▇), and the schedule as bars. ## Performance @@ -178,6 +180,6 @@ insights are empty and say so. directory's `.mock-mcpp-tests` selects them, as `.mock-mcpp-features` selects the features. - VS Code: the tree of a directory program; part results attributed to it; the diff message; the decorations' text. -- Neovim: the smoke test covers live results, the virtual text and `:Mcpp testInsights`. +- Neovim: the smoke test covers live results, the virtual text and `:Mcpp test.insights`. - CLion: the comparison failure and the nested suites, in the plugin's unit tests. - The insights page is rendered by a Chromium test, as the graph page is. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8761bba..2c2b194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ The changelog is kept by series (`X.Y`); each section lists its components' releases. +## 1.4 + +### 1.4.0 + +Every component is 1.4.0: the backend, mcpp-vscode, mcpp-clion and mcpp-nvim (from 1.3.0). + +- **A run of cases through a part names its program.** `test/caseResult` names the program and the part + as the record states them, as `test/result` does. 1.3.0 named the job's filter as the program: cases + run through a part (`unit/a`) were reported as the program `unit/a`, and CLion and Neovim placed them + under a suite of that name. mcpp names a run that selects cases (`--case`) after the program; such a + result carries no part. +- **Docs.** The Neovim command of the test insights is `:Mcpp test.insights`. + ## 1.3 ### 1.3.0 diff --git a/mcpp.toml b/mcpp.toml index 5c31729..e2a6337 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -9,7 +9,7 @@ # M2/M3), where the system it runs upon is answered when the image is loaded. [package] name = "mcpp-ide" -version = "1.3.0" +version = "1.4.0" description = "The IDE backend for mcpp: sessions, jobs, records and view models over mcpp's control contract" license = "Apache-2.0" authors = ["mcpp-community"] diff --git a/mide-plugins/clion/README.md b/mide-plugins/clion/README.md index 79dfc74..a021d5c 100644 --- a/mide-plugins/clion/README.md +++ b/mide-plugins/clion/README.md @@ -99,7 +99,7 @@ An individual packaged plugin can be checked without rebuilding the backend: ```bash xvfb-run -a gradle --no-daemon -p integration-tests test \ - -PpluginZip=/absolute/path/mcpp-clion-1.3.0.zip \ + -PpluginZip=/absolute/path/mcpp-clion-1.4.0.zip \ -PmcpplsZip=/absolute/path/mcppls-clion-0.21.0.zip \ -PideVersion=2026.1.5.2 ``` diff --git a/mide-plugins/clion/gradle.properties b/mide-plugins/clion/gradle.properties index d53b182..2bdbf07 100644 --- a/mide-plugins/clion/gradle.properties +++ b/mide-plugins/clion/gradle.properties @@ -2,7 +2,7 @@ # where it installs (2026.1 and later); the verifier checks both ends. platformType = CL platformVersion = 2026.2.3 -pluginVersion = 1.3.0 +pluginVersion = 1.4.0 # The mcppls CLion plugin this one depends on (io.github.sunrisepeak.mcppls): # its release's one package for every system, fetched for the sandbox, the tests and the verifier. mcpplsPluginVersion = 0.21.0 diff --git a/mide-plugins/nvim/lua/mcpp/session.lua b/mide-plugins/nvim/lua/mcpp/session.lua index 86d4b05..b3be537 100644 --- a/mide-plugins/nvim/lua/mcpp/session.lua +++ b/mide-plugins/nvim/lua/mcpp/session.lua @@ -13,7 +13,7 @@ local uv = vim.uv or vim.loop local is_windows = vim.fn.has('win32') == 1 -- mcpp-nvim's version: mcpp-ide's, whose release carries it. -M.version = '1.3.0' +M.version = '1.4.0' -- Neovim's version, as its release names it. function M.nvim_version() diff --git a/mide-plugins/nvim/lua/mcpp/tests.lua b/mide-plugins/nvim/lua/mcpp/tests.lua index d899745..d2df27c 100644 --- a/mide-plugins/nvim/lua/mcpp/tests.lua +++ b/mide-plugins/nvim/lua/mcpp/tests.lua @@ -323,7 +323,7 @@ function M.said_of(root, test, case) return said[(root or '') .. '\0' .. test .. '\0' .. case] end --- ── the insights as text (`:Mcpp testInsights`) ────────────────────────────────────────────── +-- ── the insights as text (`:Mcpp test.insights`) ────────────────────────────────────────────── local BLOCKS = { '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█' } diff --git a/mide-plugins/vscode/package-lock.json b/mide-plugins/vscode/package-lock.json index 1d3c683..13da408 100644 --- a/mide-plugins/vscode/package-lock.json +++ b/mide-plugins/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "mcpp-vscode", - "version": "1.3.0", + "version": "1.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mcpp-vscode", - "version": "1.3.0", + "version": "1.4.0", "license": "Apache-2.0", "devDependencies": { "@mermaid-js/tiny": "12.2.0", diff --git a/mide-plugins/vscode/package.json b/mide-plugins/vscode/package.json index c4c9524..dbe6474 100644 --- a/mide-plugins/vscode/package.json +++ b/mide-plugins/vscode/package.json @@ -2,7 +2,7 @@ "name": "mcpp-vscode", "displayName": "%displayName%", "description": "%description%", - "version": "1.3.0", + "version": "1.4.0", "publisher": "mcpp-community", "license": "Apache-2.0", "icon": "media/logo.png", diff --git a/src/server.cppm b/src/server.cppm index 0e7f14b..6673ac5 100644 --- a/src/server.cppm +++ b/src/server.cppm @@ -35,13 +35,13 @@ export namespace mcppide::server { using Json = nlohmann::json; -inline constexpr std::string_view kVersion = "1.3.0"; +inline constexpr std::string_view kVersion = "1.4.0"; // THE SERIES (mcpp design 2026-11-07, D15): `X.Y` of `X.Y.Z`, shared by the backend and every editor // plugin; components of one series work together. A plugin refuses a backend of another series, // naming both versions. The backend is always `X.Y.0`: the VS Code and CLion packages carry it, so a // change of the backend is a change of every package, and it begins a series (1.3.0). `Z` is an // editor plugin's own patch. -inline constexpr std::string_view kSeries = "1.3"; +inline constexpr std::string_view kSeries = "1.4"; static_assert(kVersion.starts_with(kSeries) && kVersion.size() == kSeries.size() + 2 && kVersion.ends_with(".0")); inline constexpr int kProtocol = 2; @@ -1848,10 +1848,17 @@ Json Session::test_result_of(const std::filesystem::path& root, const Json& reco } // The cases of one record of a test/runCases job, each sent as `test/caseResult` with its program -// and part, a failure's message as its output. +// and part, a failure's message as its output. The record names them, as a test/result's does: the +// job's filter may be a part, and mcpp names a run that selects cases after the program. void Session::protocol_case_results(std::string_view jobId, const std::filesystem::path& root, const std::string& test, const Json& record) { + std::vector programs; + { + std::lock_guard lock(mutex_); + if (auto it = programs_.find(root); it != programs_.end()) programs = it->second; + } const auto part = record.value("test", ""); + const auto program = testing::owner_of(part, record.value("part_of", ""), programs); for (const auto& o : testing::protocol_outcomes(record)) { bool truncated = false; auto output = views::bounded_output(o.outcome.status == "failed" ? std::string_view(o.outcome.message) : std::string_view{}, truncated); @@ -1859,8 +1866,8 @@ void Session::protocol_case_results(std::string_view jobId, const std::filesyste n["jobId"] = jobId; n["root"] = root.generic_string(); n["test"] = test; - n["program"] = test; - if (part != test) n["part"] = part; + n["program"] = program; + if (part != program) n["part"] = part; n["output"] = std::move(output); n["outputTruncated"] = truncated; rpc_.notify("mcpp-ide/test/caseResult", n); diff --git a/tests/e2e/protocol.cpp b/tests/e2e/protocol.cpp index d2b3009..a65e62a 100644 --- a/tests/e2e/protocol.cpp +++ b/tests/e2e/protocol.cpp @@ -425,7 +425,23 @@ E2E protocol{"protocol", [] { && events[eventsBefore + 1]["metrics"][0]["name"] == "ready"); check(events[eventsBefore + 3]["status"] == "failed" && events[eventsBefore + 3]["part"] == "unit/b"); } - for (const auto* name : {"mcpp-ide/test/result", "mcpp-ide/test/caseEvent"}) + // Cases asked for through a part: each result names its program and its own part, as the + // record states them, not the job's filter. + const auto caseResultsBefore = wire.notifications("mcpp-ide/test/caseResult").size(); + const auto finishedCases = wire.notifications("mcpp-ide/job/finished").size(); + result_of("mcpp-ide/test/runCases", Json{{"root", dir.generic_string()}, {"test", "unit/a"}, {"framework", "mtest"}, + {"cases", Json::array({"alpha"})}}); + for (int i = 0; i < 2000 && wire.notifications("mcpp-ide/job/finished").size() <= finishedCases; ++i) + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + const auto caseResults = wire.notifications("mcpp-ide/test/caseResult"); + check(caseResults.size() == caseResultsBefore + 2); + if (caseResults.size() == caseResultsBefore + 2) { + const auto& alpha = caseResults[caseResultsBefore]; + const auto& beta = caseResults[caseResultsBefore + 1]; + check(alpha["test"] == "unit/a" && alpha["program"] == "unit" && alpha["part"] == "unit/a" && alpha["case"] == "alpha"); + check(beta["program"] == "unit" && beta["part"] == "unit/b" && beta["status"] == "failed"); + } + for (const auto* name : {"mcpp-ide/test/result", "mcpp-ide/test/caseEvent", "mcpp-ide/test/caseResult"}) for (const auto& n : wire.notifications(name)) check(conforms(n, protocol["notifications"][name]["params"], protocol, name)); // The store mcpp keeps (target/mtest): three runs, the last with mcpp#917's fields. const auto noRuns = result_of("mcpp-ide/test/insights", Json{{"root", dir.generic_string()}});