From dd1a69e05f12656f9260fb8c7941405043804326 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 24 May 2023 13:47:27 +0200 Subject: [PATCH] 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 Reviewed-by: Christian Stenger --- src/plugins/qtsupport/examplesparser.cpp | 14 +++++++--- tests/auto/examples/tst_examples.cpp | 33 ++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/plugins/qtsupport/examplesparser.cpp b/src/plugins/qtsupport/examplesparser.cpp index bd6ff08918f..e5853a0f7cb 100644 --- a/src/plugins/qtsupport/examplesparser.cpp +++ b/src/plugins/qtsupport/examplesparser.cpp @@ -319,16 +319,22 @@ QList>> getCategories(const QList other; QMap> categoryMap; 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) { - const QStringList itemCategories = item->metaData.value("category"); + const QStringList itemCategories = Utils::filteredUnique( + item->metaData.value("category")); for (const QString &category : itemCategories) - categoryMap[category].append(item); + categoryMap[category].append(new ExampleItem(*item)); if (itemCategories.isEmpty()) - other.append(item); + other.append(new ExampleItem(*item)); } } QList>> categories; 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" QList featured; QList allOther; @@ -340,6 +346,8 @@ QList>> getCategories(const QList("videoLength"); QTest::addColumn("platforms"); QTest::addColumn("metaData"); + QTest::addColumn("categories"); QTest::addRow("example") << QByteArray(R"raw( @@ -102,6 +103,8 @@ void tst_Examples::parsing_data() widgets/widgets/analogclock/analogclock.cpp Graphics + Graphics + Foobar widgets @@ -120,13 +123,29 @@ void tst_Examples::parsing_data() "manifest/widgets/widgets/analogclock/analogclock.cpp")} << FilePath::fromUserInput("manifest/widgets/widgets/analogclock/analogclock.cpp") << 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( + + + + + )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() { QFETCH(QByteArray, data); QFETCH(bool, isExamples); + QFETCH(QStringList, categories); const ExampleItem expected = fetchItem(); const expected_str> result = parseExamples(data, @@ -154,7 +173,17 @@ void tst_Examples::parsing() QCOMPARE(item.videoLength, expected.videoLength); QCOMPARE(item.platforms, expected.platforms); QCOMPARE(item.metaData, expected.metaData); - qDeleteAll(*result); + + const QList>> 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)