File indexing completed on 2024-05-19 15:26:48

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include <QtTest/QtTest>
0008 
0009 #include <vector>
0010 #include <cstring>
0011 #include "io/aep/cos.hpp"
0012 
0013 using namespace glaxnimate::io::aep;
0014 
0015 #ifndef QVERIFY_THROWS_EXCEPTION
0016 #   define QVERIFY_THROWS_EXCEPTION(exceptiontype, expression) QVERIFY_EXCEPTION_THROWN(expression, exceptiontype)
0017 #endif
0018 
0019 
0020 inline QByteArray operator "" _b(const char* c, std::size_t sz)
0021 {
0022     return QByteArray(c, sz);
0023 }
0024 
0025 #define COS_VALUE(val, type_index, expected) \
0026     QCOMPARE(val.type(), CosValue::Index::type_index); \
0027     QCOMPARE(val.get<CosValue::Index::type_index>(), (expected));
0028 
0029 #define COS_TOKEN_TYPE(token, tok_type) \
0030     QCOMPARE(int(token.type), int(CosTokenType::tok_type));
0031 
0032 #define COS_TOKEN(token, tok_type, type_index, expected) \
0033     COS_TOKEN_TYPE(token, tok_type) \
0034     COS_VALUE(token.value, type_index, expected)
0035 
0036 
0037 class TestCase: public QObject
0038 {
0039     Q_OBJECT
0040 
0041     CosValue parse(QByteArray ba)
0042     {
0043         CosParser parser(std::move(ba));
0044         return parser.parse();
0045     }
0046 
0047     CosToken lex(QByteArray ba)
0048     {
0049         CosLexer lexer(std::move(ba));
0050         return lexer.next_token();
0051     }
0052 
0053 private Q_SLOTS:
0054     void test_lex_object_start()
0055     {
0056         CosLexer l("<<"_b);
0057         auto token = l.next_token();
0058         QCOMPARE(token.type, CosTokenType::ObjectStart);
0059         QVERIFY_THROWS_EXCEPTION(CosError, lex("<"));
0060         QVERIFY_THROWS_EXCEPTION(CosError, lex("<a"));
0061     }
0062 
0063     void test_lex_identidfier()
0064     {
0065         CosLexer l("/foo/bar<<"_b);
0066         auto token = l.next_token();
0067         COS_TOKEN(token, Identifier, String, "foo");
0068         token = l.next_token();
0069         COS_TOKEN(token, Identifier, String, "bar");
0070         token = l.next_token();
0071         QCOMPARE(token.type, CosTokenType::ObjectStart);
0072         COS_TOKEN(lex("/foo#62ar"_b), Identifier, String, "foobar");
0073         QVERIFY_THROWS_EXCEPTION(CosError, lex("/foo#6"));
0074         QVERIFY_THROWS_EXCEPTION(CosError, lex("/foo#6r"));
0075     }
0076 
0077     void test_lex_spaces()
0078     {
0079         CosLexer l("  \t\n/1\n /2\n% /3 /4\n /5"_b);
0080         auto token = l.next_token();
0081         COS_TOKEN(token, Identifier, String, "1");
0082         token = l.next_token();
0083         COS_TOKEN(token, Identifier, String, "2");
0084         token = l.next_token();
0085         COS_TOKEN(token, Identifier, String, "5");
0086         token = l.next_token();
0087         COS_TOKEN_TYPE(token, Eof);
0088     }
0089 
0090     void test_lex_xstring()
0091     {
0092         CosLexer l("<F00d><a><13 37>"_b);
0093         auto token = l.next_token();
0094         COS_TOKEN(token, HexString, Bytes, "\xf0\x0d"_b);
0095         token = l.next_token();
0096         COS_TOKEN(token, HexString, Bytes, "\xa0"_b);
0097         token = l.next_token();
0098         COS_TOKEN(token, HexString, Bytes, "\x13\x37"_b);
0099         QVERIFY_THROWS_EXCEPTION(CosError, lex("<F00"));
0100         QVERIFY_THROWS_EXCEPTION(CosError, lex("<G>"));
0101         QVERIFY_THROWS_EXCEPTION(CosError, lex("<FG>"));
0102     }
0103 
0104     void test_lex_object_end()
0105     {
0106         CosLexer l(">>"_b);
0107         auto token = l.next_token();
0108         COS_TOKEN_TYPE(token, ObjectEnd);
0109         QVERIFY_THROWS_EXCEPTION(CosError, lex(">"));
0110         QVERIFY_THROWS_EXCEPTION(CosError, lex(">a"));
0111     }
0112 
0113     void test_lex_array()
0114     {
0115         CosLexer l("[]"_b);
0116         auto token = l.next_token();
0117         COS_TOKEN_TYPE(token, ArrayStart);
0118         token = l.next_token();
0119         COS_TOKEN_TYPE(token, ArrayEnd);
0120     }
0121 
0122     void test_lex_string_ascii()
0123     {
0124         CosLexer l("(Hello)"_b);
0125         auto token = l.next_token();
0126         COS_TOKEN(token, String, String, "Hello");
0127     }
0128 
0129     void test_lex_string_utf8()
0130     {
0131         CosLexer l("(H\xe2\x82\xacllo)"_b);
0132         auto token = l.next_token();
0133         COS_TOKEN(token, String, String, "H€llo");
0134     }
0135 
0136     void test_lex_string_utf16le()
0137     {
0138         CosLexer l("(\xff\xfeH\0\xac l\0l\0o\0)"_b);
0139         auto token = l.next_token();
0140         COS_TOKEN(token, String, String, "H€llo");
0141     }
0142 
0143     void test_lex_string_utf16be()
0144     {
0145         CosLexer l("(\xfe\xff\0H \xac\0l\0l\0o)"_b);
0146         auto token = l.next_token();
0147         COS_TOKEN(token, String, String, "H€llo");
0148     }
0149 
0150     void test_lex_string_escapes()
0151     {
0152         CosLexer l(R"((\b\n\f\r\(\)\\\100\41a\41))"_b);
0153         auto token = l.next_token();
0154         COS_TOKEN(token, String, String, QString("\b\n\f\r()\\@!a!"));
0155     }
0156 
0157     void test_lex_string_newlines()
0158     {
0159         CosLexer l("(1\n2\n\r3\r4\n\r5)"_b);
0160         auto token = l.next_token();
0161         COS_TOKEN(token, String, String, QString("1\n2\n3\n4\n5"));
0162     }
0163 
0164     void test_lex_string_errors()
0165     {
0166         QVERIFY_THROWS_EXCEPTION(CosError, lex("(Foo"));
0167         QVERIFY_THROWS_EXCEPTION(CosError, lex("(\\@)"));
0168         QVERIFY_THROWS_EXCEPTION(CosError, lex("(\\"));
0169         QVERIFY_THROWS_EXCEPTION(CosError, lex("(\\1"));
0170     }
0171 
0172     void test_lex_keywords()
0173     {
0174         CosLexer l("true false null foo"_b);
0175         auto token = l.next_token();
0176         COS_TOKEN(token, Boolean, Boolean, true);
0177         token = l.next_token();
0178         COS_TOKEN(token, Boolean, Boolean, false);
0179         token = l.next_token();
0180         COS_TOKEN(token, Null, Null, nullptr);
0181         QVERIFY_THROWS_EXCEPTION(CosError, l.next_token());
0182     }
0183 
0184     void test_lex_number()
0185     {
0186         COS_TOKEN(lex("1234"_b), Number, Number, 1234);
0187         COS_TOKEN(lex("+1234"_b), Number, Number, 1234);
0188         COS_TOKEN(lex("-1234"_b), Number, Number, -1234);
0189         COS_TOKEN(lex(".25"_b), Number, Number, 0.25);
0190         COS_TOKEN(lex("0.25"_b), Number, Number, 0.25);
0191         COS_TOKEN(lex("16.25"_b), Number, Number, 16.25);
0192         COS_TOKEN(lex("-.25"_b), Number, Number, -0.25);
0193         COS_TOKEN(lex("+.25"_b), Number, Number, 0.25);
0194         COS_TOKEN(lex("-0.25"_b), Number, Number, -0.25);
0195         COS_TOKEN(lex("+0.25"_b), Number, Number, 0.25);
0196         CosLexer l("12true0.5false"_b);
0197         auto token = l.next_token();
0198         COS_TOKEN(token, Number, Number, 12);
0199         token = l.next_token();
0200         COS_TOKEN(token, Boolean, Boolean, true);
0201         token = l.next_token();
0202         COS_TOKEN(token, Number, Number, 0.5);
0203         token = l.next_token();
0204         COS_TOKEN(token, Boolean, Boolean, false);
0205     }
0206 
0207     void test_unkown_token()
0208     {
0209         QVERIFY_THROWS_EXCEPTION(CosError, lex("@"));
0210     }
0211 
0212     void test_parse_value()
0213     {
0214         COS_VALUE(parse("(foo)"), String, "foo");
0215         COS_VALUE(parse("<f00d>"), Bytes, "\xf0\x0d"_b);
0216         COS_VALUE(parse("null"), Null, nullptr);
0217         COS_VALUE(parse("true"), Boolean, true);
0218         COS_VALUE(parse("123"), Number, 123);
0219     }
0220 
0221     void test_parse_mid_object()
0222     {
0223         auto value = parse("/foo (bar) /bar 123");
0224         QCOMPARE(value.type(), CosValue::Index::Object);
0225         auto& obj = value.get<CosValue::Index::Object>();
0226         COS_VALUE(obj->at("foo"), String, "bar");
0227         COS_VALUE(obj->at("bar"), Number, 123);
0228     }
0229 
0230     void test_parse_object()
0231     {
0232         auto value = parse("<< /foo (bar) /bar 123 >>");
0233         QCOMPARE(value.type(), CosValue::Index::Object);
0234         auto& obj = value.get<CosValue::Index::Object>();
0235         COS_VALUE(obj->at("foo"), String, "bar");
0236         COS_VALUE(obj->at("bar"), Number, 123);
0237     }
0238 
0239     void test_parse_mid_array()
0240     {
0241         auto value = parse("(bar) 123");
0242         QCOMPARE(value.type(), CosValue::Index::Array);
0243         auto& obj = *value.get<CosValue::Index::Array>();
0244         COS_VALUE(obj[0], String, "bar");
0245         COS_VALUE(obj[1], Number, 123);
0246     }
0247 
0248     void test_parse_array()
0249     {
0250         auto value = parse("[(bar) 123]");
0251         QCOMPARE(value.type(), CosValue::Index::Array);
0252         auto& obj = *value.get<CosValue::Index::Array>();
0253         COS_VALUE(obj[0], String, "bar");
0254         COS_VALUE(obj[1], Number, 123);
0255     }
0256 };
0257 
0258 QTEST_GUILESS_MAIN(TestCase)
0259 #include "test_cos_parser.moc"
0260 
0261