Skip to content

Commit 9f56913

Browse files
committed
test: unify test gate to BUILD_TESTING and add coverage target
1. Rename the test gate option from BUILD_TEST to BUILD_TESTING 2. Enable coverage instrumentation for test targets by default 3. Add coverage target that runs ctest and generates the lcov report Log: Unify the test gate to BUILD_TESTING and add a coverage target test: 统一测试门控为 BUILD_TESTING 并新增 coverage 目标 1. 将测试门控选项由 BUILD_TEST 重命名为 BUILD_TESTING 2. 默认开启测试目标的覆盖率插桩 3. 新增 coverage 目标,执行 ctest 并生成 lcov 报告 Log: 统一测试门控为 BUILD_TESTING,并新增 coverage 目标
1 parent 1788237 commit 9f56913

17 files changed

Lines changed: 73 additions & 223 deletions

‎CMakeLists.txt‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.10)
66

77
project(dde-launchpad VERSION 0.7.0)
88

9-
option(BUILD_TEST "Whether or not to build the tests" OFF)
9+
option(BUILD_TESTING "Whether or not to build the tests" OFF)
1010
option(CMAKE_EXPORT_COMPILE_COMMANDS "clangd support" ON)
1111

1212
set(CMAKE_CXX_STANDARD 17) # blurhash requires 17, otherwish we can still use 14
@@ -65,7 +65,7 @@ add_subdirectory(src/treelandintegration)
6565
add_subdirectory(src/models)
6666
add_subdirectory(qml/windowed)
6767

68-
if(BUILD_TEST)
68+
if(BUILD_TESTING)
6969
enable_testing()
7070
add_subdirectory(tests)
7171
endif()

‎tests/CMakeLists.txt‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@ find_package(Qt6Core CONFIG REQUIRED Private) # sortproxymodel.cpp uses QAb
88

99
qt_standard_project_setup()
1010

