New upstream version 5.5.30
authorBoyuan Yang <byang@debian.org>
Tue, 12 Apr 2022 20:08:46 +0000 (16:08 -0400)
committerBoyuan Yang <byang@debian.org>
Tue, 12 Apr 2022 20:08:46 +0000 (16:08 -0400)
30 files changed:
.github/workflows/backup-to-gitlab.yml [new file with mode: 0644]
.github/workflows/call-build-deb.yml [new file with mode: 0644]
.github/workflows/call-chatOps.yml [new file with mode: 0644]
.github/workflows/call-commitlint.yml [new file with mode: 0644]
.github/workflows/cppcheck.yml [new file with mode: 0644]
src/base/dsingleton.h
src/dbus/org.desktopspec.ConfigManager.Manager.xml
src/dconfig.cpp
src/dconfig.h
src/dconfigfile.cpp
src/ddesktopentry.cpp
src/dsysinfo.cpp
src/filesystem/dfilesystemwatcher_linux.cpp
src/filesystem/dstandardpaths.cpp
src/filesystem/dstandardpaths.h
src/filesystem/filesystem.pri
src/settings/backend/gsettingsbackend.cpp
src/settings/dsettings.cpp
tests/data.qrc
tests/data/dconf-example.meta.json
tests/data/dconf-example_other_app_configure.meta.json [new file with mode: 0755]
tests/test-recoverage-qmake.sh
tests/tests.pro
tests/ut_dconfig.cpp
tests/ut_dconfigfile.cpp
tests/ut_dfilesystemwatcher.cpp
tests/ut_dfilewatcher.cpp
tests/ut_dfilewatchermanager.cpp
tests/ut_dutil.cpp
tests/ut_qsettingsbackend.cpp

diff --git a/.github/workflows/backup-to-gitlab.yml b/.github/workflows/backup-to-gitlab.yml
new file mode 100644 (file)
index 0000000..2ebbd4d
--- /dev/null
@@ -0,0 +1,52 @@
+name: backup to gitlab
+on: [push]
+
+concurrency:
+  group: ${{ github.workflow }}
+  cancel-in-progress: true
+
+jobs:
+  backup-to-gitlab:
+    if: github.repository_owner == 'linuxdeepin'
+    name: backup-to-gitlab
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          repository: "linuxdeepin/jenkins-bridge-client"
+          path: jenkins-bridge-client
+
+      - name: Install Client
+        run: |
+          cd $GITHUB_WORKSPACE/jenkins-bridge-client
+          go build .
+          sudo install -Dvm755 jenkins-bridge-client -t /usr/bin/
+      - name: Trigger sync
+        id: generate-runid
+        run: |
+          echo "::set-output name=RUN_ID::$(jenkins-bridge-client -triggerSync -token '${{ secrets.BRIDGETOKEN }}')"
+      - name: Print log
+        run: |
+          jenkins-bridge-client -printlog -token "${{ secrets.BRIDGETOKEN }}" -runid "${{ steps.generate-runid.outputs.RUN_ID }}"
+
+  backup-to-gitee:
+    if: github.repository_owner == 'linuxdeepin'
+    runs-on: ubuntu-latest
+    steps:
+      - name: create-repo
+        run: |
+          repo=${{ github.event.repository.name }}
+          homepage="https://github.com/linuxdeepin/${repo}"
+          description="mirror of ${homepage}"
+          # remove '.' prefix
+          repo=${repo#"."}
+          curl -X POST --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/enterprises/linuxdeepin/repos' -d '{"private": 1,"access_token":"${{ secrets.GITEE_SYNC_TOKEN }}","name":"'"$repo"'","description":"'"$description"'","homepage":"'"$homepage"'","has_issues":"false","has_wiki":"false","can_comment":"false"}' || true
+      - name: push
+        run: |
+          git clone --bare https://github.com/linuxdeepin/${{ github.event.repository.name }}.git .git
+          repo=${{ github.event.repository.name }}
+          # remove '.' prefix
+          repo=${repo#"."}
+          git remote set-url origin https://myml:${{ secrets.GITEE_SYNC_TOKEN }}@gitee.com/linuxdeepin/${repo}.git
+          git push -f --all --prune origin
+          git push --tags origin
diff --git a/.github/workflows/call-build-deb.yml b/.github/workflows/call-build-deb.yml
new file mode 100644 (file)
index 0000000..f95fadb
--- /dev/null
@@ -0,0 +1,16 @@
+name: Call build-deb
+on:
+  pull_request_target:
+    types: [opened, synchronize]
+    paths-ignore:
+      - ".github/workflows/**"
+
+concurrency:
+  group: ${{ github.workflow }}-pull/${{ github.event.number }}
+  cancel-in-progress: true
+
+jobs:
+  check_job:
+    uses: linuxdeepin/.github/.github/workflows/build-deb.yml@master
+    secrets:
+      BridgeToken: ${{ secrets.BridgeToken }}
diff --git a/.github/workflows/call-chatOps.yml b/.github/workflows/call-chatOps.yml
new file mode 100644 (file)
index 0000000..9888614
--- /dev/null
@@ -0,0 +1,8 @@
+name: chatOps
+on:
+  issue_comment:
+    types: [created]
+
+jobs:
+  chatopt:
+    uses: linuxdeepin/.github/.github/workflows/chatOps.yml@master
diff --git a/.github/workflows/call-commitlint.yml b/.github/workflows/call-commitlint.yml
new file mode 100644 (file)
index 0000000..69f5b2d
--- /dev/null
@@ -0,0 +1,11 @@
+name: Call commitlint
+on:
+  pull_request_target:
+
+concurrency:
+  group: ${{ github.workflow }}-pull/${{ github.event.number }}
+  cancel-in-progress: true
+
+jobs:
+  check_job:
+    uses: linuxdeepin/.github/.github/workflows/commitlint.yml@master
diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml
new file mode 100644 (file)
index 0000000..dfaef25
--- /dev/null
@@ -0,0 +1,24 @@
+name: cppcheck
+on:
+  pull_request_target:
+
+concurrency:
+  group: ${{ github.workflow }}-pull/${{ github.event.number }}
+  cancel-in-progress: true
+
+jobs:
+  cppchceck:
+    name: cppcheck
+    runs-on: ubuntu-latest
+    steps:
+      - run: export
+      - uses: actions/checkout@v2
+        with:
+          ref: ${{ github.event.pull_request.head.sha }}
+          persist-credentials: false
+      - uses: linuxdeepin/action-cppcheck@main
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+          repository: ${{ github.repository }}
+          pull_request_id: ${{ github.event.pull_request.number }}
+          allow_approve: false
index a6dfa7eb4373fa4fa7cc7d25c3c98bf79383e2d8..7af885351c600365b434c4fbb0d0e963c285f9c0 100644 (file)
@@ -58,17 +58,26 @@ template <class T>
 class LIBDTKCORESHARED_EXPORT DSingleton
 {
 public:
+    QT_DEPRECATED_X("Use ref")
     static inline T *instance()
     {
         static T  *_instance = new T;
         return _instance;
     }
 
+    static T& ref()
+    {
+        static T instance;
+        return instance;
+    }
+
+    DSingleton(T&&) = delete;
+    DSingleton(const T&) = delete;
+    void operator= (const T&) = delete;
+
 protected:
-    DSingleton(void) {}
-    ~DSingleton(void) {}
-    DSingleton(const DSingleton &) {}
-    DSingleton &operator= (const DSingleton &) {}
+    DSingleton() = default;
+    virtual ~DSingleton() = default;
 };
 
 DCORE_END_NAMESPACE
index 9e606ea3a0f302b4fade7f7da15f263ff1ff91c5..65881d60387c67d2a7a08919d8dd3f1bd3d990e8 100644 (file)
@@ -9,6 +9,9 @@
       <arg type='s' name='key' direction='in'/>
       <arg type='v' name='value' direction='in'/>
     </method>
+    <method name='reset'>
+      <arg type='s' name='key' direction='in'/>
+    </method>
     <method name='name'>
       <arg type='s' name='key' direction='in'/>
       <arg type='s' name='language' direction='in'/>
index 2db9b6b560f539915f476315fafc4a687f72c3c6..f8f6b80190fd22bfb80c6148a0b12825ec21a5cd 100644 (file)
@@ -41,12 +41,12 @@ DCORE_BEGIN_NAMESPACE
 Q_DECLARE_LOGGING_CATEGORY(cfLog)
 
 inline static QString getAppId() {
-    // TODO: 应该使用更可靠的接口获取 appid
+    // TODO: 应该使用更可靠的接口获取 appId
     return QCoreApplication::applicationName();
 }
 
 /*!
-    \class DTK::Core::DConfigBackend
+    \class Dtk::Core::DConfigBackend
     \inmodule dtkcore
 
     \brief 配置后端的抽象接口.
@@ -55,43 +55,49 @@ inline static QString getAppId() {
  */
 
 /*!
-    \fn bool load(const QString &/appid/) = 0
+    \fn bool DConfigBackend::load(const QString &) = 0
 
     \brief 初始化后端
 
-    \a appid 管理的配置信息key值,默认为应用程序名称
+    \a appId 管理的配置信息key值,默认为应用程序名称
   */
 
 /*!
-    \fn bool isValid() const = 0
+    \fn bool DConfigBackend::isValid() const = 0
 
     \sa DConfig::isValid().
 
  */
 
 /*!
-    \fn QStringList keyList() const = 0
+    \fn QStringList DConfigBackend::keyList() const = 0
 
     \sa DConfig::keyList()
 
  */
 
 /*!
-    \fn QVariant value(const QString &key, const QVariant &fallback = QVariant()) const = 0
+    \fn QVariant DConfigBackend::value(const QString &key, const QVariant &fallback = QVariant()) const = 0
 
     \sa DConfig::value()
  */
 
 /*!
-    \fn void setValue(const QString &key, const QVariant &value) = 0
+    \fn void DConfigBackend::setValue(const QString &key, const QVariant &value) = 0
 
     \sa DConfig::setValue()
  */
 
 /*!
-    \fn QString name() const = 0
+    \fn void DConfigBackend::reset(const QString &key)
 
-    \breaf 后端配置的唯一标识
+    \sa DConfig::reset()
+ */
+
+/*!
+    \fn QString DConfigBackend::name() const = 0
+
+    \brief 后端配置的唯一标识
 
  */
 
@@ -102,8 +108,14 @@ DConfigBackend::~DConfigBackend()
 class Q_DECL_HIDDEN DConfigPrivate : public DObjectPrivate
 {
 public:
-    explicit DConfigPrivate(DConfig *qq)
+    explicit DConfigPrivate(DConfig *qq,
+                            const QString &appId,
+                            const QString &name,
+                            const QString &subpath)
         : DObjectPrivate(qq)
+        , appId(appId.isEmpty() ? getAppId() : appId)
+        , name(name)
+        , subpath(subpath)
     {
     }
 
@@ -112,6 +124,7 @@ public:
     DConfigBackend *getOrCreateBackend();
     DConfigBackend *createBackendByEnv();
 
+    QString appId;
     QString name;
     QString subpath;
     QScopedPointer<DConfigBackend> backend;
@@ -137,12 +150,12 @@ public:
         return configFile && configFile->isValid();
     }
 
-    virtual bool load(const QString &appid) override
+    virtual bool load(const QString &appId) override
     {
         if (configFile)
             return true;
 
-        configFile.reset(new DConfigFile(appid,owner->name, owner->subpath));
+        configFile.reset(new DConfigFile(appId,owner->name, owner->subpath));
         configCache.reset(configFile->createUserCache(getuid()));
         const QString &prefix = localPrefix();
 
@@ -168,6 +181,12 @@ public:
         }
     }
 
+    virtual void reset(const QString &key) override
+    {
+        const auto &originValue = configFile->meta()->value(key);
+        setValue(key, originValue);
+    }
+
     virtual QString name() const override
     {
         return QString("FileBackend");
@@ -222,6 +241,17 @@ public:
         return QDBusConnection::systemBus().interface()->isServiceRegistered(DSG_CONFIG);
     }
 
+    static bool isServiceActivatable()
+    {
+         const QDBusReply<QStringList> activatableNames = QDBusConnection::systemBus().interface()->
+                 callWithArgumentList(QDBus::AutoDetect,
+                 QLatin1String("ListActivatableNames"),
+                 QList<QVariant>());
+//         qInfo() << activatableNames.value() << activatableNames.value().contains(DSG_CONFIG);
+
+         return activatableNames.value().contains(DSG_CONFIG);
+    }
+
     virtual bool isValid() const override
     {
         return config && config->isValid();
@@ -265,11 +295,45 @@ public:
         return config->keyList();
     }
 
+    static QVariant decodeQDBusArgument(const QVariant &v)
+    {
+        if (v.canConvert<QDBusArgument>()) {
+            // we use QJsonValue to resolve all data type in DConfigInfo class, so it's type is equal QJsonValue::Type,
+            // now we parse Map and Array type to QVariant explicitly.
+            const QDBusArgument &complexType = v.value<QDBusArgument>();
+            switch (complexType.currentType()) {
+            case QDBusArgument::MapType: {
+                QVariantMap list;
+                complexType >> list;
+                QVariantMap res;
+                for (auto iter = list.begin(); iter != list.end(); iter++) {
+                    res[iter.key()] = decodeQDBusArgument(iter.value());
+                }
+                return res;
+            }
+            case QDBusArgument::ArrayType: {
+                QVariantList list;
+                complexType >> list;
+                QVariantList res;
+                res.reserve(list.size());
+                for (const auto &item : qAsConst(list)) {
+                    res << decodeQDBusArgument(item);
+                }
+                return res;
+            }
+            default:
+                qWarning("Can't parse the type, it maybe need user to do it, "
+                         "QDBusArgument::ElementType: %d.", complexType.currentType());
+            }
+        }
+        return v;
+    }
+
     virtual QVariant value(const QString &key, const QVariant &fallback) const override
     {
         const QDBusVariant &dv = config->value(key);
         const QVariant &v = dv.variant();
-        return v.isValid() ? v : fallback;
+        return v.isValid() ? decodeQDBusArgument(v) : fallback;
     }
 
     virtual void setValue(const QString &key, const QVariant &value) override
@@ -277,6 +341,11 @@ public:
         config->setValue(key, QDBusVariant(value));
     }
 
