• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pcolby / doxlee / 9348706144

03 Jun 2024 10:40AM UTC coverage: 83.602% (+3.9%) from 79.689%
9348706144

push

github

pcolby
Use Doxygen's "prot" field names verbatim

3 of 3 new or added lines in 1 file covered. (100.0%)

44 existing lines in 1 file now uncovered.

933 of 1116 relevant lines covered (83.6%)

4008.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.58
/src/doxml.cpp
1
// SPDX-FileCopyrightText: 2021-2024 Paul Colby <git@colby.id.au>
2
// SPDX-License-Identifier: LGPL-3.0-or-later
3

4
#include "doxml.h"
5

6
#include "variant.h"
7

8
#include <QCoreApplication>
9
#include <QDebug>
10
#include <QJsonDocument> /// \todo Remove; only for early dev / debugging.
11

12
/// Shorten the QStringLiteral macro for readability.
13
#define QSL(str) QStringLiteral(str)
14

15
/// Shorten QCoreApplication::translate calls for readability.
16
#define QTR(str) QCoreApplication::translate("doxml", str)
17

18
namespace doxlee {
19

20
namespace doxml {
21

22
static Q_LOGGING_CATEGORY(lc, "doxlee.doxml", QtInfoMsg);
1,056✔
23

24
QPair<QStringList,QStringList> kinds(const QDir &doxmlDir)
72✔
25
{
26
    return kinds(doxmlDir.absoluteFilePath(QSL("index.xsd")));
126✔
27
}
28

29
QPair<QStringList,QStringList> kinds(const QString &indexXsdPath)
72✔
30
{
31
    // Open the file for reading.
32
    QFile file(indexXsdPath);
72✔
33
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
72✔
34
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(indexXsdPath);
×
35
        return { };
36
    }
37

38
    QStringList compoundKinds, memberKinds;
54✔
39

40
    // Parse the opening <schema> element.
41
    QXmlStreamReader xml(&file);
72✔
42
    if (!xml.readNextStartElement()) {
72✔
43
        qCWarning(lc).noquote() << QTR("Invalid XML file: %1 - %2").arg(indexXsdPath, xml.errorString());
×
44
        return { };
45
    }
46
    if (xml.name() != QSL("schema")) {
90✔
47
        qCWarning(lc).noquote() << QTR("File is not a Doxygen XML index schema: %1 - %2")
×
48
            .arg(indexXsdPath, xml.name().toString());
×
49
        return { };
50
    }
51

52
    // Parse the contained 'kind' elements.
53
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
576✔
54
        if (xml.name() == QSL("simpleType")) {
1,008✔
55
            const QString nameAttribute = xml.attributes().value(QSL("name")).toString();
252✔
56
            if ((nameAttribute == QSL("CompoundKind")) || (nameAttribute == QSL("MemberKind"))) {
252✔
57
                while ((!xml.atEnd()) && (xml.readNextStartElement())) {
288✔
58
                    if (xml.name() == QSL("restriction")) {
288✔
59
                        QStringList &kindsList = (nameAttribute == QLatin1String("CompoundKind"))
252✔
60
                            ? compoundKinds : memberKinds;
144✔
61
                        while ((!xml.atEnd()) && (xml.readNextStartElement())) {
2,184✔
62
                            if (xml.name() == QSL("enumeration")) {
4,080✔
63
                                kindsList.append(xml.attributes().value(QSL("value")).toString());
3,570✔
64
                            }
65
                            xml.skipCurrentElement();
2,040✔
66
                        }
67
                    } else xml.skipCurrentElement();
×
68
                }
69
            } else xml.skipCurrentElement();
×
70
        } else xml.skipCurrentElement();
396✔
71
    }
72
    qCInfo(lc).noquote() << QTR("Parsed %1 compound kind(s), and %2 member kind(s) from %3")
180✔
73
        .arg(compoundKinds.size()).arg(memberKinds.size()).arg(indexXsdPath);
126✔
74
    return { compoundKinds, memberKinds };
72✔
75
}
144✔
76

77
QVariantMap parseIndex(const QDir &doxmlDir, const bool extraIndexes)
72✔
78
{
79
    return parseIndex(doxmlDir.absoluteFilePath(QSL("index.xml")), extraIndexes);
126✔
80
}
81

82
QVariantMap parseIndex(const QString &indexXmlPath, const bool extraIndexes)
72✔
83
{
84
    // Open the file for reading.
85
    QFile file(indexXmlPath);
72✔
86
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
72✔
87
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(indexXmlPath);
×
88
        return QVariantMap();
89
    }
90

91
    // Parse the opening <doxygenindex> element.
92
    QXmlStreamReader xml(&file);
72✔
93
    if (!xml.readNextStartElement()) {
72✔
94
        qCWarning(lc).noquote() << QTR("Invalid XML file: %1 - %2").arg(indexXmlPath, xml.errorString());
×
95
        return QVariantMap();
96
    }
97
    qCDebug(lc) << xml.name() << "version" << xml.attributes().value(QSL("version"))
90✔
98
             << xml.attributes().value(QSL("xml:lang")).toString();
×
99
    if (xml.name() != QSL("doxygenindex")) {
90✔
100
        qCWarning(lc).noquote() << QTR("File is not a Doxygen XML index: %1 - %2")
×
101
                                .arg(indexXmlPath, xml.name().toString());
×
102
        return QVariantMap();
103
    }
104
    QVariantMap indexMap;
54✔
105
    indexMap.insert(QSL("doxygenVersion"), xml.attributes().value(QSL("version")).toString());
180✔
106
    indexMap.insert(QSL("doxygenLanguage"), xml.attributes().value(QSL("xml:lang")).toString());
180✔
107

108
    // Parse the contained <compound> elements.
109
    QVariantList compounds;
54✔
110
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
2,160✔
111
        if (xml.name() == QSL("compound")) {
4,176✔
112
            QVariantMap compound;
1,566✔
113
            compound.insert(QSL("refid"), xml.attributes().value(QSL("refid")).toString());
5,220✔
114
            compound.insert(QSL("kind"), xml.attributes().value(QSL("kind")).toString());
5,220✔
115
            if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
4,176✔
116
                qCWarning(lc).noquote() << QTR(" %1:%2:%3 <compound> does not begin with <name>")
×
117
                    .arg(indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
118
                return QVariantMap();
119
            }
120
            compound.insert(QSL("name"), xml.readElementText());
3,654✔
121
            //qCDebug(lc) << __func__ << "compound" << compound;
122
            QVariantList members;
1,566✔
123
            while ((!xml.atEnd()) && (xml.readNextStartElement())) {
5,328✔
124
                if (xml.name() == QSL("member")) {
6,480✔
125
                    QVariantMap member;
2,430✔
126
                    member.insert(QSL("refid"), xml.attributes().value(QSL("refid")).toString());
8,100✔
127
                    member.insert(QSL("kind"), xml.attributes().value(QSL("kind")).toString());
8,100✔
128
                    if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
6,480✔
129
                        qCWarning(lc).noquote() << QTR("%1:%2:%3 <member> does not begin with <name>")
×
130
                            .arg(indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
131
                        return QVariantMap();
132
                    }
133
                    member.insert(QSL("name"), xml.readElementText());
5,670✔
134
                    //qCDebug(lc) << __func__ << "member" << member;
135
                    members.append(member);
5,670✔
136
                    xml.skipCurrentElement();
3,240✔
137
                } else {
810✔
138
                    qCWarning(lc).noquote() << QTR("Skipping unknown <%1> element at %2:%3:%4")
×
139
                        .arg(xml.name().toString(), indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
140
                    xml.skipCurrentElement();
×
141
                }
142
            }
143
            compound.insert(QSL("members"), members);
3,654✔
144
            compounds.append(compound);
3,654✔
145
        } else {
522✔
146
            qCWarning(lc).noquote() << QTR("Skipping unknown <%1> element at %2:%3:%4")
×
147
                .arg(xml.name().toString(), indexXmlPath).arg(xml.lineNumber()).arg(xml.columnNumber());
×
148
            xml.skipCurrentElement();
×
149
        }
150
    }
151
    qCInfo(lc).noquote() << QTR("Parsed %1 compound(s) from %2").arg(compounds.size()).arg(indexXmlPath);
180✔
152
    indexMap.insert(QSL("compoundsList"), compounds);
126✔
153
    if (extraIndexes) {
72✔
154
        #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
155
        indexMap.insert(doxml::extraIndexes(compounds));
126✔
156
        #else
157
        const QVariantMap extra = doxml::extraIndexes(compounds);
158
        for (auto iter = extra.constBegin(); iter != extra.constEnd(); ++iter) {
159
            indexMap.insert(iter.key(), iter.value());
160
        }
161
        #endif
162
    }
163
    return indexMap;
164
}
72✔
165

