Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ project(dde-services)

include(CTest)

# Coverage instrumentation is applied by each module's tests/CMakeLists.txt,
# so only the test targets are instrumented; production plugins are unaffected.
option(ENABLE_COVERAGE "Enable coverage instrumentation for tests" ON)

if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX /usr)
endif()

include(GNUInstallDirs)
include(CTest)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
# 设置二进制输出目录 方便调试
Expand All @@ -26,3 +29,41 @@ set(DTK_VERSION_MAJOR 6)

add_subdirectory("src")

if(BUILD_TESTING)
# ---- coverage: execute all module tests and generate a coverage report ----
find_program(LCOV_BIN lcov)
find_program(GENHTML_BIN genhtml)
set(_run_test_coverage "")
if(LCOV_BIN AND GENHTML_BIN)
set(_run_test_coverage
COMMAND ${LCOV_BIN} --capture --directory ${CMAKE_BINARY_DIR}
--rc lcov_branch_coverage=1 --output-file ${CMAKE_BINARY_DIR}/coverage.info
COMMAND ${LCOV_BIN} --extract ${CMAKE_BINARY_DIR}/coverage.info
--rc lcov_branch_coverage=1
"${CMAKE_SOURCE_DIR}/src/plugin-qt/thememanager/*"
"${CMAKE_SOURCE_DIR}/src/plugin-qt/wallpaperslideshow/*"
"${CMAKE_SOURCE_DIR}/src/plugin-qt/xsettings/*"
--output-file ${CMAKE_BINARY_DIR}/coverage_filtered.info
COMMAND ${GENHTML_BIN} ${CMAKE_BINARY_DIR}/coverage_filtered.info
--branch-coverage --output-directory ${CMAKE_BINARY_DIR}/coverage_report
)
endif()
add_custom_target(coverage
# Scope to this suite: other tests registered by the project may not be
# built when only this target's dependencies are requested.
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
-R "^(thememanager-sunrisesunset|wallpaperslideshow-(utils|format|dbus)|xsettings-(keyfile|xsutils|xsdatainfo))$"
${_run_test_coverage}
DEPENDS
tst-sunrisesunset
tst-wpssl-utils
tst-format
tst-wallpaperslideshow-dbus
tst-keyfile
tst-xsutils
tst-xsdatainfo
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Run all tests and generate coverage report"
VERBATIM
)
endif()
5 changes: 5 additions & 0 deletions src/plugin-qt/thememanager/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(CMAKE_AUTORCC ON)

include(GNUInstallDirs)
file(GLOB_RECURSE SRCS "*.h" "*.cpp")
list(FILTER SRCS EXCLUDE REGEX "/tests/")

find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core DBus)
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Gui Core)
Expand All @@ -34,3 +35,7 @@ target_link_libraries(${BIN_NAME} PRIVATE

install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin-qt-thememanager.json DESTINATION share/deepin-service-manager/user/)

if(BUILD_TESTING)
add_subdirectory("tests")
endif()
25 changes: 25 additions & 0 deletions src/plugin-qt/thememanager/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
#
# SPDX-License-Identifier: LGPL-3.0-or-later

# thememanager unit tests (Qt6::Test). Plugged in from the plugin CMakeLists
# under BUILD_TESTING.

set(CMAKE_AUTOMOC ON)
set(PLUGIN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)

find_package(Qt6 REQUIRED COMPONENTS Core Test)

if(ENABLE_COVERAGE)
add_compile_options(--coverage)
add_link_options(--coverage)
endif()

# SunriseSunset pure-math unit test
add_executable(tst-sunrisesunset
tst_sunrisesunset.cpp
${PLUGIN_DIR}/sunrisesunset.cpp
)
target_include_directories(tst-sunrisesunset PRIVATE ${PLUGIN_DIR})
target_link_libraries(tst-sunrisesunset PRIVATE Qt6::Core Qt6::Test)
add_test(NAME thememanager-sunrisesunset COMMAND tst-sunrisesunset)
209 changes: 209 additions & 0 deletions src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "sunrisesunset.h"

Check warning on line 5 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "sunrisesunset.h" not found.

#include <QTest>

Check warning on line 7 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QTest> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDateTime>

