Skip to content

Commit b6defce

Browse files
ilovepiPeterChou1
andcommitted
[llvm] Add a tool to check mustache compliance against the public spec
This is a cli tool to that tests the conformance of LLVM's mustache implementation against the public Mustache spec, hosted at https://github.com/mustache/spec. This is a revised version of the patches in #111487. Co-authored-by: Peter Chou <[email protected]>
1 parent 89cea0d commit b6defce

File tree

6 files changed

+285
-0
lines changed

6 files changed

+285
-0
lines changed

llvm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ if( LLVM_INCLUDE_UTILS )
13021302
add_subdirectory(utils/yaml-bench)
13031303
add_subdirectory(utils/split-file)
13041304
add_subdirectory(utils/mlgo-utils)
1305+
add_subdirectory(utils/llvm-mustachespec)
13051306
if( LLVM_INCLUDE_TESTS )
13061307
set(LLVM_SUBPROJECT_TITLE "Third-Party/Google Test")
13071308
add_subdirectory(${LLVM_THIRD_PARTY_DIR}/unittest ${CMAKE_CURRENT_BINARY_DIR}/third-party/unittest)

llvm/docs/CommandGuide/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Developer Tools
8787
llvm-exegesis
8888
llvm-ifs
8989
llvm-locstats
90+
llvm-mustachespec
9091
llvm-pdbutil
9192
llvm-profgen
9293
llvm-tli-checker
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
llvm-mustachespec - LLVM tool to test Mustache Compliance Library
2+
=================================================================
3+
4+
llvm-mustachespec test the mustache spec conformance of the LLVM
5+
mustache library. The spec can be found here https://github.com/mustache/spec
6+
7+
$ llvm-mustachespec input-file
8+
9+
.. program:: llvm-mustachespec
10+
11+
Outputs the number of tests failures and success in the spec
12+
13+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
add_llvm_utility(llvm-mustachespec
2+
llvm-mustachespec.cpp
3+
)
4+
5+
target_link_libraries(llvm-mustachespec PRIVATE LLVMSupport)