166
QVariantMap extraIndexes(const QVariantList &compounds)
72✔
167
{
168
    QHash<QString,QVariantList> compoundsByKind, membersByKind;
54✔
169
    QVariantMap compoundsByRefId, membersByRefId;
54✔
170
    for (const QVariant &compound: compounds) {
2,160✔
171
        const QVariantMap compoundMap = compound.toMap();
2,088✔
172
        {
173
            const QString kind = compoundMap.value(QSL("kind")).toString();
4,176✔
174
            const QString refid = compoundMap.value(QSL("refid")).toString();
4,176✔
175
            compoundsByKind[kind].append(compound);
2,088✔
176
            compoundsByRefId.insert(refid, compound);
2,088✔
177
        }
522✔
178

179
        const QVariantList members = compoundMap.value(QSL("members")).toList();
4,176✔
180
        for (const QVariant &member: members) {
5,328✔
181
            const QVariantMap memberMap = member.toMap();
3,240✔
182
            const QString kind = memberMap.value(QSL("kind")).toString();
6,480✔
183
            const QString refid = memberMap.value(QSL("refid")).toString();
6,480✔
184
            membersByKind[kind].append(member);
3,240✔
185
            membersByRefId.insert(refid, member);
3,240✔
186
        }
810✔
187
    }
522✔
188
    for (QVariantList &compoundsList: compoundsByKind) sortBy(compoundsList, QSL("name"));
888✔
189
    for (QVariantList &membersList: membersByKind)     sortBy(membersList,   QSL("name"));
720✔
190
    return QVariantMap{
191
        { QSL("compoundsByKind" ), toVariant(compoundsByKind) },
144✔
192
        { QSL("compoundsByRefId"), compoundsByRefId           },
72✔
193
        { QSL("membersByKind"   ), toVariant(membersByKind)   },
144✔
194
        { QSL("membersByRefId"  ), membersByRefId             },
72✔
195
    };
558✔
196
}
72✔
197

198
QVariantMap parseCompound(const QDir &doxmlDir, const QString &refId)
384✔
199
{
200
    return parseCompound(doxmlDir.absoluteFilePath(QSL("%1.xml").arg(refId)));
672✔
201
}
202

203
QVariantMap parseCompound(const QString &compoundXmlPath)
384✔
204
{
205
    // Open the compound XML file.
206
    QFile file(compoundXmlPath);
384✔
207
    if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) {
384✔
208
        qCWarning(lc).noquote() << QTR("Error opening file for reading: %1").arg(compoundXmlPath);
×
209
        return QVariantMap();
210
    }
211

212
    // Parse, and fetch the compoundDef element.
213
    QXmlStreamReader xml(&file);
384✔
214
    const QVariantMap compoundDef = toVariant(xml).value(QSL("doxygen")).toMap().value(QSL("compounddef")).toMap();
768✔
215
    if (compoundDef.isEmpty()) {
384✔
216
        qCWarning(lc).noquote() << QTR("Error reading compond defintion: %1").arg(compoundXmlPath);
×
217
        return QVariantMap();
218
    }
219
    qCDebug(lc).noquote() << QJsonDocument::fromVariant(compoundDef).toJson();
480✔
220

221
    // Map the compoundDef properties to our own compound map.
222
    QVariantMap compound{
223
        { QSL("name"), compoundDef.value(QSL("compoundname")).toMap().value(QSL(".Characters")) },
768✔
224
        /// \todo Provide stand way of fetch, and trimming, all text from each of these...
225
        // { QSL("brief"),       compoundDef.value(QSL("briefdescription")).toMap().value(QSL(".Characters")) },
226
        // { QSL("description"), compoundDef.value(QSL("fulldescription" )).toMap().value(QSL(".Characters")) },
227
    };
1,152✔
228

229
    // Copy call compoundDef attributes to top-level compound properties.
230
    for (const QVariant &attribute: compoundDef) {
5,760✔
231
        const auto map = attribute.toMap();
5,376✔
232
        const auto name = map.find(QSL("QualifiedName"));
5,376✔
233
        const auto value = map.find(QSL("Value"));
5,376✔
234
        if ((name != map.constEnd()) && (value != map.constEnd())) {
5,376✔
235
            compound.insert(name->toString(), *value);
2,814✔
236
        }
237
    }
1,344✔
238

239
    /// \todo More parsing here...
240

241
    qCDebug(lc).noquote() << QJsonDocument::fromVariant(compound).toJson();
480✔
242
    return compound;
243
}
384✔
244

245
} // namespace doxml
246

247
Doxml::Doxml(const QString &doxmlDir) : doxmlDir(doxmlDir)
2,216✔
248
{
249

250
}
2,216✔
251

252
// bool Doxml::isValid() const
253
// {
254
//     return false; /// \todo.
255
// }
256

257
QString Doxml::location(const QXmlStreamReader &xml) const
72✔
258
{
259
    return (currentXmlFilePath.isNull()) ? QStringLiteral("%1:%2").arg(xml.lineNumber()).arg(xml.columnNumber())
156✔
260
        : QStringLiteral("%1:%2:%3").arg(currentXmlFilePath).arg(xml.lineNumber()).arg(xml.columnNumber());
246✔
261
}
262

263
void Doxml::logError(const QXmlStreamReader &xml) const
8✔
264
{
265
    Q_ASSERT(xml.hasError());
266
    qCCritical(lc).noquote().nospace() << xml.errorString() << " [" << location(xml) << ']';
18✔
267
}
8✔
268

269
void Doxml::logWarning(const QString &message, const QXmlStreamReader &xml) const
16✔
270
{
271
    Q_ASSERT(!xml.hasError());
272
    qCWarning(lc).noquote().nospace() << message << " [" << location(xml) << ']';
36✔
273
}
16✔
274

275
void Doxml::logWarning(const Warning &warning, const QXmlStreamReader &xml) const
8✔
276
{
277
    QString message;
6✔
278
    switch (warning) {
8✔
279
    case Warning::UnexpectedElement:
8✔
280
        message = QTR("Ignoring unexpected element: %1").arg(xml.name());
16✔
281
        break;
8✔
282
    default:
×
283
        Q_ASSERT_X(false, "logWarning", "Unknown warning");
284
        message = QTR("Unknown warning");
×
285
        break;
×
286
    }
287
    logWarning(message, xml);
8✔
288
}
8✔
289

290
/*!
291
 * Returns an XML Numeric Character Reference as a QString.
292
 *
293
 * QChar and QString use UCS2 and UTF-16, but XML references can represent UCS4 characters. Indeed, for Doxygen XML,
294
 * the numeric characer references are emoji characters, which all two big for UCS2. So, the resulting Unicode character
295
 * cannot fit in a QChar. Instead we return a QString, with a UTF-16 encoding, so QString::size() will typically be 2,
296
 * even though it's constains only a single Unicode character (two 16-bit values in UTF-16 format).
297
 *
298
 * \see https://www.w3.org/TR/xml/#dt-charref
299
 */
300
QString Doxml::parseNumericCharacterReference(const QStringView &view)
16✔
301
{
302
    if (!view.startsWith(QSL("&#"))) {
16✔
303
        qWarning(lc) << QTR("Numeric character reference does not start with \"&#\": %1").arg(view);
×
304
        return QString();
305
    }
306
    if (!view.endsWith(QLatin1Char(';'))) {
307
        qWarning(lc) << QTR("Numeric character reference does not end with ';': %1").arg(view);
×
308
        return QString();
309
    }
310
    const int base = view.startsWith(QSL("&#x")) ? 16 : 10;
28✔
311
    const qsizetype pos = (base == 16) ? 3 : 2;
16✔
312
    const QStringView numberView = view.mid(pos, (view.size()-pos-1));
16✔
313

314
    bool ok;
315
    const char32_t codePoint=numberView.toString().toULong(&ok, base);
16✔
316
    if (!ok) {
16✔
317
        qWarning(lc) << QTR("Failed to convert numeric character reference to integer: %1").arg(numberView);
×
318
        return QString();
319
    }
320

321
    const QString result = QString::fromUcs4(&codePoint, 1);
12✔
322
    if (result.isNull()) {
16✔
323
        qWarning(lc) << QTR("Failed to parse Unicode codepoint: %1 (%2)").arg(codePoint).arg(view);
×
324
    }
325
    return result;
326
}
4✔
327

328

329
QVariantMap Doxml::parseCompound(QXmlStreamReader &xml) const
1,376✔
330
{
331
    if (!xml.readNextStartElement()) {
1,376✔
332
        Q_ASSERT(xml.hasError());
333
        return { };
334
    }
335
    if (xml.name() != QSL("doxygen")) {
1,720✔
336
        xml.raiseError(QTR("Root element is not \"doxygen\""));
×
337
        return { };
338
    }
339
    return parseCompound_DoxygenType(xml);
1,376✔
340
}
341

342
QVariantMap Doxml::parseCompound_DoxygenType(QXmlStreamReader &xml) const
1,384✔
343
{
344
    Q_ASSERT(xml.name() == QSL("doxygen"));
345

346
    QVariantMap map {
347
        { QSL("version"), xml.attributes().value(QSL("version")).toString() },
2,768✔
348
        { QSL("language"), xml.attributes().value(QSL("xml:lang")).toString() },
2,768✔
349
    };
6,574✔
350

351
    QVariantList compounds;
1,038✔
352
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
2,768✔
353
        if (xml.name() == QSL("compounddef")) {
2,768✔
354
            compounds.append(parseCompound_compounddefType(xml));
2,422✔
355
        } else {
356
            logWarning(Warning::UnexpectedElement, xml);
×
357
            xml.skipCurrentElement();
×
358
        }
359
    }
