GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 0 / 277 / 277
Functions: -% 0 / 50 / 50
Branches: -% 0 / 1700 / 1700

test/unittest/UnitDriver.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include <gtest/gtest.h>
4
5 #include <driver/Driver.h>
6 #include <exception/CliError.h>
7
8 // LCOV_EXCL_START
9
10 namespace spice::testing {
11
12 using namespace spice::compiler;
13
14 TEST(DriverTest, BuildSubcommandMinimal) {
15 const char *argv[] = {"spice", "build", "../../media/test-project/test.spice"};
16 static constexpr int argc = std::size(argv);
17 CliOptions cliOptions;
18 Driver driver(cliOptions, true);
19 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
20 driver.enrich();
21
22 ASSERT_TRUE(driver.shouldCompile);
23 ASSERT_FALSE(driver.shouldInstall);
24 ASSERT_FALSE(driver.shouldUninstall);
25 ASSERT_FALSE(driver.shouldExecute);
26 ASSERT_FALSE(cliOptions.execute);
27 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
28 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
29 ASSERT_EQ(BuildMode::DEBUG, cliOptions.buildMode);
30 ASSERT_FALSE(cliOptions.generateTestMain);
31 ASSERT_FALSE(cliOptions.testMode);
32 ASSERT_FALSE(cliOptions.noEntryFct);
33 }
34
35 TEST(DriverTest, BuildSubcommandComplex) {
36 const char *argv[] = {
37 "spice",
38 "b",
39 "-d",
40 "-ir",
41 "-g",
42 "-Os",
43 "-m",
44 "release",
45 "-lto",
46 "--sanitizer=address",
47 "--output-container=exec",
48 "../../media/test-project/test.spice",
49 };
50 static constexpr int argc = std::size(argv);
51 CliOptions cliOptions;
52 Driver driver(cliOptions, true);
53 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
54 driver.enrich();
55
56 ASSERT_TRUE(driver.shouldCompile);
57 ASSERT_FALSE(driver.shouldInstall);
58 ASSERT_FALSE(driver.shouldUninstall);
59 ASSERT_FALSE(driver.shouldExecute);
60 ASSERT_FALSE(cliOptions.execute);
61 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
62 ASSERT_EQ(OptLevel::Os, cliOptions.optLevel); // -Os
63 ASSERT_EQ(BuildMode::RELEASE, cliOptions.buildMode); // -m release
64 ASSERT_FALSE(cliOptions.generateTestMain);
65 ASSERT_FALSE(cliOptions.testMode);
66 ASSERT_FALSE(cliOptions.noEntryFct);
67 ASSERT_TRUE(cliOptions.instrumentation.generateDebugInfo); // -g
68 ASSERT_EQ(Sanitizer::ADDRESS, cliOptions.instrumentation.sanitizer); // --sanitizer=address
69 ASSERT_EQ(OutputContainer::EXECUTABLE, cliOptions.outputContainer); // --output-container=exec
70 ASSERT_TRUE(cliOptions.useLTO); // -lto
71 ASSERT_TRUE(cliOptions.printDebugOutput); // -d
72 ASSERT_TRUE(cliOptions.dump.dumpIR); // -ir
73 ASSERT_TRUE(cliOptions.useLifetimeMarkers); // implicitly due to enabled address sanitizer
74 }
75
76 TEST(DriverTest, RunSubcommandMinimal) {
77 const char *argv[] = {"spice", "run", "../../media/test-project/test.spice"};
78 static constexpr int argc = std::size(argv);
79 CliOptions cliOptions;
80 Driver driver(cliOptions, true);
81 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
82 driver.enrich();
83
84 ASSERT_TRUE(driver.shouldCompile);
85 ASSERT_FALSE(driver.shouldInstall);
86 ASSERT_FALSE(driver.shouldUninstall);
87 ASSERT_TRUE(driver.shouldExecute);
88 ASSERT_TRUE(cliOptions.execute);
89 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
90 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
91 ASSERT_FALSE(cliOptions.generateTestMain);
92 ASSERT_FALSE(cliOptions.testMode);
93 ASSERT_FALSE(cliOptions.noEntryFct);
94 }
95
96 TEST(DriverTest, RunSubcommandComplex) {
97 const char *argv[] = {"spice", "r", "-O2", "-j", "8", "-ast", "../../media/test-project/test.spice"};
98 static constexpr int argc = std::size(argv);
99 CliOptions cliOptions;
100 Driver driver(cliOptions, true);
101 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
102 driver.enrich();
103
104 ASSERT_TRUE(driver.shouldCompile);
105 ASSERT_FALSE(driver.shouldInstall);
106 ASSERT_FALSE(driver.shouldUninstall);
107 ASSERT_TRUE(driver.shouldExecute);
108 ASSERT_TRUE(cliOptions.execute);
109 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
110 ASSERT_EQ(OptLevel::O2, cliOptions.optLevel); // -O2
111 ASSERT_FALSE(cliOptions.generateTestMain);
112 ASSERT_FALSE(cliOptions.testMode);
113 ASSERT_FALSE(cliOptions.noEntryFct);
114 ASSERT_EQ(8, cliOptions.compileJobCount); // -j 8
115 ASSERT_TRUE(cliOptions.dump.dumpAST); // -ast
116 }
117
118 TEST(DriverTest, TestSubcommandMinimal) {
119 const char *argv[] = {"spice", "test", "../../media/test-project/test.spice"};
120 static constexpr int argc = std::size(argv);
121 CliOptions cliOptions;
122 Driver driver(cliOptions, true);
123 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
124 driver.enrich();
125
126 ASSERT_TRUE(driver.shouldCompile);
127 ASSERT_FALSE(driver.shouldInstall);
128 ASSERT_FALSE(driver.shouldUninstall);
129 ASSERT_TRUE(driver.shouldExecute);
130 ASSERT_TRUE(cliOptions.execute);
131 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
132 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
133 ASSERT_EQ(BuildMode::TEST, cliOptions.buildMode); // -m test
134 ASSERT_TRUE(cliOptions.generateTestMain);
135 ASSERT_FALSE(cliOptions.testMode);
136 ASSERT_TRUE(cliOptions.noEntryFct);
137 }
138
139 TEST(DriverTest, TestSubcommandComplex) {
140 const char *argv[] = {"spice", "t", "-s", "-cst", "--sanitizer=thread", "../../media/test-project/test.spice"};
141 static constexpr int argc = std::size(argv);
142 CliOptions cliOptions;
143 Driver driver(cliOptions, true);
144 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
145 driver.enrich();
146
147 ASSERT_TRUE(driver.shouldCompile);
148 ASSERT_FALSE(driver.shouldInstall);
149 ASSERT_FALSE(driver.shouldUninstall);
150 ASSERT_TRUE(driver.shouldExecute);
151 ASSERT_TRUE(cliOptions.execute);
152 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
153 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
154 ASSERT_TRUE(cliOptions.generateTestMain);
155 ASSERT_TRUE(cliOptions.noEntryFct);
156 ASSERT_TRUE(cliOptions.dump.dumpCST); // -cst
157 ASSERT_TRUE(cliOptions.dump.dumpAssembly); // -s
158 ASSERT_EQ(Sanitizer::THREAD, cliOptions.instrumentation.sanitizer); // --sanitizer=thread
159 }
160
161 TEST(DriverTest, InstallSubcommandMinimal) {
162 const char *argv[] = {"spice", "install", "../../media/test-project/test.spice"};
163 static constexpr int argc = std::size(argv);
164 CliOptions cliOptions;
165 Driver driver(cliOptions, true);
166 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
167 driver.enrich();
168
169 ASSERT_TRUE(driver.shouldCompile);
170 ASSERT_TRUE(driver.shouldInstall);
171 ASSERT_FALSE(driver.shouldUninstall);
172 ASSERT_FALSE(driver.shouldExecute);
173 ASSERT_FALSE(cliOptions.execute);
174 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
175 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
176 ASSERT_FALSE(cliOptions.generateTestMain);
177 ASSERT_FALSE(cliOptions.testMode);
178 ASSERT_FALSE(cliOptions.noEntryFct);
179 }
180
181 TEST(DriverTest, UninstallSubcommandMinimal) {
182 const char *argv[] = {"spice", "uninstall", "../../media/test-project/test.spice"};
183 static constexpr int argc = std::size(argv);
184 CliOptions cliOptions;
185 Driver driver(cliOptions, true);
186 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
187 driver.enrich();
188
189 ASSERT_FALSE(driver.shouldCompile);
190 ASSERT_FALSE(driver.shouldInstall);
191 ASSERT_TRUE(driver.shouldUninstall);
192 ASSERT_FALSE(driver.shouldExecute);
193 ASSERT_FALSE(cliOptions.execute);
194 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
195 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
196 ASSERT_FALSE(cliOptions.generateTestMain);
197 ASSERT_FALSE(cliOptions.testMode);
198 ASSERT_FALSE(cliOptions.noEntryFct);
199 }
200
201 TEST(DriverTest, MemorySanitizerOnlyLinux) {
202 const char *argv[] = {"spice", "build", "--sanitizer=memory", "../../media/test-project/test.spice"};
203 static constexpr int argc = std::size(argv);
204 CliOptions cliOptions;
205 Driver driver(cliOptions, true);
206
207 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
208 #if OS_LINUX
209 driver.enrich();
210 ASSERT_EQ(Sanitizer::MEMORY, cliOptions.instrumentation.sanitizer);
211 ASSERT_TRUE(cliOptions.useLifetimeMarkers); // implicitly due to enabled address sanitizer
212 #else
213 try {
214 driver.enrich();
215 FAIL();
216 } catch (CliError &error) {
217 auto errorMsg = "[Error|CLI] Feature is not supported for this target: Memory sanitizer is only supported for Linux targets";
218 ASSERT_STREQ(errorMsg, error.what());
219 }
220 #endif
221 }
222
223 TEST(DriverTest, IncompatibleOptions) {
224 // --static in combination with --output-container=dylib is not allowed
225 const char *argv[] = {"spice", "build", "--static", "--output-container=dylib", "../../media/test-project/test.spice"};
226 static constexpr int argc = std::size(argv);
227 CliOptions cliOptions;
228 Driver driver(cliOptions, true);
229 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
230
231 try {
232 driver.enrich();
233 } catch (CliError &error) {
234 const auto errorMsg = "[Error|CLI] Incompatible options: Cannot link statically if compiling shared library";
235 ASSERT_STREQ(errorMsg, error.what());
236 }
237 }
238
239 TEST(DriverTest, BackendLlvmAcceptedWhenTpdeDisabled) {
240 // The default `llvm` backend must always be selectable, regardless of SPICE_ENABLE_TPDE.
241 const char *argv[] = {"spice", "build", "--backend=llvm", "../../media/test-project/test.spice"};
242 static constexpr int argc = std::size(argv);
243 CliOptions cliOptions;
244 Driver driver(cliOptions, true);
245 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
246 driver.enrich();
247
248 ASSERT_EQ(Backend::LLVM, cliOptions.backend);
249 ASSERT_EQ("llvm", cliOptions.buildVars["spice.backend"]);
250 }
251
252 TEST(DriverTest, BackendTpdeRejectedWhenBuildOptionDisabled) {
253 #ifdef SPICE_ENABLE_TPDE
254 GTEST_SKIP() << "TPDE backend is enabled in this build; rejection path is not exercised.";
255 #else
256 const char *argv[] = {"spice", "build", "--backend=tpde", "../../media/test-project/test.spice"};
257 static constexpr int argc = std::size(argv);
258 CliOptions cliOptions;
259 Driver driver(cliOptions, true);
260
261 try {
262 driver.parse(argc, argv);
263 FAIL();
264 } catch (CliError &error) {
265 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
266 "The TPDE backend is not available in this build. Rebuild the compiler with "
267 "-DSPICE_ENABLE_TPDE=ON.";
268 ASSERT_STREQ(errorMsg, error.what());
269 }
270 #endif
271 }
272
273 TEST(DriverTest, BackendTpdeRejectsLtoCombination) {
274 #ifndef SPICE_ENABLE_TPDE
275 GTEST_SKIP() << "TPDE backend is disabled in this build; combination guard is not exercised.";
276 #else
277 const char *argv[] = {"spice", "build", "--backend=tpde", "-lto", "../../media/test-project/test.spice"};
278 static constexpr int argc = std::size(argv);
279 CliOptions cliOptions;
280 Driver driver(cliOptions, true);
281 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
282
283 try {
284 driver.enrich();
285 FAIL();
286 } catch (CliError &error) {
287 const auto errorMsg = "[Error|CLI] Incompatible options: The TPDE backend does not support LTO";
288 ASSERT_STREQ(errorMsg, error.what());
289 }
290 #endif
291 }
292
293 TEST(DriverTest, BackendTpdeRejectsNonElfTarget) {
294 #ifndef SPICE_ENABLE_TPDE
295 GTEST_SKIP() << "TPDE backend is disabled in this build; target guard is not exercised.";
296 #else
297 const char *argv[] = {"spice", "build", "--backend=tpde", "--target=x86_64-pc-windows-msvc",
298 "../../media/test-project/test.spice"};
299 static constexpr int argc = std::size(argv);
300 CliOptions cliOptions;
301 Driver driver(cliOptions, true);
302 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
303
304 try {
305 driver.enrich();
306 FAIL();
307 } catch (CliError &error) {
308 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
309 "The TPDE backend only supports ELF targets (Linux)";
310 ASSERT_STREQ(errorMsg, error.what());
311 }
312 #endif
313 }
314
315 TEST(DriverTest, BackendTpdeRejectsUnsupportedArch) {
316 #ifndef SPICE_ENABLE_TPDE
317 GTEST_SKIP() << "TPDE backend is disabled in this build; arch guard is not exercised.";
318 #else
319 // Linux/RISC-V — supported OS, but arch is not one of TPDE's supported set (x86_64, aarch64).
320 const char *argv[] = {"spice", "build", "--backend=tpde", "--target=riscv64-unknown-linux-gnu",
321 "../../media/test-project/test.spice"};
322 static constexpr int argc = std::size(argv);
323 CliOptions cliOptions;
324 Driver driver(cliOptions, true);
325 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
326
327 try {
328 driver.enrich();
329 FAIL();
330 } catch (CliError &error) {
331 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
332 "The TPDE backend only supports x86_64 and aarch64";
333 ASSERT_STREQ(errorMsg, error.what());
334 }
335 #endif
336 }
337
338 using DriverInvalidEnumTestParam = std::pair<const char *, const char *>;
339 class DriverTest : public ::testing::TestWithParam<DriverInvalidEnumTestParam> {};
340
341 TEST_P(DriverTest, InvalidEnumValue) {
342 const auto &[arg, errorMessage] = GetParam();
343 const char *argv[] = {"spice", "build", arg, "../../media/test-project/test.spice"};
344 static constexpr int argc = std::size(argv);
345 CliOptions cliOptions;
346 Driver driver(cliOptions, true);
347 try {
348 driver.parse(argc, argv);
349 FAIL();
350 } catch (CliError &error) {
351 ASSERT_STREQ(errorMessage, error.what());
352 }
353 }
354
355 const auto INVALID_ENUM_TEST_VALUES = ::testing::Values(
356 DriverInvalidEnumTestParam{
357 "--build-mode=unknown",
358 "[Error|CLI] Invalid build mode: unknown",
359 },
360 DriverInvalidEnumTestParam{
361 "--sanitizer=unknown",
362 "[Error|CLI] Invalid sanitizer: unknown",
363 },
364 DriverInvalidEnumTestParam{
365 "--output-container=unknown",
366 "[Error|CLI] Invalid output container: unknown",
367 },
368 DriverInvalidEnumTestParam{
369 "--backend=unknown",
370 "[Error|CLI] Invalid backend: unknown",
371 });
372 INSTANTIATE_TEST_SUITE_P(DriverTest, DriverTest, INVALID_ENUM_TEST_VALUES);
373
374 } // namespace spice::testing
375
376 // LCOV_EXCL_STOP
377