GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 100.0% 0 / 365 / 365
Functions: -% 0 / 65 / 65
Branches: -% 0 / 2400 / 2400

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, LintSubcommandMinimal) {
162 const char *argv[] = {"spice", "lint", "../../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_FALSE(driver.shouldInstall);
171 ASSERT_FALSE(driver.shouldUninstall);
172 ASSERT_FALSE(driver.shouldExecute);
173 ASSERT_FALSE(cliOptions.execute);
174 ASSERT_TRUE(cliOptions.lintOnly);
175 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
176 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
177 ASSERT_EQ(BuildMode::DEBUG, cliOptions.buildMode);
178 ASSERT_FALSE(cliOptions.generateTestMain);
179 ASSERT_FALSE(cliOptions.testMode);
180 ASSERT_FALSE(cliOptions.noEntryFct);
181 }
182
183 TEST(DriverTest, LintSubcommandComplex) {
184 const char *argv[] = {"spice", "l", "-d", "-ast", "-Os", "-m", "release", "../../media/test-project/test.spice"};
185 static constexpr int argc = std::size(argv);
186 CliOptions cliOptions;
187 Driver driver(cliOptions, true);
188 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
189 driver.enrich();
190
191 ASSERT_TRUE(driver.shouldCompile);
192 ASSERT_FALSE(driver.shouldInstall);
193 ASSERT_FALSE(driver.shouldUninstall);
194 ASSERT_FALSE(driver.shouldExecute);
195 ASSERT_FALSE(cliOptions.execute);
196 ASSERT_TRUE(cliOptions.lintOnly);
197 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
198 ASSERT_EQ(OptLevel::Os, cliOptions.optLevel); // -Os
199 ASSERT_EQ(BuildMode::RELEASE, cliOptions.buildMode); // -m release
200 ASSERT_FALSE(cliOptions.generateTestMain);
201 ASSERT_FALSE(cliOptions.testMode);
202 ASSERT_FALSE(cliOptions.noEntryFct);
203 ASSERT_TRUE(cliOptions.printDebugOutput); // -d
204 ASSERT_TRUE(cliOptions.dump.dumpAST); // -ast
205 }
206
207 TEST(DriverTest, InstallSubcommandMinimal) {
208 const char *argv[] = {"spice", "install", "../../media/test-project/test.spice"};
209 static constexpr int argc = std::size(argv);
210 CliOptions cliOptions;
211 Driver driver(cliOptions, true);
212 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
213 driver.enrich();
214
215 ASSERT_TRUE(driver.shouldCompile);
216 ASSERT_TRUE(driver.shouldInstall);
217 ASSERT_FALSE(driver.shouldUninstall);
218 ASSERT_FALSE(driver.shouldExecute);
219 ASSERT_FALSE(cliOptions.execute);
220 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
221 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
222 ASSERT_FALSE(cliOptions.generateTestMain);
223 ASSERT_FALSE(cliOptions.testMode);
224 ASSERT_FALSE(cliOptions.noEntryFct);
225 }
226
227 TEST(DriverTest, UninstallSubcommandMinimal) {
228 const char *argv[] = {"spice", "uninstall", "../../media/test-project/test.spice"};
229 static constexpr int argc = std::size(argv);
230 CliOptions cliOptions;
231 Driver driver(cliOptions, true);
232 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
233 driver.enrich();
234
235 ASSERT_FALSE(driver.shouldCompile);
236 ASSERT_FALSE(driver.shouldInstall);
237 ASSERT_TRUE(driver.shouldUninstall);
238 ASSERT_FALSE(driver.shouldExecute);
239 ASSERT_FALSE(cliOptions.execute);
240 ASSERT_EQ("../../media/test-project/test.spice", cliOptions.mainSourceFile.relative_path().generic_string());
241 ASSERT_EQ(OptLevel::O0, cliOptions.optLevel);
242 ASSERT_FALSE(cliOptions.generateTestMain);
243 ASSERT_FALSE(cliOptions.testMode);
244 ASSERT_FALSE(cliOptions.noEntryFct);
245 }
246
247 TEST(DriverTest, MemorySanitizerOnlyLinux) {
248 const char *argv[] = {"spice", "build", "--sanitizer=memory", "../../media/test-project/test.spice"};
249 static constexpr int argc = std::size(argv);
250 CliOptions cliOptions;
251 Driver driver(cliOptions, true);
252
253 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
254 #if OS_LINUX
255 driver.enrich();
256 ASSERT_EQ(Sanitizer::MEMORY, cliOptions.instrumentation.sanitizer);
257 ASSERT_TRUE(cliOptions.useLifetimeMarkers); // implicitly due to enabled address sanitizer
258 #else
259 try {
260 driver.enrich();
261 FAIL();
262 } catch (CliError &error) {
263 auto errorMsg = "[Error|CLI] Feature is not supported for this target: Memory sanitizer is only supported for Linux targets";
264 ASSERT_STREQ(errorMsg, error.what());
265 }
266 #endif
267 }
268
269 TEST(DriverTest, CoverageImpliesDebugInfo) {
270 const char *argv[] = {"spice", "build", "--coverage", "../../media/test-project/test.spice"};
271 static constexpr int argc = std::size(argv);
272 CliOptions cliOptions;
273 Driver driver(cliOptions, true);
274 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
275 driver.enrich();
276
277 ASSERT_TRUE(cliOptions.instrumentation.codeCoverage);
278 ASSERT_TRUE(cliOptions.instrumentation.generateDebugInfo); // implicitly due to enabled code coverage
279 }
280
281 TEST(DriverTest, CoverageRejectsLtoCombination) {
282 const char *argv[] = {"spice", "build", "--coverage", "-lto", "../../media/test-project/test.spice"};
283 static constexpr int argc = std::size(argv);
284 CliOptions cliOptions;
285 Driver driver(cliOptions, true);
286 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
287
288 try {
289 driver.enrich();
290 FAIL();
291 } catch (CliError &error) {
292 const auto errorMsg = "[Error|CLI] Incompatible options: Code coverage instrumentation is not supported in combination with LTO";
293 ASSERT_STREQ(errorMsg, error.what());
294 }
295 }
296
297 TEST(DriverTest, IncompatibleOptions) {
298 // --static in combination with --output-container=dylib is not allowed
299 const char *argv[] = {"spice", "build", "--static", "--output-container=dylib", "../../media/test-project/test.spice"};
300 static constexpr int argc = std::size(argv);
301 CliOptions cliOptions;
302 Driver driver(cliOptions, true);
303 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
304
305 try {
306 driver.enrich();
307 } catch (CliError &error) {
308 const auto errorMsg = "[Error|CLI] Incompatible options: Cannot link statically if compiling shared library";
309 ASSERT_STREQ(errorMsg, error.what());
310 }
311 }
312
313 TEST(DriverTest, BackendLlvmAcceptedWhenTpdeDisabled) {
314 // The default `llvm` backend must always be selectable, regardless of SPICE_ENABLE_TPDE.
315 const char *argv[] = {"spice", "build", "--backend=llvm", "../../media/test-project/test.spice"};
316 static constexpr int argc = std::size(argv);
317 CliOptions cliOptions;
318 Driver driver(cliOptions, true);
319 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
320 driver.enrich();
321
322 ASSERT_EQ(Backend::LLVM, cliOptions.backend);
323 ASSERT_EQ("llvm", cliOptions.buildVars["spice.backend"]);
324 }
325
326 TEST(DriverTest, BackendTpdeRejectedWhenBuildOptionDisabled) {
327 #ifdef SPICE_ENABLE_TPDE
328 GTEST_SKIP() << "TPDE backend is enabled in this build; rejection path is not exercised.";
329 #else
330 const char *argv[] = {"spice", "build", "--backend=tpde", "../../media/test-project/test.spice"};
331 static constexpr int argc = std::size(argv);
332 CliOptions cliOptions;
333 Driver driver(cliOptions, true);
334
335 try {
336 driver.parse(argc, argv);
337 FAIL();
338 } catch (CliError &error) {
339 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
340 "The TPDE backend is not available in this build. Rebuild the compiler with "
341 "-DSPICE_ENABLE_TPDE=ON.";
342 ASSERT_STREQ(errorMsg, error.what());
343 }
344 #endif
345 }
346
347 TEST(DriverTest, BackendTpdeRejectsLtoCombination) {
348 #ifndef SPICE_ENABLE_TPDE
349 GTEST_SKIP() << "TPDE backend is disabled in this build; combination guard is not exercised.";
350 #else
351 const char *argv[] = {"spice", "build", "--backend=tpde", "-lto", "../../media/test-project/test.spice"};
352 static constexpr int argc = std::size(argv);
353 CliOptions cliOptions;
354 Driver driver(cliOptions, true);
355 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
356
357 try {
358 driver.enrich();
359 FAIL();
360 } catch (CliError &error) {
361 const auto errorMsg = "[Error|CLI] Incompatible options: The TPDE backend does not support LTO";
362 ASSERT_STREQ(errorMsg, error.what());
363 }
364 #endif
365 }
366
367 TEST(DriverTest, BackendTpdeRejectsCoverageCombination) {
368 #ifndef SPICE_ENABLE_TPDE
369 GTEST_SKIP() << "TPDE backend is disabled in this build; combination guard is not exercised.";
370 #else
371 const char *argv[] = {"spice", "build", "--backend=tpde", "--coverage", "../../media/test-project/test.spice"};
372 static constexpr int argc = std::size(argv);
373 CliOptions cliOptions;
374 Driver driver(cliOptions, true);
375 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
376
377 try {
378 driver.enrich();
379 FAIL();
380 } catch (CliError &error) {
381 const auto errorMsg = "[Error|CLI] Incompatible options: The TPDE backend does not support code coverage instrumentation";
382 ASSERT_STREQ(errorMsg, error.what());
383 }
384 #endif
385 }
386
387 TEST(DriverTest, BackendTpdeRejectsNonElfTarget) {
388 #ifndef SPICE_ENABLE_TPDE
389 GTEST_SKIP() << "TPDE backend is disabled in this build; target guard is not exercised.";
390 #else
391 const char *argv[] = {"spice", "build", "--backend=tpde", "--target=x86_64-pc-windows-msvc",
392 "../../media/test-project/test.spice"};
393 static constexpr int argc = std::size(argv);
394 CliOptions cliOptions;
395 Driver driver(cliOptions, true);
396 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
397
398 try {
399 driver.enrich();
400 FAIL();
401 } catch (CliError &error) {
402 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
403 "The TPDE backend only supports ELF targets (Linux)";
404 ASSERT_STREQ(errorMsg, error.what());
405 }
406 #endif
407 }
408
409 TEST(DriverTest, BackendTpdeRejectsUnsupportedArch) {
410 #ifndef SPICE_ENABLE_TPDE
411 GTEST_SKIP() << "TPDE backend is disabled in this build; arch guard is not exercised.";
412 #else
413 // Linux/RISC-V — supported OS, but arch is not one of TPDE's supported set (x86_64, aarch64).
414 const char *argv[] = {"spice", "build", "--backend=tpde", "--target=riscv64-unknown-linux-gnu",
415 "../../media/test-project/test.spice"};
416 static constexpr int argc = std::size(argv);
417 CliOptions cliOptions;
418 Driver driver(cliOptions, true);
419 ASSERT_EQ(EXIT_SUCCESS, driver.parse(argc, argv));
420
421 try {
422 driver.enrich();
423 FAIL();
424 } catch (CliError &error) {
425 const auto errorMsg = "[Error|CLI] Feature is not supported for this target: "
426 "The TPDE backend only supports x86_64 and aarch64";
427 ASSERT_STREQ(errorMsg, error.what());
428 }
429 #endif
430 }
431
432 using DriverInvalidEnumTestParam = std::pair<const char *, const char *>;
433 class DriverTest : public ::testing::TestWithParam<DriverInvalidEnumTestParam> {};
434
435 TEST_P(DriverTest, InvalidEnumValue) {
436 const auto &[arg, errorMessage] = GetParam();
437 const char *argv[] = {"spice", "build", arg, "../../media/test-project/test.spice"};
438 static constexpr int argc = std::size(argv);
439 CliOptions cliOptions;
440 Driver driver(cliOptions, true);
441 try {
442 driver.parse(argc, argv);
443 FAIL();
444 } catch (CliError &error) {
445 ASSERT_STREQ(errorMessage, error.what());
446 }
447 }
448
449 const auto INVALID_ENUM_TEST_VALUES = ::testing::Values(
450 DriverInvalidEnumTestParam{
451 "--build-mode=unknown",
452 "[Error|CLI] Invalid build mode: unknown",
453 },
454 DriverInvalidEnumTestParam{
455 "--sanitizer=unknown",
456 "[Error|CLI] Invalid sanitizer: unknown",
457 },
458 DriverInvalidEnumTestParam{
459 "--output-container=unknown",
460 "[Error|CLI] Invalid output container: unknown",
461 },
462 DriverInvalidEnumTestParam{
463 "--backend=unknown",
464 "[Error|CLI] Invalid backend: unknown",
465 });
466 INSTANTIATE_TEST_SUITE_P(DriverTest, DriverTest, INVALID_ENUM_TEST_VALUES);
467
468 } // namespace spice::testing
469
470 // LCOV_EXCL_STOP
471