File indexing completed on 2024-05-19 04:21:06
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