int main(int argc, char *argv[]) { const QString runGuardName = getRunGuardName(); Helpers::RunGuard guard(runGuardName); if (!guard.tryToRun()) { std::cerr << "Xpiks is already running"; return -1; } // will call curl_global_init and cleanup Conectivity::CurlInitHelper curlInitHelper; Q_UNUSED(curlInitHelper); // will init thread-unsafe XMP toolkit MetadataIO::Exiv2InitHelper exiv2InitHelper; Q_UNUSED(exiv2InitHelper); const char *highDpiEnvironmentVariable = setHighDpiEnvironmentVariable(); qRegisterMetaTypeStreamOperators<Models::ProxySettings>("ProxySettings"); qRegisterMetaType<Common::SpellCheckFlags>("Common::SpellCheckFlags"); initQSettings(); Helpers::AppSettings appSettings; ensureUserIdExists(&appSettings); Suggestion::LocalLibrary localLibrary; QString appDataPath = XPIKS_USERDATA_PATH; if (!appDataPath.isEmpty()) { QDir appDataDir(appDataPath); QString libraryFilePath = appDataDir.filePath(Constants::LIBRARY_FILENAME); localLibrary.setLibraryPath(libraryFilePath); } else { std::cerr << "AppDataPath is empty!"; } #ifdef WITH_LOGS const QString &logFileDir = QDir::cleanPath(appDataPath + QDir::separator() + "logs"); if (!logFileDir.isEmpty()) { QDir dir(logFileDir); if (!dir.exists()) { bool created = QDir().mkpath(logFileDir); Q_UNUSED(created); } QString time = QDateTime::currentDateTimeUtc().toString("ddMMyyyy-hhmmss-zzz"); QString logFilename = QString("xpiks-qt-%1.log").arg(time); QString logFilePath = dir.filePath(logFilename); Helpers::Logger &logger = Helpers::Logger::getInstance(); logger.setLogFilePath(logFilePath); } #endif QMLExtensions::ColorsModel colorsModel; Models::LogsModel logsModel(&colorsModel); logsModel.startLogging(); #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) qSetMessagePattern("%{time hh:mm:ss.zzz} %{type} T#%{threadid} %{function} - %{message}"); #endif qInstallMessageHandler(myMessageHandler); LOG_INFO << "Log started. Today is" << QDateTime::currentDateTimeUtc().toString("dd.MM.yyyy"); LOG_INFO << "Xpiks" << XPIKS_VERSION_STRING << "-" << STRINGIZE(BUILDNUMBER); #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) LOG_INFO << QSysInfo::productType() << QSysInfo::productVersion() << QSysInfo::currentCpuArchitecture(); #else #ifdef Q_OS_WIN LOG_INFO << QLatin1String("Windows Qt<5.4"); #elsif Q_OS_DARWIN LOG_INFO << QLatin1String("OS X Qt<5.4"); #else LOG_INFO << QLatin1String("LINUX Qt<5.4"); #endif #endif QApplication app(argc, argv); LOG_INFO << "Working directory of Xpiks is:" << QDir::currentPath(); LOG_DEBUG << "Extra files search locations:" << QStandardPaths::standardLocations(XPIKS_DATA_LOCATION_TYPE); if (highDpiEnvironmentVariable) { qunsetenv(highDpiEnvironmentVariable); } localLibrary.loadLibraryAsync(); QString userId = appSettings.value(QLatin1String(Constants::USER_AGENT_ID)).toString(); userId.remove(QRegExp("[{}-].")); Models::ArtworksRepository artworkRepository; Models::ArtItemsModel artItemsModel; Models::CombinedArtworksModel combinedArtworksModel; Models::UploadInfoRepository uploadInfoRepository; Warnings::WarningsService warningsService; Models::SettingsModel settingsModel; settingsModel.readAllValues(); Encryption::SecretsManager secretsManager; UndoRedo::UndoRedoManager undoRedoManager; Models::ZipArchiver zipArchiver; Suggestion::KeywordsSuggestor keywordsSuggestor(&localLibrary); Models::FilteredArtItemsProxyModel filteredArtItemsModel; filteredArtItemsModel.setSourceModel(&artItemsModel); Models::RecentDirectoriesModel recentDirectorieModel; Conectivity::FtpCoordinator *ftpCoordinator = new Conectivity::FtpCoordinator(settingsModel.getMaxParallelUploads()); Models::ArtworkUploader artworkUploader(ftpCoordinator); SpellCheck::SpellCheckerService spellCheckerService; SpellCheck::SpellCheckSuggestionModel spellCheckSuggestionModel; MetadataIO::BackupSaverService metadataSaverService; Warnings::WarningsModel warningsModel; warningsModel.setSourceModel(&artItemsModel); warningsModel.setWarningsSettingsModel(warningsService.getWarningsSettingsModel()); Models::LanguagesModel languagesModel; AutoComplete::AutoCompleteModel autoCompleteModel; AutoComplete::AutoCompleteService autoCompleteService(&autoCompleteModel); QMLExtensions::ImageCachingService imageCachingService; Models::FindAndReplaceModel replaceModel(&colorsModel); Models::DeleteKeywordsViewModel deleteKeywordsModel; Conectivity::UpdateService updateService(&settingsModel); MetadataIO::MetadataIOCoordinator metadataIOCoordinator; #if defined(QT_NO_DEBUG) && !defined(TELEMETRY_DISABLED) const bool telemetryEnabled = appSettings.value(Constants::USER_STATISTICS, true).toBool(); #else const bool telemetryEnabled = false; #endif Conectivity::TelemetryService telemetryService(userId, telemetryEnabled); Plugins::PluginManager pluginManager; Plugins::PluginsWithActionsModel pluginsWithActions; pluginsWithActions.setSourceModel(&pluginManager); Helpers::HelpersQmlWrapper helpersQmlWrapper; LOG_INFO << "Models created"; Commands::CommandManager commandManager; commandManager.InjectDependency(&artworkRepository); commandManager.InjectDependency(&artItemsModel); commandManager.InjectDependency(&filteredArtItemsModel); commandManager.InjectDependency(&combinedArtworksModel); commandManager.InjectDependency(&artworkUploader); commandManager.InjectDependency(&uploadInfoRepository); commandManager.InjectDependency(&warningsService); commandManager.InjectDependency(&secretsManager); commandManager.InjectDependency(&undoRedoManager); commandManager.InjectDependency(&zipArchiver); commandManager.InjectDependency(&keywordsSuggestor); commandManager.InjectDependency(&settingsModel); commandManager.InjectDependency(&recentDirectorieModel); commandManager.InjectDependency(&spellCheckerService); commandManager.InjectDependency(&spellCheckSuggestionModel); commandManager.InjectDependency(&metadataSaverService); commandManager.InjectDependency(&telemetryService); commandManager.InjectDependency(&updateService); commandManager.InjectDependency(&logsModel); commandManager.InjectDependency(&localLibrary); commandManager.InjectDependency(&metadataIOCoordinator); commandManager.InjectDependency(&pluginManager); commandManager.InjectDependency(&languagesModel); commandManager.InjectDependency(&colorsModel); commandManager.InjectDependency(&autoCompleteService); commandManager.InjectDependency(&imageCachingService); commandManager.InjectDependency(&replaceModel); commandManager.InjectDependency(&deleteKeywordsModel); commandManager.InjectDependency(&helpersQmlWrapper); commandManager.ensureDependenciesInjected(); keywordsSuggestor.initSuggestionEngines(); // other initializations secretsManager.setMasterPasswordHash(appSettings.value(Constants::MASTER_PASSWORD_HASH, "").toString()); uploadInfoRepository.initFromString(appSettings.value(Constants::UPLOAD_HOSTS, "").toString()); recentDirectorieModel.deserializeFromSettings(appSettings.value(Constants::RECENT_DIRECTORIES, "").toString()); commandManager.connectEntitiesSignalsSlots(); languagesModel.initFirstLanguage(); languagesModel.loadLanguages(); telemetryService.setInterfaceLanguage(languagesModel.getCurrentLanguage()); qmlRegisterType<Helpers::ClipboardHelper>("xpiks", 1, 0, "ClipboardHelper"); qmlRegisterType<QMLExtensions::TriangleElement>("xpiks", 1, 0, "TriangleElement"); QQmlApplicationEngine engine; Helpers::GlobalImageProvider *globalProvider = new Helpers::GlobalImageProvider(QQmlImageProviderBase::Image); QMLExtensions::CachingImageProvider *cachingProvider = new QMLExtensions::CachingImageProvider(QQmlImageProviderBase::Image); cachingProvider->setImageCachingService(&imageCachingService); QQmlContext *rootContext = engine.rootContext(); rootContext->setContextProperty("artItemsModel", &artItemsModel); rootContext->setContextProperty("artworkRepository", &artworkRepository); rootContext->setContextProperty("combinedArtworks", &combinedArtworksModel); rootContext->setContextProperty("appSettings", &appSettings); rootContext->setContextProperty("secretsManager", &secretsManager); rootContext->setContextProperty("undoRedoManager", &undoRedoManager); rootContext->setContextProperty("keywordsSuggestor", &keywordsSuggestor); rootContext->setContextProperty("settingsModel", &settingsModel); rootContext->setContextProperty("filteredArtItemsModel", &filteredArtItemsModel); rootContext->setContextProperty("helpersWrapper", &helpersQmlWrapper); rootContext->setContextProperty("recentDirectories", &recentDirectorieModel); rootContext->setContextProperty("metadataIOCoordinator", &metadataIOCoordinator); rootContext->setContextProperty("pluginManager", &pluginManager); rootContext->setContextProperty("pluginsWithActions", &pluginsWithActions); rootContext->setContextProperty("warningsModel", &warningsModel); rootContext->setContextProperty("languagesModel", &languagesModel); rootContext->setContextProperty("i18", &languagesModel); rootContext->setContextProperty("Colors", &colorsModel); rootContext->setContextProperty("acSource", &autoCompleteModel); rootContext->setContextProperty("replaceModel", &replaceModel); #ifdef QT_DEBUG QVariant isDebug(true); #else QVariant isDebug(false); #endif rootContext->setContextProperty("debug", isDebug); engine.addImageProvider("global", globalProvider); engine.addImageProvider("cached", cachingProvider); LOG_DEBUG << "About to load main view..."; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); LOG_DEBUG << "Main view loaded"; pluginManager.getUIProvider()->setQmlEngine(&engine); QQuickWindow *window = qobject_cast<QQuickWindow *>(engine.rootObjects().at(0)); pluginManager.getUIProvider()->setRoot(window->contentItem()); #ifdef QT_DEBUG if (argc > 1) { QList<QUrl> pathes; for (int i = 1; i < argc; ++i) { pathes.append(QUrl::fromLocalFile(QString::fromUtf8(argv[i]))); } commandManager.addInitialArtworks(pathes); } #endif commandManager.afterConstructionCallback(); return app.exec(); }
int AddToUserDictionaryTest::doTest() { Models::ArtItemsModel *artItemsModel = m_CommandManager->getArtItemsModel(); QList<QUrl> files; files << getImagePathForTest("images-for-tests/pixmap/seagull.jpg"); int addedCount = artItemsModel->addLocalArtworks(files); VERIFY(addedCount == files.length(), "Failed to add file"); MetadataIO::MetadataIOCoordinator *ioCoordinator = m_CommandManager->getMetadataIOCoordinator(); SignalWaiter waiter; QObject::connect(ioCoordinator, SIGNAL(metadataReadingFinished()), &waiter, SIGNAL(finished())); ioCoordinator->continueReading(true); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for reading metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while reading"); Models::ArtworkMetadata *metadata = artItemsModel->getArtwork(0); // wait for after-add spellchecking QThread::sleep(1); auto *basicKeywordsModel = metadata->getBasicModel(); QString wrongWord = "abbreviatioe"; metadata->setDescription(metadata->getDescription() + ' ' + wrongWord); metadata->setTitle(metadata->getTitle() + ' ' + wrongWord); metadata->appendKeyword("correct part " + wrongWord); metadata->setIsSelected(true); Models::FilteredArtItemsProxyModel *filteredModel = m_CommandManager->getFilteredArtItemsModel(); SpellCheck::SpellCheckerService *spellCheckService = m_CommandManager->getSpellCheckerService(); QObject::connect(spellCheckService, SIGNAL(spellCheckQueueIsEmpty()), &waiter, SIGNAL(finished())); filteredModel->spellCheckSelected(); if (!waiter.wait(5)) { VERIFY(false, "Timeout for waiting for spellcheck results"); } // wait for finding suggestions QThread::sleep(1); VERIFY(basicKeywordsModel->hasDescriptionSpellError(), "Description spell error not detected"); VERIFY(basicKeywordsModel->hasTitleSpellError(), "Title spell error not detected"); VERIFY(basicKeywordsModel->hasKeywordsSpellError(), "Keywords spell error not detected"); spellCheckService->addWordToUserDictionary(wrongWord); SignalWaiter spellingWaiter; QObject::connect(spellCheckService, SIGNAL(spellCheckQueueIsEmpty()), &spellingWaiter, SIGNAL(finished())); QCoreApplication::processEvents(QEventLoop::AllEvents); // wait add user word to finish if (!spellingWaiter.wait(5)) { VERIFY(false, "Timeout for waiting for spellcheck results"); } sleepWait(5, [=]() { return !basicKeywordsModel->hasDescriptionSpellError() && !basicKeywordsModel->hasTitleSpellError() && !basicKeywordsModel->hasKeywordsSpellError(); }); int userDictWords = spellCheckService->getUserDictWordsNumber(); LOG_DEBUG << "User dict words count:" << userDictWords; VERIFY(userDictWords == 1, "Wrong number of words in user dictionary"); VERIFY(!basicKeywordsModel->hasDescriptionSpellError(), "After adding word. Description spell error is still present"); VERIFY(!basicKeywordsModel->hasTitleSpellError(), "After adding word. Title spell error is still present"); VERIFY(!basicKeywordsModel->hasKeywordsSpellError(), "After adding word. Keywords spell error is still present"); return 0; }
int SpellCheckUndoTest::doTest() { Models::ArtItemsModel *artItemsModel = m_CommandManager->getArtItemsModel(); QList<QUrl> files; files << getImagePathForTest("images-for-tests/vector/026.jpg"); int addedCount = artItemsModel->addLocalArtworks(files); VERIFY(addedCount == files.length(), "Failed to add file"); MetadataIO::MetadataIOCoordinator *ioCoordinator = m_CommandManager->getMetadataIOCoordinator(); SignalWaiter waiter; QObject::connect(ioCoordinator, SIGNAL(metadataReadingFinished()), &waiter, SIGNAL(finished())); ioCoordinator->continueReading(true); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for reading metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while reading"); Models::ArtworkMetadata *metadata = artItemsModel->getArtwork(0); QString wrongWord = "abbreviatioe"; metadata->setDescription(metadata->getDescription() + ' ' + wrongWord); metadata->setTitle(metadata->getTitle() + ' ' + wrongWord); metadata->appendKeyword("correct part " + wrongWord); metadata->setIsSelected(true); // wait for after-add spellchecking QThread::sleep(1); Models::FilteredArtItemsProxyModel *filteredModel = m_CommandManager->getFilteredArtItemsModel(); SpellCheck::SpellCheckerService *spellCheckService = m_CommandManager->getSpellCheckerService(); QObject::connect(spellCheckService, SIGNAL(spellCheckQueueIsEmpty()), &waiter, SIGNAL(finished())); filteredModel->spellCheckSelected(); if (!waiter.wait(5)) { VERIFY(false, "Timeout for waiting for first spellcheck results"); } // wait for finding suggestions QThread::sleep(1); Common::BasicKeywordsModel *basicKeywordsModel = metadata->getKeywordsModel(); VERIFY(basicKeywordsModel->hasDescriptionSpellError(), "Description spell error not detected"); VERIFY(basicKeywordsModel->hasTitleSpellError(), "Title spell error not detected"); VERIFY(basicKeywordsModel->hasKeywordsSpellError(), "Keywords spell error not detected"); filteredModel->clearKeywords(0); QThread::sleep(1); VERIFY(!basicKeywordsModel->hasKeywordsSpellError(), "Keywords spell error not cleared"); UndoRedo::UndoRedoManager *undoRedoManager = m_CommandManager->getUndoRedoManager(); undoRedoManager->undoLastAction(); if (!waiter.wait(5)) { VERIFY(false, "Timeout for waiting for second spellcheck results"); } // wait for finding suggestions QThread::sleep(1); VERIFY(basicKeywordsModel->hasDescriptionSpellError(), "Description spell error not detected on the second time"); VERIFY(basicKeywordsModel->hasTitleSpellError(), "Title spell error not detected on the second time"); VERIFY(basicKeywordsModel->hasKeywordsSpellError(), "Keywords spell error not detected on the second time"); return 0; }
int SpellingProducesWarningsTest::doTest() { Models::ArtItemsModel *artItemsModel = m_CommandManager->getArtItemsModel(); QList<QUrl> files; files << getImagePathForTest("images-for-tests/vector/026.jpg"); int addedCount = artItemsModel->addLocalArtworks(files); VERIFY(addedCount == files.length(), "Failed to add file"); MetadataIO::MetadataIOCoordinator *ioCoordinator = m_CommandManager->getMetadataIOCoordinator(); SignalWaiter waiter; QObject::connect(ioCoordinator, SIGNAL(metadataReadingFinished()), &waiter, SIGNAL(finished())); ioCoordinator->continueReading(true); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for reading metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while reading"); Models::ArtworkMetadata *metadata = artItemsModel->getArtwork(0); sleepWait(3, [metadata]() { return !Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInTitle) && !Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInDescription) && !Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInKeywords); }); VERIFY(!Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInTitle), "Error for reading title"); VERIFY(!Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInDescription), "Error for reading description"); VERIFY(!Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInKeywords), "Error for reading keywords"); QString wrongWord = "abbreviatioe"; metadata->setDescription(metadata->getDescription() + ' ' + wrongWord); metadata->setTitle(metadata->getTitle() + ' ' + wrongWord); metadata->appendKeyword("correct part " + wrongWord); metadata->setIsSelected(true); Models::FilteredArtItemsProxyModel *filteredModel = m_CommandManager->getFilteredArtItemsModel(); SpellCheck::SpellCheckerService *spellCheckService = m_CommandManager->getSpellCheckerService(); SignalWaiter spellingWaiter; QObject::connect(spellCheckService, SIGNAL(spellCheckQueueIsEmpty()), &spellingWaiter, SIGNAL(finished())); filteredModel->spellCheckSelected(); if (!spellingWaiter.wait(5)) { VERIFY(false, "Timeout for waiting for first spellcheck results"); } LOG_INFO << "Spellchecking finished. Waiting for warnings..."; sleepWait(5, [=]() { return Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInTitle) && Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInDescription) && Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInKeywords); }); auto *keywordsModel = metadata->getBasicModel(); VERIFY(keywordsModel->hasDescriptionSpellError(), "Description spell error not detected"); VERIFY(keywordsModel->hasTitleSpellError(), "Title spell error not detected"); VERIFY(keywordsModel->hasKeywordsSpellError(), "Keywords spell error not detected"); VERIFY(Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInTitle), "Warning was not produced for title spelling error"); VERIFY(Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInDescription), "Warning was not produced for description spelling error"); VERIFY(Common::HasFlag(metadata->getWarningsFlags(), Common::WarningFlags::SpellErrorsInKeywords), "Warning was not produced for keywords spelling error"); return 0; }
int ClearMetadataTest::doTest() { Models::ArtItemsModel *artItemsModel = m_CommandManager->getArtItemsModel(); QList<QUrl> files; files << getImagePathForTest("images-for-tests/pixmap/seagull-for-clear.jpg"); int addedCount = artItemsModel->addLocalArtworks(files); VERIFY(addedCount == files.length(), "Failed to add file"); MetadataIO::MetadataIOCoordinator *ioCoordinator = m_CommandManager->getMetadataIOCoordinator(); SignalWaiter waiter; QObject::connect(ioCoordinator, SIGNAL(metadataReadingFinished()), &waiter, SIGNAL(finished())); ioCoordinator->continueReading(true); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for reading metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while reading"); Models::ArtworkMetadata *metadata = artItemsModel->getArtwork(0); const QStringList &keywords = metadata->getKeywords(); QStringList expectedKeywords = QString("picture,seagull,bird").split(','); VERIFY(expectedKeywords == keywords, "Keywords are not the same!"); VERIFY(metadata->getDescription() == "Seagull description", "Description is not the same!"); VERIFY(metadata->getTitle() == "Seagull title", "Title is not the same!"); Models::FilteredArtItemsProxyModel *filteredModel = m_CommandManager->getFilteredArtItemsModel(); metadata->setIsSelected(true); filteredModel->removeMetadataInSelected(); bool doOverwrite = true, dontSaveBackups = false; QObject::connect(ioCoordinator, SIGNAL(metadataWritingFinished()), &waiter, SIGNAL(finished())); artItemsModel->saveSelectedArtworks(QVector<int>() << 0, doOverwrite, dontSaveBackups); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for writing metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while writing"); filteredModel->removeSelectedArtworks(); addedCount = artItemsModel->addLocalArtworks(files); VERIFY(addedCount == files.length(), "Failed to add file after removal"); ioCoordinator->continueReading(true); if (!waiter.wait(20)) { VERIFY(false, "Timeout exceeded for reading metadata."); } VERIFY(!ioCoordinator->getHasErrors(), "Errors in IO Coordinator while reading"); metadata = artItemsModel->getArtwork(0); VERIFY(metadata->getBasicModel()->isDescriptionEmpty(), "Description was not empty"); VERIFY(metadata->getBasicModel()->isTitleEmpty(), "Title was not empty"); VERIFY(metadata->getBasicModel()->areKeywordsEmpty(), "Keywords were not empty"); return 0; }