360
    qCDebug(lc).noquote() << QTR("Parsed %1 compounds(s) from %2").arg(compounds.size()).arg(currentXmlFilePath);
1,730✔
361
    map.insert(QSL("compounds"), compounds);
2,422✔
362
    return map;
1,384✔
363
}
346✔
364

365
QVariantMap Doxml::parseCompound_compounddefType(QXmlStreamReader &xml) const
1,392✔
366
{
367
    Q_ASSERT(xml.name() == QSL("compounddef"));
368

369
    const QXmlStreamAttributes attributes = xml.attributes();
1,392✔
370
    QVariantMap map {
371
        { QSL("id"), attributes.value(QSL("id")).toString() },
2,784✔
372
        { QSL("kind"), attributes.value(QSL("kind")).toString() },
2,784✔
373
        { QSL("prot"), attributes.value(QSL("prot")).toString() },
2,784✔
374
    };
9,048✔
375
    {
376
        const QStringView attributeValue = attributes.value(QSL("language"));
1,740✔
377
        if (!attributeValue.isNull()) {
1,392✔
378
            map.insert(QSL("language"), attributeValue.toString());
1,610✔
379
        }
380
    }
381
    for (const QString &attributeName: QStringList{ QSL("final"), QSL("inline"), QSL("sealed"), QSL("abstract") }) {
13,920✔
382
        const QStringView attributeValue = attributes.value(attributeName);
5,568✔
383
        if (!attributeValue.isNull()) {
5,568✔
384
            map.insert(attributeName, attributeValue == QSL("yes"));
24✔
385
        }
386
    }
387

388
    QVariantList basecompoundref, derivedcompoundref, includes, includedby, innermodule, innerdir, innerfile,
1,044✔
389
        innerclass, innerconcept, innernamespace, innerpage, innergroup, qualifier, sectiondef;
1,044✔
390
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
9,648✔
391
        if (xml.name() == QSL("compoundname")) {
16,512✔
392
            map.insert(QSL("compoundname"), xml.readElementText());
2,408✔
393
        }
394

395
        else if (xml.name() == QSL("title")) {
13,760✔
396
            map.insert(QSL("title"), xml.readElementText());
784✔
397
        }
398

399
        else if (xml.name() == QSL("basecompoundref")) {
12,864✔
400
            basecompoundref.append(parseCompound_compoundRefType(xml));
168✔
401
        }
402

403
        else if (xml.name() == QSL("derivedcompoundref")) {
12,672✔
404
            derivedcompoundref.append(parseCompound_compoundRefType(xml));
154✔
405
        }
406

407
        else if (xml.name() == QSL("includes")) {
12,496✔
408
            includes.append(parseCompound_incType(xml));
112✔
409
        }
410

411
        else if (xml.name() == QSL("includedby")) {
12,368✔
412
            includedby.append(parseCompound_incType(xml));
×
413
        }
414

415
        else if (xml.name() == QSL("incdepgraph")) {
12,368✔
416
            map.insert(QSL("incdepgraph"), parseCompound_graphType(xml));
28✔
417
        }
418

419
        else if (xml.name() == QSL("invincdepgraph")) {
12,336✔
420
            map.insert(QSL("invincdepgraph"), parseCompound_graphType(xml));
×
421
        }
422

423
        else if (xml.name() == QSL("innermodule")) {
12,336✔
424
            innermodule.append(parseCompound_refType(xml));
×
425
        }
426

427
        else if (xml.name() == QSL("innerdir")) {
12,336✔
428
            innerdir.append(parseCompound_refType(xml));
×
429
        }
430

431
        else if (xml.name() == QSL("innerfile")) {
12,336✔
432
            innerfile.append(parseCompound_refType(xml));
42✔
433
        }
434

435
        else if (xml.name() == QSL("innerclass")) {
12,288✔
436
            innerclass.append(parseCompound_refType(xml));
574✔
437
        }
438

439
        else if (xml.name() == QSL("innerconcept")) {
11,632✔
440
            innerconcept.append(parseCompound_refType(xml));
×
441
        }
442

443
        else if (xml.name() == QSL("innernamespace")) {
11,632✔
444
            innernamespace.append(parseCompound_refType(xml));
112✔
445
        }
446

447
        else if (xml.name() == QSL("innerpage")) {
11,504✔
448
            innerpage.append(parseCompound_refType(xml));
14✔
449
        }
450

451
        else if (xml.name() == QSL("innergroup")) {
11,488✔
452
            innergroup.append(parseCompound_refType(xml));
56✔
453
        }
454

455
        else if (xml.name() == QSL("qualifier")) {
11,424✔
456
            map.insert(QSL("qualifier"), xml.readElementText());
×
457
        }
458

459
        else if (xml.name() == QSL("templateparamlist")) {
11,424✔
460
            map.insert(QSL("templateparamlist"), parseCompound_templateparamlistType(xml));
28✔
461
        }
462

463
        else if (xml.name() == QSL("sectiondef")) {
11,392✔
464
            sectiondef.append(parseCompound_sectiondefType(xml));
1,610✔
465
        }
466

467
        else if (xml.name() == QSL("tableofcontents")) {
9,552✔
468
            map.insert(QSL("tableofcontents"), parseCompound_tableofcontentsType(xml));
28✔
469
        }
470

471
        else if (xml.name() == QSL("requiresclause")) {
9,520✔
472
            map.insert(QSL("requiresclause"), parseCompound_linkedTextType(xml));
×
473
        }
474

475
        else if (xml.name() == QSL("initializer")) {
9,520✔
476
            map.insert(QSL("initializer"), parseCompound_linkedTextType(xml));
×
477
        }
478

479
        else if (xml.name() == QSL("briefdescription")) {
9,520✔
480
            map.insert(QSL("briefdescription"), parseCompound_descriptionType(xml));
2,408✔
481
        }
482

483
        else if (xml.name() == QSL("detaileddescription")) {
6,768✔
484
            map.insert(QSL("detaileddescription"), parseCompound_descriptionType(xml));
2,408✔
485
        }
486

487
        else if (xml.name() == QSL("exports")) {
4,016✔
488
            map.insert(QSL("exports"), parseCompound_exportsType(xml));
×
489
        }
490

491
        else if (xml.name() == QSL("inheritancegraph")) {
4,016✔
492
            map.insert(QSL("inheritancegraph"), parseCompound_graphType(xml));
210✔
493
        }
494

495
        else if (xml.name() == QSL("collaborationgraph")) {
3,776✔
496
            map.insert(QSL("collaborationgraph"), parseCompound_graphType(xml));
168✔
497
        }
498

499
        else if (xml.name() == QSL("programlisting")) {
3,584✔
500
            map.insert(QSL("programlisting"), parseCompound_listingType(xml));
×
501
        }
502

503
        else if (xml.name() == QSL("location")) {
3,584✔
504
            map.insert(QSL("location"), parseCompound_locationType(xml));
2,254✔
505
        }
506

507
        else if (xml.name() == QSL("listofallmembers")) {
1,008✔
508
            map.insert(QSL("listofallmembers"), parseCompound_listofallmembersType(xml));
882✔
509
        }
510

511
        else {
512
            logWarning(Warning::UnexpectedElement, xml);
×
513
            xml.skipCurrentElement();
×
514
        }
515
    }
516

517
    #define DOXLEE_INSERT_IF_NOT_EMPTY(name) if (!name.isEmpty()) { map.insert(QSL(#name), name); }
518
    DOXLEE_INSERT_IF_NOT_EMPTY(basecompoundref)
1,464✔
519
    DOXLEE_INSERT_IF_NOT_EMPTY(derivedcompoundref)
1,428✔
520
    DOXLEE_INSERT_IF_NOT_EMPTY(includes)
1,440✔
521
    DOXLEE_INSERT_IF_NOT_EMPTY(includedby)
1,392✔
522
    DOXLEE_INSERT_IF_NOT_EMPTY(innermodule)
1,392✔
523
    DOXLEE_INSERT_IF_NOT_EMPTY(innerdir)
1,392✔
524
    DOXLEE_INSERT_IF_NOT_EMPTY(innerfile)
1,410✔
525
    DOXLEE_INSERT_IF_NOT_EMPTY(innerclass)
1,518✔
526
    DOXLEE_INSERT_IF_NOT_EMPTY(innerconcept)
1,392✔
527
    DOXLEE_INSERT_IF_NOT_EMPTY(innernamespace)
1,428✔
528
    DOXLEE_INSERT_IF_NOT_EMPTY(innerpage)
1,398✔
529
    DOXLEE_INSERT_IF_NOT_EMPTY(innergroup)
1,404✔
530
    DOXLEE_INSERT_IF_NOT_EMPTY(qualifier)
1,392✔
531
    DOXLEE_INSERT_IF_NOT_EMPTY(sectiondef)
1,914✔
532
    #undef DOXLEE_INSERT_IF_NOT_EMPTY
