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
8 changes: 5 additions & 3 deletions .agents/docs/2026-11-10-tests-from-mcpp.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion mide-plugins/clion/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
2 changes: 1 addition & 1 deletion mide-plugins/clion/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<depends>io.github.sunrisepeak.mcppls</depends>):
# its release's one package for every system, fetched for the sandbox, the tests and the verifier.
mcpplsPluginVersion = 0.21.0
Expand Down
2 changes: 1 addition & 1 deletion mide-plugins/nvim/lua/mcpp/session.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion mide-plugins/nvim/lua/mcpp/tests.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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 = { '▁', '▂', '▃', '▄', '▅', '▆', '▇', '█' }

Expand Down
4 changes: 2 additions & 2 deletions mide-plugins/vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion mide-plugins/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 12 additions & 5 deletions src/server.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -1848,19 +1848,26 @@ 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<testing::Program> 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);
Json n = o.detail;
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);
Expand Down
18 changes: 17 additions & 1 deletion tests/e2e/protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()}});
Expand Down
Loading