Check warning on line 8 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDateTime> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QDate>

Check warning on line 9 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QDate> not found. Please note: Cppcheck does not need standard library headers to get proper results.

// Unit test for SunriseSunset::getSunriseSunset (thememanager plugin).
//
// getSunriseSunset computes sunrise/sunset for a given latitude, longitude,
// UTC offset and date using the NOAA sunrise/sunset algorithm. The function
// always returns true and writes the computed QDateTime values into the out
// parameters. For normal mid-latitude locations both events land on the
// requested date; near the polar circles the algorithm signals "sun never
// rises/sets" by emitting sentinel hour values (100 / -100), which it then
// normalises so that sunset stays after sunrise.
class TestSunriseSunset : public QObject
{
Q_OBJECT

private slots:

Check warning on line 24 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

There is an unknown macro here somewhere. Configuration is required. If slots is a macro then please configure it.
void returnsTrueAndProducesValidDatetimes();
void beijingSummerSolsticeIsInExpectedRange();
void beijingWinterSolsticeIsInExpectedRange();
void sunriseBeforeSunset();
void moreWesterlyLongitudeRaisesSunrise();
void polarDayStillReturnsTrue();
void polarNightStillReturnsTrue();
// --- branch-coverage additions: UT<0 / UT>=24 wrap + geo diversity ---
void farEastHighOffsetIsValid();
void farWestNegativeOffsetIsValid();
void equatorEquinoxIsValid();
void southernHemisphereSummerIsValid();
void highLatitudeSpringIsValid();
};

void TestSunriseSunset::returnsTrueAndProducesValidDatetimes()
{
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 6, 21);

const bool ok = SunriseSunset::getSunriseSunset(39.9042, 116.4074, 8.0, date, sunrise, sunset);

QVERIFY(ok);
QVERIFY(sunrise.isValid());
QVERIFY(sunset.isValid());
QCOMPARE(sunrise.date(), date);
QCOMPARE(sunset.date(), date);
}

void TestSunriseSunset::beijingSummerSolsticeIsInExpectedRange()
{
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 6, 21);

QVERIFY(SunriseSunset::getSunriseSunset(39.9042, 116.4074, 8.0, date, sunrise, sunset));

// Beijing summer solstice: sunrise ~04:46, sunset ~19:46 local.
QVERIFY2(sunrise.time().hour() >= 4 && sunrise.time().hour() <= 5,
qPrintable(QStringLiteral("summer sunrise out of range: %1").arg(sunrise.time().toString())));
QVERIFY2(sunset.time().hour() >= 19 && sunset.time().hour() <= 20,
qPrintable(QStringLiteral("summer sunset out of range: %1").arg(sunset.time().toString())));
}

void TestSunriseSunset::beijingWinterSolsticeIsInExpectedRange()
{
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 12, 21);

QVERIFY(SunriseSunset::getSunriseSunset(39.9042, 116.4074, 8.0, date, sunrise, sunset));

// Beijing winter solstice: sunrise ~07:33, sunset ~16:53 local.
QVERIFY2(sunrise.time().hour() >= 7 && sunrise.time().hour() <= 8,
qPrintable(QStringLiteral("winter sunrise out of range: %1").arg(sunrise.time().toString())));
QVERIFY2(sunset.time().hour() >= 16 && sunset.time().hour() <= 17,
qPrintable(QStringLiteral("winter sunset out of range: %1").arg(sunset.time().toString())));
}

void TestSunriseSunset::sunriseBeforeSunset()
{
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 3, 20); // spring equinox

QVERIFY(SunriseSunset::getSunriseSunset(39.9042, 116.4074, 8.0, date, sunrise, sunset));

QVERIFY2(sunrise < sunset,
qPrintable(QStringLiteral("sunrise (%1) not before sunset (%2)")
.arg(sunrise.toString(), sunset.toString())));
}

