forked from OSchip/llvm-project
[llvm] Add JSONScopedPrinter class
This change adds a JSONScopedPrinter as a subclass to ScopedPrinter. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D114224
This commit is contained in:
parent
d25a65030b
commit
928d17254b
|
@ -16,6 +16,7 @@
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/DataTypes.h"
|
#include "llvm/Support/DataTypes.h"
|
||||||
#include "llvm/Support/Endian.h"
|
#include "llvm/Support/Endian.h"
|
||||||
|
#include "llvm/Support/JSON.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -100,7 +101,20 @@ std::string enumToString(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) {
|
||||||
|
|
||||||
class ScopedPrinter {
|
class ScopedPrinter {
|
||||||
public:
|
public:
|
||||||
ScopedPrinter(raw_ostream &OS) : OS(OS), IndentLevel(0) {}
|
enum class ScopedPrinterKind {
|
||||||
|
Base,
|
||||||
|
JSON,
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopedPrinter(raw_ostream &OS,
|
||||||
|
ScopedPrinterKind Kind = ScopedPrinterKind::Base)
|
||||||
|
: OS(OS), IndentLevel(0), Kind(Kind) {}
|
||||||
|
|
||||||
|
ScopedPrinterKind getKind() const { return Kind; }
|
||||||
|
|
||||||
|
static bool classof(const ScopedPrinter *SP) {
|
||||||
|
return SP->getKind() == ScopedPrinterKind::Base;
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~ScopedPrinter() {}
|
virtual ~ScopedPrinter() {}
|
||||||
|
|
||||||
|
@ -487,6 +501,7 @@ private:
|
||||||
raw_ostream &OS;
|
raw_ostream &OS;
|
||||||
int IndentLevel;
|
int IndentLevel;
|
||||||
StringRef Prefix;
|
StringRef Prefix;
|
||||||
|
ScopedPrinterKind Kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -496,30 +511,329 @@ ScopedPrinter::printHex<support::ulittle16_t>(StringRef Label,
|
||||||
startLine() << Label << ": " << hex(Value) << "\n";
|
startLine() << Label << ": " << hex(Value) << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DelimitedScope;
|
||||||
|
|
||||||
|
class JSONScopedPrinter : public ScopedPrinter {
|
||||||
|
private:
|
||||||
|
enum class Scope {
|
||||||
|
Array,
|
||||||
|
Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ScopeKind {
|
||||||
|
NoAttribute,
|
||||||
|
Attribute,
|
||||||
|
NestedAttribute,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopeContext {
|
||||||
|
Scope Context;
|
||||||
|
ScopeKind Kind;
|
||||||
|
ScopeContext(Scope Context, ScopeKind Kind = ScopeKind::NoAttribute)
|
||||||
|
: Context(Context), Kind(Kind) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
SmallVector<ScopeContext, 8> ScopeHistory;
|
||||||
|
json::OStream JOS;
|
||||||
|
std::unique_ptr<DelimitedScope> OuterScope;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JSONScopedPrinter(raw_ostream &OS, bool PrettyPrint = false,
|
||||||
|
std::unique_ptr<DelimitedScope> &&OuterScope =
|
||||||
|
std::unique_ptr<DelimitedScope>{});
|
||||||
|
|
||||||
|
static bool classof(const ScopedPrinter *SP) {
|
||||||
|
return SP->getKind() == ScopedPrinter::ScopedPrinterKind::JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, uint64_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, uint32_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, uint16_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, uint8_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, int64_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, int32_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, int16_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, int8_t Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumber(StringRef Label, const APSInt &Value) override {
|
||||||
|
JOS.attributeBegin(Label);
|
||||||
|
printAPSInt(Value);
|
||||||
|
JOS.attributeEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printBoolean(StringRef Label, bool Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<bool> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<std::string> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<uint64_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<uint32_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<uint16_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<uint8_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<int64_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<int32_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<int16_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<int8_t> List) override {
|
||||||
|
printListImpl(Label, List);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printList(StringRef Label, const ArrayRef<APSInt> List) override {
|
||||||
|
JOS.attributeArray(Label, [&]() {
|
||||||
|
for (const APSInt &Item : List) {
|
||||||
|
printAPSInt(Item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printString(StringRef Value) override { JOS.value(Value); }
|
||||||
|
|
||||||
|
void printString(StringRef Label, StringRef Value) override {
|
||||||
|
JOS.attribute(Label, Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void objectBegin() override {
|
||||||
|
scopedBegin({Scope::Object, ScopeKind::NoAttribute});
|
||||||
|
}
|
||||||
|
|
||||||
|
void objectBegin(StringRef Label) override {
|
||||||
|
scopedBegin(Label, Scope::Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void objectEnd() override { scopedEnd(); }
|
||||||
|
|
||||||
|
void arrayBegin() override {
|
||||||
|
scopedBegin({Scope::Array, ScopeKind::NoAttribute});
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayBegin(StringRef Label) override {
|
||||||
|
scopedBegin(Label, Scope::Array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayEnd() override { scopedEnd(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Output HexNumbers as decimals so that they're easier to parse.
|
||||||
|
uint64_t hexNumberToInt(HexNumber Hex) { return Hex.Value; }
|
||||||
|
|
||||||
|
void printAPSInt(const APSInt &Value) {
|
||||||
|
JOS.rawValueBegin() << Value;
|
||||||
|
JOS.rawValueEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void printFlagsImpl(StringRef Label, HexNumber Value,
|
||||||
|
ArrayRef<FlagEntry> Flags) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
JOS.attribute("RawFlags", hexNumberToInt(Value));
|
||||||
|
JOS.attributeArray("Flags", [&]() {
|
||||||
|
for (const FlagEntry &Flag : Flags) {
|
||||||
|
JOS.objectBegin();
|
||||||
|
JOS.attribute("Name", Flag.Name);
|
||||||
|
JOS.attribute("Value", Flag.Value);
|
||||||
|
JOS.objectEnd();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printFlagsImpl(StringRef Label, HexNumber Value,
|
||||||
|
ArrayRef<HexNumber> Flags) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
JOS.attribute("RawFlags", hexNumberToInt(Value));
|
||||||
|
JOS.attributeArray("Flags", [&]() {
|
||||||
|
for (const HexNumber &Flag : Flags) {
|
||||||
|
JOS.value(Flag.Value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void printListImpl(StringRef Label, const T &List) {
|
||||||
|
JOS.attributeArray(Label, [&]() {
|
||||||
|
for (const auto &Item : List)
|
||||||
|
JOS.value(Item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHexListImpl(StringRef Label,
|
||||||
|
const ArrayRef<HexNumber> List) override {
|
||||||
|
JOS.attributeArray(Label, [&]() {
|
||||||
|
for (const HexNumber &Item : List) {
|
||||||
|
JOS.value(hexNumberToInt(Item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHexImpl(StringRef Label, HexNumber Value) override {
|
||||||
|
JOS.attribute(Label, hexNumberToInt(Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHexImpl(StringRef Label, StringRef Str, HexNumber Value) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
JOS.attribute("Value", Str);
|
||||||
|
JOS.attribute("RawValue", hexNumberToInt(Value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printSymbolOffsetImpl(StringRef Label, StringRef Symbol,
|
||||||
|
HexNumber Value) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
JOS.attribute("SymName", Symbol);
|
||||||
|
JOS.attribute("Offset", hexNumberToInt(Value));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printNumberImpl(StringRef Label, StringRef Str,
|
||||||
|
StringRef Value) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
JOS.attribute("Value", Str);
|
||||||
|
JOS.attributeBegin("RawValue");
|
||||||
|
JOS.rawValueBegin() << Value;
|
||||||
|
JOS.rawValueEnd();
|
||||||
|
JOS.attributeEnd();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void printBinaryImpl(StringRef Label, StringRef Str, ArrayRef<uint8_t> Value,
|
||||||
|
bool Block, uint32_t StartOffset = 0) override {
|
||||||
|
JOS.attributeObject(Label, [&]() {
|
||||||
|
if (!Str.empty())
|
||||||
|
JOS.attribute("Value", Str);
|
||||||
|
JOS.attribute("Offset", StartOffset);
|
||||||
|
JOS.attributeArray("Bytes", [&]() {
|
||||||
|
for (uint8_t Val : Value)
|
||||||
|
JOS.value(Val);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void scopedBegin(ScopeContext ScopeCtx) {
|
||||||
|
if (ScopeCtx.Context == Scope::Object)
|
||||||
|
JOS.objectBegin();
|
||||||
|
else if (ScopeCtx.Context == Scope::Array)
|
||||||
|
JOS.arrayBegin();
|
||||||
|
ScopeHistory.push_back(ScopeCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scopedBegin(StringRef Label, Scope Ctx) {
|
||||||
|
ScopeKind Kind = ScopeKind::Attribute;
|
||||||
|
if (ScopeHistory.empty() || ScopeHistory.back().Context != Scope::Object) {
|
||||||
|
JOS.objectBegin();
|
||||||
|
Kind = ScopeKind::NestedAttribute;
|
||||||
|
}
|
||||||
|
JOS.attributeBegin(Label);
|
||||||
|
scopedBegin({Ctx, Kind});
|
||||||
|
}
|
||||||
|
|
||||||
|
void scopedEnd() {
|
||||||
|
ScopeContext ScopeCtx = ScopeHistory.back();
|
||||||
|
if (ScopeCtx.Context == Scope::Object)
|
||||||
|
JOS.objectEnd();
|
||||||
|
else if (ScopeCtx.Context == Scope::Array)
|
||||||
|
JOS.arrayEnd();
|
||||||
|
if (ScopeCtx.Kind == ScopeKind::Attribute ||
|
||||||
|
ScopeCtx.Kind == ScopeKind::NestedAttribute)
|
||||||
|
JOS.attributeEnd();
|
||||||
|
if (ScopeCtx.Kind == ScopeKind::NestedAttribute)
|
||||||
|
JOS.objectEnd();
|
||||||
|
ScopeHistory.pop_back();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct DelimitedScope {
|
struct DelimitedScope {
|
||||||
DelimitedScope(ScopedPrinter &W) : W(W) {}
|
DelimitedScope(ScopedPrinter &W) : W(&W) {}
|
||||||
|
DelimitedScope() : W(nullptr) {}
|
||||||
virtual ~DelimitedScope(){};
|
virtual ~DelimitedScope(){};
|
||||||
ScopedPrinter &W;
|
virtual void setPrinter(ScopedPrinter &W) = 0;
|
||||||
|
ScopedPrinter *W;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DictScope : DelimitedScope {
|
struct DictScope : DelimitedScope {
|
||||||
|
explicit DictScope() : DelimitedScope() {}
|
||||||
explicit DictScope(ScopedPrinter &W) : DelimitedScope(W) { W.objectBegin(); }
|
explicit DictScope(ScopedPrinter &W) : DelimitedScope(W) { W.objectBegin(); }
|
||||||
|
|
||||||
DictScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
|
DictScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
|
||||||
W.objectBegin(N);
|
W.objectBegin(N);
|
||||||
}
|
}
|
||||||
|
|
||||||
~DictScope() { W.objectEnd(); }
|
void setPrinter(ScopedPrinter &W) override {
|
||||||
|
this->W = &W;
|
||||||
|
W.objectBegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
~DictScope() {
|
||||||
|
if (W)
|
||||||
|
W->objectEnd();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ListScope : DelimitedScope {
|
struct ListScope : DelimitedScope {
|
||||||
|
explicit ListScope() : DelimitedScope() {}
|
||||||
explicit ListScope(ScopedPrinter &W) : DelimitedScope(W) { W.arrayBegin(); }
|
explicit ListScope(ScopedPrinter &W) : DelimitedScope(W) { W.arrayBegin(); }
|
||||||
|
|
||||||
ListScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
|
ListScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
|
||||||
W.arrayBegin(N);
|
W.arrayBegin(N);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ListScope() { W.arrayEnd(); }
|
void setPrinter(ScopedPrinter &W) override {
|
||||||
|
this->W = &W;
|
||||||
|
W.arrayBegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ListScope() {
|
||||||
|
if (W)
|
||||||
|
W->arrayEnd();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
|
|
|
@ -43,4 +43,14 @@ void ScopedPrinter::printBinaryImpl(StringRef Label, StringRef Str,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONScopedPrinter::JSONScopedPrinter(
|
||||||
|
raw_ostream &OS, bool PrettyPrint,
|
||||||
|
std::unique_ptr<DelimitedScope> &&OuterScope)
|
||||||
|
: ScopedPrinter(OS, ScopedPrinter::ScopedPrinterKind::JSON),
|
||||||
|
JOS(OS, /*Indent=*/PrettyPrint ? 2 : 0),
|
||||||
|
OuterScope(std::move(OuterScope)) {
|
||||||
|
if (this->OuterScope)
|
||||||
|
this->OuterScope->setPrinter(*this);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
|
|
|
@ -512,7 +512,7 @@ template <typename ET>
|
||||||
void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry,
|
void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry,
|
||||||
size_t Length, off_t Offset) const {
|
size_t Length, off_t Offset) const {
|
||||||
ListScope OCC(SW, "Opcodes");
|
ListScope OCC(SW, "Opcodes");
|
||||||
OpcodeDecoder(OCC.W).Decode(Entry, Offset, Length);
|
OpcodeDecoder(SW).Decode(Entry, Offset, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ET>
|
template <typename ET>
|
||||||
|
|
|
@ -13,13 +13,63 @@
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
|
TEST(JSONScopedPrinterTest, PrettyPrintCtor) {
|
||||||
|
auto PrintFunc = [](ScopedPrinter &W) {
|
||||||
|
DictScope D(W);
|
||||||
|
W.printString("Key", "Value");
|
||||||
|
};
|
||||||
|
std::string StreamBuffer;
|
||||||
|
raw_string_ostream OS(StreamBuffer);
|
||||||
|
JSONScopedPrinter PrettyPrintWriter(OS, /*PrettyPrint=*/true);
|
||||||
|
JSONScopedPrinter NoPrettyPrintWriter(OS, /*PrettyPrint=*/false);
|
||||||
|
|
||||||
|
const char *PrettyPrintOut = R"({
|
||||||
|
"Key": "Value"
|
||||||
|
})";
|
||||||
|
const char *NoPrettyPrintOut = R"({"Key":"Value"})";
|
||||||
|
PrintFunc(PrettyPrintWriter);
|
||||||
|
EXPECT_EQ(PrettyPrintOut, OS.str());
|
||||||
|
StreamBuffer.clear();
|
||||||
|
PrintFunc(NoPrettyPrintWriter);
|
||||||
|
EXPECT_EQ(NoPrettyPrintOut, OS.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(JSONScopedPrinterTest, DelimitedScopeCtor) {
|
||||||
|
std::string StreamBuffer;
|
||||||
|
raw_string_ostream OS(StreamBuffer);
|
||||||
|
{
|
||||||
|
JSONScopedPrinter DictScopeWriter(OS, /*PrettyPrint=*/false,
|
||||||
|
std::make_unique<DictScope>());
|
||||||
|
DictScopeWriter.printString("Label", "DictScope");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(R"({"Label":"DictScope"})", OS.str());
|
||||||
|
StreamBuffer.clear();
|
||||||
|
{
|
||||||
|
JSONScopedPrinter ListScopeWriter(OS, /*PrettyPrint=*/false,
|
||||||
|
std::make_unique<ListScope>());
|
||||||
|
ListScopeWriter.printString("ListScope");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(R"(["ListScope"])", OS.str());
|
||||||
|
StreamBuffer.clear();
|
||||||
|
{
|
||||||
|
JSONScopedPrinter NoScopeWriter(OS, /*PrettyPrint=*/false);
|
||||||
|
NoScopeWriter.printString("NoScope");
|
||||||
|
}
|
||||||
|
EXPECT_EQ(R"("NoScope")", OS.str());
|
||||||
|
}
|
||||||
|
|
||||||
class ScopedPrinterTest : public ::testing::Test {
|
class ScopedPrinterTest : public ::testing::Test {
|
||||||
protected:
|
protected:
|
||||||
std::string StreamBuffer;
|
std::string StreamBuffer;
|
||||||
raw_string_ostream OS;
|
raw_string_ostream OS;
|
||||||
ScopedPrinter Writer;
|
ScopedPrinter Writer;
|
||||||
|
JSONScopedPrinter JSONWriter;
|
||||||
|
|
||||||
ScopedPrinterTest() : OS(StreamBuffer), Writer(OS) {}
|
bool HasPrintedToJSON;
|
||||||
|
|
||||||
|
ScopedPrinterTest()
|
||||||
|
: OS(StreamBuffer), Writer(OS), JSONWriter(OS, /*PrettyPrint=*/true),
|
||||||
|
HasPrintedToJSON(false) {}
|
||||||
|
|
||||||
using PrintFunc = function_ref<void(ScopedPrinter &)>;
|
using PrintFunc = function_ref<void(ScopedPrinter &)>;
|
||||||
|
|
||||||
|
@ -27,9 +77,45 @@ protected:
|
||||||
Func(Writer);
|
Func(Writer);
|
||||||
Writer.flush();
|
Writer.flush();
|
||||||
EXPECT_EQ(Expected.str(), OS.str());
|
EXPECT_EQ(Expected.str(), OS.str());
|
||||||
|
StreamBuffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyJSONScopedPrinter(StringRef Expected, PrintFunc Func) {
|
||||||
|
{
|
||||||
|
DictScope D(JSONWriter);
|
||||||
|
Func(JSONWriter);
|
||||||
|
}
|
||||||
|
JSONWriter.flush();
|
||||||
|
EXPECT_EQ(Expected.str(), OS.str());
|
||||||
|
StreamBuffer.clear();
|
||||||
|
HasPrintedToJSON = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyAll(StringRef ExpectedOut, StringRef JSONExpectedOut,
|
||||||
|
PrintFunc Func) {
|
||||||
|
verifyScopedPrinter(ExpectedOut, Func);
|
||||||
|
verifyJSONScopedPrinter(JSONExpectedOut, Func);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() {
|
||||||
|
// JSONScopedPrinter fails an assert if nothing's been printed.
|
||||||
|
if (!HasPrintedToJSON)
|
||||||
|
JSONWriter.printString("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST_F(ScopedPrinterTest, GetKind) {
|
||||||
|
EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::Base, Writer.getKind());
|
||||||
|
EXPECT_EQ(ScopedPrinter::ScopedPrinterKind::JSON, JSONWriter.getKind());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ScopedPrinterTest, ClassOf) {
|
||||||
|
EXPECT_TRUE(ScopedPrinter::classof(&Writer));
|
||||||
|
EXPECT_TRUE(JSONScopedPrinter::classof(&JSONWriter));
|
||||||
|
EXPECT_FALSE(ScopedPrinter::classof(&JSONWriter));
|
||||||
|
EXPECT_FALSE(JSONScopedPrinter::classof(&Writer));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, Indent) {
|
TEST_F(ScopedPrinterTest, Indent) {
|
||||||
auto PrintFunc = [](ScopedPrinter &W) {
|
auto PrintFunc = [](ScopedPrinter &W) {
|
||||||
W.printString("|");
|
W.printString("|");
|
||||||
|
@ -147,7 +233,15 @@ TEST_F(ScopedPrinterTest, PrintEnum) {
|
||||||
const char *ExpectedOut = R"(Exists: Name2 (0x2)
|
const char *ExpectedOut = R"(Exists: Name2 (0x2)
|
||||||
DoesNotExist: 0x5
|
DoesNotExist: 0x5
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"Exists": {
|
||||||
|
"Value": "Name2",
|
||||||
|
"RawValue": 2
|
||||||
|
},
|
||||||
|
"DoesNotExist": 5
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintFlag) {
|
TEST_F(ScopedPrinterTest, PrintFlag) {
|
||||||
|
@ -251,7 +345,169 @@ FirstSecondThirdByteMask [ (0x333)
|
||||||
ThirdByte3 (0x300)
|
ThirdByte3 (0x300)
|
||||||
]
|
]
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"ZeroFlag": {
|
||||||
|
"RawFlags": 0,
|
||||||
|
"Flags": []
|
||||||
|
},
|
||||||
|
"NoFlag": {
|
||||||
|
"RawFlags": 8,
|
||||||
|
"Flags": []
|
||||||
|
},
|
||||||
|
"Flag1": {
|
||||||
|
"RawFlags": 1,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "Name1",
|
||||||
|
"Value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Flag1&3": {
|
||||||
|
"RawFlags": 5,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "Name1",
|
||||||
|
"Value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Name3",
|
||||||
|
"Value": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ZeroFlagRaw": {
|
||||||
|
"RawFlags": 0,
|
||||||
|
"Flags": []
|
||||||
|
},
|
||||||
|
"NoFlagRaw": {
|
||||||
|
"RawFlags": 8,
|
||||||
|
"Flags": [
|
||||||
|
8
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Flag1Raw": {
|
||||||
|
"RawFlags": 1,
|
||||||
|
"Flags": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Flag1&3Raw": {
|
||||||
|
"RawFlags": 5,
|
||||||
|
"Flags": [
|
||||||
|
1,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FlagSorted": {
|
||||||
|
"RawFlags": 7,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "A",
|
||||||
|
"Value": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "B",
|
||||||
|
"Value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "C",
|
||||||
|
"Value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"NoBitMask": {
|
||||||
|
"RawFlags": 4095,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "FirstByte1",
|
||||||
|
"Value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "FirstByte2",
|
||||||
|
"Value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "FirstByte3",
|
||||||
|
"Value": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SecondByte1",
|
||||||
|
"Value": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SecondByte2",
|
||||||
|
"Value": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SecondByte3",
|
||||||
|
"Value": 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ThirdByte1",
|
||||||
|
"Value": 256
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ThirdByte2",
|
||||||
|
"Value": 512
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ThirdByte3",
|
||||||
|
"Value": 768
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FirstByteMask": {
|
||||||
|
"RawFlags": 3,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "FirstByte3",
|
||||||
|
"Value": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"SecondByteMask": {
|
||||||
|
"RawFlags": 48,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "SecondByte3",
|
||||||
|
"Value": 48
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ValueOutsideMask": {
|
||||||
|
"RawFlags": 1,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "FirstByte1",
|
||||||
|
"Value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"FirstSecondByteMask": {
|
||||||
|
"RawFlags": 255,
|
||||||
|
"Flags": []
|
||||||
|
},
|
||||||
|
"FirstSecondThirdByteMask": {
|
||||||
|
"RawFlags": 819,
|
||||||
|
"Flags": [
|
||||||
|
{
|
||||||
|
"Name": "FirstByte3",
|
||||||
|
"Value": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SecondByte3",
|
||||||
|
"Value": 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ThirdByte3",
|
||||||
|
"Value": 768
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintNumber) {
|
TEST_F(ScopedPrinterTest, PrintNumber) {
|
||||||
|
@ -321,7 +577,31 @@ int8_t-min: -128
|
||||||
apsint: 9999999999999999999999
|
apsint: 9999999999999999999999
|
||||||
label: value (0)
|
label: value (0)
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"uint64_t-max": 18446744073709551615,
|
||||||
|
"uint64_t-min": 0,
|
||||||
|
"uint32_t-max": 4294967295,
|
||||||
|
"uint32_t-min": 0,
|
||||||
|
"uint16_t-max": 65535,
|
||||||
|
"uint16_t-min": 0,
|
||||||
|
"uint8_t-max": 255,
|
||||||
|
"uint8_t-min": 0,
|
||||||
|
"int64_t-max": 9223372036854775807,
|
||||||
|
"int64_t-min": -9223372036854775808,
|
||||||
|
"int32_t-max": 2147483647,
|
||||||
|
"int32_t-min": -2147483648,
|
||||||
|
"int16_t-max": 32767,
|
||||||
|
"int16_t-min": -32768,
|
||||||
|
"int8_t-max": 127,
|
||||||
|
"int8_t-min": -128,
|
||||||
|
"apsint": 9999999999999999999999,
|
||||||
|
"label": {
|
||||||
|
"Value": "value",
|
||||||
|
"RawValue": 0
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintBoolean) {
|
TEST_F(ScopedPrinterTest, PrintBoolean) {
|
||||||
|
@ -333,7 +613,12 @@ TEST_F(ScopedPrinterTest, PrintBoolean) {
|
||||||
const char *ExpectedOut = R"(True: Yes
|
const char *ExpectedOut = R"(True: Yes
|
||||||
False: No
|
False: No
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"True": true,
|
||||||
|
"False": false
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintVersion) {
|
TEST_F(ScopedPrinterTest, PrintVersion) {
|
||||||
|
@ -402,7 +687,56 @@ int16List: [32767, -32768]
|
||||||
int8List: [127, -128]
|
int8List: [127, -128]
|
||||||
APSIntList: [9999999999999999999999, -9999999999999999999999]
|
APSIntList: [9999999999999999999999, -9999999999999999999999]
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"EmptyList": [],
|
||||||
|
"StringList": [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"baz"
|
||||||
|
],
|
||||||
|
"BoolList": [
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"uint64List": [
|
||||||
|
18446744073709551615,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"uint32List": [
|
||||||
|
4294967295,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"uint16List": [
|
||||||
|
65535,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"uint8List": [
|
||||||
|
255,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"int64List": [
|
||||||
|
9223372036854775807,
|
||||||
|
-9223372036854775808
|
||||||
|
],
|
||||||
|
"int32List": [
|
||||||
|
2147483647,
|
||||||
|
-2147483648
|
||||||
|
],
|
||||||
|
"int16List": [
|
||||||
|
32767,
|
||||||
|
-32768
|
||||||
|
],
|
||||||
|
"int8List": [
|
||||||
|
127,
|
||||||
|
-128
|
||||||
|
],
|
||||||
|
"APSIntList": [
|
||||||
|
9999999999999999999999,
|
||||||
|
-9999999999999999999999
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintListPrinter) {
|
TEST_F(ScopedPrinterTest, PrintListPrinter) {
|
||||||
|
@ -426,7 +760,15 @@ TEST_F(ScopedPrinterTest, PrintHex) {
|
||||||
const char *ExpectedOut = R"(HexNumber: 0x10
|
const char *ExpectedOut = R"(HexNumber: 0x10
|
||||||
HexLabel: Name (0x10)
|
HexLabel: Name (0x10)
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"HexNumber": 16,
|
||||||
|
"HexLabel": {
|
||||||
|
"Value": "Name",
|
||||||
|
"RawValue": 16
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintHexList) {
|
TEST_F(ScopedPrinterTest, PrintHexList) {
|
||||||
|
@ -436,7 +778,15 @@ TEST_F(ScopedPrinterTest, PrintHexList) {
|
||||||
};
|
};
|
||||||
const char *ExpectedOut = R"(HexList: [0x1, 0x10, 0x100]
|
const char *ExpectedOut = R"(HexList: [0x1, 0x10, 0x100]
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"HexList": [
|
||||||
|
1,
|
||||||
|
16,
|
||||||
|
256
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintSymbolOffset) {
|
TEST_F(ScopedPrinterTest, PrintSymbolOffset) {
|
||||||
|
@ -447,7 +797,18 @@ TEST_F(ScopedPrinterTest, PrintSymbolOffset) {
|
||||||
const char *ExpectedOut = R"(SymbolOffset: SymbolName+0x10
|
const char *ExpectedOut = R"(SymbolOffset: SymbolName+0x10
|
||||||
NoSymbolOffset: SymbolName+0x0
|
NoSymbolOffset: SymbolName+0x0
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"SymbolOffset": {
|
||||||
|
"SymName": "SymbolName",
|
||||||
|
"Offset": 16
|
||||||
|
},
|
||||||
|
"NoSymbolOffset": {
|
||||||
|
"SymName": "SymbolName",
|
||||||
|
"Offset": 0
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintString) {
|
TEST_F(ScopedPrinterTest, PrintString) {
|
||||||
|
@ -469,7 +830,16 @@ StringList [
|
||||||
Value
|
Value
|
||||||
]
|
]
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"StringRef": "Value",
|
||||||
|
"String": "Value",
|
||||||
|
"CharArray": "Value",
|
||||||
|
"StringList": [
|
||||||
|
"Value"
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintBinary) {
|
TEST_F(ScopedPrinterTest, PrintBinary) {
|
||||||
|
@ -516,7 +886,157 @@ Binary11 (
|
||||||
0000: FFFF |..|
|
0000: FFFF |..|
|
||||||
)
|
)
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"Binary1": {
|
||||||
|
"Value": "FooBar",
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary2": {
|
||||||
|
"Value": "FooBar",
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary3": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary4": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary5": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary6": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
77,
|
||||||
|
117,
|
||||||
|
108,
|
||||||
|
116,
|
||||||
|
105,
|
||||||
|
112,
|
||||||
|
108,
|
||||||
|
101,
|
||||||
|
32,
|
||||||
|
76,
|
||||||
|
105,
|
||||||
|
110,
|
||||||
|
101,
|
||||||
|
32,
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary7": {
|
||||||
|
"Offset": 20,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary8": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary9": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary10": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
77,
|
||||||
|
117,
|
||||||
|
108,
|
||||||
|
116,
|
||||||
|
105,
|
||||||
|
112,
|
||||||
|
108,
|
||||||
|
101,
|
||||||
|
32,
|
||||||
|
76,
|
||||||
|
105,
|
||||||
|
110,
|
||||||
|
101,
|
||||||
|
32,
|
||||||
|
70,
|
||||||
|
111,
|
||||||
|
111,
|
||||||
|
66,
|
||||||
|
97,
|
||||||
|
114
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Binary11": {
|
||||||
|
"Offset": 0,
|
||||||
|
"Bytes": [
|
||||||
|
255,
|
||||||
|
255
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, PrintObject) {
|
TEST_F(ScopedPrinterTest, PrintObject) {
|
||||||
|
@ -524,7 +1044,11 @@ TEST_F(ScopedPrinterTest, PrintObject) {
|
||||||
|
|
||||||
const char *ExpectedOut = R"(Object: Value
|
const char *ExpectedOut = R"(Object: Value
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"Object": "Value"
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ScopedPrinterTest, StartLine) {
|
TEST_F(ScopedPrinterTest, StartLine) {
|
||||||
|
@ -574,5 +1098,20 @@ List [
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
)";
|
)";
|
||||||
verifyScopedPrinter(ExpectedOut, PrintFunc);
|
|
||||||
|
const char *JSONExpectedOut = R"({
|
||||||
|
"Object": {
|
||||||
|
"ObjectInObject": {},
|
||||||
|
"ListInObject": []
|
||||||
|
},
|
||||||
|
"List": [
|
||||||
|
{
|
||||||
|
"ObjectInList": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ListInList": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})";
|
||||||
|
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue