File indexing completed on 2024-05-12 05:51:05
0001 // SPDX-FileCopyrightText: 2023 RĂ©mi Peuchot <kde.remi@proton.me> 0002 // 0003 // SPDX-License-Identifier: LGPL-2.0-or-later 0004 0005 #include "test_gdbvariableparser.h" 0006 0007 #include "gdbvariableparser.h" 0008 0009 #include <QDebug> 0010 #include <QJsonArray> 0011 #include <QJsonObject> 0012 #include <QString> 0013 #include <QTest> 0014 #include <cstdlib> 0015 #include <iostream> 0016 #include <qstringliteral.h> 0017 #include <qtestcase.h> 0018 #include <time.h> 0019 0020 QTEST_MAIN(TestGdbVariableParser) 0021 0022 // To make the test cases easier to write and read, the small utility 'StructuredOutputFormatter' 0023 // allows to transform several 'GDBVariableParser:variable' signal calls 0024 // into a structured output string. 0025 // 0026 // Example : 0027 // - variable "toto" has flat value "@0x555556ffaff0: {a = {b = 12, c = "hello"}, d=[101]}", 0028 // - 'GDBVariableParser:variable' signal is supposed to be called 5 times 0029 // - 'StructuredOutputFormatter::appendVariable' slot will be called 5 times and construct : 0030 // toto-->@0x555556ffaff0: 0031 // a-->{...} 0032 // b-->12 0033 // c-->"hello" 0034 // d-->[101] 0035 class StructuredOutputFormatter : public QObject 0036 { 0037 Q_OBJECT 0038 public: 0039 QString getStructuredOutput() 0040 { 0041 return m_structuredOutput; 0042 } 0043 0044 public Q_SLOTS: 0045 void appendVariable(int parentId, const dap::Variable &variable) 0046 { 0047 qDebug() << "appendVariable(parentId: " << parentId << " ; name: " << variable.name << ")"; 0048 0049 QString indentation; 0050 if (m_variableIndentation.find(parentId) != m_variableIndentation.end()) { 0051 indentation = m_variableIndentation.at(parentId) + m_indentation; 0052 } 0053 m_variableIndentation[variable.variablesReference] = indentation; 0054 0055 m_structuredOutput.append(indentation); 0056 m_structuredOutput.append(variable.name); 0057 m_structuredOutput.append(m_valueIndicator); 0058 m_structuredOutput.append(variable.value); 0059 m_structuredOutput.append(m_newLine); 0060 } 0061 0062 private: 0063 const QString m_newLine = QStringLiteral("\n"); 0064 const QString m_valueIndicator = QStringLiteral("-->"); 0065 const QString m_indentation = QStringLiteral(" "); 0066 QString m_structuredOutput; 0067 std::map<int, QString> m_variableIndentation; 0068 }; 0069 0070 void TestGdbVariableParser::parseVariable() 0071 { 0072 QFETCH(QString, variableName); 0073 QFETCH(QString, flatVariableValue); 0074 QFETCH(QString, expectedStructuredOutput); 0075 0076 StructuredOutputFormatter formatter; 0077 GDBVariableParser parser; 0078 0079 connect(&parser, &GDBVariableParser::variable, &formatter, &StructuredOutputFormatter::appendVariable); 0080 parser.insertVariable(variableName, flatVariableValue, QStringLiteral("unused_type")); 0081 // QCOMPARE and qDebug truncate the output, use std::cout to print full output 0082 std::cout << "structuredOutput:\n" << formatter.getStructuredOutput().toStdString() << std::endl; 0083 std::cout << "expectedStructuredOutput:\n" << expectedStructuredOutput.toStdString() << std::endl; 0084 QCOMPARE(formatter.getStructuredOutput(), expectedStructuredOutput); 0085 } 0086 0087 void TestGdbVariableParser::parseVariable_data() 0088 { 0089 QTest::addColumn<QString>("variableName"); 0090 QTest::addColumn<QString>("flatVariableValue"); 0091 QTest::addColumn<QString>("expectedStructuredOutput"); 0092 0093 QTest::newRow("simple_int") << "my_int" 0094 << "12" 0095 << "my_int-->12\n"; 0096 0097 QTest::newRow("simple_string") << "my_string" 0098 << "\"12\"" 0099 << "my_string-->\"12\"\n"; 0100 0101 QTest::newRow("simple_struct") << "my_struct" 0102 << "{b1 = \"aaaa\", b2 = 12, b3 = 1.23456}" 0103 << "my_struct-->{...}\n" 0104 " b1-->\"aaaa\"\n" 0105 " b2-->12\n" 0106 " b3-->1.23456\n"; 0107 0108 QTest::newRow("simple_array") << "my_array" 0109 << "{100, 150, 175}" 0110 << "my_array-->{...}\n" 0111 " [0]-->100\n" 0112 " [1]-->150\n" 0113 " [2]-->175\n"; 0114 0115 QTest::newRow("complex_struct") << "my_struct" 0116 << "{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0117 "c2 = {b1 = \"bb{bb}\\\"\\\"\\\\\\\"}}}}}\", b2 = 23, b3 = 2.5}, " 0118 "a = {12, 13, 14}}" 0119 << "my_struct-->{...}\n" 0120 " c1-->{...}\n" 0121 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0122 " b2-->12\n" 0123 " b3-->1.5\n" 0124 " c2-->{...}\n" 0125 " b1-->\"bb{bb}\\\"\\\"\\\\\\\"}}}}}\"\n" 0126 " b2-->23\n" 0127 " b3-->2.5\n" 0128 " a-->{...}\n" 0129 " [0]-->12\n" 0130 " [1]-->13\n" 0131 " [2]-->14\n"; 0132 0133 QTest::newRow("complex_array") << "my_array" 0134 << "{{d1 = {c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0135 "c2 = {}, " 0136 "a = {12, 13, 14}}, " 0137 "d2 = {{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0138 "c2 = {b1 = \"bb{bb}\\\"\\\"\\\\\\\"}}}}}\", b2 = 23, b3 = 2.5}, " 0139 "a = {12, 13, 14}}, " 0140 "{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0141 "c2 = {b1 = \"bb{bb}\\\"\\\"\\\\\\\"}}}}}\", b2 = 23, b3 = 2.5}, " 0142 "a = {12, 13, 14}}}}, " 0143 "{{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0144 "c2 = {b1 = \"bb{bb}\\\"\\\"\\\\\\\"}}}}}\", b2 = 23, b3 = 2.5}, " 0145 "a = {12, 13, 14}}, " 0146 "{{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0147 "c2 = {}, " 0148 "a = {12, 13, 14}}, " 0149 "{c1 = {b1 = \"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\", b2 = 12, b3 = 1.5}, " 0150 "c2 = {b1 = \"bb{bb}\\\"\\\"\\\\\\\"}}}}}\", b2 = 23, b3 = 2.5}, " 0151 "a = {12, 13, 14}}}}}" 0152 << "my_array-->{...}\n" 0153 " [0]-->{...}\n" 0154 " d1-->{...}\n" 0155 " c1-->{...}\n" 0156 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0157 " b2-->12\n" 0158 " b3-->1.5\n" 0159 " c2-->{}\n" 0160 " a-->{...}\n" 0161 " [0]-->12\n" 0162 " [1]-->13\n" 0163 " [2]-->14\n" 0164 " d2-->{...}\n" 0165 " [0]-->{...}\n" 0166 " c1-->{...}\n" 0167 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0168 " b2-->12\n" 0169 " b3-->1.5\n" 0170 " c2-->{...}\n" 0171 " b1-->\"bb{bb}\\\"\\\"\\\\\\\"}}}}}\"\n" 0172 " b2-->23\n" 0173 " b3-->2.5\n" 0174 " a-->{...}\n" 0175 " [0]-->12\n" 0176 " [1]-->13\n" 0177 " [2]-->14\n" 0178 " [1]-->{...}\n" 0179 " c1-->{...}\n" 0180 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0181 " b2-->12\n" 0182 " b3-->1.5\n" 0183 " c2-->{...}\n" 0184 " b1-->\"bb{bb}\\\"\\\"\\\\\\\"}}}}}\"\n" 0185 " b2-->23\n" 0186 " b3-->2.5\n" 0187 " a-->{...}\n" 0188 " [0]-->12\n" 0189 " [1]-->13\n" 0190 " [2]-->14\n" 0191 " [1]-->{...}\n" 0192 " [0]-->{...}\n" 0193 " c1-->{...}\n" 0194 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0195 " b2-->12\n" 0196 " b3-->1.5\n" 0197 " c2-->{...}\n" 0198 " b1-->\"bb{bb}\\\"\\\"\\\\\\\"}}}}}\"\n" 0199 " b2-->23\n" 0200 " b3-->2.5\n" 0201 " a-->{...}\n" 0202 " [0]-->12\n" 0203 " [1]-->13\n" 0204 " [2]-->14\n" 0205 " [1]-->{...}\n" 0206 " [0]-->{...}\n" 0207 " c1-->{...}\n" 0208 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0209 " b2-->12\n" 0210 " b3-->1.5\n" 0211 " c2-->{}\n" 0212 " a-->{...}\n" 0213 " [0]-->12\n" 0214 " [1]-->13\n" 0215 " [2]-->14\n" 0216 " [1]-->{...}\n" 0217 " c1-->{...}\n" 0218 " b1-->\"ab\\\\{aa\\\"aa\\\\\\\"\\\"}{}}{{{}}aa\"\n" 0219 " b2-->12\n" 0220 " b3-->1.5\n" 0221 " c2-->{...}\n" 0222 " b1-->\"bb{bb}\\\"\\\"\\\\\\\"}}}}}\"\n" 0223 " b2-->23\n" 0224 " b3-->2.5\n" 0225 " a-->{...}\n" 0226 " [0]-->12\n" 0227 " [1]-->13\n" 0228 " [2]-->14\n"; 0229 0230 QTest::newRow("object_with_additional_string_before_opening_brace") 0231 << "__for_range" 0232 << "@0x7fff06f96b40: {d = {d = 0x7fff06f96bc0, ptr = 0x7f45550045b5 <findVariableName(QStringView&)+119> u\"blablabla\", size = 1}, static _empty = 0 " 0233 "u'0000'}" 0234 << "__for_range-->@0x7fff06f96b40: {...}\n" 0235 " d-->{...}\n" 0236 " d-->0x7fff06f96bc0\n" 0237 " ptr-->0x7f45550045b5 <findVariableName(QStringView&)+119> u\"blablabla\"\n" 0238 " size-->1\n" 0239 " static _empty-->0 u'0000'\n"; 0240 0241 QTest::newRow("value_with_comma_in_parenthesis") 0242 << "my_var" 0243 << "{d = {data = 0, static_metacall = 0x5555555708c2 <SingleApplication::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>, " 0244 "relatedMetaObject = 0x0}}" 0245 << "my_var-->{...}\n" 0246 " d-->{...}\n" 0247 " data-->0\n" 0248 " static_metacall-->0x5555555708c2 <SingleApplication::qt_static_metacall(QObject*, QMetaObject::Call, int, void**)>\n" 0249 " relatedMetaObject-->0x0\n"; 0250 } 0251 0252 // It does not really make sense to run many fuzzy tests systematically 0253 // Try with FUZZY_TESTS_COUNT=1000000 if GDBVariableParser has been modified 0254 static const int FUZZY_TESTS_COUNT = 1; 0255 static const int FUZZY_TESTS_MAX_BUFFER_SIZE = 100; 0256 const QString MEANINGFUL_CHARS = QStringLiteral("=,{}\\\"\0"); // Meaningful to variable parser 0257 0258 QChar randomChar() 0259 { 0260 // Artificially increases the probability to return a meaningful character (more likely to create problems) 0261 if ((rand() % 2) == 0) { 0262 // Random char among meaningful chars 0263 return MEANINGFUL_CHARS[rand() % MEANINGFUL_CHARS.length()]; 0264 } else { 0265 // Truly random char 0266 return QChar(rand() % (1 << 16)); 0267 } 0268 } 0269 0270 // Call 'GDBVariableParser::insertVariable' with random noise as input 0271 // structuredOutput is not compared at the end, it is only expected to : 0272 // - not crash 0273 // - not fall in infinite loop 0274 void TestGdbVariableParser::fuzzyTest() 0275 { 0276 srand(time(nullptr)); 0277 GDBVariableParser parser; 0278 for (int t = 0; t < FUZZY_TESTS_COUNT; t++) { 0279 int buffer_size = rand() % FUZZY_TESTS_MAX_BUFFER_SIZE; 0280 QString randomBuffer(buffer_size, QChar(0)); 0281 for (int i = 0; i < buffer_size; i++) { 0282 randomBuffer[i] = randomChar(); 0283 } 0284 // Print the random buffer so we can reproduce and investigate if something goes wrong 0285 // (don't use qDebug because it can modify/truncate the output) 0286 std::cout << "fuzzyTest " << t + 1 << "/" << FUZZY_TESTS_COUNT << " randomBuffer:" << randomBuffer.toStdString() << std::endl; 0287 parser.insertVariable(QStringLiteral("fuzzy_var"), randomBuffer, QStringLiteral("")); 0288 } 0289 }; 0290 0291 #include "moc_test_gdbvariableparser.cpp" 0292 #include "test_gdbvariableparser.moc"