diff --git a/core/tests/dimg/CMakeLists.txt b/core/tests/dimg/CMakeLists.txt index 7d4bff1de0..ef0b2aebc1 100644 --- a/core/tests/dimg/CMakeLists.txt +++ b/core/tests/dimg/CMakeLists.txt @@ -1,154 +1,154 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. APPLY_COMMON_POLICIES() # Boost uses operator names (and, not, ...) string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") include_directories( $ $ $ $ $ $ ) #------------------------------------------------------------------------ set(testdimgloader_SRCS testdimgloader.cpp) add_executable(testdimgloader ${testdimgloader_SRCS}) ecm_mark_nongui_executable(testdimgloader) target_link_libraries(testdimgloader digikamcore ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ set(testequalizefilter_SRCS testequalizefilter.cpp) add_executable(testequalizefilter ${testequalizefilter_SRCS}) ecm_mark_nongui_executable(testequalizefilter) target_link_libraries(testequalizefilter digikamcore ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ set(testcolorbalancefilter_SRCS testcolorbalancefilter.cpp) add_executable(testcolorbalancefilter ${testcolorbalancefilter_SRCS}) ecm_mark_nongui_executable(testcolorbalancefilter) target_link_libraries(testcolorbalancefilter digikamcore ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ if(ImageMagick_Magick++_FOUND) set(magickloader_SRCS magickloader.cpp) add_executable(magickloader ${magickloader_SRCS}) target_link_libraries(magickloader digikamcore ${COMMON_TEST_LINK} ) endif() #------------------------------------------------------------------------ ecm_add_tests(dimgfilteractiontest.cpp GUI NAME_PREFIX "digikam-" LINK_LIBRARIES digikamcore ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ ecm_add_tests(dimgfreerotationtest.cpp GUI NAME_PREFIX "digikam-" LINK_LIBRARIES digikamcore ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ add_library(libabstracthistory STATIC dimgabstracthistorytest.cpp) ecm_add_tests(dimghistorytest.cpp GUI NAME_PREFIX "digikam-" LINK_LIBRARIES digikamcore libabstracthistory ${COMMON_TEST_LINK} ) #------------------------------------------------------------------------ # TODO: This unit-test do not link yet under Microsoft compiler -if(NOT MSVC) +#if(NOT MSVC) ecm_add_tests(dimghistorygraphtest.cpp NAME_PREFIX "digikam-" LINK_LIBRARIES digikamcore digikamdatabase libmodeltest libabstracthistory ${COMMON_TEST_LINK} ) -endif() +#endif() diff --git a/core/tests/dimg/dimghistorygraphtest.cpp b/core/tests/dimg/dimghistorygraphtest.cpp index 71ef581b9a..041dd4f563 100644 --- a/core/tests/dimg/dimghistorygraphtest.cpp +++ b/core/tests/dimg/dimghistorygraphtest.cpp @@ -1,484 +1,494 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-08-01 * Description : a test for the DImageHistory * * Copyright (C) 2010 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimghistorygraphtest.h" // Qt includes #include #include #include #include #include #include #include #include // Local includes #include "coredb.h" #include "collectionlocation.h" #include "collectionmanager.h" #include "collectionscanner.h" #include "editorcore.h" #include "dmetadata.h" #include "iteminfo.h" #include "itemhistorygraph.h" #include "itemhistorygraphdata.h" #include "itemhistorygraphmodel.h" #include "iofilesettings.h" #include "tagscache.h" #include "modeltest.h" using namespace Digikam; QTEST_MAIN(DImgHistoryGraphTest) void DImgHistoryGraphTest::initTestCase() { initBaseTestCase(); QString name = tempFileName(QLatin1String("collection")); collectionDir = QDir::temp(); collectionDir.mkdir(name); collectionDir.cd(name); QVERIFY(collectionDir.exists()); dbFile = tempFilePath(QLatin1String("database")); qDebug() << "Using database path for test: " << dbFile; DbEngineParameters params(QLatin1String("QSQLITE"), dbFile, QLatin1String("QSQLITE"), dbFile); CoreDbAccess::setParameters(params, CoreDbAccess::MainApplication); QVERIFY(CoreDbAccess::checkReadyForUse(nullptr)); QVERIFY(QFile(dbFile).exists()); CollectionManager::instance()->addLocation(QUrl::fromLocalFile(collectionDir.path())); CollectionManager::instance()->addLocation(QUrl::fromLocalFile(imagePath())); QList locs = CollectionManager::instance()->allAvailableLocations(); QVERIFY(locs.size() == 2); rescan(); QList albums = CoreDbAccess().db()->getAlbumShortInfos(); QVERIFY(albums.size() >= 2); foreach (const AlbumShortInfo& album, albums) { //qDebug() << album.relativePath << album.id; //qDebug() << CollectionManager::instance()->albumRootPath(album.albumRootId); //qDebug() << CoreDbAccess().db()->getItemURLsInAlbum(album.id); readOnlyImages << CoreDbAccess().db()->getItemURLsInAlbum(album.id); } foreach (const QString& file, readOnlyImages) { ids << ItemInfo::fromLocalFile(file).id(); } QVERIFY(!ids.contains(-1)); QVERIFY(ids.size() >= 6); // Nb of files in data sub dir. } void DImgHistoryGraphTest::cleanupTestCase() { cleanupBaseTestCase(); QFile(dbFile).remove(); QDir dir(collectionDir.path()); dir.removeRecursively(); qDebug() << "deleted test folder " << collectionDir.path(); } void DImgHistoryGraphTest::rescan() { CollectionScanner().completeScan(); } template QList mapList(const QList& l, const QMap map) { QList r; foreach (const from& f, l) { r << map.value(f); } return r; } void DImgHistoryGraphTest::testEditing() { QDir imageDir(imagePath()); /* orig 1 2 3 4 */ IOFileSettings container; m_im->load(readOnlyImages.first(), &container); m_loop.exec(); // krazy:exclude=crashy applyFilters1(); m_im->saveAs(collectionDir.filePath(QLatin1String("1.jpg")), &container, true, QString(), QString()); m_loop.exec(); // krazy:exclude=crashy applyFilters2(); m_im->saveAs(collectionDir.filePath(QLatin1String("2.jpg")), &container, true, QString(), QString()); m_loop.exec(); // krazy:exclude=crashy applyFilters3(); m_im->saveAs(collectionDir.filePath(QLatin1String("3.jpg")), &container, true, QString(), QString()); m_loop.exec(); // krazy:exclude=crashy m_im->load(collectionDir.filePath(QLatin1String("2.jpg")), &container); m_loop.exec(); // krazy:exclude=crashy applyFilters4(); m_im->saveAs(collectionDir.filePath(QLatin1String("4.jpg")), &container, true, QString(), QString()); m_loop.exec(); // krazy:exclude=crashy CollectionScanner().completeScan(); - ItemInfo orig = ItemInfo::fromLocalFile(readOnlyImages.first()); - ItemInfo one = ItemInfo::fromLocalFile(collectionDir.filePath(QLatin1String("1.jpg"))), + ItemInfo orig = ItemInfo::fromLocalFile(readOnlyImages.first()); + ItemInfo one = ItemInfo::fromLocalFile(collectionDir.filePath(QLatin1String("1.jpg"))), two = ItemInfo::fromLocalFile(collectionDir.filePath(QLatin1String("2.jpg"))), three = ItemInfo::fromLocalFile(collectionDir.filePath(QLatin1String("3.jpg"))), four = ItemInfo::fromLocalFile(collectionDir.filePath(QLatin1String("4.jpg"))); typedef QPair IdPair; QList controlCloud; controlCloud << IdPair(one.id(), orig.id()); //X controlCloud << IdPair(two.id(), one.id()); //X controlCloud << IdPair(three.id(), two.id()); //X controlCloud << IdPair(four.id(), two.id()); //X controlCloud << IdPair(three.id(), one.id()); controlCloud << IdPair(four.id(), one.id()); controlCloud << IdPair(two.id(), orig.id()); controlCloud << IdPair(three.id(), orig.id()); controlCloud << IdPair(four.id(), orig.id()); std::sort(controlCloud.begin(), controlCloud.end()); ItemHistoryGraph graph1 = ItemHistoryGraph::fromInfo(three); qDebug() << graph1; ItemHistoryGraph graph2 = ItemHistoryGraph::fromInfo(four); qDebug() << graph2; ItemHistoryGraph graph3 = ItemHistoryGraph::fromInfo(one); qDebug() << graph3; // all three must have the full cloud + QVERIFY(graph1.data().vertexCount() == 5); QVERIFY(graph2.data().vertexCount() == 5); QVERIFY(graph3.data().vertexCount() == 5); QList cloud = graph3.relationCloud(); std::sort(cloud.begin(), cloud.end()); QVERIFY(cloud == controlCloud); int needResolvingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needResolvingHistory()); int needTaggingTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::needTaggingHistoryGraph()); int originalVersionTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::originalVersion()); int currentVersionTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::currentVersion()); int intermediateVersionTag = TagsCache::instance()->getOrCreateInternalTag(InternalTagName::intermediateVersion()); //qDebug() << orig.tagIds() << one.tagIds() << two.tagIds() << three.tagIds() << four.tagIds(); + QVERIFY(!orig.tagIds().contains(needResolvingTag)); QVERIFY(!orig.tagIds().contains(needTaggingTag)); QVERIFY(orig.tagIds().contains(originalVersionTag)); QVERIFY(one.tagIds().contains(intermediateVersionTag)); QVERIFY(two.tagIds().contains(intermediateVersionTag)); QVERIFY(three.tagIds().contains(currentVersionTag)); QVERIFY(four.tagIds().contains(currentVersionTag)); QFile fileTwo(two.filePath()); fileTwo.remove(); QVERIFY(!fileTwo.exists()); CollectionScanner().completeScan(); graph2 = ItemHistoryGraph::fromInfo(four); + // graph is prepared for display, vertex of removed file cleared + QVERIFY(graph2.data().vertexCount() == 4); qDebug() << graph2; // Check that removal of current version leads to resetting of current version tag + QFile fileThree(three.filePath()); fileThree.remove(); QFile fileFour(four.filePath()); fileFour.remove(); CollectionScanner().completeScan(); qDebug() << originalVersionTag << currentVersionTag << intermediateVersionTag<< orig.tagIds() << one.tagIds(); QVERIFY(one.tagIds().contains(currentVersionTag)); QVERIFY(!one.tagIds().contains(intermediateVersionTag)); } void DImgHistoryGraphTest::testHistory() { ItemHistoryGraph graph; ItemInfo subject(ids.first()); graph.addHistory(history1(), subject); graph.reduceEdges(); QCOMPARE(graph.data().vertexCount(), 3); QCOMPARE(graph.data().edges().size(), 2); } class Q_DECL_HIDDEN lessThanById { public: explicit lessThanById(const QMap& vertexToId) : vertexToId(vertexToId) { } bool operator()(const HistoryGraph::Vertex& a, const HistoryGraph::Vertex& b) { return vertexToId.value(a) < vertexToId.value(b); } public: QMap vertexToId; }; void DImgHistoryGraphTest::testGraph() { /* 1 2 8 9 19 20 21 10 3 4 11 12 22 23 | 13-- 24 5 14 6 15 7 16 17 18 */ QList controlLeaves; controlLeaves << 8 << 19 << 20 << 21 << 10 << 3 << 11 << 22 << 24 << 14 << 15 << 16 << 17 << 18; std::sort(controlLeaves.begin(), controlLeaves.end()); QList controlRoots; controlRoots << 1; QList controlLongestPathEighteen; controlLongestPathEighteen << 1 << 7 << 18; QList controlLongestPathTwentyFour; controlLongestPathTwentyFour << 1 << 4 << 12 << 23 << 24; QList controlSubgraphTwo; controlSubgraphTwo << 2 << 8 << 9 << 10 << 19 << 20 << 21; QList controlSubgraphTwoSorted; controlSubgraphTwoSorted << 2 << 8 << 9 << 19 << 20 << 21 << 10; QList controlSubgraphFour; controlSubgraphFour << 4 << 11 << 12 << 13 << 22 << 23 << 24; QList controlRootsOfEighteen; controlRootsOfEighteen << 1; QList controlLeavesFromTwo; controlLeavesFromTwo << 8 << 10 << 19 << 20 << 21; typedef QPair IdPair; QList pairs; /** * The following description of the tree-like graph above (24 breaks (poly)tree definition) * is longer than needed (transitive reduction) and less than possible (transitive closure): * Pairs marked with "X" must remain when building the transitive reduction. * The transitive closure must additionally contain all pairs not marked, * and the pairs commented out. */ pairs << IdPair(2,1); //X pairs << IdPair(3,1); //X pairs << IdPair(4,1); //X pairs << IdPair(5,1); //X pairs << IdPair(6,1); //X pairs << IdPair(7,1); //X pairs << IdPair(8,1); //pairs << IdPair(9,1); pairs << IdPair(10,1); pairs << IdPair(11,1); pairs << IdPair(12,1); pairs << IdPair(13,1); pairs << IdPair(14,1); pairs << IdPair(15,1); pairs << IdPair(16,1); pairs << IdPair(17,1); pairs << IdPair(18,1); pairs << IdPair(22,4); pairs << IdPair(23,4); pairs << IdPair(24,4); pairs << IdPair(14,5); //X pairs << IdPair(15,6); //X //pairs << IdPair(19,1); //pairs << IdPair(20,1); //pairs << IdPair(21,1); pairs << IdPair(22,1); pairs << IdPair(23,1); pairs << IdPair(24,1); pairs << IdPair(8,2); //X pairs << IdPair(9,2); //X pairs << IdPair(10,2); //X //pairs << IdPair(19,2); //pairs << IdPair(20,2); //pairs << IdPair(21,2); pairs << IdPair(11,4); //X pairs << IdPair(12,4); //X pairs << IdPair(13,4); //X pairs << IdPair(16,7); //X pairs << IdPair(17,7); //X pairs << IdPair(18,7); //X pairs << IdPair(19,9); //X pairs << IdPair(20,9); //X pairs << IdPair(21,9); //X pairs << IdPair(22,12); //X pairs << IdPair(23,12); //X // no more a polytree pairs << IdPair(24,13); //X pairs << IdPair(24,23); //X pairs << IdPair(24,4); pairs << IdPair(24,1); pairs << IdPair(24,12); ItemHistoryGraph graph; graph.addRelations(pairs); qDebug() << "Initial graph:" << graph; graph.reduceEdges(); qDebug() << "Transitive reduction:" << graph; QList cloud = graph.relationCloud(); qDebug() << "Transitive closure:" << cloud; QVERIFY(cloud.contains(IdPair(7,1))); QVERIFY(cloud.contains(IdPair(8,1))); QVERIFY(cloud.contains(IdPair(9,1))); - /* +/* QBENCHMARK { ItemHistoryGraph benchGraph; graph.addRelations(pairs); graph.finish(); graph.relationCloud(); } - */ +*/ QMap idToVertex; QMap vertexToId; foreach (const HistoryGraph::Vertex& v, graph.data().vertices()) { HistoryVertexProperties props = graph.data().properties(v); idToVertex[props.infos.first().id()] = v; vertexToId[v] = props.infos.first().id(); } QList leaves = mapList(graph.data().leaves(), vertexToId); std::sort(leaves.begin(), leaves.end()); QVERIFY(leaves == controlLeaves); QList roots = mapList(graph.data().roots(), vertexToId); std::sort(roots.begin(), roots.end()); QVERIFY(roots == controlRoots); QList longestPath1 = mapList(graph.data().longestPathTouching(idToVertex.value(18)), vertexToId); QVERIFY(longestPath1 == controlLongestPathEighteen); QList longestPath2 = mapList(graph.data().longestPathTouching(idToVertex.value(24)), vertexToId); QVERIFY(longestPath2 == controlLongestPathTwentyFour); // depth-first + QList subgraphTwo = mapList(graph.data().verticesDominatedBy(idToVertex.value(2), idToVertex.value(1), HistoryGraph::DepthFirstOrder), vertexToId); std::sort(subgraphTwo.begin(), subgraphTwo.end()); QVERIFY(subgraphTwo == controlSubgraphTwo); // breadth-first + QList subgraphFour = mapList(graph.data().verticesDominatedBy(idToVertex.value(4), idToVertex.value(1)), vertexToId); QVERIFY(subgraphFour.indexOf(22) > subgraphFour.indexOf(13)); std::sort(subgraphFour.begin(), subgraphFour.end()); QVERIFY(subgraphFour == controlSubgraphFour); // depth-first + QList subgraphTwoSorted = mapList( graph.data().verticesDominatedByDepthFirstSorted(idToVertex.value(2), idToVertex.value(1),lessThanById(vertexToId)), vertexToId); + // no sorting this time + QVERIFY(subgraphTwoSorted == controlSubgraphTwoSorted); QList rootsOfEighteen = mapList(graph.data().rootsOf(idToVertex.value(18)), vertexToId); std::sort(rootsOfEighteen.begin(), rootsOfEighteen.end()); QVERIFY(rootsOfEighteen == controlRootsOfEighteen); QList leavesFromTwo = mapList(graph.data().leavesFrom(idToVertex.value(2)), vertexToId); std::sort(leavesFromTwo.begin(), leavesFromTwo.end()); QVERIFY(leavesFromTwo == controlLeavesFromTwo); } void DImgHistoryGraphTest::slotImageLoaded(const QString& fileName, bool success) { QVERIFY(success); qDebug() << "Loaded" << fileName; m_loop.quit(); } void DImgHistoryGraphTest::slotImageSaved(const QString& fileName, bool success) { QVERIFY(success); m_im->setLastSaved(fileName); qDebug() << "Saved to" << fileName; m_loop.quit(); }