Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
set(KCOREADDONS_INTERNAL_SKIP_PLUGIN_INSTALLATION ON)
|
||||
add_definitions(-DQT_FORCE_ASSERTS=1)
|
||||
|
||||
include(ECMAddTests)
|
||||
include(ConfigureChecks.cmake) # configure checks for QFileSystemWatcher
|
||||
include(../KF6CoreAddonsMacros.cmake)
|
||||
|
||||
find_package(Qt6Test ${REQUIRED_QT_VERSION} CONFIG QUIET)
|
||||
|
||||
if(NOT TARGET Qt6::Test)
|
||||
message(STATUS "QtTest not found, autotests will not be built.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES "[Dd]ebug$")
|
||||
set(ENABLE_BENCHMARKS 1)
|
||||
endif()
|
||||
|
||||
add_library(autotests_static STATIC)
|
||||
# Needed to link this static lib to shared libs
|
||||
set_property(TARGET autotests_static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
ecm_qt_declare_logging_category(autotests_static
|
||||
HEADER kcoreaddons_debug.h
|
||||
IDENTIFIER KCOREADDONS_DEBUG
|
||||
CATEGORY_NAME kf.coreaddons
|
||||
)
|
||||
target_link_libraries(autotests_static Qt6::Core)
|
||||
|
||||
if(ENABLE_PCH)
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/pch.cpp)
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pch.cpp "/*empty file*/")
|
||||
endif()
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/pch_tmp.h
|
||||
"#pragma once\n"
|
||||
"#include <QObject>\n"
|
||||
"#include <QTest>\n"
|
||||
"#include <QString>\n"
|
||||
"#include <QDateTime>\n"
|
||||
)
|
||||
# avoid rebuilding if there was no change
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_BINARY_DIR}/pch_tmp.h" "${CMAKE_CURRENT_BINARY_DIR}/pch.h")
|
||||
|
||||
add_library(tests_pch STATIC ${CMAKE_CURRENT_BINARY_DIR}/pch.cpp)
|
||||
target_link_libraries(tests_pch Qt6::Core Qt6::Test)
|
||||
target_precompile_headers(tests_pch PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/pch.h)
|
||||
endif()
|
||||
|
||||
|
||||
configure_file(config-tests.h.in config-tests.h)
|
||||
|
||||
add_subdirectory(plugin)
|
||||
|
||||
macro(add_unit_test testname)
|
||||
ecm_add_test(${testname}.cpp ${ARGN}
|
||||
TEST_NAME ${testname}
|
||||
LINK_LIBRARIES Qt6::Test KF6::CoreAddons autotests_static)
|
||||
if(ENABLE_PCH)
|
||||
target_precompile_headers(${testname} REUSE_FROM tests_pch)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
add_unit_test(kaboutdatatest)
|
||||
add_unit_test(kaboutdataapplicationdatatest)
|
||||
add_unit_test(kautosavefiletest)
|
||||
add_unit_test(kcompositejobtest)
|
||||
add_unit_test(kformattest)
|
||||
add_unit_test(kjobtest)
|
||||
add_unit_test(kosreleasetest)
|
||||
add_unit_test(krandomtest)
|
||||
add_unit_test(kshareddatacachetest)
|
||||
add_unit_test(kshelltest)
|
||||
add_unit_test(kurlmimedatatest)
|
||||
add_unit_test(kstringhandlertest)
|
||||
add_unit_test(kmacroexpandertest)
|
||||
add_unit_test(kusertest)
|
||||
add_unit_test(kprocesslisttest)
|
||||
add_unit_test(kfileutilstest)
|
||||
add_unit_test(kfuzzymatchertest)
|
||||
add_unit_test(knetworkmountstestcanonical)
|
||||
add_unit_test(knetworkmountstestnoconfig)
|
||||
add_unit_test(knetworkmountstestpaths)
|
||||
add_unit_test(knetworkmountsteststatic)
|
||||
add_unit_test(klibexectest)
|
||||
add_unit_test(kmemoryinfotest)
|
||||
add_unit_test(kruntimeplatformtest)
|
||||
if (WIN32)
|
||||
add_unit_test(klistopenfilesjobtest_win)
|
||||
endif ()
|
||||
if (UNIX)
|
||||
add_unit_test(klistopenfilesjobtest_unix)
|
||||
endif ()
|
||||
|
||||
if(NOT IOS)
|
||||
add_unit_test(kprocesstest)
|
||||
endif()
|
||||
|
||||
if(TARGET klistopenfilesjobtest_unix AND CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
find_package(Qt6Network ${REQUIRED_QT_VERSION} CONFIG QUIET)
|
||||
target_link_libraries(klistopenfilesjobtest_unix Qt6::Network)
|
||||
endif()
|
||||
|
||||
add_library(ktexttohtmlteststatic STATIC ${CMAKE_SOURCE_DIR}/src/lib/text/ktexttohtml.cpp ${CMAKE_SOURCE_DIR}/src/lib/text/kemoticonsparser.cpp)
|
||||
# include the binary dir in order to get kcoreaddons_export.h
|
||||
target_include_directories(ktexttohtmlteststatic PUBLIC ${KCoreAddons_BINARY_DIR}/src/lib)
|
||||
# fake static linking to prevent the export macros on Windows from kicking in
|
||||
target_compile_definitions(ktexttohtmlteststatic PUBLIC -DKCOREADDONS_STATIC_DEFINE=1)
|
||||
target_link_libraries(ktexttohtmlteststatic PUBLIC Qt6::Test autotests_static)
|
||||
ecm_add_test(ktexttohtmltest.cpp
|
||||
TEST_NAME ktexttohtmltest
|
||||
LINK_LIBRARIES ktexttohtmlteststatic
|
||||
)
|
||||
|
||||
add_executable(ktexttohtmlbenchmarktest ktexttohtmlbenchmarktest.cpp ${CMAKE_SOURCE_DIR}/src/lib/text/ktexttohtml.cpp ${CMAKE_SOURCE_DIR}/src/lib/text/kemoticonsparser.cpp)
|
||||
target_link_libraries(ktexttohtmlbenchmarktest PUBLIC ktexttohtmlteststatic)
|
||||
|
||||
if(NOT IOS)
|
||||
add_executable(kprocesstest_helper kprocesstest_helper.cpp)
|
||||
target_link_libraries(kprocesstest_helper KF6::CoreAddons)
|
||||
endif()
|
||||
|
||||
set(KDIRWATCH_BACKENDS_TO_TEST Stat) #Stat is always compiled
|
||||
|
||||
if (HAVE_SYS_INOTIFY_H)
|
||||
list(APPEND KDIRWATCH_BACKENDS_TO_TEST INotify)
|
||||
endif()
|
||||
|
||||
if (HAVE_QFILESYSTEMWATCHER)
|
||||
list(APPEND KDIRWATCH_BACKENDS_TO_TEST QFSWatch)
|
||||
endif()
|
||||
|
||||
foreach(_backendName ${KDIRWATCH_BACKENDS_TO_TEST})
|
||||
string(TOLOWER ${_backendName} _lowercaseBackendName)
|
||||
set(BACKEND_TEST_TARGET kdirwatch_${_lowercaseBackendName}_unittest)
|
||||
set(BACKEND_BENCHMARK_TARGET kdirwatch_${_lowercaseBackendName}_benchmarktest)
|
||||
add_executable(${BACKEND_TEST_TARGET} kdirwatch_unittest.cpp)
|
||||
target_link_libraries(${BACKEND_TEST_TARGET} Qt6::Test KF6::CoreAddons autotests_static)
|
||||
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(${BACKEND_TEST_TARGET} Threads::Threads)
|
||||
endif()
|
||||
|
||||
ecm_mark_as_test(${BACKEND_TEST_TARGET})
|
||||
add_test(NAME ${BACKEND_TEST_TARGET} COMMAND ${BACKEND_TEST_TARGET})
|
||||
target_compile_definitions(${BACKEND_TEST_TARGET} PUBLIC -DKDIRWATCH_TEST_METHOD=\"${_backendName}\")
|
||||
|
||||
add_executable(${BACKEND_BENCHMARK_TARGET} kdirwatch_benchmarktest.cpp)
|
||||
target_compile_definitions(${BACKEND_BENCHMARK_TARGET} PUBLIC -DKDIRWATCH_TEST_METHOD=\"${_backendName}\")
|
||||
target_link_libraries(${BACKEND_BENCHMARK_TARGET} Qt6::Test KF6::CoreAddons autotests_static)
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(${BACKEND_BENCHMARK_TARGET} Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(ENABLE_PCH AND NOT WIN32)
|
||||
target_precompile_headers(${BACKEND_TEST_TARGET} REUSE_FROM tests_pch)
|
||||
target_precompile_headers(${BACKEND_BENCHMARK_TARGET} REUSE_FROM tests_pch)
|
||||
endif()
|
||||
|
||||
endforeach()
|
||||
@@ -0,0 +1,9 @@
|
||||
set(CMAKE_REQUIRED_LIBRARIES Qt6::Core)
|
||||
check_cxx_source_compiles(
|
||||
"#include <QtCore/QFileSystemWatcher>
|
||||
int main()
|
||||
{
|
||||
QFileSystemWatcher *watcher = new QFileSystemWatcher();
|
||||
delete watcher;
|
||||
return 0;
|
||||
}" HAVE_QFILESYSTEMWATCHER)
|
||||
@@ -0,0 +1 @@
|
||||
#cmakedefine01 ENABLE_BENCHMARKS
|
||||
@@ -0,0 +1,22 @@
|
||||
NAME="Name"
|
||||
VERSION="100.5"
|
||||
ID=theid
|
||||
ID_LIKE="otherid otherotherid"
|
||||
VERSION_CODENAME=versioncodename
|
||||
VERSION_ID="500.1"
|
||||
PRETTY_NAME="Vicuña — Pretty Name #1"
|
||||
ANSI_COLOR="1;34"
|
||||
CPE_NAME="cpe:/o:foo:bar:100"
|
||||
HOME_URL="https://url.home"
|
||||
DOCUMENTATION_URL="https://url.docs"
|
||||
SUPPORT_URL="https://url.support"
|
||||
BUG_REPORT_URL="https://url.bugs"
|
||||
PRIVACY_POLICY_URL="https://url.privacy"
|
||||
BUILD_ID="105.5"
|
||||
# comment
|
||||
VARIANT="Test = Edition"
|
||||
BROKENLINE_SHOULD_BE_IGNORED
|
||||
VARIANT_ID=test
|
||||
# indented comment
|
||||
LOGO=start-here-test
|
||||
DEBIAN_BTS="debbugs://bugs.debian.org/"
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2016 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
// test object
|
||||
#include <kaboutdata.h>
|
||||
// Qt
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
// Separate test for reading & setting applicationData
|
||||
// to ensure a separate process where no other test case has
|
||||
// directly or indirectly called KAboutData::setApplicationData before
|
||||
// and thus created the global KAboutData object
|
||||
class KAboutDataApplicationDataTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testInteractionWithQApplicationData();
|
||||
};
|
||||
|
||||
static const char AppName[] = "app";
|
||||
static const char ProgramName[] = "ProgramName";
|
||||
static const char Version[] = "Version";
|
||||
static const char OrganizationDomain[] = "no.where";
|
||||
static const char DesktopFileName[] = "org.kde.someapp";
|
||||
|
||||
static const char AppName2[] = "otherapp";
|
||||
static const char ProgramName2[] = "OtherProgramName";
|
||||
static const char Version2[] = "OtherVersion";
|
||||
static const char OrganizationDomain2[] = "other.no.where";
|
||||
static const char DesktopFileName2[] = "org.kde.otherapp";
|
||||
|
||||
void KAboutDataApplicationDataTest::testInteractionWithQApplicationData()
|
||||
{
|
||||
// init the app metadata the Qt way
|
||||
QCoreApplication *app = QCoreApplication::instance();
|
||||
app->setApplicationName(QLatin1String(AppName));
|
||||
app->setProperty("applicationDisplayName", QLatin1String(ProgramName));
|
||||
app->setApplicationVersion(QLatin1String(Version));
|
||||
app->setOrganizationDomain(QLatin1String(OrganizationDomain));
|
||||
app->setProperty("desktopFileName", QLatin1String(DesktopFileName));
|
||||
|
||||
// without setting before, get KAboutData::applicationData
|
||||
const KAboutData applicationAboutData = KAboutData::applicationData();
|
||||
|
||||
// should be initialized with Q*Application metadata
|
||||
QCOMPARE(applicationAboutData.componentName(), QLatin1String(AppName));
|
||||
QCOMPARE(applicationAboutData.displayName(), QLatin1String(ProgramName));
|
||||
QCOMPARE(applicationAboutData.organizationDomain(), QLatin1String(OrganizationDomain));
|
||||
QCOMPARE(applicationAboutData.version(), QLatin1String(Version));
|
||||
QCOMPARE(applicationAboutData.desktopFileName(), QLatin1String(DesktopFileName));
|
||||
|
||||
// now set some new KAboutData, with different values
|
||||
KAboutData aboutData2(QString::fromLatin1(AppName2), QString::fromLatin1(ProgramName2), QString::fromLatin1(Version2));
|
||||
aboutData2.setOrganizationDomain(OrganizationDomain2);
|
||||
aboutData2.setDesktopFileName(QLatin1String(DesktopFileName2));
|
||||
|
||||
KAboutData::setApplicationData(aboutData2);
|
||||
|
||||
// check that Q*Application metadata has been updated, as expected per API definition
|
||||
QCOMPARE(app->applicationName(), QLatin1String(AppName2));
|
||||
QCOMPARE(app->property("applicationDisplayName").toString(), QLatin1String(ProgramName2));
|
||||
QCOMPARE(app->organizationDomain(), QLatin1String(OrganizationDomain2));
|
||||
QCOMPARE(app->applicationVersion(), QLatin1String(Version2));
|
||||
QCOMPARE(app->property("desktopFileName").toString(), QLatin1String(DesktopFileName2));
|
||||
|
||||
// and check as well KAboutData::applicationData itself
|
||||
const KAboutData applicationAboutData2 = KAboutData::applicationData();
|
||||
|
||||
QCOMPARE(applicationAboutData2.componentName(), QLatin1String(AppName2));
|
||||
QCOMPARE(applicationAboutData2.displayName(), QLatin1String(ProgramName2));
|
||||
QCOMPARE(applicationAboutData2.organizationDomain(), QLatin1String(OrganizationDomain2));
|
||||
QCOMPARE(applicationAboutData2.version(), QLatin1String(Version2));
|
||||
QCOMPARE(applicationAboutData2.desktopFileName(), QLatin1String(DesktopFileName2));
|
||||
}
|
||||
|
||||
QTEST_MAIN(KAboutDataApplicationDataTest)
|
||||
|
||||
#include "kaboutdataapplicationdatatest.moc"
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||
SPDX-FileCopyrightText: 2017 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
// test object
|
||||
#include <kaboutdata.h>
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QLatin1String>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
#include <QTextStream>
|
||||
#ifndef Q_OS_WIN
|
||||
void initLocale()
|
||||
{
|
||||
qputenv("LC_ALL", "en_US.utf-8");
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initLocale)
|
||||
#endif
|
||||
|
||||
class KAboutDataTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testLongFormConstructorWithDefaults();
|
||||
void testLongFormConstructor();
|
||||
void testShortFormConstructor();
|
||||
void testSetAddLicense();
|
||||
void testSetDesktopFileName();
|
||||
void testCopying();
|
||||
|
||||
void testKAboutDataOrganizationDomain();
|
||||
|
||||
void testLicenseSPDXID();
|
||||
void testLicenseOrLater();
|
||||
|
||||
void testProductName();
|
||||
};
|
||||
|
||||
static const char AppName[] = "app";
|
||||
static const char ProgramName[] = "ProgramName";
|
||||
static const char Version[] = "Version";
|
||||
static const char ShortDescription[] = "ShortDescription";
|
||||
static const char CopyrightStatement[] = "CopyrightStatement";
|
||||
static const char Text[] = "Text";
|
||||
static const char HomePageAddress[] = "http://test.no.where/";
|
||||
static const char HomePageSecure[] = "https://test.no.where/";
|
||||
static const char OrganizationDomain[] = "no.where";
|
||||
static const char BugsEmailAddress[] = "bugs@no.else";
|
||||
static const char LicenseText[] = "free to write, reading forbidden";
|
||||
static const char LicenseFileName[] = "testlicensefile";
|
||||
static const char LicenseFileText[] = "free to write, reading forbidden, in the file";
|
||||
|
||||
void KAboutDataTest::testLongFormConstructorWithDefaults()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::Unknown);
|
||||
|
||||
QCOMPARE(aboutData.componentName(), QString::fromLatin1(AppName));
|
||||
QCOMPARE(aboutData.productName(), QString::fromLatin1(AppName));
|
||||
QCOMPARE(aboutData.displayName(), QLatin1String(ProgramName));
|
||||
QCOMPARE(aboutData.programLogo(), QVariant());
|
||||
QCOMPARE(aboutData.organizationDomain(), QString::fromLatin1("kde.org"));
|
||||
QCOMPARE(aboutData.version(), QString::fromLatin1(Version));
|
||||
QCOMPARE(aboutData.homepage(), QString());
|
||||
QCOMPARE(aboutData.bugAddress(), QString::fromLatin1("submit@bugs.kde.org"));
|
||||
QVERIFY(aboutData.authors().isEmpty());
|
||||
QVERIFY(aboutData.credits().isEmpty());
|
||||
QVERIFY(aboutData.translators().isEmpty());
|
||||
QCOMPARE(aboutData.otherText(), QString());
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
// We don't know the default text, do we?
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::ShortName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::FullName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::FullName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
QCOMPARE(aboutData.copyrightStatement(), QString());
|
||||
QCOMPARE(aboutData.shortDescription(), (QLatin1String(ShortDescription)));
|
||||
QCOMPARE(aboutData.customAuthorPlainText(), QString());
|
||||
QCOMPARE(aboutData.customAuthorRichText(), QString());
|
||||
QVERIFY(!aboutData.customAuthorTextEnabled());
|
||||
QCOMPARE(aboutData.desktopFileName(), QStringLiteral("org.kde.app"));
|
||||
|
||||
QCOMPARE(aboutData.internalVersion(), Version);
|
||||
QCOMPARE(aboutData.internalProgramName(), ProgramName);
|
||||
QCOMPARE(aboutData.internalBugAddress(), "submit@bugs.kde.org");
|
||||
QCOMPARE(aboutData.internalProductName(), nullptr);
|
||||
}
|
||||
|
||||
void KAboutDataTest::testLongFormConstructor()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::Unknown,
|
||||
QLatin1String(CopyrightStatement),
|
||||
QLatin1String(Text),
|
||||
QString::fromLatin1(HomePageAddress),
|
||||
QString::fromLatin1(BugsEmailAddress));
|
||||
|
||||
QCOMPARE(aboutData.componentName(), QLatin1String(AppName));
|
||||
QCOMPARE(aboutData.productName(), QLatin1String(AppName));
|
||||
QCOMPARE(aboutData.displayName(), QLatin1String(ProgramName));
|
||||
QCOMPARE(aboutData.programLogo(), QVariant());
|
||||
QCOMPARE(aboutData.organizationDomain(), QString::fromLatin1(OrganizationDomain));
|
||||
QCOMPARE(aboutData.version(), QString::fromLatin1(Version));
|
||||
QCOMPARE(aboutData.homepage(), QString::fromLatin1(HomePageAddress));
|
||||
QCOMPARE(aboutData.bugAddress(), QString::fromLatin1(BugsEmailAddress));
|
||||
QVERIFY(aboutData.authors().isEmpty());
|
||||
QVERIFY(aboutData.credits().isEmpty());
|
||||
QVERIFY(aboutData.translators().isEmpty());
|
||||
QCOMPARE(aboutData.otherText(), QLatin1String(Text));
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
// We don't know the default text, do we?
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::ShortName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::FullName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::FullName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
QCOMPARE(aboutData.copyrightStatement(), QLatin1String(CopyrightStatement));
|
||||
QCOMPARE(aboutData.shortDescription(), QLatin1String(ShortDescription));
|
||||
QCOMPARE(aboutData.customAuthorPlainText(), QString());
|
||||
QCOMPARE(aboutData.customAuthorRichText(), QString());
|
||||
QVERIFY(!aboutData.customAuthorTextEnabled());
|
||||
QCOMPARE(aboutData.desktopFileName(), QStringLiteral("where.no.app"));
|
||||
|
||||
QCOMPARE(aboutData.internalVersion(), Version);
|
||||
QCOMPARE(aboutData.internalProgramName(), ProgramName);
|
||||
QCOMPARE(aboutData.internalBugAddress(), BugsEmailAddress);
|
||||
QCOMPARE(aboutData.internalProductName(), nullptr);
|
||||
|
||||
aboutData.addAuthor(QStringLiteral("John"),
|
||||
QStringLiteral("Stuff"),
|
||||
QStringLiteral("john@john.com"),
|
||||
QStringLiteral("john.com"),
|
||||
QStringLiteral("ocsjohn"));
|
||||
QCOMPARE(aboutData.authors().length(), 1);
|
||||
QCOMPARE(aboutData.credits().length(), 0);
|
||||
QCOMPARE(aboutData.authors().at(0).avatarUrl(), QUrl(QStringLiteral("https://store.kde.org/avatar/ocsjohn")));
|
||||
|
||||
// We support http and https protocols on the homepage address, ensure they
|
||||
// give the same org. domain and desktop file name.
|
||||
KAboutData aboutDataSecure(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::Unknown,
|
||||
QLatin1String(CopyrightStatement),
|
||||
QLatin1String(Text),
|
||||
QString::fromLatin1(HomePageSecure),
|
||||
QString::fromLatin1(BugsEmailAddress));
|
||||
QCOMPARE(aboutDataSecure.componentName(), QLatin1String(AppName));
|
||||
QCOMPARE(aboutDataSecure.productName(), QLatin1String(AppName));
|
||||
QCOMPARE(aboutDataSecure.organizationDomain(), QString::fromLatin1(OrganizationDomain));
|
||||
QCOMPARE(aboutDataSecure.desktopFileName(), QStringLiteral("where.no.app"));
|
||||
}
|
||||
|
||||
void KAboutDataTest::testShortFormConstructor()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName), QLatin1String(ProgramName), QString::fromLatin1(Version));
|
||||
|
||||
QCOMPARE(aboutData.componentName(), QString::fromLatin1(AppName));
|
||||
QCOMPARE(aboutData.productName(), QString::fromLatin1(AppName));
|
||||
QCOMPARE(aboutData.displayName(), QLatin1String(ProgramName));
|
||||
QCOMPARE(aboutData.programLogo(), QVariant());
|
||||
QCOMPARE(aboutData.organizationDomain(), QString::fromLatin1("kde.org"));
|
||||
QCOMPARE(aboutData.version(), QString::fromLatin1(Version));
|
||||
QCOMPARE(aboutData.homepage(), QString());
|
||||
QCOMPARE(aboutData.bugAddress(), QString::fromLatin1("submit@bugs.kde.org"));
|
||||
QVERIFY(aboutData.authors().isEmpty());
|
||||
QVERIFY(aboutData.credits().isEmpty());
|
||||
QVERIFY(aboutData.translators().isEmpty());
|
||||
QCOMPARE(aboutData.otherText(), QString());
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
// We don't know the default text, do we?
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::ShortName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::FullName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::FullName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
QCOMPARE(aboutData.copyrightStatement(), QString());
|
||||
QCOMPARE(aboutData.shortDescription(), QString());
|
||||
QCOMPARE(aboutData.customAuthorPlainText(), QString());
|
||||
QCOMPARE(aboutData.customAuthorRichText(), QString());
|
||||
QVERIFY(!aboutData.customAuthorTextEnabled());
|
||||
QCOMPARE(aboutData.desktopFileName(), QStringLiteral("org.kde.app"));
|
||||
|
||||
QCOMPARE(aboutData.internalVersion(), Version);
|
||||
QCOMPARE(aboutData.internalProgramName(), ProgramName);
|
||||
QCOMPARE(aboutData.internalBugAddress(), "submit@bugs.kde.org");
|
||||
QCOMPARE(aboutData.internalProductName(), nullptr);
|
||||
}
|
||||
|
||||
void KAboutDataTest::testKAboutDataOrganizationDomain()
|
||||
{
|
||||
KAboutData data(QString::fromLatin1("app"),
|
||||
QLatin1String("program"),
|
||||
QString::fromLatin1("version"),
|
||||
QLatin1String("description"),
|
||||
KAboutLicense::LGPL,
|
||||
QLatin1String("copyright"),
|
||||
QLatin1String("hello world"),
|
||||
QStringLiteral("http://www.koffice.org"));
|
||||
QCOMPARE(data.organizationDomain(), QString::fromLatin1("koffice.org"));
|
||||
QCOMPARE(data.desktopFileName(), QStringLiteral("org.koffice.app"));
|
||||
|
||||
KAboutData data2(QString::fromLatin1("app"),
|
||||
QLatin1String("program"),
|
||||
QString::fromLatin1("version"),
|
||||
QLatin1String("description"),
|
||||
KAboutLicense::LGPL,
|
||||
QString::fromLatin1("copyright"),
|
||||
QLatin1String("hello world"),
|
||||
QStringLiteral("app"));
|
||||
QCOMPARE(data2.organizationDomain(), QString::fromLatin1("kde.org"));
|
||||
QCOMPARE(data2.desktopFileName(), QStringLiteral("org.kde.app"));
|
||||
}
|
||||
|
||||
void KAboutDataTest::testSetAddLicense()
|
||||
{
|
||||
// prepare a file with a license text
|
||||
QFile licenseFile(QString::fromLatin1(LicenseFileName));
|
||||
licenseFile.open(QIODevice::WriteOnly);
|
||||
QTextStream licenseFileStream(&licenseFile);
|
||||
licenseFileStream << LicenseFileText;
|
||||
licenseFile.close();
|
||||
|
||||
const QString lineFeed = QString::fromLatin1("\n\n");
|
||||
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::Unknown,
|
||||
QLatin1String(CopyrightStatement),
|
||||
QLatin1String(Text),
|
||||
QString::fromLatin1(HomePageAddress),
|
||||
QString::fromLatin1(BugsEmailAddress));
|
||||
|
||||
// set to GPL2
|
||||
aboutData.setLicense(KAboutLicense::GPL_V2);
|
||||
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString::fromLatin1("GPL v2"));
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::FullName), QString::fromLatin1("GNU General Public License Version 2"));
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(GPL2Text) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
|
||||
// set to Unknown again
|
||||
aboutData.setLicense(KAboutLicense::Unknown);
|
||||
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
// We don't know the default text, do we?
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::ShortName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).name(KAboutLicense::FullName), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).name(KAboutLicense::FullName).isEmpty());
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(WarningText) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
|
||||
// add GPL3
|
||||
aboutData.addLicense(KAboutLicense::GPL_V3);
|
||||
|
||||
QCOMPARE(aboutData.licenses().count(), 1);
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString::fromLatin1("GPL v3"));
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::FullName), QString::fromLatin1("GNU General Public License Version 3"));
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(GPL3Text) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
|
||||
// add GPL2, Custom and File
|
||||
aboutData.addLicense(KAboutLicense::GPL_V2);
|
||||
aboutData.addLicenseText(QLatin1String(LicenseText));
|
||||
aboutData.addLicenseTextFile(QLatin1String(LicenseFileName));
|
||||
|
||||
QCOMPARE(aboutData.licenses().count(), 4);
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::ShortName), QString::fromLatin1("GPL v3"));
|
||||
QCOMPARE(aboutData.licenses().at(0).name(KAboutLicense::FullName), QString::fromLatin1("GNU General Public License Version 3"));
|
||||
// QCOMPARE( aboutData.licenses().at(0).text(), QString(GPL3Text) );
|
||||
QVERIFY(!aboutData.licenses().at(0).text().isEmpty());
|
||||
QCOMPARE(aboutData.licenses().at(1).name(KAboutLicense::ShortName), QString::fromLatin1("GPL v2"));
|
||||
QCOMPARE(aboutData.licenses().at(1).name(KAboutLicense::FullName), QString::fromLatin1("GNU General Public License Version 2"));
|
||||
// QCOMPARE( aboutData.licenses().at(1).text(), QString(GPL2Text) );
|
||||
QVERIFY(!aboutData.licenses().at(1).text().isEmpty());
|
||||
QCOMPARE(aboutData.licenses().at(2).name(KAboutLicense::ShortName), QString::fromLatin1("Custom"));
|
||||
QCOMPARE(aboutData.licenses().at(2).name(KAboutLicense::FullName), QString::fromLatin1("Custom"));
|
||||
QCOMPARE(aboutData.licenses().at(2).text(), QLatin1String(LicenseText));
|
||||
QCOMPARE(aboutData.licenses().at(3).name(KAboutLicense::ShortName), QString::fromLatin1("Custom"));
|
||||
QCOMPARE(aboutData.licenses().at(3).name(KAboutLicense::FullName), QString::fromLatin1("Custom"));
|
||||
QCOMPARE(aboutData.licenses().at(3).text(), QLatin1String(LicenseFileText));
|
||||
}
|
||||
|
||||
void KAboutDataTest::testCopying()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::GPL_V2);
|
||||
|
||||
{
|
||||
KAboutData aboutData2(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::GPL_V3);
|
||||
aboutData2.addLicense(KAboutLicense::GPL_V2, KAboutLicense::OrLaterVersions);
|
||||
aboutData = aboutData2;
|
||||
}
|
||||
QList<KAboutLicense> licenses = aboutData.licenses();
|
||||
QCOMPARE(licenses.count(), 2);
|
||||
QCOMPARE(licenses.at(0).key(), KAboutLicense::GPL_V3);
|
||||
QCOMPARE(aboutData.licenses().at(0).spdx(), QStringLiteral("GPL-3.0"));
|
||||
// check it doesn't crash
|
||||
QVERIFY(!licenses.at(0).text().isEmpty());
|
||||
QCOMPARE(licenses.at(1).key(), KAboutLicense::GPL_V2);
|
||||
QCOMPARE(aboutData.licenses().at(1).spdx(), QStringLiteral("GPL-2.0+"));
|
||||
// check it doesn't crash
|
||||
QVERIFY(!licenses.at(1).text().isEmpty());
|
||||
}
|
||||
|
||||
void KAboutDataTest::testSetDesktopFileName()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::Unknown);
|
||||
QCOMPARE(aboutData.desktopFileName(), QStringLiteral("org.kde.app"));
|
||||
|
||||
// set different desktopFileName
|
||||
aboutData.setDesktopFileName(QStringLiteral("foo.bar.application"));
|
||||
QCOMPARE(aboutData.desktopFileName(), QStringLiteral("foo.bar.application"));
|
||||
}
|
||||
|
||||
void KAboutDataTest::testLicenseSPDXID()
|
||||
{
|
||||
// Input with + should output with +.
|
||||
auto license = KAboutLicense::byKeyword(QStringLiteral("GPLv2+"));
|
||||
QCOMPARE(license.spdx(), QStringLiteral("GPL-2.0+"));
|
||||
// Input without should output without.
|
||||
license = KAboutLicense::byKeyword(QStringLiteral("GPLv2"));
|
||||
QCOMPARE(license.spdx(), QStringLiteral("GPL-2.0"));
|
||||
// Input of spdx with or-later should also work
|
||||
license = KAboutLicense::byKeyword(QStringLiteral("GPL-2.0-or-later"));
|
||||
QCOMPARE(license.spdx(), QStringLiteral("GPL-2.0+")); // TODO: should not return the deprecatd version
|
||||
|
||||
// we should be able to match by spdx too
|
||||
// create a KAboutLicense from enum, then make sure going to spdx and back gives the same enum
|
||||
for (int i = 1; i <= KAboutLicense::LGPL_V2_1; ++i) { /*current highest enum value*/
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::GPL_V2);
|
||||
aboutData.setLicense(KAboutLicense::LicenseKey(i));
|
||||
QVERIFY(aboutData.licenses().count() == 1);
|
||||
const auto license = aboutData.licenses().constFirst();
|
||||
auto licenseFromKeyword = KAboutLicense::byKeyword(license.spdx());
|
||||
|
||||
QCOMPARE(license.key(), licenseFromKeyword.key());
|
||||
}
|
||||
}
|
||||
|
||||
void KAboutDataTest::testLicenseOrLater()
|
||||
{
|
||||
// For kaboutdata we can replace the license with an orLater version. Or add a second one.
|
||||
KAboutData aboutData(QString::fromLatin1(AppName),
|
||||
QLatin1String(ProgramName),
|
||||
QString::fromLatin1(Version),
|
||||
QLatin1String(ShortDescription),
|
||||
KAboutLicense::GPL_V2);
|
||||
QCOMPARE(aboutData.licenses().at(0).spdx(), QStringLiteral("GPL-2.0"));
|
||||
aboutData.setLicense(KAboutLicense::GPL_V2, KAboutLicense::OrLaterVersions);
|
||||
QCOMPARE(aboutData.licenses().at(0).spdx(), QStringLiteral("GPL-2.0+"));
|
||||
aboutData.addLicense(KAboutLicense::LGPL_V3, KAboutLicense::OrLaterVersions);
|
||||
bool foundLGPL = false;
|
||||
const QList<KAboutLicense> licenses = aboutData.licenses();
|
||||
for (const auto &license : licenses) {
|
||||
if (license.key() == KAboutLicense::LGPL_V3) {
|
||||
QCOMPARE(license.spdx(), QStringLiteral("LGPL-3.0+"));
|
||||
foundLGPL = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QCOMPARE(foundLGPL, true);
|
||||
}
|
||||
|
||||
void KAboutDataTest::testProductName()
|
||||
{
|
||||
KAboutData aboutData(QString::fromLatin1(AppName), QString::fromLatin1(ProgramName));
|
||||
QCOMPARE(aboutData.productName(), QString::fromLatin1(AppName));
|
||||
QCOMPARE(aboutData.internalProductName(), nullptr);
|
||||
aboutData.setProductName("frameworks-kcoreaddons/aboutdata");
|
||||
QCOMPARE(aboutData.productName(), QString::fromLatin1("frameworks-kcoreaddons/aboutdata"));
|
||||
QCOMPARE(aboutData.internalProductName(), "frameworks-kcoreaddons/aboutdata");
|
||||
}
|
||||
|
||||
QTEST_MAIN(KAboutDataTest)
|
||||
|
||||
#include "kaboutdatatest.moc"
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006 Jacob R Rideout <kde@jacobrideout.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kautosavefiletest.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#include <QTemporaryFile>
|
||||
#include <kautosavefile.h>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KAutoSaveFileTest)
|
||||
|
||||
void KAutoSaveFileTest::initTestCase()
|
||||
{
|
||||
QCoreApplication::instance()->setApplicationName(QLatin1String("qttest")); // TODO do this in qtestlib itself
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::cleanupTestCase()
|
||||
{
|
||||
for (const QString &fileToRemove : std::as_const(filesToRemove)) {
|
||||
QFile::remove(fileToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::test_readWrite()
|
||||
{
|
||||
QTemporaryFile file;
|
||||
|
||||
QVERIFY(file.open());
|
||||
|
||||
QUrl normalFile = QUrl::fromLocalFile(QFileInfo(file).absoluteFilePath());
|
||||
|
||||
// Test basic functionality
|
||||
KAutoSaveFile saveFile(normalFile);
|
||||
|
||||
QVERIFY(!QFile::exists(saveFile.fileName()));
|
||||
QVERIFY(saveFile.open(QIODevice::ReadWrite));
|
||||
|
||||
QString inText = QString::fromLatin1("This is test data one.\n");
|
||||
|
||||
{
|
||||
QTextStream ts(&saveFile);
|
||||
ts << inText;
|
||||
ts.flush();
|
||||
}
|
||||
|
||||
saveFile.close();
|
||||
|
||||
{
|
||||
QFile testReader(saveFile.fileName());
|
||||
testReader.open(QIODevice::ReadWrite);
|
||||
QTextStream ts(&testReader);
|
||||
|
||||
QString outText = ts.readAll();
|
||||
|
||||
QCOMPARE(outText, inText);
|
||||
}
|
||||
|
||||
filesToRemove << file.fileName();
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::test_fileNameMaxLength()
|
||||
{
|
||||
// In KAutoSaveFilePrivate::tempFile() the name of the kautosavefile that's going to be created
|
||||
// is concatanated in the form:
|
||||
// fileName + junk.truncated + protocol + _ + path.truncated + junk
|
||||
// see tempFile() for details.
|
||||
//
|
||||
// Make sure that the generated filename (e.g. as you would get from QUrl::fileName()) doesn't
|
||||
// exceed NAME_MAX (the maximum length allowed for filenames, see e.g. /usr/include/linux/limits.h)
|
||||
// otherwise the file can't be opened.
|
||||
//
|
||||
// see https://phabricator.kde.org/D24489
|
||||
|
||||
QString s;
|
||||
s.fill(QLatin1Char('b'), 80);
|
||||
// create a long path that:
|
||||
// - exceeds NAME_MAX (255)
|
||||
// - is less than the maximum allowed path length, PATH_MAX (4096)
|
||||
// see e.g. /usr/include/linux/limits.h
|
||||
const QString path = QDir::tempPath() + QLatin1Char('/') + s + QLatin1Char('/') + s + QLatin1Char('/') + s + QLatin1Char('/') + s;
|
||||
|
||||
QFile file(path + QLatin1Char('/') + QLatin1String("testFile.txt"));
|
||||
|
||||
QUrl normalFile = QUrl::fromLocalFile(file.fileName());
|
||||
|
||||
KAutoSaveFile saveFile(normalFile);
|
||||
|
||||
QVERIFY(!QFile::exists(saveFile.fileName()));
|
||||
QVERIFY(saveFile.open(QIODevice::ReadWrite));
|
||||
|
||||
filesToRemove << file.fileName();
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::test_fileStaleFiles()
|
||||
{
|
||||
QUrl normalFile = QUrl::fromLocalFile(QDir::temp().absoluteFilePath(QStringLiteral("test directory/tîst me.txt")));
|
||||
|
||||
KAutoSaveFile saveFile(normalFile);
|
||||
QVERIFY(saveFile.open(QIODevice::ReadWrite));
|
||||
saveFile.write("testdata");
|
||||
|
||||
// Make sure the stale file is found
|
||||
|
||||
const auto listOfStaleFiles = saveFile.staleFiles(normalFile, QStringLiteral("qttest"));
|
||||
QVERIFY(listOfStaleFiles.count() == 1);
|
||||
saveFile.releaseLock();
|
||||
qDeleteAll(listOfStaleFiles);
|
||||
|
||||
// Make sure the stale file is deleted
|
||||
|
||||
QVERIFY(saveFile.staleFiles(normalFile, QStringLiteral("qttest")).isEmpty());
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::test_applicationStaleFiles()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void KAutoSaveFileTest::test_locking()
|
||||
{
|
||||
QUrl normalFile(QString::fromLatin1("fish://user@example.com/home/remote/test.txt"));
|
||||
|
||||
KAutoSaveFile saveFile(normalFile);
|
||||
|
||||
QVERIFY(!QFile::exists(saveFile.fileName()));
|
||||
QVERIFY(saveFile.open(QIODevice::ReadWrite));
|
||||
|
||||
const QList<KAutoSaveFile *> staleFiles(KAutoSaveFile::staleFiles(normalFile));
|
||||
|
||||
QVERIFY(!staleFiles.isEmpty());
|
||||
|
||||
KAutoSaveFile *saveFile2 = staleFiles.at(0);
|
||||
|
||||
const QString fn = saveFile2->fileName();
|
||||
// It looks like $XDG_DATA_HOME/stalefiles/qttest/test.txtXXXfish_%2Fhome%2FremoteXXXXXXX
|
||||
QVERIFY2(fn.contains(QLatin1String("stalefiles/qttest/test.txt")), qPrintable(fn));
|
||||
QVERIFY2(fn.contains(QLatin1String("fish_%2Fhome%2Fremote")), qPrintable(fn));
|
||||
|
||||
QVERIFY(QFile::exists(saveFile2->fileName()));
|
||||
QVERIFY(!saveFile2->open(QIODevice::ReadWrite));
|
||||
|
||||
saveFile.releaseLock();
|
||||
|
||||
QVERIFY(saveFile2->open(QIODevice::ReadWrite));
|
||||
|
||||
qDeleteAll(staleFiles);
|
||||
}
|
||||
|
||||
#include "moc_kautosavefiletest.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006 Jacob R Rideout <kde@jacobrideout.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef kautosavefiletest_h
|
||||
#define kautosavefiletest_h
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class KAutoSaveFileTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void test_readWrite();
|
||||
void test_fileNameMaxLength();
|
||||
void test_fileStaleFiles();
|
||||
void test_applicationStaleFiles();
|
||||
void test_locking();
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
QStringList filesToRemove;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2013 Kevin Funk <kevin@kfunk.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kcompositejobtest.h"
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QTimer>
|
||||
|
||||
TestJob::TestJob(QObject *parent)
|
||||
: KJob(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void TestJob::start()
|
||||
{
|
||||
QTimer::singleShot(1000, this, &TestJob::doEmit);
|
||||
}
|
||||
|
||||
void TestJob::doEmit()
|
||||
{
|
||||
emitResult();
|
||||
}
|
||||
|
||||
void CompositeJob::start()
|
||||
{
|
||||
if (hasSubjobs()) {
|
||||
subjobs().first()->start();
|
||||
} else {
|
||||
emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
bool CompositeJob::addSubjob(KJob *job)
|
||||
{
|
||||
return KCompositeJob::addSubjob(job);
|
||||
}
|
||||
|
||||
void CompositeJob::slotResult(KJob *job)
|
||||
{
|
||||
KCompositeJob::slotResult(job);
|
||||
|
||||
if (!error() && hasSubjobs()) {
|
||||
// start next
|
||||
subjobs().first()->start();
|
||||
} else {
|
||||
setError(job->error());
|
||||
setErrorText(job->errorText());
|
||||
emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
KCompositeJobTest::KCompositeJobTest()
|
||||
: loop(this)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* In case a composite job is deleted during execution
|
||||
* we still want to assure that we don't crash
|
||||
*
|
||||
* see bug: https://bugs.kde.org/show_bug.cgi?id=230692
|
||||
*/
|
||||
void KCompositeJobTest::testDeletionDuringExecution()
|
||||
{
|
||||
QObject *someParent = new QObject;
|
||||
KJob *job = new TestJob(someParent);
|
||||
|
||||
CompositeJob *compositeJob = new CompositeJob;
|
||||
compositeJob->setAutoDelete(false);
|
||||
QVERIFY(compositeJob->addSubjob(job));
|
||||
|
||||
QCOMPARE(job->parent(), compositeJob);
|
||||
|
||||
QSignalSpy destroyed_spy(job, &QObject::destroyed);
|
||||
// check if job got reparented properly
|
||||
delete someParent;
|
||||
someParent = nullptr;
|
||||
// the job should still exist, because it is a child of KCompositeJob now
|
||||
QCOMPARE(destroyed_spy.size(), 0);
|
||||
|
||||
// start async, the subjob takes 1 second to finish
|
||||
compositeJob->start();
|
||||
|
||||
// delete the job during the execution
|
||||
delete compositeJob;
|
||||
compositeJob = nullptr;
|
||||
// at this point, the subjob should be deleted, too
|
||||
QCOMPARE(destroyed_spy.size(), 1);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(KCompositeJobTest)
|
||||
|
||||
#include "moc_kcompositejobtest.cpp"
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2013 Kevin Funk <kevin@kfunk.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KCOMPOSITEJOBTEST_H
|
||||
#define KCOMPOSITEJOBTEST_H
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QObject>
|
||||
|
||||
#include "kcompositejob.h"
|
||||
|
||||
class TestJob : public KJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TestJob(QObject *parent = nullptr);
|
||||
|
||||
/// Takes 1 second to finish
|
||||
void start() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void doEmit();
|
||||
};
|
||||
|
||||
class CompositeJob : public KCompositeJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CompositeJob(QObject *parent = nullptr)
|
||||
: KCompositeJob(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void start() override;
|
||||
bool addSubjob(KJob *job) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
};
|
||||
|
||||
class KCompositeJobTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KCompositeJobTest();
|
||||
|
||||
private Q_SLOTS:
|
||||
void testDeletionDuringExecution();
|
||||
|
||||
private:
|
||||
QEventLoop loop;
|
||||
};
|
||||
|
||||
#endif // KCOMPOSITEJOBTEST_H
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <kdirwatch.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QSignalSpy>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
#include <sys/stat.h>
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h> // ::link()
|
||||
#endif
|
||||
|
||||
#include "config-tests.h"
|
||||
#include "kcoreaddons_debug.h"
|
||||
|
||||
#include "kdirwatch_test_utils.h"
|
||||
|
||||
using namespace KDirWatchTestUtils;
|
||||
|
||||
class KDirWatch_UnitTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KDirWatch_UnitTest()
|
||||
{
|
||||
// Speed up the test by making the kdirwatch timer (to compress changes) faster
|
||||
qputenv("KDIRWATCH_POLLINTERVAL", "50");
|
||||
qputenv("KDIRWATCH_METHOD", KDIRWATCH_TEST_METHOD);
|
||||
s_staticObjectUsingSelf();
|
||||
|
||||
m_path = m_tempDir.path() + QLatin1Char('/');
|
||||
KDirWatch *dirW = &s_staticObject()->m_dirWatch;
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Using method" << methodToString(dirW->internalMethod());
|
||||
}
|
||||
|
||||
private Q_SLOTS: // test methods
|
||||
void initTestCase()
|
||||
{
|
||||
QFileInfo pathInfo(m_path);
|
||||
QVERIFY(pathInfo.isDir() && pathInfo.isWritable());
|
||||
|
||||
// By creating the files upfront, we save waiting a full second for an mtime change
|
||||
createFile(m_path + QLatin1String("ExistingFile"));
|
||||
createFile(m_path + QLatin1String("TestFile"));
|
||||
createFile(m_path + QLatin1String("nested_0"));
|
||||
createFile(m_path + QLatin1String("nested_1"));
|
||||
|
||||
s_staticObject()->m_dirWatch.addFile(m_path + QLatin1String("ExistingFile"));
|
||||
}
|
||||
void benchCreateTree();
|
||||
void benchCreateWatcher();
|
||||
void benchNotifyWatcher();
|
||||
|
||||
private:
|
||||
QTemporaryDir m_tempDir;
|
||||
QString m_path;
|
||||
};
|
||||
|
||||
QTEST_MAIN(KDirWatch_UnitTest)
|
||||
|
||||
void KDirWatch_UnitTest::benchCreateTree()
|
||||
{
|
||||
#if !ENABLE_BENCHMARKS
|
||||
QSKIP("Benchmarks are disabled in debug mode");
|
||||
#endif
|
||||
QTemporaryDir dir;
|
||||
|
||||
QBENCHMARK {
|
||||
createDirectoryTree(dir.path());
|
||||
}
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::benchCreateWatcher()
|
||||
{
|
||||
#if !ENABLE_BENCHMARKS
|
||||
QSKIP("Benchmarks are disabled in debug mode");
|
||||
#endif
|
||||
QTemporaryDir dir;
|
||||
createDirectoryTree(dir.path());
|
||||
|
||||
QBENCHMARK {
|
||||
KDirWatch watch;
|
||||
watch.addDir(dir.path(), KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
|
||||
}
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::benchNotifyWatcher()
|
||||
{
|
||||
#if !ENABLE_BENCHMARKS
|
||||
QSKIP("Benchmarks are disabled in debug mode");
|
||||
#endif
|
||||
QTemporaryDir dir;
|
||||
// create the dir once upfront
|
||||
auto numFiles = createDirectoryTree(dir.path());
|
||||
waitUntilMTimeChange(dir.path());
|
||||
|
||||
KDirWatch watch;
|
||||
watch.addDir(dir.path(), KDirWatch::WatchSubDirs | KDirWatch::WatchFiles);
|
||||
|
||||
// now touch all the files repeatedly and wait for the dirty updates to come in
|
||||
QSignalSpy spy(&watch, &KDirWatch::dirty);
|
||||
QBENCHMARK {
|
||||
createDirectoryTree(dir.path());
|
||||
QTRY_COMPARE_WITH_TIMEOUT(spy.count(), numFiles, 30000);
|
||||
spy.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#include "kdirwatch_benchmarktest.moc"
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <kdirwatch.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
|
||||
#include "kcoreaddons_debug.h"
|
||||
|
||||
class StaticObject
|
||||
{
|
||||
public:
|
||||
KDirWatch m_dirWatch;
|
||||
};
|
||||
Q_GLOBAL_STATIC(StaticObject, s_staticObject)
|
||||
|
||||
class StaticObjectUsingSelf // like KSambaShare does, bug 353080
|
||||
{
|
||||
public:
|
||||
StaticObjectUsingSelf()
|
||||
{
|
||||
KDirWatch::self();
|
||||
}
|
||||
~StaticObjectUsingSelf()
|
||||
{
|
||||
if (KDirWatch::exists() && KDirWatch::self()->contains(QDir::homePath())) {
|
||||
KDirWatch::self()->removeDir(QDir::homePath());
|
||||
}
|
||||
}
|
||||
};
|
||||
Q_GLOBAL_STATIC(StaticObjectUsingSelf, s_staticObjectUsingSelf)
|
||||
|
||||
namespace KDirWatchTestUtils
|
||||
{
|
||||
// Just to make the inotify packets bigger
|
||||
static const char s_filePrefix[] = "This_is_a_test_file_";
|
||||
|
||||
static const char *methodToString(KDirWatch::Method method)
|
||||
{
|
||||
switch (method) {
|
||||
case KDirWatch::INotify:
|
||||
return "INotify";
|
||||
case KDirWatch::Stat:
|
||||
return "Stat";
|
||||
case KDirWatch::QFSWatch:
|
||||
return "QFSWatch";
|
||||
}
|
||||
return "ERROR!";
|
||||
}
|
||||
|
||||
// helper method: create a file
|
||||
inline void createFile(const QString &path, bool slow = false)
|
||||
{
|
||||
QFile file(path);
|
||||
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||
#ifdef Q_OS_FREEBSD
|
||||
// FreeBSD has inotify implemented as user-space library over native kevent API.
|
||||
// When using it, one has to open() a file to start watching it, so workaround
|
||||
// test breakage by giving inotify time to react to file creation.
|
||||
// Full context: https://github.com/libinotify-kqueue/libinotify-kqueue/issues/10
|
||||
if (!slow) {
|
||||
QThread::msleep(1);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(slow)
|
||||
#endif
|
||||
file.write(QByteArray("foo"));
|
||||
file.close();
|
||||
}
|
||||
|
||||
inline int createDirectoryTree(const QString &basePath, int depth = 4)
|
||||
{
|
||||
int filesCreated = 0;
|
||||
|
||||
const int numFiles = 10;
|
||||
for (int i = 0; i < numFiles; ++i) {
|
||||
createFile(basePath + QLatin1Char('/') + QLatin1String(s_filePrefix) + QString::number(i));
|
||||
++filesCreated;
|
||||
}
|
||||
|
||||
if (depth <= 0) {
|
||||
return filesCreated;
|
||||
}
|
||||
|
||||
const int numFolders = 5;
|
||||
for (int i = 0; i < numFolders; ++i) {
|
||||
const QString childPath = basePath + QLatin1String("/subdir") + QString::number(i);
|
||||
QDir().mkdir(childPath);
|
||||
filesCreated += createDirectoryTree(childPath, depth - 1);
|
||||
}
|
||||
|
||||
return filesCreated;
|
||||
}
|
||||
|
||||
inline void waitUntilAfter(const QDateTime &ctime)
|
||||
{
|
||||
int totalWait = 0;
|
||||
QDateTime now;
|
||||
Q_FOREVER {
|
||||
now = QDateTime::currentDateTime();
|
||||
if (now.toMSecsSinceEpoch() / 1000 == ctime.toMSecsSinceEpoch() / 1000) // truncate milliseconds
|
||||
{
|
||||
totalWait += 50;
|
||||
QTest::qWait(50);
|
||||
} else {
|
||||
QVERIFY(now > ctime); // can't go back in time ;)
|
||||
QTest::qWait(50); // be safe
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if (totalWait > 0)
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Waited" << totalWait << "ms so that now" << now.toString(Qt::ISODate) << "is >" << ctime.toString(Qt::ISODate);
|
||||
}
|
||||
inline void waitUntilMTimeChange(const QString &path)
|
||||
{
|
||||
// Wait until the current second is more than the file's mtime
|
||||
// otherwise this change will go unnoticed
|
||||
|
||||
QFileInfo fi(path);
|
||||
QVERIFY(fi.exists());
|
||||
const QDateTime ctime = fi.lastModified();
|
||||
waitUntilAfter(ctime);
|
||||
}
|
||||
|
||||
inline void waitUntilNewSecond()
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
waitUntilAfter(now);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <kdirwatch.h>
|
||||
#include <kdirwatch_p.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QSignalSpy>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
#include <sys/stat.h>
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <unistd.h> // ::link()
|
||||
#endif
|
||||
|
||||
#include "kcoreaddons_debug.h"
|
||||
#include "kdirwatch_test_utils.h"
|
||||
|
||||
using namespace KDirWatchTestUtils;
|
||||
|
||||
// Debugging notes: to see which inotify signals are emitted, either set s_verboseDebug=true
|
||||
// at the top of kdirwatch.cpp, or use the command-line tool "inotifywait -m /path"
|
||||
|
||||
class KDirWatch_UnitTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KDirWatch_UnitTest()
|
||||
{
|
||||
// Speed up the test by making the kdirwatch timer (to compress changes) faster
|
||||
qputenv("KDIRWATCH_POLLINTERVAL", "50");
|
||||
qputenv("KDIRWATCH_METHOD", KDIRWATCH_TEST_METHOD);
|
||||
s_staticObjectUsingSelf();
|
||||
|
||||
m_path = m_tempDir.path() + QLatin1Char('/');
|
||||
KDirWatch *dirW = &s_staticObject()->m_dirWatch;
|
||||
m_stat = dirW->internalMethod() == KDirWatch::Stat;
|
||||
m_slow = m_stat;
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Using method" << methodToString(dirW->internalMethod());
|
||||
}
|
||||
|
||||
private Q_SLOTS: // test methods
|
||||
void initTestCase()
|
||||
{
|
||||
QFileInfo pathInfo(m_path);
|
||||
QVERIFY(pathInfo.isDir() && pathInfo.isWritable());
|
||||
|
||||
// By creating the files upfront, we save waiting a full second for an mtime change
|
||||
createFile(m_path + QLatin1String("ExistingFile"));
|
||||
createFile(m_path + QLatin1String("TestFile"));
|
||||
createFile(m_path + QLatin1String("nested_0"));
|
||||
createFile(m_path + QLatin1String("nested_1"));
|
||||
|
||||
s_staticObject()->m_dirWatch.addFile(m_path + QLatin1String("ExistingFile"));
|
||||
}
|
||||
void touchOneFile();
|
||||
void touch1000Files();
|
||||
void watchAndModifyOneFile();
|
||||
void removeAndReAdd();
|
||||
void watchNonExistent();
|
||||
void watchNonExistentWithSingleton();
|
||||
void testDelete();
|
||||
void testDeleteAndRecreateFile();
|
||||
void testDeleteAndRecreateDir();
|
||||
void testMoveTo();
|
||||
void nestedEventLoop();
|
||||
void testHardlinkChange();
|
||||
void stopAndRestart();
|
||||
void testRefcounting();
|
||||
void testRelativeRefcounting();
|
||||
void testMoveToThread();
|
||||
|
||||
protected Q_SLOTS: // internal slots
|
||||
void nestedEventLoopSlot();
|
||||
|
||||
private:
|
||||
QList<QVariantList> waitForDirtySignal(KDirWatch &watch, int expected);
|
||||
QList<QVariantList> waitForDeletedSignal(KDirWatch &watch, int expected);
|
||||
bool waitForOneSignal(KDirWatch &watch, const char *sig, const QString &path);
|
||||
bool waitForRecreationSignal(KDirWatch &watch, const QString &path);
|
||||
bool verifySignalPath(QSignalSpy &spy, const char *sig, const QString &expectedPath);
|
||||
QString createFile(int num);
|
||||
void createFile(const QString &file)
|
||||
{
|
||||
KDirWatchTestUtils::createFile(file, m_slow);
|
||||
}
|
||||
void removeFile(int num);
|
||||
void appendToFile(const QString &path);
|
||||
void appendToFile(int num);
|
||||
|
||||
QTemporaryDir m_tempDir;
|
||||
QString m_path;
|
||||
bool m_slow;
|
||||
bool m_stat;
|
||||
};
|
||||
|
||||
QTEST_MAIN(KDirWatch_UnitTest)
|
||||
|
||||
static const int s_maxTries = 50;
|
||||
|
||||
// helper method: create a file (identified by number)
|
||||
QString KDirWatch_UnitTest::createFile(int num)
|
||||
{
|
||||
const QString fileName = QLatin1String(s_filePrefix) + QString::number(num);
|
||||
KDirWatchTestUtils::createFile(m_path + fileName, m_slow);
|
||||
return m_path + fileName;
|
||||
}
|
||||
|
||||
// helper method: delete a file (identified by number)
|
||||
void KDirWatch_UnitTest::removeFile(int num)
|
||||
{
|
||||
const QString fileName = QLatin1String(s_filePrefix) + QString::number(num);
|
||||
QFile::remove(m_path + fileName);
|
||||
}
|
||||
|
||||
// helper method: modifies a file
|
||||
void KDirWatch_UnitTest::appendToFile(const QString &path)
|
||||
{
|
||||
QVERIFY(QFile::exists(path));
|
||||
waitUntilMTimeChange(path);
|
||||
|
||||
QFile file(path);
|
||||
QVERIFY(file.open(QIODevice::Append | QIODevice::WriteOnly));
|
||||
file.write(QByteArray("foobar"));
|
||||
file.close();
|
||||
}
|
||||
|
||||
// helper method: modifies a file (identified by number)
|
||||
void KDirWatch_UnitTest::appendToFile(int num)
|
||||
{
|
||||
const QString fileName = QLatin1String(s_filePrefix) + QString::number(num);
|
||||
appendToFile(m_path + fileName);
|
||||
}
|
||||
|
||||
static QString removeTrailingSlash(const QString &path)
|
||||
{
|
||||
if (path.endsWith(QLatin1Char('/'))) {
|
||||
return path.left(path.length() - 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
// helper method
|
||||
QList<QVariantList> KDirWatch_UnitTest::waitForDirtySignal(KDirWatch &watch, int expected)
|
||||
{
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
int numTries = 0;
|
||||
// Give time for KDirWatch to notify us
|
||||
while (spyDirty.count() < expected) {
|
||||
if (++numTries > s_maxTries) {
|
||||
qWarning() << "Timeout waiting for KDirWatch. Got" << spyDirty.count() << "dirty() signals, expected" << expected;
|
||||
return spyDirty;
|
||||
}
|
||||
spyDirty.wait(50);
|
||||
}
|
||||
return spyDirty;
|
||||
}
|
||||
|
||||
bool KDirWatch_UnitTest::waitForOneSignal(KDirWatch &watch, const char *sig, const QString &path)
|
||||
{
|
||||
const QString expectedPath = removeTrailingSlash(path);
|
||||
while (true) {
|
||||
QSignalSpy spyDirty(&watch, sig);
|
||||
int numTries = 0;
|
||||
// Give time for KDirWatch to notify us
|
||||
while (spyDirty.isEmpty()) {
|
||||
if (++numTries > s_maxTries) {
|
||||
qWarning() << "Timeout waiting for KDirWatch signal" << QByteArray(sig).mid(1) << "(" << path << ")";
|
||||
return false;
|
||||
}
|
||||
spyDirty.wait(50);
|
||||
}
|
||||
return verifySignalPath(spyDirty, sig, expectedPath);
|
||||
}
|
||||
}
|
||||
|
||||
bool KDirWatch_UnitTest::verifySignalPath(QSignalSpy &spy, const char *sig, const QString &expectedPath)
|
||||
{
|
||||
for (int i = 0; i < spy.count(); ++i) {
|
||||
const QString got = spy[i][0].toString();
|
||||
if (got == expectedPath) {
|
||||
return true;
|
||||
}
|
||||
if (got.startsWith(expectedPath + QLatin1Char('/'))) {
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Ignoring (inotify) notification of" << (sig + 1) << '(' << got << ')';
|
||||
continue;
|
||||
}
|
||||
qWarning() << "Expected" << sig << '(' << expectedPath << ')' << "but got" << sig << '(' << got << ')';
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KDirWatch_UnitTest::waitForRecreationSignal(KDirWatch &watch, const QString &path)
|
||||
{
|
||||
// When watching for a deleted + created signal pair, the two might come so close that
|
||||
// using waitForOneSignal will miss the created signal. This function monitors both all
|
||||
// the time to ensure both are received.
|
||||
//
|
||||
// In addition, it allows dirty() to be emitted (for that same path) as an alternative
|
||||
|
||||
const QString expectedPath = removeTrailingSlash(path);
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
QSignalSpy spyDeleted(&watch, &KDirWatch::deleted);
|
||||
QSignalSpy spyCreated(&watch, &KDirWatch::created);
|
||||
|
||||
int numTries = 0;
|
||||
while (spyDeleted.isEmpty() && spyDirty.isEmpty()) {
|
||||
if (++numTries > s_maxTries) {
|
||||
return false;
|
||||
}
|
||||
spyDeleted.wait(50);
|
||||
while (!spyDirty.isEmpty()) {
|
||||
if (spyDirty.at(0).at(0).toString() != expectedPath) { // unrelated
|
||||
spyDirty.removeFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!spyDirty.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't bother waiting for the created signal if the signal spy already received a signal.
|
||||
if (spyCreated.isEmpty() && !spyCreated.wait(50 * s_maxTries)) {
|
||||
qWarning() << "Timeout waiting for KDirWatch signal created(QString) (" << path << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return verifySignalPath(spyDeleted, "deleted(QString)", expectedPath) && verifySignalPath(spyCreated, "created(QString)", expectedPath);
|
||||
}
|
||||
|
||||
QList<QVariantList> KDirWatch_UnitTest::waitForDeletedSignal(KDirWatch &watch, int expected)
|
||||
{
|
||||
QSignalSpy spyDeleted(&watch, &KDirWatch::created);
|
||||
int numTries = 0;
|
||||
// Give time for KDirWatch to notify us
|
||||
while (spyDeleted.count() < expected) {
|
||||
if (++numTries > s_maxTries) {
|
||||
qWarning() << "Timeout waiting for KDirWatch. Got" << spyDeleted.count() << "deleted() signals, expected" << expected;
|
||||
return spyDeleted;
|
||||
}
|
||||
spyDeleted.wait(50);
|
||||
}
|
||||
return spyDeleted;
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::touchOneFile() // watch a dir, create a file in it
|
||||
{
|
||||
KDirWatch watch;
|
||||
watch.addDir(m_path);
|
||||
watch.startScan();
|
||||
|
||||
waitUntilMTimeChange(m_path);
|
||||
|
||||
// dirty(the directory) should be emitted.
|
||||
QSignalSpy spyCreated(&watch, &KDirWatch::created);
|
||||
createFile(0);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
QCOMPARE(spyCreated.count(), 0); // "This is not emitted when creating a file is created in a watched directory."
|
||||
|
||||
removeFile(0);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::touch1000Files()
|
||||
{
|
||||
KDirWatch watch;
|
||||
watch.addDir(m_path);
|
||||
watch.startScan();
|
||||
|
||||
waitUntilMTimeChange(m_path);
|
||||
|
||||
const int fileCount = 100;
|
||||
for (int i = 0; i < fileCount; ++i) {
|
||||
createFile(i);
|
||||
}
|
||||
|
||||
QList<QVariantList> spy = waitForDirtySignal(watch, fileCount);
|
||||
if (watch.internalMethod() == KDirWatch::INotify) {
|
||||
QVERIFY(spy.count() >= fileCount);
|
||||
} else {
|
||||
// More stupid backends just see one mtime change on the directory
|
||||
QVERIFY(spy.count() >= 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fileCount; ++i) {
|
||||
removeFile(i);
|
||||
}
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::watchAndModifyOneFile() // watch a specific file, and modify it
|
||||
{
|
||||
KDirWatch watch;
|
||||
const QString existingFile = m_path + QLatin1String("ExistingFile");
|
||||
watch.addFile(existingFile);
|
||||
watch.startScan();
|
||||
if (m_slow) {
|
||||
waitUntilNewSecond();
|
||||
}
|
||||
appendToFile(existingFile);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), existingFile));
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::removeAndReAdd()
|
||||
{
|
||||
KDirWatch watch;
|
||||
watch.addDir(m_path);
|
||||
// This triggers bug #374075.
|
||||
watch.addDir(QStringLiteral(":/kio5/newfile-templates"));
|
||||
watch.startScan();
|
||||
if (watch.internalMethod() != KDirWatch::INotify) {
|
||||
waitUntilNewSecond(); // necessary for mtime checks in scanEntry
|
||||
}
|
||||
createFile(0);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
|
||||
// Just like KDirLister does: remove the watch, then re-add it.
|
||||
watch.removeDir(m_path);
|
||||
watch.addDir(m_path);
|
||||
if (watch.internalMethod() != KDirWatch::INotify) {
|
||||
waitUntilMTimeChange(m_path); // necessary for QFSWatcher
|
||||
}
|
||||
createFile(1);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::watchNonExistent()
|
||||
{
|
||||
KDirWatch watch;
|
||||
// Watch "subdir", that doesn't exist yet
|
||||
const QString subdir = m_path + QLatin1String("subdir");
|
||||
QVERIFY(!QFile::exists(subdir));
|
||||
watch.addDir(subdir);
|
||||
watch.startScan();
|
||||
|
||||
if (m_slow) {
|
||||
waitUntilNewSecond();
|
||||
}
|
||||
|
||||
// Now create it, KDirWatch should emit created()
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Creating" << subdir;
|
||||
QDir().mkdir(subdir);
|
||||
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(created(QString)), subdir));
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << &watch;
|
||||
|
||||
// Play with addDir/removeDir, just for fun
|
||||
watch.addDir(subdir);
|
||||
watch.removeDir(subdir);
|
||||
watch.addDir(subdir);
|
||||
|
||||
// Now watch files that don't exist yet
|
||||
const QString file = subdir + QLatin1String("/0");
|
||||
watch.addFile(file); // doesn't exist yet
|
||||
const QString file1 = subdir + QLatin1String("/1");
|
||||
watch.addFile(file1); // doesn't exist yet
|
||||
watch.removeFile(file1); // forget it again
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << &watch;
|
||||
|
||||
QVERIFY(!QFile::exists(file));
|
||||
// Now create it, KDirWatch should emit created
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Creating" << file;
|
||||
createFile(file);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(created(QString)), file));
|
||||
|
||||
appendToFile(file);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file));
|
||||
|
||||
// Create the file after all; we're not watching for it, but the dir will emit dirty
|
||||
createFile(file1);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), subdir));
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::watchNonExistentWithSingleton()
|
||||
{
|
||||
const QString file = QLatin1String("/root/.ssh/authorized_keys");
|
||||
KDirWatch::self()->addFile(file);
|
||||
// When running this test in KDIRWATCH_METHOD=QFSWatch, or when we fallback to qfswatch
|
||||
// when inotify fails above, we end up creating the fsWatch
|
||||
// in the kdirwatch singleton. Bug 261541 discovered that Qt hanged when deleting fsWatch
|
||||
// once QCoreApp was gone, this is what this test is about.
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testDelete()
|
||||
{
|
||||
const QString file1 = m_path + QLatin1String("del");
|
||||
if (!QFile::exists(file1)) {
|
||||
createFile(file1);
|
||||
}
|
||||
waitUntilMTimeChange(file1);
|
||||
|
||||
// Watch the file, then delete it, KDirWatch will emit deleted (and possibly dirty for the dir, if mtime changed)
|
||||
KDirWatch watch;
|
||||
watch.addFile(file1);
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << &watch;
|
||||
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
QFile::remove(file1);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(deleted(QString)), file1));
|
||||
QTest::qWait(40); // just in case delayed processing would emit it
|
||||
QCOMPARE(spyDirty.count(), 0);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testDeleteAndRecreateFile() // Useful for /etc/localtime for instance
|
||||
{
|
||||
const QString subdir = m_path + QLatin1String("subdir");
|
||||
QDir().mkdir(subdir);
|
||||
const QString file1 = subdir + QLatin1String("/1");
|
||||
if (!QFile::exists(file1)) {
|
||||
createFile(file1);
|
||||
}
|
||||
waitUntilMTimeChange(file1);
|
||||
|
||||
// Watch the file, then delete it, KDirWatch will emit deleted (and possibly dirty for the dir, if mtime changed)
|
||||
KDirWatch watch;
|
||||
watch.addFile(file1);
|
||||
|
||||
// Make sure this even works multiple times, as needed for ksycoca
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (m_slow || watch.internalMethod() == KDirWatch::QFSWatch) {
|
||||
waitUntilNewSecond();
|
||||
}
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Attempt #" << (i + 1) << "removing+recreating" << file1;
|
||||
|
||||
// When watching for a deleted + created signal pair, the two might come so close that
|
||||
// using waitForOneSignal will miss the created signal. This function monitors both all
|
||||
// the time to ensure both are received.
|
||||
//
|
||||
// In addition, allow dirty() to be emitted (for that same path) as an alternative
|
||||
|
||||
const QString expectedPath = file1;
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
QSignalSpy spyDeleted(&watch, &KDirWatch::deleted);
|
||||
QSignalSpy spyCreated(&watch, &KDirWatch::created);
|
||||
|
||||
// WHEN
|
||||
QFile::remove(file1);
|
||||
// And recreate immediately, to try and fool KDirWatch with unchanged ctime/mtime ;)
|
||||
// (This emulates the /etc/localtime case)
|
||||
createFile(file1);
|
||||
|
||||
// THEN
|
||||
int numTries = 0;
|
||||
while (spyDeleted.isEmpty() && spyDirty.isEmpty()) {
|
||||
if (++numTries > s_maxTries) {
|
||||
QFAIL("Failed to detect file deletion and recreation through either a deleted/created signal pair or through a dirty signal!");
|
||||
return;
|
||||
}
|
||||
spyDeleted.wait(50);
|
||||
while (!spyDirty.isEmpty()) {
|
||||
if (spyDirty.at(0).at(0).toString() != expectedPath) { // unrelated
|
||||
spyDirty.removeFirst();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!spyDirty.isEmpty()) {
|
||||
continue; // all ok
|
||||
}
|
||||
|
||||
// Don't bother waiting for the created signal if the signal spy already received a signal.
|
||||
if (spyCreated.isEmpty() && !spyCreated.wait(50 * s_maxTries)) {
|
||||
qWarning() << "Timeout waiting for KDirWatch signal created(QString) (" << expectedPath << ")";
|
||||
QFAIL("Timeout waiting for KDirWatch signal created, after deleted was emitted");
|
||||
return;
|
||||
}
|
||||
|
||||
QVERIFY(verifySignalPath(spyDeleted, "deleted(QString)", expectedPath) && verifySignalPath(spyCreated, "created(QString)", expectedPath));
|
||||
}
|
||||
|
||||
waitUntilMTimeChange(file1);
|
||||
|
||||
appendToFile(file1);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file1));
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testDeleteAndRecreateDir()
|
||||
{
|
||||
// Like KDirModelTest::testOverwriteFileWithDir does at the end.
|
||||
// The linux-2.6.31 bug made kdirwatch emit deletion signals about the -new- dir!
|
||||
QTemporaryDir *tempDir1 = new QTemporaryDir(QDir::tempPath() + QLatin1Char('/') + QLatin1String("olddir-"));
|
||||
KDirWatch watch;
|
||||
const QString path1 = tempDir1->path() + QLatin1Char('/');
|
||||
watch.addDir(path1);
|
||||
|
||||
delete tempDir1;
|
||||
QTemporaryDir *tempDir2 = new QTemporaryDir(QDir::tempPath() + QLatin1Char('/') + QLatin1String("newdir-"));
|
||||
const QString path2 = tempDir2->path() + QLatin1Char('/');
|
||||
watch.addDir(path2);
|
||||
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(deleted(QString)), path1));
|
||||
|
||||
delete tempDir2;
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testMoveTo()
|
||||
{
|
||||
// This reproduces the famous digikam crash, #222974
|
||||
// A watched file was being rewritten (overwritten by ksavefile),
|
||||
// which gives inotify notifications "moved_to" followed by "delete_self"
|
||||
//
|
||||
// What happened then was that the delayed slotRescan
|
||||
// would adjust things, making it status==Normal but the entry was
|
||||
// listed as a "non-existent sub-entry" for the parent directory.
|
||||
// That's inconsistent, and after removeFile() a dangling sub-entry would be left.
|
||||
|
||||
// Initial data: creating file subdir/1
|
||||
const QString file1 = m_path + QLatin1String("moveTo");
|
||||
createFile(file1);
|
||||
|
||||
KDirWatch watch;
|
||||
watch.addDir(m_path);
|
||||
watch.addFile(file1);
|
||||
watch.startScan();
|
||||
|
||||
if (watch.internalMethod() != KDirWatch::INotify) {
|
||||
waitUntilMTimeChange(m_path);
|
||||
}
|
||||
|
||||
// Atomic rename of "temp" to "file1", much like KAutoSave would do when saving file1 again
|
||||
// ### TODO: this isn't an atomic rename anymore. We need ::rename for that, or API from Qt.
|
||||
const QString filetemp = m_path + QLatin1String("temp");
|
||||
createFile(filetemp);
|
||||
QFile::remove(file1);
|
||||
QVERIFY(QFile::rename(filetemp, file1)); // overwrite file1 with the tempfile
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Overwrite file1 with tempfile";
|
||||
|
||||
QSignalSpy spyCreated(&watch, &KDirWatch::created);
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
|
||||
// Getting created() on an unwatched file is an inotify bonus, it's not part of the requirements.
|
||||
if (watch.internalMethod() == KDirWatch::INotify) {
|
||||
QCOMPARE(spyCreated.count(), 1);
|
||||
QCOMPARE(spyCreated[0][0].toString(), file1);
|
||||
|
||||
QCOMPARE(spyDirty.size(), 2);
|
||||
QCOMPARE(spyDirty[1][0].toString(), filetemp);
|
||||
}
|
||||
|
||||
// make sure we're still watching it
|
||||
appendToFile(file1);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), file1));
|
||||
|
||||
watch.removeFile(file1); // now we remove it
|
||||
|
||||
// Just touch another file to trigger a findSubEntry - this where the crash happened
|
||||
waitUntilMTimeChange(m_path);
|
||||
createFile(filetemp);
|
||||
#ifdef Q_OS_WIN
|
||||
if (watch.internalMethod() == KDirWatch::QFSWatch) {
|
||||
QEXPECT_FAIL(nullptr, "QFSWatch fails here on Windows!", Continue);
|
||||
}
|
||||
#endif
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::nestedEventLoop() // #220153: watch two files, and modify 2nd while in slot for 1st
|
||||
{
|
||||
KDirWatch watch;
|
||||
|
||||
const QString file0 = m_path + QLatin1String("nested_0");
|
||||
watch.addFile(file0);
|
||||
const QString file1 = m_path + QLatin1String("nested_1");
|
||||
watch.addFile(file1);
|
||||
watch.startScan();
|
||||
|
||||
if (m_slow) {
|
||||
waitUntilNewSecond();
|
||||
}
|
||||
|
||||
appendToFile(file0);
|
||||
|
||||
// use own spy, to connect it before nestedEventLoopSlot, otherwise it reverses order
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
connect(&watch, &KDirWatch::dirty, this, &KDirWatch_UnitTest::nestedEventLoopSlot);
|
||||
waitForDirtySignal(watch, 1);
|
||||
QVERIFY(spyDirty.count() >= 2);
|
||||
QCOMPARE(spyDirty[0][0].toString(), file0);
|
||||
QCOMPARE(spyDirty[spyDirty.count() - 1][0].toString(), file1);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::nestedEventLoopSlot()
|
||||
{
|
||||
const KDirWatch *const_watch = qobject_cast<const KDirWatch *>(sender());
|
||||
KDirWatch *watch = const_cast<KDirWatch *>(const_watch);
|
||||
// let's not come in this slot again
|
||||
disconnect(watch, &KDirWatch::dirty, this, &KDirWatch_UnitTest::nestedEventLoopSlot);
|
||||
|
||||
const QString file1 = m_path + QLatin1String("nested_1");
|
||||
appendToFile(file1);
|
||||
// The nested event processing here was from a messagebox in #220153
|
||||
QList<QVariantList> spy = waitForDirtySignal(*watch, 1);
|
||||
QVERIFY(spy.count() >= 1);
|
||||
QCOMPARE(spy[spy.count() - 1][0].toString(), file1);
|
||||
|
||||
// Now the user pressed reload...
|
||||
const QString file0 = m_path + QLatin1String("nested_0");
|
||||
watch->removeFile(file0);
|
||||
watch->addFile(file0);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testHardlinkChange()
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
|
||||
// The unittest for the "detecting hardlink change to /etc/localtime" problem
|
||||
// described on kde-core-devel (2009-07-03).
|
||||
// It shows that watching a specific file doesn't inform us that the file is
|
||||
// being recreated. Better watch the directory, for that.
|
||||
// Well, it works with inotify
|
||||
|
||||
const QString existingFile = m_path + QLatin1String("ExistingFile");
|
||||
KDirWatch watch;
|
||||
watch.addFile(existingFile);
|
||||
watch.startScan();
|
||||
|
||||
QFile::remove(existingFile);
|
||||
const QString testFile = m_path + QLatin1String("TestFile");
|
||||
QVERIFY(::link(QFile::encodeName(testFile).constData(), QFile::encodeName(existingFile).constData()) == 0); // make ExistingFile "point" to TestFile
|
||||
QVERIFY(QFile::exists(existingFile));
|
||||
|
||||
QVERIFY(waitForRecreationSignal(watch, existingFile));
|
||||
|
||||
// The mtime of the existing file is the one of "TestFile", so it's old.
|
||||
// We won't detect the change then, if we use that as baseline for waiting.
|
||||
// We really need msec granularity, but that requires using statx which isn't available everywhere...
|
||||
waitUntilNewSecond();
|
||||
appendToFile(existingFile);
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), existingFile));
|
||||
#else
|
||||
QSKIP("Unix-specific");
|
||||
#endif
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::stopAndRestart()
|
||||
{
|
||||
KDirWatch watch;
|
||||
watch.addDir(m_path);
|
||||
watch.startScan();
|
||||
|
||||
waitUntilMTimeChange(m_path);
|
||||
|
||||
watch.stopDirScan(m_path);
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << "create file 2 at" << QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||||
const QString file2 = createFile(2);
|
||||
QSignalSpy spyDirty(&watch, &KDirWatch::dirty);
|
||||
QTest::qWait(200);
|
||||
QCOMPARE(spyDirty.count(), 0); // suspended -> no signal
|
||||
|
||||
watch.restartDirScan(m_path);
|
||||
|
||||
QTest::qWait(200);
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
QCOMPARE(spyDirty.count(), 0); // as documented by restartDirScan: no signal
|
||||
// On Windows, however, signals will get emitted, due to the ifdef Q_OS_WIN in the timestamp
|
||||
// comparison ("trust QFSW since the mtime of dirs isn't modified")
|
||||
#endif
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << &watch;
|
||||
|
||||
waitUntilMTimeChange(m_path); // necessary for the mtime comparison in scanEntry
|
||||
|
||||
qCDebug(KCOREADDONS_DEBUG) << "create file 3 at" << QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||||
const QString file3 = createFile(3);
|
||||
#ifdef Q_OS_WIN
|
||||
if (watch.internalMethod() == KDirWatch::QFSWatch) {
|
||||
QEXPECT_FAIL(nullptr, "QFSWatch fails here on Windows!", Continue);
|
||||
}
|
||||
#endif
|
||||
QVERIFY(waitForOneSignal(watch, SIGNAL(dirty(QString)), m_path));
|
||||
|
||||
QFile::remove(file2);
|
||||
QFile::remove(file3);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testRefcounting()
|
||||
{
|
||||
#if QT_CONFIG(cxx11_future)
|
||||
bool initialExists = false;
|
||||
bool secondExists = true; // the expectation is it will be set false
|
||||
auto thread = QThread::create([&] {
|
||||
QTemporaryDir dir;
|
||||
{
|
||||
KDirWatch watch;
|
||||
watch.addFile(dir.path());
|
||||
initialExists = KDirWatch::exists();
|
||||
} // out of scope, the internal private should have been unset
|
||||
secondExists = KDirWatch::exists();
|
||||
});
|
||||
thread->start();
|
||||
thread->wait();
|
||||
delete thread;
|
||||
QVERIFY(initialExists);
|
||||
QVERIFY(!secondExists);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testRelativeRefcounting()
|
||||
{
|
||||
// Relative files aren't supported but should still result in correct ref
|
||||
// handling. Specifically when watch1 gets destroyed it should take all
|
||||
// its entries with it regardless of whether the entry was well-formed
|
||||
KDirWatch watch0;
|
||||
|
||||
if (watch0.internalMethod() != KDirWatch::INotify) {
|
||||
// Only test on inotify. Otherwise Entry count expectations may diverge.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto initialSize = watch0.d->m_mapEntries.size();
|
||||
{
|
||||
KDirWatch watch1;
|
||||
watch1.addFile(QStringLiteral("AVeryRelativePath.txt"));
|
||||
// NOTE: addFile actually adds two entires: one for '.' to watch for the appearance of the file and one for the file.
|
||||
QCOMPARE(watch0.d->m_mapEntries.size(), initialSize + 2);
|
||||
}
|
||||
// NOTE: we leak the directory entry from above but it has no clients so it's mostly harmless
|
||||
QCOMPARE(watch0.d->m_mapEntries.size(), initialSize + 1);
|
||||
}
|
||||
|
||||
void KDirWatch_UnitTest::testMoveToThread()
|
||||
{
|
||||
QTemporaryDir dir;
|
||||
{
|
||||
const QRegularExpression expression(QStringLiteral("KDirwatch is moving its thread. This is not supported at this time;.+"));
|
||||
QTest::ignoreMessage(QtCriticalMsg, expression);
|
||||
|
||||
auto watch = new KDirWatch;
|
||||
watch->addDir(dir.path());
|
||||
|
||||
auto thread = new QThread;
|
||||
watch->moveToThread(thread);
|
||||
thread->start();
|
||||
|
||||
waitUntilMTimeChange(dir.path());
|
||||
|
||||
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||||
QObject::connect(thread, &QThread::finished, watch, &QObject::deleteLater);
|
||||
|
||||
thread->quit();
|
||||
thread->wait();
|
||||
}
|
||||
// trigger an event on the now deleted watch. This should not crash!
|
||||
const QString file = dir.path() + QLatin1String("/bar");
|
||||
createFile(file);
|
||||
waitUntilMTimeChange(file);
|
||||
}
|
||||
|
||||
#include "kdirwatch_unittest.moc"
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfileutilstest.h"
|
||||
|
||||
#include <KFileUtils>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KFileUtilsTest)
|
||||
|
||||
void KFileUtilsTest::testSuggestName_data()
|
||||
{
|
||||
QTest::addColumn<QString>("oldName");
|
||||
QTest::addColumn<QStringList>("existingFiles");
|
||||
QTest::addColumn<QString>("expectedOutput");
|
||||
|
||||
QTest::newRow("non-existing") << "foobar" << QStringList() << "foobar (1)";
|
||||
QTest::newRow("existing") << "foobar" << QStringList(QStringLiteral("foobar")) << "foobar (1)";
|
||||
QTest::newRow("existing_1") << "foobar" << (QStringList() << QStringLiteral("foobar") << QStringLiteral("foobar (1)")) << "foobar (2)";
|
||||
QTest::newRow("extension") << "foobar.txt" << QStringList() << "foobar (1).txt";
|
||||
QTest::newRow("extension_exists") << "foobar.txt" << (QStringList() << QStringLiteral("foobar.txt")) << "foobar (1).txt";
|
||||
QTest::newRow("extension_exists_1") << "foobar.txt" << (QStringList() << QStringLiteral("foobar.txt") << QStringLiteral("foobar (1).txt"))
|
||||
<< "foobar (2).txt";
|
||||
QTest::newRow("two_extensions") << "foobar.tar.gz" << QStringList() << "foobar (1).tar.gz";
|
||||
QTest::newRow("two_extensions_exists") << "foobar.tar.gz" << (QStringList() << QStringLiteral("foobar.tar.gz")) << "foobar (1).tar.gz";
|
||||
QTest::newRow("two_extensions_exists_1") << "foobar.tar.gz" << (QStringList() << QStringLiteral("foobar.tar.gz") << QStringLiteral("foobar (1).tar.gz"))
|
||||
<< "foobar (2).tar.gz";
|
||||
QTest::newRow("with_space") << "foo bar" << QStringList(QStringLiteral("foo bar")) << "foo bar (1)";
|
||||
QTest::newRow("dot_at_beginning") << ".aFile.tar.gz" << QStringList() << ".aFile (1).tar.gz";
|
||||
QTest::newRow("dots_at_beginning") << "..aFile.tar.gz" << QStringList() << "..aFile (1).tar.gz";
|
||||
QTest::newRow("empty_basename") << ".txt" << QStringList() << ". (1).txt";
|
||||
QTest::newRow("empty_basename_2dots") << "..txt" << QStringList() << ". (1).txt";
|
||||
QTest::newRow("basename_with_dots") << "filename.5.3.2.tar.gz" << QStringList() << "filename.5.3.2 (1).tar.gz";
|
||||
QTest::newRow("unknown_extension_trashinfo") << "fileFromHome.trashinfo" << QStringList() << "fileFromHome (1).trashinfo";
|
||||
QTest::newRow("filename_with_numbers_parentheses") << "super.weird.filename(123).txt(54321)" << QStringList() << "super.weird.filename(123) (1).txt(54321)";
|
||||
}
|
||||
|
||||
void KFileUtilsTest::testSuggestName()
|
||||
{
|
||||
QFETCH(QString, oldName);
|
||||
QFETCH(QStringList, existingFiles);
|
||||
QFETCH(QString, expectedOutput);
|
||||
|
||||
QTemporaryDir dir;
|
||||
const QUrl baseUrl = QUrl::fromLocalFile(dir.path());
|
||||
for (const QString &localFile : std::as_const(existingFiles)) {
|
||||
QFile file(dir.path() + QLatin1Char('/') + localFile);
|
||||
QVERIFY(file.open(QIODevice::WriteOnly));
|
||||
}
|
||||
QCOMPARE(KFileUtils::suggestName(baseUrl, oldName), expectedOutput);
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
|
||||
#define XDG_PLATFORM
|
||||
#endif
|
||||
|
||||
void KFileUtilsTest::testfindAllUniqueFiles()
|
||||
{
|
||||
#ifndef XDG_PLATFORM
|
||||
QSKIP("This test requires XDG_DATA_DIRS; no way to configure QStandardPaths on Windows");
|
||||
#endif
|
||||
const QString testBaseDirPath = QDir::currentPath() + QLatin1String("/kfileutilstestdata/");
|
||||
QDir testDataBaseDir(testBaseDirPath);
|
||||
testDataBaseDir.mkpath(QStringLiteral("."));
|
||||
testDataBaseDir.mkpath(QStringLiteral("testdir1/testDirName"));
|
||||
testDataBaseDir.mkpath(QStringLiteral("testdir2/testDirName"));
|
||||
testDataBaseDir.mkpath(QStringLiteral("testdir3/testDirName"));
|
||||
|
||||
QFile file1(testBaseDirPath + QLatin1String("/testdir1/testDirName/testfile.test"));
|
||||
file1.open(QFile::WriteOnly);
|
||||
QFile file2(testBaseDirPath + QLatin1String("/testdir2/testDirName/testfile.test"));
|
||||
file2.open(QFile::WriteOnly);
|
||||
QFile file3(testBaseDirPath + QLatin1String("/testdir3/testDirName/differentfile.test"));
|
||||
file3.open(QFile::WriteOnly);
|
||||
QFile file4(testBaseDirPath + QLatin1String("/testdir3/testDirName/nomatch.txt"));
|
||||
file4.open(QFile::WriteOnly);
|
||||
|
||||
qputenv("XDG_DATA_DIRS", qPrintable(QStringLiteral("%1testdir1:%1testdir2:%1testdir3").arg(testBaseDirPath)));
|
||||
|
||||
const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("testDirName"), QStandardPaths::LocateDirectory);
|
||||
const QStringList expected = {testDataBaseDir.filePath(QStringLiteral("testdir1/testDirName/testfile.test")),
|
||||
testDataBaseDir.filePath(QStringLiteral("testdir3/testDirName/differentfile.test"))};
|
||||
|
||||
const QStringList actual = KFileUtils::findAllUniqueFiles(dirs, QStringList{QStringLiteral("*.test")});
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
#include "moc_kfileutilstest.cpp"
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEUTILSTEST_H
|
||||
#define KFILEUTILSTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KFileUtilsTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testSuggestName_data();
|
||||
void testSuggestName();
|
||||
void testfindAllUniqueFiles();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
This file is part of the KDE Frameworks
|
||||
|
||||
SPDX-FileCopyrightText: 2013 John Layt <jlayt@kde.org>
|
||||
SPDX-FileCopyrightText: 2010 Michael Leupold <lemma@confuego.org>
|
||||
SPDX-FileCopyrightText: 2009 Michael Pyne <mpyne@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kformattest.h"
|
||||
|
||||
#include <QTest>
|
||||
#include <QTimeZone>
|
||||
|
||||
#include "kformat.h"
|
||||
|
||||
void setupEnvironment()
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
// ignore translations
|
||||
qputenv("XDG_DATA_DIRS", "does-not-exist");
|
||||
#endif
|
||||
}
|
||||
Q_CONSTRUCTOR_FUNCTION(setupEnvironment)
|
||||
|
||||
void KFormatTest::formatByteSize()
|
||||
{
|
||||
QLocale locale(QLocale::c());
|
||||
locale.setNumberOptions(QLocale::DefaultNumberOptions); // Qt >= 5.6 sets QLocale::OmitGroupSeparator for the C locale
|
||||
KFormat format(locale);
|
||||
|
||||
QCOMPARE(format.formatByteSize(0), QStringLiteral("0 B"));
|
||||
QCOMPARE(format.formatByteSize(50), QStringLiteral("50 B"));
|
||||
QCOMPARE(format.formatByteSize(500), QStringLiteral("500 B"));
|
||||
QCOMPARE(format.formatByteSize(5000), QStringLiteral("4.9 KiB"));
|
||||
QCOMPARE(format.formatByteSize(50000), QStringLiteral("48.8 KiB"));
|
||||
QCOMPARE(format.formatByteSize(500000), QStringLiteral("488.3 KiB"));
|
||||
QCOMPARE(format.formatByteSize(5000000), QStringLiteral("4.8 MiB"));
|
||||
QCOMPARE(format.formatByteSize(50000000), QStringLiteral("47.7 MiB"));
|
||||
QCOMPARE(format.formatByteSize(500000000), QStringLiteral("476.8 MiB"));
|
||||
#if (defined(__WORDSIZE) && (__WORDSIZE == 64)) || defined(_LP64) || defined(__LP64__) || defined(__ILP64__)
|
||||
QCOMPARE(format.formatByteSize(5000000000), QStringLiteral("4.7 GiB"));
|
||||
QCOMPARE(format.formatByteSize(50000000000), QStringLiteral("46.6 GiB"));
|
||||
QCOMPARE(format.formatByteSize(500000000000), QStringLiteral("465.7 GiB"));
|
||||
QCOMPARE(format.formatByteSize(5000000000000), QStringLiteral("4.5 TiB"));
|
||||
QCOMPARE(format.formatByteSize(50000000000000), QStringLiteral("45.5 TiB"));
|
||||
QCOMPARE(format.formatByteSize(500000000000000), QStringLiteral("454.7 TiB"));
|
||||
#endif
|
||||
|
||||
QCOMPARE(format.formatByteSize(1024.0, 1, KFormat::IECBinaryDialect), QStringLiteral("1.0 KiB"));
|
||||
QCOMPARE(format.formatByteSize(1023.0, 1, KFormat::IECBinaryDialect), QStringLiteral("1,023 B"));
|
||||
QCOMPARE(format.formatByteSize(1163000.0, 1, KFormat::IECBinaryDialect), QStringLiteral("1.1 MiB")); // 1.2 metric
|
||||
|
||||
QCOMPARE(format.formatByteSize(-1024.0, 1, KFormat::IECBinaryDialect), QStringLiteral("-1.0 KiB"));
|
||||
QCOMPARE(format.formatByteSize(-1023.0, 1, KFormat::IECBinaryDialect), QStringLiteral("-1,023 B"));
|
||||
QCOMPARE(format.formatByteSize(-1163000.0, 1, KFormat::IECBinaryDialect), QStringLiteral("-1.1 MiB")); // 1.2 metric
|
||||
|
||||
QCOMPARE(format.formatByteSize(1024.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("1.0 KB"));
|
||||
QCOMPARE(format.formatByteSize(1023.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("1,023 B"));
|
||||
QCOMPARE(format.formatByteSize(1163000.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("1.1 MB"));
|
||||
|
||||
QCOMPARE(format.formatByteSize(-1024.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("-1.0 KB"));
|
||||
QCOMPARE(format.formatByteSize(-1023.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("-1,023 B"));
|
||||
QCOMPARE(format.formatByteSize(-1163000.0, 1, KFormat::JEDECBinaryDialect), QStringLiteral("-1.1 MB"));
|
||||
|
||||
QCOMPARE(format.formatByteSize(1024.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("1.0 kB"));
|
||||
QCOMPARE(format.formatByteSize(1023.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("1.0 kB"));
|
||||
QCOMPARE(format.formatByteSize(1163000.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("1.2 MB"));
|
||||
|
||||
QCOMPARE(format.formatByteSize(-1024.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("-1.0 kB"));
|
||||
QCOMPARE(format.formatByteSize(-1023.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("-1.0 kB"));
|
||||
QCOMPARE(format.formatByteSize(-1163000.0, 1, KFormat::MetricBinaryDialect), QStringLiteral("-1.2 MB"));
|
||||
|
||||
// Ensure all units are represented
|
||||
QCOMPARE(format.formatByteSize(2.0e9, 1, KFormat::MetricBinaryDialect), QStringLiteral("2.0 GB"));
|
||||
QCOMPARE(format.formatByteSize(3.2e12, 1, KFormat::MetricBinaryDialect), QStringLiteral("3.2 TB"));
|
||||
QCOMPARE(format.formatByteSize(4.1e15, 1, KFormat::MetricBinaryDialect), QStringLiteral("4.1 PB"));
|
||||
QCOMPARE(format.formatByteSize(6.7e18, 2, KFormat::MetricBinaryDialect), QStringLiteral("6.70 EB"));
|
||||
QCOMPARE(format.formatByteSize(5.6e20, 2, KFormat::MetricBinaryDialect), QStringLiteral("560.00 EB"));
|
||||
QCOMPARE(format.formatByteSize(2.3e22, 2, KFormat::MetricBinaryDialect), QStringLiteral("23.00 ZB"));
|
||||
QCOMPARE(format.formatByteSize(1.0e27, 1, KFormat::MetricBinaryDialect), QStringLiteral("1,000.0 YB"));
|
||||
|
||||
// Spattering of specific units
|
||||
QCOMPARE(format.formatByteSize(823000, 3, KFormat::IECBinaryDialect, KFormat::UnitMegaByte), QStringLiteral("0.785 MiB"));
|
||||
QCOMPARE(format.formatByteSize(1234034.0, 4, KFormat::JEDECBinaryDialect, KFormat::UnitByte), QStringLiteral("1,234,034 B"));
|
||||
|
||||
// Check examples from the documentation
|
||||
QCOMPARE(format.formatByteSize(1000, 1, KFormat::MetricBinaryDialect, KFormat::UnitKiloByte), QStringLiteral("1.0 kB"));
|
||||
QCOMPARE(format.formatByteSize(1000, 1, KFormat::IECBinaryDialect, KFormat::UnitKiloByte), QStringLiteral("1.0 KiB"));
|
||||
QCOMPARE(format.formatByteSize(1000, 1, KFormat::JEDECBinaryDialect, KFormat::UnitKiloByte), QStringLiteral("1.0 KB"));
|
||||
}
|
||||
|
||||
void KFormatTest::formatValue()
|
||||
{
|
||||
QLocale locale(QLocale::c());
|
||||
locale.setNumberOptions(QLocale::DefaultNumberOptions); // Qt >= 5.6 sets QLocale::OmitGroupSeparator for the C locale
|
||||
KFormat format(locale);
|
||||
|
||||
// Check examples from the documentation
|
||||
QCOMPARE(format.formatValue(1000, KFormat::Unit::Byte, 1, KFormat::UnitPrefix::Kilo, KFormat::MetricBinaryDialect), QStringLiteral("1.0 kB"));
|
||||
QCOMPARE(format.formatValue(1000, KFormat::Unit::Byte, 1, KFormat::UnitPrefix::Kilo, KFormat::IECBinaryDialect), QStringLiteral("1.0 KiB"));
|
||||
QCOMPARE(format.formatValue(1000, KFormat::Unit::Byte, 1, KFormat::UnitPrefix::Kilo, KFormat::JEDECBinaryDialect), QStringLiteral("1.0 KB"));
|
||||
|
||||
// Check examples from the documentation
|
||||
QCOMPARE(format.formatValue(1000, KFormat::Unit::Bit, 1, KFormat::UnitPrefix::Kilo, KFormat::MetricBinaryDialect), QStringLiteral("1.0 kbit"));
|
||||
QCOMPARE(format.formatValue(1000, QStringLiteral("bit"), 1, KFormat::UnitPrefix::Kilo), QStringLiteral("1.0 kbit"));
|
||||
QCOMPARE(format.formatValue(1000, QStringLiteral("bit/s"), 1, KFormat::UnitPrefix::Kilo), QStringLiteral("1.0 kbit/s"));
|
||||
|
||||
QCOMPARE(format.formatValue(100, QStringLiteral("bit/s")), QStringLiteral("100.0 bit/s"));
|
||||
QCOMPARE(format.formatValue(1000, QStringLiteral("bit/s")), QStringLiteral("1.0 kbit/s"));
|
||||
QCOMPARE(format.formatValue(10e3, QStringLiteral("bit/s")), QStringLiteral("10.0 kbit/s"));
|
||||
QCOMPARE(format.formatValue(10e6, QStringLiteral("bit/s")), QStringLiteral("10.0 Mbit/s"));
|
||||
|
||||
QCOMPARE(format.formatValue(0.010, KFormat::Unit::Meter, 1, KFormat::UnitPrefix::Milli, KFormat::MetricBinaryDialect), QStringLiteral("10.0 mm"));
|
||||
QCOMPARE(format.formatValue(10.12e-6, KFormat::Unit::Meter, 2, KFormat::UnitPrefix::Micro, KFormat::MetricBinaryDialect), QString::fromUtf8("10.12 µm"));
|
||||
QCOMPARE(format.formatValue(10.55e-6, KFormat::Unit::Meter, 1, KFormat::UnitPrefix::AutoAdjust, KFormat::MetricBinaryDialect),
|
||||
QString::fromUtf8("10.6 µm"));
|
||||
}
|
||||
|
||||
enum TimeConstants {
|
||||
MSecsInDay = 86400000,
|
||||
MSecsInHour = 3600000,
|
||||
MSecsInMinute = 60000,
|
||||
MSecsInSecond = 1000,
|
||||
};
|
||||
|
||||
void KFormatTest::formatDuration()
|
||||
{
|
||||
KFormat format(QLocale::c());
|
||||
|
||||
quint64 singleSecond = 3 * MSecsInSecond + 700;
|
||||
quint64 doubleSecond = 33 * MSecsInSecond + 700;
|
||||
quint64 singleMinute = 8 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 doubleMinute = 38 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 singleHour = 5 * MSecsInHour + 8 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 doubleHour = 15 * MSecsInHour + 8 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 singleDay = 1 * MSecsInDay + 5 * MSecsInHour + 8 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 doubleDay = 10 * MSecsInDay + 5 * MSecsInHour + 8 * MSecsInMinute + 3 * MSecsInSecond + 700;
|
||||
quint64 roundingIssues = 2 * MSecsInHour + 59 * MSecsInMinute + 59 * MSecsInSecond + 900;
|
||||
quint64 largeValue = 9999999999;
|
||||
|
||||
// Default format
|
||||
QCOMPARE(format.formatDuration(singleSecond), QStringLiteral("0:00:04"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond), QStringLiteral("0:00:34"));
|
||||
QCOMPARE(format.formatDuration(singleMinute), QStringLiteral("0:08:04"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute), QStringLiteral("0:38:04"));
|
||||
QCOMPARE(format.formatDuration(singleHour), QStringLiteral("5:08:04"));
|
||||
QCOMPARE(format.formatDuration(doubleHour), QStringLiteral("15:08:04"));
|
||||
QCOMPARE(format.formatDuration(singleDay), QStringLiteral("29:08:04"));
|
||||
QCOMPARE(format.formatDuration(doubleDay), QStringLiteral("245:08:04"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues), QStringLiteral("3:00:00"));
|
||||
QCOMPARE(format.formatDuration(largeValue), QStringLiteral("2777:46:40"));
|
||||
|
||||
// ShowMilliseconds format
|
||||
KFormat::DurationFormatOptions options = KFormat::ShowMilliseconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0:00:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0:00:33.700"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("0:08:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("0:38:03.700"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("5:08:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("15:08:03.700"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("29:08:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("245:08:03.700"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("2:59:59.900"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("2777:46:39.999"));
|
||||
|
||||
// HideSeconds format
|
||||
options = KFormat::HideSeconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0:00"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0:01"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("0:08"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("0:38"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("5:08"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("15:08"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("29:08"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("245:08"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("3:00"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("2777:47"));
|
||||
|
||||
// FoldHours format
|
||||
options = KFormat::FoldHours;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0:04"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0:34"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("8:04"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("38:04"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("308:04"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("908:04"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("1748:04"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("14708:04"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("180:00"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("166666:40"));
|
||||
|
||||
// FoldHours ShowMilliseconds format
|
||||
options = KFormat::FoldHours;
|
||||
options = options | KFormat::ShowMilliseconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0:33.700"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("8:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("38:03.700"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("308:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("908:03.700"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("1748:03.700"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("14708:03.700"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("179:59.900"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("166666:39.999"));
|
||||
|
||||
// InitialDuration format
|
||||
options = KFormat::InitialDuration;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0h00m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0h00m34s"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("0h08m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("0h38m04s"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("5h08m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("15h08m04s"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("29h08m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("245h08m04s"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("3h00m00s"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("2777h46m40s"));
|
||||
|
||||
// InitialDuration and ShowMilliseconds format
|
||||
options = KFormat::InitialDuration;
|
||||
options = options | KFormat::ShowMilliseconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0h00m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0h00m33.700s"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("0h08m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("0h38m03.700s"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("5h08m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("15h08m03.700s"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("29h08m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("245h08m03.700s"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("2h59m59.900s"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("2777h46m39.999s"));
|
||||
|
||||
// InitialDuration and HideSeconds format
|
||||
options = KFormat::InitialDuration;
|
||||
options = options | KFormat::HideSeconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0h00m"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0h01m"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("0h08m"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("0h38m"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("5h08m"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("15h08m"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("29h08m"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("245h08m"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("3h00m"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("2777h47m"));
|
||||
|
||||
// InitialDuration and FoldHours format
|
||||
options = KFormat::InitialDuration;
|
||||
options = options | KFormat::FoldHours;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0m34s"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("8m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("38m04s"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("308m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("908m04s"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("1748m04s"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("14708m04s"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("180m00s"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("166666m40s"));
|
||||
|
||||
// InitialDuration and FoldHours and ShowMilliseconds format
|
||||
options = KFormat::InitialDuration;
|
||||
options = options | KFormat::FoldHours | KFormat::ShowMilliseconds;
|
||||
QCOMPARE(format.formatDuration(singleSecond, options), QStringLiteral("0m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleSecond, options), QStringLiteral("0m33.700s"));
|
||||
QCOMPARE(format.formatDuration(singleMinute, options), QStringLiteral("8m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleMinute, options), QStringLiteral("38m03.700s"));
|
||||
QCOMPARE(format.formatDuration(singleHour, options), QStringLiteral("308m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleHour, options), QStringLiteral("908m03.700s"));
|
||||
QCOMPARE(format.formatDuration(singleDay, options), QStringLiteral("1748m03.700s"));
|
||||
QCOMPARE(format.formatDuration(doubleDay, options), QStringLiteral("14708m03.700s"));
|
||||
QCOMPARE(format.formatDuration(roundingIssues, options), QStringLiteral("179m59.900s"));
|
||||
QCOMPARE(format.formatDuration(largeValue, options), QStringLiteral("166666m39.999s"));
|
||||
}
|
||||
|
||||
void KFormatTest::formatDecimalDuration()
|
||||
{
|
||||
KFormat format(QLocale::c());
|
||||
|
||||
QCOMPARE(format.formatDecimalDuration(10), QStringLiteral("10 millisecond(s)"));
|
||||
QCOMPARE(format.formatDecimalDuration(10, 3), QStringLiteral("10 millisecond(s)"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInSecond + 10), QStringLiteral("1.01 seconds"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInSecond + 1, 3), QStringLiteral("1.001 seconds"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInMinute + 10 * MSecsInSecond), QStringLiteral("1.17 minutes"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInMinute + 10 * MSecsInSecond, 3), QStringLiteral("1.167 minutes"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInHour + 10 * MSecsInMinute), QStringLiteral("1.17 hours"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInHour + 10 * MSecsInMinute, 3), QStringLiteral("1.167 hours"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInDay + 10 * MSecsInHour), QStringLiteral("1.42 days"));
|
||||
QCOMPARE(format.formatDecimalDuration(1 * MSecsInDay + 10 * MSecsInHour, 3), QStringLiteral("1.417 days"));
|
||||
}
|
||||
|
||||
void KFormatTest::formatSpelloutDuration()
|
||||
{
|
||||
KFormat format(QLocale::c());
|
||||
|
||||
QCOMPARE(format.formatSpelloutDuration(1000), QStringLiteral("1 second(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(5000), QStringLiteral("5 second(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(60000), QStringLiteral("1 minute(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(300000), QStringLiteral("5 minute(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(3600000), QStringLiteral("1 hour(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(18000000), QStringLiteral("5 hour(s)"));
|
||||
QCOMPARE(format.formatSpelloutDuration(75000), QStringLiteral("1 minute(s) and 15 second(s)"));
|
||||
// Problematic case #1 (there is a reference to this case on kformat.cpp)
|
||||
QCOMPARE(format.formatSpelloutDuration(119999), QStringLiteral("2 minute(s)"));
|
||||
// This case is strictly 2 hours, 15 minutes and 59 seconds. However, since the range is
|
||||
// pretty high between hours and seconds, formatSpelloutDuration always omits seconds when there
|
||||
// are hours in scene.
|
||||
QCOMPARE(format.formatSpelloutDuration(8159000), QStringLiteral("2 hour(s) and 15 minute(s)"));
|
||||
// This case is strictly 1 hour and 10 seconds. For the same reason, formatSpelloutDuration
|
||||
// detects that 10 seconds is just garbage compared to 1 hour, and omits it on the result.
|
||||
QCOMPARE(format.formatSpelloutDuration(3610000), QStringLiteral("1 hour(s)"));
|
||||
}
|
||||
|
||||
void KFormatTest::formatRelativeDate()
|
||||
{
|
||||
KFormat format(QLocale::c());
|
||||
|
||||
QDate testDate = QDate::currentDate();
|
||||
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("Today"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QStringLiteral("Today"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QStringLiteral("Today"));
|
||||
|
||||
testDate = QDate::currentDate().addDays(1);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("Tomorrow"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QStringLiteral("Tomorrow"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QStringLiteral("Tomorrow"));
|
||||
|
||||
testDate = QDate::currentDate().addDays(-1);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("Yesterday"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QStringLiteral("Yesterday"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QStringLiteral("Yesterday"));
|
||||
|
||||
testDate = QDate::currentDate().addDays(2);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("In two days"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QStringLiteral("In two days"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QStringLiteral("In two days"));
|
||||
|
||||
testDate = QDate::currentDate().addDays(-2);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("Two days ago"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QStringLiteral("Two days ago"));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QStringLiteral("Two days ago"));
|
||||
|
||||
testDate = QDate::currentDate().addDays(-3);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QLocale::c().toString(testDate, QLocale::LongFormat));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QLocale::c().toString(testDate, QLocale::ShortFormat));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QLocale::c().toString(testDate, QLocale::NarrowFormat));
|
||||
|
||||
testDate = QDate::currentDate().addDays(3);
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QLocale::c().toString(testDate, QLocale::LongFormat));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::ShortFormat), QLocale::c().toString(testDate, QLocale::ShortFormat));
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::NarrowFormat), QLocale::c().toString(testDate, QLocale::NarrowFormat));
|
||||
|
||||
testDate = QDate(); // invalid date
|
||||
QCOMPARE(format.formatRelativeDate(testDate, QLocale::LongFormat), QStringLiteral("Invalid date"));
|
||||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
// An hour ago is **usually** today, except after midnight; just bump
|
||||
// to after 2am to make the "today" test work.
|
||||
if (now.time().hour() == 0) {
|
||||
now = now.addSecs(7201);
|
||||
}
|
||||
|
||||
QDateTime testDateTime = now.addSecs(-3600);
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::ShortFormat),
|
||||
QStringLiteral("Today at %1").arg(testDateTime.toString(QStringLiteral("hh:mm:ss"))));
|
||||
|
||||
// 1 second ago
|
||||
now = QDateTime::currentDateTime();
|
||||
testDateTime = now.addSecs(-1);
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::ShortFormat), QStringLiteral("Just now"));
|
||||
|
||||
// 5 minutes ago
|
||||
testDateTime = now.addSecs(-300);
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::ShortFormat), QStringLiteral("5 minute(s) ago"));
|
||||
|
||||
testDateTime = QDateTime(QDate::currentDate().addDays(8), QTime(3, 0, 0));
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::LongFormat),
|
||||
QStringLiteral("%1 at %2")
|
||||
.arg(QLocale::c().toString(testDateTime.date(), QLocale::LongFormat), QLocale::c().toString(testDateTime.time(), QLocale::ShortFormat)));
|
||||
|
||||
// 2021-10-03 07:33:57.000
|
||||
testDateTime = QDateTime::fromMSecsSinceEpoch(1633239237000, QTimeZone::utc());
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::LongFormat),
|
||||
QStringLiteral("%1 at %2")
|
||||
.arg(QLocale::c().toString(testDateTime.date(), QLocale::LongFormat), QLocale::c().toString(testDateTime.time(), QLocale::ShortFormat)));
|
||||
QCOMPARE(format.formatRelativeDateTime(testDateTime, QLocale::ShortFormat),
|
||||
QStringLiteral("%1 at %2")
|
||||
.arg(QLocale::c().toString(testDateTime.date(), QLocale::ShortFormat), QLocale::c().toString(testDateTime.time(), QLocale::ShortFormat)));
|
||||
|
||||
// With a different local for double check
|
||||
QLocale frenchLocal = QLocale::French;
|
||||
KFormat formatFrench(frenchLocal);
|
||||
QCOMPARE(formatFrench.formatRelativeDateTime(testDateTime, QLocale::LongFormat), QStringLiteral("Dimanche 3 octobre 2021 at 05:33"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(KFormatTest)
|
||||
|
||||
#include "moc_kformattest.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of the KDE Frameworks
|
||||
|
||||
SPDX-FileCopyrightText: 2013 John Layt <jlayt@kde.org>
|
||||
SPDX-FileCopyrightText: 2010 Michael Leupold <lemma@confuego.org>
|
||||
SPDX-FileCopyrightText: 2009 Michael Pyne <mpyne@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Albert Astals Cid <aacid@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFORMATTEST_H
|
||||
#define KFORMATTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KFormatTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void formatByteSize();
|
||||
void formatDuration();
|
||||
void formatDecimalDuration();
|
||||
void formatSpelloutDuration();
|
||||
void formatRelativeDate();
|
||||
void formatValue();
|
||||
};
|
||||
|
||||
#endif // KFORMATTEST_H
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include "kfuzzymatchertest.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTest>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "kfuzzymatcher.h"
|
||||
|
||||
QTEST_MAIN(KFuzzyMatcherTest)
|
||||
|
||||
void KFuzzyMatcherTest::testMatchSimple_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::addColumn<QString>("inputstr");
|
||||
QTest::addColumn<bool>("expected");
|
||||
|
||||
QTest::newRow("AbcD") << QStringLiteral("AbcD") << QStringLiteral("AbCdefg") << true;
|
||||
QTest::newRow("WithSpace") << QStringLiteral("Wa qa") << QStringLiteral("Wa qar") << true;
|
||||
QTest::newRow("RTL") << QStringLiteral("ارو") << QStringLiteral("اردو") << true;
|
||||
QTest::newRow("WithSep") << QStringLiteral("tf") << QStringLiteral("the_file") << true;
|
||||
QTest::newRow("Umlaut") << QStringLiteral("Häu") << QStringLiteral("Häuser") << true;
|
||||
QTest::newRow("Unmatched") << QStringLiteral("Name") << QStringLiteral("Nam") << false;
|
||||
QTest::newRow("Empty Pattern") << QStringLiteral("") << QStringLiteral("Nam") << true;
|
||||
}
|
||||
|
||||
void KFuzzyMatcherTest::testMatchSimple()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
QFETCH(QString, inputstr);
|
||||
QFETCH(bool, expected);
|
||||
|
||||
QVERIFY(KFuzzyMatcher::matchSimple(pattern, inputstr) == expected);
|
||||
}
|
||||
|
||||
void KFuzzyMatcherTest::testMatch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::addColumn<QStringList>("input");
|
||||
QTest::addColumn<QStringList>("expected");
|
||||
QTest::addColumn<int>("size");
|
||||
// clang-format off
|
||||
QTest::newRow("pattern=sort") << QStringLiteral("sort")
|
||||
<< QStringList{
|
||||
QStringLiteral("Sort"),
|
||||
QStringLiteral("Some other right test"),
|
||||
QStringLiteral("Soup rate"),
|
||||
QStringLiteral("Someother"),
|
||||
QStringLiteral("irrelevant"),
|
||||
}
|
||||
<< QStringList{
|
||||
QStringLiteral("Sort"),
|
||||
QStringLiteral("Some other right test"),
|
||||
QStringLiteral("Soup rate"),
|
||||
}
|
||||
<< 3;
|
||||
|
||||
|
||||
QTest::newRow("pattern=kateapp") << QStringLiteral("kaapp")
|
||||
<< QStringList{
|
||||
QStringLiteral("kateapp.cpp"),
|
||||
QStringLiteral("kate_application"),
|
||||
QStringLiteral("kateapp.h"),
|
||||
QStringLiteral("katepap.c")
|
||||
}
|
||||
<< QStringList{
|
||||
QStringLiteral("kate_application"),
|
||||
QStringLiteral("kateapp.h"),
|
||||
QStringLiteral("kateapp.cpp")
|
||||
}
|
||||
<< 3;
|
||||
|
||||
QTest::newRow("pattern=this") << QStringLiteral("this")
|
||||
<< QStringList{
|
||||
QStringLiteral("th"),
|
||||
QStringLiteral("ths"),
|
||||
QStringLiteral("thsi")
|
||||
}
|
||||
<< QStringList{
|
||||
}
|
||||
<< 0;
|
||||
|
||||
QTest::newRow("pattern=marath") << QStringLiteral("marath")
|
||||
<< QStringList{
|
||||
QStringLiteral("Maralen of the Mornsong"),
|
||||
QStringLiteral("Silumgar, the Drifting Death"),
|
||||
QStringLiteral("Maralen of the Mornsong Avatar"),
|
||||
QStringLiteral("Marshaling the Troops"),
|
||||
QStringLiteral("Homeward Path"),
|
||||
QStringLiteral("Marath, Will of the Wild"),
|
||||
QStringLiteral("Marshal's Anthem"),
|
||||
QStringLiteral("Marchesa, the Black Rose"),
|
||||
QStringLiteral("Mark for Death"),
|
||||
QStringLiteral("Master Apothecary"),
|
||||
QStringLiteral("Mazirek, Kraul Death Priest"),
|
||||
QStringLiteral("Akroma, Angel of Wrath"),
|
||||
QStringLiteral("Akroma, Angel of Wrath Avatar"),
|
||||
QStringLiteral("Commander's Authority"),
|
||||
QStringLiteral("Shaman of the Great Hunt"),
|
||||
QStringLiteral("Halimar Wavewatch"),
|
||||
QStringLiteral("Pyromancer's Swath")
|
||||
}
|
||||
<< QStringList{
|
||||
QStringLiteral("Marath, Will of the Wild"),
|
||||
QStringLiteral("Maralen of the Mornsong"),
|
||||
QStringLiteral("Maralen of the Mornsong Avatar"),
|
||||
QStringLiteral("Marshal's Anthem"),
|
||||
QStringLiteral("Marshaling the Troops"),
|
||||
QStringLiteral("Marchesa, the Black Rose"),
|
||||
QStringLiteral("Mark for Death"),
|
||||
QStringLiteral("Master Apothecary"),
|
||||
QStringLiteral("Mazirek, Kraul Death Priest"),
|
||||
QStringLiteral("Akroma, Angel of Wrath"),
|
||||
QStringLiteral("Akroma, Angel of Wrath Avatar"),
|
||||
QStringLiteral("Commander's Authority"),
|
||||
QStringLiteral("Homeward Path"),
|
||||
QStringLiteral("Shaman of the Great Hunt"),
|
||||
QStringLiteral("Halimar Wavewatch"),
|
||||
QStringLiteral("Pyromancer's Swath"),
|
||||
QStringLiteral("Silumgar, the Drifting Death")
|
||||
}
|
||||
<< 17;
|
||||
|
||||
// This tests our recursive best match
|
||||
QTest::newRow("pattern=lll") << QStringLiteral("lll")
|
||||
<< QStringList{
|
||||
QStringLiteral("SVisualLoggerLogsList.h"),
|
||||
QStringLiteral("SimpleFileLogger.cpp"),
|
||||
QStringLiteral("StringHandlerLogList.txt"),
|
||||
QStringLiteral("LeapFromLostAllan"),
|
||||
QStringLiteral("BumpLLL"),
|
||||
}
|
||||
<< QStringList{
|
||||
QStringLiteral("SVisualLoggerLogsList.h"),
|
||||
QStringLiteral("LeapFromLostAllan"),
|
||||
QStringLiteral("BumpLLL"),
|
||||
QStringLiteral("StringHandlerLogList.txt"),
|
||||
QStringLiteral("SimpleFileLogger.cpp"),
|
||||
}
|
||||
<< 5;
|
||||
|
||||
QTest::newRow("pattern=") << QStringLiteral("")
|
||||
<< QStringList{
|
||||
QStringLiteral("th"),
|
||||
QStringLiteral("ths"),
|
||||
QStringLiteral("thsi")
|
||||
}
|
||||
<< QStringList{
|
||||
QStringLiteral("th"),
|
||||
QStringLiteral("ths"),
|
||||
QStringLiteral("thsi")
|
||||
}
|
||||
<< 3;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static QStringList matchHelper(const QString &pattern, const QStringList &input)
|
||||
{
|
||||
QList<QPair<QString, int>> actual;
|
||||
for (int i = 0; i < input.size(); ++i) {
|
||||
KFuzzyMatcher::Result res = KFuzzyMatcher::match(pattern, input.at(i));
|
||||
if (res.matched) {
|
||||
actual.push_back({input.at(i), res.score});
|
||||
}
|
||||
}
|
||||
|
||||
// sort descending based on score
|
||||
std::sort(actual.begin(), actual.end(), [](const QPair<QString, int> &l, const QPair<QString, int> &r) {
|
||||
return l.second > r.second;
|
||||
});
|
||||
|
||||
QStringList actualOut;
|
||||
for (const auto &s : actual) {
|
||||
actualOut << s.first;
|
||||
}
|
||||
return actualOut;
|
||||
}
|
||||
|
||||
void KFuzzyMatcherTest::testMatch()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
QFETCH(QStringList, input);
|
||||
QFETCH(QStringList, expected);
|
||||
QFETCH(int, size);
|
||||
|
||||
const QStringList actual = matchHelper(pattern, input);
|
||||
|
||||
QCOMPARE(actual.size(), size);
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void KFuzzyMatcherTest::testMatchedRanges_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::addColumn<QString>("string");
|
||||
|
||||
using Range = QPair<int, int>;
|
||||
QTest::addColumn<QList<Range>>("expectedRanges");
|
||||
|
||||
QTest::addColumn<bool>("matchingOnly");
|
||||
|
||||
QTest::newRow("Emtpy") << QStringLiteral("") << QStringLiteral("") << QList<Range>{} << true;
|
||||
QTest::newRow("Hello") << QStringLiteral("Hlo") << QStringLiteral("Hello") << QList<Range>{{0, 1}, {3, 2}} << true;
|
||||
QTest::newRow("lll") << QStringLiteral("lll") << QStringLiteral("SVisualLoggerLogsList") << QList<Range>{{7, 1}, {13, 1}, {17, 1}} << true;
|
||||
QTest::newRow("Sort") << QStringLiteral("sort") << QStringLiteral("SorT") << QList<Range>{{0, 4}} << true;
|
||||
QTest::newRow("Unmatching") << QStringLiteral("git") << QStringLiteral("gti") << QList<Range>{} << true;
|
||||
QTest::newRow("UnmatchingWithAllMatches") << QStringLiteral("git") << QStringLiteral("gti") << QList<Range>{{0, 1}, {2, 1}} << false;
|
||||
}
|
||||
|
||||
void KFuzzyMatcherTest::testMatchedRanges()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
QFETCH(QString, string);
|
||||
QFETCH(bool, matchingOnly);
|
||||
using Range = QPair<int, int>;
|
||||
QFETCH(QList<Range>, expectedRanges);
|
||||
|
||||
const auto matchMode = matchingOnly ? KFuzzyMatcher::RangeType::FullyMatched : KFuzzyMatcher::RangeType::All;
|
||||
|
||||
auto resultRanges = KFuzzyMatcher::matchedRanges(pattern, string, matchMode);
|
||||
QCOMPARE(resultRanges.size(), expectedRanges.size());
|
||||
|
||||
bool res = std::equal(expectedRanges.begin(), expectedRanges.end(), resultRanges.begin(), [](const Range &l, const KFuzzyMatcher::Range &r) {
|
||||
return l.first == r.start && l.second == r.length;
|
||||
});
|
||||
QVERIFY(res);
|
||||
}
|
||||
|
||||
#include "moc_kfuzzymatchertest.cpp"
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KFUZZYMATCHERTEST_H
|
||||
#define KFUZZYMATCHERTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KFuzzyMatcherTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testMatchSimple_data();
|
||||
void testMatchSimple();
|
||||
void testMatch_data();
|
||||
void testMatch();
|
||||
void testMatchedRanges_data();
|
||||
void testMatchedRanges();
|
||||
};
|
||||
|
||||
#endif // KFUZZYMATCHERTEST_H
|
||||
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kjobtest.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMetaEnum>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QTimer>
|
||||
|
||||
#include <string>
|
||||
|
||||
QTEST_MAIN(KJobTest)
|
||||
|
||||
KJobTest::KJobTest()
|
||||
: loop(this)
|
||||
{
|
||||
}
|
||||
|
||||
void KJobTest::testEmitResult_data()
|
||||
{
|
||||
QTest::addColumn<int>("errorCode");
|
||||
QTest::addColumn<QString>("errorText");
|
||||
|
||||
QTest::newRow("no error") << int(KJob::NoError) << QString();
|
||||
QTest::newRow("error no text") << 2 << QString();
|
||||
QTest::newRow("error with text") << 6 << "oops! an error? naaah, really?";
|
||||
}
|
||||
|
||||
void KJobTest::testEmitResult()
|
||||
{
|
||||
TestJob *job = new TestJob;
|
||||
|
||||
connect(job, &KJob::result, this, [this](KJob *job) {
|
||||
slotResult(job);
|
||||
loop.quit();
|
||||
});
|
||||
|
||||
QFETCH(int, errorCode);
|
||||
QFETCH(QString, errorText);
|
||||
|
||||
job->setError(errorCode);
|
||||
job->setErrorText(errorText);
|
||||
|
||||
QSignalSpy destroyed_spy(job, &QObject::destroyed);
|
||||
job->start();
|
||||
QVERIFY(!job->isFinished());
|
||||
loop.exec();
|
||||
QVERIFY(job->isFinished());
|
||||
|
||||
QCOMPARE(m_lastError, errorCode);
|
||||
QCOMPARE(m_lastErrorText, errorText);
|
||||
|
||||
// Verify that the job is not deleted immediately...
|
||||
QCOMPARE(destroyed_spy.size(), 0);
|
||||
QTimer::singleShot(0, &loop, &QEventLoop::quit);
|
||||
// ... but when we enter the event loop again.
|
||||
loop.exec();
|
||||
QCOMPARE(destroyed_spy.size(), 1);
|
||||
}
|
||||
|
||||
void KJobTest::testProgressTracking()
|
||||
{
|
||||
TestJob *testJob = new TestJob;
|
||||
KJob *job = testJob;
|
||||
|
||||
qRegisterMetaType<KJob *>("KJob*");
|
||||
qRegisterMetaType<qulonglong>("qulonglong");
|
||||
|
||||
QSignalSpy processedChanged_spy(job, &KJob::processedAmountChanged);
|
||||
QSignalSpy totalChanged_spy(job, &KJob::totalAmountChanged);
|
||||
QSignalSpy percentChanged_spy(job, &KJob::percentChanged);
|
||||
|
||||
/* Process a first item. Corresponding signal should be emitted.
|
||||
* Total size didn't change.
|
||||
* Since the total size is unknown, no percent signal is emitted.
|
||||
*/
|
||||
testJob->setProcessedSize(1);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 1);
|
||||
QCOMPARE(processedChanged_spy.at(0).at(0).value<KJob *>(), static_cast<KJob *>(job));
|
||||
QCOMPARE(processedChanged_spy.at(0).at(2).value<qulonglong>(), qulonglong(1));
|
||||
QCOMPARE(totalChanged_spy.size(), 0);
|
||||
QCOMPARE(percentChanged_spy.size(), 0);
|
||||
|
||||
/* Now, we know the total size. It's signaled.
|
||||
* The new percentage is signaled too.
|
||||
*/
|
||||
testJob->setTotalSize(10);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 1);
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(totalChanged_spy.at(0).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(totalChanged_spy.at(0).at(2).value<qulonglong>(), qulonglong(10));
|
||||
QCOMPARE(percentChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.at(0).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(percentChanged_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(10));
|
||||
|
||||
/* We announce a new percentage by hand.
|
||||
* Total, and processed didn't change, so no signal is emitted for them.
|
||||
*/
|
||||
testJob->setPercent(15);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 1);
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.size(), 2);
|
||||
QCOMPARE(percentChanged_spy.at(1).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(percentChanged_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(15));
|
||||
|
||||
/* We make some progress.
|
||||
* Processed size and percent are signaled.
|
||||
*/
|
||||
testJob->setProcessedSize(3);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 2);
|
||||
QCOMPARE(processedChanged_spy.at(1).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(processedChanged_spy.at(1).at(2).value<qulonglong>(), qulonglong(3));
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.size(), 3);
|
||||
QCOMPARE(percentChanged_spy.at(2).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(percentChanged_spy.at(2).at(1).value<unsigned long>(), static_cast<unsigned long>(30));
|
||||
|
||||
/* We set a new total size, but equals to the previous one.
|
||||
* No signal is emitted.
|
||||
*/
|
||||
testJob->setTotalSize(10);
|
||||
QCOMPARE(processedChanged_spy.size(), 2);
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.size(), 3);
|
||||
|
||||
/* We 'lost' the previous work done.
|
||||
* Signals both percentage and new processed size.
|
||||
*/
|
||||
testJob->setProcessedSize(0);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 3);
|
||||
QCOMPARE(processedChanged_spy.at(2).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(processedChanged_spy.at(2).at(2).value<qulonglong>(), qulonglong(0));
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.size(), 4);
|
||||
QCOMPARE(percentChanged_spy.at(3).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(percentChanged_spy.at(3).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
|
||||
|
||||
/* We process more than the total size!?
|
||||
* Signals both percentage and new processed size.
|
||||
* Percentage is 150%
|
||||
*
|
||||
* Might sounds weird, but verify that this case is handled gracefully.
|
||||
*/
|
||||
testJob->setProcessedSize(15);
|
||||
|
||||
QCOMPARE(processedChanged_spy.size(), 4);
|
||||
QCOMPARE(processedChanged_spy.at(3).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(processedChanged_spy.at(3).at(2).value<qulonglong>(), qulonglong(15));
|
||||
QCOMPARE(totalChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.size(), 5);
|
||||
QCOMPARE(percentChanged_spy.at(4).at(0).value<KJob *>(), job);
|
||||
QCOMPARE(percentChanged_spy.at(4).at(1).value<unsigned long>(), static_cast<unsigned long>(150));
|
||||
|
||||
processedChanged_spy.clear();
|
||||
totalChanged_spy.clear();
|
||||
percentChanged_spy.clear();
|
||||
|
||||
/**
|
||||
* Try again with Files as the progress unit
|
||||
*/
|
||||
testJob->setProgressUnit(KJob::Files);
|
||||
testJob->setProcessedSize(16);
|
||||
QCOMPARE(percentChanged_spy.size(), 0);
|
||||
|
||||
testJob->setTotalFiles(5);
|
||||
QCOMPARE(percentChanged_spy.size(), 1);
|
||||
QCOMPARE(percentChanged_spy.at(0).at(1).value<unsigned long>(), static_cast<unsigned long>(0));
|
||||
|
||||
testJob->setProcessedFiles(2);
|
||||
QCOMPARE(percentChanged_spy.size(), 2);
|
||||
QCOMPARE(percentChanged_spy.at(1).at(1).value<unsigned long>(), static_cast<unsigned long>(40));
|
||||
|
||||
delete job;
|
||||
}
|
||||
|
||||
void KJobTest::testExec_data()
|
||||
{
|
||||
QTest::addColumn<int>("errorCode");
|
||||
QTest::addColumn<QString>("errorText");
|
||||
|
||||
QTest::newRow("no error") << int(KJob::NoError) << QString();
|
||||
QTest::newRow("error no text") << 2 << QString();
|
||||
QTest::newRow("error with text") << 6 << "oops! an error? naaah, really?";
|
||||
}
|
||||
|
||||
void KJobTest::testExec()
|
||||
{
|
||||
TestJob *job = new TestJob;
|
||||
|
||||
QFETCH(int, errorCode);
|
||||
QFETCH(QString, errorText);
|
||||
|
||||
job->setError(errorCode);
|
||||
job->setErrorText(errorText);
|
||||
|
||||
int resultEmitted = 0;
|
||||
// Prove to Kai Uwe that one can connect a job to a lambdas, despite the "private" signal
|
||||
connect(job, &KJob::result, this, [&resultEmitted](KJob *) {
|
||||
++resultEmitted;
|
||||
});
|
||||
|
||||
QSignalSpy destroyed_spy(job, &QObject::destroyed);
|
||||
|
||||
QVERIFY(!job->isFinished());
|
||||
bool status = job->exec();
|
||||
QVERIFY(job->isFinished());
|
||||
|
||||
QCOMPARE(resultEmitted, 1);
|
||||
QCOMPARE(status, (errorCode == KJob::NoError));
|
||||
QCOMPARE(job->error(), errorCode);
|
||||
QCOMPARE(job->errorText(), errorText);
|
||||
|
||||
// Verify that the job is not deleted immediately...
|
||||
QCOMPARE(destroyed_spy.size(), 0);
|
||||
QTimer::singleShot(0, &loop, &QEventLoop::quit);
|
||||
// ... but when we enter the event loop again.
|
||||
loop.exec();
|
||||
QCOMPARE(destroyed_spy.size(), 1);
|
||||
}
|
||||
|
||||
void KJobTest::testKill_data()
|
||||
{
|
||||
QTest::addColumn<int>("killVerbosity");
|
||||
QTest::addColumn<int>("errorCode");
|
||||
QTest::addColumn<QString>("errorText");
|
||||
QTest::addColumn<int>("resultEmitCount");
|
||||
QTest::addColumn<int>("finishedEmitCount");
|
||||
|
||||
QTest::newRow("killed with result") << int(KJob::EmitResult) << int(KJob::KilledJobError) << QString() << 1 << 1;
|
||||
QTest::newRow("killed quietly") << int(KJob::Quietly) << int(KJob::KilledJobError) << QString() << 0 << 1;
|
||||
}
|
||||
|
||||
void KJobTest::testKill()
|
||||
{
|
||||
auto *const job = setupErrorResultFinished();
|
||||
QSignalSpy destroyed_spy(job, &QObject::destroyed);
|
||||
|
||||
QFETCH(int, killVerbosity);
|
||||
QFETCH(int, errorCode);
|
||||
QFETCH(QString, errorText);
|
||||
QFETCH(int, resultEmitCount);
|
||||
QFETCH(int, finishedEmitCount);
|
||||
|
||||
QVERIFY(!job->isFinished());
|
||||
job->kill(static_cast<KJob::KillVerbosity>(killVerbosity));
|
||||
QVERIFY(job->isFinished());
|
||||
loop.processEvents(QEventLoop::AllEvents, 2000);
|
||||
|
||||
QCOMPARE(m_lastError, errorCode);
|
||||
QCOMPARE(m_lastErrorText, errorText);
|
||||
|
||||
QCOMPARE(job->error(), errorCode);
|
||||
QCOMPARE(job->errorText(), errorText);
|
||||
|
||||
QCOMPARE(m_resultCount, resultEmitCount);
|
||||
QCOMPARE(m_finishedCount, finishedEmitCount);
|
||||
|
||||
// Verify that the job is not deleted immediately...
|
||||
QCOMPARE(destroyed_spy.size(), 0);
|
||||
QTimer::singleShot(0, &loop, &QEventLoop::quit);
|
||||
// ... but when we enter the event loop again.
|
||||
loop.exec();
|
||||
QCOMPARE(destroyed_spy.size(), 1);
|
||||
|
||||
QVERIFY(m_jobFinishCount.size() == (finishedEmitCount - resultEmitCount));
|
||||
m_jobFinishCount.clear();
|
||||
}
|
||||
|
||||
void KJobTest::testDestroy()
|
||||
{
|
||||
auto *const job = setupErrorResultFinished();
|
||||
QVERIFY(!job->isFinished());
|
||||
delete job;
|
||||
QCOMPARE(m_lastError, static_cast<int>(KJob::NoError));
|
||||
QCOMPARE(m_lastErrorText, QString{});
|
||||
QCOMPARE(m_resultCount, 0);
|
||||
QCOMPARE(m_finishedCount, 1);
|
||||
|
||||
QVERIFY(m_jobFinishCount.size() == 1);
|
||||
m_jobFinishCount.clear();
|
||||
}
|
||||
|
||||
void KJobTest::testEmitAtMostOnce_data()
|
||||
{
|
||||
QTest::addColumn<bool>("autoDelete");
|
||||
QTest::addColumn<QList<Action>>("actions");
|
||||
|
||||
const auto actionName = [](Action action) {
|
||||
return QMetaEnum::fromType<Action>().valueToKey(static_cast<int>(action));
|
||||
};
|
||||
|
||||
for (bool autoDelete : {true, false}) {
|
||||
for (Action a : {Action::Start, Action::KillQuietly, Action::KillVerbosely}) {
|
||||
for (Action b : {Action::Start, Action::KillQuietly, Action::KillVerbosely}) {
|
||||
const auto dataTag = std::string{actionName(a)} + '-' + actionName(b) + (autoDelete ? "-autoDelete" : "");
|
||||
QTest::newRow(dataTag.c_str()) << autoDelete << QList<Action>{a, b};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KJobTest::testEmitAtMostOnce()
|
||||
{
|
||||
auto *const job = setupErrorResultFinished();
|
||||
QSignalSpy destroyed_spy(job, &QObject::destroyed);
|
||||
|
||||
QFETCH(bool, autoDelete);
|
||||
job->setAutoDelete(autoDelete);
|
||||
|
||||
QFETCH(QList<Action>, actions);
|
||||
for (auto action : actions) {
|
||||
switch (action) {
|
||||
case Action::Start:
|
||||
job->start(); // in effect calls QTimer::singleShot(0, ... emitResult)
|
||||
break;
|
||||
case Action::KillQuietly:
|
||||
QTimer::singleShot(0, job, [=] {
|
||||
job->kill(KJob::Quietly);
|
||||
});
|
||||
break;
|
||||
case Action::KillVerbosely:
|
||||
QTimer::singleShot(0, job, [=] {
|
||||
job->kill(KJob::EmitResult);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QVERIFY(!job->isFinished());
|
||||
loop.processEvents(QEventLoop::AllEvents, 2000);
|
||||
QCOMPARE(destroyed_spy.size(), autoDelete);
|
||||
if (!autoDelete) {
|
||||
QVERIFY(job->isFinished());
|
||||
}
|
||||
|
||||
QVERIFY(!actions.empty());
|
||||
// The first action alone should determine the job's error and result.
|
||||
const auto firstAction = actions.front();
|
||||
|
||||
const int errorCode = firstAction == Action::Start ? KJob::NoError : KJob::KilledJobError;
|
||||
QCOMPARE(m_lastError, errorCode);
|
||||
QCOMPARE(m_lastErrorText, QString{});
|
||||
if (!autoDelete) {
|
||||
QCOMPARE(job->error(), m_lastError);
|
||||
QCOMPARE(job->errorText(), m_lastErrorText);
|
||||
}
|
||||
|
||||
QCOMPARE(m_resultCount, firstAction == Action::KillQuietly ? 0 : 1);
|
||||
QCOMPARE(m_finishedCount, 1);
|
||||
|
||||
if (!autoDelete) {
|
||||
delete job;
|
||||
}
|
||||
}
|
||||
|
||||
void KJobTest::testDelegateUsage()
|
||||
{
|
||||
TestJob *job1 = new TestJob;
|
||||
TestJob *job2 = new TestJob;
|
||||
TestJobUiDelegate *delegate = new TestJobUiDelegate;
|
||||
QPointer<TestJobUiDelegate> guard(delegate);
|
||||
|
||||
QVERIFY(job1->uiDelegate() == nullptr);
|
||||
job1->setUiDelegate(delegate);
|
||||
QVERIFY(job1->uiDelegate() == delegate);
|
||||
|
||||
QVERIFY(job2->uiDelegate() == nullptr);
|
||||
job2->setUiDelegate(delegate);
|
||||
QVERIFY(job2->uiDelegate() == nullptr);
|
||||
|
||||
delete job1;
|
||||
delete job2;
|
||||
QVERIFY(guard.isNull()); // deleted by job1
|
||||
}
|
||||
|
||||
void KJobTest::testNestedExec()
|
||||
{
|
||||
m_innerJob = nullptr;
|
||||
QTimer::singleShot(100, this, &KJobTest::slotStartInnerJob);
|
||||
m_outerJob = new WaitJob();
|
||||
m_outerJob->exec();
|
||||
}
|
||||
|
||||
void KJobTest::testElapseTime()
|
||||
{
|
||||
m_innerJob = new WaitJob();
|
||||
QTimer::singleShot(10, m_innerJob, &WaitJob::makeItFinish);
|
||||
QVERIFY(m_innerJob->exec());
|
||||
|
||||
QCOMPARE_GE(m_innerJob->elapsedTime(), 10);
|
||||
QCOMPARE_LE(m_innerJob->elapsedTime(), 14);
|
||||
}
|
||||
|
||||
void KJobTest::testElapseTimeSuspendResume()
|
||||
{
|
||||
m_innerJob = new WaitJob();
|
||||
|
||||
QSignalSpy suspended_spy(m_innerJob, &KJob::suspended);
|
||||
QSignalSpy resume_spy(m_innerJob, &KJob::resumed);
|
||||
|
||||
QTimer::singleShot(5, m_innerJob, [this]() {
|
||||
m_innerJob->suspend();
|
||||
QCOMPARE_GE(m_innerJob->elapsedTime(), 5);
|
||||
QCOMPARE_LE(m_innerJob->elapsedTime(), 8);
|
||||
});
|
||||
QTimer::singleShot(10, m_innerJob, &KJob::resume);
|
||||
|
||||
QTimer::singleShot(15, m_innerJob, [this]() {
|
||||
m_innerJob->suspend();
|
||||
QCOMPARE_GE(m_innerJob->elapsedTime(), 10);
|
||||
QCOMPARE_LE(m_innerJob->elapsedTime(), 14);
|
||||
});
|
||||
QTimer::singleShot(20, m_innerJob, [this]() {
|
||||
m_innerJob->resume();
|
||||
m_innerJob->makeItFinish();
|
||||
});
|
||||
|
||||
QVERIFY(m_innerJob->exec());
|
||||
|
||||
QCOMPARE_GE(m_innerJob->elapsedTime(), 10);
|
||||
QCOMPARE_LE(m_innerJob->elapsedTime(), 14);
|
||||
|
||||
QCOMPARE(suspended_spy.count(), 2);
|
||||
QCOMPARE(resume_spy.count(), 2);
|
||||
}
|
||||
|
||||
void KJobTest::slotStartInnerJob()
|
||||
{
|
||||
QTimer::singleShot(100, this, &KJobTest::slotFinishOuterJob);
|
||||
m_innerJob = new WaitJob();
|
||||
m_innerJob->exec();
|
||||
}
|
||||
|
||||
void KJobTest::slotFinishOuterJob()
|
||||
{
|
||||
QTimer::singleShot(100, this, &KJobTest::slotFinishInnerJob);
|
||||
m_outerJob->makeItFinish();
|
||||
}
|
||||
|
||||
void KJobTest::slotFinishInnerJob()
|
||||
{
|
||||
m_innerJob->makeItFinish();
|
||||
}
|
||||
|
||||
void KJobTest::slotResult(KJob *job)
|
||||
{
|
||||
const auto testJob = qobject_cast<const TestJob *>(job);
|
||||
QVERIFY(testJob);
|
||||
QVERIFY(testJob->isFinished());
|
||||
|
||||
// Ensure the job has already emitted finished() if we are tracking from
|
||||
// setupErrorResultFinished
|
||||
if (m_jobFinishCount.contains(job)) {
|
||||
QVERIFY(m_jobFinishCount.value(job) == 1);
|
||||
QVERIFY(m_jobFinishCount.remove(job) == 1 /* num items removed */);
|
||||
}
|
||||
|
||||
if (job->error()) {
|
||||
m_lastError = job->error();
|
||||
m_lastErrorText = job->errorText();
|
||||
} else {
|
||||
m_lastError = KJob::NoError;
|
||||
m_lastErrorText.clear();
|
||||
}
|
||||
|
||||
m_resultCount++;
|
||||
}
|
||||
|
||||
void KJobTest::slotFinished(KJob *job)
|
||||
{
|
||||
QVERIFY2(m_jobFinishCount.value(job) == 0, "Ensure we have not double-emitted KJob::finished()");
|
||||
m_jobFinishCount[job]++;
|
||||
|
||||
if (job->error()) {
|
||||
m_lastError = job->error();
|
||||
m_lastErrorText = job->errorText();
|
||||
} else {
|
||||
m_lastError = KJob::NoError;
|
||||
m_lastErrorText.clear();
|
||||
}
|
||||
|
||||
m_finishedCount++;
|
||||
}
|
||||
|
||||
TestJob *KJobTest::setupErrorResultFinished()
|
||||
{
|
||||
m_lastError = KJob::UserDefinedError;
|
||||
m_lastErrorText.clear();
|
||||
m_resultCount = 0;
|
||||
m_finishedCount = 0;
|
||||
|
||||
auto *job = new TestJob;
|
||||
m_jobFinishCount[job] = 0;
|
||||
connect(job, &KJob::result, this, &KJobTest::slotResult);
|
||||
connect(job, &KJob::finished, this, &KJobTest::slotFinished);
|
||||
return job;
|
||||
}
|
||||
|
||||
TestJob::TestJob()
|
||||
: KJob()
|
||||
{
|
||||
}
|
||||
|
||||
TestJob::~TestJob()
|
||||
{
|
||||
}
|
||||
|
||||
void TestJob::start()
|
||||
{
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
emitResult();
|
||||
});
|
||||
}
|
||||
|
||||
bool TestJob::doKill()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestJob::setError(int errorCode)
|
||||
{
|
||||
KJob::setError(errorCode);
|
||||
}
|
||||
|
||||
void TestJob::setErrorText(const QString &errorText)
|
||||
{
|
||||
KJob::setErrorText(errorText);
|
||||
}
|
||||
|
||||
void TestJob::setProcessedSize(qulonglong size)
|
||||
{
|
||||
KJob::setProcessedAmount(KJob::Bytes, size);
|
||||
}
|
||||
|
||||
void TestJob::setTotalSize(qulonglong size)
|
||||
{
|
||||
KJob::setTotalAmount(KJob::Bytes, size);
|
||||
}
|
||||
|
||||
void TestJob::setProcessedFiles(qulonglong files)
|
||||
{
|
||||
KJob::setProcessedAmount(KJob::Files, files);
|
||||
}
|
||||
|
||||
void TestJob::setTotalFiles(qulonglong files)
|
||||
{
|
||||
KJob::setTotalAmount(KJob::Files, files);
|
||||
}
|
||||
|
||||
void TestJob::setPercent(unsigned long percentage)
|
||||
{
|
||||
KJob::setPercent(percentage);
|
||||
}
|
||||
|
||||
void WaitJob::start()
|
||||
{
|
||||
startElapsedTimer();
|
||||
}
|
||||
|
||||
bool WaitJob::doResume()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitJob::doSuspend()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void WaitJob::makeItFinish()
|
||||
{
|
||||
emitResult();
|
||||
}
|
||||
|
||||
void TestJobUiDelegate::connectJob(KJob *job)
|
||||
{
|
||||
QVERIFY(job->uiDelegate() != nullptr);
|
||||
}
|
||||
|
||||
#include "moc_kjobtest.cpp"
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KJOBTEST_H
|
||||
#define KJOBTEST_H
|
||||
|
||||
#include "kjob.h"
|
||||
#include "kjobuidelegate.h"
|
||||
#include <QEventLoop>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class TestJob : public KJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestJob();
|
||||
~TestJob() override;
|
||||
|
||||
void start() override;
|
||||
using KJob::isFinished;
|
||||
using KJob::setProgressUnit;
|
||||
|
||||
protected:
|
||||
bool doKill() override;
|
||||
|
||||
public:
|
||||
void setError(int errorCode);
|
||||
void setErrorText(const QString &errorText);
|
||||
void setProcessedSize(qulonglong size);
|
||||
void setTotalSize(qulonglong size);
|
||||
void setProcessedFiles(qulonglong files);
|
||||
void setTotalFiles(qulonglong files);
|
||||
void setPercent(unsigned long percentage);
|
||||
};
|
||||
|
||||
class TestJobUiDelegate : public KJobUiDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
virtual void connectJob(KJob *job);
|
||||
};
|
||||
|
||||
class WaitJob;
|
||||
|
||||
class KJobTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class Action {
|
||||
Start,
|
||||
KillQuietly,
|
||||
KillVerbosely,
|
||||
};
|
||||
Q_ENUM(Action)
|
||||
|
||||
KJobTest();
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
// These slots need to be public, otherwise qtestlib calls them as part of the test
|
||||
void slotStartInnerJob();
|
||||
void slotFinishOuterJob();
|
||||
void slotFinishInnerJob();
|
||||
|
||||
private Q_SLOTS:
|
||||
void testEmitResult_data();
|
||||
void testEmitResult();
|
||||
void testProgressTracking();
|
||||
void testExec_data();
|
||||
void testExec();
|
||||
void testKill_data();
|
||||
void testKill();
|
||||
void testDestroy();
|
||||
void testEmitAtMostOnce_data();
|
||||
void testEmitAtMostOnce();
|
||||
void testDelegateUsage();
|
||||
void testNestedExec();
|
||||
void testElapseTime();
|
||||
void testElapseTimeSuspendResume();
|
||||
|
||||
void slotResult(KJob *job);
|
||||
void slotFinished(KJob *job);
|
||||
|
||||
private:
|
||||
TestJob *setupErrorResultFinished();
|
||||
|
||||
QEventLoop loop;
|
||||
int m_lastError;
|
||||
QString m_lastErrorText;
|
||||
int m_resultCount;
|
||||
int m_finishedCount;
|
||||
|
||||
WaitJob *m_outerJob;
|
||||
WaitJob *m_innerJob;
|
||||
QMap<KJob *, int> m_jobFinishCount;
|
||||
};
|
||||
|
||||
class WaitJob : public KJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void start() override;
|
||||
bool doResume() override;
|
||||
bool doSuspend() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void makeItFinish();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
|
||||
*/
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
#include <KLibexec>
|
||||
|
||||
class KLibexecTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
const QString m_relative = QStringLiteral("fakeexec/kf6");
|
||||
const QString m_fixtureName =
|
||||
#ifdef Q_OS_WIN
|
||||
QStringLiteral("klibexectest-fixture-binary.exe");
|
||||
#else
|
||||
QStringLiteral("klibexectest-fixture-binary");
|
||||
#endif
|
||||
QString m_fixtureDir;
|
||||
QString m_fixturePath;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase()
|
||||
{
|
||||
m_fixtureDir = QDir::cleanPath(QCoreApplication::applicationDirPath() + QDir::separator() + m_relative);
|
||||
m_fixturePath = QDir::cleanPath(m_fixtureDir + QDir::separator() + m_fixtureName);
|
||||
QVERIFY(QDir().mkpath(m_fixtureDir));
|
||||
QFile fixture(m_fixturePath);
|
||||
QVERIFY(fixture.open(QFile::ReadWrite));
|
||||
fixture.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
|
||||
|
||||
m_fixtureDir = QFileInfo(m_fixtureDir).canonicalFilePath();
|
||||
m_fixturePath = QFileInfo(m_fixtureDir).canonicalFilePath();
|
||||
}
|
||||
|
||||
void testPath()
|
||||
{
|
||||
QCOMPARE(KLibexec::path(m_relative), m_fixtureDir);
|
||||
}
|
||||
|
||||
void testKDEFrameworksPaths()
|
||||
{
|
||||
auto paths = KLibexec::kdeFrameworksPaths(m_relative);
|
||||
QVERIFY(paths.contains(QCoreApplication::applicationDirPath()));
|
||||
QVERIFY(paths.contains(m_fixtureDir));
|
||||
// not exhaustive verification
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(KLibexecTest)
|
||||
|
||||
#include "klibexectest.moc"
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "klistopenfilesjobtest_unix.h"
|
||||
#include "klistopenfilesjob.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringLiteral>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef Q_OS_FREEBSD
|
||||
// See implementation note in testOpenFiles()
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
QTEST_MAIN(KListOpenFilesJobTest)
|
||||
void initLocale()
|
||||
{
|
||||
qputenv("LC_ALL", "en_US.utf-8");
|
||||
}
|
||||
|
||||
Q_CONSTRUCTOR_FUNCTION(initLocale)
|
||||
|
||||
namespace
|
||||
{
|
||||
bool hasLsofInstalled()
|
||||
{
|
||||
return !QStandardPaths::findExecutable(QStringLiteral("lsof")).isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void KListOpenFilesJobTest::testOpenFiles()
|
||||
{
|
||||
if (!hasLsofInstalled()) {
|
||||
QSKIP("lsof is not installed - skipping test");
|
||||
}
|
||||
|
||||
// Create a file and hold it open, so that lsof must report us
|
||||
QTemporaryDir tempDir;
|
||||
QFile tempFile(tempDir.path() + QStringLiteral("/file"));
|
||||
QVERIFY(tempFile.open(QIODevice::WriteOnly));
|
||||
bool xfail_zfs = false; // Expected failure because of ZFS
|
||||
#ifdef Q_OS_FREEBSD
|
||||
// FIXME: On FreeBSD, lsof does not support zfs (as of 2022), see
|
||||
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=253553
|
||||
//
|
||||
// This affects regular files only. So for FreeBSD, check if
|
||||
// the tempDir seems to be on a ZFS filesystem, e.g.
|
||||
//
|
||||
// ```
|
||||
// [adridg@beastie .../invent/kcoreaddons]$ lsof +d /tmp > /dev/null
|
||||
// lsof: WARNING: no ZFS support has been defined.
|
||||
// See 00FAQ for more information.
|
||||
// ```
|
||||
{
|
||||
QProcess lsof;
|
||||
lsof.start(QStringLiteral("lsof"), {QStringLiteral("+d"), tempDir.path()});
|
||||
lsof.waitForFinished();
|
||||
|
||||
auto stderr = lsof.readAllStandardError();
|
||||
xfail_zfs = (lsof.exitCode() != 0) && stderr.contains("ZFS");
|
||||
}
|
||||
#endif
|
||||
|
||||
auto job = new KListOpenFilesJob(tempDir.path());
|
||||
QVERIFY2(job->exec(), qPrintable(job->errorString()));
|
||||
QCOMPARE(job->error(), KJob::NoError);
|
||||
auto processInfoList = job->processInfoList();
|
||||
if (xfail_zfs) {
|
||||
// Ths list is empty, so the subsequent find and validity-checks
|
||||
// don't make sense.
|
||||
QEXPECT_FAIL("", "lsof(8) does not support regular files on ZFS", Abort);
|
||||
}
|
||||
QVERIFY(!processInfoList.empty());
|
||||
auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), [](const KProcessList::KProcessInfo &info) {
|
||||
return info.pid() == QCoreApplication::applicationPid();
|
||||
});
|
||||
QVERIFY(testProcessIterator != processInfoList.end());
|
||||
const auto &processInfo = *testProcessIterator;
|
||||
QVERIFY(processInfo.isValid());
|
||||
QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid());
|
||||
}
|
||||
|
||||
void KListOpenFilesJobTest::testNoOpenFiles()
|
||||
{
|
||||
if (!hasLsofInstalled()) {
|
||||
QSKIP("lsof is not installed - skipping test");
|
||||
}
|
||||
QTemporaryDir tempDir;
|
||||
auto job = new KListOpenFilesJob(tempDir.path());
|
||||
QVERIFY2(job->exec(), qPrintable(job->errorString()));
|
||||
QCOMPARE(job->error(), KJob::NoError);
|
||||
QVERIFY(job->processInfoList().empty());
|
||||
}
|
||||
|
||||
void KListOpenFilesJobTest::testNonExistingDir()
|
||||
{
|
||||
if (!hasLsofInstalled()) {
|
||||
QSKIP("lsof is not installed - skipping test");
|
||||
}
|
||||
QString nonExistingDir(QStringLiteral("/does/not/exist"));
|
||||
auto job = new KListOpenFilesJob(nonExistingDir);
|
||||
QVERIFY(!job->exec());
|
||||
QCOMPARE(job->error(), static_cast<int>(KListOpenFilesJob::Error::DoesNotExist));
|
||||
QCOMPARE(job->errorText(), QStringLiteral("Path %1 doesn't exist").arg(nonExistingDir));
|
||||
QVERIFY(job->processInfoList().empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper class to temporarily set an environment variable and reset it on destruction
|
||||
*/
|
||||
class ScopedEnvVariable
|
||||
{
|
||||
public:
|
||||
ScopedEnvVariable(const QLatin1String &Name, const QByteArray &NewValue)
|
||||
: name(Name)
|
||||
, originalValue(qgetenv(name.latin1()))
|
||||
{
|
||||
qputenv(name.latin1(), NewValue);
|
||||
}
|
||||
~ScopedEnvVariable()
|
||||
{
|
||||
qputenv(name.latin1(), originalValue);
|
||||
}
|
||||
|
||||
private:
|
||||
const QLatin1String name;
|
||||
const QByteArray originalValue;
|
||||
};
|
||||
|
||||
void KListOpenFilesJobTest::testLsofNotFound()
|
||||
{
|
||||
// This test relies on clearing the PATH variable so that lsof is not found
|
||||
ScopedEnvVariable emptyPathEnvironment(QLatin1String("PATH"), QByteArray());
|
||||
QDir path(QCoreApplication::applicationDirPath());
|
||||
auto job = new KListOpenFilesJob(path.path());
|
||||
QVERIFY(!job->exec());
|
||||
QCOMPARE(job->error(), static_cast<int>(KListOpenFilesJob::Error::InternalError));
|
||||
QVERIFY(job->processInfoList().empty());
|
||||
}
|
||||
|
||||
#include "moc_klistopenfilesjobtest_unix.cpp"
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KLISTOPENFILESJOBTEST_UNIX_H
|
||||
#define KLISTOPENFILESJOBTEST_UNIX_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KListOpenFilesJobTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testOpenFiles();
|
||||
void testNoOpenFiles();
|
||||
void testNonExistingDir();
|
||||
void testLsofNotFound();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "klistopenfilesjobtest_win.h"
|
||||
#include "klistopenfilesjob.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QStringLiteral>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KListOpenFilesJobTest)
|
||||
|
||||
void KListOpenFilesJobTest::testNotSupported()
|
||||
{
|
||||
QDir path(QCoreApplication::applicationDirPath());
|
||||
auto job = new KListOpenFilesJob(path.path());
|
||||
job->exec();
|
||||
QCOMPARE(job->error(), static_cast<int>(KListOpenFilesJob::Error::NotSupported));
|
||||
QCOMPARE(job->errorText(), QStringLiteral("KListOpenFilesJob is not supported on Windows"));
|
||||
QVERIFY(job->processInfoList().empty());
|
||||
}
|
||||
|
||||
#include "moc_klistopenfilesjobtest_win.cpp"
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KLISTOPENFILESJOBTEST_WIN_H
|
||||
#define KLISTOPENFILESJOBTEST_WIN_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KListOpenFilesJobTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testNotSupported();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003, 2008 Oswald Buddenhagen <ossi@kde.org>
|
||||
SPDX-FileCopyrightText: 2005 Thomas Braxton <brax108@cox.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <QTest>
|
||||
#include <kmacroexpander.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
class KMacroExpanderTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void expandMacros();
|
||||
void expandMacrosShellQuote();
|
||||
void expandMacrosShellQuoteParens();
|
||||
void expandMacrosSubClass();
|
||||
};
|
||||
|
||||
class MyCExpander : public KCharMacroExpander
|
||||
{
|
||||
QString exp;
|
||||
|
||||
public:
|
||||
MyCExpander()
|
||||
: KCharMacroExpander()
|
||||
, exp(QStringLiteral("expanded"))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool expandMacro(QChar ch, QStringList &ret) override
|
||||
{
|
||||
if (ch == QLatin1Char('m')) {
|
||||
ret = QStringList(exp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class MyWExpander : public KWordMacroExpander
|
||||
{
|
||||
QString exp;
|
||||
|
||||
public:
|
||||
MyWExpander()
|
||||
: KWordMacroExpander()
|
||||
, exp(QStringLiteral("expanded"))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
bool expandMacro(const QString &str, QStringList &ret) override
|
||||
{
|
||||
if (str == QLatin1String("macro")) {
|
||||
ret = QStringList(exp);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void KMacroExpanderTest::expandMacros()
|
||||
{
|
||||
QHash<QChar, QStringList> map;
|
||||
QStringList list;
|
||||
QString s;
|
||||
|
||||
list << QStringLiteral("Restaurant \"Chew It\"");
|
||||
map.insert(QLatin1Char('n'), list);
|
||||
list.clear();
|
||||
list << QStringLiteral("element1") << QStringLiteral("'element2'");
|
||||
map.insert(QLatin1Char('l'), list);
|
||||
|
||||
s = QStringLiteral("%% text %l text %n");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, map), QLatin1String("% text element1 'element2' text Restaurant \"Chew It\""));
|
||||
s = QStringLiteral("text \"%l %n\" text");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, map), QLatin1String("text \"element1 'element2' Restaurant \"Chew It\"\" text"));
|
||||
|
||||
QHash<QChar, QString> map2;
|
||||
map2.insert(QLatin1Char('a'), QStringLiteral("%n"));
|
||||
map2.insert(QLatin1Char('f'), QStringLiteral("filename.txt"));
|
||||
map2.insert(QLatin1Char('u'), QStringLiteral("https://www.kde.org/index.html"));
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant \"Chew It\""));
|
||||
s = QStringLiteral("Title: %a - %f - %u - %n - %%");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, map2), QLatin1String("Title: %n - filename.txt - https://www.kde.org/index.html - Restaurant \"Chew It\" - %"));
|
||||
|
||||
QHash<QString, QString> smap;
|
||||
smap.insert(QStringLiteral("foo"), QStringLiteral("%n"));
|
||||
smap.insert(QStringLiteral("file"), QStringLiteral("filename.txt"));
|
||||
smap.insert(QStringLiteral("url"), QStringLiteral("https://www.kde.org/index.html"));
|
||||
smap.insert(QStringLiteral("name"), QStringLiteral("Restaurant \"Chew It\""));
|
||||
|
||||
s = QStringLiteral("Title: %foo - %file - %url - %name - %");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("Title: %n - filename.txt - https://www.kde.org/index.html - Restaurant \"Chew It\" - %"));
|
||||
s = QStringLiteral("%foo - %file - %url - %name");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("%n - filename.txt - https://www.kde.org/index.html - Restaurant \"Chew It\""));
|
||||
|
||||
s = QStringLiteral("Title: %{foo} - %{file} - %{url} - %{name} - %");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("Title: %n - filename.txt - https://www.kde.org/index.html - Restaurant \"Chew It\" - %"));
|
||||
s = QStringLiteral("%{foo} - %{file} - %{url} - %{name}");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("%n - filename.txt - https://www.kde.org/index.html - Restaurant \"Chew It\""));
|
||||
|
||||
s = QStringLiteral("Title: %foo-%file-%url-%name-%");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("Title: %n-filename.txt-https://www.kde.org/index.html-Restaurant \"Chew It\"-%"));
|
||||
|
||||
s = QStringLiteral("Title: %{file} %{url");
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String("Title: filename.txt %{url"));
|
||||
|
||||
s = QStringLiteral(" * Copyright (C) 2008 %{AUTHOR}");
|
||||
smap.clear();
|
||||
QCOMPARE(KMacroExpander::expandMacros(s, smap), QLatin1String(" * Copyright (C) 2008 %{AUTHOR}"));
|
||||
}
|
||||
|
||||
void KMacroExpanderTest::expandMacrosShellQuote()
|
||||
{
|
||||
QHash<QChar, QStringList> map;
|
||||
QStringList list;
|
||||
QString s;
|
||||
|
||||
list << QStringLiteral("Restaurant \"Chew It\"");
|
||||
map.insert(QLatin1Char('n'), list);
|
||||
list.clear();
|
||||
list << QStringLiteral("element1") << QStringLiteral("'element2'") << QStringLiteral("\"element3\"");
|
||||
map.insert(QLatin1Char('l'), list);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
s = QStringLiteral("text %l %n text");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map),
|
||||
QLatin1String("text element1 'element2' \\^\"element3\\^\" \"Restaurant \"\\^\"\"Chew It\"\\^\" text"));
|
||||
|
||||
s = QStringLiteral("text \"%l %n\" text");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map),
|
||||
QLatin1String("text \"element1 'element2' \"\\^\"\"element3\"\\^\"\" Restaurant \"\\^\"\"Chew It\"\\^\"\"\" text"));
|
||||
#else
|
||||
s = QStringLiteral("text %l %n text");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map), QLatin1String("text element1 ''\\''element2'\\''' '\"element3\"' 'Restaurant \"Chew It\"' text"));
|
||||
|
||||
s = QStringLiteral("text \"%l %n\" text");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map), QLatin1String("text \"element1 'element2' \\\"element3\\\" Restaurant \\\"Chew It\\\"\" text"));
|
||||
#endif
|
||||
|
||||
QHash<QChar, QString> map2;
|
||||
map2.insert(QLatin1Char('a'), QStringLiteral("%n"));
|
||||
map2.insert(QLatin1Char('f'), QStringLiteral("filename.txt"));
|
||||
map2.insert(QLatin1Char('u'), QStringLiteral("https://www.kde.org/index.html"));
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant \"Chew It\""));
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
s = QStringLiteral("Title: %a - %f - %u - %n - %% - %VARIABLE% foo");
|
||||
QCOMPARE(
|
||||
KMacroExpander::expandMacrosShellQuote(s, map2),
|
||||
QLatin1String(
|
||||
"Title: %PERCENT_SIGN%n - filename.txt - https://www.kde.org/index.html - \"Restaurant \"\\^\"\"Chew It\"\\^\" - %PERCENT_SIGN% - %VARIABLE% foo"));
|
||||
|
||||
s = QStringLiteral("kedit --caption %n %f");
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant 'Chew It'"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant 'Chew It'\" filename.txt"));
|
||||
|
||||
s = QStringLiteral("kedit --caption \"%n\" %f");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant 'Chew It'\" filename.txt"));
|
||||
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant \"Chew It\""));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant \"\\^\"\"Chew It\"\\^\"\"\" filename.txt"));
|
||||
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant %HOME%"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant %PERCENT_SIGN%HOME%PERCENT_SIGN%\" filename.txt"));
|
||||
|
||||
s = QStringLiteral("kedit c:\\%f");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit c:\\filename.txt"));
|
||||
|
||||
s = QStringLiteral("kedit \"c:\\%f\"");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit \"c:\\filename.txt\""));
|
||||
|
||||
map2.insert(QLatin1Char('f'), QStringLiteral("\"filename.txt\""));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit \"c:\\\\\"\\^\"\"filename.txt\"\\^\"\"\""));
|
||||
|
||||
map2.insert(QLatin1Char('f'), QStringLiteral("path\\"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit \"c:\\path\\\\\"\"\""));
|
||||
#else
|
||||
s = QStringLiteral("Title: %a - %f - %u - %n - %%");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2),
|
||||
QLatin1String("Title: %n - filename.txt - https://www.kde.org/index.html - 'Restaurant \"Chew It\"' - %"));
|
||||
|
||||
s = QStringLiteral("kedit --caption %n %f");
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant 'Chew It'"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption 'Restaurant '\\''Chew It'\\''' filename.txt"));
|
||||
|
||||
s = QStringLiteral("kedit --caption \"%n\" %f");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant 'Chew It'\" filename.txt"));
|
||||
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant \"Chew It\""));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant \\\"Chew It\\\"\" filename.txt"));
|
||||
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant $HOME"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant \\$HOME\" filename.txt"));
|
||||
|
||||
map2.insert(QLatin1Char('n'), QStringLiteral("Restaurant `echo hello`"));
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"Restaurant \\`echo hello\\`\" filename.txt"));
|
||||
|
||||
s = QStringLiteral("kedit --caption \"`echo %n`\" %f");
|
||||
QCOMPARE(KMacroExpander::expandMacrosShellQuote(s, map2), QLatin1String("kedit --caption \"$( echo 'Restaurant `echo hello`')\" filename.txt"));
|
||||
#endif
|
||||
}
|
||||
|
||||
class DummyMacroExpander : public KMacroExpanderBase
|
||||
{
|
||||
public:
|
||||
DummyMacroExpander()
|
||||
: KMacroExpanderBase(QChar(0x4567))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
int expandPlainMacro(const QString &, int, QStringList &) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int expandEscapedMacro(const QString &, int, QStringList &) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
void KMacroExpanderTest::expandMacrosShellQuoteParens()
|
||||
{
|
||||
QString s;
|
||||
|
||||
s = QStringLiteral("( echo \"just testing (parens)\" ) ) after");
|
||||
int pos = 0;
|
||||
DummyMacroExpander kmx;
|
||||
QVERIFY(kmx.expandMacrosShellQuote(s, pos));
|
||||
QCOMPARE(s.mid(pos), QLatin1String(") after"));
|
||||
QVERIFY(!kmx.expandMacrosShellQuote(s));
|
||||
}
|
||||
|
||||
void KMacroExpanderTest::expandMacrosSubClass()
|
||||
{
|
||||
QString s;
|
||||
|
||||
MyCExpander mx1;
|
||||
s = QStringLiteral("subst %m but not %n equ %%");
|
||||
mx1.expandMacros(s);
|
||||
QCOMPARE(s, QLatin1String("subst expanded but not %n equ %"));
|
||||
|
||||
MyWExpander mx2;
|
||||
s = QStringLiteral("subst %macro but not %not equ %%");
|
||||
mx2.expandMacros(s);
|
||||
QCOMPARE(s, QLatin1String("subst expanded but not %not equ %"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(KMacroExpanderTest)
|
||||
|
||||
#include "kmacroexpandertest.moc"
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
This file is part of the KDE Frameworks
|
||||
|
||||
SPDX-FileCopyrightText: 2022 Mirco Miranda
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kmemoryinfotest.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "kmemoryinfo.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(KMemoryInfoTest)
|
||||
|
||||
KMemoryInfoTest::KMemoryInfoTest(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void KMemoryInfoTest::isNull()
|
||||
{
|
||||
KMemoryInfo m;
|
||||
QVERIFY(!m.isNull());
|
||||
}
|
||||
|
||||
void KMemoryInfoTest::operators()
|
||||
{
|
||||
KMemoryInfo m;
|
||||
auto m1 = m;
|
||||
QVERIFY(m == m1);
|
||||
|
||||
// paranoia check
|
||||
QVERIFY(m.totalPhysical() != 0);
|
||||
QCOMPARE(m.totalPhysical(), m1.totalPhysical());
|
||||
}
|
||||
|
||||
#include "moc_kmemoryinfotest.cpp"
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
This file is part of the KDE Frameworks
|
||||
|
||||
SPDX-FileCopyrightText: 2022 Mirco Miranda
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#ifndef KMEMORYINFOTEST_H
|
||||
#define KMEMORYINFOTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
/**
|
||||
* @brief The KMemoryInfoTest class
|
||||
*/
|
||||
class KMemoryInfoTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KMemoryInfoTest(QObject *parent = nullptr);
|
||||
|
||||
private Q_SLOTS:
|
||||
void isNull();
|
||||
|
||||
void operators();
|
||||
};
|
||||
|
||||
#endif // KMEMORYINFOTEST_H
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "knetworkmountstestcanonical.h"
|
||||
|
||||
#include <KNetworkMounts>
|
||||
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KNetworkMountsTestCanonical)
|
||||
|
||||
void KNetworkMountsTestCanonical::initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
m_configFileName = QStringLiteral("%1/network_mounts").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
|
||||
|
||||
QFile::remove(m_configFileName);
|
||||
QVERIFY(!QFile::exists(m_configFileName));
|
||||
|
||||
// create directory structure
|
||||
QVERIFY(m_tmpDir.isValid());
|
||||
|
||||
const QString relLinkToPath = QStringLiteral("dir");
|
||||
const QString relSymlinkDirectory = QStringLiteral("symlinkDirectory");
|
||||
const QStringList relPaths = {relLinkToPath,
|
||||
QStringLiteral("dir/subdir1"),
|
||||
QStringLiteral("dir/subdir1/subdir1"),
|
||||
QStringLiteral("dir/subdir1/subdir2"),
|
||||
QStringLiteral("dir/subdir1/subdir3"),
|
||||
QStringLiteral("dir/subdir2"),
|
||||
QStringLiteral("dir/subdir2/subdir1"),
|
||||
QStringLiteral("dir/subdir2/subdir2"),
|
||||
QStringLiteral("dir/subdir2/subdir3"),
|
||||
relSymlinkDirectory};
|
||||
|
||||
const QString relSymlinkToSmbPath = QStringLiteral("symlinkToSmbPath");
|
||||
|
||||
QDir dir(m_tmpDir.path());
|
||||
for (const QString &relPath : relPaths) {
|
||||
QVERIFY(dir.mkpath(relPath));
|
||||
QVERIFY(QFile::exists(m_tmpDir.path() + QLatin1Char('/') + relPath));
|
||||
|
||||
const QString fileName = m_tmpDir.path() + QLatin1Char('/') + relPath + QLatin1String("/file.txt");
|
||||
QFile file(fileName);
|
||||
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
|
||||
file.close();
|
||||
QVERIFY(QFile::exists(fileName));
|
||||
}
|
||||
|
||||
const QString linkToPath = m_tmpDir.path() + QLatin1Char('/') + relLinkToPath;
|
||||
|
||||
// SymlinkToNetworkMount
|
||||
const QString symlinkToSmbPath = dir.path() + QLatin1Char('/') + relSymlinkToSmbPath;
|
||||
|
||||
QVERIFY(QFile::link(linkToPath, symlinkToSmbPath));
|
||||
qDebug() << "linkToPath=" << linkToPath << ", symlinkToSmbPath=" << symlinkToSmbPath;
|
||||
|
||||
// SymlinkDirectory
|
||||
QVERIFY(dir.cd(relSymlinkDirectory));
|
||||
const QString symlinkDirectory = dir.path();
|
||||
const QString linkStr = symlinkDirectory + QLatin1Char('/') + relLinkToPath;
|
||||
|
||||
QVERIFY(QFile::link(linkToPath, linkStr));
|
||||
qDebug() << "linkToPath=" << linkToPath << ", symlinkDirectory=" << symlinkDirectory << ", linkStr=" << linkStr;
|
||||
|
||||
// setup config
|
||||
KNetworkMounts::self()->setEnabled(true);
|
||||
|
||||
const QStringList paths = {linkToPath};
|
||||
KNetworkMounts::self()->setPaths(paths, KNetworkMounts::SmbPaths);
|
||||
|
||||
const QStringList savedPaths = {linkToPath + QLatin1Char('/')};
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), savedPaths);
|
||||
|
||||
// SymlinkDirectory
|
||||
const QStringList symlinkDirectories = {symlinkDirectory};
|
||||
KNetworkMounts::self()->setPaths(symlinkDirectories, KNetworkMounts::SymlinkDirectory);
|
||||
|
||||
const QStringList savedSymlinkDirectories = {symlinkDirectory + QLatin1Char('/')};
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), savedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), savedSymlinkDirectories);
|
||||
|
||||
// SymlinkToNetworkMount
|
||||
// addPath
|
||||
KNetworkMounts::self()->addPath(symlinkToSmbPath, KNetworkMounts::SymlinkToNetworkMount);
|
||||
|
||||
const QString savedSymlinkToSmbPath = symlinkToSmbPath + QLatin1Char('/');
|
||||
const QStringList savedSymlinkToSmbPaths = {savedSymlinkToSmbPath};
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), savedSymlinkToSmbPaths);
|
||||
|
||||
// setPaths
|
||||
const QStringList symlinkToSmbPaths = {symlinkToSmbPath};
|
||||
KNetworkMounts::self()->setPaths(symlinkToSmbPaths, KNetworkMounts::SymlinkToNetworkMount);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), savedSymlinkToSmbPaths);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), savedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), savedSymlinkDirectories);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestCanonical::cleanupTestCase()
|
||||
{
|
||||
KNetworkMounts::self()->sync();
|
||||
QFile::remove(m_configFileName);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestCanonical::testCanonicalSymlinkPath_data()
|
||||
{
|
||||
QTest::addColumn<QString>("relPath");
|
||||
QTest::addColumn<QString>("symlinkedRelPath");
|
||||
|
||||
// SymlinkDirectory
|
||||
QTest::newRow("symlinkDirectory/dir") << "dir"
|
||||
<< "symlinkDirectory/dir";
|
||||
QTest::newRow("symlinkDirectory/dir/file.txt") << "dir/file.txt"
|
||||
<< "symlinkDirectory/dir/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1") << "dir/subdir1"
|
||||
<< "symlinkDirectory/dir/subdir1";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/file.txt") << "dir/subdir1/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir1/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir1") << "dir/subdir1/subdir1"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir1";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir1/file.txt") << "dir/subdir1/subdir1/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir1/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir2") << "dir/subdir1/subdir2"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir2";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir2/file.txt") << "dir/subdir1/subdir2/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir2/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir3") << "dir/subdir1/subdir3"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir3";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir1/subdir3/file.txt") << "dir/subdir1/subdir3/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir1/subdir3/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2") << "dir/subdir2"
|
||||
<< "symlinkDirectory/dir/subdir2";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/file.txt") << "dir/subdir2/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir2/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir1") << "dir/subdir2/subdir1"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir1";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir1/file.txt") << "dir/subdir2/subdir1/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir1/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir2") << "dir/subdir2/subdir2"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir2";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir2/file.txt") << "dir/subdir2/subdir2/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir2/file.txt";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir3") << "dir/subdir2/subdir3"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir3";
|
||||
QTest::newRow("symlinkDirectory/dir/subdir2/subdir3/file.txt") << "dir/subdir2/subdir3/file.txt"
|
||||
<< "symlinkDirectory/dir/subdir2/subdir3/file.txt";
|
||||
QTest::newRow("symlinkDirectory") << "symlinkDirectory"
|
||||
<< "symlinkDirectory";
|
||||
QTest::newRow("symlinkDirectory/file.txt") << "symlinkDirectory/file.txt"
|
||||
<< "symlinkDirectory/file.txt";
|
||||
|
||||
// SymlinkToNetworkMount
|
||||
QTest::newRow("symlinkToSmbPath") << "dir"
|
||||
<< "symlinkToSmbPath";
|
||||
QTest::newRow("symlinkToSmbPath/file.txt") << "dir/file.txt"
|
||||
<< "symlinkToSmbPath/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1") << "dir/subdir1"
|
||||
<< "symlinkToSmbPath/subdir1";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/file.txt") << "dir/subdir1/file.txt"
|
||||
<< "symlinkToSmbPath/subdir1/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir1") << "dir/subdir1/subdir1"
|
||||
<< "symlinkToSmbPath/subdir1/subdir1";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir1/file.txt") << "dir/subdir1/subdir1/file.txt"
|
||||
<< "symlinkToSmbPath/subdir1/subdir1/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir2") << "dir/subdir1/subdir2"
|
||||
<< "symlinkToSmbPath/subdir1/subdir2";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir2/file.txt") << "dir/subdir1/subdir2/file.txt"
|
||||
<< "symlinkToSmbPath/subdir1/subdir2/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir3") << "dir/subdir1/subdir3"
|
||||
<< "symlinkToSmbPath/subdir1/subdir3";
|
||||
QTest::newRow("symlinkToSmbPath/subdir1/subdir3/file.txt") << "dir/subdir1/subdir3/file.txt"
|
||||
<< "symlinkToSmbPath/subdir1/subdir3/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2") << "dir/subdir2"
|
||||
<< "symlinkToSmbPath/subdir2";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/file.txt") << "dir/subdir2/file.txt"
|
||||
<< "symlinkToSmbPath/subdir2/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir1") << "dir/subdir2/subdir1"
|
||||
<< "symlinkToSmbPath/subdir2/subdir1";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir1/file.txt") << "dir/subdir2/subdir1/file.txt"
|
||||
<< "symlinkToSmbPath/subdir2/subdir1/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir2") << "dir/subdir2/subdir2"
|
||||
<< "symlinkToSmbPath/subdir2/subdir2";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir2/file.txt") << "dir/subdir2/subdir2/file.txt"
|
||||
<< "symlinkToSmbPath/subdir2/subdir2/file.txt";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir3") << "dir/subdir2/subdir3"
|
||||
<< "symlinkToSmbPath/subdir2/subdir3";
|
||||
QTest::newRow("symlinkToSmbPath/subdir2/subdir3/file.txt") << "dir/subdir2/subdir3/file.txt"
|
||||
<< "symlinkToSmbPath/subdir2/subdir3/file.txt";
|
||||
}
|
||||
|
||||
void KNetworkMountsTestCanonical::testCanonicalSymlinkPath()
|
||||
{
|
||||
QFETCH(QString, relPath);
|
||||
QFETCH(QString, symlinkedRelPath);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QSKIP("QFile::link creates a shortcut on Windows, not a symlink, so no effect on canonical paths, skipped");
|
||||
#endif
|
||||
|
||||
const QString path = m_tmpDir.path() + QLatin1Char('/') + relPath;
|
||||
const QString symlinkedPath = m_tmpDir.path() + QLatin1Char('/') + symlinkedRelPath;
|
||||
const QString canonicalPath = QFileInfo(symlinkedPath).canonicalFilePath();
|
||||
|
||||
// default with cache
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
QCOMPARE(path, canonicalPath);
|
||||
qDebug() << "path=" << path << ", canonicalPath=" << canonicalPath << ", symlinkedPath=" << symlinkedPath;
|
||||
|
||||
// from cache
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
|
||||
// no cache
|
||||
KNetworkMounts::self()->clearCache();
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
|
||||
KNetworkMounts::self()->clearCache();
|
||||
KNetworkMounts::self()->setOption(KNetworkMounts::SymlinkPathsUseCache, false);
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
|
||||
// with cache
|
||||
KNetworkMounts::self()->setOption(KNetworkMounts::SymlinkPathsUseCache, true);
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(symlinkedPath), canonicalPath);
|
||||
}
|
||||
|
||||
#include "moc_knetworkmountstestcanonical.cpp"
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KNETWORKMOUNTSTESTCANONICAL_H
|
||||
#define KNETWORKMOUNTSTESTCANONICAL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
class KNetworkMountsTestCanonical : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testCanonicalSymlinkPath_data();
|
||||
void testCanonicalSymlinkPath();
|
||||
|
||||
private:
|
||||
QString m_configFileName;
|
||||
QTemporaryDir m_tmpDir;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "knetworkmountstestnoconfig.h"
|
||||
|
||||
#include <KNetworkMounts>
|
||||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KNetworkMountsTestNoConfig)
|
||||
|
||||
void KNetworkMountsTestNoConfig::initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
m_configFileName = QStringLiteral("%1/network_mounts").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
|
||||
|
||||
QFile::remove(m_configFileName);
|
||||
QVERIFY(!QFile::exists(m_configFileName));
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::cleanupTestCase()
|
||||
{
|
||||
QVERIFY(!QFile::exists(m_configFileName));
|
||||
QVERIFY(!KNetworkMounts::self()->isEnabled());
|
||||
|
||||
KNetworkMounts::self()->sync();
|
||||
QFile::remove(m_configFileName);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigPathTypes_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<KNetworkMounts::KNetworkMountsType>("type");
|
||||
|
||||
QTest::newRow("NfsPaths/") << "/" << KNetworkMounts::NfsPaths;
|
||||
QTest::newRow("SmbPaths/") << "/" << KNetworkMounts::SmbPaths;
|
||||
QTest::newRow("SymlinkDirectory/") << "/" << KNetworkMounts::SymlinkDirectory;
|
||||
QTest::newRow("SymlinkToNetworkMount/") << "/" << KNetworkMounts::SymlinkToNetworkMount;
|
||||
QTest::newRow("Any/") << "/" << KNetworkMounts::Any;
|
||||
|
||||
QTest::newRow("NfsPaths/mnt") << "/mnt" << KNetworkMounts::NfsPaths;
|
||||
QTest::newRow("SmbPaths/mnt") << "/mnt" << KNetworkMounts::SmbPaths;
|
||||
QTest::newRow("SymlinkDirectory/mnt") << "/mnt" << KNetworkMounts::SymlinkDirectory;
|
||||
QTest::newRow("SymlinkToNetworkMount/mnt") << "/mnt" << KNetworkMounts::SymlinkToNetworkMount;
|
||||
QTest::newRow("Any/mnt") << "/mnt" << KNetworkMounts::Any;
|
||||
|
||||
QTest::newRow("NfsPaths/mnt/") << "/mnt/" << KNetworkMounts::NfsPaths;
|
||||
QTest::newRow("SmbPaths/mnt/") << "/mnt/" << KNetworkMounts::SmbPaths;
|
||||
QTest::newRow("SymlinkDirectory/mnt/") << "/mnt/" << KNetworkMounts::SymlinkDirectory;
|
||||
QTest::newRow("SymlinkToNetworkMount/mnt/") << "/mnt/" << KNetworkMounts::SymlinkToNetworkMount;
|
||||
QTest::newRow("Any/mnt/") << "/mnt/" << KNetworkMounts::Any;
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigPathTypes()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFETCH(KNetworkMounts::KNetworkMountsType, type);
|
||||
|
||||
QVERIFY(!QFile::exists(m_configFileName));
|
||||
QVERIFY(!KNetworkMounts::self()->isEnabled());
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(type), QStringList());
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), QStringList());
|
||||
QCOMPARE(KNetworkMounts::self()->canonicalSymlinkPath(path), path);
|
||||
QVERIFY(!KNetworkMounts::self()->isSlowPath(path, type));
|
||||
QVERIFY(!KNetworkMounts::self()->isSlowPath(path));
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigPathOptions_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<KNetworkMounts::KNetworkMountOption>("option");
|
||||
|
||||
QTest::newRow("LowSideEffectsOptimizations/") << "/" << KNetworkMounts::LowSideEffectsOptimizations;
|
||||
QTest::newRow("MediumSideEffectsOptimizations/") << "/" << KNetworkMounts::MediumSideEffectsOptimizations;
|
||||
QTest::newRow("StrongSideEffectsOptimizations/") << "/" << KNetworkMounts::StrongSideEffectsOptimizations;
|
||||
QTest::newRow("KDirWatchDontAddWatches/") << "/" << KNetworkMounts::KDirWatchDontAddWatches;
|
||||
QTest::newRow("SymlinkPathsUseCache/") << "/" << KNetworkMounts::SymlinkPathsUseCache;
|
||||
|
||||
QTest::newRow("LowSideEffectsOptimizations/mnt") << "/mnt" << KNetworkMounts::LowSideEffectsOptimizations;
|
||||
QTest::newRow("MediumSideEffectsOptimizations/mnt") << "/mnt" << KNetworkMounts::MediumSideEffectsOptimizations;
|
||||
QTest::newRow("StrongSideEffectsOptimizations/mnt") << "/mnt" << KNetworkMounts::StrongSideEffectsOptimizations;
|
||||
QTest::newRow("KDirWatchDontAddWatches/mnt") << "/mnt" << KNetworkMounts::KDirWatchDontAddWatches;
|
||||
QTest::newRow("SymlinkPathsUseCache/mnt") << "/mnt" << KNetworkMounts::SymlinkPathsUseCache;
|
||||
|
||||
QTest::newRow("LowSideEffectsOptimizations/mnt/") << "/mnt/" << KNetworkMounts::LowSideEffectsOptimizations;
|
||||
QTest::newRow("MediumSideEffectsOptimizations/mnt/") << "/mnt/" << KNetworkMounts::MediumSideEffectsOptimizations;
|
||||
QTest::newRow("StrongSideEffectsOptimizations/mnt/") << "/mnt/" << KNetworkMounts::StrongSideEffectsOptimizations;
|
||||
QTest::newRow("KDirWatchDontAddWatches/mnt/") << "/mnt/" << KNetworkMounts::KDirWatchDontAddWatches;
|
||||
QTest::newRow("SymlinkPathsUseCache/mnt/") << "/mnt/" << KNetworkMounts::SymlinkPathsUseCache;
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigPathOptions()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFETCH(KNetworkMounts::KNetworkMountOption, option);
|
||||
|
||||
QVERIFY(!KNetworkMounts::self()->isOptionEnabledForPath(path, option));
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigOptions_data()
|
||||
{
|
||||
QTest::addColumn<KNetworkMounts::KNetworkMountOption>("option");
|
||||
QTest::addColumn<bool>("default_value");
|
||||
QTest::addColumn<bool>("expected_value");
|
||||
|
||||
QTest::newRow("LowSideEffectsOptimizations_false") << KNetworkMounts::LowSideEffectsOptimizations << false << false;
|
||||
QTest::newRow("LowSideEffectsOptimizations_true") << KNetworkMounts::LowSideEffectsOptimizations << true << true;
|
||||
|
||||
QTest::newRow("MediumSideEffectsOptimizationss_false") << KNetworkMounts::MediumSideEffectsOptimizations << false << false;
|
||||
QTest::newRow("MediumSideEffectsOptimizations_true") << KNetworkMounts::MediumSideEffectsOptimizations << true << true;
|
||||
|
||||
QTest::newRow("StrongSideEffectsOptimizations_false") << KNetworkMounts::StrongSideEffectsOptimizations << false << false;
|
||||
QTest::newRow("StrongSideEffectsOptimizationss_true") << KNetworkMounts::StrongSideEffectsOptimizations << true << true;
|
||||
|
||||
QTest::newRow("KDirWatchDontAddWatches_false") << KNetworkMounts::KDirWatchDontAddWatches << false << false;
|
||||
QTest::newRow("KDirWatchDontAddWatches_true") << KNetworkMounts::KDirWatchDontAddWatches << true << true;
|
||||
|
||||
QTest::newRow("SymlinkPathsUseCache_false") << KNetworkMounts::SymlinkPathsUseCache << false << false;
|
||||
QTest::newRow("SymlinkPathsUseCache_true") << KNetworkMounts::SymlinkPathsUseCache << true << true;
|
||||
}
|
||||
|
||||
void KNetworkMountsTestNoConfig::testNoConfigOptions()
|
||||
{
|
||||
QFETCH(KNetworkMounts::KNetworkMountOption, option);
|
||||
QFETCH(bool, default_value);
|
||||
QFETCH(bool, expected_value);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabled(option, default_value), expected_value);
|
||||
}
|
||||
|
||||
#include "moc_knetworkmountstestnoconfig.cpp"
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KNETWORKMOUNTSTESTNOCONFIG_H
|
||||
#define KNETWORKMOUNTSTESTNOCONFIG_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KNetworkMountsTestNoConfig : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testNoConfigPathTypes_data();
|
||||
void testNoConfigPathTypes();
|
||||
|
||||
void testNoConfigPathOptions_data();
|
||||
void testNoConfigPathOptions();
|
||||
|
||||
void testNoConfigOptions_data();
|
||||
void testNoConfigOptions();
|
||||
|
||||
private:
|
||||
QString m_configFileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "knetworkmountstestpaths.h"
|
||||
|
||||
#include <KNetworkMounts>
|
||||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KNetworkMountsTestPaths)
|
||||
|
||||
void KNetworkMountsTestPaths::initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
m_configFileName = QStringLiteral("%1/network_mounts").arg(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
|
||||
|
||||
QFile::remove(m_configFileName);
|
||||
QVERIFY(!QFile::exists(m_configFileName));
|
||||
|
||||
KNetworkMounts::self()->setEnabled(true);
|
||||
QVERIFY(KNetworkMounts::self()->isEnabled());
|
||||
KNetworkMounts::self()->sync();
|
||||
QVERIFY(QFile::exists(m_configFileName));
|
||||
QVERIFY(KNetworkMounts::self()->isEnabled());
|
||||
|
||||
// nfs path
|
||||
const QString nfsPath = QStringLiteral("/mnt/nfs");
|
||||
const QString savedNfsPath = QStringLiteral("/mnt/nfs/");
|
||||
const QStringList savedNfsPaths = {savedNfsPath};
|
||||
KNetworkMounts::self()->addPath(nfsPath, KNetworkMounts::NfsPaths);
|
||||
|
||||
QStringList allSavedPaths = savedNfsPaths;
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), allSavedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), QStringList());
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::NfsPaths), savedNfsPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), QStringList());
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), QStringList());
|
||||
|
||||
// smb shares
|
||||
const QStringList paths = {QStringLiteral("/mnt/server1"), QStringLiteral("/mnt/server2")};
|
||||
const QStringList savedSmbPaths = {QStringLiteral("/mnt/server1/"), QStringLiteral("/mnt/server2/")};
|
||||
KNetworkMounts::self()->setPaths(paths, KNetworkMounts::SmbPaths);
|
||||
|
||||
allSavedPaths << savedSmbPaths;
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), allSavedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), savedSmbPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::NfsPaths), savedNfsPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), QStringList());
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), QStringList());
|
||||
|
||||
// symlink dir
|
||||
const QStringList symlinkDirs = {QStringLiteral("/home/user/netshares")};
|
||||
const QStringList savedSymlinkDirs = {QStringLiteral("/home/user/netshares/")};
|
||||
KNetworkMounts::self()->setPaths(symlinkDirs, KNetworkMounts::SymlinkDirectory);
|
||||
|
||||
allSavedPaths << savedSymlinkDirs;
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), allSavedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), savedSmbPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), savedSymlinkDirs);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::NfsPaths), savedNfsPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), QStringList());
|
||||
|
||||
// symlink to nfs or smb
|
||||
const QString symlinkToNfs = QStringLiteral("/somedir/symlinkToNfs");
|
||||
const QString savedSymlinkToNfs = QStringLiteral("/somedir/symlinkToNfs/");
|
||||
const QStringList savedSymlinkToNfsPaths = {savedSymlinkToNfs};
|
||||
KNetworkMounts::self()->addPath(symlinkToNfs, KNetworkMounts::SymlinkToNetworkMount);
|
||||
|
||||
allSavedPaths << savedSymlinkToNfsPaths;
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->paths(), allSavedPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SmbPaths), savedSmbPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkDirectory), savedSymlinkDirs);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::NfsPaths), savedNfsPaths);
|
||||
QCOMPARE(KNetworkMounts::self()->paths(KNetworkMounts::SymlinkToNetworkMount), savedSymlinkToNfsPaths);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestPaths::cleanupTestCase()
|
||||
{
|
||||
KNetworkMounts::self()->sync();
|
||||
QFile::remove(m_configFileName);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestPaths::testPaths_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<bool>("expected_path_option");
|
||||
QTest::addColumn<bool>("expected_path");
|
||||
QTest::addColumn<bool>("expected_symlink_dir");
|
||||
QTest::addColumn<bool>("expected_symlink_to_nfs_or_smb");
|
||||
QTest::addColumn<bool>("expected_nfs");
|
||||
QTest::addColumn<bool>("expected_smb");
|
||||
|
||||
QTest::newRow("fast_path") << "/mnt" << false << false << false << false << false << false;
|
||||
QTest::newRow("fast_path_slash_end") << "/mnt/" << false << false << false << false << false << false;
|
||||
QTest::newRow("slow_path1") << "/mnt/server1" << true << true << false << false << false << true;
|
||||
QTest::newRow("slow_path2") << "/mnt/server2" << true << true << false << false << false << true;
|
||||
QTest::newRow("slow_path2_dir") << "/mnt/server2/dir" << true << true << false << false << false << true;
|
||||
QTest::newRow("slow_path2_dir_subdir") << "/mnt/server2/dir/subdir" << true << true << false << false << false << true;
|
||||
QTest::newRow("slow_path2_dir_subdir_slash_end") << "/mnt/server2/dir/subdir/" << true << true << false << false << false << true;
|
||||
QTest::newRow("slow_symlink_path") << "/home/user/netshares" << true << true << true << false << false << false;
|
||||
QTest::newRow("fast_path_root") << "/" << false << false << false << false << false << false;
|
||||
QTest::newRow("fast_path_home") << "/home" << false << false << false << false << false << false;
|
||||
QTest::newRow("fast_path_home_user") << "/home/user" << false << false << false << false << false << false;
|
||||
QTest::newRow("slow_symlink_path_subdir1") << "/home/user/netshares/subdir1" << true << true << true << false << false << false;
|
||||
QTest::newRow("slow_symlink_path_subdir1_subdir2") << "/home/user/netshares/subdir1/subdir2" << true << true << true << false << false << false;
|
||||
QTest::newRow("slow_symlink_path_subdir1_subdir2_slash_end") << "/home/user/netshares/subdir1/subdir2/" << true << true << true << false << false << false;
|
||||
QTest::newRow("slow_path_nfs") << "/mnt/nfs" << true << true << false << false << true << false;
|
||||
QTest::newRow("slow_path_nfs_dir") << "/mnt/nfs/dir" << true << true << false << false << true << false;
|
||||
QTest::newRow("slow_path_nfs_dir_subdir") << "/mnt/nfs/dir/subdir" << true << true << false << false << true << false;
|
||||
QTest::newRow("slow_path_nfs_dir_subdir_slash_end") << "/mnt/nfs/dir/subdir/" << true << true << false << false << true << false;
|
||||
QTest::newRow("slow_path_symlink_to_nfs") << "/somedir/symlinkToNfs" << true << true << false << true << false << false;
|
||||
QTest::newRow("slow_path_symlink_to_nfs_dir") << "/somedir/symlinkToNfs/dir" << true << true << false << true << false << false;
|
||||
QTest::newRow("slow_path_symlink_to_nfs_dir_subdir") << "/somedir/symlinkToNfs/dir/subdir" << true << true << false << true << false << false;
|
||||
QTest::newRow("slow_path_symlink_to_nfs_dir_subdir_slash_end") << "/somedir/symlinkToNfs/dir/subdir/" << true << true << false << true << false << false;
|
||||
}
|
||||
|
||||
void KNetworkMountsTestPaths::testPaths()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFETCH(bool, expected_path_option);
|
||||
QFETCH(bool, expected_path);
|
||||
QFETCH(bool, expected_symlink_dir);
|
||||
QFETCH(bool, expected_symlink_to_nfs_or_smb);
|
||||
QFETCH(bool, expected_nfs);
|
||||
QFETCH(bool, expected_smb);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabledForPath(path, KNetworkMounts::SymlinkPathsUseCache), expected_path_option);
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabledForPath(path, KNetworkMounts::KDirWatchDontAddWatches), expected_path_option);
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabledForPath(path, KNetworkMounts::LowSideEffectsOptimizations), expected_path_option);
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabledForPath(path, KNetworkMounts::MediumSideEffectsOptimizations), expected_path_option);
|
||||
QCOMPARE(KNetworkMounts::self()->isOptionEnabledForPath(path, KNetworkMounts::StrongSideEffectsOptimizations), expected_path_option);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path), expected_path);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path, KNetworkMounts::KNetworkMountsType::Any), expected_path);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path, KNetworkMounts::SymlinkDirectory), expected_symlink_dir);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path, KNetworkMounts::SymlinkToNetworkMount), expected_symlink_to_nfs_or_smb);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path, KNetworkMounts::NfsPaths), expected_nfs);
|
||||
|
||||
QCOMPARE(KNetworkMounts::self()->isSlowPath(path, KNetworkMounts::SmbPaths), expected_smb);
|
||||
}
|
||||
|
||||
#include "moc_knetworkmountstestpaths.cpp"
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KNETWORKMOUNTSTESTPATHS_H
|
||||
#define KNETWORKMOUNTSTESTPATHS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KNetworkMountsTestPaths : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testPaths_data();
|
||||
void testPaths();
|
||||
|
||||
private:
|
||||
QString m_configFileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "knetworkmountsteststatic.h"
|
||||
|
||||
// include static functions
|
||||
#include "knetworkmounts_p.h"
|
||||
|
||||
#include <KNetworkMounts>
|
||||
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KNetworkMountsTestStatic)
|
||||
|
||||
void KNetworkMountsTestStatic::testStaticFunctions_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<QStringList>("paths");
|
||||
QTest::addColumn<bool>("expected_is_slash_added_to_path");
|
||||
QTest::addColumn<QString>("expected_path_str");
|
||||
QTest::addColumn<bool>("expected_is_slash_added_to_paths");
|
||||
QTest::addColumn<QStringList>("expected_paths_str");
|
||||
QTest::addColumn<QString>("expected_matching");
|
||||
|
||||
QTest::newRow("empty1") << QString() << QStringList() << false << "" << false << QStringList() << QString();
|
||||
QTest::newRow("empty2") << "" << QStringList{QString(), QString()} << false << "" << false << QStringList{QString(), QString()} << QString();
|
||||
|
||||
QTest::newRow("/1") << "/" << QStringList() << false << "/" << false << QStringList() << QString();
|
||||
QTest::newRow("/2") << "/" << QStringList{QString(), QString()} << false << "/" << false << QStringList{QString(), QString()} << QString();
|
||||
QTest::newRow("/3") << "/" << (QStringList{QStringLiteral("/")}) << false << "/" << false << QStringList{QStringLiteral("/")} << "/";
|
||||
QTest::newRow("/4") << "/" << QStringList{QStringLiteral("/"), QString()} << false << "/" << false << QStringList{QStringLiteral("/"), QString()} << "/";
|
||||
|
||||
QTest::newRow("/mnt1") << "/mnt" << QStringList() << true << "/mnt/" << false << QStringList() << QString();
|
||||
QTest::newRow("/mnt2") << "/mnt" << QStringList{QStringLiteral("/mnt")} << true << "/mnt/" << true << QStringList{QStringLiteral("/mnt/")} << "/mnt";
|
||||
QTest::newRow("/mnt3") << "/mnt" << QStringList{QStringLiteral("/mnt/")} << true << "/mnt/" << false << QStringList{QStringLiteral("/mnt/")} << "/mnt/";
|
||||
|
||||
QTest::newRow("/mnt/test1") << "/mnt" << (QStringList() << QStringLiteral("/mnt/test1") << QStringLiteral("/mnt/test2/")) << true << "/mnt/" << true
|
||||
<< QStringList{QStringLiteral("/mnt/test1/"), QStringLiteral("/mnt/test2/")} << "";
|
||||
QTest::newRow("/mnt/test2") << "/mnt/test2" << QStringList{QStringLiteral("/mnt/test1/"), QStringLiteral("/mnt/test2/")} << true << "/mnt/test2/" << false
|
||||
<< QStringList{QStringLiteral("/mnt/test1/"), QStringLiteral("/mnt/test2/")} << "/mnt/test2/";
|
||||
QTest::newRow("/mnt/test3") << "/mnt/test2/" << (QStringList() << QStringLiteral("/mnt/test1/") << QStringLiteral("/mnt/test2/")) << false << "/mnt/test2/"
|
||||
<< false << QStringList{QStringLiteral("/mnt/test1/"), QStringLiteral("/mnt/test2/")} << "/mnt/test2/";
|
||||
}
|
||||
|
||||
void KNetworkMountsTestStatic::testStaticFunctions()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFETCH(QStringList, paths);
|
||||
QFETCH(bool, expected_is_slash_added_to_path);
|
||||
QFETCH(QString, expected_path_str);
|
||||
QFETCH(bool, expected_is_slash_added_to_paths);
|
||||
QFETCH(QStringList, expected_paths_str);
|
||||
QFETCH(QString, expected_matching);
|
||||
|
||||
QCOMPARE(getMatchingPath(path, paths), expected_matching);
|
||||
|
||||
QCOMPARE(ensureTrailingSlash(&path), expected_is_slash_added_to_path);
|
||||
QCOMPARE(path, expected_path_str);
|
||||
QCOMPARE(ensureTrailingSlashes(&paths), expected_is_slash_added_to_paths);
|
||||
QCOMPARE(paths, expected_paths_str);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestStatic::testStaticKNetworkMountOptionToString_data()
|
||||
{
|
||||
QTest::addColumn<KNetworkMounts::KNetworkMountOption>("option");
|
||||
QTest::addColumn<QString>("string");
|
||||
|
||||
QTest::newRow("LowSideEffectsOptimizations") << KNetworkMounts::LowSideEffectsOptimizations << "LowSideEffectsOptimizations";
|
||||
QTest::newRow("MediumSideEffectsOptimizations") << KNetworkMounts::MediumSideEffectsOptimizations << "MediumSideEffectsOptimizations";
|
||||
QTest::newRow("StrongSideEffectsOptimizations") << KNetworkMounts::StrongSideEffectsOptimizations << "StrongSideEffectsOptimizations";
|
||||
QTest::newRow("KDirWatchDontAddWatches") << KNetworkMounts::KDirWatchDontAddWatches << "KDirWatchDontAddWatches";
|
||||
QTest::newRow("SymlinkPathsUseCache") << KNetworkMounts::SymlinkPathsUseCache << "SymlinkPathsUseCache";
|
||||
}
|
||||
void KNetworkMountsTestStatic::testStaticKNetworkMountOptionToString()
|
||||
{
|
||||
QFETCH(KNetworkMounts::KNetworkMountOption, option);
|
||||
QFETCH(QString, string);
|
||||
|
||||
QCOMPARE(enumToString(option), string);
|
||||
}
|
||||
|
||||
void KNetworkMountsTestStatic::testStaticKNetworkMountsTypeToString_data()
|
||||
{
|
||||
QTest::addColumn<KNetworkMounts::KNetworkMountsType>("type");
|
||||
QTest::addColumn<QString>("string");
|
||||
|
||||
QTest::newRow("NfsPaths") << KNetworkMounts::NfsPaths << "NfsPaths";
|
||||
QTest::newRow("SmbPaths") << KNetworkMounts::SmbPaths << "SmbPaths";
|
||||
QTest::newRow("SymlinkDirectory") << KNetworkMounts::SymlinkDirectory << "SymlinkDirectory";
|
||||
QTest::newRow("SymlinkToNetworkMount") << KNetworkMounts::SymlinkToNetworkMount << "SymlinkToNetworkMount";
|
||||
QTest::newRow("Any") << KNetworkMounts::Any << "Any";
|
||||
}
|
||||
void KNetworkMountsTestStatic::testStaticKNetworkMountsTypeToString()
|
||||
{
|
||||
QFETCH(KNetworkMounts::KNetworkMountsType, type);
|
||||
QFETCH(QString, string);
|
||||
|
||||
QCOMPARE(enumToString(type), string);
|
||||
}
|
||||
|
||||
#include "moc_knetworkmountsteststatic.cpp"
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
This software is a contribution of the LiMux project of the city of Munich.
|
||||
SPDX-FileCopyrightText: 2021 Robert Hoffmann <robert@roberthoffmann.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KNETWORKMOUNTSTESTSTATIC_H
|
||||
#define KNETWORKMOUNTSTESTSTATIC_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KNetworkMountsTestStatic : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testStaticFunctions_data();
|
||||
void testStaticFunctions();
|
||||
void testStaticKNetworkMountOptionToString_data();
|
||||
void testStaticKNetworkMountOptionToString();
|
||||
void testStaticKNetworkMountsTypeToString_data();
|
||||
void testStaticKNetworkMountsTypeToString();
|
||||
|
||||
private:
|
||||
QString m_configFileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014-2019 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "kosrelease.h"
|
||||
|
||||
class KOSReleaseTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testParse()
|
||||
{
|
||||
KOSRelease r(QFINDTESTDATA("data/os-release"));
|
||||
QCOMPARE(r.name(), QStringLiteral("Name"));
|
||||
QCOMPARE(r.version(), QStringLiteral("100.5"));
|
||||
QCOMPARE(r.id(), QStringLiteral("theid"));
|
||||
QCOMPARE(r.idLike(), QStringList({QStringLiteral("otherid"), QStringLiteral("otherotherid")}));
|
||||
QCOMPARE(r.versionCodename(), QStringLiteral("versioncodename"));
|
||||
QCOMPARE(r.versionId(), QStringLiteral("500.1"));
|
||||
QCOMPARE(r.prettyName(), QStringLiteral("Vicuña — Pretty Name #1"));
|
||||
QCOMPARE(r.ansiColor(), QStringLiteral("1;34"));
|
||||
QCOMPARE(r.cpeName(), QStringLiteral("cpe:/o:foo:bar:100"));
|
||||
QCOMPARE(r.homeUrl(), QStringLiteral("https://url.home"));
|
||||
QCOMPARE(r.documentationUrl(), QStringLiteral("https://url.docs"));
|
||||
QCOMPARE(r.supportUrl(), QStringLiteral("https://url.support"));
|
||||
QCOMPARE(r.bugReportUrl(), QStringLiteral("https://url.bugs"));
|
||||
QCOMPARE(r.privacyPolicyUrl(), QStringLiteral("https://url.privacy"));
|
||||
QCOMPARE(r.buildId(), QStringLiteral("105.5"));
|
||||
QCOMPARE(r.variant(), QStringLiteral("Test = Edition"));
|
||||
QCOMPARE(r.variantId(), QStringLiteral("test"));
|
||||
QCOMPARE(r.logo(), QStringLiteral("start-here-test"));
|
||||
QCOMPARE(r.extraKeys(), QStringList({QStringLiteral("DEBIAN_BTS")}));
|
||||
QCOMPARE(r.extraValue(QStringLiteral("DEBIAN_BTS")), QStringLiteral("debbugs://bugs.debian.org/"));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(KOSReleaseTest)
|
||||
|
||||
#include "kosreleasetest.moc"
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "kprocesslisttest.h"
|
||||
#include "kprocesslist.h"
|
||||
#include "kuser.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QTest>
|
||||
#include <algorithm>
|
||||
|
||||
namespace
|
||||
{
|
||||
QString getTestExeName()
|
||||
{
|
||||
static QString testExeName = QCoreApplication::instance()->applicationFilePath().section(QLatin1Char('/'), -1);
|
||||
return testExeName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QTEST_MAIN(KProcessListTest)
|
||||
|
||||
void KProcessListTest::testKProcessInfoConstructionAssignment()
|
||||
{
|
||||
KProcessList::KProcessInfo processInfoDefaultConstructed;
|
||||
QVERIFY(processInfoDefaultConstructed.isValid() == false);
|
||||
const qint64 pid(42);
|
||||
const QString name(QStringLiteral("/bin/some_exe"));
|
||||
const QString user(QStringLiteral("some_user"));
|
||||
KProcessList::KProcessInfo processInfo(pid, name, user);
|
||||
QVERIFY(processInfo.isValid() == true);
|
||||
QCOMPARE(processInfo.pid(), pid);
|
||||
QCOMPARE(processInfo.name(), name);
|
||||
QCOMPARE(processInfo.user(), user);
|
||||
KProcessList::KProcessInfo processInfoCopy(processInfo);
|
||||
QVERIFY(processInfoCopy.isValid() == true);
|
||||
QCOMPARE(processInfoCopy.pid(), pid);
|
||||
QCOMPARE(processInfoCopy.name(), name);
|
||||
QCOMPARE(processInfoCopy.user(), user);
|
||||
KProcessList::KProcessInfo processInfoAssignment;
|
||||
processInfoAssignment = processInfo;
|
||||
QVERIFY(processInfoAssignment.isValid() == true);
|
||||
QCOMPARE(processInfoAssignment.pid(), pid);
|
||||
QCOMPARE(processInfoAssignment.name(), name);
|
||||
QCOMPARE(processInfoAssignment.user(), user);
|
||||
}
|
||||
|
||||
void KProcessListTest::testProcessInfoList()
|
||||
{
|
||||
KProcessList::KProcessInfoList processInfoList = KProcessList::processInfoList();
|
||||
QVERIFY(processInfoList.empty() == false);
|
||||
auto testProcessIterator = std::find_if(processInfoList.begin(), processInfoList.end(), [](const KProcessList::KProcessInfo &info) {
|
||||
return QDir::fromNativeSeparators(info.command()).endsWith(QLatin1String("/") + getTestExeName());
|
||||
});
|
||||
QVERIFY(testProcessIterator != processInfoList.end());
|
||||
const auto &processInfo = *testProcessIterator;
|
||||
QVERIFY(processInfo.isValid() == true);
|
||||
QVERIFY(QDir::fromNativeSeparators(processInfo.command()).endsWith(QLatin1String("/") + getTestExeName()));
|
||||
QCOMPARE(processInfo.name(), getTestExeName());
|
||||
QCOMPARE(processInfo.pid(), QCoreApplication::applicationPid());
|
||||
QCOMPARE(processInfo.user(), KUser().loginName());
|
||||
}
|
||||
|
||||
void KProcessListTest::testProcessInfo()
|
||||
{
|
||||
const qint64 testExePid = QCoreApplication::applicationPid();
|
||||
KProcessList::KProcessInfo processInfo = KProcessList::processInfo(testExePid);
|
||||
QVERIFY(processInfo.isValid() == true);
|
||||
QVERIFY(QDir::fromNativeSeparators(processInfo.command()).endsWith(QLatin1String("/") + getTestExeName()));
|
||||
QCOMPARE(processInfo.pid(), testExePid);
|
||||
QCOMPARE(processInfo.user(), KUser().loginName());
|
||||
}
|
||||
|
||||
void KProcessListTest::testProcessInfoNotFound()
|
||||
{
|
||||
KProcessList::KProcessInfo processInfo = KProcessList::processInfo(-1);
|
||||
QVERIFY(processInfo.isValid() == false);
|
||||
}
|
||||
|
||||
#include "moc_kprocesslisttest.cpp"
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2019 David Hallas <david@davidhallas.dk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#ifndef KPROCESSLISTTEST_H
|
||||
#define KPROCESSLISTTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KProcessListTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testKProcessInfoConstructionAssignment();
|
||||
void testProcessInfoList();
|
||||
void testProcessInfo();
|
||||
void testProcessInfoNotFound();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kprocesstest_helper.h"
|
||||
#include <QFile>
|
||||
#include <QObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
#include <kprocess.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
class KProcessTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void test_channels();
|
||||
void test_setShellCommand();
|
||||
void test_inheritance();
|
||||
};
|
||||
|
||||
// IOCCC nomination pending
|
||||
|
||||
static QString callHelper(KProcess::OutputChannelMode how)
|
||||
{
|
||||
QProcess p;
|
||||
p.setProcessChannelMode(QProcess::MergedChannels);
|
||||
|
||||
QString helper = QCoreApplication::applicationDirPath() + QStringLiteral("/kprocesstest_helper");
|
||||
#ifdef Q_OS_WIN
|
||||
helper += QStringLiteral(".exe");
|
||||
#endif
|
||||
|
||||
Q_ASSERT(QFile::exists(helper));
|
||||
p.start(helper, QStringList() << QString::number(how) << QStringLiteral("--nocrashhandler"));
|
||||
p.waitForFinished();
|
||||
return QString::fromLatin1(p.readAllStandardOutput());
|
||||
}
|
||||
|
||||
#define EO EOUT "\n"
|
||||
#define EE EERR "\n"
|
||||
#define TESTCHAN(me, ms, pout, rout, rerr) \
|
||||
e = QStringLiteral("mode: " ms "\n" POUT pout ROUT rout RERR rerr); \
|
||||
a = QStringLiteral("mode: " ms "\n") + callHelper(KProcess::me); \
|
||||
QCOMPARE(a, e)
|
||||
|
||||
void KProcessTest::test_channels()
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
QString e;
|
||||
QString a;
|
||||
TESTCHAN(SeparateChannels, "separate", "", EO, EE);
|
||||
TESTCHAN(ForwardedChannels, "forwarded", EO EE, "", "");
|
||||
TESTCHAN(OnlyStderrChannel, "forwarded stdout", EO, "", EE);
|
||||
TESTCHAN(OnlyStdoutChannel, "forwarded stderr", EE, EO, "");
|
||||
TESTCHAN(MergedChannels, "merged", "", EO EE, "");
|
||||
#else
|
||||
Q_UNUSED(callHelper);
|
||||
QSKIP("This test needs a UNIX system");
|
||||
#endif
|
||||
}
|
||||
|
||||
void KProcessTest::test_setShellCommand()
|
||||
{
|
||||
// Condition copied from kprocess.cpp
|
||||
#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__GNU__)
|
||||
QSKIP("This test needs a free UNIX system");
|
||||
#else
|
||||
KProcess p;
|
||||
|
||||
p.setShellCommand(QStringLiteral("cat"));
|
||||
QCOMPARE(p.program().count(), 1);
|
||||
QCOMPARE(p.program().at(0), QStandardPaths::findExecutable(QStringLiteral("cat")));
|
||||
QVERIFY(p.program().at(0).endsWith(QLatin1String("/cat")));
|
||||
p.setShellCommand(QStringLiteral("true || false"));
|
||||
QCOMPARE(p.program(), QStringList() << QStringLiteral("/bin/sh") << QStringLiteral("-c") << QString::fromLatin1("true || false"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void KProcessTest::test_inheritance()
|
||||
{
|
||||
KProcess kproc;
|
||||
QProcess *qproc = &kproc;
|
||||
const QString program = QStringLiteral("foobar");
|
||||
const QStringList arguments{QStringLiteral("meow")};
|
||||
|
||||
kproc.setProgram(program, arguments);
|
||||
QCOMPARE(qproc->program(), program);
|
||||
QCOMPARE(qproc->arguments(), arguments);
|
||||
kproc.clearProgram();
|
||||
QCOMPARE(qproc->program(), QString());
|
||||
QCOMPARE(qproc->arguments(), QStringList());
|
||||
|
||||
kproc << program << arguments;
|
||||
QCOMPARE(qproc->program(), program);
|
||||
QCOMPARE(qproc->arguments(), arguments);
|
||||
kproc.clearProgram();
|
||||
QCOMPARE(qproc->program(), QString());
|
||||
QCOMPARE(qproc->arguments(), QStringList());
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// not all distros have /bin/true, e.g. NixOS lacks it
|
||||
if (!QFile::exists(QStringLiteral("/bin/true"))) {
|
||||
QSKIP("This test needs /bin/true");
|
||||
}
|
||||
|
||||
kproc.setShellCommand(QStringLiteral("/bin/true meow"));
|
||||
QCOMPARE(qproc->program(), QStringLiteral("/bin/true"));
|
||||
QCOMPARE(qproc->arguments(), arguments);
|
||||
kproc.clearProgram();
|
||||
QCOMPARE(qproc->program(), QString());
|
||||
QCOMPARE(qproc->arguments(), QStringList());
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(KProcessTest)
|
||||
|
||||
#include "kprocesstest.moc"
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kprocesstest_helper.h"
|
||||
#include <kprocess.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("Missing parameter");
|
||||
return -1;
|
||||
}
|
||||
KProcess p;
|
||||
p.setShellCommand(QString::fromLatin1("echo " EOUT "; echo " EERR " >&2"));
|
||||
p.setOutputChannelMode(static_cast<KProcess::OutputChannelMode>(atoi(argv[1])));
|
||||
fputs(POUT, stdout);
|
||||
fflush(stdout);
|
||||
p.execute();
|
||||
fputs(ROUT, stdout);
|
||||
fputs(p.readAllStandardOutput().constData(), stdout);
|
||||
fputs(RERR, stdout);
|
||||
if (p.outputChannelMode() != KProcess::MergedChannels) {
|
||||
fputs(p.readAllStandardError().constData(), stdout);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#define EOUT "foo - stdout"
|
||||
#define EERR "bar - stderr"
|
||||
#define POUT "program output:\n"
|
||||
#define ROUT "received stdout:\n"
|
||||
#define RERR "received stderr:\n"
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2016 Michael Pyne <mpyne@kde.org>
|
||||
SPDX-FileCopyrightText: 2016 Arne Spiegelhauer <gm2.asp@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <krandom.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <QTest>
|
||||
#include <QThread>
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
#include <QVarLengthArray>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
typedef QVarLengthArray<int> intSequenceType;
|
||||
|
||||
static const char *binpath;
|
||||
|
||||
class KRandomTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void test_randomString();
|
||||
void test_randomStringThreaded();
|
||||
void test_shuffle();
|
||||
};
|
||||
|
||||
void KRandomTest::test_randomString()
|
||||
{
|
||||
const int desiredLength = 12;
|
||||
const QString testString = KRandom::randomString(desiredLength);
|
||||
const QRegularExpression outputFormat(QRegularExpression::anchoredPattern(QStringLiteral("[A-Za-z0-9]+")));
|
||||
const QRegularExpressionMatch match = outputFormat.match(testString);
|
||||
|
||||
QCOMPARE(testString.length(), desiredLength);
|
||||
QVERIFY(match.hasMatch());
|
||||
}
|
||||
|
||||
void KRandomTest::test_shuffle()
|
||||
{
|
||||
{
|
||||
QRandomGenerator rg(1);
|
||||
QList<int> list = {1, 2, 3, 4, 5};
|
||||
const QList<int> shuffled = {5, 2, 4, 3, 1};
|
||||
KRandom::shuffle(list, &rg);
|
||||
QCOMPARE(list, shuffled);
|
||||
}
|
||||
|
||||
{
|
||||
QRandomGenerator rg(1);
|
||||
QList<int> vector = {1, 2, 3, 4, 5};
|
||||
const QList<int> shuffled = {5, 2, 4, 3, 1};
|
||||
KRandom::shuffle(vector, &rg);
|
||||
QCOMPARE(vector, shuffled);
|
||||
}
|
||||
|
||||
{
|
||||
QRandomGenerator rg(1);
|
||||
std::vector<int> std_vector = {1, 2, 3, 4, 5};
|
||||
const std::vector<int> shuffled = {5, 2, 4, 3, 1};
|
||||
KRandom::shuffle(std_vector, &rg);
|
||||
QCOMPARE(std_vector, shuffled);
|
||||
}
|
||||
}
|
||||
|
||||
class KRandomTestThread : public QThread
|
||||
{
|
||||
protected:
|
||||
void run() override
|
||||
{
|
||||
result = KRandom::randomString(32);
|
||||
};
|
||||
|
||||
public:
|
||||
QString result;
|
||||
};
|
||||
|
||||
void KRandomTest::test_randomStringThreaded()
|
||||
{
|
||||
static const int size = 5;
|
||||
KRandomTestThread *threads[size];
|
||||
for (int i = 0; i < size; ++i) {
|
||||
threads[i] = new KRandomTestThread();
|
||||
threads[i]->start();
|
||||
}
|
||||
QSet<QString> results;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
threads[i]->wait(2000);
|
||||
results.insert(threads[i]->result);
|
||||
}
|
||||
// each thread should have returned a unique result
|
||||
QCOMPARE(results.size(), size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
delete threads[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Manually implemented to dispatch to child process if needed to support
|
||||
// subtests
|
||||
int main([[maybe_unused]] int argc, char *argv[])
|
||||
{
|
||||
binpath = argv[0];
|
||||
KRandomTest randomTest;
|
||||
return QTest::qExec(&randomTest);
|
||||
}
|
||||
|
||||
#include "krandomtest.moc"
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "kruntimeplatform.h"
|
||||
#include <QObject>
|
||||
#include <QTest>
|
||||
|
||||
class KRuntimePlatformTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testRuntimePlatform()
|
||||
{
|
||||
qputenv("PLASMA_PLATFORM", "mobile:bigscreen");
|
||||
QStringList expected{QStringLiteral("mobile"), QStringLiteral("bigscreen")};
|
||||
QCOMPARE(KRuntimePlatform::runtimePlatform(), expected);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(KRuntimePlatformTest)
|
||||
|
||||
#include "kruntimeplatformtest.moc"
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <kshareddatacache.h>
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <string.h> // strcpy
|
||||
|
||||
class KSharedDataCacheTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void simpleInsert();
|
||||
};
|
||||
|
||||
void KSharedDataCacheTest::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void KSharedDataCacheTest::simpleInsert()
|
||||
{
|
||||
const QLatin1String cacheName("myTestCache");
|
||||
const QLatin1String key("mypic");
|
||||
// clear the cache
|
||||
QString cacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QLatin1String("/") + cacheName + QLatin1String(".kcache");
|
||||
QFile file(cacheFile);
|
||||
if (file.exists()) {
|
||||
QVERIFY(file.remove());
|
||||
}
|
||||
// insert something into it
|
||||
KSharedDataCache cache(cacheName, 5 * 1024 * 1024);
|
||||
#ifndef Q_OS_WIN // the windows implementation is currently only memory based and not really shared
|
||||
QVERIFY(file.exists()); // make sure we got the cache filename right
|
||||
#endif
|
||||
QByteArray data;
|
||||
data.resize(9228);
|
||||
strcpy(data.data(), "Hello world");
|
||||
QVERIFY(cache.insert(key, data));
|
||||
// read it out again
|
||||
QByteArray result;
|
||||
QVERIFY(cache.find(key, &result));
|
||||
QCOMPARE(result, data);
|
||||
// another insert
|
||||
strcpy(data.data(), "Hello KDE");
|
||||
QVERIFY(cache.insert(key, data));
|
||||
// and another read
|
||||
QVERIFY(cache.find(key, &result));
|
||||
QCOMPARE(result, data);
|
||||
}
|
||||
|
||||
QTEST_MAIN(KSharedDataCacheTest)
|
||||
|
||||
#include "kshareddatacachetest.moc"
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2003, 2007-2008 Oswald Buddenhagen <ossi@kde.org>
|
||||
SPDX-FileCopyrightText: 2005 Thomas Braxton <brax108@cox.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include <kshell.h>
|
||||
#include <kuser.h>
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class KShellTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void tildeExpand();
|
||||
void tildeCollapse();
|
||||
void quoteArg();
|
||||
void joinArgs();
|
||||
void splitJoin();
|
||||
void quoteSplit();
|
||||
void quoteSplit_data();
|
||||
void abortOnMeta();
|
||||
};
|
||||
|
||||
// The expansion of ~me isn't exactly QDir::homePath(), in case $HOME has a trailing slash, it's kept.
|
||||
static QString myHomePath()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
return QDir::homePath();
|
||||
#else
|
||||
return QString::fromLocal8Bit(qgetenv("HOME"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void KShellTest::tildeExpand()
|
||||
{
|
||||
QString me(KUser().loginName());
|
||||
QCOMPARE(KShell::tildeExpand(QStringLiteral("~")), QDir::homePath());
|
||||
QCOMPARE(KShell::tildeExpand(QStringLiteral("~/dir")), QString(QDir::homePath() + QStringLiteral("/dir")));
|
||||
QCOMPARE(KShell::tildeExpand(QLatin1Char('~') + me), myHomePath());
|
||||
QCOMPARE(KShell::tildeExpand(QLatin1Char('~') + me + QStringLiteral("/dir")), QString(myHomePath() + QStringLiteral("/dir")));
|
||||
#ifdef Q_OS_WIN
|
||||
QCOMPARE(KShell::tildeExpand(QStringLiteral("^~") + me), QString(QLatin1Char('~') + me));
|
||||
#else
|
||||
QCOMPARE(KShell::tildeExpand(QStringLiteral("\\~") + me), QString(QStringLiteral("~") + me));
|
||||
#endif
|
||||
}
|
||||
|
||||
void KShellTest::tildeCollapse()
|
||||
{
|
||||
QCOMPARE(KShell::tildeCollapse(QDir::homePath()), QStringLiteral("~"));
|
||||
QCOMPARE(KShell::tildeCollapse(QDir::homePath() + QStringLiteral("/Documents")), QStringLiteral("~/Documents"));
|
||||
QCOMPARE(KShell::tildeCollapse(QStringLiteral("/test/") + QDir::homePath()), QStringLiteral("/test/") + QDir::homePath());
|
||||
}
|
||||
|
||||
void KShellTest::quoteArg()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("a space")), QStringLiteral("\"a space\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("fds\\\"")), QStringLiteral("fds\\\\\\^\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("\\\\foo")), QStringLiteral("\\\\foo"));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("\"asdf\"")), QStringLiteral("\\^\"asdf\\^\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("with\\")), QStringLiteral("\"with\\\\\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("\\\\")), QStringLiteral("\"\\\\\\\\\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("\"a space\\\"")), QStringLiteral("\\^\"\"a space\"\\\\\\^\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("as df\\")), QStringLiteral("\"as df\\\\\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("foo bar\"\\\"bla")), QStringLiteral("\"foo bar\"\\^\"\\\\\\^\"\"bla\""));
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("a % space")), QStringLiteral("\"a %PERCENT_SIGN% space\""));
|
||||
#else
|
||||
QCOMPARE(KShell::quoteArg(QStringLiteral("a space")), QStringLiteral("'a space'"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void KShellTest::joinArgs()
|
||||
{
|
||||
QStringList list;
|
||||
list << QStringLiteral("this") << QStringLiteral("is") << QStringLiteral("a") << QStringLiteral("test");
|
||||
QCOMPARE(KShell::joinArgs(list), QStringLiteral("this is a test"));
|
||||
}
|
||||
|
||||
static QString sj(const QString &str, KShell::Options flags, KShell::Errors *ret)
|
||||
{
|
||||
return KShell::joinArgs(KShell::splitArgs(str, flags, ret));
|
||||
}
|
||||
|
||||
void KShellTest::splitJoin()
|
||||
{
|
||||
KShell::Errors err = KShell::NoError;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QCOMPARE(sj(QStringLiteral("\"(sulli)\" text"), KShell::NoOptions, &err), QStringLiteral("\"(sulli)\" text"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral(" ha\\ lo "), KShell::NoOptions, &err), QStringLiteral("\"ha\\\\\" lo"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("say \" error"), KShell::NoOptions, &err), QString());
|
||||
QVERIFY(err == KShell::BadQuoting);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("no \" error\""), KShell::NoOptions, &err), QStringLiteral("no \" error\""));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("say \" still error"), KShell::NoOptions, &err), QString());
|
||||
QVERIFY(err == KShell::BadQuoting);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("BLA;asdf sdfess d"), KShell::NoOptions, &err), QStringLiteral("\"BLA;asdf\" sdfess d"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("B\"L\"A&sdf FOO|bar sdf wer "), KShell::NoOptions, &err), QStringLiteral("\"BLA&sdf\" \"FOO|bar\" sdf wer"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("\"\"\"just \"\" fine\"\"\""), KShell::NoOptions, &err), QStringLiteral("\\^\"\"just \"\\^\"\" fine\"\\^\""));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
#else
|
||||
QCOMPARE(sj(QString::fromUtf8("\"~qU4rK\" 'text' 'jo'\"jo\" $'crap' $'\\\\\\'\\e\\x21' ha\\ lo \\a"), KShell::NoOptions, &err),
|
||||
QString::fromUtf8("'~qU4rK' text jojo crap '\\'\\''\x1b!' 'ha lo' a"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("\"~qU4rK\" 'text'"), KShell::TildeExpand, &err), QStringLiteral("'~qU4rK' text"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("~\"qU4rK\" 'text'"), KShell::TildeExpand, &err), QStringLiteral("'~qU4rK' text"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("~/\"dir\" 'text'"), KShell::TildeExpand, &err), QString(QDir::homePath() + QStringLiteral("/dir text")));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("~ 'text' ~"), KShell::TildeExpand, &err), QString(QDir::homePath() + QStringLiteral(" text ") + QDir::homePath()));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("\\~ blah"), KShell::TildeExpand, &err), QStringLiteral("'~' blah"));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("~qU4rK ~") + KUser().loginName(), KShell::TildeExpand, &err), QString(QStringLiteral("'~qU4rK' ") + myHomePath()));
|
||||
QVERIFY(err == KShell::NoError);
|
||||
|
||||
const QString unicodeSpaceFileName = QStringLiteral("test テスト.txt"); // #345140
|
||||
QCOMPARE(sj(unicodeSpaceFileName, KShell::AbortOnMeta | KShell::TildeExpand, &err), unicodeSpaceFileName);
|
||||
QVERIFY(err == KShell::NoError);
|
||||
#endif
|
||||
}
|
||||
|
||||
void KShellTest::quoteSplit_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
|
||||
QTest::newRow("no space") << QStringLiteral("hiho");
|
||||
QTest::newRow("regular space") << QStringLiteral("hi there");
|
||||
QTest::newRow("special space") << QString::fromUtf8("如何定期清潔典型的電風扇 講義.pdf");
|
||||
}
|
||||
|
||||
void KShellTest::quoteSplit()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
|
||||
// Splitting a quote arg should always just return one argument
|
||||
const QStringList args = KShell::splitArgs(KShell::quoteArg(string));
|
||||
QCOMPARE(args.count(), 1);
|
||||
}
|
||||
|
||||
void KShellTest::abortOnMeta()
|
||||
{
|
||||
KShell::Errors err1 = KShell::NoError;
|
||||
KShell::Errors err2 = KShell::NoError;
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("text"), KShell::AbortOnMeta, &err1), QStringLiteral("text"));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QVERIFY(KShell::splitArgs(QStringLiteral("BLA & asdf sdfess d"), KShell::AbortOnMeta, &err1).isEmpty());
|
||||
QVERIFY(err1 == KShell::FoundMeta);
|
||||
|
||||
QVERIFY(KShell::splitArgs(QStringLiteral("foo %PATH% bar"), KShell::AbortOnMeta, &err1).isEmpty());
|
||||
QVERIFY(err1 == KShell::FoundMeta);
|
||||
QCOMPARE(sj(QStringLiteral("foo %PERCENT_SIGN% bar"), KShell::AbortOnMeta, &err1), QStringLiteral("foo %PERCENT_SIGN% bar"));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("@foo ^& bar"), KShell::AbortOnMeta, &err1), QStringLiteral("foo \"&\" bar"));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("\"BLA|asdf\" sdfess d"), KShell::AbortOnMeta, &err1), QStringLiteral("\"BLA|asdf\" sdfess d"));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("B\"L\"A\"|\"sdf \"FOO | bar\" sdf wer"), KShell::AbortOnMeta, &err1), QStringLiteral("\"BLA|sdf\" \"FOO | bar\" sdf wer"));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("b-q me \\\\^|\\\\\\^\""), KShell::AbortOnMeta, &err1), QStringLiteral("b-q me \"\\\\|\"\\\\\\^\""));
|
||||
QVERIFY(err1 == KShell::NoError);
|
||||
#else
|
||||
QCOMPARE(sj(QStringLiteral("say \" error"), KShell::NoOptions, &err1), QString());
|
||||
QVERIFY(err1 != KShell::NoError);
|
||||
|
||||
QCOMPARE(sj(QStringLiteral("say \" still error"), KShell::AbortOnMeta, &err1), QString());
|
||||
QVERIFY(err1 != KShell::NoError);
|
||||
|
||||
QVERIFY(sj(QStringLiteral("say `echo no error`"), KShell::NoOptions, &err1) != sj(QStringLiteral("say `echo no error`"), KShell::AbortOnMeta, &err2));
|
||||
QVERIFY(err1 != err2);
|
||||
|
||||
QVERIFY(sj(QStringLiteral("BLA=say echo meta"), KShell::NoOptions, &err1) != sj(QStringLiteral("BLA=say echo meta"), KShell::AbortOnMeta, &err2));
|
||||
QVERIFY(err1 != err2);
|
||||
|
||||
QVERIFY(sj(QStringLiteral("B\"L\"A=say FOO=bar echo meta"), KShell::NoOptions, &err1)
|
||||
== sj(QStringLiteral("B\"L\"A=say FOO=bar echo meta"), KShell::AbortOnMeta, &err2));
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(KShellTest)
|
||||
|
||||
#include "kshelltest.moc"
|
||||
@@ -0,0 +1,263 @@
|
||||
|
||||
#include "kstringhandlertest.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
#include <QTest>
|
||||
|
||||
QTEST_MAIN(KStringHandlerTest)
|
||||
|
||||
#include "kstringhandler.h"
|
||||
|
||||
QString KStringHandlerTest::test = QStringLiteral("The quick brown fox jumped over the lazy bridge. ");
|
||||
|
||||
void KStringHandlerTest::capwords()
|
||||
{
|
||||
QCOMPARE(KStringHandler::capwords(test), QStringLiteral("The Quick Brown Fox Jumped Over The Lazy Bridge. "));
|
||||
}
|
||||
|
||||
void KStringHandlerTest::tagURLs()
|
||||
{
|
||||
QString test = QStringLiteral("Click on https://foo@bar:www.kde.org/yoyo/dyne.html#a1 for info.");
|
||||
QCOMPARE(KStringHandler::tagUrls(test),
|
||||
QStringLiteral("Click on <a href=\"https://foo@bar:www.kde.org/yoyo/dyne.html#a1\">https://foo@bar:www.kde.org/yoyo/dyne.html#a1</a> for info."));
|
||||
|
||||
test = QStringLiteral("http://www.foo.org/story$806");
|
||||
QCOMPARE(KStringHandler::tagUrls(test), QStringLiteral("<a href=\"http://www.foo.org/story$806\">http://www.foo.org/story$806</a>"));
|
||||
|
||||
test = QStringLiteral("http://www.foo.org/bla-(bli)");
|
||||
QCOMPARE(KStringHandler::tagUrls(test), QStringLiteral("<a href=\"http://www.foo.org/bla-(bli)\">http://www.foo.org/bla-(bli)</a>"));
|
||||
|
||||
test = QStringLiteral("http://www.foo.org/bla-bli");
|
||||
QCOMPARE(KStringHandler::tagUrls(test), QStringLiteral("<a href=\"http://www.foo.org/bla-bli\">http://www.foo.org/bla-bli</a>"));
|
||||
|
||||
// Test with Unicode characters
|
||||
test = QStringLiteral("Click on https://foo@bar:www.kde.org/ÿöyo/dyne.html#a1 for info.");
|
||||
QCOMPARE(KStringHandler::tagUrls(test),
|
||||
QStringLiteral("Click on <a href=\"https://foo@bar:www.kde.org/ÿöyo/dyne.html#a1\">https://foo@bar:www.kde.org/ÿöyo/dyne.html#a1</a> for info."));
|
||||
}
|
||||
|
||||
void KStringHandlerTest::perlSplitTextSep()
|
||||
{
|
||||
QStringList expected;
|
||||
expected << QStringLiteral("some") << QStringLiteral("string") << QStringLiteral("for") << QStringLiteral("you__here");
|
||||
QCOMPARE(KStringHandler::perlSplit(QStringLiteral("__"), QStringLiteral("some__string__for__you__here"), 4), expected);
|
||||
|
||||
expected.clear();
|
||||
expected << QStringLiteral("kparts") << QStringLiteral("reaches") << QStringLiteral("the parts other parts can't");
|
||||
QCOMPARE(KStringHandler::perlSplit(QLatin1Char(' '), QStringLiteral("kparts reaches the parts other parts can't"), 3), expected);
|
||||
}
|
||||
|
||||
void KStringHandlerTest::perlSplitRegexSep()
|
||||
{
|
||||
QCOMPARE(KStringHandler::perlSplit(QRegularExpression(QStringLiteral("[! ]")), QStringLiteral("Split me up ! I'm bored ! OK ?"), 3),
|
||||
(QStringList{QStringLiteral("Split"), QStringLiteral("me"), QStringLiteral("up ! I'm bored ! OK ?")}));
|
||||
|
||||
QCOMPARE(KStringHandler::perlSplit(QRegularExpression(QStringLiteral("\\W")), QStringLiteral("aaa ggg cd ef"), 3),
|
||||
(QStringList{QStringLiteral("aaa"), QStringLiteral("ggg"), QStringLiteral("cd ef")}));
|
||||
|
||||
// Test with Unicode characters
|
||||
QCOMPARE(KStringHandler::perlSplit(QRegularExpression(QStringLiteral("\\W")), QStringLiteral("aaa gǵg cd ef"), 3),
|
||||
(QStringList{QStringLiteral("aaa"), QStringLiteral("gǵg"), QStringLiteral("cd ef")}));
|
||||
}
|
||||
|
||||
void KStringHandlerTest::obscure()
|
||||
{
|
||||
// See bug 167900, obscure() produced chars that could not properly be converted to and from
|
||||
// UTF8. The result was that storing passwords with '!' in them did not work.
|
||||
QString test = QStringLiteral("!TEST!");
|
||||
QString obscured = KStringHandler::obscure(test);
|
||||
QByteArray obscuredBytes = obscured.toUtf8();
|
||||
QCOMPARE(KStringHandler::obscure(QString::fromUtf8(obscuredBytes.constData())), test);
|
||||
}
|
||||
|
||||
// Zero-Width Space
|
||||
static const QChar ZWSP(0x200b);
|
||||
// Word Joiner
|
||||
static const QChar WJ(0x2060);
|
||||
|
||||
void KStringHandlerTest::preProcessWrap_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
// Should result in no additional breaks
|
||||
QTest::newRow("spaces") << "foo bar baz"
|
||||
<< "foo bar baz";
|
||||
|
||||
// Should insert a ZWSP after each '_'
|
||||
QTest::newRow("underscores") << "foo_bar_baz" << QString(QStringLiteral("foo_") + ZWSP + QStringLiteral("bar_") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP after each '-'
|
||||
QTest::newRow("hyphens") << "foo-bar-baz" << QString(QStringLiteral("foo-") + ZWSP + QStringLiteral("bar-") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP after each '.'
|
||||
QTest::newRow("periods") << "foo.bar.baz" << QString(QStringLiteral("foo.") + ZWSP + QStringLiteral("bar.") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP after each ','
|
||||
QTest::newRow("commas") << "foo,bar,baz" << QString(QStringLiteral("foo,") + ZWSP + QStringLiteral("bar,") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should result in no additional breaks since the '_'s are followed by spaces
|
||||
QTest::newRow("mixed underscores and spaces") << "foo_ bar_ baz"
|
||||
<< "foo_ bar_ baz";
|
||||
|
||||
// Should result in no additional breaks since the '_' is the last char
|
||||
QTest::newRow("ends with underscore") << "foo_"
|
||||
<< "foo_";
|
||||
|
||||
// Should insert a ZWSP before '(' and after ')'
|
||||
QTest::newRow("parens") << "foo(bar)baz" << QString(QStringLiteral("foo") + ZWSP + QStringLiteral("(bar)") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP before '[' and after ']'
|
||||
QTest::newRow("brackets") << "foo[bar]baz" << QString(QStringLiteral("foo") + ZWSP + QStringLiteral("[bar]") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP before '{' and after '}'
|
||||
QTest::newRow("curly braces") << "foo{bar}baz" << QString(QStringLiteral("foo") + ZWSP + QStringLiteral("{bar}") + ZWSP + QStringLiteral("baz"));
|
||||
|
||||
// Should insert a ZWSP before '(' but not after ')' since it's the last char
|
||||
QTest::newRow("ends with ')'") << "foo(bar)" << QString(QStringLiteral("foo") + ZWSP + QStringLiteral("(bar)"));
|
||||
|
||||
// Should insert a single ZWSP between the '_' and the '('
|
||||
QTest::newRow("'_' followed by '('") << "foo_(bar)" << QString(QStringLiteral("foo_") + ZWSP + QStringLiteral("(bar)"));
|
||||
|
||||
// Should insert ZWSP's between the '_' and the '[', between the double
|
||||
// '['s and the double ']'s, but not before and after 'bar'
|
||||
QTest::newRow("'_' before double brackets") << "foo_[[bar]]"
|
||||
<< QString(QStringLiteral("foo_") + ZWSP + QStringLiteral("[") + ZWSP + QStringLiteral("[bar]") + ZWSP
|
||||
+ QStringLiteral("]"));
|
||||
|
||||
// Should only insert ZWSP's between the double '['s and the double ']'s
|
||||
QTest::newRow("space before double brackets") << "foo [[bar]]"
|
||||
<< QString(QStringLiteral("foo [") + ZWSP + QStringLiteral("[bar]") + ZWSP + QStringLiteral("]"));
|
||||
|
||||
// Shouldn't result in any additional breaks since the '(' is preceded
|
||||
// by a space, and the ')' is followed by a space.
|
||||
QTest::newRow("parens with spaces") << "foo (bar) baz"
|
||||
<< "foo (bar) baz";
|
||||
|
||||
// Should insert a WJ (Word Joiner) before a single quote
|
||||
QTest::newRow("single quote") << "foo'bar" << QString(QStringLiteral("foo") + WJ + QStringLiteral("'bar"));
|
||||
|
||||
// Should insert a ZWSP between sub-words, but not before nor after the word
|
||||
QTest::newRow("camelCase") << "camelCase" << QString(QStringLiteral("camel") + ZWSP + QStringLiteral("Case"));
|
||||
|
||||
// Why limiting yourself to ASCII? More and more programming languages these days allow for Unicode identifiers.
|
||||
QTest::newRow("camelCase international") << "приветМир" << QString(QStringLiteral("привет") + ZWSP + QStringLiteral("Мир"));
|
||||
|
||||
// Should insert a ZWSP between sub-words, but not before first (upper case) letter
|
||||
QTest::newRow("PascalCase") << "PascalCase" << QString(QStringLiteral("Pascal") + ZWSP + QStringLiteral("Case"));
|
||||
|
||||
// But if a string already contains whitespaces, no need to insert ZWSPs
|
||||
QTest::newRow("camelCase with whitespaces") << "camelCase PascalCase"
|
||||
<< "camelCase PascalCase";
|
||||
|
||||
// However, single quote and other tricks should still work.
|
||||
QTest::newRow("single quote with whitespaces") << "foo(bar) PascalCase'camelCase"
|
||||
<< QString(QStringLiteral("foo") + ZWSP + QStringLiteral("(bar) PascalCase") + WJ
|
||||
+ QStringLiteral("'camelCase"));
|
||||
}
|
||||
|
||||
// Little helper function to make tests diagnostics more readable by humans
|
||||
static QString replaceZwsp(const QString &string)
|
||||
{
|
||||
const QString replacement = QStringLiteral("<ZWSP>");
|
||||
QString result;
|
||||
result.reserve(string.length() + string.count(ZWSP) * replacement.length());
|
||||
for (const auto i : string) {
|
||||
if (i == ZWSP) {
|
||||
result += replacement;
|
||||
} else {
|
||||
result += i;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void KStringHandlerTest::preProcessWrap()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QCOMPARE(replaceZwsp(KStringHandler::preProcessWrap(string)), replaceZwsp(expected));
|
||||
}
|
||||
|
||||
void KStringHandlerTest::logicalLength_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<int>("expected");
|
||||
|
||||
QTest::newRow("Latin") << "foo bar baz" << 11;
|
||||
QTest::newRow("Chinese") << QString::fromUtf8("\xe4\xbd\xa0\xe5\xa5\xbd") << 4;
|
||||
QTest::newRow("Japanese") << QString::fromUtf8("\xe9\x9d\x92\xe3\x81\x84\xe7\xa9\xba") << 6;
|
||||
QTest::newRow("Korean") << QString::fromUtf8("\xed\x95\x9c\xea\xb5\xad\xec\x96\xb4") << 6;
|
||||
QTest::newRow("Mixed") << QString::fromUtf8("KDE\xe6\xa1\x8c\xe9\x9d\xa2") << 7;
|
||||
}
|
||||
|
||||
void KStringHandlerTest::logicalLength()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(int, expected);
|
||||
QCOMPARE(KStringHandler::logicalLength(string), expected);
|
||||
}
|
||||
|
||||
void KStringHandlerTest::lsqueeze_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<int>("length");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("kde_is_awesome") << "KDE is awesome" << 11 << "... awesome";
|
||||
QTest::newRow("kde_is_really_awesome") << "KDE is really awesome" << 20 << "...is really awesome";
|
||||
QTest::newRow("kde_is_really_awesome_full") << "KDE is really awesome" << 30 << "KDE is really awesome";
|
||||
}
|
||||
|
||||
void KStringHandlerTest::lsqueeze()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(int, length);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QCOMPARE(KStringHandler::lsqueeze(string, length), expected);
|
||||
}
|
||||
|
||||
void KStringHandlerTest::csqueeze_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<int>("length");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("kde_is_awesome") << "KDE is awesome" << 11 << "KDE ...some";
|
||||
QTest::newRow("kde_is_really_awesome") << "KDE is really awesome" << 20 << "KDE is r... awesome";
|
||||
QTest::newRow("kde_is_really_awesome_full") << "KDE is really awesome" << 30 << "KDE is really awesome";
|
||||
}
|
||||
|
||||
void KStringHandlerTest::csqueeze()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(int, length);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QCOMPARE(KStringHandler::csqueeze(string, length), expected);
|
||||
}
|
||||
|
||||
void KStringHandlerTest::rsqueeze_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
QTest::addColumn<int>("length");
|
||||
QTest::addColumn<QString>("expected");
|
||||
|
||||
QTest::newRow("kde_is_awesome") << "KDE is awesome" << 11 << "KDE is a...";
|
||||
QTest::newRow("kde_is_really_awesome") << "KDE is really awesome" << 20 << "KDE is really awe...";
|
||||
QTest::newRow("kde_is_really_awesome_full") << "KDE is really awesome" << 30 << "KDE is really awesome";
|
||||
}
|
||||
|
||||
void KStringHandlerTest::rsqueeze()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFETCH(int, length);
|
||||
QFETCH(QString, expected);
|
||||
|
||||
QCOMPARE(KStringHandler::rsqueeze(string, length), expected);
|
||||
}
|
||||
|
||||
#include "moc_kstringhandlertest.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef KSTRINGHANDLERTEST_H
|
||||
#define KSTRINGHANDLERTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KStringHandlerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void capwords();
|
||||
void tagURLs();
|
||||
void perlSplitTextSep();
|
||||
void perlSplitRegexSep();
|
||||
void obscure();
|
||||
void preProcessWrap_data();
|
||||
void preProcessWrap();
|
||||
void logicalLength_data();
|
||||
void logicalLength();
|
||||
void lsqueeze();
|
||||
void lsqueeze_data();
|
||||
void csqueeze();
|
||||
void csqueeze_data();
|
||||
void rsqueeze();
|
||||
void rsqueeze_data();
|
||||
|
||||
private:
|
||||
static QString test;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2005 Ingo Kloecker <kloecker@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Allen Winter <winter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "../src/lib/text/ktexttohtml.h"
|
||||
#include "../src/lib/text/ktexttohtml_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <QUrl>
|
||||
|
||||
Q_DECLARE_METATYPE(KTextToHTML::Options)
|
||||
|
||||
class KTextToHTMLTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void benchHtmlConvert_data()
|
||||
{
|
||||
QTest::addColumn<QString>("text");
|
||||
QTest::addColumn<KTextToHTML::Options>("options");
|
||||
|
||||
auto text = QStringLiteral("foo bar asdf :)").repeated(1000);
|
||||
QTest::newRow("plain") << text << KTextToHTML::Options();
|
||||
QTest::newRow("preserve-spaces") << text << KTextToHTML::Options(KTextToHTML::PreserveSpaces);
|
||||
QTest::newRow("highlight-text") << text << KTextToHTML::Options(KTextToHTML::HighlightText);
|
||||
QTest::newRow("replace-smileys") << text << KTextToHTML::Options(KTextToHTML::ReplaceSmileys);
|
||||
QTest::newRow("preserve-spaces+highlight-text") << text << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText);
|
||||
QTest::newRow("preserve-spaces+highlight-text+replace-smileys")
|
||||
<< text << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText | KTextToHTML::ReplaceSmileys);
|
||||
}
|
||||
|
||||
void benchHtmlConvert()
|
||||
{
|
||||
QFETCH(QString, text);
|
||||
QFETCH(KTextToHTML::Options, options);
|
||||
|
||||
QBENCHMARK {
|
||||
const QString html = KTextToHTML::convertToHtml(text, options);
|
||||
Q_UNUSED(html);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(KTextToHTMLTest)
|
||||
|
||||
#include "ktexttohtmlbenchmarktest.moc"
|
||||
@@ -0,0 +1,537 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2005 Ingo Kloecker <kloecker@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Allen Winter <winter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "ktexttohtmltest.h"
|
||||
#include "kcoreaddons_debug.h"
|
||||
|
||||
#include "../src/lib/text/ktexttohtml.h"
|
||||
#include "../src/lib/text/ktexttohtml_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
#include <QUrl>
|
||||
|
||||
QTEST_MAIN(KTextToHTMLTest)
|
||||
|
||||
Q_DECLARE_METATYPE(KTextToHTML::Options)
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
void initLocale()
|
||||
{
|
||||
setenv("LC_ALL", "en_US.utf-8", 1);
|
||||
}
|
||||
Q_CONSTRUCTOR_FUNCTION(initLocale)
|
||||
#endif
|
||||
|
||||
void KTextToHTMLTest::testGetEmailAddress()
|
||||
{
|
||||
// empty input
|
||||
const QString emptyQString;
|
||||
KTextToHTMLHelper ll1(emptyQString, 0);
|
||||
QVERIFY(ll1.getEmailAddress().isEmpty());
|
||||
|
||||
// no '@' at scan position
|
||||
KTextToHTMLHelper ll2(QStringLiteral("foo@bar.baz"), 0);
|
||||
QVERIFY(ll2.getEmailAddress().isEmpty());
|
||||
|
||||
// '@' in local part
|
||||
KTextToHTMLHelper ll3(QStringLiteral("foo@bar@bar.baz"), 7);
|
||||
QVERIFY(ll3.getEmailAddress().isEmpty());
|
||||
|
||||
// empty local part
|
||||
KTextToHTMLHelper ll4(QStringLiteral("@bar.baz"), 0);
|
||||
QVERIFY(ll4.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper ll5(QStringLiteral(".@bar.baz"), 1);
|
||||
QVERIFY(ll5.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper ll6(QStringLiteral(" @bar.baz"), 1);
|
||||
QVERIFY(ll6.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper ll7(QStringLiteral(".!#$%&'*+-/=?^_`{|}~@bar.baz"), qstrlen(".!#$%&'*+-/=?^_`{|}~"));
|
||||
QVERIFY(ll7.getEmailAddress().isEmpty());
|
||||
|
||||
// allowed special chars in local part of address
|
||||
KTextToHTMLHelper ll8(QStringLiteral("a.!#$%&'*+-/=?^_`{|}~@bar.baz"), qstrlen("a.!#$%&'*+-/=?^_`{|}~"));
|
||||
QCOMPARE(ll8.getEmailAddress(), QStringLiteral("a.!#$%&'*+-/=?^_`{|}~@bar.baz"));
|
||||
|
||||
// '@' in domain part
|
||||
KTextToHTMLHelper ll9(QStringLiteral("foo@bar@bar.baz"), 3);
|
||||
QVERIFY(ll9.getEmailAddress().isEmpty());
|
||||
|
||||
// domain part without dot
|
||||
KTextToHTMLHelper lla(QStringLiteral("foo@bar"), 3);
|
||||
QVERIFY(lla.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper llb(QStringLiteral("foo@bar."), 3);
|
||||
QVERIFY(llb.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper llc(QStringLiteral(".foo@bar"), 4);
|
||||
QVERIFY(llc.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper lld(QStringLiteral("foo@bar "), 3);
|
||||
QVERIFY(lld.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper lle(QStringLiteral(" foo@bar"), 4);
|
||||
QVERIFY(lle.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper llf(QStringLiteral("foo@bar-bar"), 3);
|
||||
QVERIFY(llf.getEmailAddress().isEmpty());
|
||||
|
||||
// empty domain part
|
||||
KTextToHTMLHelper llg(QStringLiteral("foo@"), 3);
|
||||
QVERIFY(llg.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper llh(QStringLiteral("foo@."), 3);
|
||||
QVERIFY(llh.getEmailAddress().isEmpty());
|
||||
KTextToHTMLHelper lli(QStringLiteral("foo@-"), 3);
|
||||
QVERIFY(lli.getEmailAddress().isEmpty());
|
||||
|
||||
// simple address
|
||||
KTextToHTMLHelper llj(QStringLiteral("foo@bar.baz"), 3);
|
||||
QCOMPARE(llj.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper llk(QStringLiteral("foo@bar.baz."), 3);
|
||||
QCOMPARE(llk.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper lll(QStringLiteral(".foo@bar.baz"), 4);
|
||||
QCOMPARE(lll.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper llm(QStringLiteral("foo@bar.baz-"), 3);
|
||||
QCOMPARE(llm.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper lln(QStringLiteral("-foo@bar.baz"), 4);
|
||||
QCOMPARE(lln.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper llo(QStringLiteral("foo@bar.baz "), 3);
|
||||
QCOMPARE(llo.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper llp(QStringLiteral(" foo@bar.baz"), 4);
|
||||
QCOMPARE(llp.getEmailAddress(), QStringLiteral("foo@bar.baz"));
|
||||
KTextToHTMLHelper llq(QStringLiteral("foo@bar-bar.baz"), 3);
|
||||
QCOMPARE(llq.getEmailAddress(), QStringLiteral("foo@bar-bar.baz"));
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testGetUrl()
|
||||
{
|
||||
QStringList brackets;
|
||||
brackets << QString() << QString(); // no brackets
|
||||
brackets << QStringLiteral("<") << QStringLiteral(">");
|
||||
brackets << QStringLiteral("[") << QStringLiteral("]");
|
||||
brackets << QStringLiteral("\"") << QStringLiteral("\"");
|
||||
brackets << QStringLiteral("<link>") << QStringLiteral("</link>");
|
||||
|
||||
for (int i = 0; i < brackets.count(); i += 2) {
|
||||
testGetUrl2(brackets[i], brackets[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testGetUrl2(const QString &left, const QString &right)
|
||||
{
|
||||
QStringList schemas;
|
||||
schemas << QStringLiteral("http://");
|
||||
schemas << QStringLiteral("https://");
|
||||
schemas << QStringLiteral("vnc://");
|
||||
schemas << QStringLiteral("fish://");
|
||||
schemas << QStringLiteral("ftp://");
|
||||
schemas << QStringLiteral("ftps://");
|
||||
schemas << QStringLiteral("sftp://");
|
||||
schemas << QStringLiteral("smb://");
|
||||
schemas << QStringLiteral("file://");
|
||||
schemas << QStringLiteral("irc://");
|
||||
schemas << QStringLiteral("ircs://");
|
||||
|
||||
QStringList urls;
|
||||
urls << QStringLiteral("www.kde.org");
|
||||
urls << QStringLiteral("user@www.kde.org");
|
||||
urls << QStringLiteral("user:pass@www.kde.org");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path?a=1");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path?a=1#anchor");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/\npath \n /long/ path \t ?a=1#anchor");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path/special(123)?a=1#anchor");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor[bla");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor[bla]");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/\nsub/path:with:colon/\nspecial(123)?\na=1#anchor[bla]");
|
||||
urls << QStringLiteral("user:pass@www.kde.org:1234/ \n sub/path:with:colon/ \n\t \t special(123)?") + QStringLiteral("\n\t \n\t a=1#anchor[bla]");
|
||||
urls << QStringLiteral("en.wikipedia.org/wiki/%C3%98_(disambiguation)");
|
||||
|
||||
for (const QString &schema : std::as_const(schemas)) {
|
||||
for (QString url : std::as_const(urls)) {
|
||||
// by definition: if the URL is enclosed in brackets, the URL itself is not allowed
|
||||
// to contain the closing bracket, as this would be detected as the end of the URL
|
||||
if ((left.length() == 1) && (url.contains(right[0]))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the url contains a whitespace, it must be enclosed with brackets
|
||||
if ((url.contains(QLatin1Char('\n')) || url.contains(QLatin1Char('\t')) || url.contains(QLatin1Char(' '))) && left.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString test(left + schema + url + right);
|
||||
KTextToHTMLHelper ll(test, left.length());
|
||||
QString gotUrl = ll.getUrl();
|
||||
|
||||
// we want to have the url without whitespace
|
||||
url.remove(QLatin1Char(' '));
|
||||
url.remove(QLatin1Char('\n'));
|
||||
url.remove(QLatin1Char('\t'));
|
||||
|
||||
bool ok = (gotUrl == (schema + url));
|
||||
if (!ok) {
|
||||
qCDebug(KCOREADDONS_DEBUG) << "got:" << gotUrl;
|
||||
}
|
||||
QVERIFY2(ok, qPrintable(test));
|
||||
}
|
||||
}
|
||||
|
||||
QStringList urlsWithoutSchema;
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path?a=1");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path?a=1#anchor");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path/special(123)?a=1#anchor");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor[bla");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/sub/path:with:colon/special(123)?a=1#anchor[bla]");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/\nsub/path:with:colon/\nspecial(123)?\na=1#anchor[bla]");
|
||||
urlsWithoutSchema << QStringLiteral(".kde.org:1234/ \n sub/path:with:colon/ \n\t \t special(123)?") + QStringLiteral("\n\t \n\t a=1#anchor[bla]");
|
||||
|
||||
QStringList starts;
|
||||
starts << QStringLiteral("www") << QStringLiteral("ftp") << QStringLiteral("news:www");
|
||||
|
||||
for (const QString &start : std::as_const(starts)) {
|
||||
for (QString url : std::as_const(urlsWithoutSchema)) {
|
||||
// by definition: if the URL is enclosed in brackets, the URL itself is not allowed
|
||||
// to contain the closing bracket, as this would be detected as the end of the URL
|
||||
if ((left.length() == 1) && (url.contains(right[0]))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the url contains a whitespace, it must be enclosed with brackets
|
||||
if ((url.contains(QLatin1Char('\n')) || url.contains(QLatin1Char('\t')) || url.contains(QLatin1Char(' '))) && left.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString test(left + start + url + right);
|
||||
KTextToHTMLHelper ll(test, left.length());
|
||||
QString gotUrl = ll.getUrl();
|
||||
|
||||
// we want to have the url without whitespace
|
||||
url.remove(QLatin1Char(' '));
|
||||
url.remove(QLatin1Char('\n'));
|
||||
url.remove(QLatin1Char('\t'));
|
||||
|
||||
bool ok = (gotUrl == (start + url));
|
||||
if (!ok) {
|
||||
qCDebug(KCOREADDONS_DEBUG) << "got:" << gotUrl;
|
||||
}
|
||||
QVERIFY2(ok, qPrintable(gotUrl));
|
||||
}
|
||||
}
|
||||
|
||||
// test max url length
|
||||
QString url = QStringLiteral("https://www.kde.org/this/is/a_very_loooooong_url/test/test/test");
|
||||
{
|
||||
KTextToHTMLHelper ll(url, 0, 10);
|
||||
QVERIFY(ll.getUrl().isEmpty()); // url too long
|
||||
}
|
||||
{
|
||||
KTextToHTMLHelper ll(url, 0, url.length() - 1);
|
||||
QVERIFY(ll.getUrl().isEmpty()); // url too long
|
||||
}
|
||||
{
|
||||
KTextToHTMLHelper ll(url, 0, url.length());
|
||||
QCOMPARE(ll.getUrl(), url);
|
||||
}
|
||||
{
|
||||
KTextToHTMLHelper ll(url, 0, url.length() + 1);
|
||||
QCOMPARE(ll.getUrl(), url);
|
||||
}
|
||||
|
||||
// mailto
|
||||
{
|
||||
QString addr = QStringLiteral("mailto:test@kde.org");
|
||||
QString test(left + addr + right);
|
||||
KTextToHTMLHelper ll(test, left.length());
|
||||
|
||||
QString gotUrl = ll.getUrl();
|
||||
|
||||
bool ok = (gotUrl == addr);
|
||||
if (!ok) {
|
||||
qCDebug(KCOREADDONS_DEBUG) << "got:" << gotUrl;
|
||||
}
|
||||
QVERIFY2(ok, qPrintable(gotUrl));
|
||||
}
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testHtmlConvert_data()
|
||||
{
|
||||
QTest::addColumn<QString>("plainText");
|
||||
QTest::addColumn<KTextToHTML::Options>("flags");
|
||||
QTest::addColumn<QString>("htmlText");
|
||||
|
||||
// Linker error when using PreserveSpaces, therefore the hardcoded 0x01 or 0x09
|
||||
|
||||
// Test preserving whitespace correctly
|
||||
QTest::newRow("") << " foo" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << " foo";
|
||||
QTest::newRow("") << " foo" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << " foo";
|
||||
QTest::newRow("") << " foo " << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << " foo ";
|
||||
QTest::newRow("") << " foo " << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << " foo ";
|
||||
QTest::newRow("") << "bla bla bla bla bla" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "bla bla bla bla bla";
|
||||
QTest::newRow("") << "bla bla bla \n bla bla bla " << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "bla bla bla <br />\n bla bla bla ";
|
||||
QTest::newRow("") << "bla bla bla" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "bla bla bla";
|
||||
QTest::newRow("") << " bla bla \n bla bla a\n bla bla " << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< " bla bla <br />\n bla bla a<br />\n"
|
||||
" bla bla ";
|
||||
|
||||
// Test highlighting with *, / and _
|
||||
QTest::newRow("") << "Ce paragraphe _contient_ des mots ou des _groupes de mots_ à mettre en"
|
||||
" forme…"
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "Ce paragraphe <u>_contient_</u> des mots ou des"
|
||||
" <u>_groupes de mots_</u> à mettre en forme…";
|
||||
QTest::newRow("punctation-bug") << "Ce texte *a l'air* de _fonctionner_, à condition"
|
||||
" d’utiliser le guillemet ASCII."
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "Ce texte <b>*a l'air*</b> de <u>_fonctionner_</u>, à"
|
||||
" condition d’utiliser le guillemet ASCII.";
|
||||
QTest::newRow("punctation-bug") << "Un répertoire /est/ un *dossier* où on peut mettre des"
|
||||
" *fichiers*."
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "Un répertoire <i>/est/</i> un"
|
||||
" <b>*dossier*</b> où on peut mettre des <b>*fichiers*</b>.";
|
||||
QTest::newRow("punctation-bug") << "*BLA BLA BLA BLA*." << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "<b>BLA BLA BLA BLA</b>.";
|
||||
QTest::newRow("") << "Je vais tenter de repérer des faux positif*" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "Je vais tenter de repérer des faux positif*";
|
||||
QTest::newRow("") << "*Ouais !* *Yes!*" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "<b>*Ouais !*</b> <b>*Yes!*</b>";
|
||||
|
||||
QTest::newRow("multispace") << "*Ouais foo*" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "<b>*Ouais foo*</b>";
|
||||
|
||||
QTest::newRow("multispace3") << "*Ouais: foo*" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "<b>*Ouais: foo*</b>";
|
||||
|
||||
QTest::newRow("multi-") << "** Ouais: foo **" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "** Ouais: foo **";
|
||||
|
||||
QTest::newRow("multi-") << "*** Ouais: foo ***" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "*** Ouais: foo ***";
|
||||
|
||||
QTest::newRow("nohtmlversion") << "* Ouais: foo *" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "* Ouais: foo *";
|
||||
|
||||
QTest::newRow("nohtmlversion2") << "*Ouais: foo *" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "*Ouais: foo *";
|
||||
|
||||
QTest::newRow("nohtmlversion3") << "* Ouais: foo*" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "* Ouais: foo*";
|
||||
|
||||
QTest::newRow("nohtmlversion3") << "* Ouais: *ff sfsdf* foo *" << KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "* Ouais: <b>*ff sfsdf*</b> foo *";
|
||||
|
||||
QTest::newRow("") << "the /etc/{rsyslog.d,syslog-ng.d}/package.rpmnew file"
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces | KTextToHTML::HighlightText)
|
||||
<< "the /etc/{rsyslog.d,syslog-ng.d}/package.rpmnew file";
|
||||
|
||||
// This test has problems with the encoding, apparently.
|
||||
// QTest::newRow( "" ) << "*Ça fait plaisir de pouvoir utiliser des lettres accentuées dans du"
|
||||
// " texte mis en forme*." << 0x09 << "<b>Ça fait plaisir de pouvoir"
|
||||
// " utiliser des lettres accentuées dans du texte mis en forme</b>.";
|
||||
|
||||
// Bug reported by dfaure, the <hostname> would get lost
|
||||
QTest::newRow("") << "QUrl url(\"http://strange<hostname>/\");" << KTextToHTML::Options(KTextToHTML::ReplaceSmileys | KTextToHTML::HighlightText)
|
||||
<< "QUrl url("<a href=\"http://strange<hostname>/\">"
|
||||
"http://strange<hostname>/</a>");";
|
||||
|
||||
// Bug: 211128 - plain text emails should not replace ampersand & with &
|
||||
QTest::newRow("bug211128") << "https://green-site/?Ticket=85&Page=next" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"https://green-site/?Ticket=85&Page=next\">"
|
||||
"https://green-site/?Ticket=85&Page=next</a>";
|
||||
|
||||
QTest::newRow("dotBeforeEnd") << "Look at this file: www.example.com/example.h" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "Look at this file: <a href=\"http://www.example.com/example.h\">"
|
||||
"www.example.com/example.h</a>";
|
||||
QTest::newRow("dotInMiddle") << "Look at this file: www.example.com/.bashrc" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "Look at this file: <a href=\"http://www.example.com/.bashrc\">"
|
||||
"www.example.com/.bashrc</a>";
|
||||
|
||||
// A dot at the end of an URL is explicitly ignored
|
||||
QTest::newRow("dotAtEnd") << "Look at this file: www.example.com/test.cpp." << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "Look at this file: <a href=\"http://www.example.com/test.cpp\">"
|
||||
"www.example.com/test.cpp</a>.";
|
||||
|
||||
// Bug 313719 - URL in parenthesis
|
||||
QTest::newRow("url-in-parenthesis-1") << "KDE (website https://www.kde.org)" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "KDE (website <a href=\"https://www.kde.org\">https://www.kde.org</a>)";
|
||||
QTest::newRow("url-in-parenthesis-2") << "KDE website (https://www.kde.org)" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "KDE website (<a href=\"https://www.kde.org\">https://www.kde.org</a>)";
|
||||
QTest::newRow("url-in-parenthesis-3") << "bla (https://www.kde.org - section 5.2)" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "bla (<a href=\"https://www.kde.org\">https://www.kde.org</a> - section 5.2)";
|
||||
|
||||
// Fix url as foo <<url> <url>> when we concatened them.
|
||||
QTest::newRow("url-with-url")
|
||||
<< "foo <https://www.kde.org/ <https://www.kde.org/>>" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "foo <<a href=\"https://www.kde.org/ \">https://www.kde.org/ </a><<a href=\"https://www.kde.org/\">https://www.kde.org/</a>>>";
|
||||
|
||||
// Fix url exploit
|
||||
QTest::newRow("url-exec-html") << "https://\"><!--" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "https://"><!--";
|
||||
|
||||
QTest::newRow("url-exec-html-2") << "https://192.168.1.1:\"><!--" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "https://192.168.1.1:"><!--";
|
||||
|
||||
QTest::newRow("url-exec-html-3") << "https://<IP>:\"><!--" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "https://<IP>:"><!--";
|
||||
|
||||
QTest::newRow("url-exec-html-4") << "https://<IP>:/\"><!--" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "https://<IP>:/"><!--";
|
||||
|
||||
QTest::newRow("url-exec-html-5") << "https://<IP>:/\"><script>alert(1);</script><!--" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "https://<IP>:/"><script>alert(1);</script><!--";
|
||||
|
||||
QTest::newRow("url-exec-html-6") << "https://<IP>:/\"><script>alert(1);</script><!--\nTest2" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "https://<IP>:/"><script>alert(1);</script><!--\nTest2";
|
||||
|
||||
QTest::newRow("url-with-ref-in-[") << "https://www.kde.org[1]" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"https://www.kde.org\">https://www.kde.org</a>[1]";
|
||||
|
||||
QTest::newRow("url-with-ref-in-[2") << "[http://www.example.org/][whatever]" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "[<a href=\"http://www.example.org/\">http://www.example.org/</a>][whatever]";
|
||||
// Bug 346132
|
||||
QTest::newRow("url-with-ref-in-<") << "http://www.foo.bar<http://foo.bar/>" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"http://www.foo.bar\">http://www.foo.bar</a><<a href=\"http://foo.bar/\">http://foo.bar/</a>>";
|
||||
|
||||
QTest::newRow("url-with-ref-in-]") << "[Please visit our booth 24-25 http://example.com/]" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "[Please visit our booth 24-25 <a href=\"http://example.com/\">http://example.com/</a>]";
|
||||
|
||||
QTest::newRow("two url with space") << "http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"http://www.kde.org/standards/kcfg/1.0\">http://www.kde.org/standards/kcfg/1.0</a> <a "
|
||||
"href=\"http://www.kde.org/\">http://www.kde.org/</a>";
|
||||
|
||||
// Bug kmail
|
||||
QTest::newRow("two url with space-2")
|
||||
<< "@@ -55,6 +55,10 @@ xsi:schemaLocation=\"http://www.kde.org/standards/kcfg/1.0 http://www.kde.org/"
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "@@ -55,6 +55,10 @@ xsi:schemaLocation="<a href=\"http://www.kde.org/standards/kcfg/1.0\">http://www.kde.org/standards/kcfg/1.0</a> <a "
|
||||
"href=\"http://www.kde.org/\">http://www.kde.org/</a>";
|
||||
|
||||
const auto opt = KTextToHTML::PreserveSpaces | KTextToHTML::ConvertPhoneNumbers;
|
||||
// tel: urls
|
||||
QTest::newRow("tel url compact") << "bla bla <tel:+491234567890> bla bla" << opt
|
||||
<< "bla bla <<a href=\"tel:+491234567890\">tel:+491234567890</a>> bla bla";
|
||||
QTest::newRow("tel url fancy") << "bla bla tel:+49-321-123456 bla bla" << opt << "bla bla <a href=\"tel:+49-321-123456\">tel:+49-321-123456</a> bla bla";
|
||||
|
||||
// negative tel: url tests
|
||||
QTest::newRow("empty tel url") << "bla tel: blub" << opt << "bla tel: blub";
|
||||
|
||||
// phone numbers
|
||||
QTest::newRow("tel compact international") << "call +49123456789, then hang up" << opt
|
||||
<< "call <a href=\"tel:+49123456789\">+49123456789</a>, then hang up";
|
||||
QTest::newRow("tel parenthesis/spaces international")
|
||||
<< "phone:+33 (01) 12 34 56 78 blub" << opt << "phone:<a href=\"tel:+330112345678\">+33 (01) 12 34 56 78</a> blub";
|
||||
QTest::newRow("tel dashes international") << "bla +44-321-1-234-567" << opt << "bla <a href=\"tel:+443211234567\">+44-321-1-234-567</a>";
|
||||
QTest::newRow("tel dashes/spaces international") << "+1 123-456-7000 blub" << opt << "<a href=\"tel:+11234567000\">+1 123-456-7000</a> blub";
|
||||
QTest::newRow("tel spaces international") << "bla +32 1 234 5678 blub" << opt << "bla <a href=\"tel:+3212345678\">+32 1 234 5678</a> blub";
|
||||
QTest::newRow("tel slash domestic") << "bla 030/12345678 blub" << opt << "bla <a href=\"tel:03012345678\">030/12345678</a> blub";
|
||||
QTest::newRow("tel slash/space domestic") << "Tel.: 089 / 12 34 56 78" << opt << "Tel.: <a href=\"tel:08912345678\">089 / 12 34 56 78</a>";
|
||||
QTest::newRow("tel follow by parenthesis") << "Telefon: 0 18 05 / 12 23 46 (14 Cent/Min.*)" << opt
|
||||
<< "Telefon: <a href=\"tel:01805122346\">0 18 05 / 12 23 46</a> (14 Cent/Min.*)";
|
||||
QTest::newRow("tel space single digit at end") << "0123/123 456 7" << opt << "<a href=\"tel:01231234567\">0123/123 456 7</a>";
|
||||
QTest::newRow("tel space around dash") << "bla +49 (0) 12 23 - 45 6000 blub" << opt
|
||||
<< "bla <a href=\"tel:+4901223456000\">+49 (0) 12 23 - 45 6000</a> blub";
|
||||
QTest::newRow("tel two numbers speparated by dash")
|
||||
<< "bla +49 (0) 12 23 46 78 - +49 0123/123 456 78 blub" << opt
|
||||
<< "bla <a href=\"tel:+49012234678\">+49 (0) 12 23 46 78</a> - <a href=\"tel:+49012312345678\">+49 0123/123 456 78</a> blub";
|
||||
|
||||
// negative tests for phone numbers
|
||||
QTest::newRow("non-tel number") << "please send 1200 cakes" << opt << "please send 1200 cakes";
|
||||
QTest::newRow("non-tel alpha-numeric") << "bla 1-123-456-ABCD blub" << opt << "bla 1-123-456-ABCD blub";
|
||||
QTest::newRow("non-tel alpha prefix") << "ABCD0123-456-789" << opt << "ABCD0123-456-789";
|
||||
QTest::newRow("non-tel date") << "bla 02/03/2019 blub" << opt << "bla 02/03/2019 blub";
|
||||
QTest::newRow("non-tel too long") << "bla +012-4567890123456 blub" << opt << "bla +012-4567890123456 blub";
|
||||
QTest::newRow("non-tel unbalanced") << "bla +012-456789(01 blub" << opt << "bla +012-456789(01 blub";
|
||||
QTest::newRow("non-tel nested") << "bla +012-4(56(78)90)1 blub" << opt << "bla +012-4(56(78)90)1 blub";
|
||||
QTest::newRow("tel extraction disabled") << "call +49123456789 now" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "call +49123456789 now";
|
||||
|
||||
QTest::newRow("bug-414360")
|
||||
<< "https://www.openstreetmap.org/directions?engine=graphhopper_foot&route=44.85765%2C-0.55931%3B44.85713%2C-0.56117#map=18/44.85756/-0.56094"
|
||||
<< KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a "
|
||||
"href=\"https://www.openstreetmap.org/directions?engine=graphhopper_foot&route=44.85765%2C-0.55931%3B44.85713%2C-0.56117#map=18/44.85756/"
|
||||
"-0.56094\">https://www.openstreetmap.org/directions?engine=graphhopper_foot&route=44.85765%2C-0.55931%3B44.85713%2C-0.56117#map=18/44.85756/"
|
||||
"-0.56094</a>";
|
||||
|
||||
// xmpp bug 422291
|
||||
QTest::newRow("xmpp1") << "xmpp:username@server.tld" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"xmpp:username@server.tld\">xmpp:username@server.tld</a>";
|
||||
QTest::newRow("xmpp2") << "xmpp:conversations@conference.siacs.eu" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"xmpp:conversations@conference.siacs.eu\">xmpp:conversations@conference.siacs.eu</a>";
|
||||
QTest::newRow("xmpp3") << "xmpp:conversations@conference.siacs.eu?join" << KTextToHTML::Options(KTextToHTML::PreserveSpaces)
|
||||
<< "<a href=\"xmpp:conversations@conference.siacs.eu?join\">xmpp:conversations@conference.siacs.eu?join</a>";
|
||||
|
||||
// Test news: only
|
||||
QTest::newRow("news") << "news: " << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "news: ";
|
||||
|
||||
QTest::newRow("ftp") << "ftp: " << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "ftp: ";
|
||||
QTest::newRow("mailto") << "mailto: " << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "mailto: ";
|
||||
QTest::newRow("empty") << "" << KTextToHTML::Options(KTextToHTML::PreserveSpaces) << "";
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testHtmlConvert()
|
||||
{
|
||||
QFETCH(QString, plainText);
|
||||
QFETCH(KTextToHTML::Options, flags);
|
||||
QFETCH(QString, htmlText);
|
||||
|
||||
QEXPECT_FAIL("punctation-bug", "Linklocator does not properly detect punctation as boundaries", Continue);
|
||||
|
||||
const QString actualHtml = KTextToHTML::convertToHtml(plainText, flags);
|
||||
QCOMPARE(actualHtml, htmlText);
|
||||
}
|
||||
|
||||
#define s(x) QStringLiteral(x)
|
||||
|
||||
void KTextToHTMLTest::testEmoticons_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("output");
|
||||
QTest::newRow("empty") << QString() << QString();
|
||||
QTest::newRow("trailing") << s("Hello :-)") << s("Hello 🙂");
|
||||
QTest::newRow("embedded") << s("Hello :-) How are you?") << s("Hello 🙂 How are you?");
|
||||
QTest::newRow("leading") << s(":-( Bye") << s("🙁 Bye");
|
||||
QTest::newRow("embedded-html") << s("<b>:(</b>") << s("<b>:(</b>");
|
||||
QTest::newRow("html-attribute") << s("<img src=\"...\" title=\":-)\" />") << s("<img src="..." title=":-)" />");
|
||||
QTest::newRow("broken-1") << s(":))") << s("😆");
|
||||
QTest::newRow("broken-4") << s(":D and :-D are not the same as :d and :-d") << s("😀 and 😀 are not the same as :d and :-d");
|
||||
QTest::newRow("broken-5") << s("4d:D>:)F:/>:-(:Pu:d9") << s("4d:D>:)F:/&gt;:-(:Pu:d9");
|
||||
QTest::newRow("broken-6") << s("<::pvar:: test=1>") << s("&lt;::pvar:: test=1&gt;");
|
||||
QTest::newRow("working-5") << s("(&)") << s("(&amp;)");
|
||||
QTest::newRow("working-6") << s("Bla ( )") << s("Bla (&nbsp;)");
|
||||
QTest::newRow("working-7") << s("a non-breaking space ( ) character") << s("a non-breaking space (&nbsp;) character");
|
||||
|
||||
QTest::newRow("angle-bracket-1") << s(">:)") << s("😈");
|
||||
QTest::newRow("angle-bracket-2") << s("<b>:)") << s("<b>:)");
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testEmoticons()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, output);
|
||||
QCOMPARE(KTextToHTML::convertToHtml(input, KTextToHTML::ReplaceSmileys | KTextToHTML::IgnoreUrls), output);
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testEmoticonsNoReplace_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::newRow("empty") << QString();
|
||||
QTest::newRow("no-space-spearator") << s("Very happy! :-):-)");
|
||||
QTest::newRow("broken-2") << s("In a sentence:practical example");
|
||||
QTest::newRow("broken-8") << s("-+-[-:-(-:-)-:-]-+-");
|
||||
QTest::newRow("broken-9") << s("::shrugs::");
|
||||
QTest::newRow("broken-10") << s(":Ptesting:P");
|
||||
QTest::newRow("working-1") << s(":):)");
|
||||
QTest::newRow("working-4") << s("http://www.kde.org");
|
||||
QTest::newRow("working-3") << s("End of sentence:p");
|
||||
QTest::newRow("xmpp-1") << s("an xmpp emoticon (%)");
|
||||
}
|
||||
|
||||
void KTextToHTMLTest::testEmoticonsNoReplace()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QCOMPARE(KTextToHTML::convertToHtml(input, KTextToHTML::ReplaceSmileys | KTextToHTML::IgnoreUrls), input);
|
||||
}
|
||||
|
||||
#include "moc_ktexttohtmltest.cpp"
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2007 Allen Winter <winter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KTEXTTOHTMLTEST_H
|
||||
#define KTEXTTOHTMLTEST_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class KTextToHTMLTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testGetEmailAddress();
|
||||
void testGetUrl();
|
||||
void testHtmlConvert();
|
||||
void testHtmlConvert_data();
|
||||
void testEmoticons_data();
|
||||
void testEmoticons();
|
||||
void testEmoticonsNoReplace_data();
|
||||
void testEmoticonsNoReplace();
|
||||
|
||||
private:
|
||||
void testGetUrl2(const QString &left, const QString &right);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kurlmimedatatest.h"
|
||||
#include <QMimeData>
|
||||
#include <QTest>
|
||||
#include <kurlmimedata.h>
|
||||
#include <kurlmimedata_p.h>
|
||||
#include <memory>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QTEST_MAIN(KUrlMimeDataTest)
|
||||
|
||||
void KUrlMimeDataTest::testURLList()
|
||||
{
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
QVERIFY(!mimeData->hasUrls());
|
||||
|
||||
QList<QUrl> urls;
|
||||
urls.append(QUrl(QLatin1String("https://www.kde.org")));
|
||||
urls.append(QUrl(QLatin1String("http://wstephenson:secret@example.com/path")));
|
||||
urls.append(QUrl(QLatin1String("file:///home/dfaure/konqtests/Mat%C3%A9riel")));
|
||||
QMap<QString, QString> metaData;
|
||||
metaData[QLatin1String("key")] = QLatin1String("value");
|
||||
metaData[QLatin1String("key2")] = QLatin1String("value2");
|
||||
|
||||
KUrlMimeData::setUrls(QList<QUrl>(), urls, mimeData);
|
||||
KUrlMimeData::setMetaData(metaData, mimeData);
|
||||
|
||||
QVERIFY(mimeData->hasUrls());
|
||||
QVERIFY(mimeData->hasText());
|
||||
|
||||
QMap<QString, QString> decodedMetaData;
|
||||
QList<QUrl> decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData, KUrlMimeData::PreferKdeUrls, &decodedMetaData);
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
QList<QUrl> expectedUrls = urls;
|
||||
expectedUrls[1] = QUrl(QLatin1String("http://wstephenson:secret@example.com/path")); // password kept, unlike in KDE4, but that's okay, it's not displayed
|
||||
QCOMPARE(expectedUrls, decodedURLs);
|
||||
|
||||
const QList<QUrl> qurls = mimeData->urls();
|
||||
QCOMPARE(qurls.count(), urls.count());
|
||||
for (int i = 0; i < qurls.count(); ++i) {
|
||||
QCOMPARE(qurls[i], decodedURLs[i]);
|
||||
}
|
||||
|
||||
QVERIFY(!decodedMetaData.isEmpty());
|
||||
QCOMPARE(decodedMetaData[QLatin1String("key")], QString::fromLatin1("value"));
|
||||
QCOMPARE(decodedMetaData[QLatin1String("key2")], QString::fromLatin1("value2"));
|
||||
|
||||
delete mimeData;
|
||||
}
|
||||
|
||||
void KUrlMimeDataTest::testOneURL()
|
||||
{
|
||||
QUrl oneURL(QLatin1String("file:///tmp"));
|
||||
QList<QUrl> oneEltList;
|
||||
oneEltList.append(oneURL);
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
|
||||
KUrlMimeData::setUrls(QList<QUrl>(), oneEltList, mimeData);
|
||||
QVERIFY(mimeData->hasUrls());
|
||||
|
||||
QMap<QString, QString> decodedMetaData;
|
||||
QList<QUrl> decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData, KUrlMimeData::PreferKdeUrls, &decodedMetaData);
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
QCOMPARE(decodedURLs.count(), 1);
|
||||
QCOMPARE(decodedURLs[0], oneURL);
|
||||
QVERIFY(decodedMetaData.isEmpty());
|
||||
delete mimeData;
|
||||
}
|
||||
|
||||
void KUrlMimeDataTest::testFromQUrl()
|
||||
{
|
||||
QList<QUrl> qurls;
|
||||
qurls.append(QUrl(QLatin1String("https://www.kde.org")));
|
||||
qurls.append(QUrl(QLatin1String("file:///home/dfaure/konqtests/Mat%C3%A9riel")));
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
KUrlMimeData::setUrls(QList<QUrl>(), qurls, mimeData);
|
||||
QVERIFY(mimeData->hasUrls());
|
||||
|
||||
QMap<QString, QString> decodedMetaData;
|
||||
QList<QUrl> decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData, KUrlMimeData::PreferKdeUrls, &decodedMetaData);
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
QCOMPARE(decodedURLs.count(), 2);
|
||||
QCOMPARE(decodedURLs[0], qurls[0]);
|
||||
QCOMPARE(decodedURLs[1], qurls[1]);
|
||||
QVERIFY(decodedMetaData.isEmpty());
|
||||
delete mimeData;
|
||||
}
|
||||
|
||||
void KUrlMimeDataTest::testMostLocalUrlList_data()
|
||||
{
|
||||
QTest::addColumn<bool>("withKdeUrls");
|
||||
QTest::addColumn<bool>("withLocalUrls");
|
||||
QTest::addColumn<bool>("expectedLocalUrls");
|
||||
|
||||
QTest::newRow("both") << true << true << false;
|
||||
QTest::newRow("local_only") << false << true << true;
|
||||
QTest::newRow("kde_only") << true << false << false;
|
||||
}
|
||||
|
||||
void KUrlMimeDataTest::testMostLocalUrlList()
|
||||
{
|
||||
QFETCH(bool, withKdeUrls);
|
||||
QFETCH(bool, withLocalUrls);
|
||||
QFETCH(bool, expectedLocalUrls);
|
||||
|
||||
QMimeData *mimeData = new QMimeData();
|
||||
QList<QUrl> urls;
|
||||
urls.append(QUrl(QLatin1String("desktop:/foo")));
|
||||
urls.append(QUrl(QLatin1String("desktop:/bar")));
|
||||
QList<QUrl> localUrls;
|
||||
localUrls.append(QUrl(QLatin1String("file:/home/dfaure/Desktop/foo")));
|
||||
localUrls.append(QUrl(QLatin1String("file:/home/dfaure/Desktop/bar")));
|
||||
|
||||
if (withKdeUrls && withLocalUrls) {
|
||||
KUrlMimeData::setUrls(urls, localUrls, mimeData);
|
||||
} else if (withKdeUrls) {
|
||||
KUrlMimeData::setUrls(urls, {}, mimeData);
|
||||
} else if (withLocalUrls) {
|
||||
KUrlMimeData::setUrls({}, localUrls, mimeData);
|
||||
}
|
||||
|
||||
QVERIFY(mimeData->hasUrls());
|
||||
QVERIFY(mimeData->hasText());
|
||||
// The support for urls is done in hasText, a direct call to hasFormat will say false.
|
||||
// QVERIFY(mimeData->hasFormat(QLatin1String("text/plain")));
|
||||
|
||||
// urlsFromMimeData decodes the real "kde" urls by default, if any
|
||||
QList<QUrl> decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData);
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
if (expectedLocalUrls) {
|
||||
QCOMPARE(decodedURLs, localUrls);
|
||||
} else {
|
||||
QCOMPARE(decodedURLs, urls);
|
||||
}
|
||||
|
||||
// urlsFromMimeData can also be told to decode the "most local" urls
|
||||
decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData, KUrlMimeData::PreferLocalUrls);
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
if (withLocalUrls) {
|
||||
QCOMPARE(decodedURLs, localUrls);
|
||||
} else {
|
||||
QCOMPARE(decodedURLs, urls);
|
||||
}
|
||||
|
||||
// QMimeData decodes the "most local" urls
|
||||
const QList<QUrl> qurls = mimeData->urls();
|
||||
if (withLocalUrls) {
|
||||
QCOMPARE(qurls.count(), localUrls.count());
|
||||
for (int i = 0; i < qurls.count(); ++i) {
|
||||
QCOMPARE(qurls[i], static_cast<QUrl>(localUrls[i]));
|
||||
}
|
||||
} else {
|
||||
QCOMPARE(qurls.count(), 0);
|
||||
}
|
||||
|
||||
delete mimeData;
|
||||
}
|
||||
|
||||
#if HAVE_QTDBUS
|
||||
|
||||
void KUrlMimeDataTest::testSameProcessCopyPaste()
|
||||
{
|
||||
if (!KUrlMimeData::isDocumentsPortalAvailable()) {
|
||||
QSKIP("Documents portal not available");
|
||||
}
|
||||
|
||||
std::unique_ptr<QMimeData> mimeData(new QMimeData());
|
||||
QList<QUrl> urls{QUrl(QStringLiteral("desktop:/foo")), QUrl(QStringLiteral("desktop:/bar"))};
|
||||
|
||||
KUrlMimeData::setUrls(urls, {}, mimeData.get());
|
||||
mimeData->setData(QStringLiteral("application/vnd.portal.filetransfer"), QFile::encodeName(QStringLiteral("bad-transfer-id")));
|
||||
|
||||
// Should fail because the transfer id is invalid
|
||||
QList<QUrl> decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData.get());
|
||||
QVERIFY(decodedURLs.isEmpty());
|
||||
|
||||
// Should succeed because we put in the source id, so the portal is skipped
|
||||
KUrlMimeData::setSourceId(mimeData.get());
|
||||
decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData.get());
|
||||
QVERIFY(!decodedURLs.isEmpty());
|
||||
QCOMPARE(decodedURLs, urls);
|
||||
|
||||
// Should fail because we put in a bad source id, so the portal is not skipped
|
||||
mimeData->setData(QStringLiteral("application/x-kde-source-id"), QStringLiteral("bad-source-id").toUtf8());
|
||||
decodedURLs = KUrlMimeData::urlsFromMimeData(mimeData.get());
|
||||
QVERIFY(decodedURLs.isEmpty());
|
||||
}
|
||||
|
||||
#endif // HAVE_QTDBUS
|
||||
|
||||
void KUrlMimeDataTest::initTestCase()
|
||||
{
|
||||
#if HAVE_QTDBUS
|
||||
qputenv("KCOREADDONS_FORCE_DOCUMENTS_PORTAL", "1"_ba);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "moc_kurlmimedatatest.cpp"
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KURLMIMEDATATEST_H
|
||||
#define KURLMIMEDATATEST_H
|
||||
|
||||
#include "config-kdirwatch.h"
|
||||
#include <QObject>
|
||||
|
||||
class KUrlMimeDataTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testURLList();
|
||||
void testOneURL();
|
||||
void testFromQUrl();
|
||||
void testMostLocalUrlList_data();
|
||||
void testMostLocalUrlList();
|
||||
#if HAVE_QTDBUS
|
||||
void testSameProcessCopyPaste();
|
||||
#endif
|
||||
void initTestCase();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include <QDebug>
|
||||
#include <QTest>
|
||||
|
||||
#include "kcoreaddons_debug.h"
|
||||
#include "kuser.h"
|
||||
|
||||
namespace QTest
|
||||
{
|
||||
template<>
|
||||
char *toString(const KUserId &id)
|
||||
{
|
||||
return qstrdup(id.toString().toLocal8Bit().data());
|
||||
}
|
||||
template<>
|
||||
char *toString(const KGroupId &id)
|
||||
{
|
||||
return qstrdup(id.toString().toLocal8Bit().data());
|
||||
}
|
||||
}
|
||||
|
||||
class KUserTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testKUser();
|
||||
void testKUserGroup();
|
||||
void testKUserId();
|
||||
void testKGroupId();
|
||||
};
|
||||
|
||||
static inline void printUserInfo(KUser user)
|
||||
{
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Login name:" << user.loginName();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Full name:" << user.property(KUser::FullName);
|
||||
qCDebug(KCOREADDONS_DEBUG) << "User ID:" << user.userId().toString();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Group ID:" << user.groupId().toString();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Home dir:" << user.homeDir();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Superuser:" << user.isSuperUser();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Shell: " << user.shell();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Face icon path:" << user.faceIconPath();
|
||||
qCDebug(KCOREADDONS_DEBUG) << "Groups:" << user.groupNames();
|
||||
qCDebug(KCOREADDONS_DEBUG);
|
||||
}
|
||||
|
||||
void KUserTest::testKUser()
|
||||
{
|
||||
KUser user(KUser::UseRealUserID);
|
||||
KUser effectiveUser(KUser::UseRealUserID);
|
||||
QVERIFY(user.isValid());
|
||||
QVERIFY(effectiveUser.isValid());
|
||||
QCOMPARE(user, effectiveUser); // should be the same, no suid
|
||||
QVERIFY(user.groupId().isValid());
|
||||
QCOMPARE(user.groupId(), KGroupId::currentGroupId());
|
||||
QVERIFY(!user.groups().isEmpty()); // user must be in at least one group
|
||||
QVERIFY(!user.groupNames().isEmpty()); // user must be in at least one group
|
||||
QCOMPARE(user.groups().size(), user.groupNames().size());
|
||||
|
||||
QStringList allUserNames = KUser::allUserNames();
|
||||
QList<KUser> allUsers = KUser::allUsers();
|
||||
QVERIFY(!allUserNames.isEmpty());
|
||||
QVERIFY(!allUsers.isEmpty());
|
||||
QCOMPARE(allUsers.size(), allUserNames.size());
|
||||
// check that the limiting works
|
||||
QCOMPARE(user.groups(1).size(), 1);
|
||||
QCOMPARE(user.groupNames(1).size(), 1);
|
||||
qCDebug(KCOREADDONS_DEBUG) << "All users: " << allUserNames;
|
||||
// check that the limiting works
|
||||
QCOMPARE(KUser::allUserNames(1).size(), 1);
|
||||
QCOMPARE(KUser::allUsers(1).size(), 1);
|
||||
// We can't test the KUser properties, since they differ on each system
|
||||
// instead just print them all out, this can be verified by the person running the test
|
||||
printUserInfo(user);
|
||||
#if 0 // enable this if you think that KUser might not be working correctly
|
||||
Q_FOREACH (const KUser &u, allUsers) {
|
||||
printUserInfo(u);
|
||||
}
|
||||
#endif
|
||||
|
||||
// test operator==
|
||||
KUser invalidKUser = KUser(KUserId());
|
||||
QVERIFY(invalidKUser != invalidKUser); // invalid never equal
|
||||
QVERIFY(invalidKUser != user);
|
||||
QVERIFY(user != invalidKUser); // now test the other way around
|
||||
QCOMPARE(user, user);
|
||||
|
||||
// make sure we don't crash when accessing properties of an invalid instance
|
||||
QCOMPARE(invalidKUser.faceIconPath(), QString());
|
||||
QCOMPARE(invalidKUser.groupId(), KGroupId());
|
||||
invalidKUser.groupNames(); // could be empty, or "nogroup", so no checks here
|
||||
invalidKUser.groups(); // same as above
|
||||
QCOMPARE(invalidKUser.homeDir(), QString());
|
||||
QCOMPARE(invalidKUser.isSuperUser(), false);
|
||||
QCOMPARE(invalidKUser.loginName(), QString());
|
||||
QCOMPARE(invalidKUser.shell(), QString());
|
||||
QCOMPARE(invalidKUser.userId(), KUserId());
|
||||
QCOMPARE(invalidKUser.userId(), KUserId());
|
||||
QCOMPARE(invalidKUser.property(KUser::RoomNumber), QVariant());
|
||||
}
|
||||
|
||||
void KUserTest::testKUserGroup()
|
||||
{
|
||||
KUserGroup group(KUser::UseRealUserID);
|
||||
KUserGroup effectiveUser(KUser::UseEffectiveUID);
|
||||
QVERIFY(group.isValid());
|
||||
QVERIFY(effectiveUser.isValid());
|
||||
QCOMPARE(group, effectiveUser); // should be the same, no suid
|
||||
#ifdef Q_OS_WIN
|
||||
// on Windows the special group "None" has no members (often the only group that exists)
|
||||
if (group.name() != QLatin1String("None")) {
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
QStringList groupUserNames = group.userNames();
|
||||
QList<KUser> groupUsers = group.users();
|
||||
QVERIFY(!groupUsers.isEmpty()); // group must have at least one user (the current user)
|
||||
QVERIFY(!groupUserNames.isEmpty()); // group must have at least one user (the current user)
|
||||
QCOMPARE(groupUsers.size(), groupUserNames.size());
|
||||
// check that the limiting works
|
||||
QCOMPARE(group.users(1).size(), 1);
|
||||
QCOMPARE(group.userNames(1).size(), 1);
|
||||
}
|
||||
|
||||
QStringList allGroupNames = KUserGroup::allGroupNames();
|
||||
QList<KUserGroup> allGroups = KUserGroup::allGroups();
|
||||
QVERIFY(!allGroupNames.isEmpty());
|
||||
QVERIFY(!allGroups.isEmpty());
|
||||
QCOMPARE(allGroups.size(), allGroupNames.size());
|
||||
qCDebug(KCOREADDONS_DEBUG) << "All groups: " << allGroupNames;
|
||||
// check that the limiting works
|
||||
QCOMPARE(KUserGroup::allGroupNames(1).size(), 1);
|
||||
QCOMPARE(KUserGroup::allGroups(1).size(), 1);
|
||||
// We can't test the KUser properties, since they differ on each system
|
||||
// instead just print them all out, this can be verified by the person running the test
|
||||
qCDebug(KCOREADDONS_DEBUG).nospace() << "Current group: " << group.name() << ", group ID =" << group.groupId().toString()
|
||||
<< ", members = " << group.userNames();
|
||||
#if 0 // enable this if you think that KUser might not be working correctly
|
||||
for (int i = 0; i < allGroups.size(); ++i) {
|
||||
qDebug().nospace() << "Group " << i << ": name = " << allGroups[i].name()
|
||||
<< ", group ID =" << allGroups[i].groupId().toString();
|
||||
qDebug() << allGroups[i].name() << "members:" << allGroups[i].userNames();
|
||||
}
|
||||
#endif
|
||||
// test operator==
|
||||
KUserGroup invalidKUserGroup = KUserGroup(KGroupId());
|
||||
QVERIFY(invalidKUserGroup != invalidKUserGroup); // invalid never equal
|
||||
QVERIFY(invalidKUserGroup != group);
|
||||
QVERIFY(group != invalidKUserGroup); // now test the other way around
|
||||
QCOMPARE(group, group);
|
||||
|
||||
// make sure we don't crash when accessing an invalid KUserGroup
|
||||
QCOMPARE(invalidKUserGroup.groupId(), KGroupId());
|
||||
invalidKUserGroup.name(); // could be empty, or "nogroup", so no checks here
|
||||
QCOMPARE(invalidKUserGroup.userNames(), QStringList());
|
||||
QCOMPARE(invalidKUserGroup.users(), QList<KUser>());
|
||||
}
|
||||
|
||||
void KUserTest::testKUserId()
|
||||
{
|
||||
// make sure KUser::currentUserId() and KUser::curretEffectiveUserId() work
|
||||
KUserId currentUser = KUserId::currentUserId();
|
||||
QVERIFY(currentUser.isValid());
|
||||
KUserId currentEffectiveUser = KUserId::currentEffectiveUserId();
|
||||
QVERIFY(currentEffectiveUser.isValid());
|
||||
// these should be the same since this is not a setuid program
|
||||
QCOMPARE(currentUser, currentEffectiveUser);
|
||||
|
||||
KUser kuser(currentUser);
|
||||
// now get the same user from his name
|
||||
QString userName = kuser.loginName();
|
||||
qDebug("Current user: %s, id: %s", qPrintable(userName), qPrintable(currentUser.toString()));
|
||||
QVERIFY(!userName.isEmpty());
|
||||
KUserId currentUserFromStr = KUserId::fromName(userName);
|
||||
QVERIFY(currentUserFromStr.isValid());
|
||||
KUserId currentUserCopyFromKUser = kuser.userId();
|
||||
QVERIFY(currentUserCopyFromKUser.isValid());
|
||||
KUserId invalid;
|
||||
QVERIFY(!invalid.isValid());
|
||||
#ifdef Q_OS_WIN
|
||||
KUserId invalid2(nullptr);
|
||||
#else
|
||||
KUserId invalid2(-1);
|
||||
#endif
|
||||
QVERIFY(!invalid2.isValid());
|
||||
// I guess it is safe to assume no user with this name exists
|
||||
KUserId invalid3 = KUserId::fromName(QStringLiteral("This_user_does_not_exist"));
|
||||
QVERIFY(!invalid3.isValid());
|
||||
|
||||
// check comparison
|
||||
QCOMPARE(invalid, KUserId());
|
||||
QCOMPARE(invalid, invalid2);
|
||||
QCOMPARE(invalid, invalid3);
|
||||
QCOMPARE(currentUser, currentUserFromStr);
|
||||
QCOMPARE(currentUser, currentEffectiveUser);
|
||||
QCOMPARE(currentUser, currentUserCopyFromKUser);
|
||||
QVERIFY(currentUser != invalid);
|
||||
QVERIFY(currentUser != invalid2);
|
||||
QVERIFY(currentUser != invalid3);
|
||||
QVERIFY(invalid != currentUser);
|
||||
// Copy constructor and assignment
|
||||
KUserId currentUserCopy = currentUser;
|
||||
QCOMPARE(currentUser, currentUserCopy);
|
||||
QCOMPARE(currentUser, KUserId(currentUser));
|
||||
QCOMPARE(currentEffectiveUser, KUserId(currentUser));
|
||||
}
|
||||
|
||||
void KUserTest::testKGroupId()
|
||||
{
|
||||
// make sure KGroup::currentGroupId() and KGroup::curretEffectiveGroupId() work
|
||||
KGroupId currentGroup = KGroupId::currentGroupId();
|
||||
QVERIFY(currentGroup.isValid());
|
||||
KGroupId currentEffectiveGroup = KGroupId::currentEffectiveGroupId();
|
||||
QVERIFY(currentEffectiveGroup.isValid());
|
||||
// these should be the same since this is not a setuid program
|
||||
QCOMPARE(currentGroup, currentEffectiveGroup);
|
||||
|
||||
// now get the same Group from his name
|
||||
KUserGroup kuserGroup(currentGroup);
|
||||
QString groupName = kuserGroup.name();
|
||||
qDebug("Current group: %s, id: %s", qPrintable(groupName), qPrintable(currentGroup.toString()));
|
||||
QVERIFY(!groupName.isEmpty());
|
||||
KGroupId currentGroupFromStr = KGroupId::fromName(groupName);
|
||||
QVERIFY(currentGroupFromStr.isValid());
|
||||
KGroupId currentGroupCopyFromKUserGroup = kuserGroup.groupId();
|
||||
QVERIFY(currentGroupCopyFromKUserGroup.isValid());
|
||||
KGroupId invalid;
|
||||
QVERIFY(!invalid.isValid());
|
||||
#ifdef Q_OS_WIN
|
||||
KGroupId invalid2(nullptr);
|
||||
#else
|
||||
KGroupId invalid2(-1);
|
||||
#endif
|
||||
QVERIFY(!invalid2.isValid());
|
||||
// I guess it is safe to assume no Group with this name exists
|
||||
KGroupId invalid3 = KGroupId::fromName(QStringLiteral("This_Group_does_not_exist"));
|
||||
QVERIFY(!invalid3.isValid());
|
||||
|
||||
// check comparison
|
||||
QCOMPARE(invalid, KGroupId());
|
||||
QCOMPARE(invalid, invalid2);
|
||||
QCOMPARE(invalid, invalid3);
|
||||
QCOMPARE(currentGroup, currentGroupFromStr);
|
||||
QCOMPARE(currentGroup, currentEffectiveGroup);
|
||||
QCOMPARE(currentGroup, currentGroupCopyFromKUserGroup);
|
||||
QVERIFY(invalid != currentGroup);
|
||||
QVERIFY(currentGroup != invalid);
|
||||
QVERIFY(currentGroup != invalid2);
|
||||
QVERIFY(currentGroup != invalid3);
|
||||
// Copy constructor and assignment
|
||||
KGroupId currentGroupCopy = currentGroup;
|
||||
QCOMPARE(currentGroup, currentGroupCopy);
|
||||
QCOMPARE(currentGroup, KGroupId(currentGroup));
|
||||
QCOMPARE(currentEffectiveGroup, KGroupId(currentGroup));
|
||||
}
|
||||
|
||||
QTEST_MAIN(KUserTest)
|
||||
|
||||
#include "kusertest.moc"
|
||||
@@ -0,0 +1,58 @@
|
||||
find_package(Qt6 ${REQUIRED_QT_VERSION} CONFIG QUIET OPTIONAL_COMPONENTS Widgets)
|
||||
if (NOT TARGET Qt6::Widgets)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(plugin_classes plugins.cpp)
|
||||
generate_export_header(plugin_classes)
|
||||
|
||||
kcoreaddons_add_plugin(jsonplugin_cmake_macro SOURCES kpluginclass.cpp INSTALL_NAMESPACE "namespace")
|
||||
ecm_mark_as_test(jsonplugin_cmake_macro)
|
||||
target_link_libraries(jsonplugin_cmake_macro KF6::CoreAddons autotests_static plugin_classes)
|
||||
target_link_libraries(plugin_classes Qt6::Core)
|
||||
|
||||
kcoreaddons_add_plugin(qtplugin SOURCES qtplugin.cpp INSTALL_NAMESPACE "namespace")
|
||||
ecm_mark_as_test(qtplugin)
|
||||
target_link_libraries(qtplugin KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_add_plugin(pluginwithoutmetadata SOURCES pluginwithoutmetadata.cpp INSTALL_NAMESPACE "namespace")
|
||||
ecm_mark_as_test(pluginwithoutmetadata)
|
||||
target_link_libraries(pluginwithoutmetadata KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_add_plugin(widgetsplugin SOURCES widgetsplugin.cpp INSTALL_NAMESPACE "widgets")
|
||||
ecm_mark_as_test(widgetsplugin)
|
||||
target_link_libraries(widgetsplugin KF6::CoreAddons Qt6::Widgets)
|
||||
|
||||
add_library(org.kde.test MODULE qtplugin.cpp)
|
||||
target_link_libraries(org.kde.test KF6::CoreAddons)
|
||||
|
||||
|
||||
ecm_add_tests(
|
||||
kpluginmetadatatest.cpp
|
||||
kpluginfactorytest.cpp
|
||||
${autotests_OPTIONAL_SRCS}
|
||||
LINK_LIBRARIES Qt6::Test Qt6::Widgets KF6::CoreAddons autotests_static
|
||||
)
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(kpluginfactorytest plugin_classes)
|
||||
endif()
|
||||
|
||||
kcoreaddons_add_plugin(static_jsonplugin_cmake_macro SOURCES statickpluginclass.cpp INSTALL_NAMESPACE "staticnamespace" STATIC)
|
||||
target_link_libraries(static_jsonplugin_cmake_macro KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_add_plugin(static_jsonplugin_cmake_macro_2 SOURCES statickpluginclass_2.cpp INSTALL_NAMESPACE "staticnamespace2" STATIC)
|
||||
target_link_libraries(static_jsonplugin_cmake_macro_2 KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_add_plugin(static_plugin_without_metadata SOURCES staticpluginwithoutmetadata.cpp INSTALL_NAMESPACE "staticnamespace3" STATIC)
|
||||
target_link_libraries(static_plugin_without_metadata KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_add_plugin(org.kde.test-staticplugin SOURCES statickpluginclass_3.cpp INSTALL_NAMESPACE "rdnstatic" STATIC)
|
||||
target_link_libraries(org.kde.test-staticplugin KF6::CoreAddons autotests_static)
|
||||
|
||||
kcoreaddons_target_static_plugins(kpluginfactorytest NAMESPACE "staticnamespace")
|
||||
kcoreaddons_target_static_plugins(kpluginfactorytest NAMESPACE "staticnamespace2")
|
||||
kcoreaddons_target_static_plugins(kpluginmetadatatest NAMESPACE "staticnamespace")
|
||||
kcoreaddons_target_static_plugins(kpluginmetadatatest NAMESPACE "staticnamespace2")
|
||||
kcoreaddons_target_static_plugins(kpluginmetadatatest NAMESPACE "rdnstatic")
|
||||
# Make the last one use the target by targets list instead of namespace
|
||||
kcoreaddons_target_static_plugins(kpluginmetadatatest TARGETS static_plugin_without_metadata)
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Description": "This is a plugin",
|
||||
"Description[nl]": "Dit is een plug-in",
|
||||
"Description[pt]": "Isto é um 'plugin'",
|
||||
"Description[pt_BR]": "Isto é um plugin",
|
||||
"Description[sv]": "Det här är ett insticksprogram",
|
||||
"Description[uk]": "Це додаток",
|
||||
"Description[x-test]": "xxThis is a pluginxx",
|
||||
"MimeTypes": [ "text/plain"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Description": "This is another plugin",
|
||||
"MimeTypes": [ "text/html" ]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "Aleix Pol"
|
||||
}
|
||||
],
|
||||
"Description": "Test stuff.",
|
||||
"Icon": "kdevelop",
|
||||
"License": "GPL",
|
||||
"Name": "Test"
|
||||
},
|
||||
"X-Plasma-MainScript": "ui/main.qml",
|
||||
"X-Purpose-PluginTypes": [ "Export" ],
|
||||
"SomeInt" : 42,
|
||||
"SomeIntAsString" : "42",
|
||||
"SomeStringNotAInt" : "not-a-string",
|
||||
"SomeBool" : true,
|
||||
"SomeBoolAsString" : "true",
|
||||
"SomeBoolThatIsFalse": false
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "kpluginfactory.h"
|
||||
#include "plugins.h"
|
||||
|
||||
class SimplePluginClass : public MyPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SimplePluginClass(QObject *parent, const QVariantList &args)
|
||||
: MyPlugin(parent)
|
||||
{
|
||||
setProperty("arg", args.isEmpty() ? QVariant() : args.first());
|
||||
}
|
||||
};
|
||||
class SimplePluginClass2 : public MyPlugin2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SimplePluginClass2(QObject *parent)
|
||||
: MyPlugin2(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_FACTORY_WITH_JSON(MyFactory, "data/jsonplugin.json", registerPlugin<SimplePluginClass>(); registerPlugin<SimplePluginClass2>();)
|
||||
|
||||
#include "kpluginclass.moc"
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QPluginLoader>
|
||||
#include <kpluginfactory.h>
|
||||
#ifndef Q_OS_WIN
|
||||
#include "plugins.h"
|
||||
#endif
|
||||
|
||||
class KPluginFactoryTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void testCreate()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
#ifndef Q_OS_WIN
|
||||
KPluginMetaData data(QStringLiteral("namespace/jsonplugin_cmake_macro"));
|
||||
QVERIFY(data.isValid());
|
||||
KPluginFactory::Result<KPluginFactory> factoryResult = KPluginFactory::loadFactory(data);
|
||||
auto factory = factoryResult.plugin;
|
||||
QVERIFY(factory);
|
||||
|
||||
const QString testProp = QStringLiteral("testme");
|
||||
QObject *obj = factory->create<MyPlugin>(this, {testProp});
|
||||
QVERIFY(obj);
|
||||
QCOMPARE(obj->metaObject()->className(), "SimplePluginClass");
|
||||
QCOMPARE(obj->property("arg").toString(), testProp);
|
||||
|
||||
QObject *obj2 = factory->create<MyPlugin2>(this);
|
||||
QVERIFY(obj2);
|
||||
QCOMPARE(obj2->metaObject()->className(), "SimplePluginClass2");
|
||||
QVERIFY(obj != obj2);
|
||||
delete obj;
|
||||
delete obj2;
|
||||
|
||||
// Try creating a part without keyword/args
|
||||
QWidget parentWidget;
|
||||
QObject *partTest = factory->create<QObject>(&parentWidget, this);
|
||||
QVERIFY(partTest);
|
||||
delete partTest;
|
||||
#endif
|
||||
}
|
||||
|
||||
void testPluginWithoutMetaData()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
KPluginFactory::Result<KPluginFactory> factoryResult = KPluginFactory::loadFactory(KPluginMetaData(QStringLiteral("namespace/pluginwithoutmetadata")));
|
||||
QVERIFY(factoryResult);
|
||||
auto plugin = factoryResult.plugin->create<QObject>();
|
||||
QVERIFY(plugin);
|
||||
QCOMPARE(plugin->metaObject()->className(), "PluginWithoutMetaData");
|
||||
delete plugin;
|
||||
}
|
||||
|
||||
void testCreateUsingUtilityMethods()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
auto result = KPluginFactory::instantiatePlugin<QObject>(KPluginMetaData(QStringLiteral("namespace/jsonplugin_cmake_macro")));
|
||||
QVERIFY(result.plugin);
|
||||
QCOMPARE(result.plugin->metaObject()->className(), "SimplePluginClass");
|
||||
QVERIFY(result.errorString.isEmpty());
|
||||
QCOMPARE(result.errorReason, KPluginFactory::NO_PLUGIN_ERROR);
|
||||
delete result.plugin;
|
||||
}
|
||||
|
||||
void testCreateUsingUtilityMethodsErrorHandling()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
{
|
||||
auto result = KPluginFactory::instantiatePlugin<QObject>(KPluginMetaData(QFINDTESTDATA("data/jsonplugin.json")));
|
||||
QVERIFY(!result.plugin);
|
||||
QCOMPARE(result.errorReason, KPluginFactory::INVALID_PLUGIN);
|
||||
}
|
||||
{
|
||||
// it is a valid plugin, but does not contain a KPluginFactory
|
||||
QVERIFY(QPluginLoader(QStringLiteral("namespace/qtplugin")).instance());
|
||||
auto result = KPluginFactory::instantiatePlugin<QObject>(KPluginMetaData(QStringLiteral("namespace/qtplugin")));
|
||||
QVERIFY(!result.plugin);
|
||||
// But does not contain a valid plugin factory
|
||||
QCOMPARE(result.errorReason, KPluginFactory::INVALID_FACTORY);
|
||||
}
|
||||
{
|
||||
// it is a QObject, but not a KPluginFactoryTest instance
|
||||
auto result = KPluginFactory::instantiatePlugin<KPluginFactoryTest>(KPluginMetaData(QStringLiteral("namespace/jsonplugin_cmake_macro")));
|
||||
QVERIFY(!result.plugin);
|
||||
QCOMPARE(result.errorReason, KPluginFactory::INVALID_KPLUGINFACTORY_INSTANTIATION);
|
||||
QVERIFY(result.errorText.contains(QStringLiteral("KPluginFactoryTest")));
|
||||
}
|
||||
}
|
||||
|
||||
void testStaticPlugins()
|
||||
{
|
||||
const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("staticnamespace"));
|
||||
QCOMPARE(plugins.count(), 1);
|
||||
auto factory = KPluginFactory::loadFactory(plugins.first()).plugin;
|
||||
QCOMPARE(factory->metaObject()->className(), "static_jsonplugin_cmake_macro_factory");
|
||||
auto result = KPluginFactory::instantiatePlugin<QObject>(plugins.first());
|
||||
QVERIFY(result);
|
||||
delete result.plugin;
|
||||
}
|
||||
|
||||
void testNonExistingPlugin()
|
||||
{
|
||||
KPluginMetaData data(QStringLiteral("does/not/exist"));
|
||||
QVERIFY(!data.isValid());
|
||||
const auto res = KPluginFactory::instantiatePlugin<QObject>(data);
|
||||
QVERIFY(!res);
|
||||
QCOMPARE(res.errorReason, KPluginFactory::INVALID_PLUGIN);
|
||||
QCOMPARE(res.errorText, QStringLiteral("Could not find plugin does/not/exist"));
|
||||
}
|
||||
|
||||
void testInstantiateWidget()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
KPluginMetaData data(QStringLiteral("widgets/widgetsplugin"), KPluginMetaData::AllowEmptyMetaData);
|
||||
QVERIFY(data.isValid());
|
||||
auto factory = KPluginFactory::loadFactory(data).plugin;
|
||||
QVERIFY(factory);
|
||||
|
||||
// This has two constructors, make sure we choose the first one that takes args
|
||||
auto obj = factory->create<QWidget>(nullptr, QVariantList{true});
|
||||
QVERIFY(obj);
|
||||
QVERIFY(obj->property("firstarg").toBool());
|
||||
delete obj;
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(KPluginFactoryTest)
|
||||
|
||||
#include "kpluginfactorytest.moc"
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QPluginLoader>
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
#include "kcoreaddons_debug.h"
|
||||
#include <kaboutdata.h>
|
||||
#include <kpluginmetadata.h>
|
||||
|
||||
#include <QLocale>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
class LibraryPathRestorer
|
||||
{
|
||||
public:
|
||||
explicit LibraryPathRestorer(const QStringList &paths)
|
||||
: mPaths(paths)
|
||||
{
|
||||
}
|
||||
~LibraryPathRestorer()
|
||||
{
|
||||
QCoreApplication::setLibraryPaths(mPaths);
|
||||
}
|
||||
|
||||
private:
|
||||
QStringList mPaths;
|
||||
};
|
||||
|
||||
class KPluginMetaDataTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
bool m_canMessage = false;
|
||||
|
||||
Q_REQUIRED_RESULT bool doMessagesWork()
|
||||
{
|
||||
// Q_SKIP returns, but since this is called in multiple tests we want to return a bool so the caller can
|
||||
// return easily.
|
||||
auto internalCheck = [this] {
|
||||
// Make sure output is well formed AND generated. To that end we cannot run this test when any of the
|
||||
// overriding environment variables are set.
|
||||
// https://bugs.kde.org/show_bug.cgi?id=387006
|
||||
if (qEnvironmentVariableIsSet("QT_MESSAGE_PATTERN")) {
|
||||
QSKIP("QT_MESSAGE_PATTERN prevents warning expectations from matching");
|
||||
}
|
||||
if (qEnvironmentVariableIsSet("QT_LOGGING_RULES")) {
|
||||
QSKIP("QT_LOGGING_RULES prevents warning expectations from matching");
|
||||
}
|
||||
if (qEnvironmentVariableIsSet("QT_LOGGING_CONF")) {
|
||||
QSKIP("QT_LOGGING_CONF prevents warning expectations from matching");
|
||||
}
|
||||
m_canMessage = true;
|
||||
// Ensure all frameworks output is enabled so the expectations can match.
|
||||
// qtlogging.ini may have disabled it but we can fix that because setFilterRules overrides the ini files.
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("kf.*=true"));
|
||||
};
|
||||
internalCheck();
|
||||
return m_canMessage;
|
||||
}
|
||||
private Q_SLOTS:
|
||||
|
||||
void testFromPluginLoader()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
QString location;
|
||||
location = QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro")).fileName();
|
||||
QVERIFY2(!location.isEmpty(), "Could not find jsonplugin");
|
||||
|
||||
// now that this file is translated we need to read it instead of hardcoding the contents here
|
||||
QString jsonLocation = QFINDTESTDATA("data/jsonplugin.json");
|
||||
QVERIFY2(!jsonLocation.isEmpty(), "Could not find jsonplugin.json");
|
||||
QFile jsonFile(jsonLocation);
|
||||
QVERIFY(jsonFile.open(QFile::ReadOnly));
|
||||
QJsonParseError e;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonFile.readAll(), &e);
|
||||
QCOMPARE(e.error, QJsonParseError::NoError);
|
||||
|
||||
location = QFileInfo(location).absoluteFilePath();
|
||||
|
||||
KPluginMetaData fromQPluginLoader(QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro")));
|
||||
KPluginMetaData fromFullPath(location);
|
||||
KPluginMetaData fromRelativePath(QStringLiteral("namespace/jsonplugin_cmake_macro"));
|
||||
KPluginMetaData fromRawData(jsonDoc.object(), location);
|
||||
|
||||
auto description = QStringLiteral("This is a plugin");
|
||||
|
||||
QVERIFY(fromQPluginLoader.isValid());
|
||||
QCOMPARE(fromQPluginLoader.description(), description);
|
||||
QVERIFY(fromFullPath.isValid());
|
||||
QCOMPARE(fromFullPath.description(), description);
|
||||
QVERIFY(fromRelativePath.isValid());
|
||||
QCOMPARE(fromRelativePath.description(), description);
|
||||
QVERIFY(fromRawData.isValid());
|
||||
QCOMPARE(fromRawData.description(), description);
|
||||
|
||||
// check operator==
|
||||
QCOMPARE(fromRawData, fromRawData);
|
||||
QCOMPARE(fromQPluginLoader, fromQPluginLoader);
|
||||
QCOMPARE(fromFullPath, fromFullPath);
|
||||
|
||||
QCOMPARE(fromQPluginLoader, fromFullPath);
|
||||
QCOMPARE(fromQPluginLoader, fromRawData);
|
||||
|
||||
QCOMPARE(fromFullPath, fromQPluginLoader);
|
||||
QCOMPARE(fromFullPath, fromRawData);
|
||||
|
||||
QCOMPARE(fromRawData, fromQPluginLoader);
|
||||
QCOMPARE(fromRawData, fromFullPath);
|
||||
|
||||
QVERIFY(!KPluginMetaData(QPluginLoader(QStringLiteral("doesnotexist"))).isValid());
|
||||
QVERIFY(!KPluginMetaData(QJsonObject(), QString()).isValid());
|
||||
}
|
||||
|
||||
void testAllKeys()
|
||||
{
|
||||
QJsonParseError e;
|
||||
QJsonObject jo = QJsonDocument::fromJson(
|
||||
"{\n"
|
||||
" \"KPlugin\": {\n"
|
||||
" \"Name\": \"Date and Time\",\n"
|
||||
" \"Description\": \"Date and time by timezone\",\n"
|
||||
" \"Icon\": \"preferences-system-time\",\n"
|
||||
" \"Authors\": { \"Name\": \"Aaron Seigo\", \"Email\": \"aseigo@kde.org\" },\n"
|
||||
" \"Translators\": { \"Name\": \"No One\", \"Email\": \"no.one@kde.org\" },\n"
|
||||
" \"OtherContributors\": { \"Name\": \"No One\", \"Email\": \"no.one@kde.org\" },\n"
|
||||
" \"Category\": \"Date and Time\",\n"
|
||||
" \"EnabledByDefault\": \"true\",\n"
|
||||
" \"ExtraInformation\": \"Something else\",\n"
|
||||
" \"License\": \"LGPL\",\n"
|
||||
" \"Copyright\": \"(c) Alex Richardson 2015\",\n"
|
||||
" \"Id\": \"time\",\n"
|
||||
" \"Version\": \"1.0\",\n"
|
||||
" \"Website\": \"https://plasma.kde.org/\",\n"
|
||||
" \"MimeTypes\": [ \"image/png\" ]\n"
|
||||
" }\n}\n",
|
||||
&e)
|
||||
.object();
|
||||
QCOMPARE(e.error, QJsonParseError::NoError);
|
||||
KPluginMetaData m(jo, QString());
|
||||
QVERIFY(m.isValid());
|
||||
QCOMPARE(m.pluginId(), QStringLiteral("time"));
|
||||
QCOMPARE(m.name(), QStringLiteral("Date and Time"));
|
||||
QCOMPARE(m.description(), QStringLiteral("Date and time by timezone"));
|
||||
QCOMPARE(m.iconName(), QStringLiteral("preferences-system-time"));
|
||||
QCOMPARE(m.category(), QStringLiteral("Date and Time"));
|
||||
QCOMPARE(m.authors().size(), 1);
|
||||
QCOMPARE(m.authors().constFirst().name(), QStringLiteral("Aaron Seigo"));
|
||||
QCOMPARE(m.authors().constFirst().emailAddress(), QStringLiteral("aseigo@kde.org"));
|
||||
QCOMPARE(m.translators().size(), 1);
|
||||
QCOMPARE(m.translators().constFirst().name(), QStringLiteral("No One"));
|
||||
QCOMPARE(m.translators().constFirst().emailAddress(), QStringLiteral("no.one@kde.org"));
|
||||
QCOMPARE(m.otherContributors().size(), 1);
|
||||
QCOMPARE(m.otherContributors().constFirst().name(), QStringLiteral("No One"));
|
||||
QCOMPARE(m.otherContributors().constFirst().emailAddress(), QStringLiteral("no.one@kde.org"));
|
||||
QVERIFY(m.isEnabledByDefault());
|
||||
QCOMPARE(m.license(), QStringLiteral("LGPL"));
|
||||
QCOMPARE(m.copyrightText(), QStringLiteral("(c) Alex Richardson 2015"));
|
||||
QCOMPARE(m.version(), QStringLiteral("1.0"));
|
||||
QCOMPARE(m.website(), QStringLiteral("https://plasma.kde.org/"));
|
||||
QCOMPARE(m.mimeTypes(), QStringList(QStringLiteral("image/png")));
|
||||
}
|
||||
|
||||
void testTranslations()
|
||||
{
|
||||
QJsonParseError e;
|
||||
QJsonObject jo = QJsonDocument::fromJson(
|
||||
"{ \"KPlugin\": {\n"
|
||||
"\"Name\": \"Name\",\n"
|
||||
"\"Name[de]\": \"Name (de)\",\n"
|
||||
"\"Name[de_DE]\": \"Name (de_DE)\",\n"
|
||||
"\"Description\": \"Description\",\n"
|
||||
"\"Description[de]\": \"Beschreibung (de)\",\n"
|
||||
"\"Description[de_DE]\": \"Beschreibung (de_DE)\"\n"
|
||||
"}\n}",
|
||||
&e)
|
||||
.object();
|
||||
KPluginMetaData m(jo, QString());
|
||||
QLocale::setDefault(QLocale::c());
|
||||
QCOMPARE(m.name(), QStringLiteral("Name"));
|
||||
QCOMPARE(m.description(), QStringLiteral("Description"));
|
||||
|
||||
QLocale::setDefault(QLocale(QStringLiteral("de_DE")));
|
||||
QCOMPARE(m.name(), QStringLiteral("Name (de_DE)"));
|
||||
QCOMPARE(m.description(), QStringLiteral("Beschreibung (de_DE)"));
|
||||
|
||||
QLocale::setDefault(QLocale(QStringLiteral("de_CH")));
|
||||
QCOMPARE(m.name(), QStringLiteral("Name (de)"));
|
||||
QCOMPARE(m.description(), QStringLiteral("Beschreibung (de)"));
|
||||
|
||||
QLocale::setDefault(QLocale(QStringLiteral("fr_FR")));
|
||||
QCOMPARE(m.name(), QStringLiteral("Name"));
|
||||
QCOMPARE(m.description(), QStringLiteral("Description"));
|
||||
}
|
||||
|
||||
void testReadStringList()
|
||||
{
|
||||
if (!doMessagesWork()) {
|
||||
return;
|
||||
}
|
||||
QJsonParseError e;
|
||||
QJsonObject jo = QJsonDocument::fromJson(
|
||||
"{\n"
|
||||
"\"String\": \"foo\",\n"
|
||||
"\"OneArrayEntry\": [ \"foo\" ],\n"
|
||||
"\"Bool\": true,\n" // make sure booleans are accepted
|
||||
"\"QuotedBool\": \"true\",\n" // make sure booleans are accepted
|
||||
"\"Number\": 12345,\n" // number should also work
|
||||
"\"QuotedNumber\": \"12345\",\n" // number should also work
|
||||
"\"EmptyArray\": [],\n"
|
||||
"\"NumberArray\": [1, 2, 3],\n"
|
||||
"\"BoolArray\": [true, false, true],\n"
|
||||
"\"StringArray\": [\"foo\", \"bar\"],\n"
|
||||
"\"Null\": null,\n" // should return empty list
|
||||
"\"QuotedNull\": \"null\",\n" // this is okay, it is a string
|
||||
"\"ArrayWithNull\": [ \"foo\", null, \"bar\"],\n" // TODO: null is converted to empty string, is this okay?
|
||||
"\"Object\": { \"foo\": \"bar\" }\n" // should return empty list
|
||||
"}",
|
||||
&e)
|
||||
.object();
|
||||
QCOMPARE(e.error, QJsonParseError::NoError);
|
||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(QStringLiteral("Expected JSON property ")));
|
||||
KPluginMetaData data(jo, QStringLiteral("test"));
|
||||
QCOMPARE(data.value(QStringLiteral("String"), QStringList()), QStringList(QStringLiteral("foo")));
|
||||
QCOMPARE(data.value(QStringLiteral("OneArrayEntry"), QStringList()), QStringList(QStringLiteral("foo")));
|
||||
QCOMPARE(data.value(QStringLiteral("Bool"), QStringList()), QStringList(QStringLiteral("true")));
|
||||
QCOMPARE(data.value(QStringLiteral("QuotedBool"), QStringList()), QStringList(QStringLiteral("true")));
|
||||
QCOMPARE(data.value(QStringLiteral("Number"), QStringList()), QStringList(QStringLiteral("12345")));
|
||||
QCOMPARE(data.value(QStringLiteral("QuotedNumber"), QStringList()), QStringList(QStringLiteral("12345")));
|
||||
QCOMPARE(data.value(QStringLiteral("EmptyArray"), QStringList()), QStringList());
|
||||
QCOMPARE(data.value(QStringLiteral("NumberArray"), QStringList()), QStringList() << QStringLiteral("1") << QStringLiteral("2") << QStringLiteral("3"));
|
||||
QCOMPARE(data.value(QStringLiteral("BoolArray"), QStringList()),
|
||||
QStringList() << QStringLiteral("true") << QStringLiteral("false") << QStringLiteral("true"));
|
||||
QCOMPARE(data.value(QStringLiteral("StringArray"), QStringList()), QStringList() << QStringLiteral("foo") << QStringLiteral("bar"));
|
||||
QCOMPARE(data.value(QStringLiteral("Null"), QStringList()), QStringList());
|
||||
QCOMPARE(data.value(QStringLiteral("QuotedNull"), QStringList()), QStringList(QStringLiteral("null")));
|
||||
QCOMPARE(data.value(QStringLiteral("ArrayWithNull"), QStringList()), QStringList() << QStringLiteral("foo") << QString() << QStringLiteral("bar"));
|
||||
QCOMPARE(data.value(QStringLiteral("Object"), QStringList()), QStringList());
|
||||
}
|
||||
|
||||
void testJSONMetadata()
|
||||
{
|
||||
const QString inputPath = QFINDTESTDATA("data/testmetadata.json");
|
||||
KPluginMetaData md = KPluginMetaData::fromJsonFile(inputPath);
|
||||
QVERIFY(md.isValid());
|
||||
QCOMPARE(md.name(), QStringLiteral("Test"));
|
||||
|
||||
QCOMPARE(md.value(QStringLiteral("X-Plasma-MainScript")), QStringLiteral("ui/main.qml"));
|
||||
QJsonArray expected;
|
||||
expected.append(QStringLiteral("Export"));
|
||||
QCOMPARE(md.rawData().value(QStringLiteral("X-Purpose-PluginTypes")).toArray(), expected);
|
||||
QCOMPARE(md.value(QStringLiteral("SomeInt"), 24), 42);
|
||||
QCOMPARE(md.value(QStringLiteral("SomeIntAsString"), 24), 42);
|
||||
QCOMPARE(md.value(QStringLiteral("SomeStringNotAInt"), 24), 24);
|
||||
QCOMPARE(md.value(QStringLiteral("DoesNotExist"), 24), 24);
|
||||
|
||||
QVERIFY(md.value(QStringLiteral("SomeBool"), false));
|
||||
QVERIFY(!md.value(QStringLiteral("SomeBoolThatIsFalse"), true));
|
||||
QVERIFY(md.value(QStringLiteral("SomeBoolAsString"), false));
|
||||
QVERIFY(md.value(QStringLiteral("DoesNotExist"), true));
|
||||
}
|
||||
|
||||
void testPathIsAbsolute_data()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
QTest::addColumn<QString>("inputAbsolute");
|
||||
QTest::addColumn<QString>("pluginPath");
|
||||
|
||||
// But for the .json based plugin both are the same.
|
||||
QTest::newRow("json") << QFINDTESTDATA("data/testmetadata.json") << QFINDTESTDATA("data/testmetadata.json");
|
||||
// And also for the library with embedded JSON metadata.
|
||||
QPluginLoader shlibLoader(QCoreApplication::applicationDirPath() + QStringLiteral("/namespace/jsonplugin_cmake_macro"));
|
||||
QVERIFY2(!shlibLoader.fileName().isEmpty(), "Could not find jsonplugin_cmake_macro");
|
||||
QString shlibPath = QFileInfo(shlibLoader.fileName()).absoluteFilePath();
|
||||
QTest::newRow("library") << shlibPath << shlibPath;
|
||||
}
|
||||
|
||||
void testPathIsAbsolute()
|
||||
{
|
||||
// Test that the fileName() accessor always returns an absolute path if it was used.
|
||||
QFETCH(QString, inputAbsolute);
|
||||
QVERIFY2(QDir::isAbsolutePath(inputAbsolute), qPrintable(inputAbsolute));
|
||||
QFETCH(QString, pluginPath);
|
||||
|
||||
const auto createMetaData = [](const QString &path) {
|
||||
if (path.endsWith(QLatin1String(".json"))) {
|
||||
return KPluginMetaData::fromJsonFile(path);
|
||||
} else {
|
||||
return KPluginMetaData(path);
|
||||
}
|
||||
};
|
||||
|
||||
KPluginMetaData mdAbsolute = createMetaData(inputAbsolute);
|
||||
QVERIFY(mdAbsolute.isValid());
|
||||
QCOMPARE(mdAbsolute.fileName(), pluginPath);
|
||||
|
||||
// All files that have been opened should be stored as absolute paths.
|
||||
QString inputRelative;
|
||||
if (QLibrary::isLibrary(inputAbsolute)) {
|
||||
// We have a plugin without namespace, with the code path below we would end up with
|
||||
// a path relative to the PWD, but we want to check a path relative to the plugin dir.
|
||||
// Because of that we simply use the baseName of the file.
|
||||
inputRelative = QStringLiteral("namespace/") + QFileInfo(inputAbsolute).baseName();
|
||||
} else {
|
||||
inputRelative = QDir::current().relativeFilePath(inputAbsolute);
|
||||
}
|
||||
QVERIFY2(QDir::isRelativePath(inputRelative), qPrintable(inputRelative));
|
||||
KPluginMetaData mdRelative = createMetaData(inputRelative);
|
||||
QVERIFY(mdRelative.isValid());
|
||||
QCOMPARE(mdRelative.fileName(), pluginPath);
|
||||
|
||||
// Check that creating it with the parsed JSON object and a path keeps the path unchanged
|
||||
const QJsonObject json = mdAbsolute.rawData();
|
||||
QString pluginRelative = QDir::current().relativeFilePath(pluginPath);
|
||||
QVERIFY2(QDir::isRelativePath(pluginRelative), qPrintable(pluginRelative));
|
||||
|
||||
// We should not be normalizing files that have not been openened, so both arguments should be unchanged.
|
||||
KPluginMetaData mdFromJson1(json, pluginRelative);
|
||||
KPluginMetaData mdFromJson2(json, inputRelative);
|
||||
QCOMPARE(mdFromJson1.fileName(), pluginRelative);
|
||||
QCOMPARE(mdFromJson2.fileName(), inputRelative);
|
||||
}
|
||||
|
||||
void testFindPlugins()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
auto sortPlugins = [](const KPluginMetaData &a, const KPluginMetaData &b) {
|
||||
return a.pluginId() < b.pluginId();
|
||||
};
|
||||
// it should find jsonplugin and jsonplugin2 since unversionedplugin does not have any meta data
|
||||
auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"));
|
||||
std::sort(plugins.begin(), plugins.end(), sortPlugins);
|
||||
QCOMPARE(plugins.size(), 2);
|
||||
QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro")); // ID is not the filename, it is set in the JSON metadata
|
||||
QCOMPARE(plugins[0].description(), QStringLiteral("This is a plugin"));
|
||||
QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin")); // ID is not the filename, it is set in the JSON metadata
|
||||
|
||||
// filter accepts none
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), [](const KPluginMetaData &) {
|
||||
return false;
|
||||
});
|
||||
std::sort(plugins.begin(), plugins.end(), sortPlugins);
|
||||
QCOMPARE(plugins.size(), 0);
|
||||
|
||||
// filter accepts all
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), [](const KPluginMetaData &) {
|
||||
return true;
|
||||
});
|
||||
std::sort(plugins.begin(), plugins.end(), sortPlugins);
|
||||
QCOMPARE(plugins.size(), 2);
|
||||
|
||||
// mimetype filter. Only one match, jsonplugin2 is specific to text/html.
|
||||
auto supportTextPlain = [](const KPluginMetaData &metaData) {
|
||||
return metaData.supportsMimeType(QStringLiteral("text/plain"));
|
||||
};
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportTextPlain);
|
||||
QCOMPARE(plugins.size(), 1);
|
||||
QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro"));
|
||||
|
||||
// mimetype filter. Two matches, both support text/html, via inheritance.
|
||||
auto supportTextHtml = [](const KPluginMetaData &metaData) {
|
||||
return metaData.supportsMimeType(QStringLiteral("text/html"));
|
||||
};
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportTextHtml);
|
||||
std::sort(plugins.begin(), plugins.end(), sortPlugins);
|
||||
QCOMPARE(plugins.size(), 2);
|
||||
QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro"));
|
||||
QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin"));
|
||||
|
||||
// mimetype filter with invalid mimetype
|
||||
auto supportDoesNotExist = [](const KPluginMetaData &metaData) {
|
||||
return metaData.supportsMimeType(QStringLiteral("does/not/exist"));
|
||||
};
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), supportDoesNotExist);
|
||||
QCOMPARE(plugins.size(), 0);
|
||||
|
||||
// by plugin invalid id
|
||||
KPluginMetaData plugin = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("invalidid"));
|
||||
QVERIFY(!plugin.isValid());
|
||||
|
||||
// absolute path, no filter
|
||||
plugins = KPluginMetaData::findPlugins(QCoreApplication::applicationDirPath() + QStringLiteral("/namespace"));
|
||||
std::sort(plugins.begin(), plugins.end(), sortPlugins);
|
||||
QCOMPARE(plugins.size(), 2);
|
||||
QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro"));
|
||||
QCOMPARE(plugins[1].pluginId(), QStringLiteral("qtplugin"));
|
||||
|
||||
// This plugin has no explicit pluginId and will fall back to basename of file
|
||||
const KPluginMetaData validPlugin = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("jsonplugin_cmake_macro"));
|
||||
QVERIFY(validPlugin.isValid());
|
||||
QCOMPARE(plugins[0].pluginId(), QStringLiteral("jsonplugin_cmake_macro"));
|
||||
}
|
||||
|
||||
void testStaticPlugins()
|
||||
{
|
||||
#if defined(QT_SHARED)
|
||||
// in a static Qt build we can already have other built-in static plugins here
|
||||
QCOMPARE(QPluginLoader::staticPlugins().count(), 0);
|
||||
#endif
|
||||
const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("staticnamespace"));
|
||||
QCOMPARE(plugins.count(), 1);
|
||||
|
||||
QCOMPARE(plugins.first().description(), QStringLiteral("This is a plugin"));
|
||||
QCOMPARE(plugins.first().fileName(), QStringLiteral("staticnamespace/static_jsonplugin_cmake_macro"));
|
||||
}
|
||||
|
||||
void testPluginsWithoutMetaData()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
KPluginMetaData emptyMetaData(QStringLiteral("namespace/pluginwithoutmetadata"), KPluginMetaData::AllowEmptyMetaData);
|
||||
QVERIFY(emptyMetaData.isValid());
|
||||
QCOMPARE(emptyMetaData.pluginId(), QStringLiteral("pluginwithoutmetadata"));
|
||||
|
||||
const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData);
|
||||
QCOMPARE(plugins.count(), 3);
|
||||
for (auto plugin : plugins) {
|
||||
QVERIFY(plugin.isValid());
|
||||
if (plugin.pluginId() == QLatin1String("pluginwithoutmetadata")) {
|
||||
QVERIFY(plugin.rawData().isEmpty());
|
||||
} else if (plugin.pluginId() == QLatin1String("jsonplugin_cmake_macro") || plugin.pluginId() == QLatin1String("qtplugin")) {
|
||||
QVERIFY(!plugin.rawData().isEmpty());
|
||||
} else {
|
||||
QVERIFY2(false, "should not be reachable");
|
||||
}
|
||||
}
|
||||
const auto pluginInvalid = KPluginMetaData::findPluginById(QStringLiteral("namespace"), QStringLiteral("pluginwithoutmetadata"));
|
||||
const auto pluginValid = KPluginMetaData::findPluginById(QStringLiteral("namespace"), //
|
||||
QStringLiteral("pluginwithoutmetadata"),
|
||||
KPluginMetaData::AllowEmptyMetaData);
|
||||
QVERIFY(!pluginInvalid.isValid());
|
||||
QVERIFY(pluginValid.isValid());
|
||||
}
|
||||
|
||||
void testStaticPluginsWithoutMetadata()
|
||||
{
|
||||
QVERIFY(KPluginMetaData::findPlugins(QStringLiteral("staticnamespace3")).isEmpty());
|
||||
const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("staticnamespace3"), {}, KPluginMetaData::AllowEmptyMetaData);
|
||||
QCOMPARE(plugins.count(), 1);
|
||||
QVERIFY(plugins.first().isValid());
|
||||
QCOMPARE(plugins.first().pluginId(), QStringLiteral("static_plugin_without_metadata"));
|
||||
}
|
||||
|
||||
void testReverseDomainNotationPluginId()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
KPluginMetaData data(QStringLiteral("org.kde.test"));
|
||||
QVERIFY(data.isValid());
|
||||
QCOMPARE(data.pluginId(), QStringLiteral("org.kde.test"));
|
||||
}
|
||||
|
||||
// We can't use the plain pluginId as a classname, thus check if the replacement compiles and the original identifies it used for lookup
|
||||
void testReverseDomanNotationStaticPlugin()
|
||||
{
|
||||
KPluginMetaData data = KPluginMetaData::findPluginById(QStringLiteral("rdnstatic"), QStringLiteral("org.kde.test-staticplugin"));
|
||||
QVERIFY(data.isValid());
|
||||
QCOMPARE(data.pluginId(), QStringLiteral("org.kde.test-staticplugin"));
|
||||
}
|
||||
|
||||
void testFindingPluginInAppDirFirst()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
const QString originalPluginPath = QPluginLoader(QStringLiteral("namespace/jsonplugin_cmake_macro")).fileName();
|
||||
const QString pluginFileName = QFileInfo(originalPluginPath).fileName();
|
||||
const QString pluginNamespace = QStringLiteral("somepluginnamespace");
|
||||
const QString pluginAppDir = QCoreApplication::applicationDirPath() + QLatin1Char('/') + pluginNamespace;
|
||||
QDir(pluginAppDir).mkpath(QStringLiteral("."));
|
||||
const QString pluginAppPath = pluginAppDir + QLatin1Char('/') + pluginFileName;
|
||||
QFile::remove(pluginAppPath);
|
||||
|
||||
QVERIFY(QFile::copy(originalPluginPath, pluginAppPath));
|
||||
|
||||
QTemporaryDir temp;
|
||||
QVERIFY(temp.isValid());
|
||||
QDir dir(temp.path());
|
||||
QVERIFY(dir.mkdir(pluginNamespace));
|
||||
QVERIFY(dir.cd(pluginNamespace));
|
||||
|
||||
const QString pluginInNamespacePath = dir.absoluteFilePath(pluginFileName);
|
||||
QVERIFY(QFile::copy(originalPluginPath, pluginInNamespacePath));
|
||||
|
||||
LibraryPathRestorer restorer(QCoreApplication::libraryPaths());
|
||||
QCoreApplication::setLibraryPaths(QStringList() << temp.path());
|
||||
|
||||
// Our plugin in the applicationDirPath should come first
|
||||
const QString relativePathWithNamespace = QStringLiteral("somepluginnamespace/jsonplugin_cmake_macro");
|
||||
KPluginMetaData data(relativePathWithNamespace);
|
||||
QVERIFY(data.isValid());
|
||||
QCOMPARE(data.fileName(), pluginAppPath);
|
||||
|
||||
// The other one must be valid
|
||||
QVERIFY(KPluginMetaData(pluginInNamespacePath).isValid());
|
||||
// And after removing the plugin in the applicationDirPath, it should be found
|
||||
QVERIFY(QFile::remove(pluginAppPath));
|
||||
QCOMPARE(KPluginMetaData(relativePathWithNamespace).fileName(), pluginInNamespacePath);
|
||||
}
|
||||
|
||||
void testMetaDataQDebugOperator()
|
||||
{
|
||||
#if !defined(QT_SHARED)
|
||||
QSKIP("Dynamic plugin loading not supported with a static Qt build");
|
||||
#endif
|
||||
const auto list = KPluginMetaData::findPlugins(QStringLiteral("namespace"));
|
||||
qDebug() << list.first();
|
||||
qDebug() << list;
|
||||
qDebug() << (QList<KPluginMetaData>() << list << list << list);
|
||||
}
|
||||
void benchmarkFindPlugins()
|
||||
{
|
||||
QSKIP("Skipped by default");
|
||||
int loopIterations = 10;
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < loopIterations; ++i) {
|
||||
const auto plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData);
|
||||
Q_UNUSED(plugins)
|
||||
}
|
||||
}
|
||||
QList<KPluginMetaData> plugins =
|
||||
KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData | KPluginMetaData::CacheMetaData);
|
||||
QBENCHMARK {
|
||||
for (int i = 0; i < loopIterations; ++i) {
|
||||
plugins = KPluginMetaData::findPlugins(QStringLiteral("namespace"), {}, KPluginMetaData::AllowEmptyMetaData | KPluginMetaData::CacheMetaData);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(KPluginMetaDataTest)
|
||||
|
||||
#include "kpluginmetadatatest.moc"
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "plugins.h"
|
||||
|
||||
MyPlugin::MyPlugin(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
MyPlugin::~MyPlugin() = default;
|
||||
MyPlugin2::~MyPlugin2() = default;
|
||||
|
||||
MyPlugin2::MyPlugin2(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
#include "moc_plugins.cpp"
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include "plugin_classes_export.h"
|
||||
#include <QObject>
|
||||
|
||||
class PLUGIN_CLASSES_EXPORT MyPlugin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MyPlugin(QObject *parent);
|
||||
~MyPlugin() override;
|
||||
};
|
||||
|
||||
class PLUGIN_CLASSES_EXPORT MyPlugin2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MyPlugin2(QObject *parent);
|
||||
~MyPlugin2() override;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <kpluginfactory.h>
|
||||
|
||||
class PluginWithoutMetaData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// Add a default arg to make sure we do not get an ambiguity compiler error
|
||||
PluginWithoutMetaData(const QObject *, const QVariantList &args = {})
|
||||
: QObject()
|
||||
{
|
||||
Q_UNUSED(args)
|
||||
};
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS(PluginWithoutMetaData)
|
||||
|
||||
#include "pluginwithoutmetadata.moc"
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <QObject>
|
||||
#include <QtPlugin>
|
||||
|
||||
class MyQtPlugin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "somepluginid" FILE "data/jsonplugin2.json")
|
||||
};
|
||||
|
||||
#include "qtplugin.moc"
|
||||
@@ -0,0 +1,22 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "kpluginfactory.h"
|
||||
|
||||
class StaticSimplePluginClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Next to the assertion below, ensure that we have no ambiguity!
|
||||
explicit StaticSimplePluginClass(QObject *parent, const KPluginMetaData &data = {})
|
||||
: QObject(parent)
|
||||
{
|
||||
// We have added a default arg, but KPluginFactory should still provide the valid metadata instead of the default one
|
||||
Q_ASSERT(data.isValid());
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(StaticSimplePluginClass, "data/jsonplugin.json")
|
||||
|
||||
#include "statickpluginclass.moc"
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "kpluginfactory.h"
|
||||
|
||||
class OtherStaticSimplePluginClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OtherStaticSimplePluginClass(QObject *)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(OtherStaticSimplePluginClass, "data/jsonplugin.json")
|
||||
|
||||
#include "statickpluginclass_2.moc"
|
||||
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "kpluginfactory.h"
|
||||
|
||||
class AnotherStaticSimplePluginClass : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AnotherStaticSimplePluginClass(QObject *)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS_WITH_JSON(AnotherStaticSimplePluginClass, "data/jsonplugin.json")
|
||||
|
||||
#include "statickpluginclass_3.moc"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// SPDX-FileCopyrightText: 2021 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
|
||||
#include "kpluginfactory.h"
|
||||
|
||||
class StaticPluginWithoutMetaData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StaticPluginWithoutMetaData(QObject *)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_CLASS(StaticPluginWithoutMetaData)
|
||||
|
||||
#include "staticpluginwithoutmetadata.moc"
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
#include <QWidget>
|
||||
#include <kpluginfactory.h>
|
||||
|
||||
class MyWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit MyWidget(QWidget *parent, const KPluginMetaData &data)
|
||||
: QWidget(parent)
|
||||
{
|
||||
Q_ASSERT(!data.fileName().isEmpty());
|
||||
}
|
||||
};
|
||||
class MyWidgetArgs : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit MyWidgetArgs(QWidget *parent, const KPluginMetaData &data, const QVariantList &args)
|
||||
: QWidget(parent)
|
||||
{
|
||||
Q_ASSERT(!data.fileName().isEmpty());
|
||||
setProperty("firstarg", args.first());
|
||||
}
|
||||
};
|
||||
|
||||
class MyWidgetNoMetaData : public QWidget
|
||||
{
|
||||
public:
|
||||
explicit MyWidgetNoMetaData(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Ignore the duplicate registration, just make sure it compiles
|
||||
K_PLUGIN_FACTORY(MyWidgetFactory, registerPlugin<MyWidgetArgs>(); registerPlugin<MyWidget>(); registerPlugin<MyWidgetNoMetaData>();)
|
||||
|
||||
#include "widgetsplugin.moc"
|
||||
Reference in New Issue
Block a user