File indexing completed on 2024-05-12 05:51:05

0001 /*
0002     SPDX-FileCopyrightText: 2022 Héctor Mesa Jiménez <wmj.py@gmx.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "test_gdbmi.h"
0008 
0009 #include "../gdbmi/parser.h"
0010 #include "../gdbmi/tokens.h"
0011 
0012 #include <QSignalSpy>
0013 #include <QString>
0014 #include <QTest>
0015 
0016 QTEST_MAIN(TestGdbmi)
0017 
0018 void TestGdbmi::tryToken()
0019 {
0020     QFETCH(QString, msg);
0021     QFETCH(int, start);
0022     QFETCH(bool, empty);
0023     QFETCH(bool, error);
0024     QFETCH(int, position);
0025     QFETCH(int, value);
0026 
0027     const auto tok = gdbmi::tryToken(msg.toUtf8(), start);
0028 
0029     QCOMPARE(tok.isEmpty(), empty);
0030     if (empty) {
0031         QCOMPARE(tok.hasError(), error);
0032         QCOMPARE(tok.position, start);
0033     } else {
0034         QVERIFY(tok.value.has_value());
0035         QCOMPARE(tok.value.value(), value);
0036         QCOMPARE(tok.position, position);
0037     }
0038 }
0039 
0040 void TestGdbmi::tryToken_data()
0041 {
0042     QTest::addColumn<QString>("msg");
0043     QTest::addColumn<int>("start");
0044     QTest::addColumn<bool>("empty");
0045     QTest::addColumn<bool>("error");
0046     QTest::addColumn<int>("position");
0047     QTest::addColumn<int>("value");
0048 
0049     QTest::newRow("1234") << "1234" << 0 << false << false << 4 << 1234;
0050     QTest::newRow("1234a") << "1234a" << 0 << false << false << 4 << 1234;
0051     QTest::newRow("1234\\n") << "1234\n" << 0 << false << false << 4 << 1234;
0052     QTest::newRow("__1234") << "__1234" << 2 << false << false << 6 << 1234;
0053     QTest::newRow("asdf") << "asdf" << 0 << true << false << 0 << -1;
0054 }
0055 
0056 void TestGdbmi::advanceBlanks()
0057 {
0058     QFETCH(QString, msg);
0059     QFETCH(int, start);
0060     QFETCH(int, position);
0061 
0062     const int pos = gdbmi::advanceBlanks(msg.toUtf8(), start);
0063     QCOMPARE(pos, position);
0064 }
0065 
0066 void TestGdbmi::advanceBlanks_data()
0067 {
0068     QTest::addColumn<QString>("msg");
0069     QTest::addColumn<int>("start");
0070     QTest::addColumn<int>("position");
0071 
0072     QTest::newRow(" \\t  asdf") << " \t  asdf" << 0 << 4;
0073     QTest::newRow(" \\t  \\nasdf") << " \t  \nasdf" << 0 << 4;
0074     QTest::newRow(" \\t  \\nasdf[2]") << " \t  \nasdf" << 2 << 4;
0075     QTest::newRow("asdf") << "asdf" << 2 << 2;
0076 }
0077 
0078 void TestGdbmi::tryString()
0079 {
0080     QFETCH(QString, msg);
0081     QFETCH(int, start);
0082     QFETCH(bool, empty);
0083     QFETCH(bool, error);
0084     QFETCH(int, position);
0085     QFETCH(QString, value);
0086 
0087     const auto tok = gdbmi::tryString(msg.toUtf8(), start);
0088 
0089     QCOMPARE(tok.isEmpty(), empty);
0090     if (empty) {
0091         QCOMPARE(tok.hasError(), error);
0092         QCOMPARE(tok.position, start);
0093     } else {
0094         QVERIFY(tok.value.has_value());
0095         QCOMPARE(tok.value.value(), value);
0096         QCOMPARE(tok.position, position);
0097     }
0098 }
0099 
0100 void TestGdbmi::tryString_data()
0101 {
0102     QTest::addColumn<QString>("msg");
0103     QTest::addColumn<int>("start");
0104     QTest::addColumn<bool>("empty");
0105     QTest::addColumn<bool>("error");
0106     QTest::addColumn<int>("position");
0107     QTest::addColumn<QString>("value");
0108 
0109     QTest::newRow("\"12\\n34\"") << R"-("12\n34")-" << 0 << false << false << 8 << "12\n34";
0110     QTest::newRow("\"\"") << "\"\"" << 0 << false << false << 2 << "";
0111     // base
0112     QTest::newRow("\"1234\"") << "\"1234\"" << 0 << false << false << 6 << "1234";
0113     // escaped quote
0114     QTest::newRow("\"12\\\"34\"") << R"-("12\\\"34")-" << 0 << false << false << 10 << R"-(12\"34)-";
0115     // incomplete (start)
0116     QTest::newRow("\"1234\"") << "\"1234\"" << 1 << true << true << 0 << "";
0117     // incomplete (end)
0118     QTest::newRow("\"1234") << "\"1234" << 0 << true << true << 0 << "";
0119 }
0120 
0121 void TestGdbmi::tryClassName()
0122 {
0123     QFETCH(QString, msg);
0124     QFETCH(int, start);
0125     QFETCH(bool, empty);
0126     QFETCH(bool, error);
0127     QFETCH(int, position);
0128     QFETCH(QString, value);
0129 
0130     const auto tok = gdbmi::tryClassName(msg.toUtf8(), start);
0131 
0132     QCOMPARE(tok.isEmpty(), empty);
0133     if (empty) {
0134         QCOMPARE(tok.hasError(), error);
0135         QCOMPARE(tok.position, start);
0136     } else {
0137         QVERIFY(tok.value.has_value());
0138         QCOMPARE(tok.value.value(), value);
0139         QCOMPARE(tok.position, position);
0140     }
0141 }
0142 
0143 void TestGdbmi::tryClassName_data()
0144 {
0145     QTest::addColumn<QString>("msg");
0146     QTest::addColumn<int>("start");
0147     QTest::addColumn<bool>("empty");
0148     QTest::addColumn<bool>("error");
0149     QTest::addColumn<int>("position");
0150     QTest::addColumn<QString>("value");
0151 
0152     QTest::newRow("^exit") << "^exit" << 1 << false << false << 5 << "exit";
0153     QTest::newRow("^exit\\n") << "^exit\n" << 1 << false << false << 5 << "exit";
0154     QTest::newRow("^exit\\r\\n") << "^exit\r\n" << 1 << false << false << 5 << "exit";
0155     QTest::newRow("^__exit") << "^  exit" << 1 << false << false << 7 << "exit";
0156     QTest::newRow("^stopped,reason") << "^stopped,reason" << 1 << false << false << 8 << "stopped";
0157     QTest::newRow("_") << "" << 0 << true << true << -1 << "";
0158     QTest::newRow("^running\\n*running,id=1") << "^running\n*running,id=1" << 1 << false << false << 8 << "running";
0159 }
0160 
0161 void TestGdbmi::tryVariable()
0162 {
0163     QFETCH(QString, msg);
0164     QFETCH(int, start);
0165     QFETCH(bool, empty);
0166     QFETCH(bool, error);
0167     QFETCH(int, position);
0168     QFETCH(QString, value);
0169 
0170     const auto tok = gdbmi::tryVariable(msg.toUtf8(), start);
0171 
0172     QCOMPARE(tok.isEmpty(), empty);
0173     if (empty) {
0174         QCOMPARE(tok.hasError(), error);
0175         QCOMPARE(tok.position, start);
0176     } else {
0177         QVERIFY(tok.value.has_value());
0178         QCOMPARE(tok.value.value(), value);
0179         QCOMPARE(tok.position, position);
0180     }
0181 }
0182 
0183 void TestGdbmi::tryVariable_data()
0184 {
0185     QTest::addColumn<QString>("msg");
0186     QTest::addColumn<int>("start");
0187     QTest::addColumn<bool>("empty");
0188     QTest::addColumn<bool>("error");
0189     QTest::addColumn<int>("position");
0190     QTest::addColumn<QString>("value");
0191 
0192     QTest::newRow("reason=\"breakpoint-hit\"") << "reason=\"breakpoint-hit\"" << 0 << false << false << 7 << "reason";
0193     QTest::newRow("reason =\"breakpoint-hit\"") << "reason =\"breakpoint-hit\"" << 0 << false << false << 8 << "reason";
0194     QTest::newRow("__reason=\"breakpoint-hit\"") << "  reason=\"breakpoint-hit\"" << 0 << false << false << 9 << "reason";
0195     QTest::newRow("reason") << "reason" << 0 << true << true << 0 << "";
0196 }
0197 
0198 void TestGdbmi::tryStreamOutput()
0199 {
0200     QFETCH(QString, msg);
0201     QFETCH(int, start);
0202     QFETCH(bool, empty);
0203     QFETCH(bool, error);
0204     QFETCH(int, position);
0205     QFETCH(QString, value);
0206     QFETCH(char, prefix);
0207 
0208     const auto tok = gdbmi::tryStreamOutput(prefix, msg.toUtf8(), start);
0209 
0210     QCOMPARE(tok.isEmpty(), empty);
0211     if (empty) {
0212         QCOMPARE(tok.hasError(), error);
0213         QCOMPARE(tok.position, start);
0214     } else {
0215         QVERIFY(tok.value.has_value());
0216         QCOMPARE(tok.value.value().message, value);
0217         QCOMPARE(tok.position, position);
0218     }
0219 }
0220 
0221 void TestGdbmi::tryStreamOutput_data()
0222 {
0223     QTest::addColumn<QString>("msg");
0224     QTest::addColumn<int>("start");
0225     QTest::addColumn<bool>("empty");
0226     QTest::addColumn<bool>("error");
0227     QTest::addColumn<int>("position");
0228     QTest::addColumn<QString>("value");
0229     QTest::addColumn<char>("prefix");
0230 
0231     QTest::newRow("~\"algo\",^running") << "~\"algo\",^running" << 0 << false << false << 7 << "algo" << '~';
0232     QTest::newRow("~\"algo\"") << "~\"algo\"" << 0 << false << false << 7 << "algo" << '~';
0233     QTest::newRow("~\"algo\"\\n") << "~\"algo\"\n" << 0 << false << false << 8 << "algo" << '~';
0234     QTest::newRow("~\"algo\"\\n") << "~\"algo\"\\n" << 0 << false << false << 7 << "algo" << '~';
0235     QTest::newRow("~\"algo") << "~\"algo" << 0 << false << false << 6 << "\"algo" << '~';
0236     QTest::newRow("~\"algo\\n") << "~\"algo\n" << 0 << false << false << 7 << "\"algo" << '~';
0237 }
0238 
0239 void TestGdbmi::tryResult()
0240 {
0241     QFETCH(QString, msg);
0242     QFETCH(int, start);
0243     QFETCH(bool, empty);
0244     QFETCH(bool, error);
0245     QFETCH(int, position);
0246     QFETCH(QJsonObject, value);
0247 
0248     const auto tok = gdbmi::tryResult(msg.toUtf8(), start);
0249 
0250     QCOMPARE(tok.isEmpty(), empty);
0251     if (empty) {
0252         QCOMPARE(tok.hasError(), error);
0253         QCOMPARE(tok.position, start);
0254     } else {
0255         QCOMPARE(tok.position, position);
0256         QVERIFY(tok.value.has_value());
0257         const auto tokval = tok.value.value();
0258         QVERIFY(value.contains(tokval.name));
0259         compare(value[tokval.name], tokval.value);
0260     }
0261 }
0262 
0263 void TestGdbmi::tryResult_data()
0264 {
0265     QTest::addColumn<QString>("msg");
0266     QTest::addColumn<int>("start");
0267     QTest::addColumn<bool>("empty");
0268     QTest::addColumn<bool>("error");
0269     QTest::addColumn<int>("position");
0270     QTest::addColumn<QJsonObject>("value");
0271 
0272     QTest::newRow("reason=\"breakpoint-hit\"") << "reason=\"breakpoint-hit\"" << 0 << false << false << 23
0273                                                << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}};
0274     QTest::newRow("reason=\"breakpoint-hit") << "reason=\"breakpoint-hit" << 0 << true << true << 0 << QJsonObject{};
0275     QTest::newRow("^done,reason=\"breakpoint-hit\"") << "^done,reason=\"breakpoint-hit\"" << 6 << false << false << 29
0276                                                      << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}};
0277     QTest::newRow("thread-groups=[\"i1\"]") << "thread-groups=[\"i1\"]" << 0 << false << false << 20
0278                                             << QJsonObject{{QStringLiteral("thread-groups"), QJsonArray{QStringLiteral("i1")}}};
0279     QTest::newRow("args=[]") << "args=[]" << 0 << false << false << 7 << QJsonObject{{QStringLiteral("args"), QJsonArray()}};
0280     QTest::newRow("frame={}") << "frame={}" << 0 << false << false << 8 << QJsonObject{{QStringLiteral("frame"), QJsonObject()}};
0281 }
0282 
0283 void TestGdbmi::tryResults()
0284 {
0285     QFETCH(QString, msg);
0286     QFETCH(int, start);
0287     QFETCH(bool, empty);
0288     QFETCH(bool, error);
0289     QFETCH(int, position);
0290     QFETCH(QJsonObject, value);
0291 
0292     const auto tok = gdbmi::tryResults(msg.toUtf8(), start);
0293 
0294     QCOMPARE(tok.isEmpty(), empty);
0295     if (empty) {
0296         QCOMPARE(tok.hasError(), error);
0297         QCOMPARE(tok.position, start);
0298     } else {
0299         QCOMPARE(tok.position, position);
0300         QVERIFY(tok.value.has_value());
0301         compare(value, tok.value.value());
0302     }
0303 }
0304 
0305 void TestGdbmi::tryResults_data()
0306 {
0307     QTest::addColumn<QString>("msg");
0308     QTest::addColumn<int>("start");
0309     QTest::addColumn<bool>("empty");
0310     QTest::addColumn<bool>("error");
0311     QTest::addColumn<int>("position");
0312     QTest::addColumn<QJsonObject>("value");
0313 
0314     QTest::newRow("string1") << "reason=\"breakpoint-hit\"" << 0 << false << false << 23
0315                              << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}};
0316     QTest::newRow("breakpoint1")
0317         << "number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0x08048564\",func=\"main\",file=\"myprog.c\",fullname=\"/home/nickrob/"
0318            "myprog.c\",line=\"68\",thread-groups=[\"i1\"],times=\"0\""
0319         << 0 << false << false << 173
0320         << QJsonObject{{QStringLiteral("number"), QStringLiteral("1")},
0321                        {QStringLiteral("type"), QStringLiteral("breakpoint")},
0322                        {QStringLiteral("disp"), QStringLiteral("keep")},
0323                        {QStringLiteral("enabled"), QStringLiteral("y")},
0324                        {QStringLiteral("addr"), QStringLiteral("0x08048564")},
0325                        {QStringLiteral("func"), QStringLiteral("main")},
0326                        {QStringLiteral("file"), QStringLiteral("myprog.c")},
0327                        {QStringLiteral("fullname"), QStringLiteral("/home/nickrob/myprog.c")},
0328                        {QStringLiteral("line"), QStringLiteral("68")},
0329                        {QStringLiteral("thread-groups"), QJsonArray{QStringLiteral("i1")}},
0330                        {QStringLiteral("times"), QStringLiteral("0")}};
0331     QTest::newRow("breakpoint2")
0332         << "number=\"1\",type=\"breakpoint\",disp=\"del\",enabled=\"y\",addr=\"0x00005555555551f5\",func=\"main()\",file=\"/pruebas/cpp1/"
0333            "main.cpp\",fullname=\"/pruebas/cpp1/main.cpp\",line=\"7\",thread-groups=[\"i1\"],times=\"1\",original-location=\"main\""
0334         << 0 << false << false << 220
0335         << QJsonObject{{QStringLiteral("number"), QStringLiteral("1")},
0336                        {QStringLiteral("type"), QStringLiteral("breakpoint")},
0337                        {QStringLiteral("disp"), QStringLiteral("del")},
0338                        {QStringLiteral("enabled"), QStringLiteral("y")},
0339                        {QStringLiteral("addr"), QStringLiteral("0x00005555555551f5")},
0340                        {QStringLiteral("func"), QStringLiteral("main()")},
0341                        {QStringLiteral("file"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0342                        {QStringLiteral("fullname"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0343                        {QStringLiteral("line"), QStringLiteral("7")},
0344                        {QStringLiteral("thread-groups"), QJsonArray{QStringLiteral("i1")}},
0345                        {QStringLiteral("times"), QStringLiteral("1")},
0346                        {QStringLiteral("original-location"), QStringLiteral("main")}};
0347 
0348     QTest::newRow("breakpoint-frame-noargs") << "addr=\"0x00005555555551f5\",func=\"main\",args=[],file=\"/pruebas/cpp1/main.cpp\"" << 0 << false << false << 75
0349                                              << QJsonObject{{QStringLiteral("addr"), QStringLiteral("0x00005555555551f5")},
0350                                                             {QStringLiteral("func"), QStringLiteral("main")},
0351                                                             {QStringLiteral("args"), QJsonArray()},
0352                                                             {QStringLiteral("file"), QStringLiteral("/pruebas/cpp1/main.cpp")}};
0353 
0354     QTest::newRow("breakpoint-frame-noargs-full") << "addr=\"0x00005555555551f5\",func=\"main\",args=[],file=\"/pruebas/cpp1/main.cpp\",fullname=\"/pruebas/"
0355                                                      "cpp1/main.cpp\",line=\"7\",arch=\"i386:x86-64\""
0356                                                   << 0 << false << false << 137
0357                                                   << QJsonObject{{QStringLiteral("addr"), QStringLiteral("0x00005555555551f5")},
0358                                                                  {QStringLiteral("func"), QStringLiteral("main")},
0359                                                                  {QStringLiteral("args"), QJsonArray()},
0360                                                                  {QStringLiteral("file"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0361                                                                  {QStringLiteral("fullname"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0362                                                                  {QStringLiteral("line"), QStringLiteral("7")},
0363                                                                  {QStringLiteral("arch"), QStringLiteral("i386:x86-64")}};
0364 
0365     QTest::newRow("breakpoint-full")
0366         << "reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"1\",frame={addr=\"0x00005555555551f5\",func=\"main\",args=[],file=\"/pruebas/cpp1/"
0367            "main.cpp\",fullname=\"/pruebas/cpp1/main.cpp\",line=\"7\",arch=\"i386:x86-64\"},thread-id=\"1\",stopped-threads=\"all\",core=\"1\"\n"
0368         << 0 << false << false << 236
0369         << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")},
0370                        {QStringLiteral("disp"), QStringLiteral("del")},
0371                        {QStringLiteral("bkptno"), QStringLiteral("1")},
0372                        {QStringLiteral("frame"),
0373                         QJsonObject{{QStringLiteral("addr"), QStringLiteral("0x00005555555551f5")},
0374                                     {QStringLiteral("func"), QStringLiteral("main")},
0375                                     {QStringLiteral("args"), QJsonArray()},
0376                                     {QStringLiteral("file"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0377                                     {QStringLiteral("fullname"), QStringLiteral("/pruebas/cpp1/main.cpp")},
0378                                     {QStringLiteral("line"), QStringLiteral("7")},
0379                                     {QStringLiteral("arch"), QStringLiteral("i386:x86-64")}}},
0380                        {QStringLiteral("thread-id"), QStringLiteral("1")},
0381                        {QStringLiteral("stopped-threads"), QStringLiteral("all")},
0382                        {QStringLiteral("core"), QStringLiteral("1")}};
0383 }
0384 
0385 void TestGdbmi::tryTuple()
0386 {
0387     QFETCH(QString, msg);
0388     QFETCH(int, start);
0389     QFETCH(bool, empty);
0390     QFETCH(bool, error);
0391     QFETCH(int, position);
0392     QFETCH(QJsonObject, value);
0393 
0394     const auto tok = gdbmi::tryTuple(msg.toUtf8(), start);
0395 
0396     QCOMPARE(tok.isEmpty(), empty);
0397     if (empty) {
0398         QCOMPARE(tok.hasError(), error);
0399         QCOMPARE(tok.position, start);
0400     } else {
0401         QCOMPARE(tok.position, position);
0402         QVERIFY(tok.value.has_value());
0403         compare(value, tok.value.value());
0404     }
0405 }
0406 
0407 void TestGdbmi::tryTuple_data()
0408 {
0409     QTest::addColumn<QString>("msg");
0410     QTest::addColumn<int>("start");
0411     QTest::addColumn<bool>("empty");
0412     QTest::addColumn<bool>("error");
0413     QTest::addColumn<int>("position");
0414     QTest::addColumn<QJsonObject>("value");
0415 
0416     QTest::newRow("{}") << "{}" << 0 << false << false << 2 << QJsonObject();
0417     QTest::newRow("{ }") << "{ }" << 0 << false << false << 3 << QJsonObject();
0418     QTest::newRow("{reason=\"breakpoint-hit\"}") << "{reason=\"breakpoint-hit\"}" << 0 << false << false << 25
0419                                                  << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}};
0420     QTest::newRow(
0421         "{number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0x08048564\",func=\"main\",file=\"myprog.c\",fullname=\"/home/nickrob/"
0422         "myprog.c\",line=\"68\",thread-groups=[\"i1\"],times=\"0\"}")
0423         << "{number=\"1\",type=\"breakpoint\",disp=\"keep\",enabled=\"y\",addr=\"0x08048564\",func=\"main\",file=\"myprog.c\",fullname=\"/home/nickrob/"
0424            "myprog.c\",line=\"68\",thread-groups=[\"i1\"],times=\"0\"}"
0425         << 0 << false << false << 175
0426         << QJsonObject{{QStringLiteral("number"), QStringLiteral("1")},
0427                        {QStringLiteral("type"), QStringLiteral("breakpoint")},
0428                        {QStringLiteral("disp"), QStringLiteral("keep")},
0429                        {QStringLiteral("enabled"), QStringLiteral("y")},
0430                        {QStringLiteral("addr"), QStringLiteral("0x08048564")},
0431                        {QStringLiteral("func"), QStringLiteral("main")},
0432                        {QStringLiteral("file"), QStringLiteral("myprog.c")},
0433                        {QStringLiteral("fullname"), QStringLiteral("/home/nickrob/myprog.c")},
0434                        {QStringLiteral("line"), QStringLiteral("68")},
0435                        {QStringLiteral("thread-groups"), QJsonArray{QStringLiteral("i1")}},
0436                        {QStringLiteral("times"), QStringLiteral("0")}};
0437 }
0438 
0439 void TestGdbmi::tryValue()
0440 {
0441     QFETCH(QString, msg);
0442     QFETCH(int, start);
0443     QFETCH(bool, empty);
0444     QFETCH(bool, error);
0445     QFETCH(int, position);
0446     QFETCH(QJsonValue, value);
0447 
0448     const auto tok = gdbmi::tryValue(msg.toUtf8(), start);
0449 
0450     QCOMPARE(tok.isEmpty(), empty);
0451     if (empty) {
0452         QCOMPARE(tok.hasError(), error);
0453         QCOMPARE(tok.position, start);
0454     } else {
0455         QCOMPARE(tok.position, position);
0456         QVERIFY(tok.value.has_value());
0457         compare(value, tok.value.value());
0458     }
0459 }
0460 
0461 void TestGdbmi::tryValue_data()
0462 {
0463     QTest::addColumn<QString>("msg");
0464     QTest::addColumn<int>("start");
0465     QTest::addColumn<bool>("empty");
0466     QTest::addColumn<bool>("error");
0467     QTest::addColumn<int>("position");
0468     QTest::addColumn<QJsonValue>("value");
0469 
0470     QTest::newRow("\"breakpoint-hit\"") << "\"breakpoint-hit\"" << 0 << false << false << 16 << QJsonValue(QStringLiteral("breakpoint-hit"));
0471     QTest::newRow("{reason=\"breakpoint-hit\"}") << "{reason=\"breakpoint-hit\"}" << 0 << false << false << 25
0472                                                  << QJsonValue(QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}});
0473     QTest::newRow("[\"breakpoint-hit\", \"i1\"]") << "[\"breakpoint-hit\", \"i1\"]" << 0 << false << false << 24
0474                                                   << QJsonValue(QJsonArray{QStringLiteral("breakpoint-hit"), QStringLiteral("i1")});
0475 }
0476 
0477 void TestGdbmi::tryList()
0478 {
0479     QFETCH(QString, msg);
0480     QFETCH(int, start);
0481     QFETCH(bool, empty);
0482     QFETCH(bool, error);
0483     QFETCH(int, position);
0484     QFETCH(QJsonValue, value);
0485 
0486     const auto tok = gdbmi::tryList(msg.toUtf8(), start);
0487 
0488     QCOMPARE(tok.isEmpty(), empty);
0489     if (empty) {
0490         QCOMPARE(tok.hasError(), error);
0491         QCOMPARE(tok.position, start);
0492     } else {
0493         QCOMPARE(tok.position, position);
0494         QVERIFY(tok.value.has_value());
0495         compare(value, tok.value.value());
0496     }
0497 }
0498 
0499 void TestGdbmi::tryList_data()
0500 {
0501     QTest::addColumn<QString>("msg");
0502     QTest::addColumn<int>("start");
0503     QTest::addColumn<bool>("empty");
0504     QTest::addColumn<bool>("error");
0505     QTest::addColumn<int>("position");
0506     QTest::addColumn<QJsonValue>("value");
0507 
0508     QTest::newRow("empty") << "[]" << 0 << false << false << 2 << QJsonValue(QJsonArray());
0509     QTest::newRow("empty_with_blank") << "[ ]" << 0 << false << false << 3 << QJsonValue(QJsonArray());
0510     QTest::newRow("single_str_item") << "[\"breakpoint-hit\"]" << 0 << false << false << 18 << QJsonValue(QJsonArray{QStringLiteral("breakpoint-hit")});
0511     QTest::newRow("item_collection") << "[\"breakpoint-hit\", \"i1\"]" << 0 << false << false << 24
0512                                      << QJsonValue(QJsonArray{QStringLiteral("breakpoint-hit"), QStringLiteral("i1")});
0513     QTest::newRow("single_tuple") << "[reason=\"breakpoint-hit\"]" << 0 << false << false << 25
0514                                   << QJsonValue(QJsonArray{QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")}}});
0515     QTest::newRow("single_object") << "[{key1=\"value1\", key2=\"value2\"}]" << 0 << false << false << 32
0516                                    << QJsonValue(QJsonArray{
0517                                           QJsonObject{{QStringLiteral("key1"), QStringLiteral("value1")}, {QStringLiteral("key2"), QStringLiteral("value2")}}});
0518     QTest::newRow("tuple_collection") << "[frame=\"frame1\", frame=\"frame2\"]" << 0 << false << false << 32
0519                                       << QJsonValue(QJsonArray{QJsonObject{{QStringLiteral("frame"), QStringLiteral("frame1")}},
0520                                                                QJsonObject{{QStringLiteral("frame"), QStringLiteral("frame2")}}});
0521 }
0522 
0523 void TestGdbmi::quoted()
0524 {
0525     QFETCH(QString, original);
0526     QFETCH(QString, expected);
0527 
0528     const auto replaced = gdbmi::quotedString(original);
0529 
0530     QCOMPARE(replaced, expected);
0531 }
0532 
0533 void TestGdbmi::quoted_data()
0534 {
0535     QTest::addColumn<QString>("original");
0536     QTest::addColumn<QString>("expected");
0537 
0538     QTest::newRow("nothing") << "nothing"
0539                              << "nothing";
0540     QTest::newRow("quotable") << "abc \"cde\""
0541                               << "abc \\\"cde\\\"";
0542     QTest::newRow("already_quoted") << "abc \\\"cde\\\""
0543                                     << "abc \\\"cde\\\"";
0544 }
0545 
0546 void TestGdbmi::tryRecord()
0547 {
0548     QFETCH(QString, msg);
0549     QFETCH(int, start);
0550     QFETCH(bool, empty);
0551     QFETCH(bool, error);
0552     QFETCH(int, position);
0553     QFETCH(int, category);
0554     QFETCH(QString, resultClass);
0555     QFETCH(int, token);
0556     QFETCH(QJsonObject, value);
0557 
0558     const auto tok = gdbmi::tryRecord(msg.toUtf8().at(start), msg.toUtf8(), start, token);
0559 
0560     QCOMPARE(tok.isEmpty(), empty);
0561     if (empty) {
0562         QCOMPARE(tok.hasError(), error);
0563         QCOMPARE(tok.position, start);
0564     } else {
0565         QCOMPARE(tok.position, position);
0566         QVERIFY(tok.value.has_value());
0567         const auto record = tok.value.value();
0568         QCOMPARE(category, (int)record.category);
0569         QCOMPARE(record.token.value_or(-1), token);
0570         QCOMPARE(resultClass, record.resultClass);
0571         compare(value, record.value);
0572     }
0573 }
0574 
0575 void TestGdbmi::tryRecord_data()
0576 {
0577     QTest::addColumn<QString>("msg");
0578     QTest::addColumn<int>("start");
0579     QTest::addColumn<bool>("empty");
0580     QTest::addColumn<bool>("error");
0581     QTest::addColumn<int>("position");
0582     QTest::addColumn<int>("category");
0583     QTest::addColumn<QString>("resultClass");
0584     QTest::addColumn<int>("token");
0585     QTest::addColumn<QJsonObject>("value");
0586 
0587     QTest::newRow("^running") << "^running" << 0 << false << false << 8 << (int)gdbmi::Record::Result << QStringLiteral("running") << -1 << QJsonObject();
0588     QTest::newRow("123^done") << "123^done" << 3 << false << false << 8 << (int)gdbmi::Record::Result << QStringLiteral("done") << 123 << QJsonObject();
0589     QTest::newRow("*stopped,reason=\"exited-normally\"")
0590         << "*stopped,reason=\"exited-normally\"" << 0 << false << false << 33 << (int)gdbmi::Record::Exec << QStringLiteral("stopped") << -1
0591         << QJsonObject{{QStringLiteral("reason"), QStringLiteral("exited-normally")}};
0592     QTest::newRow("=thread-created,id=\"id\",group-id=\"gid\"")
0593         << "=thread-created,id=\"id\",group-id=\"gid\"" << 0 << false << false << 38 << (int)gdbmi::Record::Notify << QStringLiteral("thread-created") << -1
0594         << QJsonObject{{QStringLiteral("id"), QStringLiteral("id")}, {QStringLiteral("group-id"), QStringLiteral("gid")}};
0595     QTest::newRow(
0596         "*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"1\",thread-id=\"0\",frame={addr=\"0x08048564\",func=\"main\",args=[{name=\"argc\",value="
0597         "\"1\"},{name=\"argv\",value=\"0xbfc4d4d4\"}],file=\"myprog.c\",fullname=\"/home/nickrob/myprog.c\",line=\"68\",arch=\"i386:x86_64\"}")
0598         << "*stopped,reason=\"breakpoint-hit\",disp=\"keep\",bkptno=\"1\",thread-id=\"0\",frame={addr=\"0x08048564\",func=\"main\",args=[{name=\"argc\",value="
0599            "\"1\"},{name=\"argv\",value=\"0xbfc4d4d4\"}],file=\"myprog.c\",fullname=\"/home/nickrob/myprog.c\",line=\"68\",arch=\"i386:x86_64\"}"
0600         << 0 << false << false << 250 << (int)gdbmi::Record::Exec << QStringLiteral("stopped") << -1
0601         << QJsonObject{{QStringLiteral("reason"), QStringLiteral("breakpoint-hit")},
0602                        {QStringLiteral("disp"), QStringLiteral("keep")},
0603                        {QStringLiteral("bkptno"), QStringLiteral("1")},
0604                        {QStringLiteral("thread-id"), QStringLiteral("0")},
0605                        {QStringLiteral("frame"),
0606                         QJsonObject{{QStringLiteral("addr"), QStringLiteral("0x08048564")},
0607                                     {QStringLiteral("func"), QStringLiteral("main")},
0608                                     {QStringLiteral("args"),
0609                                      QJsonArray{QJsonObject{{QStringLiteral("name"), QStringLiteral("argc")}, {QStringLiteral("value"), QStringLiteral("1")}},
0610                                                 QJsonObject{{QStringLiteral("name"), QStringLiteral("argv")},
0611                                                             {QStringLiteral("value"), QStringLiteral("0xbfc4d4d4")}}}},
0612                                     {QStringLiteral("file"), QStringLiteral("myprog.c")},
0613                                     {QStringLiteral("fullname"), QStringLiteral("/home/nickrob/myprog.c")},
0614                                     {QStringLiteral("line"), QStringLiteral("68")},
0615                                     {QStringLiteral("arch"), QStringLiteral("i386:x86_64")}}}};
0616 }
0617 
0618 void TestGdbmi::parseResponse()
0619 {
0620     gdbmi::GdbmiParser parser;
0621 
0622     qRegisterMetaType<gdbmi::StreamOutput>("StreamOutput");
0623     qRegisterMetaType<gdbmi::Record>("Record");
0624     QSignalSpy outputSpy(&parser, &gdbmi::GdbmiParser::outputProduced);
0625     QVERIFY(outputSpy.isValid());
0626     QSignalSpy recSpy(&parser, &gdbmi::GdbmiParser::recordProduced);
0627     QVERIFY(recSpy.isValid());
0628 
0629     const QString text = QStringLiteral(R"--(
0630 ~algo
0631 =thread-created,id="id",group-id="gid"
0632 *stopped,reason="breakpoint-hit",disp="keep",bkptno="1",thread-id="0",frame={addr="0x08048564",func="main",args=[{name="argc",value="1"},{name="argv",value="0xbfc4d4d4"}],file="myprog.c",fullname="/home/nickrob/myprog.c",line="68",arch="i386:x86_64"}
0633 123^done,bkpt={number="1",type="breakpoint",disp="keep",enabled="y",addr="0x08048564",func="main",file="myprog.c",fullname="/home/nickrob/myprog.c",line="68",thread-groups=["i1"],times="0"}
0634     )--");
0635 
0636     parser.parseResponse(text.toLocal8Bit());
0637 
0638     QCOMPARE(outputSpy.size(), 1);
0639     QCOMPARE(recSpy.size(), 3);
0640 
0641     QCOMPARE(QStringLiteral("algo"), qvariant_cast<gdbmi::StreamOutput>(outputSpy[0][0]).message);
0642 
0643     QCOMPARE(QStringLiteral("thread-created"), qvariant_cast<gdbmi::Record>(recSpy[0][0]).resultClass);
0644     QCOMPARE(QStringLiteral("stopped"), qvariant_cast<gdbmi::Record>(recSpy[1][0]).resultClass);
0645     QCOMPARE(QStringLiteral("done"), qvariant_cast<gdbmi::Record>(recSpy[2][0]).resultClass);
0646 }
0647 
0648 void TestGdbmi::parseResponse2()
0649 {
0650     gdbmi::GdbmiParser parser;
0651 
0652     qRegisterMetaType<gdbmi::StreamOutput>("StreamOutput");
0653     qRegisterMetaType<gdbmi::Record>("Record");
0654     QSignalSpy outputSpy(&parser, &gdbmi::GdbmiParser::outputProduced);
0655     QVERIFY(outputSpy.isValid());
0656     QSignalSpy recSpy(&parser, &gdbmi::GdbmiParser::recordProduced);
0657     QVERIFY(recSpy.isValid());
0658 
0659     const QString text = QStringLiteral(R"--(
0660 ~"Starting program: /home/nobody/pruebas/build-cpp1/cpp1 \n"
0661 =thread-group-started,id="i1",pid="5271"
0662 =thread-created,id="1",group-id="i1"
0663 =library-loaded,id="/lib64/ld-linux-x86-64.so.2",target-name="/lib64/ld-linux-x86-64.so.2",host-name="/lib64/ld-linux-x86-64.so.2",symbols-loaded="0",thread-group="i1",ranges=[{from="0x00007ffff7fc5090",to="0x00007ffff7fee335"}]
0664 ^running
0665 *running,thread-id="all"
0666 (gdb)
0667 )--");
0668 
0669     parser.parseResponse(text.toLocal8Bit());
0670 
0671     QCOMPARE(outputSpy.size(), 1);
0672     QCOMPARE(recSpy.size(), 6);
0673 
0674     QCOMPARE(QStringLiteral("Starting program: /home/nobody/pruebas/build-cpp1/cpp1 \n"), qvariant_cast<gdbmi::StreamOutput>(outputSpy[0][0]).message);
0675 
0676     QCOMPARE(QStringLiteral("thread-group-started"), qvariant_cast<gdbmi::Record>(recSpy[0][0]).resultClass);
0677     QCOMPARE(QStringLiteral("thread-created"), qvariant_cast<gdbmi::Record>(recSpy[1][0]).resultClass);
0678     QCOMPARE(QStringLiteral("library-loaded"), qvariant_cast<gdbmi::Record>(recSpy[2][0]).resultClass);
0679     QCOMPARE(QStringLiteral("running"), qvariant_cast<gdbmi::Record>(recSpy[3][0]).resultClass);
0680     QCOMPARE(gdbmi::Record::Result, qvariant_cast<gdbmi::Record>(recSpy[3][0]).category);
0681     QCOMPARE(QStringLiteral("running"), qvariant_cast<gdbmi::Record>(recSpy[4][0]).resultClass);
0682     QCOMPARE(gdbmi::Record::Exec, qvariant_cast<gdbmi::Record>(recSpy[4][0]).category);
0683     QCOMPARE(gdbmi::Record::Prompt, qvariant_cast<gdbmi::Record>(recSpy[5][0]).category);
0684 }
0685 
0686 void TestGdbmi::parseResponse3()
0687 {
0688     gdbmi::GdbmiParser parser;
0689 
0690     qRegisterMetaType<gdbmi::StreamOutput>("StreamOutput");
0691     qRegisterMetaType<gdbmi::Record>("Record");
0692     QSignalSpy outputSpy(&parser, &gdbmi::GdbmiParser::outputProduced);
0693     QVERIFY(outputSpy.isValid());
0694     QSignalSpy recSpy(&parser, &gdbmi::GdbmiParser::recordProduced);
0695     QVERIFY(recSpy.isValid());
0696     QSignalSpy errorSpy(&parser, &gdbmi::GdbmiParser::parserError);
0697     QVERIFY(errorSpy.isValid());
0698 
0699     const QString text = QStringLiteral(R"--(
0700 =breakpoint-modified,bkpt={number="1",type="breakpoint",disp="del",enabled="y",addr="0x00005555555551f5",func="main()",file="/home/hector/pruebas/cpp1/main.cpp",fullname="/home/hector/pruebas/cpp1/main.cpp",line="7",thread-groups=["i1"],times="1",original-location="main"}
0701 ~"\n"
0702 ~"Temporary breakpoint 1, main () at /home/hector/pruebas/cpp1/main.cpp:7\n"
0703 ~"7\t{\n"
0704 *stopped,reason="breakpoint-hit",disp="del",bkptno="1",frame={addr="0x00005555555551f5",func="main",args=[],file="/home/hector/pruebas/cpp1/main.cpp",fullname="/home/hector/pruebas/cpp1/main.cpp",line="7",arch="i386:x86-64"},thread-id="1",stopped-threads="all",core="1"
0705 =breakpoint-deleted,id="1"
0706 (gdb)
0707 )--");
0708 
0709     parser.parseResponse(text.toLocal8Bit());
0710 
0711     QCOMPARE(errorSpy.size(), 0);
0712     QCOMPARE(outputSpy.size(), 3);
0713 
0714     QCOMPARE(QStringLiteral("\n"), qvariant_cast<gdbmi::StreamOutput>(outputSpy[0][0]).message);
0715     QCOMPARE(QStringLiteral("Temporary breakpoint 1, main () at /home/hector/pruebas/cpp1/main.cpp:7\n"),
0716              qvariant_cast<gdbmi::StreamOutput>(outputSpy[1][0]).message);
0717     QCOMPARE(QStringLiteral("7\t{\n"), qvariant_cast<gdbmi::StreamOutput>(outputSpy[2][0]).message);
0718 
0719     QCOMPARE(recSpy.size(), 4);
0720 
0721     QCOMPARE(QStringLiteral("breakpoint-modified"), qvariant_cast<gdbmi::Record>(recSpy[0][0]).resultClass);
0722     QCOMPARE(QStringLiteral("stopped"), qvariant_cast<gdbmi::Record>(recSpy[1][0]).resultClass);
0723     QCOMPARE(QStringLiteral("breakpoint-deleted"), qvariant_cast<gdbmi::Record>(recSpy[2][0]).resultClass);
0724     QCOMPARE(gdbmi::Record::Prompt, qvariant_cast<gdbmi::Record>(recSpy[3][0]).category);
0725 }
0726 
0727 void TestGdbmi::compare(const QJsonValue &ref, const QJsonValue &result)
0728 {
0729     if (ref.isNull()) {
0730         QVERIFY(result.isNull());
0731     } else if (ref.isUndefined()) {
0732         QVERIFY(result.isUndefined());
0733     } else if (ref.isString()) {
0734         QVERIFY(result.isString());
0735         QCOMPARE(ref.toString(), result.toString());
0736     } else if (ref.isBool()) {
0737         QVERIFY(result.isBool());
0738         QCOMPARE(ref.toBool(), result.toBool());
0739     } else if (ref.isDouble()) {
0740         QVERIFY(result.isDouble());
0741         QCOMPARE(ref.toDouble(), result.toDouble());
0742     } else if (ref.isArray()) {
0743         QVERIFY(result.isArray());
0744         compare(ref.toArray(), result.toArray());
0745     } else if (ref.isObject()) {
0746         QVERIFY(result.isObject());
0747         compare(ref.toObject(), result.toObject());
0748     }
0749 }
0750 
0751 void TestGdbmi::compare(const QJsonArray &ref, const QJsonArray &result)
0752 {
0753     QCOMPARE(ref.size(), result.size());
0754 
0755     for (int idx = 0; idx < ref.size(); ++idx) {
0756         compare(ref.at(idx), result.at(idx));
0757     }
0758 }
0759 
0760 void TestGdbmi::compare(const QJsonObject &ref, const QJsonObject &result)
0761 {
0762     QCOMPARE(ref.size(), result.size());
0763 
0764     for (auto it = ref.constBegin(); it != ref.constEnd(); ++it) {
0765         compare(it.value(), result[it.key()]);
0766     }
0767 }
0768 
0769 #include "moc_test_gdbmi.cpp"