+    virtual void reset(const QString &key) override
+    {
+        config->reset(key);
+    }
+
     virtual QString name() const override
     {
         return QString("DBusBackend");
@@ -379,7 +448,7 @@ DConfigBackend *DConfigPrivate::getOrCreateBackend()
     }
 #ifndef D_DISABLE_DCONFIG
 #ifndef D_DISABLE_DBUS_CONFIG
-    if (DBusBackend::isServiceRegistered()) {
+    if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) {
         qCDebug(cfLog, "Fallback to DBus mode");
         backend.reset(new DBusBackend(this));
     } else {
@@ -411,7 +480,7 @@ DConfigBackend *DConfigPrivate::createBackendByEnv()
 
 #ifndef D_DISABLE_DCONFIG
 #ifndef D_DISABLE_DBUS_CONFIG
-            if (DBusBackend::isServiceRegistered()) {
+            if (DBusBackend::isServiceRegistered() || DBusBackend::isServiceActivatable()) {
                 qCDebug(cfLog, "Fallback to DBus mode");
                 return new DBusBackend(this);
             }
@@ -436,7 +505,7 @@ DConfigBackend *DConfigPrivate::createBackendByEnv()
 }
 
 /*!
-    \class DTK::Core::DConfig
+    \class Dtk::Core::DConfig
     \inmodule dtkcore
 
     \brief 配置策略提供的接口类
@@ -457,34 +526,55 @@ DConfig::DConfig(const QString &name, const QString &subpath, QObject *parent)
 {
 }
 
+DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent)
+    : DConfig(backend, QString(), name, subpath, parent)
+{
+
+}
+/*!
+ * \brief 构造配置策略提供的对象, 指定配置所属的应用Id
+ * \a appId
+ * \a name
+ * \a subpath
+ * \a parent
+ * \return 构造的配置策略对象,由调用者释放
+ */
+DConfig *DConfig::create(const QString &appId, const QString &name, const QString &subpath, QObject *parent)
+{
+    return new DConfig(nullptr, appId, name, subpath, parent);
+}
+
+DConfig *DConfig::create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent)
+{
+    return new DConfig(backend, appId, name, subpath, parent);
+}
+
 /*!
  * \brief 使用自定义的配置策略后端构造对象
- * \a name 配置文件名
  * \a backend 调用者继承于DConfigBackend的配置策略后端
+ * \a appId 配置文件所属的应用Id,为空时默认为本应用Id
+ * \a name 配置文件名
  * \a subpath 配置文件对应的子目录
  * \a parent 父对象
  * \note 调用者只构造backend,由DConfig释放。
  */