llvm/utils/llvm-mustachespec/\

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Simple drivers to test the mustache spec found here
10+
// https://github.com/mustache/
11+
// It is used to verify that the current implementation conforms to the spec
12+
// Simply download the spec and pass the test files to the driver
13+
//
14+
// Currently Triple Mustache is not supported, so we expect the following spec
15+
// test to fail:
16+
// Triple Mustache
17+
// Triple Mustache Integer Interpolation
18+
// Triple Mustache Decimal Interpolation
19+
// Triple Mustache Null Interpolation
20+
// Triple Mustache Context Miss Interpolation
21+
// Dotted Names - Triple Mustache Interpolation
22+
// Implicit Iterators - Triple Mustache
23+
// Triple Mustache - Surrounding Whitespace
24+
// Triple Mustache - Standalone
25+
// Triple Mustache With Padding
26+
// Standalone Indentation
27+
// Implicit Iterator - Triple mustache
28+
//
29+
// Usage:
30+
// mustache path/to/test/file/test.json path/to/test/file/test2.json ...
31+
//===----------------------------------------------------------------------===//
32+
33+
#include "llvm/Support/CommandLine.h"
34+
#include "llvm/Support/Error.h"
35+
#include "llvm/Support/MemoryBuffer.h"
36+
#include "llvm/Support/Mustache.h"
37+
#include "llvm/Support/WithColor.h"
38+
#include <string>
39+
40+
using namespace llvm;
41+
using namespace llvm::json;
42+
using namespace llvm::mustache;
43+
44+
cl::list<std::string> InputFiles(cl::Positional, cl::desc("<input files>"),
45+
cl::OneOrMore);
46+
47+
static ExitOnError ExitOnErr;
48+
49+
void runThroughTest(StringRef InputFile) {
50+
outs() << "Running Tests: " << InputFile << "\n";
51+
std::unique_ptr<MemoryBuffer> Buffer =
52+
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
53+
54+
StringRef FileContent = Buffer->getBuffer();
55+
json::Value Json = ExitOnErr(parse(FileContent));
56+
57+
// Get test
58+
Array *Obj = Json.getAsObject()->getArray("tests");
59+
size_t Total = 0;
60+
size_t Success = 0;
61+
for (Value V : *Obj) {
62+
Object *TestCase = V.getAsObject();
63+
StringRef TemplateStr = TestCase->getString("template").value();
64+
StringRef ExpectedStr = TestCase->getString("expected").value();
65+
StringRef Name = TestCase->getString("name").value();
66+
Value *Data = TestCase->get("data");
67+
Value *Partials = TestCase->get("partials");
68+
69+
if (!Data)
70+
continue;
71+
72+
Template T = Template(TemplateStr);
73+
if (Partials) {
74+
for (auto &PartialPairs : *Partials->getAsObject()) {
75+
const auto &[Partial, Str] = PartialPairs;
76+
T.registerPartial(Str.getAsString()->str(), Partial.str());
77+
}
78+
}
79+
80+
std::string ActualStr;
81+
raw_string_ostream OS(ActualStr);
82+
T.render(*Data, OS);
83+
if (ExpectedStr == ActualStr) {
84+
Success++;
85+
} else {
86+
llvm::errs() << "Template: " << TemplateStr <<"\n";
87+
if (Partials)
88+
Partials->print(llvm::errs());
89+
llvm::errs() << "JSON Data: "; Data->print(errs()); errs() << "\n";
90+
outs() << "Test Failed: " << Name << "\n"
91+
<< " Expected: \'" << ExpectedStr << "\'\n"
92+
<< " Actual: \'" << ActualStr << "\'\n";
93+
llvm::errs() << "==========\n";
94+
}
95+
Total++;
96+
}
97+
98+
outs() << "Result " << Success << "/" << Total << " succeeded\n";
99+
}
100+
101+
int main(int argc, char **argv) {
102+
ExitOnErr.setBanner(std::string(argv[0]) + " error:");
103+
cl::ParseCommandLineOptions(argc, argv);
104+
for (const auto &FileName : InputFiles) {
105+
runThroughTest(FileName);
106+
}
107+
return 0;
108+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Simple drivers to test the mustache spec found at
10+
// https://github.com/mustache/
11+
//
12+
// It is used to verify that the current implementation conforms to the spec.
13+
// Simply download the spec and pass the test files to the driver.
14+
//
15+
// The current implementation only supports non-optional parts of the spec, so
16+
// we do not expect any of the dynamic-names, inheritance, or lambda tests to
17+
// pass. Additionally, Triple Mustache is not supported, so we expect the
18+
// following tests to fail:
19+
// Triple Mustache
20+
// Triple Mustache Integer Interpolation
21+
// Triple Mustache Decimal Interpolation
22+
// Triple Mustache Null Interpolation
23+
// Triple Mustache Context Miss Interpolation
24+
// Dotted Names - Triple Mustache Interpolation
25+
// Implicit Iterators - Triple Mustache
26+
// Triple Mustache - Surrounding Whitespace
27+
// Triple Mustache - Standalone
28+
// Triple Mustache With Padding
29+
// Standalone Indentation
30+
// Implicit Iterator - Triple mustache
31+
//
32+
// Usage:
33+
// llvm-mustachespec path/to/test/file.json path/to/test/file2.json ...
34+
//===----------------------------------------------------------------------===//
35+
36+
#include "llvm/Support/CommandLine.h"
37+
#include "llvm/Support/Debug.h"
38+
#include "llvm/Support/Error.h"
39+
#include "llvm/Support/MemoryBuffer.h"
40+
#include "llvm/Support/Mustache.h"
41+
#include <string>
42+
43+
using namespace llvm;
44+
using namespace llvm::json;
45+
using namespace llvm::mustache;
46+
47+
#define DEBUG_TYPE "llvm-mustachespec"
48+
49+
static cl::OptionCategory Cat("llvm-mustachespec Options");
50+
51+
cl::list<std::string> InputFiles(cl::Positional, cl::desc("<input files>"),
52+
cl::OneOrMore);
53+
54+
cl::opt<bool> ReportErrors("report-errors",
55+
cl::desc("Report errors in spec tests"),
56+
cl::cat(Cat));
57+
58+
static ExitOnError ExitOnErr;
59+
60+
struct TestData {
61+
static Expected<TestData> createTestData(json::Object *TestCase,
62+
StringRef InputFile) {
63+
// If any of the needed elements are missing, we cannot continue.
64+
// NOTE: partials are optional in the test schema.
65+
if (!TestCase || !TestCase->getString("template") ||
66+
!TestCase->getString("expected") || !TestCase->getString("name") ||
67+
!TestCase->get("data"))
68+
return createStringError(
69+
llvm::inconvertibleErrorCode(),
70+
"invalid JSON schema in test file: " + InputFile + "\n");
71+
72+
return TestData{TestCase->getString("template").value(),
73+
TestCase->getString("expected").value(),
74+
TestCase->getString("name").value(), TestCase->get("data"),
75+
TestCase->get("partials")};
76+
}
77+
78+
TestData() = default;
79+
80+
StringRef TemplateStr;
81+
StringRef ExpectedStr;
82+
StringRef Name;
83+
Value *Data;
84+
Value *Partials;
85+
};
86+
87+
static void reportTestFailure(const TestData &TD, StringRef ActualStr) {
88+
LLVM_DEBUG(dbgs() << "Template: " << TD.TemplateStr << "\n");
89+
if (TD.Partials) {
90+
LLVM_DEBUG(dbgs() << "Partial: ");
91+
LLVM_DEBUG(TD.Partials->print(dbgs()));
92+
LLVM_DEBUG(dbgs() << "\n");
93+
}
94+
LLVM_DEBUG(dbgs() << "JSON Data: ");
95+
LLVM_DEBUG(TD.Data->print(dbgs()));
96+
LLVM_DEBUG(dbgs() << "\n");
97+
outs() << "Test Failed: " << TD.Name << "\n";
98+
if (ReportErrors) {
99+
outs() << " Expected: \'" << TD.ExpectedStr << "\'\n"
100+
<< " Actual: \'" << ActualStr << "\'\n"
101+
<< " ====================\n";
102+
}
103+
}
104+
105+
static void registerPartials(Value *Partials, Template &T) {
106+
if (!Partials)
107+
return;
108+
for (const auto &[Partial, Str] : *Partials->getAsObject())
109+
T.registerPartial(Partial.str(), Str.getAsString()->str());
110+
}
111+
112+
static json::Value readJsonFromFile(StringRef &InputFile) {
113+
std::unique_ptr<MemoryBuffer> Buffer =
114+
ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputFile)));
115+
return ExitOnErr(parse(Buffer->getBuffer()));
116+
}
117+
118+
static void runTest(StringRef InputFile) {
119+
outs() << "Running Tests: " << InputFile << "\n";
120+
json::Value Json = readJsonFromFile(InputFile);
121+
122+
json::Object *Obj = Json.getAsObject();
123+
Array *TestArray = Obj->getArray("tests");
124+
// Even though we parsed the JSON, it can have a bad format, so check it.
125+
if (!TestArray)
126+
ExitOnErr(createStringError(
127+
llvm::inconvertibleErrorCode(),
128+
"invalid JSON schema in test file: " + InputFile + "\n"));
129+
130+
const size_t Total = TestArray->size();
131+
size_t Success = 0;
132+
133+
for (Value V : *TestArray) {
134+
auto TestData =
135+
ExitOnErr(TestData::createTestData(V.getAsObject(), InputFile));
136+
Template T(TestData.TemplateStr);
137+
registerPartials(TestData.Partials, T);
138+
139+
std::string ActualStr;
140+
raw_string_ostream OS(ActualStr);
141+
T.render(*TestData.Data, OS);
142+
if (TestData.ExpectedStr == ActualStr)
143+
++Success;
144+
else
145+
reportTestFailure(TestData, ActualStr);
146+
}
147+
148+
outs() << "Result [" << Success << "/" << Total << "] succeeded\n";
149+
}
150+
151+
int main(int argc, char **argv) {
152+
ExitOnErr.setBanner(std::string(argv[0]) + " error: ");
153+
cl::ParseCommandLineOptions(argc, argv);
154+
for (const auto &FileName : InputFiles)
155+
runTest(FileName);
156+
return 0;
157+
}

0 commit comments

Comments
 (0)