533
    return map;
1,392✔
534
}
348✔
535

536
QVariantList Doxml::parseCompound_listofallmembersType(QXmlStreamReader &xml) const
512✔
537
{
538
    Q_ASSERT(xml.name() == QSL("listofallmembers"));
539

540
    QVariantList list;
384✔
541
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
1,392✔
542
        if (xml.name() == QSL("member")) {
1,760✔
543
            list.append(parseCompound_memberRefType(xml));
1,540✔
544
        } else {
UNCOV
545
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
546
            xml.skipCurrentElement();
×
547
        }
548
    }
549
    return list;
512✔
550
}
551

552
QVariantMap Doxml::parseCompound_memberRefType(QXmlStreamReader &xml) const
888✔
553
{
554
    Q_ASSERT(xml.name() == QSL("member"));
555

556
    const QXmlStreamAttributes attributes = xml.attributes();
888✔
557
    QVariantMap map;
666✔
558
    for (const QString &attributeName: QStringList{ QSL("refid"), QSL("prot"), QSL("virt"), QSL("ambiguityscope") }) {
8,880✔
559
        const QStringView attributeValue = attributes.value(attributeName);
3,552✔
560
        if (!attributeValue.isNull()) {
3,552✔
561
            map.insert(attributeName, attributeValue.toString());
2,688✔
562
        }
563
    }
564

565
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
2,664✔
566
        if (xml.name() == QSL("scope")) {
3,552✔
567
            map.insert(QSL("scope"), xml.readElementText());
1,554✔
568
        }
569

570
        else if (xml.name() == QSL("name")) {
1,776✔
571
            map.insert(QSL("name"), xml.readElementText());
1,554✔
572
        }
573

574
        else {
UNCOV
575
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
576
            xml.skipCurrentElement();
×
577
        }
578
    }
579
    return map;
888✔
580
}
581

582
QVariantMap Doxml::parseCompound_docHtmlOnlyType(QXmlStreamReader &xml) const
16✔
583
{
584
    Q_ASSERT(xml.name() == QSL("htmlonly"));
585

586
    QVariantMap map;
12✔
587
    const QStringView block = xml.attributes().value(QSL("block"));
20✔
588
    if (!block.isNull()) {
16✔
589
        map.insert(QSL("block"), block.toString());
14✔
590
    }
591
    map.insert(QSL("text"), xml.readElementText());
28✔
592
    return map;
16✔
593
}
594

595
QVariantMap Doxml::parseCompound_compoundRefType(QXmlStreamReader &xml) const
200✔
596
{
597
    Q_ASSERT((xml.name() == QSL("basecompoundref")) || (xml.name() == QSL("derivedcompoundref")));
598

599
    const QXmlStreamAttributes attributes = xml.attributes();
200✔
600
    QVariantMap map;
150✔
601
    for (const QString &attributeName: QStringList{ QSL("refid"), QSL("prot"), QSL("virt") }) {
1,600✔
602
        const QStringView attributeValue = attributes.value(attributeName);
600✔
603
        if (!attributeValue.isNull()) {
600✔
604
            map.insert(attributeName, attributeValue.toString());
568✔
605
        }
606
    }
607
    map.insert(QSL("text"), xml.readElementText());
350✔
608
    return map;
200✔
609
}
610

611
QVariantMap Doxml::parseCompound_reimplementType(QXmlStreamReader &xml) const
16✔
612
{
613
    Q_ASSERT(xml.name() == QSL("reimplements"));
614

615
    QVariantMap map;
12✔
616
    const QStringView refid = xml.attributes().value(QSL("refid"));
20✔
617
    if (!refid.isNull()) {
16✔
618
        map.insert(QSL("refid"), refid.toString());
14✔
619
    }
620
    map.insert(QSL("text"), xml.readElementText());
28✔
621
    return map;
16✔
622
}
623

624
QVariantMap Doxml::parseCompound_incType(QXmlStreamReader &xml) const
80✔
625
{
626
    Q_ASSERT((xml.name() == QSL("includes")) || (xml.name() == QSL("includedby")));
627

628
    const QXmlStreamAttributes attributes = xml.attributes();
80✔
629
    QVariantMap map;
60✔
630
    const QStringView refid = attributes.value(QSL("refid"));
100✔
631
    if (!refid.isNull()) {
80✔
632
        map.insert(QSL("refid"), refid.toString());
98✔
633
    }
634
    const QStringView local = attributes.value(QSL("local"));
100✔
635
    if (!local.isNull()) {
80✔
636
        map.insert(QSL("local"), local.toString() == QSL("yes"));
126✔
637
    }
638
    map.insert(QSL("text"), xml.readElementText());
140✔
639
    return map;
80✔
640
}
641

642
QVariantList Doxml::parseCompound_exportsType(QXmlStreamReader &xml) const
8✔
643
{
644
    Q_ASSERT(xml.name() == QSL("exports"));
645

646
    QVariantList list;
6✔
647
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
24✔
648
        if (xml.name() == QSL("export")) {
32✔
649
            list.append(parseCompound_exportType(xml));
28✔
650
        } else {
UNCOV
651
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
652
            xml.skipCurrentElement();
×
653
        }
654
    }
655
    return list;
8✔
656
}
657

658
QVariantMap Doxml::parseCompound_exportType(QXmlStreamReader &xml) const
32✔
659
{
660
    Q_ASSERT(xml.name() == QSL("export"));
661

662
    QVariantMap map;
24✔
663
    const QStringView refid = xml.attributes().value(QSL("refid"));
40✔
664
    if (!refid.isNull()) {
32✔
665
        map.insert(QSL("refid"), refid.toString());
28✔
666
    }
667
    map.insert(QSL("text"), xml.readElementText());
56✔
668
    return map;
32✔
669
}
670

671
QVariantMap Doxml::parseCompound_refType(QXmlStreamReader &xml) const
472✔
672
{
673
    Q_ASSERT(xml.name().startsWith(QSL("inner")));
674

675
    const QXmlStreamAttributes attributes = xml.attributes();
472✔
676
    QVariantMap map;
354✔
677
    for (const QString &attributeName: QStringList{ QSL("refid"), QSL("prot") }) {
2,832✔
678
        const QStringView attributeValue = attributes.value(attributeName);
944✔
679
        if (!attributeValue.isNull()) {
944✔
680
            map.insert(attributeName, attributeValue.toString());
800✔
681
        }
682
    }
683
    const QStringView localView = attributes.value(QSL("inline"));
590✔
684
    if (!localView.isNull()) {
472✔
685
        map.insert(QSL("inline"), localView.toString() == QSL("yes"));
28✔
686
    }
687
    map.insert(QSL("text"), xml.readElementText());
826✔
688
    return map;
472✔
689
}
690

691
QVariantMap Doxml::parseCompound_refTextType(QXmlStreamReader &xml) const
16✔
692
{
693
    Q_ASSERT(xml.name() == QSL("ref"));
694

695
    const QXmlStreamAttributes attributes = xml.attributes();
16✔
696
    QVariantMap map;
12✔
697
    for (const QString &attributeName: QStringList{ QSL("refid"), QSL("kindref"), QSL("external"), QSL("tooltip") }) {
160✔
698
        const QStringView attributeValue = attributes.value(attributeName);
64✔
699
        if (!attributeValue.isNull()) {
64✔
700
            map.insert(attributeName, attributeValue.toString());
32✔
701
        }
702
    }
703
    map.insert(QSL("text"), xml.readElementText());
28✔
704
    return map;
16✔
705
}
706

707
QVariantMap Doxml::parseCompound_MemberType(QXmlStreamReader &xml) const
16✔
708
{
709
    Q_ASSERT(xml.name() == QSL("member"));
710

711
    const QXmlStreamAttributes attributes = xml.attributes();
16✔
712
    QVariantMap map;
12✔
713
    for (const QString &attributeName: QStringList{ QSL("refid"), QSL("kind") }) {
96✔
714
        const QStringView attributeValue = attributes.value(attributeName);
32✔
715
        if (!attributeValue.isNull()) {
32✔
716
            map.insert(attributeName, attributeValue.toString());
32✔
717
        }
718
    }
719

720
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
32✔
721
        if (xml.name() == QSL("name")) {
32✔
722
            map.insert(QSL("name"), xml.readElementText());
28✔
723
        } else {
724
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
725
            xml.skipCurrentElement();
×
726
        }
727
    }
728
    return map;
16✔
729
}
730

731
QVariantMap Doxml::parseCompound_sectiondefType(QXmlStreamReader &xml) const
928✔
732
{
733
    Q_ASSERT(xml.name() == QSL("sectiondef"));
734

735
    QVariantMap map;
696✔
736
    const QStringView kind = xml.attributes().value(QSL("kind"));
1,160✔
737
    if (!kind.isNull()) {
928✔
738
        map.insert(QSL("kind"), kind.toString());
1,624✔
739
    }
740

741
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
2,840✔
742
        if (xml.name() == QSL("header")) {
3,824✔
743
            map.insert(QSL("header"), xml.readElementText());
14✔
744
        }
745

746
        else if (xml.name() == QSL("description")) {
3,808✔
UNCOV
747
            map.insert(QSL("description"), parseCompound_descriptionType(xml));
×
748
        }
749

750
        else if (xml.name() == QSL("memberdef")) {
3,808✔
751
            map.insert(QSL("memberdef"), parseCompound_memberdefType(xml));
3,318✔
752
        }
753

754
        else if (xml.name() == QSL("member")) {
16✔
755
            map.insert(QSL("member"), parseCompound_MemberType(xml));
14✔
756
        }
757

758
        else {
UNCOV
759
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
760
            xml.skipCurrentElement();
×
761
        }
762
    }