-DConfig::DConfig(DConfigBackend *backend, const QString &name, const QString &subpath, QObject *parent)
+DConfig::DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath, QObject *parent)
     : QObject(parent)
-    , DObject(*new DConfigPrivate(this))
+    , DObject(*new DConfigPrivate(this, appId, name, subpath))
 {
     D_D(DConfig);
-    d->name = name;
-    d->subpath = subpath;
 
-    const auto &appid = getAppId();
-    Q_ASSERT(!appid.isEmpty());
+    Q_ASSERT(!d->appId.isEmpty());
 
     qCDebug(cfLog, "Load config of appid=%s name=%s, subpath=%s",
-            qPrintable(appid), qPrintable(d->name), qPrintable(d->subpath));
+            qPrintable(d->appId), qPrintable(d->name), qPrintable(d->subpath));
 
     if (backend) {
         d->backend.reset(backend);
     }
 
     if (auto backend = d->getOrCreateBackend()) {
-        backend->load(appid);
+        backend->load(d->appId);
     }
 }
 
@@ -542,6 +632,16 @@ void DConfig::setValue(const QString &key, const QVariant &value)
     d->backend->setValue(key, value);
 }
 
+/*!
+ * \brief 设置其配置项对应的默认值,此值为经过override机制覆盖后的值,不一定为此配置文件中meta中定义的值
+ * \param 配置项名称
+ */
+void DConfig::reset(const QString &key)
+{
+    D_D(DConfig);
+    d->backend->reset(key);
+}
+
 /*!
  * \brief 返回配置文件名称
  * \return
index 38584c2ca93d2ea84192e8252fc0cfa183ddbab1..317e30cf45bd5ed9d977b26cf1abcb1e4dcb47ee 100644 (file)
@@ -32,10 +32,11 @@ class DConfigBackend {
 public:
     virtual ~DConfigBackend();
     virtual bool isValid() const = 0;
-    virtual bool load(const QString &/*appid*/) = 0;
+    virtual bool load(const QString &/*appId*/) = 0;
     virtual QStringList keyList() const = 0;
     virtual QVariant value(const QString &/*key*/, const QVariant &/*fallback*/) const = 0;
     virtual void setValue(const QString &/*key*/, const QVariant &/*value*/) = 0;
+    virtual void reset(const QString &key) { setValue(key, QVariant());}
     virtual QString name() const {return QString("");}
 };
 
@@ -54,6 +55,11 @@ public:
     explicit DConfig(DConfigBackend *backend, const QString &name, const QString &subpath = QString(),
                      QObject *parent = nullptr);
 
+    static DConfig *create(const QString &appId, const QString &name, const QString &subpath = QString(),
+                           QObject *parent = nullptr);
+    static DConfig *create(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath = QString(),
+                                  QObject *parent = nullptr);
+
     QString backendName() const;
 
     QStringList keyList() const;
@@ -61,12 +67,17 @@ public:
     bool isValid() const;
     QVariant value(const QString &key, const QVariant &fallback = QVariant()) const;
     void setValue(const QString &key, const QVariant &value);
+    void reset(const QString &key);
 
     QString name() const;
     QString subpath() const;
 
 Q_SIGNALS:
     void valueChanged(const QString &key);
+
+private:
+    explicit DConfig(DConfigBackend *backend, const QString &appId, const QString &name, const QString &subpath,
+                     QObject *parent = nullptr);
 };
 
 DCORE_END_NAMESPACE
index 51346c2e1c961e160af1ec4098c6fa508142acd3..8db11d5d68e68b5b5ea5f3b98ccd93ded5deb14d 100644 (file)
@@ -57,8 +57,8 @@ Q_LOGGING_CATEGORY(cfLog, "dtk.dsg.config");
 
     \brief 按子目录查找机制查找配置文件
 
-    在\a baseDir目录下,查找名称为\a name的文件,
-    若存在 \a subpath,则从\a subpath叶子目录逐级向上查找名称为\a name的文件,
+    在 \a baseDir目录下,查找名称为 \a name的文件,
+    若存在 \a subpath,则从 \a subpath叶子目录逐级向上查找名称为 \a name的文件,
     若不存在此文件,则返回无效路径.
  */
 inline QString getFile(const QString &baseDir, const QString &subpath, const QString &name,
@@ -180,9 +180,15 @@ inline static QString getUserName(const uint uid) {
 }
 
 /*!
-    \enum DConfigFile::Flag
+    \class Dtk::Core::DConfigFile
     \inmodule dtkcore
 
+    \brief 规范配置文件读写的相关接口的配置文件实现.
+ */
+
+/*!
+    \enum DConfigFile::Flag
+
     \value NoOverride  存在此标记时,将表明则此配置项不可被覆盖(详见下述 override 机制)。
     反之,不存在此标记时表明此配置项允许被覆盖,对于此类配置项,
     如若其有界面设置入口,则当此项不可写时,应当隐藏或禁用界面的设置入口.
@@ -192,7 +198,6 @@ inline static QString getUserName(const uint uid) {
 
 /*!
     \enum DConfigFile::Permissions
-    \inmodule dtkcore
 
     \value ReadOnly 将配置项覆盖为只读,
     \value ReadWrite 将配置项覆盖为可读可写.
@@ -200,7 +205,6 @@ inline static QString getUserName(const uint uid) {
 
 /*!
     \enum DConfigFile::Visibility
-    \inmodule dtkcore
 
     \value Private 仅限程序内部使用,
     对外不可见。此类配置项完全由程序自己读写,可随意增删改写其含义,无需做兼容性考虑,
@@ -212,9 +216,8 @@ inline static QString getUserName(const uint uid) {
 */
 
 /*!
-    \class DConfigFile::Version
+    \struct Dtk::Core::DConfigFile::Version
     \inmodule dtkcore
-
     \brief 版本信息
 
     此文件的内容格式的版本。版本号使用两位数字描述,
@@ -424,17 +427,17 @@ private:
 
 
 /*!
-    \class DConfigMeta
+    \class Dtk::Core::DConfigMeta
     \inmodule dtkcore
 
-    \brief 提供配置文件的原型和覆盖机制的访问接口
+    \brief 提供配置文件的原型和覆盖机制的访问接口.
 
 */
 
 /*!
     \fn DConfigFile::Version DConfigMeta::version() const = 0;
 
-    \breaf 返回配置版本信息
+    \brief 返回配置版本信息.
     \return
 */
 
@@ -449,7 +452,7 @@ private:
 /*!
     \fn bool DConfigMeta::load(const QString &localPrefix = QString()) = 0;
 
-    \breaf 解析配置文件
+    \brief 解析配置文件
     \a localPrefix 为目录前缀
     \return
 */
@@ -457,7 +460,7 @@ private:
 /*!
     \fn bool DConfigMeta::load(QIODevice *meta, const QList<QIODevice*> &overrides) = 0;
 
-    \breaf 解析配置文件流
+    \brief 解析配置文件流
     \a meta 为原型流
     \a overrides 为覆盖机制查找的文件流
     \return
@@ -466,14 +469,14 @@ private:
 /*!
     \fn QStringList DConfigMeta::keyList() const = 0;
 
-    \breaf 返回配置内容的所有配置项
+    \brief 返回配置内容的所有配置项
     \return
 */
 
 /*!
     \fn DConfigFile::Flags DConfigMeta::flags(const QString &key) const = 0;
 
-    \breaf 返回指定配置项的特性
+    \brief 返回指定配置项的特性
     \a key 配置项名称, NoOverride为此配置项不可被覆盖, Global为忽略用户身份
     \return
 */
@@ -481,7 +484,7 @@ private:
 /*!
     \fn DConfigFile::Permissions DConfigMeta::permissions(const QString &key) const = 0;
 
-    \breaf 返回指定配置项的权限
+    \brief 返回指定配置项的权限
     \a key 配置项名称
     \return
 
@@ -490,7 +493,7 @@ private:
 /*!
     \fn DConfigFile::Visibility DConfigMeta::visibility(const QString &key) const = 0;
 
-    \breaf 返回指定配置项的可见性
+    \brief 返回指定配置项的可见性
     \a key 配置项名称
     \return
 
@@ -499,7 +502,7 @@ private:
 /*!
     \fn int DConfigMeta::serial(const QString &key) const = 0;
 
-    \breaf 返回配置项的单调递增值
+    \brief 返回配置项的单调递增值
     \a key 配置项名称
     \return -1为无效值,表明没有配置此项
 */
@@ -507,7 +510,7 @@ private:
 /*!
     \fn QString DConfigMeta::displayName(const QString &key, const QLocale &locale) = 0;
 
-    \breaf 返回指定配置项的显示名
+    \brief 返回指定配置项的显示名
     \a key 配置项名称
     \a locale 为语言版本
     \return
@@ -516,7 +519,7 @@ private:
 /*!
     \fn QString DConfigMeta::description(const QString &key, const QLocale &locale) = 0;
 
-    \breaf 返回指定配置项的描述信息
+    \brief 返回指定配置项的描述信息
     \a key 配置项名称
     \a locale 为语言版本
     \return
@@ -526,7 +529,7 @@ private:
 /*!
     \fn QString DConfigMeta::metaPath(const QString &localPrefix = QString(), bool *useAppId = nullptr) const = 0;
 
-    \breaf 返回描述文件的路径
+    \brief 返回描述文件的路径
     \a localPrefix 目录的所有需要查找的覆盖机制目录
     \return
 */
@@ -534,7 +537,7 @@ private:
 /*!
     \fn QStringList DConfigMeta::allOverrideDirs(const bool useAppId, const QString &prefix = QString()) const = 0;
 
-    \breaf 获得前缀为\a prefix目录的所有需要查找的覆盖机制目录
+    \brief 获得前缀为 \a prefix 目录的所有需要查找的覆盖机制目录
     \a userAppId 是否不使用通用目录
     \return
 */
@@ -542,7 +545,7 @@ private:
 /*!
     \fn QVariant DConfigMeta::value(const QString &key) const = 0;
 
-    \breaf meta初始值经过覆盖机制覆盖后的原始值
+    \brief meta初始值经过覆盖机制覆盖后的原始值
     \a key 配置项名称
     \return
 */
@@ -595,30 +598,46 @@ public:
         return values.value(key);
     }
 
-    inline QString applicationMetaDir(const QString &prefix, const bool useOptDir = false) const
+    inline QStringList applicationMetaDirs(const QString &prefix) const
     {
-        if (useOptDir)
-            return QString("%1/opt/apps/%2/files/schemas/configs").arg(prefix, configKey.appId);
-
-        return QString("%1/usr/share/dsg/apps/%2/configs").arg(prefix, configKey.appId);
+        QStringList paths;
+        // lower priority is higher.
+        const auto &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir);
+        paths.reserve(dataPaths.size());
+        for (auto item : dataPaths) {
+            paths.prepend(QString("%1/%2/configs/%3").arg(prefix, item, configKey.appId));
+        }
+        return paths;
     }
 
-    inline static QString genericMetaDir(const QString &prefix) {
-        return prefix + DStandardPaths::filePath(DStandardPaths::DSG::DataDir,
-                                                 QString("configs"));
+    inline static QStringList genericMetaDirs(const QString &prefix) {
+        QStringList paths;
+        for (auto item: DStandardPaths::paths(DStandardPaths::DSG::DataDir)) {
+            paths.prepend(QString("%1/%2/configs").arg(prefix, item));
+        }
+        return paths;
     }
 
     QString metaPath(const QString &localPrefix, bool *useAppId) const override
     {
         bool useAppIdForOverride = true;
 
-        QString path = getFile(applicationMetaDir(localPrefix), configKey.subpath, configKey.fileName + FILE_SUFFIX);
-        if (path.isEmpty())
-            path = getFile(applicationMetaDir(localPrefix, true), configKey.subpath, configKey.fileName + FILE_SUFFIX);
+        QString path;
+        const QStringList &applicationMetas = applicationMetaDirs(localPrefix);
+        for (auto iter = applicationMetas.rbegin(); iter != applicationMetas.rend(); iter++) {
+            path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX);
+            if (!path.isEmpty())
+                break;
+        }
 
         if (path.isEmpty()) {
             useAppIdForOverride = false;
-            path = getFile(genericMetaDir(localPrefix), configKey.subpath, configKey.fileName + FILE_SUFFIX);
+            const QStringList &genericnMetas = genericMetaDirs(localPrefix);
+            for (auto iter = genericnMetas.rbegin(); iter != genericnMetas.rend(); iter++) {
+                path = getFile(*iter, configKey.subpath, configKey.fileName + FILE_SUFFIX);
+                if (!path.isEmpty())
+                    break;
+            }
         }
         if (useAppId) {
             *useAppId = useAppIdForOverride;
@@ -733,12 +752,17 @@ public:
         const QString &path2 = QString("%1/etc/dsg/configs/overrides/%2/%3")
                 .arg(prefix, useAppId ? configKey.appId : QString(), configKey.fileName);
 
-        const QString &path1 = QString("%1%2/configs/overrides/%3/%4")
-                .arg(prefix, DStandardPaths::path(DStandardPaths::DSG::DataDir),
-                     useAppId ? configKey.appId : QString(), configKey.fileName);
-
+        QStringList paths;
+        const QStringList &dataPaths = DStandardPaths::paths(DStandardPaths::DSG::DataDir);
+        paths.reserve(dataPaths.size() + 1);
+        for (auto path: dataPaths) {
+            // reverse `DataDir`'s paths, previous `DataDir`'s value has high priority
+            paths.prepend(QString("%1%2/configs/overrides/%3/%4")
+                          .arg(prefix, path, useAppId ? configKey.appId : QString(), configKey.fileName));
+        }
         // 在后面的优先级更高
-        return {path1, path2};
+        paths.append(path2);
+        return paths;
     }
 
     inline QStringList allOverrideDirs(const bool useAppId, const QString &prefix) const override
@@ -832,81 +856,74 @@ DConfigMetaImpl::~DConfigMetaImpl()
 }
 
 /*!
-    \class DConfigCache
+    \class Dtk::Core::DConfigCache
     \inmodule dtkcore
 
-    \brief 提供配置文件的用户和全局运行缓存访问接口
+    \brief 提供配置文件的用户和全局运行缓存访问接口.
 
 */
 
-/*
+/*!
     \fn bool DConfigCache::load(const QString &localPrefix = QString()) = 0;
-    \breaf 解析缓存配置文件
+    \brief 解析缓存配置文件
     \return
 */
 
-/*
+/*!
     \fn bool DConfigCache::save(const QString &localPrefix = QString(), QJsonDocument::JsonFormat format = QJsonDocument::Indented, bool sync = false) = 0;
-    \breaf 保存缓存的值到磁盘中
+    \brief 保存缓存的值到磁盘中
     \a localPrefix 为目录前缀
     \a format 保存格式
     \a sync 是否立即刷新
     \return
 */
 
-/*
+/*!
     \fn bool DConfigCache::isGlobal() const = 0;
     \brief 是否是全局缓存
     \return
 */
 
-/*
-    \fn void DConfigCache::resetMeta(DConfigMeta *meta) = 0;
-    \breaf 重置配置描述对象
-    \a meta 描述对象
-    \return
-*/
-
-/*
+/*!
     \fn void DConfigCache::remove(const QString &key) = 0;
-    \breaf 删除缓存中的配置项
+    \brief 删除缓存中的配置项
     \a key 配置项名称
     \return
 */
 
-/*
+/*!
     \fn QStringList DConfigCache::keyList() const = 0;
-    \breaf 返回配置内容的所有配置项
+    \brief 返回配置内容的所有配置项
     \return
 */
 
-/*
-    \fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const uint uid, const QString &appid) = 0;
-    \breaf 设置缓存中的值
+/*!
+    \fn bool DConfigCache::setValue(const QString &key, const QVariant &value, const int serial, const uint uid, const QString &callerAppid) = 0;
+    \brief 设置缓存中的值
     \a key 配置项名称
     \a value 需要设置的值
     \a uid 设置时的用户id
-    \a appid 设置时的应用id
+    \a callerAppid 设置时的应用id
     \return 为true时表示重新设置了新值,false表示没有设置
 */
 
-/*
-    \fn QVariant DConfigCache::value(const QString &key) = 0;
-    \breaf 获取缓存中的值
+/*!
+    \fn QVariant DConfigCache::value(const QString &key) const = 0;
+    \brief 获取缓存中的值
     \a key 配置项名称
     \return
 */
 
-/*
+/*!
     \fn int DConfigCache::serial(const QString &key) const = 0;
-    \breaf 返回配置项的单调递增值
+    \brief 返回配置项的单调递增值
     \a key 配置项名称
     \return -1为无效值,表明没有配置此项
 */
 
-/*
+/*!
     \fn uint DConfigCache::uid() const = 0;
-    \breaf 用户标识,为全局缓存时,uid为非用户标识的特定值
+    \brief 用户标识,为全局缓存时,uid为非用户标识的特定值
     \return
 */
 
@@ -937,7 +954,7 @@ public:
         if (homePath.isEmpty()) {
             return QString();
         }
-        const QString userHomeConfigDir = homePath + QStringLiteral("/.config");
+        const QString userHomeConfigDir = homePath + QStringLiteral("/.config/dsg/configs/");
         return prefix + userHomeConfigDir + "/" + configKey.appId;
     }
 
@@ -948,11 +965,11 @@ public:
 
     inline QString globalCacheDir(const QString &prefix) const {
         // TODO `DSG_APP_DATA` is not set and `appid` is not captured in `DStandardPaths::path`.
-        if (DStandardPaths::path(DStandardPaths::DSG::AppData).isEmpty())
-            return prefix + QString("/var/dsg/appdata/%1/configs").arg(configKey.appId);
+        QString appDataDir = DStandardPaths::path(DStandardPaths::DSG::AppData);
+        if (appDataDir.isEmpty())
+            appDataDir = QString("/var/dsg/appdata");
 
-        return prefix + DStandardPaths::filePath(DStandardPaths::DSG::AppData,
-                                                 QString("configs"));
+        return QString("%1/%2/configs/%3").arg(prefix, appDataDir, configKey.appId);
     }
 
     QString getCacheDir(const QString &localPrefix = QString())
@@ -1126,7 +1143,20 @@ public:
                 cache->remove(key);
                 return true;
             } else {
-                return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid);
+                const auto &metaValue = configMeta->value(key);
+                // sample judgement to reduce a copy of convert.
+                if (metaValue.type() == value.type())
+                    return cache->setValue(key, value, configMeta->serial(key), cache->uid(), appid);
+
+                // convert copy to meta's type, it promises `setValue` don't change meta's type.
+                auto copy = value;
+                if (!copy.convert(metaValue.userType())) {
+                    qCWarning(cfLog) << "check type error, meta type is " << metaValue.type()
+                                     << ", and now type is " << value.type();
+                    return false;
+                }
+
+                return cache->setValue(key, copy, configMeta->serial(key), cache->uid(), appid);
             }
         }
         return false;
@@ -1174,14 +1204,6 @@ DConfigFilePrivate::~DConfigFilePrivate()
     }
 }
 
-/*!
-    \class DTK::Core::DConfigFile
-    \inmodule dtkcore
-
-    \brief 规范配置文件读写的相关接口的配置文件实现
-
- */
-
 /*!
     \brief 支持的版本
     \return
@@ -1215,8 +1237,8 @@ DConfigFile::DConfigFile(const DConfigFile &other)
     d->globalCache = cache;
 }
 
-/*
-    \breaf 解析配置文件
+/*!
+    \brief 解析配置文件
     \a localPrefix 为目录前缀
     \return
 */
@@ -1226,8 +1248,8 @@ bool DConfigFile::load(const QString &localPrefix)
     return d->load(localPrefix);
 }
 
-/*
-    \breaf 解析配置文件流
+/*!
+    \brief 解析配置文件流
     \a meta 为原型流
     \a overrides 为覆盖机制查找的文件流
     \return
@@ -1237,8 +1259,8 @@ bool DConfigFile::load(QIODevice *meta, const QList<QIODevice *> &overrides)
     return this->meta()->load(meta, overrides);
 }
 
-/*
-    \breaf 保存缓存的值到磁盘中
+/*!
+    \brief 保存缓存的值到磁盘中
     \a format 保存格式
     \a sync 是否立即刷新
     \return
@@ -1265,7 +1287,7 @@ QVariant DConfigFile::value(const QString &key, DConfigCache *userCache) const
 }
 
 /*!
-    \breaf 设置缓存中的值
+    \brief 设置缓存中的值
     \a key 配置项名称
     \a value 需要设置的值
     \a uid 设置时的用户id
index 2ececb56ecf5fb2c26cfdfe1563fed481534f608..f5efa94009f27d18e36055ca6152cb39d03c2d8e 100644 (file)
@@ -339,6 +339,7 @@ bool DDesktopEntryPrivate::fuzzyLoad()
         }
     }
 
+    setStatus(DDesktopEntry::NoError);
     return true;
 }
 
index 5df0d84c81cf1952500e3f3816b2f28b44470342..02c5789858058f996d1b8be728aae6be4ce06c02 100644 (file)
@@ -240,24 +240,40 @@ bool DSysInfoPrivate::ensureOsVersion()
     DDesktopEntry entry(OS_VERSION_FILE);
     bool ok = false;
 
+#define D_ASSET_EXIT(con, msg) do { \
+    if (!(con)) { \
+        qWarning() << __func__ << msg; \
+        return false; \
+    } \
+} while (false)
+
+    D_ASSET_EXIT(entry.status() == DDesktopEntry::NoError, entry.status());
+
     // 先获取版本信息
     // ABCDE.xyz
     QString osb = entry.stringValue("OsBuild", "Version");
     QStringList osbs = osb.split(".");
-    Q_ASSERT(osbs.size() == 2 && osbs.value(0).size() == 5);
-    uint left = osbs.value(0).trimmed().toUInt(&ok);
-    Q_ASSERT(ok);
-    if (ok) {
-        osBuild.E = left % 10;
-        left /= 10;
-        osBuild.D = left % 10;
-        left /= 10;
-        osBuild.C = left % 10; // default C is 0
-        left /= 10;
-        osBuild.B = left % 10;
-        left /= 10;
-        osBuild.A = left % 10;
+    ok = (osbs.size() == 2 && osbs.value(0).size() == 5);
+    D_ASSET_EXIT(ok, "OsBuild version invalid!");
+
+    const QStringList &left = osbs.value(0).split(QString(), QString::SkipEmptyParts);
+    D_ASSET_EXIT(left.size() == 5, "OsBuild version(ls) invalid!");
+
+    int idx = 0;
+    osBuild.A = left.value(idx++, "0").toUInt(&ok);
+    D_ASSET_EXIT(ok, "OsBuild version(a) invalid!");
+    osBuild.B = left.value(idx++, "0").toUInt(&ok);
+    D_ASSET_EXIT(ok, "OsBuild version(b) invalid!");
+    osBuild.C = left.value(idx++, "0").toUInt(&ok);
+    if (!ok) {
+        auto c = left.value(idx-1, "0").toLatin1();
+        D_ASSET_EXIT(c.size()>0, "OsBuild version(c) invalid!");
+        osBuild.C = uint(c.at(0));
     }
+    osBuild.D = left.value(idx++, "0").toUInt(&ok);
+    D_ASSET_EXIT(ok, "OsBuild version(d) invalid!");
+    osBuild.E = left.value(idx++, "0").toUInt(&ok);
+    D_ASSET_EXIT(ok, "OsBuild version(e) invalid!");
 
     // xyz
     osBuild.xyz = osbs.value(1).trimmed().toUInt(&ok);
index 419364ca5ed8865adc279db11c7f88126d9c25a0..864073ae95878adce20b2c1cd686a9ee4a34a411 100644 (file)
@@ -438,8 +438,11 @@ DFileSystemWatcher::DFileSystemWatcher(QObject *parent)
         fd = inotify_init1(O_NONBLOCK);
     }
 
-    if (fd != -1)
+    if (fd != -1) {
         d_d_ptr.reset(new DFileSystemWatcherPrivate(fd, this));
+    } else {
+        qCritical() << "inotify_init1 failed, and the DFileSystemWatcher is invalid." << strerror(errno);
+    }
 }
 
 /*!
index b2b9b53da4f1dc4e26e284558cbf511f8ef5b946..798045b3cec79397c6e83eb598611e220f58604f 100644 (file)
@@ -39,7 +39,7 @@ public:
         switch (type) {
         case QStandardPaths::GenericDataLocation: {
             QString snapRoot = env.value("SNAP");
-            QString genericDataDir = snapRoot + "/usr/share/";
+            QString genericDataDir = snapRoot + PREFIX"/share/";
             return QStringList() << genericDataDir;
         }
         default:
@@ -169,20 +169,30 @@ QString DStandardPaths::path(DStandardPaths::XDG type)
 
 QString DStandardPaths::path(DStandardPaths::DSG type)
 {
-    switch (type) {
-    case DSG::AppData: {
+    const auto list = paths(type);
+    return list.isEmpty() ? nullptr : list.first();
+}
+
+QStringList DStandardPaths::paths(DSG type)
+{
+    QStringList paths;
+
+    if (type == DSG::DataDir) {
+        const QByteArray &path = qgetenv("DSG_DATA_DIRS");
+        if (path.isEmpty()) {
+            return {QLatin1String(PREFIX"/share/dsg")};
+        }
+        const auto list = path.split(':');
+        paths.reserve(list.size());
+        for (const auto &i : list)
+            paths.push_back(QString::fromLocal8Bit(i));
+    } else if (type == DSG::AppData) {
         const QByteArray &path = qgetenv("DSG_APP_DATA");
-        //TODO 应用数据目录规范:`/deepin/appdata/{appid}`, now `appid` is not captured.
-        return QString::fromLocal8Bit(path);
+        //TODO 应用数据目录规范:`/persistent/appdata/{appid}`, now `appid` is not captured.
+        paths.push_back(QString::fromLocal8Bit(path));
     }
-    case DSG::DataDir: {
-        const QByteArray &path = qgetenv("DSG_DATA_DIR");
-        if (!path.isEmpty())
-            return QString::fromLocal8Bit(path);
-        return QStringLiteral("/usr/share/dsg");
-    }
-    }
-    return QString();
+
+    return paths;
 }
 
 QString DStandardPaths::filePath(DStandardPaths::XDG type, QString fileName)
index 4cdbf5b20a923639b050b2b219bdba4a3eb86d4d..a2daf78eb781b08a1a01b7aed76c34edfafa18f3 100644 (file)
@@ -58,6 +58,7 @@ public:
     static QString homePath(const uint uid);
     static QString path(XDG type);
     static QString path(DSG type);
+    static QStringList paths(DSG type);
     static QString filePath(XDG type, QString fileName);
     static QString filePath(DSG type, QString fileName);
 
index d97756da376c7a7775da0744d54e7ba622568687..e15ee08a545665861aab5b13e6dbf5fb1f70fb2f 100644 (file)
@@ -2,6 +2,8 @@ include($$PWD/private/private.pri)
 
 INCLUDEPATH += $$PWD/../base
 
+DEFINES += PREFIX=\\\"$$PREFIX\\\"
+
 HEADERS += \
     $$PWD/dbasefilewatcher.h \
     $$PWD/dfilesystemwatcher.h \
index 5c2e0730488b99f284cb14ea2d32be8b67024107..4874f13521fac0619cc61bde181d5483097ca59e 100644 (file)
@@ -5,7 +5,12 @@
 #include <QJsonDocument>
 #include <QJsonObject>
 #include <QVariant>
+
+#if QT_HAS_INCLUDE(<QGSettings/QGSettings>)
 #include <QGSettings/QGSettings>
+#else
+#include <QGSettings>
+#endif
 
 #include <DSettings>
 
index 512ccc999d969f70114d01c656b5348735c48029..2d2663eff5e680aa848349e346744ad6eecaeed1 100644 (file)
@@ -281,6 +281,14 @@ void DSettings::setBackend(DSettingsBackend *backend)
     this, [ = ](const QString & key, const QVariant & value) {
         option(key)->setValue(value);
     });
+    // exit and delete thread
+    connect(this, &DSettings::destroyed, this, [backendWriteThread](){
+        if (backendWriteThread->isRunning()) {
+            backendWriteThread->quit();
+            backendWriteThread->wait();
+        }
+        backendWriteThread->deleteLater();
+    });
 
     backendWriteThread->start();
 
index 1c1b4712ad1711dd4f09c29d8d40b67b3e7db93f..09e108a62d95b6526d91367a48a2c548bbab258e 100644 (file)
@@ -7,5 +7,6 @@
         <file>data/dconf-override/dconf-example.override.a.b.json</file>
         <file>data/dconf-global.meta.json</file>
         <file>data/dconf-global.override.json</file>
+        <file>data/dconf-example_other_app_configure.meta.json</file>
     </qresource>
 </RCC>
index e3ed1514d96bdd60881b995f3f06ef215f8485e3..d591a1c2667d9154c812101072d90bde536b1a60 100755 (executable)
       "description": "I am description",
       "permissions": "readwrite",
       "visibility": "public"
+    },
+    "number": {
+      "value": 1,
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "array": {
+      "value": ["value1", "value2"],
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "array_map": {
+      "value": [{"key1": "value1", "key2": "value2"}],
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "array_map_struct": {
+      "value": [{"key1": {"field1": "value1"}, "key2": "value2"}],
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "map": {
+      "value": {"key1": "value1", "key2": "value2"},
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "map_array": {
+      "value": {"key1": ["value1"], "key2": ["value2"]},
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
+    },
+    "struct": {
+      "value": {"key1": "value1", "key2": "value2"},
+      "serial": 0,
+      "flags": ["global"],
+      "name": "array value type",
+      "permissions": "readwrite",
+      "visibility": "public"
     }
   }
 }
diff --git a/tests/data/dconf-example_other_app_configure.meta.json b/tests/data/dconf-example_other_app_configure.meta.json
new file mode 100755 (executable)
index 0000000..f6555ac
--- /dev/null
@@ -0,0 +1,24 @@
+{
+  "magic": "dsg.config.meta",
+  "version": "1.0",
+  "contents": {
+    "appPublic": {
+      "value": "publicValue",
+      "serial": 0,
+      "flags": ["global"],
+      "name": "I am a public field for all application",
+      "description": "I am description",
+      "permissions": "readwrite",
+      "visibility": "private"
+    },
+    "appPrivate": {
+      "value": "appPrivate",
+      "serial": 0,
+      "flags": ["nooverride"],
+      "name": "I am a private field for the application",
+      "description": "I am description",
+      "permissions": "readwrite",
+      "visibility": "public"
+    }
+  }
+}
index 3a452a4bdeaf2045b81be836e766976bd21b3f92..f89898b233b1e0c2fcaccdf09f96ff0b249abc33 100755 (executable)
@@ -1,7 +1,8 @@
 #!/bin/bash
 
-BUILD_DIR=build
-REPORT_DIR=report
+BUILD_DIR=`pwd`/../build-ut
+HTML_DIR=${BUILD_DIR}/html
+XML_DIR=${BUILD_DIR}/report
 #EXTRACT_ARGS="src"
 cd ../
 rm -rf $BUILD_DIR
@@ -16,16 +17,13 @@ mkdir $BUILD_DIR
 cd $BUILD_DIR
 qmake ../ CONFIG+=debug
 export ASAN_OPTIONS=halt_on_error=0
-TESTARGS="--gtest_output=xml:dde_test_report_dtkcore.xml"  make check -j$(nproc)
+TESTARGS="--gtest_output=xml:${XML_DIR}/report_dtkcore.xml"  make check -j$(nproc)
 
 lcov -d ./ -c -o coverage_all.info
 #lcov --extract coverage_all.info $EXTRACT_ARGS --output-file coverage.info
-lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" --output-file coverage.info
+lcov --remove coverage_all.info "*/tests/*" "*/usr/include*" "*build/src*" "*build-ut/src*" --output-file coverage.info
 cd ..
-genhtml -o $REPORT_DIR $BUILD_DIR/coverage.info
+genhtml -o $HTML_DIR $BUILD_DIR/coverage.info && mv ${BUILD_DIR}/html/index.html ${BUILD_DIR}/html/cov_dtkcore.html
 
-#rm -rf $BUILD_DIR
-#rm -rf ../$BUILD_DIR
-
-test -e ./build/asan.log* && mv ./build/asan.log* ./build/asan_dtkcore.log || touch ./build/asan.log
+test -e ${BUILD_DIR}/asan.log* && mv ${BUILD_DIR}/asan.log* ${BUILD_DIR}/asan_dtkcore.log || touch ${BUILD_DIR}/asan.log
 
index 470e5d839b22ee2c80e65674b7e19739c13071d0..a3caf6322dec7d1d03e566067457ea43667a7419 100644 (file)
@@ -24,7 +24,7 @@ DEPENDPATH += $$PWD/../src
 
 unix: {
 QMAKE_RPATHDIR += $$OUT_PWD/../src
-LIBS += -L$$OUT_PWD/../src/ -ldtkcore -lgtest
+LIBS += -lgtest
 # for dlsym
 LIBS += -ldl
 # TODO: vtabhook release test failed
index 7e99d81702e0f2de8d7d0d46b9614a4638969599..f3b476e1fc2888725b991199d5dc61a10fe603b6 100644 (file)
@@ -31,15 +31,16 @@ DCORE_USE_NAMESPACE
 
 static constexpr char const *APP_ID = "tests";
 static constexpr char const *FILE_NAME = "example";
-
+static EnvGuard dsgDataDir;
 class ut_DConfig : public testing::Test
 {
 protected:
     static void SetUpTestCase() {
         fileBackendLocalPerfix.set("DSG_DCONFIG_FILE_BACKEND_LOCAL_PREFIX", "/tmp/example");
-        metaGuard = new FileCopyGuard(":/data/dconf-example.meta.json", QString("%1/opt/apps/%2/files/schemas/configs/%3.json").arg(fileBackendLocalPerfix.value(), APP_ID, FILE_NAME));
+        metaGuard = new FileCopyGuard(":/data/dconf-example.meta.json", QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_ID, FILE_NAME));
 
         backendType.set("DSG_DCONFIG_BACKEND_TYPE", "FileBackend");
+        dsgDataDir.set("DSG_DATA_DIRS", "/usr/share/dsg");
     }
     static void TearDownTestCase() {
         QDir(fileBackendLocalPerfix.value()).removeRecursively();
@@ -47,6 +48,7 @@ protected:
         delete metaGuard;
 
         backendType.restore();
+        dsgDataDir.restore();
     }
     virtual void SetUp() override;
 
@@ -71,14 +73,47 @@ TEST_F(ut_DConfig, isValid) {
 }
 
 TEST_F(ut_DConfig, value) {
+    const QStringList array{"1", "2"};
+    QVariantMap map;
+    map.insert("key1", "value1");
+    map.insert("key2", "value2");
     {
         DConfig config(FILE_NAME);
         config.setValue("key2", "126");
         ASSERT_EQ(config.value("key2").toString(), QString("126"));
+
+        config.setValue("array", array);
+        ASSERT_EQ(config.value("array").toStringList(), array);
+
+        config.setValue("map", map);
+        ASSERT_EQ(config.value("map").toMap(), map);
     }
     {
         DConfig config(FILE_NAME);
         ASSERT_EQ(config.value("key2").toString(), QString("126"));
+        ASSERT_EQ(config.value("array").toStringList(), array);
+        ASSERT_EQ(config.value("map").toMap(), map);
+    }
+    {
+        DConfig config(FILE_NAME);
+        config.reset("canExit");
+        ASSERT_EQ(config.value("canExit").toBool(), true);
+
+        config.reset("key2");
+        ASSERT_EQ(config.value("key2").toString(), QString("125"));
+
+        config.reset("number");
+        ASSERT_EQ(config.value("number").toInt(), 1);
+
+        config.reset("array");
+        const QStringList &originArray {"value1", "value2"};
+        ASSERT_EQ(config.value("array").toStringList(), originArray);
+
+        config.reset("map");
+        QVariantMap originMap;
+        originMap.insert("key1", "value1");
+        originMap.insert("key2", "value2");
+        ASSERT_EQ(config.value("map").toMap(), originMap);
     }
 }
 
@@ -91,4 +126,14 @@ TEST_F(ut_DConfig, keyList) {
     }
 }
 
+TEST_F(ut_DConfig, OtherAppConfigfile) {
+
+    constexpr char const *APP_OTHER = "tests_other";
+    FileCopyGuard gurand(":/data/dconf-example_other_app_configure.meta.json", QString("%1" PREFIX"/share/dsg/configs/%2/%3.json").arg(fileBackendLocalPerfix.value(), APP_OTHER, FILE_NAME));
+
+    QScopedPointer<DConfig> config(DConfig::create(APP_OTHER, FILE_NAME));
+    ASSERT_TRUE(config->isValid());
+    ASSERT_EQ(config->value("appPublic").toString(), QString("publicValue"));
+}
+
 void ut_DConfig::SetUp() {}
index c3c156d031a07163ca1f88b24e947debf22e3f09..3fc0f9c08345c1b370b79daaa8276872df6b9cc6 100644 (file)
@@ -35,7 +35,6 @@ class ut_DConfigFile : public testing::Test
 {
 protected:
     static void SetUpTestCase() {
-        dsgDataDir.set("DSG_DATA_DIR", "/tmp/dconfig/dsg_data");
         home.set("HOME", "/tmp/home");
     }
     static void TearDownTestCase() {
@@ -46,9 +45,9 @@ protected:
 
     const char *APP_ID = "org.foo.appid";
     const char *FILE_NAME = "org.foo.name";
-    QString metaPath = QString("%1/opt/apps/%2/files/schemas/configs").arg(LocalPrefix, APP_ID);
-    QString metaGlobalPath = QString("%1%2/configs").arg(LocalPrefix, DStandardPaths::path(DStandardPaths::DSG::DataDir));
-    QString overridePath = QString("%1%2/configs/overrides/%3/%4").arg(LocalPrefix, DStandardPaths::path(DStandardPaths::DSG::DataDir), APP_ID, FILE_NAME);
+    QString metaPath = QString("%1" PREFIX"/share/dsg/configs/%2").arg(LocalPrefix, APP_ID);
+    QString metaGlobalPath = QString("%1" PREFIX"/share/dsg/configs").arg(LocalPrefix);
+    QString overridePath = QString("%1" PREFIX"/share/dsg/configs/overrides/%2/%3").arg(LocalPrefix, APP_ID, FILE_NAME);
     uint uid = getuid();
     static EnvGuard dsgDataDir;
     static EnvGuard home;
@@ -105,6 +104,86 @@ TEST_F(ut_DConfigFile, testLoad) {
     ASSERT_EQ(config.meta()->permissions("canExit"), DConfigFile::ReadWrite);
 }
 
+TEST_F(ut_DConfigFile, setValueTypeCheck) {
+
+    FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
+    DConfigFile config(APP_ID, FILE_NAME);
+    config.load(LocalPrefix);
+    QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
+    userCache->load(LocalPrefix);
+    {
+        const auto type = config.value("canExit", userCache.get()).type();
+        ASSERT_TRUE(config.setValue("canExit", false, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("canExit", "true", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("canExit", "true2", "test", userCache.get()));
+        ASSERT_EQ(config.value("canExit", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("key2", userCache.get()).type();
+        ASSERT_TRUE(config.setValue("key2", "121", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("key2", 121, "test", userCache.get()));
+        ASSERT_EQ(config.value("key2", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("number", userCache.get()).type();
+        ASSERT_TRUE(config.setValue("number", 1, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("number", 2.0, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("number", "3", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("number", "1ab", "test", userCache.get()));
+        ASSERT_EQ(config.value("number", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("array", userCache.get()).type();
+        const QStringList array{"value1", "value2"};
+        ASSERT_TRUE(config.setValue("array", QStringList(), "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("array", array, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("array", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("array", "", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("array", "value1", "test", userCache.get()));
+        ASSERT_EQ(config.value("array", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("array_map", userCache.get()).type();
+        QVariantList array;
+        QVariantMap map1;
+        map1["key1"] = "value1";
+        map1["key2"] = "value2";
+        array.append(map1);
+        ASSERT_EQ(config.value("array_map", userCache.get()).toList(), array);
+        ASSERT_TRUE(config.setValue("array_map", QVariantList(), "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("array_map", array, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("array_map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("array_map", "", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("array_map", "value1", "test", userCache.get()));
+        ASSERT_EQ(config.value("array_map", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("map", userCache.get()).type();
+        QVariantMap map;
+        map.insert("key1", "value1");
+        map.insert("key2", "value2");
+        ASSERT_TRUE(config.setValue("map", QVariantMap(), "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("map", map, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("map", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("map", QJsonDocument::fromJson("[]").toVariant(), "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("map", "key1", "test", userCache.get()));
+        ASSERT_EQ(config.value("map", userCache.get()).type(), type);
+    }
+    {
+        const auto type = config.value("map_array", userCache.get()).type();
+        QVariantMap map;
+        map.insert("key1", QStringList{"value1"});
+        map.insert("key2", QStringList{"value2"});
+        ASSERT_EQ(config.value("map_array", userCache.get()).toMap(), map);
+        ASSERT_TRUE(config.setValue("map_array", QVariantMap(), "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("map_array", map, "test", userCache.get()));
+        ASSERT_TRUE(config.setValue("map_array", QJsonDocument::fromJson("{}").toVariant(), "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("map_array", "", "test", userCache.get()));
+        ASSERT_FALSE(config.setValue("map_array", "value1", "test", userCache.get()));
+        ASSERT_EQ(config.value("map_array", userCache.get()).type(), type);
+    }
+}
+
 TEST_F(ut_DConfigFile, fileIODevice) {
 
     FileCopyGuard gurad(":/data/dconf-example.meta.json", QString("%1/%2.json").arg(metaPath, FILE_NAME));
@@ -169,6 +248,12 @@ TEST_F(ut_DConfigFile, meta) {
     QScopedPointer<DConfigCache> userCache(config.createUserCache(uid));
     ASSERT_TRUE(userCache->load(LocalPrefix));
     ASSERT_EQ(config.value("key3", userCache.get()), QString("application"));
+    const QStringList array{"value1", "value2"};
+    ASSERT_EQ(config.value("array", userCache.get()), array);
+    QVariantMap map;
+    map.insert("key1", "value1");
+    map.insert("key2", "value2");
+    ASSERT_EQ(config.value("map", userCache.get()).toMap(), map);
 }
 
 TEST_F(ut_DConfigFile, fileOverride) {
index 2d847ce35ddfe27b747508852d5be00ee608df73..2c08d7a364d35782e2f930167b97bfb3173206a1 100644 (file)
@@ -21,7 +21,9 @@
 
 #include <gtest/gtest.h>
 #include <QDir>
+#define private public
 #include "filesystem/dfilesystemwatcher.h"
+#undef private
 
 DCORE_USE_NAMESPACE
 
@@ -63,13 +65,18 @@ void ut_DFileSystemWatcher::TearDown()
 
 TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPath)
 {
+    if (!fileSystemWatcher->d_func()) return;
+
     fileSystemWatcher->addPath("/tmp/etc0");
+
     QStringList dirs = fileSystemWatcher->directories();
     ASSERT_TRUE(dirs.contains("/tmp/etc0"));
 }
 
 TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPaths)
 {
+    if (!fileSystemWatcher->d_func()) return;
+
     fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1");
     QStringList dirs = fileSystemWatcher->directories();
     ASSERT_TRUE(dirs.contains("/tmp/etc0"));
@@ -78,6 +85,8 @@ TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherAddPaths)
 
 TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePath)
 {
+    if (!fileSystemWatcher->d_func()) return;
+
     fileSystemWatcher->addPath("/tmp/etc0");
     QStringList dirs0 = fileSystemWatcher->directories();
     ASSERT_TRUE(dirs0.contains("/tmp/etc0"));
@@ -88,6 +97,8 @@ TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePath)
 
 TEST_F(ut_DFileSystemWatcher, testDFileSystemWatcherRemovePaths)
 {
+    if (!fileSystemWatcher->d_func()) return;
+
     fileSystemWatcher->addPaths( QStringList() << "/tmp/etc0" << "/tmp/etc1");
     QStringList dirs0 = fileSystemWatcher->directories();
     ASSERT_TRUE(dirs0.contains("/tmp/etc0"));
index 0cb2bdfd759c8492628e481cf781093ab5058470..8c9db93faa8dd7a1ab09302608329e1ccacd6df5 100644 (file)
@@ -77,24 +77,32 @@ TEST_F(ut_DFileWatcher, testDFileWatcherFileUrl)
 
 TEST_F(ut_DFileWatcher, testDFileWatcherStartWatcher)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     fileWatcher->setEnabledSubfileWatcher(QUrl());
     ASSERT_TRUE(fileWatcher->startWatcher());
 }
 
 TEST_F(ut_DFileWatcher, testDFileWatcherStopWatcher)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     ASSERT_TRUE(fileWatcher->stopWatcher());
 }
 
 TEST_F(ut_DFileWatcher, testDFileWatcherRestartWatcher)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     ASSERT_TRUE(fileWatcher->restartWatcher());
 }
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileDeleted)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileDeleted);
     QFile file("/tmp/etc/test");
@@ -108,6 +116,8 @@ TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileDeleted)
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileAttributeChanged)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileAttributeChanged);
     QFile file("/tmp/etc/test");
@@ -127,6 +137,8 @@ TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileAttributeChanged)
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileMoved)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileMoved);
     QString oldFile("/tmp/etc/test");
@@ -141,6 +153,8 @@ TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileMoved)
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherSubfileCreated)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::subfileCreated);
     QFile file("/tmp/etc/test");
@@ -158,6 +172,8 @@ TEST_F(ut_DFileWatcher, testDFileSystemWatcherSubfileCreated)
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileModified)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileModified);
     QFile file("/tmp/etc/test");
@@ -175,6 +191,8 @@ TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileModified)
 
 TEST_F(ut_DFileWatcher, testDFileSystemWatcherFileClosed)
 {
+    if (!fileWatcher->startWatcher()) return;
+
     ASSERT_TRUE(fileWatcher->startWatcher());
     QSignalSpy spy(fileWatcher, &DBaseFileWatcher::fileClosed);
     QFile file("/tmp/etc/test");
index 98d841851c92a013a50f0efd86250672aad30b61..abf714051a3debd8fd204e7009dfd2a8e80af161 100644 (file)
@@ -68,6 +68,7 @@ void ut_DFileWatcherManager::TearDown()
 TEST_F(ut_DFileWatcherManager, testDFileWatcherManagerAdd)
 {
     auto watcher = fileWatcherManager->add("/tmp/test");
+    if (!watcher->startWatcher()) return;
 
     // test fileDeleted signal
     QSignalSpy spy(watcher, &DBaseFileWatcher::fileDeleted);
index 740c528e6cf7c93f36fc4e7c53b67896a79345da..e021e96eead377ba92d86f2da071d925e23e4466 100644 (file)
@@ -309,7 +309,7 @@ TEST_F(ut_DUtil, testOsVersion)
     entry.setStringValue("专业版", "EditionName[zh_CN]", "Version");
     entry.setStringValue("20", "MajorVersion", "Version");
     entry.setStringValue("100A", "MinorVersion", "Version");
-    entry.setStringValue("11018.107", "OsBuild", "Version");
+    entry.setStringValue("11Z18.107", "OsBuild", "Version");
     ASSERT_TRUE(entry.save());
 
     ASSERT_TRUE(DSysInfo::uosSystemName(QLocale("C")) == "UnionTech OS Desktop");
index 6060214d27401220c4c481cb97f6d1c4795b6e38..f62a5a856945c8fdd9a0d1b0298fd51b23933f43 100644 (file)
@@ -100,7 +100,7 @@ TEST_F(ut_QSettingsBackend, testQSettingsBackendDoOption)
 {
     QPointer<DSettings> tmpSetting = DSettings::fromJson(jsonContent.toLatin1());
     QScopedPointer<DSettings> scopeSettings(tmpSetting.data());
-    static QSettingBackend qBackend("/tmp/test.ini");
+    QSettingBackend qBackend("/tmp/test.ini");
     scopeSettings->setBackend(&qBackend);
     Q_EMIT qBackend.setOption("Test", true);
 
@@ -108,4 +108,7 @@ TEST_F(ut_QSettingsBackend, testQSettingsBackendDoOption)
     ASSERT_TRUE(!qKeys.isEmpty());
     QVariant value = qBackend.getOption("Test");
     ASSERT_TRUE(!value.toBool());
+
+    // ensure `DSettings` is released before `SettingBackend` if `doSetOption` maybe execute.
+    scopeSettings.reset();
 }