diff options
Diffstat (limited to 'tools/slang-unit-test/unit-test-json.cpp')
| -rw-r--r-- | tools/slang-unit-test/unit-test-json.cpp | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/tools/slang-unit-test/unit-test-json.cpp b/tools/slang-unit-test/unit-test-json.cpp new file mode 100644 index 000000000..2d2874cc8 --- /dev/null +++ b/tools/slang-unit-test/unit-test-json.cpp @@ -0,0 +1,320 @@ + +#include "../../source/compiler-core/slang-json-lexer.h" +#include "../../source/core/slang-string-escape-util.h" +#include "../../source/compiler-core/slang-json-parser.h" +#include "../../source/compiler-core/slang-json-value.h" + +#include "tools/unit-test/slang-unit-test.h" + +using namespace Slang; + +namespace { // anonymous + +struct Element +{ + JSONTokenType type; + const char* value; +}; + +} // anonymous + +static SlangResult _lex(const char* in, DiagnosticSink* sink, List<JSONToken>& toks) +{ + SourceManager* sourceManager = sink->getSourceManager(); + + String contents(in); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(PathInfo::makeUnknown(), contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + + JSONLexer lexer; + + lexer.init(sourceView, sink); + + while (lexer.peekType() != JSONTokenType::EndOfFile) + { + if (lexer.peekType() == JSONTokenType::Invalid) + { + toks.add(lexer.peekToken()); + return SLANG_FAIL; + } + + toks.add(lexer.peekToken()); + lexer.advance(); + } + + toks.add(lexer.peekToken()); + + // If we advance from end of file we should still be at EndOfFile + SLANG_ASSERT(lexer.advance() == JSONTokenType::EndOfFile); + + return SLANG_OK; +} + +static SlangResult _parse(const char* in, DiagnosticSink* sink, JSONListener* listener) +{ + SourceManager* sourceManager = sink->getSourceManager(); + + String contents(in); + SourceFile* sourceFile = sourceManager->createSourceFileWithString(PathInfo::makeUnknown(), contents); + SourceView* sourceView = sourceManager->createSourceView(sourceFile, nullptr, SourceLoc()); + + JSONLexer lexer; + lexer.init(sourceView, sink); + + JSONParser parser; + SLANG_RETURN_ON_FAIL(parser.parse(&lexer, sourceView, listener, sink)); + return SLANG_OK; +} + +static bool _areEqual(SourceManager* sourceManager, const List<JSONToken>& toks, const Element* eles, Index elesCount) +{ + if (toks.getCount() != elesCount) + { + return false; + } + + SourceView* sourceView = toks.getCount() ? sourceManager->findSourceView(toks[0].loc) : nullptr; + const char*const content = sourceView ? sourceView->getContent().begin() : nullptr; + + for (Index i = 0; i < toks.getCount(); ++i) + { + const JSONToken& tok = toks[i]; + const auto& ele = eles[i]; + + if (tok.type != ele.type) + { + return false; + } + + SLANG_ASSERT(sourceView->getRange().contains(tok.loc)); + + const char* start = content + sourceView->getRange().getOffset(tok.loc); + + UnownedStringSlice lexeme(start, tok.length); + + if (lexeme != ele.value) + { + return false; + } + } + + return true; +} + +SLANG_UNIT_TEST(json) +{ + SourceManager sourceManager; + sourceManager.initialize(nullptr, nullptr); + DiagnosticSink sink(&sourceManager, nullptr); + + { + const char text[] = " { \"Hello\" : [ \"World\", 1, 2.0, -3.0, -435.5345435, 45e-10, 421.00e+20, 17e1] }"; + + const Element eles[] = + { + {JSONTokenType::LBrace, "{" }, + {JSONTokenType::StringLiteral, "\"Hello\""}, + {JSONTokenType::Colon, ":" }, + {JSONTokenType::LBracket, "[" }, + {JSONTokenType::StringLiteral, "\"World\"" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::IntegerLiteral, "1" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "2.0" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "-3.0" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "-435.5345435" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "45e-10" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "421.00e+20" }, + {JSONTokenType::Comma, "," }, + {JSONTokenType::FloatLiteral, "17e1" }, + {JSONTokenType::RBracket, "]" }, + {JSONTokenType::RBrace, "}" }, + {JSONTokenType::EndOfFile, "" }, + }; + + List<JSONToken> toks; + SLANG_CHECK(SLANG_SUCCEEDED(_lex(text, &sink, toks))); + + SLANG_CHECK(_areEqual(&sourceManager, toks, eles, SLANG_COUNT_OF(eles))); + } + + { + StringEscapeHandler* handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::JSON); + + + { + const auto slice = UnownedStringSlice::fromLiteral("\n\r\b\f\t \"\\/ Some text..."); + + SLANG_CHECK(handler->isEscapingNeeded(slice)); + SLANG_CHECK(!handler->isEscapingNeeded(UnownedStringSlice::fromLiteral("Hello!"))); + + StringBuilder escaped; + handler->appendEscaped(slice, escaped); + + StringBuilder unescaped; + handler->appendUnescaped(escaped.getUnownedSlice(), unescaped); + + SLANG_CHECK(unescaped == slice); + } + + { + uint32_t v = 0x7f; + + StringBuilder buf; + while (v < 0x10000) + { + char work[10] = "\\u"; + + for (Int i = 0; i < 4; ++i) + { + const uint32_t digitValue = (v >> ((3 - i) * 4)) & 0xf; + + char digitC = (digitValue > 9) ? char(digitValue - 10 + 'a') : char(digitValue + '0'); + work[i + 2] = digitC; + } + + buf << UnownedStringSlice(work, 6); + + v += v; + } + + // Decode it + StringBuilder unescaped; + handler->appendUnescaped(buf.getUnownedSlice(), unescaped); + + // Encode it + StringBuilder escaped; + handler->appendEscaped(unescaped.getUnownedSlice(), escaped); + + SLANG_CHECK(escaped == buf); + } + } + + { + const char in[] = "{ \"Hello\" : \"Json\", \"!\" : 10, \"array\" : [1, 2, 3.0] }"; + + { + auto style = JSONWriter::IndentationStyle::Allman; + + JSONWriter writer(style); + _parse(in, &sink, &writer); + + JSONWriter writerCheck(style); + _parse(writer.getBuilder().getBuffer(), &sink, &writerCheck); + + SLANG_CHECK(writerCheck.getBuilder() == writer.getBuilder()); + } + + { + auto style = JSONWriter::IndentationStyle::KNR; + + JSONWriter writer(style, 80); + _parse(in, &sink, &writer); + + JSONWriter writerCheck(style); + _parse(writer.getBuilder().getBuffer(), &sink, &writerCheck); + + SLANG_CHECK(writerCheck.getBuilder() == writer.getBuilder()); + } + + { + // Let's parse into a Value + RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); + + JSONValue value; + { + JSONBuilder builder(container); + + SLANG_CHECK(SLANG_SUCCEEDED(_parse(in, &sink, &builder))); + value = builder.getRootValue(); + } + // Let's recreate + JSONValue copy; + { + JSONBuilder builder(container); + container->traverseRecursively(value, &builder); + copy = builder.getRootValue(); + } + + SLANG_CHECK(container->areEqual(value, copy)); + + } + } + + { + // Only need a SourceManager if we are going to store lexemes + RefPtr<JSONContainer> container = new JSONContainer(nullptr); + + { + List<JSONValue> values; + + for (Int i = 0; i < 100; ++i) + { + + values.add(JSONValue::makeInt(i)); + values.add(JSONValue::makeFloat(-double(i))); + } + + JSONValue array = container->createArray(values.getBuffer(), values.getCount()); + + auto arrayView = container->getArray(array); + + SLANG_CHECK(arrayView.getCount() == values.getCount()); + + // Check the values are the same + SLANG_CHECK(container->areEqual(arrayView.getBuffer(), values.getBuffer(), arrayView.getCount())); + + { + JSONWriter writer(JSONWriter::IndentationStyle::KNR, 80); + + container->traverseRecursively(array, &writer); + } + } + { + JSONValue obj = JSONValue::makeEmptyObject(); + + JSONKey key = container->getKey(UnownedStringSlice::fromLiteral("Hello")); + + container->setKeyValue(obj, key, JSONValue::makeNull()); + container->setKeyValue(obj, key, JSONValue::makeInt(10)); + + auto objView = container->getObject(obj); + + SLANG_CHECK(objView.getCount() == 1); + + SLANG_CHECK(objView[0].value.asInteger() == 10); + } + } + + // Check repeated keys works out + // Check out comparison works with different key orders + { + RefPtr<JSONContainer> container = new JSONContainer(&sourceManager); + const char aText[] = "{ \"a\" : 10, \"b\" : 20.0, \"a\" : \"Hello\" }"; + + + JSONBuilder builder(container); + SLANG_CHECK(SLANG_SUCCEEDED(_parse(aText, &sink, &builder))); + const JSONValue a = builder.getRootValue(); + + builder.reset(); + + const char bText[] = "{ \"b\" : 20.0, \"a\" : \"Hello\"}"; + SLANG_CHECK(SLANG_SUCCEEDED(_parse(bText, &sink, &builder))); + const JSONValue b = builder.getRootValue(); + + SLANG_CHECK(container->areEqual(a, b)); + + JSONBuilder convertBuilder(container, JSONBuilder::Flag::ConvertLexemes); + + SLANG_CHECK(SLANG_SUCCEEDED(_parse(aText, &sink, &convertBuilder))); + const JSONValue c = builder.getRootValue(); + + SLANG_CHECK(container->areEqual(a, c)); + } +} + |