763
    return map;
928✔
764
}
765

766
QVariantMap Doxml::parseCompound_memberdefType(QXmlStreamReader &xml) const
1,904✔
767
{
768
    Q_ASSERT(xml.name() == QSL("memberdef"));
769

770
    const QXmlStreamAttributes attributes = xml.attributes();
1,904✔
771
    QVariantMap map {
772
        { QSL("kind"), attributes.value(QSL("kind")).toString() },
3,808✔
773
        { QSL("id"), attributes.value(QSL("id")).toString() },
3,808✔
774
        { QSL("prot"), attributes.value(QSL("prot")).toString() },
3,808✔
775
        { QSL("static"), attributes.value(QSL("static")) == QSL("yes") },
4,284✔
776
    };
15,708✔
777
    for (const QString &attributeName: QStringList{ QSL("refqual"), QSL("virt"), QSL("accessor") }) {
15,232✔
778
        const QStringView attributeValue = attributes.value(attributeName);
5,712✔
779
        if (!attributeValue.isNull()) {
5,712✔
780
            map.insert(attributeName, attributeValue.toString());
1,352✔
781
        }
782
    }
783
    for (const QString &attributeName: QStringList{
784
        QSL("extern"), QSL("strong"), QSL("const"), QSL("explicit"), QSL("inline"), QSL("volatile"), QSL("mutable"),
1,428✔
785
        QSL("noexcept"), QSL("constexpr"), QSL("readable"), QSL("writable"), QSL("initonly"), QSL("settable"),
1,428✔
786
        QSL("privatesettable"), QSL("protectedsettable"), QSL("gettable"), QSL("privategettable"),
1,428✔
787
        QSL("protectedgettable"), QSL("final"), QSL("sealed"), QSL("new"), QSL("add"), QSL("remove"), QSL("raise"),
1,428✔
788
        QSL("optional"), QSL("required"), QSL("attribute"), QSL("property"), QSL("readonly"), QSL("bound"),
1,428✔
789
        QSL("removable"), QSL("constrained"), QSL("transient"), QSL("maybevoid"), QSL("maybedefault"),
1,428✔
790
        QSL("maybeambiguous"),
1,428✔
791
    }) {
140,896✔
792
        const QStringView attributeValue = attributes.value(attributeName);
68,544✔
793
        if (!attributeValue.isNull()) {
68,544✔
794
            map.insert(attributeName, attributeValue == QSL("yes"));
4,664✔
795
        }
796
    }
797

798
    QVariantList reimplements, reimplementedby, qualifier, param, enumvalue, references, referencedby;
1,428✔
799
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
19,768✔
800
        if (xml.name() == QSL("templateparamlist")) {
35,728✔
801
            map.insert(QSL("templateparamlist"), parseCompound_templateparamlistType(xml));
266✔
802
        }
803

804
        else if (xml.name() == QSL("type")) {
35,424✔
805
            map.insert(QSL("type"), parseCompound_linkedTextType(xml));
3,220✔
806
        }
807

808
        else if (xml.name() == QSL("definition")) {
31,744✔
809
            map.insert(QSL("definition"), xml.readElementText());
2,968✔
810
        }
811

812
        else if (xml.name() == QSL("argsstring")) {
28,352✔
813
            map.insert(QSL("argsstring"), xml.readElementText());
2,968✔
814
        }
815

816
        else if (xml.name() == QSL("name")) {
24,960✔
817
            map.insert(QSL("name"), xml.readElementText());
3,332✔
818
        }
819

820
        else if (xml.name() == QSL("qualifiedname")) {
21,152✔
821
            map.insert(QSL("qualifiedname"), xml.readElementText());
2,072✔
822
        }
823

824
        else if (xml.name() == QSL("read")) {
18,784✔
UNCOV
825
            map.insert(QSL("read"), xml.readElementText());
×
826
        }
827

828
        else if (xml.name() == QSL("write")) {
18,784✔
UNCOV
829
            map.insert(QSL("write"), xml.readElementText());
×
830
        }
831

832
        else if (xml.name() == QSL("bitfield")) {
18,784✔
UNCOV
833
            map.insert(QSL("bitfield"), xml.readElementText());
×
834
        }
835

836
        else if (xml.name() == QSL("reimplements")) {
18,784✔
UNCOV
837
            reimplements.append(parseCompound_reimplementType(xml));
×
838
        }
839

840
        else if (xml.name() == QSL("reimplementedby")) {
18,784✔
UNCOV
841
            reimplementedby.append(parseCompound_reimplementType(xml));
×
842
        }
843

844
        else if (xml.name() == QSL("qualifier")) {
18,784✔
UNCOV
845
            map.insert(QSL("qualifier"), xml.readElementText());
×
846
        }
847

848
        else if (xml.name() == QSL("param")) {
18,784✔
849
            param.append(parseCompound_paramType(xml));
2,632✔
850
        }
851

852
        else if (xml.name() == QSL("enumvalue")) {
15,776✔
853
            enumvalue.append(parseCompound_enumvalueType(xml));
406✔
854
        }
855

856
        else if (xml.name() == QSL("requiresclause")) {
15,312✔
UNCOV
857
            map.insert(QSL("requiresclause"), parseCompound_linkedTextType(xml));
×
858

859
        }
860

861
        else if (xml.name() == QSL("initializer")) {
15,312✔
862
            map.insert(QSL("initializer"), parseCompound_linkedTextType(xml));
168✔
863
        }
864

865
        else if (xml.name() == QSL("exceptions")) {
15,120✔
866
           map.insert(QSL("exceptions"), parseCompound_linkedTextType(xml));
14✔
867
        }
868

869
        else if (xml.name() == QSL("briefdescription")) {
15,104✔
870
          map.insert(QSL("briefdescription"), parseCompound_descriptionType(xml));
3,304✔
871
        }
872

873
        else if (xml.name() == QSL("detaileddescription")) {
11,328✔
874
          map.insert(QSL("detaileddescription"), parseCompound_descriptionType(xml));
3,304✔
875
        }
876

877
        else if (xml.name() == QSL("inbodydescription")) {
7,552✔
878
          map.insert(QSL("inbodydescription"), parseCompound_descriptionType(xml));
3,304✔
879
        }
880

881
        else if (xml.name() == QSL("location")) {
3,776✔
882
          map.insert(QSL("location"), parseCompound_locationType(xml));
3,304✔
883
        }
884

UNCOV
885
        else if (xml.name() == QSL("references")) {
×
UNCOV
886
            references.append(parseCompound_referenceType(xml));
×
887
        }
888

UNCOV
889
        else if (xml.name() == QSL("referencedby")) {
×
UNCOV
890
            referencedby.append(parseCompound_referenceType(xml));
×
891
        }
892

893
        else {
UNCOV
894
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
895
            xml.skipCurrentElement();
×
896
        }
897
    }
898

899
    #define DOXLEE_INSERT_IF_NOT_EMPTY(name) if (!name.isEmpty()) { map.insert(QSL(#name), name); }
900
    DOXLEE_INSERT_IF_NOT_EMPTY(reimplements)
1,904✔
901
    DOXLEE_INSERT_IF_NOT_EMPTY(reimplementedby)
1,904✔
902
    DOXLEE_INSERT_IF_NOT_EMPTY(qualifier)
1,904✔
903
    DOXLEE_INSERT_IF_NOT_EMPTY(param)
2,612✔
904
    DOXLEE_INSERT_IF_NOT_EMPTY(enumvalue)
2,000✔
905
    DOXLEE_INSERT_IF_NOT_EMPTY(references)
1,904✔
906
    DOXLEE_INSERT_IF_NOT_EMPTY(referencedby)
1,904✔
907
    #undef DOXLEE_INSERT_IF_NOT_EMPTY
908
    return map;
1,904✔
909
}
476✔
910

911
QVariantMap Doxml::parseCompound_descriptionType(QXmlStreamReader &xml) const
8,424✔
912
{
913
    /// \todo Implement Doxml::parseCompound_descriptionType().
914
    xml.skipCurrentElement();
8,424✔
915
    return {};
8,424✔
916
}
917

918
QVariantMap Doxml::parseCompound_enumvalueType(QXmlStreamReader &xml) const
240✔
919
{
920
    /// \todo Implement Doxml::parseCompound_enumvalueType().
921
    xml.skipCurrentElement();
240✔
922
    return {};
240✔
923
}
924