11+
# Coverage instrumentation (on by default so `coverage` produces a report).
12+
# Only the test-directory targets (OBJECT libs + test executables) are affected;
13+
# the production `launchpadcommon` shared library is left untouched.
14+
option(ENABLE_COVERAGE "Enable coverage instrumentation for tests" ON)
15+
if(ENABLE_COVERAGE)
16+
add_compile_options(--coverage)
17+
add_link_options(--coverage)
18+
endif()
19+
1120
# ---------------------------------------------------------------------------
1221
# Tested-source OBJECT libraries
1322
#
@@ -72,6 +81,14 @@ target_link_libraries(launchpad-test-utils PUBLIC
7281
macro(launchpad_add_tests)
7382
foreach(_testname ${ARGN})
7483
qt_add_executable(launchpad-${_testname} ${_testname}.cpp)
84+
# Test sources include the headers under test by bare name (e.g.
85+
# `#include "categoryutils.h"`), matching how src/ includes its own
86+
# headers — no `../src/...` relative paths.
87+
target_include_directories(launchpad-${_testname} PRIVATE
88+
${CMAKE_SOURCE_DIR}/src/models
89+
${CMAKE_SOURCE_DIR}/src/utils
90+
${CMAKE_SOURCE_DIR}/src/gioutils
91+
)
7592
# OBJECT libs first: their symbols take precedence over launchpadcommon.so,
7693
# so the gcov-instrumented copies are the ones that execute at test time.
7794
target_link_libraries(launchpad-${_testname} PRIVATE
@@ -83,6 +100,7 @@ macro(launchpad_add_tests)
83100
launchpadcommon
84101
)
85102
add_test(NAME launchpad-${_testname} COMMAND launchpad-${_testname})
103+
list(APPEND LAUNCHPAD_TEST_TARGETS launchpad-${_testname})
86104
endforeach()
87105
endmacro()
88106

@@ -103,3 +121,31 @@ launchpad_add_tests(
103121
iconutilstest
104122
itemspagemodeltest
105123
)
124+
125+
# ---- coverage: execute all tests and generate a coverage report ----
126+
# Produces: ${CMAKE_BINARY_DIR}/coverage_report/index.html
127+
find_program(LCOV_BIN lcov)
128+
find_program(GENHTML_BIN genhtml)
129+
set(_run_test_coverage "")
130+
if(LCOV_BIN AND GENHTML_BIN)
131+
set(_run_test_coverage
132+
COMMAND ${LCOV_BIN} --capture --directory ${CMAKE_BINARY_DIR}
133+
--rc lcov_branch_coverage=1 --output-file ${CMAKE_BINARY_DIR}/coverage.info
134+
COMMAND ${LCOV_BIN} --extract ${CMAKE_BINARY_DIR}/coverage.info
135+
--rc lcov_branch_coverage=1
136+
"${CMAKE_SOURCE_DIR}/src/models/*"
137+
"${CMAKE_SOURCE_DIR}/src/utils/*"
138+
"${CMAKE_SOURCE_DIR}/src/gioutils/*"
139+
--output-file ${CMAKE_BINARY_DIR}/coverage_filtered.info
140+
COMMAND ${GENHTML_BIN} ${CMAKE_BINARY_DIR}/coverage_filtered.info
141+
--branch-coverage --output-directory ${CMAKE_BINARY_DIR}/coverage_report
142+
)
143+
endif()
144+
add_custom_target(coverage
145+
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
146+
${_run_test_coverage}
147+
DEPENDS ${LAUNCHPAD_TEST_TARGETS}
148+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
149+
COMMENT "Run all tests and generate coverage report"
150+
VERBATIM
151+
)

‎tests/blurhashtest.cpp‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include <QTest>
6-
#include <QLoggingCategory>
76

8-
#include "../src/utils/blurhash.hpp"
7+
#include "blurhash.hpp"
98

109
#include <vector>
1110

1211
namespace {
13-
Q_LOGGING_CATEGORY(logTest, "dde.launchpad.test")
1412

1513
std::vector<unsigned char> solidImage(size_t width, size_t height,
1614
unsigned char r, unsigned char g, unsigned char b)
@@ -48,7 +46,6 @@ private slots:
4846

4947
void TestBlurhash::decodeEmptyStringReturnsEmpty()
5048
{
51-
qCInfo(logTest) << "Decoding an empty hash should return an empty image";
5249
const auto img = blurhash::decode(std::string_view{}, 8, 8);
5350
QVERIFY(img.image.empty());
5451
QCOMPARE(img.width, size_t(0));
@@ -57,14 +54,12 @@ void TestBlurhash::decodeEmptyStringReturnsEmpty()
5754

5855
void TestBlurhash::decodeInvalidHashReturnsEmpty()
5956
{
60-
qCInfo(logTest) << "Decoding a hash with invalid characters should return an empty image";
6157
const auto img = blurhash::decode(std::string_view("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"), 8, 8);
6258
QVERIFY(img.image.empty());
6359
}
6460

6561
void TestBlurhash::encodeRejectsInvalidInput()
6662
{
67-
qCInfo(logTest) << "encode should return an empty string for invalid parameters";
6863
std::vector<unsigned char> image = solidImage(4, 4, 255, 0, 0);
6964
QVERIFY(blurhash::encode(image.data(), 0, 4, 1, 1).empty()); // width 0
7065
QVERIFY(blurhash::encode(nullptr, 4, 4, 1, 1).empty()); // null image
@@ -74,7 +69,6 @@ void TestBlurhash::encodeRejectsInvalidInput()
7469

7570
void TestBlurhash::encodedHashHasExpectedLength()
7671
{
77-
qCInfo(logTest) << "encode should produce a hash of the documented length";
7872
std::vector<unsigned char> image = solidImage(8, 8, 128, 64, 200);
7973
QCOMPARE(blurhash::encode(image.data(), 8, 8, 1, 1).size(), size_t(6)); // no AC components
8074
QCOMPARE(blurhash::encode(image.data(), 8, 8, 1, 3).size(), expectedHashLength(1, 3)); // 10
@@ -84,7 +78,6 @@ void TestBlurhash::encodedHashHasExpectedLength()
8478

8579
void TestBlurhash::decodedImageHasExpectedDimensions()
8680
{
87-
qCInfo(logTest) << "decode should produce an image matching the requested dimensions";
8881
constexpr size_t width = 16, height = 16;
8982
std::vector<unsigned char> image = solidImage(width, height, 128, 64, 200);
9083
const std::string hash = blurhash::encode(image.data(), width, height, 2, 2);
@@ -98,7 +91,6 @@ void TestBlurhash::decodedImageHasExpectedDimensions()
9891

9992
void TestBlurhash::roundTripPreservesAverageColor()
10093
{
101-
qCInfo(logTest) << "Round-trip should preserve the average color (DC component)";
10294
constexpr size_t width = 16, height = 16;
10395
const unsigned char r = 128, g = 64, b = 200;
10496
std::vector<unsigned char> image = solidImage(width, height, r, g, b);
@@ -125,7 +117,6 @@ void TestBlurhash::roundTripPreservesAverageColor()
125117

126118
void TestBlurhash::decodeWithFourBytesPerPixel()
127119
{
128-
qCInfo(logTest) << "decode with bytesPerPixel=4 should produce a larger buffer";
129120
constexpr size_t width = 16, height = 16;
130121
std::vector<unsigned char> image = solidImage(width, height, 128, 64, 200);
131122
const std::string hash = blurhash::encode(image.data(), width, height, 3, 3);
@@ -142,15 +133,13 @@ void TestBlurhash::decodeWithFourBytesPerPixel()
142133

143134
void TestBlurhash::decodeTooShortHashReturnsEmpty()
144135
{
145-
qCInfo(logTest) << "decode with a hash shorter than 10 chars should return empty";
146136
// hash must be >= 10 chars (1 + 1 + 4 + at least 4 for 2x2 components - 1 AC = 2 bytes -> 8, but 10 is the minimum check)
147137
const auto img = blurhash::decode(std::string_view("short"), 8, 8);
148138
QVERIFY(img.image.empty());
149139
}
150140

151141
void TestBlurhash::decodeWrongSizeForComponentsReturnsEmpty()
152142
{
153-
qCInfo(logTest) << "decode with a hash whose length doesn't match the component count should return empty";
154143
// 1 component char says 4x4 = 16 components -> needs 1+1+4+(16-1)*2 = 36 chars
155144
// but we'll give only 12 chars
156145
const auto img = blurhash::decode(std::string_view("LFE.}?a]a]a]a]a]a]a]"), 8, 8);

‎tests/categoryutilstest.cpp‎

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include <QTest>
6-
#include <QLoggingCategory>
76

8-
#include "../src/utils/categoryutils.h"
7+
#include "categoryutils.h"
98

109
namespace {
11-
Q_LOGGING_CATEGORY(logTest, "dde.launchpad.test")
1210

1311
using C = CategoryUtils::Categorytype;
1412
}
@@ -47,7 +45,6 @@ private slots:
4745

4846
void TestCategoryUtils::parseDDECategoryStringKnown()
4947
{
50-
qCInfo(logTest) << "parseDDECategoryString should map the known DDE category names";
5148
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("music"))), int(C::CategoryMusic));
5249
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("internet"))), int(C::CategoryInternet));
5350
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("development"))), int(C::CategoryDevelopment));
@@ -56,22 +53,19 @@ void TestCategoryUtils::parseDDECategoryStringKnown()
5653

5754
void TestCategoryUtils::parseDDECategoryStringUnknown()
5855
{
59-
qCInfo(logTest) << "Unknown DDE category strings should yield CategoryErr";
6056
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("unknown"))), int(C::CategoryErr));
6157
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("Music"))), int(C::CategoryErr)); // case sensitive
6258
}
6359