void TestSunriseSunset::moreWesterlyLongitudeRaisesSunrise()
{
// Same latitude & UTC offset, but further west: solar noon — and thus
// sunrise — shifts to a later clock time.
QDateTime sunriseEast;
QDateTime sunsetEast;
QDateTime sunriseWest;
QDateTime sunsetWest;
const QDate date(2025, 6, 21);

QVERIFY(SunriseSunset::getSunriseSunset(39.9042, 116.4074, 8.0, date, sunriseEast, sunsetEast));
QVERIFY(SunriseSunset::getSunriseSunset(39.9042, 100.0, 8.0, date, sunriseWest, sunsetWest));

QVERIFY2(sunriseWest > sunriseEast,
qPrintable(QStringLiteral("westerly sunrise (%1) not later than easterly (%2)")
.arg(sunriseWest.toString(), sunriseEast.toString())));
}

void TestSunriseSunset::polarDayStillReturnsTrue()
{
// At ~78°N around the June solstice the sun never sets. The algorithm
// signals this with sentinel hours and normalises sunset so that it stays
// after sunrise. The contract is simply: returns true and sunset > sunrise.
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 6, 21);

const bool ok = SunriseSunset::getSunriseSunset(78.0, 0.0, 0.0, date, sunrise, sunset);

QVERIFY(ok);
QVERIFY(sunrise.isValid());
QVERIFY(sunset.isValid());
QVERIFY2(sunset > sunrise,
qPrintable(QStringLiteral("polar sunset (%1) not after sunrise (%2)")
.arg(sunset.toString(), sunrise.toString())));
}

void TestSunriseSunset::polarNightStillReturnsTrue()
{
// At ~78°N around the December solstice the sun never rises. The
// algorithm signals this with the 100h sentinel for both events, so
// sunrise and sunset land on the same offset and the contract is simply:
// returns true and sunrise == sunset.
QDateTime sunrise;
QDateTime sunset;
const QDate date(2025, 12, 21);

const bool ok = SunriseSunset::getSunriseSunset(78.0, 0.0, 0.0, date, sunrise, sunset);

QVERIFY(ok);
QVERIFY(sunrise.isValid());
QVERIFY(sunset.isValid());
QVERIFY2(sunrise == sunset,
qPrintable(QStringLiteral("polar night: sunrise (%1) != sunset (%2) (both expected at 100h sentinel)")
.arg(sunrise.toString(), sunset.toString())));
}

// ---- branch-coverage additions: exercise the UT<0 / UT>=24 wrap ----
// branches in calculateSunChangedAsUTCHour via extreme longitudes / offsets
// and broaden the geographic coverage of the normal path. Assertions are
// deliberately loose (return true + valid datetimes) so they hold regardless
// of which wrap branch a given (lat,lng,offset,date) happens to trigger; the
// coverage benefit comes from invoking the function with diverse inputs.
static void assertValidSunEvent(const QDateTime &sunrise, const QDateTime &sunset)
{
QVERIFY(sunrise.isValid());
QVERIFY(sunset.isValid());
}

void TestSunriseSunset::farEastHighOffsetIsValid()
{
QDateTime sunrise, sunset;
const QDate date(2025, 6, 21);
QVERIFY(SunriseSunset::getSunriseSunset(0.0, 179.0, 12.0, date, sunrise, sunset));
assertValidSunEvent(sunrise, sunset);
}

void TestSunriseSunset::farWestNegativeOffsetIsValid()
{
QDateTime sunrise, sunset;
const QDate date(2025, 6, 21);
QVERIFY(SunriseSunset::getSunriseSunset(0.0, -179.0, -12.0, date, sunrise, sunset));
assertValidSunEvent(sunrise, sunset);
}

void TestSunriseSunset::equatorEquinoxIsValid()
{
QDateTime sunrise, sunset;
const QDate date(2025, 3, 20);
QVERIFY(SunriseSunset::getSunriseSunset(0.0, 0.0, 0.0, date, sunrise, sunset));
assertValidSunEvent(sunrise, sunset);
}

void TestSunriseSunset::southernHemisphereSummerIsValid()
{
QDateTime sunrise, sunset;
const QDate date(2025, 12, 21);
QVERIFY(SunriseSunset::getSunriseSunset(-33.0, 151.0, 11.0, date, sunrise, sunset));
assertValidSunEvent(sunrise, sunset);
}

