refactor(tests): CMake-based CDB, workspace fixture, test cleanup (#378)

## Summary

- **CMake-based CDB generation for module tests**: Replace hand-written
compile_commands.json with CMakeLists.txt (CMake 3.28 `FILE_SET
CXX_MODULES`) in all 26 `tests/data/modules/*/` directories. CDB is
generated on-the-fly via `cmake -G Ninja` during test setup.
- **`@pytest.mark.workspace()` decorator**: Introduce a marker + fixture
pattern so tests declare their workspace via decorator and receive a
resolved `workspace` path. The fixture auto-generates CDB when a
CMakeLists.txt is present.
- **`CliceClient` helper methods**: Add `initialize()`, `open()`,
`wait_diagnostics()`, and `open_and_wait()` to reduce boilerplate across
all test files.
- **Use `asyncio_mode = "auto"`**: Switch from `@pytest_asyncio.fixture`
+ `@pytest.mark.asyncio` to `@pytest.fixture` + auto mode for proper
Pylance type inference on fixtures.
- **Test cleanup**: Remove redundant section separators and docstrings,
delete `tests/pyproject.toml` (config moved to `pytest.ini`).
- **Format task**: Add `.cppm` to `format-cpp` glob pattern.
- **CI fix**: Disable `CMAKE_CXX_SCAN_FOR_MODULES` and prefer pixi
clang++ to fix macOS CI where CMake rejects module scanning.

## Test plan

- [x] All 26 module test directories have CMakeLists.txt with FILE_SET
CXX_MODULES
- [x] generate_cdb() produces valid compile_commands.json with module
flags
- [x] Integration tests pass locally
- [ ] CI passes on all platforms (Linux, macOS, Windows)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Unified fixtures and client workflow: new init/open/wait helpers,
workspace marker support, bounded diagnostics waiting, CMake-based
compilation-database generation, and directory-backed temp-file
workflows; enabled asyncio test mode.
* **Chores**
* Added many C++20 module test projects and test data; removed prior
test pyproject in favor of pytest config; updated formatter to include
.cppm files.
* **Style**
* Reformatted many module/source implementations to consistent
multi-line function bodies.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ykiko
2026-03-31 16:57:48 +08:00
committed by GitHub
parent 0a891d8b4a
commit bc04845293
93 changed files with 1009 additions and 970 deletions

View File

@@ -13,11 +13,6 @@ namespace {
namespace et = eventide;
void write_cdb(TempDir& tmp, CompilationDatabase& cdb, llvm::StringRef json_content) {
tmp.touch("compile_commands.json", json_content);
cdb.load(tmp.path("compile_commands.json"));
}
/// Build a dispatch_fn that compiles PCMs in-process (no workers).
/// Clang requires ALL transitive PCM deps (not just direct imports)
/// in PrebuiltModuleFiles, so we pass every available PCM.

View File

@@ -21,13 +21,15 @@ namespace et = eventide;
TEST_SUITE(ModuleWorker) {
TEST_CASE(BuildPCMThenCompileWithImport) {
TempDir tmp;
// Module interface: produces PCM.
TempFile iface(
"mod_iface.cppm",
"export module Hello;\n" R"(export const char* hello() { return "world"; })" "\n");
tmp.touch("mod_iface.cppm",
"export module Hello;\n" R"(export const char* hello() { return "world"; })" "\n");
auto iface = tmp.path("mod_iface.cppm");
// Consumer: imports the module.
TempFile consumer("consumer.cpp", "import Hello;\n" "int main() { return hello()[0]; }\n");
tmp.touch("consumer.cpp", "import Hello;\n" "int main() { return hello()[0]; }\n");
auto consumer = tmp.path("consumer.cpp");
// --- Phase 1: Build PCM via stateless worker ---
WorkerHandle sl;
@@ -38,14 +40,14 @@ TEST_CASE(BuildPCMThenCompileWithImport) {
sl.run([&]() -> et::task<> {
worker::BuildPCMParams params;
params.file = iface.path;
params.file = iface;
params.directory = "/tmp";
params.arguments = {"clang++",
"-resource-dir",
std::string(resource_dir()),
"-std=c++20",
"--precompile",
iface.path};
iface};
params.module_name = "Hello";
auto result = co_await sl.peer->send_request(params);
@@ -69,7 +71,7 @@ TEST_CASE(BuildPCMThenCompileWithImport) {
sf.run([&]() -> et::task<> {
worker::CompileParams params;
params.path = consumer.path;
params.path = consumer;
params.version = 1;
params.text = "import Hello;\n" "int main() { return hello()[0]; }\n";
params.directory = "/tmp";
@@ -78,7 +80,7 @@ TEST_CASE(BuildPCMThenCompileWithImport) {
std::string(resource_dir()),
"-std=c++20",
"-fsyntax-only",
consumer.path};
consumer};
// Pass the PCM — same as MasterServer fills CompileParams.pcms.
params.pcms = {
{"Hello", pcm_path}
@@ -99,15 +101,19 @@ TEST_CASE(BuildPCMThenCompileWithImport) {
}
TEST_CASE(BuildPCMChainThenCompile) {
TempDir tmp;
// Module A: no deps.
TempFile mod_a("chain_a.cppm", "export module A;\n" "export int val_a() { return 1; }\n");
tmp.touch("chain_a.cppm", "export module A;\n" "export int val_a() { return 1; }\n");
auto mod_a = tmp.path("chain_a.cppm");
// Module B: imports A.
TempFile mod_b("chain_b.cppm",
"export module B;\n"
"import A;\n"
"export int val_b() { return val_a() + 1; }\n");
tmp.touch("chain_b.cppm",
"export module B;\n"
"import A;\n"
"export int val_b() { return val_a() + 1; }\n");
auto mod_b = tmp.path("chain_b.cppm");
// Consumer: imports B (transitively needs A).
TempFile consumer("chain_consumer.cpp", "import B;\n" "int main() { return val_b(); }\n");
tmp.touch("chain_consumer.cpp", "import B;\n" "int main() { return val_b(); }\n");
auto consumer = tmp.path("chain_consumer.cpp");
WorkerHandle sl;
ASSERT_TRUE(sl.spawn("stateless-worker"));
@@ -119,14 +125,14 @@ TEST_CASE(BuildPCMChainThenCompile) {
// Build PCM for A first.
{
worker::BuildPCMParams params;
params.file = mod_a.path;
params.file = mod_a;
params.directory = "/tmp";
params.arguments = {"clang++",
"-resource-dir",
std::string(resource_dir()),
"-std=c++20",
"--precompile",
mod_a.path};
mod_a};
params.module_name = "A";
auto result = co_await sl.peer->send_request(params);
@@ -137,14 +143,14 @@ TEST_CASE(BuildPCMChainThenCompile) {
// Build PCM for B, passing A's PCM (transitive dep).
{
worker::BuildPCMParams params;
params.file = mod_b.path;
params.file = mod_b;
params.directory = "/tmp";
params.arguments = {"clang++",
"-resource-dir",
std::string(resource_dir()),
"-std=c++20",
"--precompile",
mod_b.path};
mod_b};
params.module_name = "B";
params.pcms = {
{"A", pcm_a}
@@ -169,7 +175,7 @@ TEST_CASE(BuildPCMChainThenCompile) {
sf.run([&]() -> et::task<> {
worker::CompileParams params;
params.path = consumer.path;
params.path = consumer;
params.version = 1;
params.text = "import B;\n" "int main() { return val_b(); }\n";
params.directory = "/tmp";
@@ -178,7 +184,7 @@ TEST_CASE(BuildPCMChainThenCompile) {
std::string(resource_dir()),
"-std=c++20",
"-fsyntax-only",
consumer.path};
consumer};
// Clang needs ALL transitive PCMs.
params.pcms = {
{"A", pcm_a},
@@ -200,10 +206,13 @@ TEST_CASE(BuildPCMChainThenCompile) {
}
TEST_CASE(ModuleImplementationUnitWithWorker) {
TempDir tmp;
// Module interface.
TempFile iface("impl_iface.cppm", "export module Calc;\n" "export int add(int a, int b);\n");
tmp.touch("impl_iface.cppm", "export module Calc;\n" "export int add(int a, int b);\n");
auto iface = tmp.path("impl_iface.cppm");
// Module implementation unit (no export).
TempFile impl("impl_unit.cpp", "module Calc;\n" "int add(int a, int b) { return a + b; }\n");
tmp.touch("impl_unit.cpp", "module Calc;\n" "int add(int a, int b) { return a + b; }\n");
auto impl = tmp.path("impl_unit.cpp");
// Build PCM for interface.
WorkerHandle sl;
@@ -214,14 +223,14 @@ TEST_CASE(ModuleImplementationUnitWithWorker) {
sl.run([&]() -> et::task<> {
worker::BuildPCMParams params;
params.file = iface.path;
params.file = iface;
params.directory = "/tmp";
params.arguments = {"clang++",
"-resource-dir",
std::string(resource_dir()),
"-std=c++20",
"--precompile",
iface.path};
iface};
params.module_name = "Calc";
auto result = co_await sl.peer->send_request(params);
@@ -242,7 +251,7 @@ TEST_CASE(ModuleImplementationUnitWithWorker) {
sf.run([&]() -> et::task<> {
worker::CompileParams params;
params.path = impl.path;
params.path = impl;
params.version = 1;
params.text = "module Calc;\n" "int add(int a, int b) { return a + b; }\n";
params.directory = "/tmp";
@@ -251,7 +260,7 @@ TEST_CASE(ModuleImplementationUnitWithWorker) {
std::string(resource_dir()),
"-std=c++20",
"-fsyntax-only",
impl.path};
impl};
params.pcms = {
{"Calc", pcm_path}
};

View File

@@ -24,7 +24,9 @@ TEST_CASE(SpawnAndExit) {
}
TEST_CASE(CompileRequest) {
TempFile src("compile_test.cpp", "int main() { return 0; }\n");
TempDir tmp;
tmp.touch("compile_test.cpp", "int main() { return 0; }\n");
auto src = tmp.path("compile_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateful-worker"));
@@ -33,11 +35,11 @@ TEST_CASE(CompileRequest) {
w.run([&]() -> et::task<> {
worker::CompileParams params;
params.path = src.path;
params.path = src;
params.version = 1;
params.text = "int main() { return 0; }\n";
params.directory = "/tmp";
params.arguments = make_args(src.path);
params.arguments = make_args(src);
params.pch = {"", 0};
params.pcms = {};
@@ -76,7 +78,9 @@ TEST_CASE(HoverWithoutCompile) {
TEST_CASE(CompileThenHover) {
std::string text = "int foo() { return 42; }\nint main() { return foo(); }\n";
TempFile src("hover_test.cpp", text);
TempDir tmp;
tmp.touch("hover_test.cpp", text);
auto src = tmp.path("hover_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateful-worker"));
@@ -86,11 +90,11 @@ TEST_CASE(CompileThenHover) {
w.run([&]() -> et::task<> {
// First compile
worker::CompileParams cp;
cp.path = src.path;
cp.path = src;
cp.version = 1;
cp.text = text;
cp.directory = "/tmp";
cp.arguments = make_args(src.path);
cp.arguments = make_args(src);
auto compile_result = co_await w.peer->send_request(cp);
CO_ASSERT_TRUE(compile_result.has_value());
@@ -98,7 +102,7 @@ TEST_CASE(CompileThenHover) {
// After successful compilation, hover should return info.
// "int foo() { return 42; }\n" is 25 chars, then char 22 on line 1 = offset 47
worker::HoverParams hp;
hp.path = src.path;
hp.path = src;
hp.offset = 47; // position of 'foo' in 'return foo();'
auto hover_result = co_await w.peer->send_request(hp);
@@ -114,7 +118,9 @@ TEST_CASE(CompileThenHover) {
}
TEST_CASE(DocumentUpdate) {
TempFile src("update_test.cpp", "int x = 1;\n");
TempDir tmp;
tmp.touch("update_test.cpp", "int x = 1;\n");
auto src = tmp.path("update_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateful-worker"));
@@ -124,25 +130,25 @@ TEST_CASE(DocumentUpdate) {
w.run([&]() -> et::task<> {
// Compile first
worker::CompileParams cp;
cp.path = src.path;
cp.path = src;
cp.version = 1;
cp.text = "int x = 1;\n";
cp.directory = "/tmp";
cp.arguments = make_args(src.path);
cp.arguments = make_args(src);
auto r1 = co_await w.peer->send_request(cp);
CO_ASSERT_TRUE(r1.has_value());
// Send document update notification
worker::DocumentUpdateParams up;
up.path = src.path;
up.path = src;
up.version = 2;
up.text = "int x = 2;\nint y = 3;\n";
w.peer->send_notification(up);
// After update, hover still returns stale AST results (not null).
worker::HoverParams hp;
hp.path = src.path;
hp.path = src;
hp.offset = 4;
auto hover_result = co_await w.peer->send_request(hp);
@@ -299,13 +305,15 @@ TEST_CASE(InlayHintsWithoutCompile) {
}
TEST_CASE(MultipleSequentialRequests) {
TempFile src("seq_test.cpp",
TempDir tmp;
tmp.touch("seq_test.cpp",
"int foo(int x) {\n"
" return x + 1;\n"
"}\n"
"int main() {\n"
" return foo(0);\n"
"}\n");
auto src = tmp.path("seq_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateful-worker"));
@@ -315,24 +323,24 @@ TEST_CASE(MultipleSequentialRequests) {
w.run([&]() -> et::task<> {
// Compile first so feature requests return real data.
worker::CompileParams cp;
cp.path = src.path;
cp.path = src;
cp.version = 1;
cp.text = "int foo(int x) {\n return x + 1;\n}\nint main() {\n return foo(0);\n}\n";
cp.directory = "/tmp";
cp.arguments = make_args(src.path);
cp.arguments = make_args(src);
auto cr = co_await w.peer->send_request(cp);
CO_ASSERT_TRUE(cr.has_value());
// Now send multiple different feature requests sequentially.
worker::HoverParams hp;
hp.path = src.path;
hp.path = src;
hp.offset = 4; // 'foo' on line 0
auto r1 = co_await w.peer->send_request(hp);
EXPECT_TRUE(r1.has_value());
worker::CodeActionParams cap;
cap.path = src.path;
cap.path = src;
auto r2 = co_await w.peer->send_request(cap);
EXPECT_TRUE(r2.has_value());
@@ -340,18 +348,18 @@ TEST_CASE(MultipleSequentialRequests) {
// lines: "int foo(int x) {\n"=17, " return x + 1;\n"=18, "}\n"=2, "int main() {\n"=14
// offset = 17+18+2+14+11 = 62
worker::GoToDefinitionParams gdp;
gdp.path = src.path;
gdp.path = src;
gdp.offset = 62;
auto r3 = co_await w.peer->send_request(gdp);
EXPECT_TRUE(r3.has_value());
worker::SemanticTokensParams stp;
stp.path = src.path;
stp.path = src;
auto r4 = co_await w.peer->send_request(stp);
EXPECT_TRUE(r4.has_value());
worker::FoldingRangeParams frp;
frp.path = src.path;
frp.path = src;
auto r5 = co_await w.peer->send_request(frp);
EXPECT_TRUE(r5.has_value());
@@ -363,12 +371,15 @@ TEST_CASE(MultipleSequentialRequests) {
}
TEST_CASE(MultipleDocuments) {
std::vector<std::unique_ptr<TempFile>> files;
TempDir tmp;
std::vector<std::string> paths;
std::vector<std::string> texts;
for(int i = 0; i < 3; i++) {
auto name = "multi_" + std::to_string(i) + ".cpp";
auto text = "int var_" + std::to_string(i) + " = " + std::to_string(i) + ";\n";
tmp.touch(name, text);
paths.push_back(tmp.path(name));
texts.push_back(text);
files.push_back(std::make_unique<TempFile>("multi_" + std::to_string(i) + ".cpp", text));
}
WorkerHandle w;
@@ -380,11 +391,11 @@ TEST_CASE(MultipleDocuments) {
// Compile 3 different documents.
for(int i = 0; i < 3; i++) {
worker::CompileParams cp;
cp.path = files[i]->path;
cp.path = paths[i];
cp.version = 1;
cp.text = texts[i];
cp.directory = "/tmp";
cp.arguments = make_args(files[i]->path);
cp.arguments = make_args(paths[i]);
auto result = co_await w.peer->send_request(cp);
EXPECT_TRUE(result.has_value());
@@ -393,7 +404,7 @@ TEST_CASE(MultipleDocuments) {
// Hover on each document after compilation.
for(int i = 0; i < 3; i++) {
worker::HoverParams hp;
hp.path = files[i]->path;
hp.path = paths[i];
hp.offset = 4; // 'var_N'
auto result = co_await w.peer->send_request(hp);
@@ -436,7 +447,9 @@ TEST_CASE(EvictNotification) {
}
TEST_CASE(SpawnWithMemoryLimit) {
TempFile src("memlimit_test.cpp", "int memlimit_var = 42;\n");
TempDir tmp;
tmp.touch("memlimit_test.cpp", "int memlimit_var = 42;\n");
auto src = tmp.path("memlimit_test.cpp");
WorkerHandle w;
// Spawn with a specific memory limit to test the CLI flag is accepted.
@@ -447,18 +460,18 @@ TEST_CASE(SpawnWithMemoryLimit) {
w.run([&]() -> et::task<> {
// Compile first.
worker::CompileParams cp;
cp.path = src.path;
cp.path = src;
cp.version = 1;
cp.text = "int memlimit_var = 42;\n";
cp.directory = "/tmp";
cp.arguments = make_args(src.path);
cp.arguments = make_args(src);
auto cr = co_await w.peer->send_request(cp);
EXPECT_TRUE(cr.has_value());
// Feature request should work after compilation.
worker::HoverParams hp;
hp.path = src.path;
hp.path = src;
hp.offset = 4; // 'memlimit_var'
auto result = co_await w.peer->send_request(hp);

View File

@@ -83,7 +83,9 @@ TEST_CASE(SpawnAndExit) {
}
TEST_CASE(BuildPCHRequest) {
TempFile hdr("test_pch.h", "#pragma once\nint pch_global = 42;\n");
TempDir tmp;
tmp.touch("test_pch.h", "#pragma once\nint pch_global = 42;\n");
auto hdr = tmp.path("test_pch.h");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateless-worker"));
@@ -92,10 +94,10 @@ TEST_CASE(BuildPCHRequest) {
w.run([&]() -> et::task<> {
worker::BuildPCHParams params;
params.file = hdr.path;
params.file = hdr;
params.directory = "/tmp";
params.arguments =
{"clang++", "-resource-dir", std::string(resource_dir()), "-x", "c++-header", hdr.path};
{"clang++", "-resource-dir", std::string(resource_dir()), "-x", "c++-header", hdr};
params.content = "#pragma once\nint pch_global = 42;\n";
auto result = co_await w.peer->send_request(params);
@@ -108,7 +110,9 @@ TEST_CASE(BuildPCHRequest) {
}
TEST_CASE(IndexRequest) {
TempFile src("test_index.cpp", "int indexed_var = 1;\n");
TempDir tmp;
tmp.touch("test_index.cpp", "int indexed_var = 1;\n");
auto src = tmp.path("test_index.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateless-worker"));
@@ -117,9 +121,9 @@ TEST_CASE(IndexRequest) {
w.run([&]() -> et::task<> {
worker::IndexParams params;
params.file = src.path;
params.file = src;
params.directory = "/tmp";
params.arguments = make_args(src.path);
params.arguments = make_args(src);
auto result = co_await w.peer->send_request(params);
EXPECT_TRUE(result.has_value());
@@ -139,8 +143,10 @@ TEST_CASE(IndexRequest) {
TEST_SUITE(StatelessWorkerExtended) {
TEST_CASE(BuildPCMRequest) {
TempFile src("test_module.cppm",
"export module test_module;\nexport int module_func() { return 1; }\n");
TempDir tmp;
tmp.touch("test_module.cppm",
"export module test_module;\nexport int module_func() { return 1; }\n");
auto src = tmp.path("test_module.cppm");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateless-worker"));
@@ -149,14 +155,14 @@ TEST_CASE(BuildPCMRequest) {
w.run([&]() -> et::task<> {
worker::BuildPCMParams params;
params.file = src.path;
params.file = src;
params.directory = "/tmp";
params.arguments = {"clang++",
"-resource-dir",
std::string(resource_dir()),
"-std=c++20",
"--precompile",
src.path};
src};
params.module_name = "test_module";
auto result = co_await w.peer->send_request(params);
@@ -170,7 +176,9 @@ TEST_CASE(BuildPCMRequest) {
TEST_CASE(CompletionRequest) {
std::string text = "int foo = 1;\nint bar = fo";
TempFile src("completion_test.cpp", text);
TempDir tmp;
tmp.touch("completion_test.cpp", text);
auto src = tmp.path("completion_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateless-worker"));
@@ -179,11 +187,11 @@ TEST_CASE(CompletionRequest) {
w.run([&]() -> et::task<> {
worker::CompletionParams params;
params.path = src.path;
params.path = src;
params.version = 1;
params.text = text;
params.directory = "/tmp";
params.arguments = make_args(src.path);
params.arguments = make_args(src);
params.offset = 25; // after "fo" in "int bar = fo" (13 + 12)
auto result = co_await w.peer->send_request(params);
@@ -197,7 +205,9 @@ TEST_CASE(CompletionRequest) {
TEST_CASE(SignatureHelpRequest) {
std::string text = "void foo(int a, int b) {}\nint main() { foo(";
TempFile src("sighelp_test.cpp", text);
TempDir tmp;
tmp.touch("sighelp_test.cpp", text);
auto src = tmp.path("sighelp_test.cpp");
WorkerHandle w;
ASSERT_TRUE(w.spawn("stateless-worker"));
@@ -206,11 +216,11 @@ TEST_CASE(SignatureHelpRequest) {
w.run([&]() -> et::task<> {
worker::SignatureHelpParams params;
params.path = src.path;
params.path = src;
params.version = 1;
params.text = text;
params.directory = "/tmp";
params.arguments = make_args(src.path);
params.arguments = make_args(src);
params.offset = 45; // after "foo(" (26 + 19)
auto result = co_await w.peer->send_request(params);
@@ -224,11 +234,13 @@ TEST_CASE(SignatureHelpRequest) {
}
TEST_CASE(MultipleStatelessRequests) {
std::vector<std::unique_ptr<TempFile>> files;
TempDir tmp;
std::vector<std::string> paths;
for(int i = 0; i < 3; i++) {
auto name = "multi_index_" + std::to_string(i) + ".cpp";
auto text = "int idx_var_" + std::to_string(i) + " = " + std::to_string(i) + ";\n";
files.push_back(
std::make_unique<TempFile>("multi_index_" + std::to_string(i) + ".cpp", text));
tmp.touch(name, text);
paths.push_back(tmp.path(name));
}
WorkerHandle w;
@@ -240,9 +252,9 @@ TEST_CASE(MultipleStatelessRequests) {
// Send multiple index requests to test stateless worker handles them sequentially.
for(int i = 0; i < 3; i++) {
worker::IndexParams params;
params.file = files[i]->path;
params.file = paths[i];
params.directory = "/tmp";
params.arguments = make_args(files[i]->path);
params.arguments = make_args(paths[i]);
auto result = co_await w.peer->send_request(params);
EXPECT_TRUE(result.has_value());

View File

@@ -1,8 +1,6 @@
#pragma once
#include <csignal>
#include <fstream>
#include <memory>
#include <string>
#ifndef _WIN32
@@ -10,6 +8,7 @@
#include <unistd.h>
#endif
#include "test/temp_dir.h"
#include "command/argument_parser.h"
#include "command/command.h"
#include "eventide/async/async.h"
@@ -47,31 +46,6 @@ inline std::string clice_binary() {
return std::string(path);
}
/// RAII temporary file: writes content to disk, removes on destruction.
struct TempFile {
std::string path;
TempFile(const std::string& name, const std::string& content) {
llvm::SmallString<256> tmp_dir;
llvm::sys::path::system_temp_directory(true, tmp_dir);
llvm::sys::path::append(tmp_dir, "clice_test_" + name);
path = std::string(tmp_dir);
std::ofstream ofs(path);
ofs << content;
}
~TempFile() {
std::remove(path.c_str());
}
std::string uri() const {
return "file://" + path;
}
TempFile(const TempFile&) = delete;
TempFile& operator=(const TempFile&) = delete;
};
/// Build compile arguments for a source file, including -resource-dir.
inline std::vector<std::string> make_args(const std::string& file_path,
const std::string& extra = "") {