LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
EnumCAPITestGen.cpp
Go to the documentation of this file.
1//===- EnumCAPITestGen.cpp - C API test generator for enums ---------------===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9//
10// EnumCAPITestGen generates unit tests for the C API enums generated by
11// EnumCAPIGen. These are link-time tests that ensure all generated enum
12// declarations and wrap/unwrap functions compile and link properly.
13//
14// Test Strategy:
15// - Tests verify enum values can be assigned and compared
16// - Tests verify wrap/unwrap functions compile and link (though the C++ type
17// doesn't exist in test context, the functions still need to link)
18// - This ensures all enum cases are properly declared and wrap/unwrap functions
19// have correct signatures
20//
21//===----------------------------------------------------------------------===//
22
23#include "CAPIGenRegistration.h"
24#include "CommonCAPIGen.h"
25
26#include <mlir/TableGen/EnumInfo.h>
27#include <mlir/TableGen/GenInfo.h>
28
29#include <llvm/ADT/StringExtras.h>
30#include <llvm/Support/CommandLine.h>
31#include <llvm/Support/FormatVariadic.h>
32#include <llvm/TableGen/Record.h>
33#include <llvm/TableGen/TableGenBackend.h>
34
35using namespace mlir;
36using namespace mlir::tblgen;
37
38namespace {
39
44struct EnumTestGenerator : public TestGenerator {
47 EnumTestGenerator(llvm::raw_ostream &outputStream) : TestGenerator("Enum", outputStream) {}
48
49 void genExtraMethod(const ExtraMethod & /*method*/) const override {
50 llvm_unreachable("Enums cannot have extra methods");
51 }
52
56 void genEnumUsageTest(StringRef cEnumName, StringRef firstCaseValue) const {
57 static constexpr char fmt[] = R"(
59TEST_F({0}EnumLinkTests, Enum_{1}_Usage) {{
60 // We create a variable and check that enum values can be assigned.
61 {1} enumValue = {2};
62 (void)enumValue;
63
64 // Verify we can compare enum values
65 EXPECT_EQ(enumValue, {2});
66}
67)";
68 os << llvm::formatv(fmt, dialectNameCapitalized, cEnumName, firstCaseValue);
69 }
70
74 void genWrapUnwrapTest(StringRef cEnumName, StringRef firstCaseValue) const {
75 static constexpr char fmt[] = R"(
77TEST_F({0}EnumLinkTests, Enum_{1}_WrapUnwrap) {{
78 // We use the first enum case value for testing.
79 {1} cValue = {2};
80
81 // Test that wrap and unwrap are inverses (at compile/link time)
82 // The actual C++ type doesn't exist in test context, so we just
83 // verify the functions exist and link.
84 {1} roundTrip = wrap(unwrap(cValue));
85 EXPECT_EQ(cValue, roundTrip);
86}
87)";
88 os << llvm::formatv(fmt, dialectNameCapitalized, cEnumName, firstCaseValue);
89 }
90
93 void genCompleteRecord(const EnumInfo &enumInfo) {
94 // EnumInfo does not contain a Dialect reference, so filter by C++ namespace instead.
95 if (!getDialectName().empty() &&
96 !enumInfo.getCppNamespace().contains_insensitive(getDialectName())) {
97 return;
98 }
99
100 std::vector<EnumCase> enumCases = enumInfo.getAllCases();
101 if (enumCases.empty()) {
102 return;
103 }
104
105 // Get enum name
106 std::string cEnumName = toPascalCase(getFunctionPrefix()) + toPascalCase(getDialectName()) +
107 toPascalCase(enumInfo.getEnumClassName());
108
109 // Get first enum case for testing
110 std::string firstCaseValue =
111 llvm::formatv("{0}_{1}", cEnumName, enumCases[0].getSymbol()).str();
112
113 // Generate enum usage test
114 this->genEnumUsageTest(cEnumName, firstCaseValue);
115
116 // Generate wrap/unwrap test
117 this->genWrapUnwrapTest(cEnumName, firstCaseValue);
118 }
119};
120
121} // namespace
122
124static bool emitEnumCAPITests(const llvm::RecordKeeper &records, raw_ostream &os) {
125 // Generate file header
126 emitSourceFileHeader("Enum C API Tests", os, records);
127
128 // Create generator
129 EnumTestGenerator generator(os);
130
131 // Generate test class prologue
132 generator.genTestClassPrologue();
133
134 // Generate tests for each enum
135 for (const auto *def : records.getAllDerivedDefinitionsIfDefined("EnumAttrInfo")) {
136 EnumInfo enumInfo(def);
137 generator.genCompleteRecord(enumInfo);
138 }
139
140 return false;
141}
142
144 static mlir::GenRegistration genEnumCAPITests(
145 "gen-enum-capi-tests", "Generate enum C API unit tests", &emitEnumCAPITests
146 );
147}
llvm::cl::opt< std::string > & getFunctionPrefix()
Return the FunctionPrefix option, registering it on first access.
llvm::cl::opt< std::string > & getDialectName()
Return the DialectName option, registering it on first access.
std::string toPascalCase(mlir::StringRef str)
Convert names separated by underscore or colon to PascalCase.
void registerEnumCAPITestGenerator()
Register the enum C API test generator before command-line parsing.
Generator for common test implementation file elements.