Fix crash when examples are in multiple categories

When items are added into multiple categories, we got into a double
delete when cleaning up, e.g. when changing the shown examples to a
different Qt version.

Sort copies into categories and delete the originals as a quickfix.
Cleaner would be if we didn't allocate them on the heap in the first
place.

Fixes: QTCREATORBUG-29197
Change-Id: I6490111aba87335b0f7189c4957ed1da8681806f
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Eike Ziller
2023-05-24 13:47:27 +02:00
parent 8ca4f909e8
commit dd1a69e05f
2 changed files with 42 additions and 5 deletions

View File

@@ -319,16 +319,22 @@ QList<std::pair<QString, QList<ExampleItem *>>> getCategories(const QList<Exampl
QList<ExampleItem *> other; QList<ExampleItem *> other;
QMap<QString, QList<ExampleItem *>> categoryMap; QMap<QString, QList<ExampleItem *>> categoryMap;
if (useCategories) { if (useCategories) {
// Append copies of the items and delete the original ones,
// because items might be added to multiple categories and that needs individual items
for (ExampleItem *item : items) { for (ExampleItem *item : items) {
const QStringList itemCategories = item->metaData.value("category"); const QStringList itemCategories = Utils::filteredUnique(
item->metaData.value("category"));
for (const QString &category : itemCategories) for (const QString &category : itemCategories)
categoryMap[category].append(item); categoryMap[category].append(new ExampleItem(*item));
if (itemCategories.isEmpty()) if (itemCategories.isEmpty())
other.append(item); other.append(new ExampleItem(*item));
} }
} }
QList<std::pair<QString, QList<ExampleItem *>>> categories; QList<std::pair<QString, QList<ExampleItem *>>> categories;
if (categoryMap.isEmpty()) { if (categoryMap.isEmpty()) {
// If we tried sorting into categories, but none were defined, we copied the items
// into "other", which we don't use here. Get rid of them again.
qDeleteAll(other);
// The example set doesn't define categories. Consider the "highlighted" ones as "featured" // The example set doesn't define categories. Consider the "highlighted" ones as "featured"
QList<ExampleItem *> featured; QList<ExampleItem *> featured;
QList<ExampleItem *> allOther; QList<ExampleItem *> allOther;
@@ -340,6 +346,8 @@ QList<std::pair<QString, QList<ExampleItem *>>> getCategories(const QList<Exampl
if (!allOther.isEmpty()) if (!allOther.isEmpty())
categories.append({otherDisplayName, allOther}); categories.append({otherDisplayName, allOther});
} else { } else {
// All original items have been copied into a category or other, delete.
qDeleteAll(items);
const auto end = categoryMap.constKeyValueEnd(); const auto end = categoryMap.constKeyValueEnd();
for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it)
categories.append(*it); categories.append(*it);

View File

@@ -88,6 +88,7 @@ void tst_Examples::parsing_data()
QTest::addColumn<QString>("videoLength"); QTest::addColumn<QString>("videoLength");
QTest::addColumn<QStringList>("platforms"); QTest::addColumn<QStringList>("platforms");
QTest::addColumn<MetaData>("metaData"); QTest::addColumn<MetaData>("metaData");
QTest::addColumn<QStringList>("categories");
QTest::addRow("example") QTest::addRow("example")
<< QByteArray(R"raw( << QByteArray(R"raw(
@@ -102,6 +103,8 @@ void tst_Examples::parsing_data()
<fileToOpen mainFile="true">widgets/widgets/analogclock/analogclock.cpp</fileToOpen> <fileToOpen mainFile="true">widgets/widgets/analogclock/analogclock.cpp</fileToOpen>
<meta> <meta>
<entry name="category">Graphics</entry> <entry name="category">Graphics</entry>
<entry name="category">Graphics</entry>
<entry name="category">Foobar</entry>
<entry name="tags">widgets</entry> <entry name="tags">widgets</entry>
</meta> </meta>
</example> </example>
@@ -120,13 +123,29 @@ void tst_Examples::parsing_data()
"manifest/widgets/widgets/analogclock/analogclock.cpp")} "manifest/widgets/widgets/analogclock/analogclock.cpp")}
<< FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.cpp") << FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.cpp")
<< FilePaths() << Example << true << false << false << "" << FilePaths() << Example << true << false << false << ""
<< "" << QStringList() << MetaData({{"category", {"Graphics"}}, {"tags", {"widgets"}}}); << "" << QStringList()
<< MetaData({{"category", {"Graphics", "Graphics", "Foobar"}}, {"tags", {"widgets"}}})
<< QStringList{"Foobar", "Graphics"};
QTest::addRow("no category, highlighted")
<< QByteArray(R"raw(
<examples>
<example name="No Category, highlighted"
isHighlighted="true">
</example>
</examples>
)raw") << /*isExamples=*/true
<< "No Category, highlighted" << QString() << QString() << QStringList()
<< FilePath("manifest") << QString() << FilePaths() << FilePath() << FilePaths() << Example
<< /*hasSourceCode=*/false << false << /*isHighlighted=*/true << ""
<< "" << QStringList() << MetaData() << QStringList{"Featured"};
} }
void tst_Examples::parsing() void tst_Examples::parsing()
{ {
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
QFETCH(bool, isExamples); QFETCH(bool, isExamples);
QFETCH(QStringList, categories);
const ExampleItem expected = fetchItem(); const ExampleItem expected = fetchItem();
const expected_str<QList<ExampleItem *>> result const expected_str<QList<ExampleItem *>> result
= parseExamples(data, = parseExamples(data,
@@ -154,7 +173,17 @@ void tst_Examples::parsing()
QCOMPARE(item.videoLength, expected.videoLength); QCOMPARE(item.videoLength, expected.videoLength);
QCOMPARE(item.platforms, expected.platforms); QCOMPARE(item.platforms, expected.platforms);
QCOMPARE(item.metaData, expected.metaData); QCOMPARE(item.metaData, expected.metaData);
qDeleteAll(*result);
const QList<std::pair<QString, QList<ExampleItem *>>> resultCategories = getCategories(*result,
true);
QCOMPARE(resultCategories.size(), categories.size());
for (int i = 0; i < resultCategories.size(); ++i) {
QCOMPARE(resultCategories.at(i).first, categories.at(i));
QCOMPARE(resultCategories.at(i).second.size(), 1);
}
for (const auto &category : resultCategories)
qDeleteAll(category.second);
} }
QTEST_APPLESS_MAIN(tst_Examples) QTEST_APPLESS_MAIN(tst_Examples)