diff --git a/tests/data.qrc b/tests/data.qrc
index b271332..8f2b8eb 100644
--- a/tests/data.qrc
+++ b/tests/data.qrc
@@ -11,5 +11,8 @@
data/LGPLv3.txt
data/example-license.json
data/dconf-example.override.noexistitem.json
+ data/dconfig2cpp/basic-types.meta.json
+ data/dconfig2cpp/numeric-types.meta.json
+ data/dconfig2cpp/complex-types.meta.json
diff --git a/tests/data/dconfig2cpp/basic-types.meta.json b/tests/data/dconfig2cpp/basic-types.meta.json
new file mode 100644
index 0000000..9c46c44
--- /dev/null
+++ b/tests/data/dconfig2cpp/basic-types.meta.json
@@ -0,0 +1,86 @@
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "booleanTrue": {
+ "value": true,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Boolean true value",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "booleanFalse": {
+ "value": false,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Boolean false value",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "stringValue": {
+ "value": "hello world",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "String value",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "emptyString": {
+ "value": "",
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Empty string value",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "integerPositive": {
+ "value": 42,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Positive integer",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "integerNegative": {
+ "value": -10,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Negative integer",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "integerZero": {
+ "value": 0,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Zero integer",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "doubleValue": {
+ "value": 3.14,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Double value with decimal",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "doubleZero": {
+ "value": 0.0,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Double zero with decimal point",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "scientificValue": {
+ "value": 1.5e3,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Scientific notation value",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/data/dconfig2cpp/complex-types.meta.json b/tests/data/dconfig2cpp/complex-types.meta.json
new file mode 100644
index 0000000..85e8d38
--- /dev/null
+++ b/tests/data/dconfig2cpp/complex-types.meta.json
@@ -0,0 +1,46 @@
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "emptyArray": {
+ "value": [],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Empty array",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "stringArray": {
+ "value": ["item1", "item2", "item3"],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "String array",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "mixedArray": {
+ "value": ["string", 42, true, 3.14],
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Mixed type array",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "emptyObject": {
+ "value": {},
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Empty object",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "simpleObject": {
+ "value": {"key1": "value1", "key2": "value2"},
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Simple object",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/data/dconfig2cpp/numeric-types.meta.json b/tests/data/dconfig2cpp/numeric-types.meta.json
new file mode 100644
index 0000000..2ec7ce8
--- /dev/null
+++ b/tests/data/dconfig2cpp/numeric-types.meta.json
@@ -0,0 +1,46 @@
+{
+ "magic": "dsg.config.meta",
+ "version": "1.0",
+ "contents": {
+ "pureInteger": {
+ "value": 123,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Pure integer - should be int/qint64",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "doubleWithDecimal": {
+ "value": 123.45,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Double with decimal - should be double",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "doubleWithZeroDecimal": {
+ "value": 123.0,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Double with .0 - should be double",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "scientificNotation": {
+ "value": 1.0e0,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Scientific notation - should be double",
+ "permissions": "readwrite",
+ "visibility": "public"
+ },
+ "largeInteger": {
+ "value": 9223372036854775807,
+ "serial": 0,
+ "flags": ["global"],
+ "name": "Large integer - should be qint64",
+ "permissions": "readwrite",
+ "visibility": "public"
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ut_dconfig2cpp.cpp b/tests/ut_dconfig2cpp.cpp
new file mode 100644
index 0000000..05cbd7f
--- /dev/null
+++ b/tests/ut_dconfig2cpp.cpp
@@ -0,0 +1,250 @@
+// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class ut_dconfig2cpp : public testing::Test
+{
+protected:
+ void SetUp() override {
+ tempDir = new QTemporaryDir();
+ ASSERT_TRUE(tempDir->isValid());
+ }
+
+ void TearDown() override {
+ delete tempDir;
+ tempDir = nullptr;
+ }
+
+ // Get dconfig2cpp tool path
+ QString getToolPath() const {
+ QStringList possiblePaths = {
+ "../tools/dconfig2cpp/dconfig2cpp",
+ "../../tools/dconfig2cpp/dconfig2cpp",
+ "./tools/dconfig2cpp/dconfig2cpp",
+ "tools/dconfig2cpp/dconfig2cpp"
+ };
+
+ for (const QString &path : possiblePaths) {
+ if (QFile::exists(path)) {
+ return path;
+ }
+ }
+ return "dconfig2cpp"; // Assume it's in PATH
+ }
+
+ // Call dconfig2cpp to generate code
+ struct GenerationResult {
+ bool success;
+ QString generatedFilePath;
+ QString errorMessage;
+ int exitCode;
+ };
+
+ GenerationResult generateCode(const QString& jsonFilePath) {
+ GenerationResult result;
+ result.success = false;
+ result.exitCode = -1;
+
+ // If it's a QRC path, copy to temporary directory first
+ QString actualJsonPath = jsonFilePath;
+ if (jsonFilePath.startsWith(":/")) {
+ QFile sourceFile(jsonFilePath);
+ if (!sourceFile.open(QIODevice::ReadOnly)) {
+ result.errorMessage = QString("Failed to open resource file: %1").arg(jsonFilePath);
+ return result;
+ }
+
+ QFileInfo fileInfo(jsonFilePath);
+ QString tempJsonPath = tempDir->path() + "/" + fileInfo.fileName();
+
+ QFile tempFile(tempJsonPath);
+ if (!tempFile.open(QIODevice::WriteOnly)) {
+ result.errorMessage = QString("Failed to create temp file: %1").arg(tempJsonPath);
+ return result;
+ }
+
+ tempFile.write(sourceFile.readAll());
+ tempFile.close();
+ sourceFile.close();
+
+ actualJsonPath = tempJsonPath;
+ }
+
+ if (!QFile::exists(actualJsonPath)) {
+ result.errorMessage = QString("Input JSON file does not exist: %1").arg(actualJsonPath);
+ return result;
+ }
+
+ QString toolPath = getToolPath();
+ QProcess process;
+
+ QFileInfo fileInfo(actualJsonPath);
+ QString baseName = fileInfo.completeBaseName().replace('.', '_').replace('-', '_');
+ result.generatedFilePath = tempDir->path() + "/" + baseName + ".hpp";
+
+ QStringList arguments;
+ arguments << "-o" << result.generatedFilePath << actualJsonPath;
+
+ process.start(toolPath, arguments);
+
+ if (!process.waitForStarted(5000)) {
+ result.errorMessage = QString("Failed to start dconfig2cpp tool: %1").arg(process.errorString());
+ return result;
+ }
+
+ if (!process.waitForFinished(10000)) {
+ result.errorMessage = "dconfig2cpp tool execution timeout";
+ process.kill();
+ return result;
+ }
+
+ result.exitCode = process.exitCode();
+
+ if (result.exitCode == 0) {
+ result.success = true;
+ if (!QFile::exists(result.generatedFilePath)) {
+ result.success = false;
+ result.errorMessage = QString("Generated file not found: %1").arg(result.generatedFilePath);
+ }
+ } else {
+ result.errorMessage = QString("dconfig2cpp failed with exit code %1. Error: %2")
+ .arg(result.exitCode)
+ .arg(QString::fromUtf8(process.readAllStandardError()));
+ }
+
+ return result;
+ }
+
+ QTemporaryDir *tempDir;
+};
+
+TEST_F(ut_dconfig2cpp, BasicTypesGeneration) {
+ QString testFile = ":/data/dconfig2cpp/basic-types.meta.json";
+
+ // If resource file doesn't exist, use filesystem path
+ if (!QFile::exists(testFile)) {
+ testFile = "./data/dconfig2cpp/basic-types.meta.json";
+ }
+
+ ASSERT_TRUE(QFile::exists(testFile)) << "Test data file not found: " << testFile.toStdString();
+
+ auto result = generateCode(testFile);
+ ASSERT_TRUE(result.success) << result.errorMessage.toStdString();
+ ASSERT_TRUE(QFile::exists(result.generatedFilePath));
+}
+
+TEST_F(ut_dconfig2cpp, BasicTypesPropertyValidation) {
+ QString testFile = ":/data/dconfig2cpp/basic-types.meta.json";
+ if (!QFile::exists(testFile)) {
+ testFile = "./data/dconfig2cpp/basic-types.meta.json";
+ }
+
+ ASSERT_TRUE(QFile::exists(testFile));
+
+ auto result = generateCode(testFile);
+ ASSERT_TRUE(result.success) << result.errorMessage.toStdString();
+
+ // Read generated header file content
+ QFile generatedFile(result.generatedFilePath);
+ ASSERT_TRUE(generatedFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString generatedContent = generatedFile.readAll();
+ generatedFile.close();
+
+ // Expected Q_PROPERTY declarations
+ QStringList expectedProperties = {
+ "Q_PROPERTY(bool booleanTrue READ booleanTrue WRITE setBooleanTrue",
+ "Q_PROPERTY(bool booleanFalse READ booleanFalse WRITE setBooleanFalse",
+ "Q_PROPERTY(QString stringValue READ stringValue WRITE setStringValue",
+ "Q_PROPERTY(QString emptyString READ emptyString WRITE setEmptyString",
+ "Q_PROPERTY(qlonglong integerPositive READ integerPositive WRITE setIntegerPositive",
+ "Q_PROPERTY(qlonglong integerNegative READ integerNegative WRITE setIntegerNegative",
+ "Q_PROPERTY(qlonglong integerZero READ integerZero WRITE setIntegerZero",
+ "Q_PROPERTY(double doubleValue READ doubleValue WRITE setDoubleValue",
+ "Q_PROPERTY(double doubleZero READ doubleZero WRITE setDoubleZero",
+ "Q_PROPERTY(double scientificValue READ scientificValue WRITE setScientificValue"
+ };
+
+ // Verify each expected Q_PROPERTY exists
+ for (const QString &expectedProperty : expectedProperties) {
+ EXPECT_TRUE(generatedContent.contains(expectedProperty))
+ << "Expected property not found: " << expectedProperty.toStdString();
+ }
+}
+
+TEST_F(ut_dconfig2cpp, NumericTypesDetection) {
+ QString testFile = ":/data/dconfig2cpp/numeric-types.meta.json";
+ if (!QFile::exists(testFile)) {
+ testFile = "./data/dconfig2cpp/numeric-types.meta.json";
+ }
+
+ ASSERT_TRUE(QFile::exists(testFile));
+
+ auto result = generateCode(testFile);
+ ASSERT_TRUE(result.success) << result.errorMessage.toStdString();
+
+ // Read generated header file content
+ QFile generatedFile(result.generatedFilePath);
+ ASSERT_TRUE(generatedFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString generatedContent = generatedFile.readAll();
+ generatedFile.close();
+
+ // Expected Q_PROPERTY declarations - based on numeric type detection rules
+ QStringList expectedProperties = {
+ "Q_PROPERTY(qlonglong pureInteger READ pureInteger WRITE setPureInteger", // Pure integer -> qlonglong
+ "Q_PROPERTY(double doubleWithDecimal READ doubleWithDecimal WRITE setDoubleWithDecimal", // Decimal -> double
+ "Q_PROPERTY(double doubleWithZeroDecimal READ doubleWithZeroDecimal WRITE setDoubleWithZeroDecimal", // .0 -> double
+ "Q_PROPERTY(double scientificNotation READ scientificNotation WRITE setScientificNotation", // Scientific notation -> double
+ "Q_PROPERTY(qlonglong largeInteger READ largeInteger WRITE setLargeInteger" // Large integer -> qlonglong
+ };
+
+ // Verify each expected Q_PROPERTY exists
+ for (const QString &expectedProperty : expectedProperties) {
+ EXPECT_TRUE(generatedContent.contains(expectedProperty))
+ << "Expected property not found: " << expectedProperty.toStdString();
+ }
+}
+
+TEST_F(ut_dconfig2cpp, ComplexTypesGeneration) {
+ QString testFile = ":/data/dconfig2cpp/complex-types.meta.json";
+ if (!QFile::exists(testFile)) {
+ testFile = "./data/dconfig2cpp/complex-types.meta.json";
+ }
+
+ ASSERT_TRUE(QFile::exists(testFile));
+
+ auto result = generateCode(testFile);
+ ASSERT_TRUE(result.success) << result.errorMessage.toStdString();
+
+ // Read generated header file content
+ QFile generatedFile(result.generatedFilePath);
+ ASSERT_TRUE(generatedFile.open(QIODevice::ReadOnly | QIODevice::Text));
+ QString generatedContent = generatedFile.readAll();
+ generatedFile.close();
+
+ // Expected Q_PROPERTY declarations - complex types
+ QStringList expectedProperties = {
+ "Q_PROPERTY(QList emptyArray READ emptyArray WRITE setEmptyArray",
+ "Q_PROPERTY(QList stringArray READ stringArray WRITE setStringArray",
+ "Q_PROPERTY(QList mixedArray READ mixedArray WRITE setMixedArray",
+ "Q_PROPERTY(QVariantMap emptyObject READ emptyObject WRITE setEmptyObject",
+ "Q_PROPERTY(QVariantMap simpleObject READ simpleObject WRITE setSimpleObject"
+ };
+
+ // Verify each expected Q_PROPERTY exists
+ for (const QString &expectedProperty : expectedProperties) {
+ EXPECT_TRUE(generatedContent.contains(expectedProperty))
+ << "Expected property not found: " << expectedProperty.toStdString();
+ }
+}
diff --git a/tools/dconfig2cpp/main.cpp b/tools/dconfig2cpp/main.cpp
index a284976..85d92a6 100644
--- a/tools/dconfig2cpp/main.cpp
+++ b/tools/dconfig2cpp/main.cpp
@@ -20,6 +20,24 @@ static QString toUnicodeEscape(const QString& input) {
return result;
}
+// Check if the original JSON value is in floating-point format
+static bool isOriginalValueFloat(const QByteArray& jsonData, const QString& propertyName, const QJsonValue& value) {
+ if (!value.isDouble()) {
+ return false;
+ }
+ // Build search pattern to find the number in "propertyName": { "value": number }
+ QString searchPattern = QString("\"%1\"\\s*:\\s*\\{[^}]*\"value\"\\s*:\\s*([0-9.eE+-]+)").arg(propertyName);
+ QRegularExpression regex(searchPattern);
+ QString jsonString = QString::fromUtf8(jsonData);
+ QRegularExpressionMatch match = regex.match(jsonString);
+ if (match.hasMatch()) {
+ QString numberStr = match.captured(1);
+ // If contains decimal point or scientific notation, it's a floating-point number
+ return numberStr.contains('.') || numberStr.contains('e', Qt::CaseInsensitive);
+ }
+ return false;
+}
+
// Converts a QJsonValue to a corresponding C++ code representation
static QString jsonValueToCppCode(const QJsonValue &value){
if (value.isBool()) {
@@ -216,8 +234,17 @@ int main(int argc, char *argv[]) {
} else if (value.isObject()) {
typeName = "QVariantMap";
} else if (value.isDouble()) {
- const auto variantValue = value.toVariant();
- typeName = QString::fromLatin1(variantValue.typeName());
+ // QJson treats all numbers in JSON as double. Although converting QJsonValue to
+ // QVariant can distinguish between double and int, Qt's JSON parsing attempts to
+ // convert floating-point numbers with decimal parts of 0, such as 1.0, to integers,
+ // resulting in the generated property type being qlonglong. However, dconfig expects
+ // floating-point numbers, so we try to identify whether it is a floating-point number
+ // or an integer by matching strings.
+ if (isOriginalValueFloat(data, propertyName, value)) {
+ typeName = "double";
+ } else {
+ typeName = "qlonglong";
+ }
} else if (value.isString()) {
typeName = "QString";
} else {
@@ -535,7 +562,7 @@ int main(int argc, char *argv[]) {
// Property variables
for (const Property &property : std::as_const(properties)) {
if (property.typeName == QLatin1String("int") || property.typeName == QLatin1String("qint64")) {
- headerStream << " // Note: If you expect a double type, add 'e' to the number in the JSON value field, e.g., \"value\": 1.0e, not just 1.0\n";
+ headerStream << " // Note: If you expect a double type, use XXX.0\n";
} else if (property.typeName == QLatin1String("QString")) {
headerStream << " // Default value: \"" << property.defaultValue.toString().replace("\n", "\\n").replace("\r", "\\r") << "\"\n";
}