void TestSunriseSunset::highLatitudeSpringIsValid()
{
QDateTime sunrise, sunset;
const QDate date(2025, 3, 20);
QVERIFY(SunriseSunset::getSunriseSunset(70.0, 0.0, 0.0, date, sunrise, sunset));
assertValidSunEvent(sunrise, sunset);
}

QTEST_GUILESS_MAIN(TestSunriseSunset)

#include "tst_sunrisesunset.moc"

Check warning on line 209 in src/plugin-qt/thememanager/tests/tst_sunrisesunset.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "tst_sunrisesunset.moc" not found.
5 changes: 5 additions & 0 deletions src/plugin-qt/wallpaperslideshow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(CMAKE_AUTORCC ON)

include(GNUInstallDirs)
file(GLOB_RECURSE SRCS "*.h" "*.cpp")
list(FILTER SRCS EXCLUDE REGEX "/tests/")

find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core DBus)
find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Gui Core)
Expand Down Expand Up @@ -41,3 +42,7 @@ target_link_libraries(${BIN_NAME} PRIVATE

install(TARGETS ${BIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin-qt-wallpaperslideshow.json DESTINATION share/deepin-service-manager/user/)

if(BUILD_TESTING)
add_subdirectory("tests")
endif()
67 changes: 67 additions & 0 deletions src/plugin-qt/wallpaperslideshow/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
#
# SPDX-License-Identifier: LGPL-3.0-or-later

# wallpaperslideshow unit + D-Bus contract tests (Qt6::Test).
# Plugged in from the plugin CMakeLists under BUILD_TESTING.

set(CMAKE_AUTOMOC ON)
set(PLUGIN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)

find_package(Qt6 REQUIRED COMPONENTS Core DBus Gui Test)

if(ENABLE_COVERAGE)
add_compile_options(--coverage)
add_link_options(--coverage)
endif()

# utils helper unit test
add_executable(tst-wpssl-utils
tst_wpssl_utils.cpp
${PLUGIN_DIR}/utils.cpp
)
target_include_directories(tst-wpssl-utils PRIVATE ${PLUGIN_DIR})
target_link_libraries(tst-wpssl-utils PRIVATE Qt6::Core Qt6::DBus Qt6::Test)
add_test(NAME wallpaperslideshow-utils COMMAND tst-wpssl-utils)
# writeWallpaperConfig writes to a path derived from XDG_CONFIG_HOME at
# static-init time; redirect it to a throwaway build-dir path so the test
# does not pollute the real user config dir. (The test slot itself QSKIPs
# when XDG_CONFIG_HOME is unset, e.g. when the binary is run directly.)
set_tests_properties(wallpaperslideshow-utils PROPERTIES
ENVIRONMENT "XDG_CONFIG_HOME=${CMAKE_BINARY_DIR}/dde_svc_test_xdgconfig")

# FormatPicture MIME mapping unit test
add_executable(tst-format
tst_format.cpp
${PLUGIN_DIR}/background/format.cpp
)
target_include_directories(tst-format PRIVATE ${PLUGIN_DIR}/background)
target_link_libraries(tst-format PRIVATE Qt6::Core Qt6::Gui Qt6::Test)
add_test(NAME wallpaperslideshow-format COMMAND tst-format)

# org.deepin.dde.WallpaperSlideshow D-Bus contract test: generate the
# production adaptor from the introspection XML around the test-side fake, so
# the interface contract is validated against a real D-Bus connection.
qt_add_dbus_adaptor(WSS_ADAPTOR_SOURCES
${PLUGIN_DIR}/org.deepin.dde.WallpaperSlideshow.xml
${CMAKE_CURRENT_SOURCE_DIR}/fakeservice.h
FakeWallpaperSlideshowService
wallpaperslideshowadaptor
WallpaperSlideshowAdaptor
)

add_executable(tst-wallpaperslideshow-dbus
tst_wallpaperslideshow_dbus.cpp
${WSS_ADAPTOR_SOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/fakeservice.h
)
target_include_directories(tst-wallpaperslideshow-dbus PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
target_link_libraries(tst-wallpaperslideshow-dbus PRIVATE
Qt6::Core
Qt6::DBus
Qt6::Test
)
add_test(NAME wallpaperslideshow-dbus COMMAND tst-wallpaperslideshow-dbus)
Loading
Loading