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"