File indexing completed on 2024-05-19 15:45:31
0001 /* 0002 SPDX-FileCopyrightText: 2018 Friedrich W. H. Kossebau <kossebau@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "test_miparser.h" 0008 0009 // SUT 0010 #include <mi/miparser.h> 0011 // Qt 0012 #include <QTest> 0013 #include <QStandardPaths> 0014 0015 struct ResultData 0016 { 0017 QString name; 0018 QVariant value; 0019 }; 0020 0021 struct ResultRecordData 0022 { 0023 uint32_t token; 0024 QString reason; 0025 QVector<ResultData> results; 0026 0027 QVariant toVariant() { return QVariant::fromValue<ResultRecordData>(*this); } 0028 }; 0029 Q_DECLARE_METATYPE(ResultRecordData) 0030 0031 struct AsyncRecordData 0032 { 0033 int subkind; 0034 QString reason; 0035 QVector<ResultData> results; 0036 0037 QVariant toVariant() { return QVariant::fromValue<AsyncRecordData>(*this); } 0038 }; 0039 Q_DECLARE_METATYPE(AsyncRecordData) 0040 0041 struct StreamRecordData 0042 { 0043 int subkind; 0044 QString message; 0045 0046 QVariant toVariant() { return QVariant::fromValue<StreamRecordData>(*this); } 0047 }; 0048 Q_DECLARE_METATYPE(StreamRecordData) 0049 0050 void TestMIParser::initTestCase() 0051 { 0052 QStandardPaths::setTestModeEnabled(true); 0053 } 0054 0055 void TestMIParser::testParseLine_data() 0056 { 0057 QTest::addColumn<QByteArray>("line"); 0058 QTest::addColumn<int>("recordKind"); 0059 QTest::addColumn<QVariant>("recordData"); 0060 0061 // prompt 0062 QTest::newRow("gdpprompt") 0063 << QByteArray("(gdb)") 0064 << (int)KDevMI::MI::Record::Prompt 0065 << QVariant(); 0066 0067 // result records 0068 QTest::newRow("done") 0069 << QByteArray("^done") 0070 << (int)KDevMI::MI::Record::Result 0071 << ResultRecordData{0, "done", {}}.toVariant(); 0072 QTest::newRow("doneWToken") 0073 << QByteArray("11^done") 0074 << (int)KDevMI::MI::Record::Result 0075 << ResultRecordData{11, "done", {}}.toVariant(); 0076 QTest::newRow("runningWToken") 0077 << QByteArray("25^running") 0078 << (int)KDevMI::MI::Record::Result 0079 << ResultRecordData{25, "running", {}}.toVariant(); 0080 0081 // Out-of-band records 0082 QTest::newRow("stopreply") 0083 << QByteArray("*stop,reason=\"stop\",address=\"0x123\",source=\"a.c:123\"") 0084 << (int)KDevMI::MI::Record::Async 0085 << AsyncRecordData{KDevMI::MI::AsyncRecord::Exec, "stop", 0086 {{"reason", "stop"}, {"address", "0x123"}, {"source", "a.c:123"}}}.toVariant(); 0087 QTest::newRow("threadgroupadded") 0088 << QByteArray("=thread-group-added,id=\"i1\"") 0089 << (int)KDevMI::MI::Record::Async 0090 << AsyncRecordData{KDevMI::MI::AsyncRecord::Notify, "thread-group-added", 0091 {{"id", "i1"}}}.toVariant(); 0092 QTest::newRow("symbolfilereply") << QByteArray("*breakpoint,nr=\"3\",address=\"0x123\",source=\"a.c:123\"") 0093 << (int)KDevMI::MI::Record::Async 0094 << AsyncRecordData{KDevMI::MI::AsyncRecord::Exec, "breakpoint", 0095 {{"nr", "3"}, {"address", "0x123"}, {"source", "a.c:123"}}}.toVariant(); 0096 0097 // breakpoint creation records 0098 QTest::newRow("breakreply") 0099 << QByteArray("&\"break /path/to/some/file.cpp:28\\n\"") 0100 << (int)KDevMI::MI::Record::Stream 0101 << StreamRecordData{KDevMI::MI::StreamRecord::Log, "break /path/to/some/file.cpp:28\n"}.toVariant(); 0102 QTest::newRow("breakreply2") 0103 << QByteArray("~\"Breakpoint 1 at 0x400ab0: file /path/to/some/file.cpp, line 28.\\n\"") 0104 << (int)KDevMI::MI::Record::Stream 0105 << StreamRecordData{KDevMI::MI::StreamRecord::Console, "Breakpoint 1 at 0x400ab0: file /path/to/some/file.cpp, line 28.\n"}.toVariant(); 0106 QTest::newRow("breakreply3") 0107 << QByteArray("=breakpoint-created,bkpt={number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0x0000000000400ab0\",func=\"main(int, char**)\",file=\"/path/to/some/file.cpp\",fullname=\"/path/to/some/file.cpp\",line=\"28\",thread-groups=[\"i1\"],times=\"0\",original-location=\"/path/to/some/file.cpp:28\"}") 0108 << (int)KDevMI::MI::Record::Async 0109 << AsyncRecordData{KDevMI::MI::AsyncRecord::Notify, "breakpoint-created", 0110 {{"bkpt", QVariantMap{ 0111 {"number", "1"}, 0112 {"type", "breakpoint"}, 0113 {"disp", "keep"}, 0114 {"enabled", "y"}, 0115 {"addr", "0x0000000000400ab0"}, 0116 {"func", "main(int, char**)"}, 0117 {"file", "/path/to/some/file.cpp"}, 0118 {"fullname", "/path/to/some/file.cpp"}, 0119 {"line", "28"}, 0120 {"thread-groups", QVariantList{"i1"}}, 0121 {"times", "0"}, 0122 {"original-location", "/path/to/some/file.cpp:28"}}}}}.toVariant(); 0123 } 0124 0125 0126 void TestMIParser::doTestResult(const KDevMI::MI::Value& actualValue, const QVariant& expectedValue) 0127 { 0128 if (expectedValue.type() == QVariant::String) { 0129 QCOMPARE((int)actualValue.kind, (int)KDevMI::MI::Value::StringLiteral); 0130 QCOMPARE(actualValue.literal(), expectedValue.toString()); 0131 } else if (expectedValue.type() == QVariant::List) { 0132 QCOMPARE(actualValue.kind, KDevMI::MI::Value::List); 0133 0134 const auto expectedList = expectedValue.toList(); 0135 QCOMPARE(actualValue.size(), expectedList.size()); 0136 for (int i = 0; i < expectedList.size(); ++i) { 0137 doTestResult(actualValue[i], expectedList[i]); 0138 } 0139 } else if (expectedValue.type() == QVariant::Map) { 0140 QCOMPARE(actualValue.kind, KDevMI::MI::Value::Tuple); 0141 0142 const auto expectedMap = expectedValue.toMap(); 0143 0144 for (auto it = expectedMap.begin(), end = expectedMap.end(); it != end; ++it) { 0145 const auto& expectedMapField = it.key(); 0146 QVERIFY(actualValue.hasField(expectedMapField)); 0147 const auto& expectedMapValue = it.value(); 0148 doTestResult(actualValue[expectedMapField], expectedMapValue); 0149 } 0150 } else { 0151 QFAIL("Using unexpected QVariant type"); 0152 } 0153 } 0154 0155 0156 void TestMIParser::testParseLine() 0157 { 0158 QFETCH(QByteArray, line); 0159 QFETCH(int, recordKind); 0160 QFETCH(QVariant, recordData); 0161 0162 KDevMI::MI::MIParser m_parser; 0163 0164 KDevMI::MI::FileSymbol file; 0165 file.contents = line; 0166 0167 std::unique_ptr<KDevMI::MI::Record> record(m_parser.parse(&file)); 0168 QVERIFY(record != nullptr); 0169 QCOMPARE((int)record->kind, recordKind); 0170 0171 switch(recordKind) { 0172 case KDevMI::MI::Record::Result: { 0173 const auto& resultRecord = static_cast<KDevMI::MI::ResultRecord&>(*record); 0174 const auto resultData = recordData.value<ResultRecordData>(); 0175 0176 QCOMPARE(resultRecord.token, resultData.token); 0177 QCOMPARE(resultRecord.reason, resultData.reason); 0178 0179 for (const auto& result : resultData.results) { 0180 QVERIFY(resultRecord.hasField(result.name)); 0181 doTestResult(resultRecord[result.name], result.value); 0182 } 0183 break; 0184 } 0185 case KDevMI::MI::Record::Async: { 0186 const auto& asyncRecord = static_cast<KDevMI::MI::AsyncRecord&>(*record); 0187 const auto asyncData = recordData.value<AsyncRecordData>(); 0188 0189 QCOMPARE((int)asyncRecord.subkind, asyncData.subkind); 0190 QCOMPARE(asyncRecord.reason, asyncData.reason); 0191 0192 for (const auto& result : asyncData.results) { 0193 QVERIFY(asyncRecord.hasField(result.name)); 0194 doTestResult(asyncRecord[result.name], result.value); 0195 } 0196 break; 0197 } 0198 case KDevMI::MI::Record::Stream: { 0199 const auto& streamRecord = static_cast<KDevMI::MI::StreamRecord&>(*record); 0200 const auto streamData = recordData.value<StreamRecordData>(); 0201 0202 QCOMPARE((int)streamRecord.subkind, streamData.subkind); 0203 QCOMPARE(streamRecord.message, streamData.message); 0204 break; 0205 } 0206 case KDevMI::MI::Record::Prompt: 0207 break; 0208 } 0209 0210 } 0211 0212 QTEST_GUILESS_MAIN(TestMIParser) 0213 0214 #include "moc_test_miparser.cpp"