6460
void TestCategoryUtils::parseXdgCategoryStringSingle()
6561
{
66-
qCInfo(logTest) << "parseXdgCategoryString should map single-valued XDG categories";
6762
const auto result = CategoryUtils::parseXdgCategoryString(QStringLiteral("webbrowser"));
6863
QCOMPARE(result.size(), 1);
6964
QVERIFY(result.contains(C::CategoryInternet));
7065
}
7166

7267
void TestCategoryUtils::parseXdgCategoryStringMultiValued()
7368
{
74-
qCInfo(logTest) << "audiovideo should map to both music and video";
7569
const auto result = CategoryUtils::parseXdgCategoryString(QStringLiteral("audiovideo"));
7670
QCOMPARE(result.size(), 2);
7771
QVERIFY(result.contains(C::CategoryMusic));
@@ -80,61 +74,52 @@ void TestCategoryUtils::parseXdgCategoryStringMultiValued()
8074

8175
void TestCategoryUtils::parseXdgCategoryStringUnknown()
8276
{
83-
qCInfo(logTest) << "Unknown XDG category strings should yield an empty list";
8477
const auto result = CategoryUtils::parseXdgCategoryString(QStringLiteral("totally-unknown-category"));
8578
QVERIFY(result.isEmpty());
8679
}
8780

8881
void TestCategoryUtils::bestMatchedSingleCategory()
8982
{
90-
qCInfo(logTest) << "A single matching category should win";
9183
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("music")})), int(C::CategoryMusic));
9284
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("network")})), int(C::CategoryInternet));
9385
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("spreadsheet")})), int(C::CategoryOffice));
9486
}
9587