925
QVariantMap Doxml::parseCompound_templateparamlistType(QXmlStreamReader &xml) const
176✔
926
{
927
    /// \todo Implement Doxml::parseCompound_templateparamlistType().
928
    xml.skipCurrentElement();
176✔
929
    return {};
176✔
930
}
931

932
QVariantMap Doxml::parseCompound_paramType(QXmlStreamReader &xml) const
1,512✔
933
{
934
    /// \todo Implement Doxml::parseCompound_paramType().
935
    xml.skipCurrentElement();
1,512✔
936
    return {};
1,512✔
937
}
938

939
QVariantMap Doxml::parseCompound_linkedTextType(QXmlStreamReader &xml) const
1,952✔
940
{
941
    /// \todo Implement Doxml::parseCompound_linkedTextType().
942
    xml.skipCurrentElement();
1,952✔
943
    return {};
1,952✔
944
}
945

946
QVariantMap Doxml::parseCompound_graphType(QXmlStreamReader &xml) const
240✔
947
{
948
    /// \todo Implement Doxml::parseCompound_graphType().
949
    xml.skipCurrentElement();
240✔
950
    return {};
240✔
951
}
952

953
QVariantMap Doxml::parseCompound_nodeType(QXmlStreamReader &xml) const
8✔
954
{
955
    /// \todo Implement Doxml::parseCompound_nodeType().
956
    xml.skipCurrentElement();
8✔
957
    return {};
8✔
958
}
959

960
QVariantMap Doxml::parseCompound_childnodeType(QXmlStreamReader &xml) const
8✔
961
{
962
    /// \todo Implement Doxml::parseCompound_childnodeType().
963
    xml.skipCurrentElement();
8✔
964
    return {};
8✔
965
}
966

967
QVariantMap Doxml::parseCompound_linkType(QXmlStreamReader &xml) const
8✔
968
{
969
    /// \todo Implement Doxml::parseCompound_linkType().
970
    xml.skipCurrentElement();
8✔
971
    return {};
8✔
972
}
973

974
QVariantMap Doxml::parseCompound_listingType(QXmlStreamReader &xml) const
8✔
975
{
976
    /// \todo Implement Doxml::parseCompound_listingType().
977
    xml.skipCurrentElement();
8✔
978
    return {};
8✔
979
}
980

981
QVariantMap Doxml::parseCompound_codelineType(QXmlStreamReader &xml) const
8✔
982
{
983
    /// \todo Implement Doxml::parseCompound_codelineType().
984
    xml.skipCurrentElement();
8✔
985
    return {};
8✔
986
}
987

988
QVariantMap Doxml::parseCompound_highlightType(QXmlStreamReader &xml) const
8✔
989
{
990
    /// \todo Implement Doxml::parseCompound_highlightType().
991
    xml.skipCurrentElement();
8✔
992
    return {};
8✔
993
}
994

995
QVariantMap Doxml::parseCompound_spType(QXmlStreamReader &xml) const
8✔
996
{
997
    /// \todo Implement Doxml::parseCompound_spType().
998
    xml.skipCurrentElement();
8✔
999
    return {};
8✔
1000
}
1001

1002
QVariantMap Doxml::parseCompound_referenceType(QXmlStreamReader &xml) const
8✔
1003
{
1004
    /// \todo Implement Doxml::parseCompound_referenceType().
1005
    xml.skipCurrentElement();
8✔
1006
    return {};
8✔
1007
}
1008

1009
QVariantMap Doxml::parseCompound_locationType(QXmlStreamReader &xml) const
3,184✔
1010
{
1011
    /// \todo Implement Doxml::parseCompound_locationType().
1012
    xml.skipCurrentElement();
3,184✔
1013
    return {};
3,184✔
1014
}
1015

1016
QVariantMap Doxml::parseCompound_docSect1Type(QXmlStreamReader &xml) const
8✔
1017
{
1018
    /// \todo Implement Doxml::parseCompound_docSect1Type().
1019
    xml.skipCurrentElement();
8✔
1020
    return {};
8✔
1021
}
1022

1023
QVariantMap Doxml::parseCompound_docSect2Type(QXmlStreamReader &xml) const
8✔
1024
{
1025
    /// \todo Implement Doxml::parseCompound_docSect2Type().
1026
    xml.skipCurrentElement();
8✔
1027
    return {};
8✔
1028
}
1029

1030
QVariantMap Doxml::parseCompound_docSect3Type(QXmlStreamReader &xml) const
8✔
1031
{
1032
    /// \todo Implement Doxml::parseCompound_docSect3Type().
1033
    xml.skipCurrentElement();
8✔
1034
    return {};
8✔
1035
}
1036

1037
QVariantMap Doxml::parseCompound_docSect4Type(QXmlStreamReader &xml) const
8✔
1038
{
1039
    /// \todo Implement Doxml::parseCompound_docSect4Type().
1040
    xml.skipCurrentElement();
8✔
1041
    return {};
8✔
1042
}
1043

1044
QVariantMap Doxml::parseCompound_docInternalType(QXmlStreamReader &xml) const
8✔
1045
{
1046
    /// \todo Implement Doxml::parseCompound_docInternalType().
1047
    xml.skipCurrentElement();
8✔
1048
    return {};
8✔
1049
}
1050

1051
QVariantMap Doxml::parseCompound_docInternalS1Type(QXmlStreamReader &xml) const
8✔
1052
{
1053
    /// \todo Implement Doxml::parseCompound_docInternalS1Type().
1054
    xml.skipCurrentElement();
8✔
1055
    return {};
8✔
1056
}
1057

1058
QVariantMap Doxml::parseCompound_docInternalS2Type(QXmlStreamReader &xml) const
8✔
1059
{
1060
    /// \todo Implement Doxml::parseCompound_docInternalS2Type().
1061
    xml.skipCurrentElement();
8✔
1062
    return {};
8✔
1063
}
1064

1065
QVariantMap Doxml::parseCompound_docInternalS3Type(QXmlStreamReader &xml) const
8✔
1066
{
1067
    /// \todo Implement Doxml::parseCompound_docInternalS3Type().
1068
    xml.skipCurrentElement();
8✔
1069
    return {};
8✔
1070
}
1071

1072
QVariantMap Doxml::parseCompound_docInternalS4Type(QXmlStreamReader &xml) const
8✔
1073
{
1074
    /// \todo Implement Doxml::parseCompound_docInternalS4Type().
1075
    xml.skipCurrentElement();
8✔
1076
    return {};
8✔
1077
}
1078

1079
QVariantMap Doxml::parseCompound_docTitleType(QXmlStreamReader &xml) const
8✔
1080
{
1081
    /// \todo Implement Doxml::parseCompound_docTitleType().
1082
    xml.skipCurrentElement();
8✔
1083
    return {};
8✔
1084
}
1085

1086
QVariantMap Doxml::parseCompound_docSummaryType(QXmlStreamReader &xml) const
8✔
1087
{
1088
    /// \todo Implement Doxml::parseCompound_docSummaryType().
1089
    xml.skipCurrentElement();
8✔
1090
    return {};
8✔
1091
}
1092

1093
QVariantMap Doxml::parseCompound_docParaType(QXmlStreamReader &xml) const
8✔
1094
{
1095
    /// \todo Implement Doxml::parseCompound_docParaType().
1096
    xml.skipCurrentElement();
8✔
1097
    return {};
8✔
1098
}
1099

1100
QVariantMap Doxml::parseCompound_docMarkupType(QXmlStreamReader &xml) const
8✔
1101
{
1102
    /// \todo Implement Doxml::parseCompound_docMarkupType().
1103
    xml.skipCurrentElement();
8✔
1104
    return {};
8✔
1105
}
1106

1107
QVariantMap Doxml::parseCompound_docURLLink(QXmlStreamReader &xml) const
8✔
1108
{
1109
    /// \todo Implement Doxml::parseCompound_docURLLink().
1110
    xml.skipCurrentElement();
8✔
1111
    return {};
8✔
1112
}
1113

1114
QVariantMap Doxml::parseCompound_docAnchorType(QXmlStreamReader &xml) const
8✔
1115
{
1116
    /// \todo Implement Doxml::parseCompound_docAnchorType().
1117
    xml.skipCurrentElement();
8✔
1118
    return {};
8✔
1119
}
1120

1121
QVariantMap Doxml::parseCompound_docFormulaType(QXmlStreamReader &xml) const
8✔
1122
{
1123
    /// \todo Implement Doxml::parseCompound_docFormulaType().
1124
    xml.skipCurrentElement();
8✔
1125
    return {};
8✔
1126
}
1127

1128
QVariantMap Doxml::parseCompound_docIndexEntryType(QXmlStreamReader &xml) const
8✔
1129
{
1130
    /// \todo Implement Doxml::parseCompound_docIndexEntryType().
1131
    xml.skipCurrentElement();
8✔
1132
    return {};
8✔
1133
}
1134

1135
QVariantMap Doxml::parseCompound_docListType(QXmlStreamReader &xml) const
8✔
1136
{
1137
    /// \todo Implement Doxml::parseCompound_docListType().
1138
    xml.skipCurrentElement();
8✔
1139
    return {};
8✔
1140
}
1141

