data/SettingsMergePlan.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "SettingsMergePlan.h" | ||
| 2 | |||
| 3 | #include <QJsonArray> | ||
| 4 | #include <QJsonDocument> | ||
| 5 | #include <QJsonObject> | ||
| 6 | #include <iterator> | ||
| 7 | |||
| 8 | namespace SettingsMergePlan { | ||
| 9 | |||
| 10 | namespace { | ||
| 11 | |||
| 12 | 133 | QString json(const SyncPayload &payload) { | |
| 13 |
4/4✓ Branch 2 → 3 taken 133 times.
✓ Branch 3 → 4 taken 133 times.
✓ Branch 4 → 5 taken 133 times.
✓ Branch 5 → 6 taken 133 times.
|
133 | return QString::fromUtf8(QJsonDocument(payload.toJson()).toJson(QJsonDocument::Compact)); |
| 14 | } | ||
| 15 | |||
| 16 | 53 | SyncPayload payloadOf(const QString &text) { | |
| 17 |
4/4✓ Branch 2 → 3 taken 53 times.
✓ Branch 3 → 4 taken 53 times.
✓ Branch 4 → 5 taken 53 times.
✓ Branch 5 → 6 taken 53 times.
|
53 | return SyncPayload::fromJson(QJsonDocument::fromJson(text.toUtf8()).object()); |
| 18 | } | ||
| 19 | |||
| 20 | 31 | QString listText(const QStringList &values) { | |
| 21 |
4/4✓ Branch 2 → 3 taken 31 times.
✓ Branch 3 → 4 taken 31 times.
✓ Branch 4 → 5 taken 31 times.
✓ Branch 5 → 6 taken 31 times.
|
31 | return QString::fromUtf8(QJsonDocument(QJsonArray::fromStringList(values)).toJson(QJsonDocument::Compact)); |
| 22 | } | ||
| 23 | |||
| 24 | // Account ids come from a sync message and may contain ':', the separator | ||
| 25 | // of the item keys ("davAccounts:<id>", "davAccounts:<id>:books"): in a | ||
| 26 | // key '%' and ':' of the id are escaped, so every id maps to its own keys. | ||
| 27 | 48 | QString keyOfAccount(const QString &id) { | |
| 28 | 48 | QString key = id; | |
| 29 |
1/1✓ Branch 8 → 9 taken 48 times.
|
48 | key.replace(QLatin1Char('%'), QStringLiteral("%25")); |
| 30 |
1/1✓ Branch 16 → 17 taken 48 times.
|
48 | key.replace(QLatin1Char(':'), QStringLiteral("%3A")); |
| 31 | 48 | return key; | |
| 32 | ✗ | } | |
| 33 | |||
| 34 | 22 | QString accountOfKey(const QString &key) { | |
| 35 | 22 | QString id = key; | |
| 36 |
1/1✓ Branch 9 → 10 taken 22 times.
|
44 | id.replace(QStringLiteral("%3A"), QStringLiteral(":")); |
| 37 |
1/1✓ Branch 20 → 21 taken 22 times.
|
44 | id.replace(QStringLiteral("%25"), QStringLiteral("%")); |
| 38 | 22 | return id; | |
| 39 | ✗ | } | |
| 40 | |||
| 41 | 64 | const SyncPayload::CardDavAccount *accountOf(const SyncPayload &payload, const QString &id) { | |
| 42 |
2/2✓ Branch 10 → 4 taken 60 times.
✓ Branch 10 → 11 taken 21 times.
|
81 | for (const SyncPayload::CardDavAccount &account : payload.carddavAccounts) |
| 43 |
2/2✓ Branch 6 → 7 taken 43 times.
✓ Branch 6 → 8 taken 17 times.
|
60 | if (account.id == id) |
| 44 | 43 | return &account; | |
| 45 | 21 | return nullptr; | |
| 46 | } | ||
| 47 | |||
| 48 | 46 | const SyncPayload::CalDavSyncConfig *configOf(const SyncPayload &payload, const QString &id) { | |
| 49 |
2/2✓ Branch 10 → 4 taken 44 times.
✓ Branch 10 → 11 taken 5 times.
|
49 | for (const SyncPayload::CalDavSyncConfig &config : payload.caldavConfigs) |
| 50 |
2/2✓ Branch 6 → 7 taken 41 times.
✓ Branch 6 → 8 taken 3 times.
|
44 | if (config.carddavAccountId == id) |
| 51 | 41 | return &config; | |
| 52 | 5 | return nullptr; | |
| 53 | } | ||
| 54 | |||
| 55 | 33 | QString configText(const SyncPayload::CalDavSyncConfig *config) { | |
| 56 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 31 times.
|
33 | if (!config) |
| 57 | 2 | return {}; | |
| 58 |
1/1✓ Branch 4 → 5 taken 31 times.
|
31 | QJsonObject object; |
| 59 |
2/2✓ Branch 5 → 6 taken 31 times.
✓ Branch 10 → 11 taken 31 times.
|
62 | object.insert(QStringLiteral("selected"), QJsonArray::fromStringList(config->selectedCalendars)); |
| 60 |
2/2✓ Branch 15 → 16 taken 31 times.
✓ Branch 20 → 21 taken 31 times.
|
62 | object.insert(QStringLiteral("readOnly"), QJsonArray::fromStringList(config->readOnlyCalendars)); |
| 61 |
3/3✓ Branch 25 → 26 taken 31 times.
✓ Branch 26 → 27 taken 31 times.
✓ Branch 27 → 28 taken 31 times.
|
31 | return QString::fromUtf8(QJsonDocument(object).toJson(QJsonDocument::Compact)); |
| 62 | 31 | } | |
| 63 | |||
| 64 | 6 | QString accountText(const SyncPayload::CardDavAccount &account) { | |
| 65 |
1/1✓ Branch 2 → 3 taken 6 times.
|
6 | QJsonObject object; |
| 66 |
2/2✓ Branch 3 → 4 taken 6 times.
✓ Branch 7 → 8 taken 6 times.
|
12 | object.insert(QStringLiteral("serverUrl"), account.serverUrl); |
| 67 |
2/2✓ Branch 11 → 12 taken 6 times.
✓ Branch 15 → 16 taken 6 times.
|
12 | object.insert(QStringLiteral("username"), account.username); |
| 68 |
2/2✓ Branch 19 → 20 taken 6 times.
✓ Branch 24 → 25 taken 6 times.
|
12 | object.insert(QStringLiteral("selectedBooks"), QJsonArray::fromStringList(account.selectedBooks)); |
| 69 |
3/3✓ Branch 29 → 30 taken 6 times.
✓ Branch 30 → 31 taken 6 times.
✓ Branch 31 → 32 taken 6 times.
|
12 | return QString::fromUtf8(QJsonDocument(object).toJson(QJsonDocument::Compact)); |
| 70 | 6 | } | |
| 71 | |||
| 72 | // Identities and templates as item values: compact JSON (keys sorted). | ||
| 73 | 66 | QMap<QString, QString> textMap(const QMap<QString, QJsonObject> &objects) { | |
| 74 | 66 | QMap<QString, QString> out; | |
| 75 |
4/4✓ Branch 2 → 3 taken 66 times.
✓ Branch 14 → 15 taken 86 times.
✓ Branch 16 → 4 taken 20 times.
✓ Branch 16 → 17 taken 66 times.
|
86 | for (auto it = objects.constBegin(); it != objects.constEnd(); ++it) |
| 76 |
4/4✓ Branch 5 → 6 taken 20 times.
✓ Branch 6 → 7 taken 20 times.
✓ Branch 7 → 8 taken 20 times.
✓ Branch 9 → 10 taken 20 times.
|
20 | out.insert(it.key(), QString::fromUtf8(QJsonDocument(it.value()).toJson(QJsonDocument::Compact))); |
| 77 | 66 | return out; | |
| 78 | ✗ | } | |
| 79 | |||
| 80 | struct Builder { | ||
| 81 | Merge::Plan *plan; | ||
| 82 | bool threeWay; | ||
| 83 | |||
| 84 | 177 | void add(const QString &id, Merge::Kind kind, Merge::Value base, Merge::Value local, Merge::Value server, | |
| 85 | const QString &group = {}) { | ||
| 86 | 177 | Merge::Item item; | |
| 87 | 177 | item.id = id; | |
| 88 |
1/1✓ Branch 6 → 7 taken 177 times.
|
177 | item.name = id.section(QLatin1Char(':'), 0, 0); |
| 89 |
1/1✓ Branch 12 → 13 taken 177 times.
|
177 | item.context = id.section(QLatin1Char(':'), 1); |
| 90 | 177 | item.kind = kind; | |
| 91 | 177 | item.group = group; | |
| 92 |
2/2✓ Branch 16 → 17 taken 145 times.
✓ Branch 16 → 18 taken 32 times.
|
177 | item.base = threeWay ? base : Merge::Value{}; |
| 93 | 177 | item.local = local; | |
| 94 | 177 | item.server = server; | |
| 95 |
2/2✓ Branch 23 → 24 taken 145 times.
✓ Branch 23 → 26 taken 32 times.
|
177 | if (threeWay) { |
| 96 |
1/1✓ Branch 24 → 25 taken 145 times.
|
145 | item.state = Merge::stateOf(item.base, local, server); |
| 97 | } else { | ||
| 98 |
5/6✓ Branch 26 → 27 taken 29 times.
✓ Branch 26 → 31 taken 3 times.
✓ Branch 27 → 28 taken 29 times.
✗ Branch 27 → 30 not taken.
✓ Branch 29 → 30 taken 26 times.
✓ Branch 29 → 31 taken 3 times.
|
32 | const bool same = local.present == server.present && (!local.present || local.text == server.text); |
| 99 |
2/2✓ Branch 32 → 33 taken 26 times.
✓ Branch 32 → 34 taken 6 times.
|
32 | item.state = same ? Merge::State::Same : Merge::State::Conflict; |
| 100 | } | ||
| 101 |
1/1✓ Branch 36 → 37 taken 177 times.
|
177 | plan->items.append(item); |
| 102 | 177 | } | |
| 103 | |||
| 104 | 65 | void addMap(const QString &category, const QMap<QString, QString> *base, const QMap<QString, QString> &local, | |
| 105 | const QMap<QString, QString> &remote) { | ||
| 106 |
3/3✓ Branch 2 → 3 taken 65 times.
✓ Branch 3 → 4 taken 65 times.
✓ Branch 4 → 5 taken 65 times.
|
65 | QStringList keys = local.keys() + remote.keys(); |
| 107 |
2/2✓ Branch 7 → 8 taken 52 times.
✓ Branch 7 → 12 taken 13 times.
|
65 | if (base) |
| 108 |
2/2✓ Branch 8 → 9 taken 52 times.
✓ Branch 9 → 10 taken 52 times.
|
52 | keys += base->keys(); |
| 109 |
1/1✓ Branch 12 → 13 taken 65 times.
|
65 | keys.removeDuplicates(); |
| 110 |
1/1✓ Branch 13 → 14 taken 65 times.
|
65 | keys.sort(); |
| 111 | 183 | const auto value = [](const QMap<QString, QString> *map, const QString &key) { | |
| 112 |
8/10✓ Branch 2 → 3 taken 173 times.
✓ Branch 2 → 8 taken 10 times.
✓ Branch 3 → 4 taken 173 times.
✓ Branch 4 → 5 taken 164 times.
✓ Branch 4 → 8 taken 9 times.
✓ Branch 6 → 7 taken 164 times.
✓ Branch 10 → 11 taken 164 times.
✓ Branch 10 → 12 taken 19 times.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 16 not taken.
|
366 | return map && map->contains(key) ? Merge::Value{true, map->value(key)} : Merge::Value{}; |
| 113 | }; | ||
| 114 |
4/4✓ Branch 14 → 15 taken 65 times.
✓ Branch 15 → 16 taken 65 times.
✓ Branch 35 → 17 taken 61 times.
✓ Branch 35 → 36 taken 65 times.
|
126 | for (const QString &key : keys) |
| 115 |
5/5✓ Branch 20 → 21 taken 61 times.
✓ Branch 21 → 22 taken 61 times.
✓ Branch 24 → 25 taken 61 times.
✓ Branch 25 → 26 taken 61 times.
✓ Branch 26 → 27 taken 61 times.
|
183 | add(category + QLatin1Char(':') + key, Merge::Kind::Scalar, value(base, key), value(&local, key), |
| 116 |
1/1✓ Branch 19 → 20 taken 61 times.
|
122 | value(&remote, key)); |
| 117 | 65 | } | |
| 118 | }; | ||
| 119 | |||
| 120 | 66 | QStringList sharedCategories(const SyncPayload &local, const SyncPayload &remote) { | |
| 121 | 66 | QStringList shared; | |
| 122 |
2/2✓ Branch 15 → 4 taken 211 times.
✓ Branch 15 → 16 taken 66 times.
|
277 | for (const QString &category : local.enabledCategories) |
| 123 |
3/6✓ Branch 6 → 7 taken 211 times.
✗ Branch 6 → 10 not taken.
✓ Branch 8 → 9 taken 211 times.
✗ Branch 8 → 10 not taken.
✓ Branch 11 → 12 taken 211 times.
✗ Branch 11 → 13 not taken.
|
211 | if (remote.enabledCategories.contains(category) && !shared.contains(category)) |
| 124 |
1/1✓ Branch 12 → 13 taken 211 times.
|
211 | shared.append(category); |
| 125 | 66 | return shared; | |
| 126 | ✗ | } | |
| 127 | |||
| 128 | 92 | const SyncPayload &sourceOf(const Merge::Item &item, const SyncPayload &base, const SyncPayload &local, | |
| 129 | const SyncPayload &remote) { | ||
| 130 |
2/3✓ Branch 2 → 3 taken 28 times.
✗ Branch 2 → 4 not taken.
✓ Branch 2 → 5 taken 64 times.
|
92 | switch (item.choice) { |
| 131 | 28 | case Merge::Choice::Server: | |
| 132 | 28 | return remote; | |
| 133 | ✗ | case Merge::Choice::Base: | |
| 134 | ✗ | return base; | |
| 135 | 64 | default: | |
| 136 | 64 | return local; | |
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | } // namespace | ||
| 141 | |||
| 142 | ✗ | QString categoryOf(const Merge::Item &item) { return item.name; } | |
| 143 | |||
| 144 | 47 | Merge::Plan analyze(const std::optional<SyncPayload> &base, const SyncPayload &local, const SyncPayload &remote) { | |
| 145 | 47 | Merge::Plan plan; | |
| 146 | 47 | plan.domain = QStringLiteral("settings"); | |
| 147 |
3/3✓ Branch 9 → 10 taken 39 times.
✓ Branch 9 → 12 taken 8 times.
✓ Branch 11 → 13 taken 39 times.
|
47 | plan.baseText = base ? json(*base) : QString(); |
| 148 |
1/1✓ Branch 15 → 16 taken 47 times.
|
47 | plan.localText = json(local); |
| 149 |
1/1✓ Branch 18 → 19 taken 47 times.
|
47 | plan.serverText = json(remote); |
| 150 | 47 | Builder builder{&plan, base.has_value()}; | |
| 151 |
1/1✓ Branch 22 → 23 taken 47 times.
|
47 | const SyncPayload empty; |
| 152 |
2/2✓ Branch 24 → 25 taken 39 times.
✓ Branch 24 → 26 taken 8 times.
|
47 | const SyncPayload &b = base ? *base : empty; |
| 153 |
1/1✓ Branch 27 → 28 taken 47 times.
|
47 | const QStringList categories = sharedCategories(local, remote); |
| 154 |
2/2✓ Branch 34 → 35 taken 30 times.
✓ Branch 34 → 46 taken 17 times.
|
47 | if (categories.contains(QStringLiteral("folderIcons"))) |
| 155 |
3/3✓ Branch 36 → 37 taken 26 times.
✓ Branch 36 → 38 taken 4 times.
✓ Branch 42 → 43 taken 30 times.
|
60 | builder.addMap(QStringLiteral("folderIcons"), base ? &b.folderIcons : nullptr, local.folderIcons, |
| 156 | 30 | remote.folderIcons); | |
| 157 |
2/2✓ Branch 52 → 53 taken 12 times.
✓ Branch 52 → 64 taken 35 times.
|
47 | if (categories.contains(QStringLiteral("folderColors"))) |
| 158 |
3/3✓ Branch 54 → 55 taken 9 times.
✓ Branch 54 → 56 taken 3 times.
✓ Branch 60 → 61 taken 12 times.
|
24 | builder.addMap(QStringLiteral("folderColors"), base ? &b.folderColors : nullptr, local.folderColors, |
| 159 | 12 | remote.folderColors); | |
| 160 |
2/2✓ Branch 70 → 71 taken 12 times.
✓ Branch 70 → 82 taken 35 times.
|
47 | if (categories.contains(QStringLiteral("calendarColors"))) |
| 161 |
3/3✓ Branch 72 → 73 taken 9 times.
✓ Branch 72 → 74 taken 3 times.
✓ Branch 78 → 79 taken 12 times.
|
24 | builder.addMap(QStringLiteral("calendarColors"), base ? &b.calendarColors : nullptr, local.calendarColors, |
| 162 | 12 | remote.calendarColors); | |
| 163 |
2/2✓ Branch 88 → 89 taken 12 times.
✓ Branch 88 → 129 taken 35 times.
|
47 | if (categories.contains(QStringLiteral("hiddenFolders"))) { |
| 164 |
2/2✓ Branch 89 → 90 taken 12 times.
✓ Branch 90 → 91 taken 12 times.
|
12 | QStringList paths = local.hiddenFolders + remote.hiddenFolders + b.hiddenFolders; |
| 165 |
1/1✓ Branch 92 → 93 taken 12 times.
|
12 | paths.removeDuplicates(); |
| 166 |
4/4✓ Branch 93 → 94 taken 12 times.
✓ Branch 94 → 95 taken 12 times.
✓ Branch 126 → 96 taken 16 times.
✓ Branch 126 → 127 taken 12 times.
|
28 | for (const QString &path : paths) { |
| 167 | 56 | const auto in = [&path](const QStringList &list) { return Merge::Value{list.contains(path), path}; }; | |
| 168 |
2/2✓ Branch 104 → 105 taken 16 times.
✓ Branch 105 → 106 taken 16 times.
|
48 | builder.add(QStringLiteral("hiddenFolders:") + path, Merge::Kind::SetValue, in(b.hiddenFolders), |
| 169 | 32 | in(local.hiddenFolders), in(remote.hiddenFolders)); | |
| 170 |
2/2✓ Branch 114 → 115 taken 4 times.
✓ Branch 114 → 124 taken 12 times.
|
16 | if (!base) // two-way: a path on one side only is a difference, not "added there" |
| 171 |
1/1✓ Branch 120 → 121 taken 4 times.
|
8 | plan.items.last().state = in(local.hiddenFolders).present == in(remote.hiddenFolders).present |
| 172 |
2/2✓ Branch 117 → 118 taken 3 times.
✓ Branch 117 → 119 taken 1 time.
|
4 | ? Merge::State::Same |
| 173 | : Merge::State::Conflict; | ||
| 174 | } | ||
| 175 | 12 | } | |
| 176 |
2/2✓ Branch 135 → 136 taken 14 times.
✓ Branch 135 → 237 taken 33 times.
|
47 | if (categories.contains(QStringLiteral("davAccounts"))) { |
| 177 | 14 | QStringList seen; | |
| 178 |
2/2✓ Branch 234 → 138 taken 19 times.
✓ Branch 234 → 235 taken 14 times.
|
33 | for (const SyncPayload::CardDavAccount &remoteAccount : remote.carddavAccounts) { |
| 179 | 19 | const QString id = remoteAccount.id; | |
| 180 |
2/2✓ Branch 141 → 142 taken 1 time.
✓ Branch 141 → 143 taken 18 times.
|
19 | if (seen.contains(id)) |
| 181 | 1 | continue; // a repeated id: the first one counts, as in accountOf() | |
| 182 |
1/1✓ Branch 143 → 144 taken 18 times.
|
18 | seen.append(id); |
| 183 |
1/1✓ Branch 144 → 145 taken 18 times.
|
18 | const QString keyId = keyOfAccount(id); |
| 184 |
1/1✓ Branch 145 → 146 taken 18 times.
|
18 | const SyncPayload::CardDavAccount *localAccount = accountOf(local, id); |
| 185 |
1/1✓ Branch 146 → 147 taken 18 times.
|
18 | const SyncPayload::CardDavAccount *baseAccount = accountOf(b, id); |
| 186 |
2/2✓ Branch 147 → 148 taken 6 times.
✓ Branch 147 → 171 taken 12 times.
|
18 | if (!localAccount) { |
| 187 |
2/2✓ Branch 154 → 155 taken 6 times.
✓ Branch 155 → 156 taken 6 times.
|
18 | builder.add(accountItemId(id), Merge::Kind::Component, |
| 188 |
1/3✗ Branch 150 → 151 not taken.
✓ Branch 150 → 152 taken 6 times.
✗ Branch 151 → 153 not taken.
|
12 | baseAccount ? Merge::Value{true, accountText(*baseAccount)} : Merge::Value{}, {}, |
| 189 | {true, accountText(remoteAccount)}); | ||
| 190 |
4/6✓ Branch 163 → 164 taken 4 times.
✓ Branch 163 → 165 taken 2 times.
✓ Branch 164 → 165 taken 4 times.
✗ Branch 164 → 166 not taken.
✓ Branch 167 → 168 taken 6 times.
✗ Branch 167 → 170 not taken.
|
6 | if (!base || !baseAccount) // new on the server: offered, never a forced conflict |
| 191 |
1/1✓ Branch 168 → 169 taken 6 times.
|
6 | plan.items.last().state = Merge::State::ServerOnly; |
| 192 | 6 | continue; | |
| 193 | } | ||
| 194 | // Identity changes are never adopted (applyRemote): only selections. | ||
| 195 |
5/6✓ Branch 172 → 173 taken 11 times.
✓ Branch 172 → 175 taken 1 time.
✗ Branch 174 → 175 not taken.
✓ Branch 174 → 176 taken 11 times.
✓ Branch 177 → 178 taken 1 time.
✓ Branch 177 → 179 taken 11 times.
|
12 | if (localAccount->serverUrl != remoteAccount.serverUrl || localAccount->username != remoteAccount.username) |
| 196 | 1 | continue; | |
| 197 |
1/1✓ Branch 182 → 183 taken 11 times.
|
11 | const QString group = QStringLiteral("dav:") + keyId; |
| 198 |
2/2✓ Branch 193 → 194 taken 11 times.
✓ Branch 194 → 195 taken 11 times.
|
22 | builder.add(accountItemId(id, QStringLiteral("books")), Merge::Kind::Scalar, |
| 199 |
3/3✓ Branch 187 → 188 taken 9 times.
✓ Branch 187 → 189 taken 2 times.
✓ Branch 188 → 190 taken 9 times.
|
22 | baseAccount ? Merge::Value{true, listText(baseAccount->selectedBooks)} : Merge::Value{}, |
| 200 | 22 | {true, listText(localAccount->selectedBooks)}, {true, listText(remoteAccount.selectedBooks)}, group); | |
| 201 |
1/1✓ Branch 201 → 202 taken 11 times.
|
11 | const SyncPayload::CalDavSyncConfig *baseConfig = configOf(b, id); |
| 202 |
1/1✓ Branch 202 → 203 taken 11 times.
|
11 | const SyncPayload::CalDavSyncConfig *localConfig = configOf(local, id); |
| 203 |
1/1✓ Branch 203 → 204 taken 11 times.
|
11 | const SyncPayload::CalDavSyncConfig *remoteConfig = configOf(remote, id); |
| 204 |
1/4✗ Branch 204 → 205 not taken.
✓ Branch 204 → 206 taken 11 times.
✗ Branch 205 → 206 not taken.
✗ Branch 205 → 221 not taken.
|
11 | if (localConfig || remoteConfig) |
| 205 |
2/2✓ Branch 212 → 213 taken 11 times.
✓ Branch 213 → 214 taken 11 times.
|
11 | builder.add(accountItemId(id, QStringLiteral("calendars")), Merge::Kind::Scalar, |
| 206 | 22 | {baseConfig != nullptr, configText(baseConfig)}, {localConfig != nullptr, configText(localConfig)}, | |
| 207 | 11 | {remoteConfig != nullptr, configText(remoteConfig)}, group); | |
| 208 | 26 | } | |
| 209 | 14 | } | |
| 210 | // Sprint 108: addresses both installations have (applyRemote never adds | ||
| 211 | // one), and every template (added, changed or removed). | ||
| 212 |
2/2✓ Branch 243 → 244 taken 11 times.
✓ Branch 243 → 290 taken 36 times.
|
47 | if (categories.contains(QStringLiteral("identities"))) { |
| 213 |
2/2✓ Branch 244 → 245 taken 11 times.
✓ Branch 245 → 246 taken 11 times.
|
11 | const QMap<QString, QString> l = textMap(local.identities), r = textMap(remote.identities), |
| 214 |
1/1✓ Branch 246 → 247 taken 11 times.
|
11 | bt = textMap(b.identities); |
| 215 |
4/4✓ Branch 247 → 248 taken 11 times.
✓ Branch 283 → 284 taken 13 times.
✓ Branch 285 → 249 taken 2 times.
✓ Branch 285 → 286 taken 11 times.
|
13 | for (auto it = l.constBegin(); it != l.constEnd(); ++it) |
| 216 |
2/3✓ Branch 250 → 251 taken 2 times.
✓ Branch 251 → 252 taken 2 times.
✗ Branch 251 → 282 not taken.
|
2 | if (r.contains(it.key())) |
| 217 |
2/2✓ Branch 269 → 270 taken 2 times.
✓ Branch 270 → 271 taken 2 times.
|
10 | builder.add(QStringLiteral("identities:") + it.key(), Merge::Kind::Scalar, |
| 218 |
4/8✓ Branch 259 → 260 taken 2 times.
✓ Branch 260 → 261 taken 2 times.
✗ Branch 260 → 264 not taken.
✓ Branch 263 → 265 taken 2 times.
✓ Branch 275 → 276 taken 2 times.
✗ Branch 275 → 277 not taken.
✗ Branch 493 → 494 not taken.
✗ Branch 493 → 495 not taken.
|
4 | bt.contains(it.key()) ? Merge::Value{true, bt.value(it.key())} : Merge::Value{}, |
| 219 | 2 | {true, it.value()}, {true, r.value(it.key())}); | |
| 220 | 11 | } | |
| 221 |
2/2✓ Branch 296 → 297 taken 11 times.
✓ Branch 296 → 314 taken 36 times.
|
47 | if (categories.contains(QStringLiteral("templates"))) { |
| 222 |
1/1✓ Branch 297 → 298 taken 11 times.
|
11 | const QMap<QString, QString> bt = textMap(b.templates); |
| 223 |
5/5✓ Branch 298 → 299 taken 11 times.
✓ Branch 299 → 300 taken 11 times.
✓ Branch 301 → 302 taken 8 times.
✓ Branch 301 → 303 taken 3 times.
✓ Branch 307 → 308 taken 11 times.
|
22 | builder.addMap(QStringLiteral("templates"), base ? &bt : nullptr, textMap(local.templates), textMap(remote.templates)); |
| 224 | 11 | } | |
| 225 |
2/2✓ Branch 320 → 321 taken 35 times.
✓ Branch 320 → 350 taken 12 times.
|
47 | if (categories.contains(QStringLiteral("general"))) { |
| 226 |
1/1✓ Branch 328 → 329 taken 35 times.
|
70 | builder.add(QStringLiteral("general:defaultView"), Merge::Kind::Scalar, {true, b.general.defaultView}, |
| 227 | 70 | {true, local.general.defaultView}, {true, remote.general.defaultView}); | |
| 228 |
1/1✓ Branch 342 → 343 taken 35 times.
|
70 | builder.add(QStringLiteral("general:language"), Merge::Kind::Scalar, {true, b.general.language}, |
| 229 | 70 | {true, local.general.language}, {true, remote.general.language}); | |
| 230 | } | ||
| 231 |
1/1✓ Branch 350 → 351 taken 47 times.
|
47 | Merge::applyDefaults(&plan); |
| 232 | 47 | return plan; | |
| 233 | 147 | } | |
| 234 | |||
| 235 | 19 | SyncPayload apply(const Merge::Plan &plan) { | |
| 236 |
1/1✓ Branch 2 → 3 taken 19 times.
|
19 | const SyncPayload local = payloadOf(plan.localText); |
| 237 |
1/1✓ Branch 3 → 4 taken 19 times.
|
19 | const SyncPayload remote = payloadOf(plan.serverText); |
| 238 |
9/31✓ Branch 5 → 6 taken 4 times.
✓ Branch 5 → 17 taken 15 times.
✓ Branch 17 → 18 taken 15 times.
✓ Branch 18 → 19 taken 4 times.
✓ Branch 18 → 20 taken 15 times.
✓ Branch 20 → 21 taken 4 times.
✓ Branch 20 → 22 taken 15 times.
✓ Branch 22 → 23 taken 4 times.
✓ Branch 22 → 24 taken 15 times.
✗ Branch 246 → 247 not taken.
✗ Branch 246 → 249 not taken.
✗ Branch 247 → 248 not taken.
✗ Branch 247 → 249 not taken.
✗ Branch 250 → 251 not taken.
✗ Branch 250 → 252 not taken.
✗ Branch 254 → 255 not taken.
✗ Branch 254 → 257 not taken.
✗ Branch 255 → 256 not taken.
✗ Branch 255 → 257 not taken.
✗ Branch 258 → 259 not taken.
✗ Branch 258 → 260 not taken.
✗ Branch 262 → 263 not taken.
✗ Branch 262 → 265 not taken.
✗ Branch 263 → 264 not taken.
✗ Branch 263 → 265 not taken.
✗ Branch 266 → 267 not taken.
✗ Branch 266 → 268 not taken.
✗ Branch 270 → 271 not taken.
✗ Branch 270 → 273 not taken.
✗ Branch 271 → 272 not taken.
✗ Branch 271 → 273 not taken.
|
31 | const SyncPayload base = plan.baseText.isEmpty() ? SyncPayload{} : payloadOf(plan.baseText); |
| 239 | 19 | SyncPayload result = local; | |
| 240 |
1/1✓ Branch 25 → 26 taken 19 times.
|
19 | result.enabledCategories = sharedCategories(local, remote); |
| 241 | 19 | QStringList hidden; | |
| 242 |
2/2✓ Branch 195 → 30 taken 92 times.
✓ Branch 195 → 196 taken 19 times.
|
111 | for (const Merge::Item &item : plan.items) { |
| 243 | 92 | const SyncPayload &source = sourceOf(item, base, local, remote); | |
| 244 | 92 | const QString key = item.context; | |
| 245 | 92 | const QString category = item.name; | |
| 246 |
5/6✓ Branch 36 → 37 taken 72 times.
✓ Branch 36 → 43 taken 20 times.
✓ Branch 39 → 40 taken 64 times.
✓ Branch 39 → 43 taken 8 times.
✗ Branch 42 → 43 not taken.
✓ Branch 42 → 44 taken 64 times.
|
156 | if (category == QLatin1String("folderIcons") || category == QLatin1String("folderColors") || |
| 247 |
2/2✓ Branch 45 → 46 taken 28 times.
✓ Branch 45 → 75 taken 64 times.
|
156 | category == QLatin1String("calendarColors")) { |
| 248 | 28 | QMap<QString, QString> &target = category == QLatin1String("folderIcons") ? result.folderIcons | |
| 249 | 8 | : category == QLatin1String("folderColors") ? result.folderColors | |
| 250 |
3/4✓ Branch 48 → 49 taken 20 times.
✓ Branch 48 → 50 taken 8 times.
✓ Branch 52 → 53 taken 8 times.
✗ Branch 52 → 54 not taken.
|
36 | : result.calendarColors; |
| 251 | 28 | const QMap<QString, QString> &from = category == QLatin1String("folderIcons") ? source.folderIcons | |
| 252 | 8 | : category == QLatin1String("folderColors") ? source.folderColors | |
| 253 |
3/4✓ Branch 58 → 59 taken 20 times.
✓ Branch 58 → 60 taken 8 times.
✓ Branch 62 → 63 taken 8 times.
✗ Branch 62 → 64 not taken.
|
36 | : source.calendarColors; |
| 254 |
3/3✓ Branch 66 → 67 taken 28 times.
✓ Branch 67 → 68 taken 27 times.
✓ Branch 67 → 74 taken 1 time.
|
28 | if (from.contains(key)) |
| 255 |
2/2✓ Branch 69 → 70 taken 27 times.
✓ Branch 70 → 71 taken 27 times.
|
27 | target.insert(key, from.value(key)); |
| 256 | else | ||
| 257 |
1/1✓ Branch 74 → 183 taken 1 time.
|
1 | target.remove(key); |
| 258 |
2/2✓ Branch 77 → 78 taken 8 times.
✓ Branch 77 → 81 taken 56 times.
|
64 | } else if (category == QLatin1String("hiddenFolders")) { |
| 259 |
2/2✓ Branch 79 → 80 taken 7 times.
✓ Branch 79 → 183 taken 1 time.
|
8 | if (source.hiddenFolders.contains(key)) |
| 260 |
1/1✓ Branch 80 → 183 taken 7 times.
|
7 | hidden.append(key); |
| 261 |
2/2✓ Branch 83 → 84 taken 22 times.
✓ Branch 83 → 144 taken 34 times.
|
56 | } else if (category == QLatin1String("davAccounts")) { |
| 262 | // "<id>" (the whole account) or "<id>:books" / "<id>:calendars"; the | ||
| 263 | // id is escaped (keyOfAccount), so the first ':' ends it. | ||
| 264 |
2/2✓ Branch 87 → 88 taken 22 times.
✓ Branch 88 → 89 taken 22 times.
|
22 | const QString id = accountOfKey(key.section(QLatin1Char(':'), 0, 0)); |
| 265 |
1/1✓ Branch 93 → 94 taken 22 times.
|
22 | const QString part = key.section(QLatin1Char(':'), 1); |
| 266 |
2/2✓ Branch 95 → 96 taken 8 times.
✓ Branch 95 → 109 taken 14 times.
|
22 | if (part.isEmpty()) { |
| 267 |
1/1✓ Branch 96 → 97 taken 8 times.
|
8 | const SyncPayload::CardDavAccount *offered = accountOf(remote, id); |
| 268 |
7/9✓ Branch 97 → 98 taken 6 times.
✓ Branch 97 → 102 taken 2 times.
✓ Branch 98 → 99 taken 6 times.
✗ Branch 98 → 102 not taken.
✓ Branch 99 → 100 taken 6 times.
✓ Branch 100 → 101 taken 6 times.
✗ Branch 100 → 102 not taken.
✓ Branch 103 → 104 taken 6 times.
✓ Branch 103 → 108 taken 2 times.
|
8 | if (item.choice == Merge::Choice::Server && offered && !accountOf(result, id)) { |
| 269 |
1/1✓ Branch 104 → 105 taken 6 times.
|
6 | result.carddavAccounts.append(*offered); |
| 270 |
3/3✓ Branch 105 → 106 taken 6 times.
✓ Branch 106 → 107 taken 3 times.
✓ Branch 106 → 108 taken 3 times.
|
6 | if (const auto *config = configOf(remote, id)) |
| 271 |
1/1✓ Branch 107 → 108 taken 3 times.
|
3 | result.caldavConfigs.append(*config); |
| 272 | } | ||
| 273 | 8 | continue; | |
| 274 | 8 | } | |
| 275 |
1/1✓ Branch 109 → 110 taken 14 times.
|
14 | const auto *sourceAccount = accountOf(source, id); |
| 276 |
4/4✓ Branch 110 → 111 taken 14 times.
✓ Branch 111 → 112 taken 14 times.
✓ Branch 126 → 113 taken 14 times.
✓ Branch 126 → 127 taken 14 times.
|
28 | for (SyncPayload::CardDavAccount &account : result.carddavAccounts) |
| 277 |
6/8✓ Branch 115 → 116 taken 14 times.
✗ Branch 115 → 121 not taken.
✓ Branch 118 → 119 taken 7 times.
✓ Branch 118 → 121 taken 7 times.
✓ Branch 119 → 120 taken 7 times.
✗ Branch 119 → 121 not taken.
✓ Branch 122 → 123 taken 7 times.
✓ Branch 122 → 124 taken 7 times.
|
14 | if (account.id == id && part == QLatin1String("books") && sourceAccount) |
| 278 | 7 | account.selectedBooks = sourceAccount->selectedBooks; | |
| 279 |
2/2✓ Branch 129 → 130 taken 7 times.
✓ Branch 129 → 134 taken 7 times.
|
14 | if (part == QLatin1String("calendars")) { |
| 280 |
1/1✓ Branch 130 → 131 taken 7 times.
|
7 | result.caldavConfigs.removeIf( |
| 281 | 7 | [&id](const SyncPayload::CalDavSyncConfig &config) { return config.carddavAccountId == id; }); | |
| 282 |
2/3✓ Branch 131 → 132 taken 7 times.
✓ Branch 132 → 133 taken 7 times.
✗ Branch 132 → 134 not taken.
|
7 | if (const auto *config = configOf(source, id)) |
| 283 |
1/1✓ Branch 133 → 134 taken 7 times.
|
7 | result.caldavConfigs.append(*config); |
| 284 | } | ||
| 285 |
10/10✓ Branch 136 → 137 taken 14 times.
✓ Branch 136 → 138 taken 8 times.
✓ Branch 140 → 141 taken 14 times.
✓ Branch 140 → 143 taken 8 times.
✓ Branch 146 → 147 taken 33 times.
✓ Branch 146 → 150 taken 1 time.
✓ Branch 149 → 150 taken 3 times.
✓ Branch 149 → 151 taken 30 times.
✓ Branch 152 → 153 taken 4 times.
✓ Branch 152 → 172 taken 30 times.
|
64 | } else if (category == QLatin1String("identities") || category == QLatin1String("templates")) { |
| 286 |
2/2✓ Branch 155 → 156 taken 1 time.
✓ Branch 155 → 157 taken 3 times.
|
4 | QMap<QString, QJsonObject> &target = category == QLatin1String("identities") ? result.identities : result.templates; |
| 287 |
2/2✓ Branch 160 → 161 taken 1 time.
✓ Branch 160 → 162 taken 3 times.
|
4 | const QMap<QString, QJsonObject> &from = category == QLatin1String("identities") ? source.identities : source.templates; |
| 288 |
3/3✓ Branch 163 → 164 taken 4 times.
✓ Branch 164 → 165 taken 3 times.
✓ Branch 164 → 171 taken 1 time.
|
4 | if (from.contains(key)) |
| 289 |
3/3✓ Branch 165 → 166 taken 3 times.
✓ Branch 166 → 167 taken 3 times.
✓ Branch 167 → 168 taken 3 times.
|
3 | target.insert(key, from.value(key)); |
| 290 | else | ||
| 291 |
1/1✓ Branch 171 → 183 taken 1 time.
|
1 | target.remove(key); |
| 292 |
1/2✓ Branch 174 → 175 taken 30 times.
✗ Branch 174 → 183 not taken.
|
30 | } else if (category == QLatin1String("general")) { |
| 293 |
2/2✓ Branch 177 → 178 taken 15 times.
✓ Branch 177 → 179 taken 15 times.
|
30 | if (key == QLatin1String("defaultView")) |
| 294 | 15 | result.general.defaultView = source.general.defaultView; | |
| 295 |
1/2✓ Branch 181 → 182 taken 15 times.
✗ Branch 181 → 183 not taken.
|
15 | else if (key == QLatin1String("language")) |
| 296 | 15 | result.general.language = source.general.language; | |
| 297 | } | ||
| 298 | 100 | } | |
| 299 | // Hidden folders: the local order, then additions in the server's order. | ||
| 300 | 19 | QStringList folders; | |
| 301 |
2/2✓ Branch 213 → 198 taken 57 times.
✓ Branch 213 → 214 taken 19 times.
|
76 | for (const QStringList *list : {&local.hiddenFolders, &remote.hiddenFolders, &base.hiddenFolders}) |
| 302 |
2/2✓ Branch 211 → 200 taken 24 times.
✓ Branch 211 → 212 taken 57 times.
|
81 | for (const QString &path : *list) |
| 303 |
6/6✓ Branch 202 → 203 taken 19 times.
✓ Branch 202 → 206 taken 5 times.
✓ Branch 204 → 205 taken 7 times.
✓ Branch 204 → 206 taken 12 times.
✓ Branch 207 → 208 taken 7 times.
✓ Branch 207 → 209 taken 17 times.
|
24 | if (hidden.contains(path) && !folders.contains(path)) |
| 304 |
1/1✓ Branch 208 → 209 taken 7 times.
|
7 | folders.append(path); |
| 305 |
2/2✓ Branch 220 → 221 taken 7 times.
✓ Branch 220 → 222 taken 12 times.
|
38 | if (result.enabledCategories.contains(QStringLiteral("hiddenFolders"))) |
| 306 | 7 | result.hiddenFolders = folders; | |
| 307 | // Parsing keeps MaxComposeEntries per map; the merge of three payloads | ||
| 308 | // could hold more, which a round trip of the result would cut. | ||
| 309 |
2/2✓ Branch 238 → 224 taken 38 times.
✓ Branch 238 → 239 taken 19 times.
|
57 | for (QMap<QString, QJsonObject> *map : {&result.identities, &result.templates}) |
| 310 |
2/5✗ Branch 225 → 226 not taken.
✗ Branch 233 → 234 not taken.
✓ Branch 235 → 236 taken 38 times.
✗ Branch 236 → 225 not taken.
✓ Branch 236 → 237 taken 38 times.
|
38 | while (map->size() > SyncPayload::MaxComposeEntries) map->erase(std::prev(map->end())); |
| 311 | 19 | return result; | |
| 312 | 19 | } | |
| 313 | |||
| 314 | 13 | QStringList conflictCategories(const Merge::Plan &plan) { | |
| 315 | 13 | QStringList categories; | |
| 316 |
2/2✓ Branch 14 → 4 taken 57 times.
✓ Branch 14 → 15 taken 13 times.
|
70 | for (const Merge::Item &item : plan.items) |
| 317 |
5/6✓ Branch 5 → 6 taken 12 times.
✓ Branch 5 → 9 taken 45 times.
✓ Branch 7 → 8 taken 12 times.
✗ Branch 7 → 9 not taken.
✓ Branch 10 → 11 taken 12 times.
✓ Branch 10 → 12 taken 45 times.
|
57 | if (item.state == Merge::State::Conflict && !categories.contains(item.name)) |
| 318 |
1/1✓ Branch 11 → 12 taken 12 times.
|
12 | categories.append(item.name); |
| 319 | 13 | return categories; | |
| 320 | ✗ | } | |
| 321 | |||
| 322 | 30 | QString accountItemId(const QString &accountId, const QString &part) { | |
| 323 |
2/2✓ Branch 2 → 3 taken 30 times.
✓ Branch 6 → 7 taken 30 times.
|
60 | const QString id = QStringLiteral("davAccounts:") + keyOfAccount(accountId); |
| 324 |
6/8✓ Branch 11 → 12 taken 7 times.
✓ Branch 11 → 13 taken 23 times.
✓ Branch 15 → 16 taken 23 times.
✓ Branch 16 → 17 taken 23 times.
✓ Branch 18 → 19 taken 23 times.
✓ Branch 18 → 20 taken 7 times.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 34 not taken.
|
60 | return part.isEmpty() ? id : id + QLatin1Char(':') + part; |
| 325 | 30 | } | |
| 326 | |||
| 327 | } // namespace SettingsMergePlan | ||
| 328 |