9688
void TestCategoryUtils::bestMatchedEmptyReturnsOthers()
9789
{
98-
qCInfo(logTest) << "An empty category list should be classified as others";
9990
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({})), int(C::CategoryOthers));
10091
}
10192

10293
void TestCategoryUtils::bestMatchedOthersOnlyReturnsOthers()
10394
{
104-
qCInfo(logTest) << "An only-others list should be classified as others";
10595
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("others")})), int(C::CategoryOthers));
10696
}
10797

10898
void TestCategoryUtils::bestMatchedMusicVideoTiePrefersVideo()
10999
{
110-
qCInfo(logTest) << "A music/video tie should prefer video";
111100
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("music"), QStringLiteral("video")})),
112101
int(C::CategoryVideo));
113102
}
114103

115104
void TestCategoryUtils::bestMatchedPlayerMapsToVideo()
116105
{
117-
qCInfo(logTest) << "player (maps to music+video) should resolve to video";
118106
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("player")})), int(C::CategoryVideo));
119107
}
120108

121109
void TestCategoryUtils::bestMatchedMaxCountWins()
122110
{
123-
qCInfo(logTest) << "The category with the highest count should win";
124111
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("game"), QStringLiteral("game"), QStringLiteral("music")})),
125112
int(C::CategoryGame));
126113
}
127114

128115
void TestCategoryUtils::bestMatchedMultipleInternet()
129116
{
130-
qCInfo(logTest) << "Several internet categories should aggregate into internet";
131117
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("webbrowser"), QStringLiteral("email")})),
132118
int(C::CategoryInternet));
133119
}
134120

135121
void TestCategoryUtils::parseDDECategoryStringAllKnown()
136122
{
137-
qCInfo(logTest) << "parseDDECategoryString should map all known DDE category names";
138123
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("chat"))), int(C::CategoryChat));
139124
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("video"))), int(C::CategoryVideo));
140125
QCOMPARE(int(CategoryUtils::parseDDECategoryString(QStringLiteral("graphics"))), int(C::CategoryGraphics));
@@ -146,7 +131,6 @@ void TestCategoryUtils::parseDDECategoryStringAllKnown()
146131

147132
void TestCategoryUtils::parseXdgCategoryStringMultipleDistinctCategories()
148133
{
149-
qCInfo(logTest) << "parseXdgCategoryString for several distinct known categories";
150134
// Each call returns the list for that single string; verify a batch of them
151135
QCOMPARE(CategoryUtils::parseXdgCategoryString(QStringLiteral("ide")).size(), 1);
152136
QVERIFY(CategoryUtils::parseXdgCategoryString(QStringLiteral("ide")).contains(C::CategoryDevelopment));
@@ -164,15 +148,13 @@ void TestCategoryUtils::parseXdgCategoryStringMultipleDistinctCategories()
164148

165149
void TestCategoryUtils::parseXdgCategoryStringAudioMapsToMusic()
166150
{
167-
qCInfo(logTest) << "audio should map to music";
168151
const auto result = CategoryUtils::parseXdgCategoryString(QStringLiteral("audio"));
169152
QCOMPARE(result.size(), 1);
170153
QVERIFY(result.contains(C::CategoryMusic));
171154
}
172155

173156
void TestCategoryUtils::bestMatchedDDECategoryWinsOverXdg()
174157
{
175-
qCInfo(logTest) << "A DDE category name should be matched before falling to XDG parsing";
176158
// 'music' is a DDE name -> CategoryMusic (via parseDDECategoryString)
177159
// 'audio' is an XDG name -> CategoryMusic (via parseXdgCategoryString)
178160
// mixing them should still yield Music
@@ -182,7 +164,6 @@ void TestCategoryUtils::bestMatchedDDECategoryWinsOverXdg()
182164

183165
void TestCategoryUtils::bestMatchedGameVariants()
184166
{
185-
qCInfo(logTest) << "Various game XDG categories should aggregate into game";
186167
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("actiongame"), QStringLiteral("arcadegame")})),
187168
int(C::CategoryGame));
188169
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("boardgame"), QStringLiteral("cardgame"), QStringLiteral("puzzlegame")})),
@@ -191,7 +172,6 @@ void TestCategoryUtils::bestMatchedGameVariants()
191172