1142
QVariantMap Doxml::parseCompound_docListItemType(QXmlStreamReader &xml) const
8✔
1143
{
1144
    /// \todo Implement Doxml::parseCompound_docListItemType().
1145
    xml.skipCurrentElement();
8✔
1146
    return {};
8✔
1147
}
1148

1149
QVariantMap Doxml::parseCompound_docSimpleSectType(QXmlStreamReader &xml) const
8✔
1150
{
1151
    /// \todo Implement Doxml::parseCompound_docSimpleSectType().
1152
    xml.skipCurrentElement();
8✔
1153
    return {};
8✔
1154
}
1155

1156
QVariantMap Doxml::parseCompound_docVarListEntryType(QXmlStreamReader &xml) const
8✔
1157
{
1158
    /// \todo Implement Doxml::parseCompound_docVarListEntryType().
1159
    xml.skipCurrentElement();
8✔
1160
    return {};
8✔
1161
}
1162

1163
QVariantMap Doxml::parseCompound_docVariableListType(QXmlStreamReader &xml) const
8✔
1164
{
1165
    /// \todo Implement Doxml::parseCompound_docVariableListType().
1166
    xml.skipCurrentElement();
8✔
1167
    return {};
8✔
1168
}
1169

1170
QVariantMap Doxml::parseCompound_docRefTextType(QXmlStreamReader &xml) const
8✔
1171
{
1172
    /// \todo Implement Doxml::parseCompound_docRefTextType().
1173
    xml.skipCurrentElement();
8✔
1174
    return {};
8✔
1175
}
1176

1177
QVariantMap Doxml::parseCompound_docTableType(QXmlStreamReader &xml) const
8✔
1178
{
1179
    /// \todo Implement Doxml::parseCompound_docTableType().
1180
    xml.skipCurrentElement();
8✔
1181
    return {};
8✔
1182
}
1183

1184
QVariantMap Doxml::parseCompound_docRowType(QXmlStreamReader &xml) const
8✔
1185
{
1186
    /// \todo Implement Doxml::parseCompound_docRowType().
1187
    xml.skipCurrentElement();
8✔
1188
    return {};
8✔
1189
}
1190

1191
QVariantMap Doxml::parseCompound_docEntryType(QXmlStreamReader &xml) const
8✔
1192
{
1193
    /// \todo Implement Doxml::parseCompound_docEntryType().
1194
    xml.skipCurrentElement();
8✔
1195
    return {};
8✔
1196
}
1197

1198
QVariantMap Doxml::parseCompound_docCaptionType(QXmlStreamReader &xml) const
8✔
1199
{
1200
    /// \todo Implement Doxml::parseCompound_docCaptionType().
1201
    xml.skipCurrentElement();
8✔
1202
    return {};
8✔
1203
}
1204

1205
QVariantMap Doxml::parseCompound_docHeadingType(QXmlStreamReader &xml) const
8✔
1206
{
1207
    /// \todo Implement Doxml::parseCompound_docHeadingType().
1208
    xml.skipCurrentElement();
8✔
1209
    return {};
8✔
1210
}
1211

1212
QVariantMap Doxml::parseCompound_docImageType(QXmlStreamReader &xml) const
8✔
1213
{
1214
    /// \todo Implement Doxml::parseCompound_docImageType().
1215
    xml.skipCurrentElement();
8✔
1216
    return {};
8✔
1217
}
1218

1219
QVariantMap Doxml::parseCompound_docDotMscType(QXmlStreamReader &xml) const
8✔
1220
{
1221
    /// \todo Implement Doxml::parseCompound_docDotMscType().
1222
    xml.skipCurrentElement();
8✔
1223
    return {};
8✔
1224
}
1225

1226
QVariantMap Doxml::parseCompound_docImageFileType(QXmlStreamReader &xml) const
8✔
1227
{
1228
    /// \todo Implement Doxml::parseCompound_docImageFileType().
1229
    xml.skipCurrentElement();
8✔
1230
    return {};
8✔
1231
}
1232

1233
QVariantMap Doxml::parseCompound_docPlantumlType(QXmlStreamReader &xml) const
8✔
1234
{
1235
    /// \todo Implement Doxml::parseCompound_docPlantumlType().
1236
    xml.skipCurrentElement();
8✔
1237
    return {};
8✔
1238
}
1239

1240
QVariantMap Doxml::parseCompound_docTocItemType(QXmlStreamReader &xml) const
8✔
1241
{
1242
    /// \todo Implement Doxml::parseCompound_docTocItemType().
1243
    xml.skipCurrentElement();
8✔
1244
    return {};
8✔
1245
}
1246

1247
QVariantMap Doxml::parseCompound_docTocListType(QXmlStreamReader &xml) const
8✔
1248
{
1249
    /// \todo Implement Doxml::parseCompound_docTocListType().
1250
    xml.skipCurrentElement();
8✔
1251
    return {};
8✔
1252
}
1253

1254
QVariantMap Doxml::parseCompound_docLanguageType(QXmlStreamReader &xml) const
8✔
1255
{
1256
    /// \todo Implement Doxml::parseCompound_docLanguageType().
1257
    xml.skipCurrentElement();
8✔
1258
    return {};
8✔
1259
}
1260

1261
QVariantMap Doxml::parseCompound_docParamListType(QXmlStreamReader &xml) const
8✔
1262
{
1263
    /// \todo Implement Doxml::parseCompound_docParamListType().
1264
    xml.skipCurrentElement();
8✔
1265
    return {};
8✔
1266
}
1267

1268
QVariantMap Doxml::parseCompound_docParamListItem(QXmlStreamReader &xml) const
8✔
1269
{
1270
    /// \todo Implement Doxml::parseCompound_docParamListItem().
1271
    xml.skipCurrentElement();
8✔
1272
    return {};
8✔
1273
}
1274

1275
QVariantMap Doxml::parseCompound_docParamNameList(QXmlStreamReader &xml) const
8✔
1276
{
1277
    /// \todo Implement Doxml::parseCompound_docParamNameList().
1278
    xml.skipCurrentElement();
8✔
1279
    return {};
8✔
1280
}
1281

1282
QVariantMap Doxml::parseCompound_docParamType(QXmlStreamReader &xml) const
8✔
1283
{
1284
    /// \todo Implement Doxml::parseCompound_docParamType().
1285
    xml.skipCurrentElement();
8✔
1286
    return {};
8✔
1287
}
1288

1289
QVariantMap Doxml::parseCompound_docParamName(QXmlStreamReader &xml) const
8✔
1290
{
1291
    /// \todo Implement Doxml::parseCompound_docParamName().
1292
    xml.skipCurrentElement();
8✔
1293
    return {};
8✔
1294
}
1295

1296
QVariantMap Doxml::parseCompound_docXRefSectType(QXmlStreamReader &xml) const
8✔
1297
{
1298
    /// \todo Implement Doxml::parseCompound_docXRefSectType().
1299
    xml.skipCurrentElement();
8✔
1300
    return {};
8✔
1301
}
1302

1303
QVariantMap Doxml::parseCompound_docCopyType(QXmlStreamReader &xml) const
8✔
1304
{
1305
    /// \todo Implement Doxml::parseCompound_docCopyType().
1306
    xml.skipCurrentElement();
8✔
1307
    return {};
8✔
1308
}
1309

1310
QVariantMap Doxml::parseCompound_docDetailsType(QXmlStreamReader &xml) const
8✔
1311
{
1312
    /// \todo Implement Doxml::parseCompound_docDetailsType().
1313
    xml.skipCurrentElement();
8✔
1314
    return {};
8✔
1315
}
1316

1317
QVariantMap Doxml::parseCompound_docBlockQuoteType(QXmlStreamReader &xml) const
8✔
1318
{
1319
    /// \todo Implement Doxml::parseCompound_docBlockQuoteType().
1320
    xml.skipCurrentElement();
8✔
1321
    return {};
8✔
1322
}
1323

1324
QVariantMap Doxml::parseCompound_docParBlockType(QXmlStreamReader &xml) const
8✔
1325
{
1326
    /// \todo Implement Doxml::parseCompound_docParBlockType().
1327
    xml.skipCurrentElement();
8✔
1328
    return {};
8✔
1329
}
1330

1331
QVariantMap Doxml::parseCompound_docEmptyType(QXmlStreamReader &xml) const
8✔
1332
{
1333
    /// \todo Implement Doxml::parseCompound_docEmptyType().
1334
    xml.skipCurrentElement();
8✔
1335
    return {};
8✔
1336
}
1337

1338
QVariantList Doxml::parseCompound_tableofcontentsType(QXmlStreamReader &xml) const
104✔
1339
{
1340
    Q_ASSERT(xml.name() == QSL("tableofcontents"));
1341
    QVariantList sections;
78✔
1342
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
232✔
1343
        if (xml.name() == QSL("tocsect")) {
256✔
1344
            sections.append(parseCompound_tableofcontentsKindType(xml));
224✔
1345
        } else {
UNCOV
1346
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1347
            xml.skipCurrentElement();
×
1348
        }
1349
    }
1350
    return sections;
104✔
1351
}
1352

