[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:
Jayson Yan 2021-12-10 18:44:45 +00:00
parent d25a65030b
commit 928d17254b
4 changed files with 882 additions and 19 deletions

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@ -100,7 +101,20 @@ std::string enumToString(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues) {
class ScopedPrinter {
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() {}
@ -487,6 +501,7 @@ private:
raw_ostream &OS;
int IndentLevel;
StringRef Prefix;
ScopedPrinterKind Kind;
};
template <>
@ -496,30 +511,329 @@ ScopedPrinter::printHex<support::ulittle16_t>(StringRef Label,
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 {
DelimitedScope(ScopedPrinter &W) : W(W) {}
DelimitedScope(ScopedPrinter &W) : W(&W) {}
DelimitedScope() : W(nullptr) {}
virtual ~DelimitedScope(){};
ScopedPrinter &W;
virtual void setPrinter(ScopedPrinter &W) = 0;
ScopedPrinter *W;
};
struct DictScope : DelimitedScope {
explicit DictScope() : DelimitedScope() {}
explicit DictScope(ScopedPrinter &W) : DelimitedScope(W) { W.objectBegin(); }
DictScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
W.objectBegin(N);
}
~DictScope() { W.objectEnd(); }
void setPrinter(ScopedPrinter &W) override {
this->W = &W;
W.objectBegin();
}
~DictScope() {
if (W)
W->objectEnd();
}
};
struct ListScope : DelimitedScope {
explicit ListScope() : DelimitedScope() {}
explicit ListScope(ScopedPrinter &W) : DelimitedScope(W) { W.arrayBegin(); }
ListScope(ScopedPrinter &W, StringRef N) : DelimitedScope(W) {
W.arrayBegin(N);
}
~ListScope() { W.arrayEnd(); }
void setPrinter(ScopedPrinter &W) override {
this->W = &W;
W.arrayBegin();
}
~ListScope() {
if (W)
W->arrayEnd();
}
};
} // namespace llvm

View File

@ -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

View File

@ -512,7 +512,7 @@ template <typename ET>
void PrinterContext<ET>::PrintOpcodes(const uint8_t *Entry,
size_t Length, off_t Offset) const {
ListScope OCC(SW, "Opcodes");
OpcodeDecoder(OCC.W).Decode(Entry, Offset, Length);
OpcodeDecoder(SW).Decode(Entry, Offset, Length);
}
template <typename ET>

View File

@ -13,13 +13,63 @@
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 {
protected:
std::string StreamBuffer;
raw_string_ostream OS;
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 &)>;
@ -27,9 +77,45 @@ protected:
Func(Writer);
Writer.flush();
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) {
auto PrintFunc = [](ScopedPrinter &W) {
W.printString("|");
@ -147,7 +233,15 @@ TEST_F(ScopedPrinterTest, PrintEnum) {
const char *ExpectedOut = R"(Exists: Name2 (0x2)
DoesNotExist: 0x5
)";
verifyScopedPrinter(ExpectedOut, PrintFunc);
const char *JSONExpectedOut = R"({
"Exists": {
"Value": "Name2",
"RawValue": 2
},
"DoesNotExist": 5
})";
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
}
TEST_F(ScopedPrinterTest, PrintFlag) {
@ -251,7 +345,169 @@ FirstSecondThirdByteMask [ (0x333)
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) {
@ -321,7 +577,31 @@ int8_t-min: -128
apsint: 9999999999999999999999
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) {
@ -333,7 +613,12 @@ TEST_F(ScopedPrinterTest, PrintBoolean) {
const char *ExpectedOut = R"(True: Yes
False: No
)";
verifyScopedPrinter(ExpectedOut, PrintFunc);
const char *JSONExpectedOut = R"({
"True": true,
"False": false
})";
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
}
TEST_F(ScopedPrinterTest, PrintVersion) {
@ -402,7 +687,56 @@ int16List: [32767, -32768]
int8List: [127, -128]
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) {
@ -426,7 +760,15 @@ TEST_F(ScopedPrinterTest, PrintHex) {
const char *ExpectedOut = R"(HexNumber: 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) {
@ -436,7 +778,15 @@ TEST_F(ScopedPrinterTest, PrintHexList) {
};
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) {
@ -447,7 +797,18 @@ TEST_F(ScopedPrinterTest, PrintSymbolOffset) {
const char *ExpectedOut = R"(SymbolOffset: SymbolName+0x10
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) {
@ -469,7 +830,16 @@ StringList [
Value
]
)";
verifyScopedPrinter(ExpectedOut, PrintFunc);
const char *JSONExpectedOut = R"({
"StringRef": "Value",
"String": "Value",
"CharArray": "Value",
"StringList": [
"Value"
]
})";
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
}
TEST_F(ScopedPrinterTest, PrintBinary) {
@ -516,7 +886,157 @@ Binary11 (
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) {
@ -524,7 +1044,11 @@ TEST_F(ScopedPrinterTest, PrintObject) {
const char *ExpectedOut = R"(Object: Value
)";
verifyScopedPrinter(ExpectedOut, PrintFunc);
const char *JSONExpectedOut = R"({
"Object": "Value"
})";
verifyAll(ExpectedOut, JSONExpectedOut, PrintFunc);
}
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);
}