Skip to content

Commit de534da

Browse files
committed
tests: native spellings on Windows, the default toolchain on every platform, python3 in the hermetic llvm step
The unit tests compare a native-spelled directory as a path and JSON-escape a Windows path; e2e 781 and 782 build with the machine's default toolchain, and 781 asks for no plan only where the fast path exists (ELF); e2e 211 derives the sibling fixture from the entry's own file, which mcpp spells; the hermetic container gains python3 for the compile-database scripts.
1 parent 71dcfb6 commit de534da

6 files changed

Lines changed: 42 additions & 25 deletions

‎.github/workflows/ci-linux-e2e.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,15 @@ jobs:
457457
- name: "Compile database e2e that needs llvm (783, 784, 785, 786)"
458458
run: |
459459
set -o pipefail
460+
# The scripts read the database with python3, which this container
461+
# lacks. It is not a toolchain: the job's property (no host compiler,
462+
# no host C runtime) was asserted before the checkout and still holds.
463+
if ! command -v python3 >/dev/null 2>&1; then
464+
apt-get install -y -qq python3 >/dev/null
465+
if command -v gcc >/dev/null 2>&1; then
466+
echo "::error::installing python3 brought a host compiler"; exit 1
467+
fi
468+
fi
460469
export PATH="$HOME/.xlings/subos/current/bin:$PATH"
461470
export MCPP
462471
run_and_assert() { # $1 = script; the remaining arguments are lines it must print

‎tests/e2e/211_configure_only_cdb.sh‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,21 @@ grep -q 'tests[\/][\/]*test_smoke.cpp' compile_commands.json || {
6666
exit 1
6767
}
6868
if command -v python3 >/dev/null 2>&1; then
69-
# The devkit path is passed in directly rather than derived from the CDB's
70-
# `directory`: since C3 (design 2026-09-26 §3.3), `directory` is the
71-
# OUTPUT directory the compiler runs in, not the project root, so a
72-
# sibling fixture's path can no longer be recovered from it.
73-
python3 - compile_commands.json "$TMP/devkit/include" <<'PY'
69+
# The devkit path is derived from the entry's own `file`, in the spelling
70+
# mcpp wrote: the shell's `$TMP` is not it on Windows (an MSYS root) or on
71+
# macOS (`/var` against the resolved `/private/var`). `directory` no longer
72+
# serves, because since C3 (design 2026-09-26 §3.3) it is the output
73+
# directory the compiler runs in, not the project root.
74+
python3 - compile_commands.json <<'PY'
7475
import json, sys
7576
entries = json.load(open(sys.argv[1], encoding="utf-8"))
76-
devkit_include = sys.argv[2]
7777
normal = lambda p: p.replace("\\", "/").rstrip("/")
7878
test = next(e for e in entries if normal(e["file"]).endswith("/tests/test_smoke.cpp"))
7979
main = next(e for e in entries if normal(e["file"]).endswith("/src/main.cpp"))
8080
args = test["arguments"]
81-
expected_include = normal(devkit_include).casefold()
81+
project = normal(test["file"])[: -len("/tests/test_smoke.cpp")]
82+
fixture = project.rsplit("/", 1)[0]
83+
expected_include = f"{fixture}/devkit/include".casefold()
8284
include_args = {
8385
normal(a[2:]).casefold()
8486
for a in args

‎tests/e2e/781_deleted_root_cdb_is_restored.sh‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,8 @@ trap 'rm -rf "$TMP"' EXIT
1515
cd "$TMP"
1616
"$MCPP" new app > /dev/null
1717
cd app
18-
# Pinned rather than left to the machine's default: the property under test
19-
# does not depend on which toolchain is used, and pinning keeps the test's
20-
# reading independent of shared state outside this tree.
21-
TCFLAG=(--toolchain gcc@16.1.0)
2218

23-
"$MCPP" build "${TCFLAG[@]}" > build1.log 2>&1 || { cat build1.log; echo "FAIL: the first build failed"; exit 1; }
19+
"$MCPP" build > build1.log 2>&1 || { cat build1.log; echo "FAIL: the first build failed"; exit 1; }
2420
[[ -s compile_commands.json ]] || { echo "FAIL: no compile_commands.json after the first build"; exit 1; }
2521

2622
config_cdb=$(find target -name compile_commands.json | head -1)
@@ -29,7 +25,7 @@ before=$(cat "$config_cdb")
2925

3026
rm compile_commands.json
3127

32-
"$MCPP" build "${TCFLAG[@]}" > build2.log 2>&1 || {
28+
"$MCPP" build > build2.log 2>&1 || {
3329
cat build2.log
3430
echo "FAIL: the build after deleting the root database failed"
3531
exit 1
@@ -45,7 +41,9 @@ rm compile_commands.json
4541
# real prepare pass has nothing to compile -- the restore must not trigger
4642
# one. A plan prints a "Compiling <pkg>" line; the fast path prints only
4743
# "Finished ... in <time>".
48-
if grep -q "Compiling" build2.log; then
44+
# The fast path replays ELF builds only (#400); on macOS and Windows the
45+
# second build plans again, and the full path's writer restores the file.
46+
if [[ "$(uname -s)" == Linux ]] && grep -q "Compiling" build2.log; then
4947
echo "FAIL: restoring the root database ran a full plan"
5048
cat build2.log
5149
exit 1
@@ -61,7 +59,7 @@ after=$(cat compile_commands.json)
6159
# The restore survives a SECOND round-trip too (not a one-shot fluke of the
6260
# cache entry the first build happened to leave behind).
6361
rm compile_commands.json
64-
"$MCPP" build "${TCFLAG[@]}" > build3.log 2>&1 || { cat build3.log; echo "FAIL: the third build failed"; exit 1; }
62+
"$MCPP" build > build3.log 2>&1 || { cat build3.log; echo "FAIL: the third build failed"; exit 1; }
6563
[[ -f compile_commands.json ]] || { echo "FAIL: the second delete was not restored"; exit 1; }
6664

6765
echo "OK"

‎tests/e2e/782_foreign_root_cdb_entries_are_replaced.sh‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ trap 'rm -rf "$TMP"' EXIT
1414
cd "$TMP"
1515
"$MCPP" new app > /dev/null
1616
cd app
17-
# Pinned rather than left to the machine's default: the property under test
18-
# does not depend on which toolchain is used, and pinning keeps the test's
19-
# reading independent of shared state outside this tree.
20-
TCFLAG=(--toolchain gcc@16.1.0)
21-
"$MCPP" build "${TCFLAG[@]}" > /dev/null 2>&1
17+
"$MCPP" build > /dev/null 2>&1
2218

2319
# Another writer's database: two entries with no `output` field at all -- the
2420
# shape a hand-written or xmake-style database uses, and never mcpp's own
@@ -34,7 +30,7 @@ EOF
3430

3531
# Force a real prepare pass (not the fast path), so the replace actually runs.
3632
touch src/main.cpp
37-
out=$("$MCPP" build "${TCFLAG[@]}" 2>&1) || { echo "$out"; echo "FAIL: build failed"; exit 1; }
33+
out=$("$MCPP" build 2>&1) || { echo "$out"; echo "FAIL: build failed"; exit 1; }
3834

3935
warn_count=$(printf '%s\n' "$out" | grep -c 'held 2 entries mcpp did not write')
4036
[[ "$warn_count" -eq 1 ]] || {
@@ -66,7 +62,7 @@ grep -q "main.cpp" compile_commands.json || {
6662

6763
# A second build over the now-mcpp-owned file warns no further: nothing
6864
# foreign is left to replace.
69-
out2=$("$MCPP" build "${TCFLAG[@]}" --no-cache 2>&1) || { echo "$out2"; echo "FAIL: second build failed"; exit 1; }
65+
out2=$("$MCPP" build --no-cache 2>&1) || { echo "$out2"; echo "FAIL: second build failed"; exit 1; }
7066
if printf '%s\n' "$out2" | grep -q "did not write"; then
7167
echo "FAIL: warned again on a file that already held only mcpp's configuration"
7268
echo "$out2"

‎tests/unit/test_build_directives.cpp‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,12 +869,22 @@ TEST(BuildDirectives, PrepareActionWithNoOutputDirIsRefused) {
869869
// replayed `prepare` payload decodes to the same role and output_dir a fresh
870870
// run would -- the cache hit must replay both exactly.
871871
TEST(BuildDirectives, PrepareActionSurvivesTheCacheRoundTrip) {
872+
// The paths are JSON strings: a Windows path's backslashes are escaped,
873+
// as the bundled module's serializer escapes them.
874+
auto json_string = [](std::string_view v) {
875+
std::string out;
876+
for (char c : v) {
877+
if (c == '\\' || c == '"') out.push_back('\\');
878+
out.push_back(c);
879+
}
880+
return out;
881+
};
872882
const std::string payload = std::format(
873883
"{{\"id\":\"prep\",\"role\":\"prepare\","
874884
"\"description\":\"\",\"blocking\":false,\"output_dir\":\"{}\","
875885
"\"inputs\":[],\"outputs\":[\"{}\"],"
876886
"\"command\":[\"install.sh\"],\"provides\":[],\"imports\":[],\"targets\":[]}}",
877-
under_root("prefix"), under_root("prep.stamp"));
887+
json_string(under_root("prefix")), json_string(under_root("prep.stamp")));
878888
dirs::Directives replayed;
879889
ASSERT_TRUE(dirs::accept_cache_record(replayed, "action", payload));
880890
auto a = dirs::decode_action(replayed.at(dirs::Slot::Actions).front());

‎tests/unit/test_compile_commands.cpp‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,8 +635,10 @@ TEST(CompileCommandsEmit, AProjectUnitsDirectoryIsTheOutputDirectory) {
635635

636636
auto invs = unit_invocations(plan, flags);
637637
ASSERT_EQ(invs.size(), 1u);
638-
EXPECT_EQ(invs[0].directory, plan.outputDir.string());
639-
EXPECT_NE(invs[0].directory, plan.projectRoot.string());
638+
// Compared as paths: the field is written in the native spelling, which
639+
// on Windows turns this fixture's `/` into `\`.
640+
EXPECT_EQ(std::filesystem::path(invs[0].directory), plan.outputDir);
641+
EXPECT_NE(std::filesystem::path(invs[0].directory), plan.projectRoot);
640642
}
641643

642644
// ── C4: the module interface language flag ──────────────────────────────────

0 commit comments

Comments
 (0)