1353
QVariantMap Doxml::parseCompound_tableofcontentsKindType(QXmlStreamReader &xml) const
144✔
1354
{
1355
    Q_ASSERT(xml.name() == QSL("tocsect"));
1356

1357
    if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
288✔
UNCOV
1358
        xml.raiseError(QTR("<tocsect> does not begin with <name>"));
×
1359
        return { };
1360
    }
1361
    QVariantMap map { { QSL("name"), xml.readElementText() } };
468✔
1362

1363
    if ((!xml.readNextStartElement()) || (xml.name() != QSL("reference"))) {
288✔
UNCOV
1364
        xml.raiseError(QTR("<tocsect> does not contain <reference>"));
×
1365
        return { };
1366
    }
1367
    map.insert(QSL("reference"), xml.readElementText());
252✔
1368

1369
    QVariantList tableofcontentsList; // A list of lists. Not sure why Doxygen models it that way.
108✔
1370
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
224✔
1371
        if (xml.name() == QSL("tableofcontents")) {
160✔
1372
            tableofcontentsList.append(parseCompound_tableofcontentsType(xml));
160✔
1373
        } else {
UNCOV
1374
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1375
            xml.skipCurrentElement();
×
1376
        }
1377
    }
1378
    if (!tableofcontentsList.isEmpty()) {
144✔
1379
        map.insert(QSL("tableofcontents"), tableofcontentsList);
140✔
1380
    }
1381
    return map;
1382
}
36✔
1383

1384
QVariantMap Doxml::parseCompound_docEmojiType(QXmlStreamReader &xml) const
8✔
1385
{
1386
    const auto unicode = xml.attributes().value(QSL("unicode"));
15✔
1387
    const auto value = parseNumericCharacterReference(unicode);
8✔
1388
    if (value.isNull()) {
8✔
UNCOV
1389
        logWarning(QTR("Invalid numeric character reference: %1").arg(unicode), xml);
×
1390
    }
1391
    return {
1392
        { QSL("name"), xml.attributes().value(QSL("name")).toString() },
16✔
1393
        { QSL("unicode"), unicode.toString() },
10✔
1394
        { QSL("value"), value },
8✔
1395
    };
54✔
1396
}
2✔
1397

1398
QVariantMap Doxml::parseDoxyfile(QXmlStreamReader &xml) const
8✔
1399
{
1400
    if (!xml.readNextStartElement()) {
8✔
1401
        Q_ASSERT(xml.hasError());
1402
        return { };
1403
    }
1404
    if (xml.name() != QSL("doxyfile")) {
10✔
UNCOV
1405
        xml.raiseError(QTR("Root element is not \"doxyfile\""));
×
1406
        return { };
1407
    }
1408
    return parseDoxyfile_DoxygenFileType(xml);
8✔
1409
}
1410

1411
QVariantMap Doxml::parseDoxyfile_DoxygenFileType(QXmlStreamReader &xml) const
16✔
1412
{
1413
    Q_ASSERT(xml.name() == QSL("doxyfile"));
1414

1415
    QVariantMap map;
12✔
1416
    map.insert(QSL("version"), xml.attributes().value(QSL("version")).toString());
40✔
1417
    map.insert(QSL("language"), xml.attributes().value(QSL("xml:lang")).toString());
40✔
1418

1419
    QVariantMap options;
12✔
1420
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
112✔
1421
        if (xml.name() == QSL("option")) {
192✔
1422
            options.insert(parseDoxyfile_OptionType(xml));
168✔
1423
        } else {
UNCOV
1424
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1425
            xml.skipCurrentElement();
×
1426
        }
1427
    }
1428
    qCDebug(lc).noquote() << QTR("Parsed %1 options(s) from %2").arg(options.size()).arg(currentXmlFilePath);
20✔
1429
    map.insert(QSL("options"), options);
28✔
1430
    return map;
16✔
1431
}
4✔
1432

1433
QVariantMap Doxml::parseDoxyfile_OptionType(QXmlStreamReader &xml) const
144✔
1434
{
1435
    Q_ASSERT(xml.name() == QSL("option"));
1436
    const auto attributes = xml.attributes();
144✔
1437
    const auto id = attributes.value(QSL("id"));
270✔
1438
    const auto type = attributes.value(QSL("type"));
270✔
1439
    if (type == QSL("int")) {
144✔
1440
        return { { id.toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements).toInt() } };
72✔
1441
    } else if (type == QSL("bool")) {
120✔
1442
        return { { id.toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements) == QSL("YES") } };
108✔
1443
    } else if (type == QSL("string")) {
72✔
1444
        return { { id.toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements) } };
60✔
1445
    } else if (type == QSL("stringlist")) {
48✔
1446
        QStringList values;
36✔
1447
        while ((!xml.atEnd()) && (xml.readNextStartElement())) {
96✔
1448
            if (xml.name() == QSL("value")) {
96✔
1449
                values.append(xml.readElementText());
84✔
1450
            } else {
UNCOV
1451
                logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1452
                xml.skipCurrentElement();
×
1453
            }
1454
        }
1455
        return { { id.toString(), values } };
108✔
1456
    }
UNCOV
1457
    logWarning(QTR("Treating Doxyfile option \"%1\" of unknown type \"%2\" as string").arg(id, type), xml);
×
UNCOV
1458
    return { { id.toString(), xml.readElementText(QXmlStreamReader::IncludeChildElements) } };
×
1459
}
1460

1461
QVariantMap Doxml::parseIndex(QXmlStreamReader &xml) const
8✔
1462
{
1463
    if (!xml.readNextStartElement()) {
8✔
1464
        Q_ASSERT(xml.hasError());
1465
        return { };
1466
    }
1467
    if (xml.name() != QSL("doxygenindex")) {
10✔
UNCOV
1468
        xml.raiseError(QTR("Root element is not \"doxygenindex\""));
×
1469
        return { };
1470
    }
1471
    return parseIndex_DoxygenType(xml);
8✔
1472
}
1473

1474
QVariantMap Doxml::parseIndex_DoxygenType(QXmlStreamReader &xml) const
16✔
1475
{
1476
    Q_ASSERT(xml.name() == QSL("doxygenindex"));
1477

1478
    QVariantMap map {
1479
        { QSL("version"), xml.attributes().value(QSL("version")).toString() },
32✔
1480
        { QSL("language"), xml.attributes().value(QSL("xml:lang")).toString() },
32✔
1481
    };
76✔
1482

1483
    QVariantList compounds;
12✔
1484
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
32✔
1485
        if (xml.name() == QSL("compound")) {
32✔
1486
            compounds.append(parseIndex_CompoundType(xml));
28✔
1487
        } else {
UNCOV
1488
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1489
            xml.skipCurrentElement();
×
1490
        }
1491
    }
1492
    qCDebug(lc).noquote() << QTR("Parsed %1 compounds(s) from %2").arg(compounds.size()).arg(currentXmlFilePath);
20✔
1493
    map.insert(QSL("compounds"), compounds);
28✔
1494
    return map;
16✔
1495
}
4✔
1496

1497
QVariantMap Doxml::parseIndex_CompoundType(QXmlStreamReader &xml) const
32✔
1498
{
1499
    Q_ASSERT(xml.name() == QSL("compound"));
1500
    QVariantMap map {
1501
        { QSL("refid"), xml.attributes().value(QSL("refid")).toString() },
64✔
1502
        { QSL("kind"),  xml.attributes().value(QSL("kind")).toString() },
64✔
1503
    };
152✔
1504

1505
    if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
64✔
UNCOV
1506
        xml.raiseError(QTR("<compound> does not begin with <name>"));
×
1507
        return { };
1508
    }
1509
    map.insert(QSL("name"), xml.readElementText());
56✔
1510

1511
    QVariantList members;
24✔
1512
    while ((!xml.atEnd()) && (xml.readNextStartElement())) {
48✔
1513
        if (xml.name() == QSL("member")) {
32✔
1514
            members.append(parseIndex_MemberType(xml));
28✔
1515
        } else {
UNCOV
1516
            logWarning(Warning::UnexpectedElement, xml);
×
UNCOV
1517
            xml.skipCurrentElement();
×
1518
        }
1519
    }
1520
    map.insert(QSL("members"), members);
56✔
1521
    return map;
1522
}
8✔
1523

1524
QVariantMap Doxml::parseIndex_MemberType(QXmlStreamReader &xml) const
24✔
1525
{
1526
    Q_ASSERT(xml.name() == QSL("member"));
1527
    QVariantMap map {
1528
        { QSL("refid"), xml.attributes().value(QSL("refid")).toString() },
48✔
1529
        { QSL("kind"),  xml.attributes().value(QSL("kind")).toString() },
48✔
1530
    };
114✔
1531
    if ((!xml.readNextStartElement()) || (xml.name() != QSL("name"))) {
48✔
UNCOV
1532
        xml.raiseError(QTR("<member> does not begin with <name>"));
×
1533
        return { };
1534
    }
1535
    map.insert(QSL("name"), xml.readElementText());
42✔
1536
    xml.skipCurrentElement();
24✔
1537
    return map;
1538
}
6✔
1539

1540
} // namespace doxlee
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc