From: Boyuan Yang Date: Tue, 12 Apr 2022 20:08:46 +0000 (-0400) Subject: New upstream version 5.5.30 X-Git-Tag: archive/raspbian/5.7.12-2+rpi1^2~3^2~8 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=5a874f8b074314c7d502ae479d24232ffeb83cf4;p=dtkcore.git New upstream version 5.5.30 --- diff --git a/.github/workflows/backup-to-gitlab.yml b/.github/workflows/backup-to-gitlab.yml new file mode 100644 index 0000000..2ebbd4d --- /dev/null +++ b/.github/workflows/backup-to-gitlab.yml @@ -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 index 0000000..f95fadb --- /dev/null +++ b/.github/workflows/call-build-deb.yml @@ -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 index 0000000..9888614 --- /dev/null +++ b/.github/workflows/call-chatOps.yml @@ -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 index 0000000..69f5b2d --- /dev/null +++ b/.github/workflows/call-commitlint.yml @@ -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 index 0000000..dfaef25 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -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 diff --git a/src/base/dsingleton.h b/src/base/dsingleton.h index a6dfa7e..7af8853 100644 --- a/src/base/dsingleton.h +++ b/src/base/dsingleton.h @@ -58,17 +58,26 @@ template 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 diff --git a/src/dbus/org.desktopspec.ConfigManager.Manager.xml b/src/dbus/org.desktopspec.ConfigManager.Manager.xml index 9e606ea..65881d6 100644 --- a/src/dbus/org.desktopspec.ConfigManager.Manager.xml +++ b/src/dbus/org.desktopspec.ConfigManager.Manager.xml @@ -9,6 +9,9 @@ + + + diff --git a/src/dconfig.cpp b/src/dconfig.cpp index 2db9b6b..f8f6b80 100644 --- a/src/dconfig.cpp +++ b/src/dconfig.cpp @@ -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 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 activatableNames = QDBusConnection::systemBus().interface()-> + callWithArgumentList(QDBus::AutoDetect, + QLatin1String("ListActivatableNames"), + QList()); +// 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()) { + // 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(); + 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 diff --git a/src/dconfig.h b/src/dconfig.h index 38584c2..317e30c 100644 --- a/src/dconfig.h +++ b/src/dconfig.h @@ -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 diff --git a/src/dconfigfile.cpp b/src/dconfigfile.cpp index 51346c2..8db11d5 100644 --- a/src/dconfigfile.cpp +++ b/src/dconfigfile.cpp @@ -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 &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 &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 diff --git a/src/ddesktopentry.cpp b/src/ddesktopentry.cpp index 2ececb5..f5efa94 100644 --- a/src/ddesktopentry.cpp +++ b/src/ddesktopentry.cpp @@ -339,6 +339,7 @@ bool DDesktopEntryPrivate::fuzzyLoad() } } + setStatus(DDesktopEntry::NoError); return true; } diff --git a/src/dsysinfo.cpp b/src/dsysinfo.cpp index 5df0d84..02c5789 100644 --- a/src/dsysinfo.cpp +++ b/src/dsysinfo.cpp @@ -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); diff --git a/src/filesystem/dfilesystemwatcher_linux.cpp b/src/filesystem/dfilesystemwatcher_linux.cpp index 419364c..864073a 100644 --- a/src/filesystem/dfilesystemwatcher_linux.cpp +++ b/src/filesystem/dfilesystemwatcher_linux.cpp @@ -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); + } } /*! diff --git a/src/filesystem/dstandardpaths.cpp b/src/filesystem/dstandardpaths.cpp index b2b9b53..798045b 100644 --- a/src/filesystem/dstandardpaths.cpp +++ b/src/filesystem/dstandardpaths.cpp @@ -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) diff --git a/src/filesystem/dstandardpaths.h b/src/filesystem/dstandardpaths.h index 4cdbf5b..a2daf78 100644 --- a/src/filesystem/dstandardpaths.h +++ b/src/filesystem/dstandardpaths.h @@ -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); diff --git a/src/filesystem/filesystem.pri b/src/filesystem/filesystem.pri index d97756d..e15ee08 100644 --- a/src/filesystem/filesystem.pri +++ b/src/filesystem/filesystem.pri @@ -2,6 +2,8 @@ include($$PWD/private/private.pri) INCLUDEPATH += $$PWD/../base +DEFINES += PREFIX=\\\"$$PREFIX\\\" + HEADERS += \ $$PWD/dbasefilewatcher.h \ $$PWD/dfilesystemwatcher.h \ diff --git a/src/settings/backend/gsettingsbackend.cpp b/src/settings/backend/gsettingsbackend.cpp index 5c2e073..4874f13 100644 --- a/src/settings/backend/gsettingsbackend.cpp +++ b/src/settings/backend/gsettingsbackend.cpp @@ -5,7 +5,12 @@ #include #include #include + +#if QT_HAS_INCLUDE() #include +#else +#include +#endif #include diff --git a/src/settings/dsettings.cpp b/src/settings/dsettings.cpp index 512ccc9..2d2663e 100644 --- a/src/settings/dsettings.cpp +++ b/src/settings/dsettings.cpp @@ -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(); diff --git a/tests/data.qrc b/tests/data.qrc index 1c1b471..09e108a 100644 --- a/tests/data.qrc +++ b/tests/data.qrc @@ -7,5 +7,6 @@ data/dconf-override/dconf-example.override.a.b.json data/dconf-global.meta.json data/dconf-global.override.json + data/dconf-example_other_app_configure.meta.json diff --git a/tests/data/dconf-example.meta.json b/tests/data/dconf-example.meta.json index e3ed151..d591a1c 100755 --- a/tests/data/dconf-example.meta.json +++ b/tests/data/dconf-example.meta.json @@ -31,6 +31,62 @@ "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 index 0000000..f6555ac --- /dev/null +++ b/tests/data/dconf-example_other_app_configure.meta.json @@ -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" + } + } +} diff --git a/tests/test-recoverage-qmake.sh b/tests/test-recoverage-qmake.sh index 3a452a4..f89898b 100755 --- a/tests/test-recoverage-qmake.sh +++ b/tests/test-recoverage-qmake.sh @@ -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 diff --git a/tests/tests.pro b/tests/tests.pro index 470e5d8..a3caf63 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -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 diff --git a/tests/ut_dconfig.cpp b/tests/ut_dconfig.cpp index 7e99d81..f3b476e 100644 --- a/tests/ut_dconfig.cpp +++ b/tests/ut_dconfig.cpp @@ -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 config(DConfig::create(APP_OTHER, FILE_NAME)); + ASSERT_TRUE(config->isValid()); + ASSERT_EQ(config->value("appPublic").toString(), QString("publicValue")); +} + void ut_DConfig::SetUp() {} diff --git a/tests/ut_dconfigfile.cpp b/tests/ut_dconfigfile.cpp index c3c156d..3fc0f9c 100644 --- a/tests/ut_dconfigfile.cpp +++ b/tests/ut_dconfigfile.cpp @@ -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 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 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) { diff --git a/tests/ut_dfilesystemwatcher.cpp b/tests/ut_dfilesystemwatcher.cpp index 2d847ce..2c08d7a 100644 --- a/tests/ut_dfilesystemwatcher.cpp +++ b/tests/ut_dfilesystemwatcher.cpp @@ -21,7 +21,9 @@ #include #include +#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")); diff --git a/tests/ut_dfilewatcher.cpp b/tests/ut_dfilewatcher.cpp index 0cb2bdf..8c9db93 100644 --- a/tests/ut_dfilewatcher.cpp +++ b/tests/ut_dfilewatcher.cpp @@ -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"); diff --git a/tests/ut_dfilewatchermanager.cpp b/tests/ut_dfilewatchermanager.cpp index 98d8418..abf7140 100644 --- a/tests/ut_dfilewatchermanager.cpp +++ b/tests/ut_dfilewatchermanager.cpp @@ -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); diff --git a/tests/ut_dutil.cpp b/tests/ut_dutil.cpp index 740c528..e021e96 100644 --- a/tests/ut_dutil.cpp +++ b/tests/ut_dutil.cpp @@ -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"); diff --git a/tests/ut_qsettingsbackend.cpp b/tests/ut_qsettingsbackend.cpp index 6060214..f62a5a8 100644 --- a/tests/ut_qsettingsbackend.cpp +++ b/tests/ut_qsettingsbackend.cpp @@ -100,7 +100,7 @@ TEST_F(ut_QSettingsBackend, testQSettingsBackendDoOption) { QPointer tmpSetting = DSettings::fromJson(jsonContent.toLatin1()); QScopedPointer 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(); }