192173
void TestCategoryUtils::bestMatchedDevelopmentVariants()
193174
{
194-
qCInfo(logTest) << "Development XDG categories should aggregate into development";
195175
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("ide"), QStringLiteral("debugger")})),
196176
int(C::CategoryDevelopment));
197177
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("building"), QStringLiteral("revisioncontrol")})),
@@ -200,14 +180,12 @@ void TestCategoryUtils::bestMatchedDevelopmentVariants()
200180

201181
void TestCategoryUtils::bestMatchedReadingVariants()
202182
{
203-
qCInfo(logTest) << "Reading XDG categories should aggregate into reading";
204183
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("news"), QStringLiteral("translation")})),
205184
int(C::CategoryReading));
206185
}
207186

208187
void TestCategoryUtils::bestMatchedSystemVariants()
209188
{
210-
qCInfo(logTest) << "System XDG categories should aggregate into system";
211189
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("desktopsettings"), QStringLiteral("packagemanager")})),
212190
int(C::CategorySystem));
213191
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("terminalemulator"), QStringLiteral("filemanager")})),
@@ -216,7 +194,6 @@ void TestCategoryUtils::bestMatchedSystemVariants()
216194

217195
void TestCategoryUtils::bestMatchedGraphicsVariants()
218196
{
219-
qCInfo(logTest) << "Graphics XDG categories should aggregate into graphics";
220197
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("2dgraphics"), QStringLiteral("rastergraphics")})),
221198
int(C::CategoryGraphics));
222199
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("photography"), QStringLiteral("viewer")})),
@@ -225,14 +202,12 @@ void TestCategoryUtils::bestMatchedGraphicsVariants()
225202

226203
void TestCategoryUtils::bestMatchedChatVariants()
227204
{
228-
qCInfo(logTest) << "Chat XDG categories should aggregate into chat";
229205
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("instantmessaging"), QStringLiteral("contactmanagement")})),
230206
int(C::CategoryChat));
231207
}
232208

233209
void TestCategoryUtils::bestMatchedOfficeVariants()
234210
{
235-
qCInfo(logTest) << "Office XDG categories should aggregate into office";
236211
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("spreadsheet"), QStringLiteral("presentation")})),
237212
int(C::CategoryOffice));
238213
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("science"), QStringLiteral("math")})),
@@ -241,7 +216,6 @@ void TestCategoryUtils::bestMatchedOfficeVariants()
241216

242217
void TestCategoryUtils::bestMatchedMixedUnknownAndKnown()
243218
{
244-
qCInfo(logTest) << "Unknown categories mixed with known ones should ignore unknowns";
245219
// 'totally-unknown' yields CategoryErr from DDE and empty from XDG -> ignored
246220
// 'music' yields CategoryMusic -> should win
247221
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("totally-unknown"), QStringLiteral("music")})),
@@ -250,14 +224,12 @@ void TestCategoryUtils::bestMatchedMixedUnknownAndKnown()
250224

251225
void TestCategoryUtils::bestMatchedAudioVideoEditingTie()
252226
{
253-
qCInfo(logTest) << "audiovideoediting maps to both music and video -> tie -> video";
254227
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("audiovideoediting")})),
255228
int(C::CategoryVideo));
256229
}
257230

258231
void TestCategoryUtils::bestMatchedRecorderTie()
259232
{
260-
qCInfo(logTest) << "recorder maps to both music and video -> tie -> video";
261233
QCOMPARE(int(CategoryUtils::parseBestMatchedCategory({QStringLiteral("recorder")})),
262234
int(C::CategoryVideo));
263235
}

0 commit comments

Comments
 (0)