• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17779724487

16 Sep 2025 09:37PM UTC coverage: 71.659% (-0.8%) from 72.505%
17779724487

push

github

paulmthompson
implementing testing for analysis dashboard widget

51 of 60 new or added lines in 5 files covered. (85.0%)

116 existing lines in 11 files now uncovered.

39156 of 54642 relevant lines covered (71.66%)

1301.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.21
/src/DataManager/Entity/EntityGroupManager.cpp
1
#include "EntityGroupManager.hpp"
2

3
#include <algorithm>
4
#include <cassert>
5

6
// ========== Group Management ==========
7

8
GroupId EntityGroupManager::createGroup(std::string const & name, std::string const & description) {
161✔
9
    GroupId const id = m_next_group_id++;
161✔
10

11
    m_group_names.emplace(id, name);
161✔
12
    m_group_descriptions.emplace(id, description);
161✔
13
    m_group_entities.emplace(id, std::unordered_set<EntityId>{});
161✔
14

15
    return id;
161✔
16
}
17

18
bool EntityGroupManager::deleteGroup(GroupId group_id) {
7✔
19
    auto it = m_group_entities.find(group_id);
7✔
20
    if (it == m_group_entities.end()) {
7✔
21
        return false;
2✔
22
    }
23

24
    // Remove this group from all entities' reverse lookup
25
    for (EntityId const entity_id: it->second) {
7✔
26
        auto entity_it = m_entity_groups.find(entity_id);
2✔
27
        if (entity_it != m_entity_groups.end()) {
2✔
28
            entity_it->second.erase(group_id);
2✔
29
            // Clean up empty entity entries
30
            if (entity_it->second.empty()) {
2✔
31
                m_entity_groups.erase(entity_it);
2✔
32
            }
33
        }
34
    }
35

36
    // Remove group data
37
    m_group_entities.erase(it);
5✔
38
    m_group_names.erase(group_id);
5✔
39
    m_group_descriptions.erase(group_id);
5✔
40

41
    return true;
5✔
42
}
43

44
bool EntityGroupManager::hasGroup(GroupId group_id) const {
36✔
45
    return m_group_entities.find(group_id) != m_group_entities.end();
36✔
46
}
47

48
std::optional<GroupDescriptor> EntityGroupManager::getGroupDescriptor(GroupId group_id) const {
30✔
49
    auto entities_it = m_group_entities.find(group_id);
30✔
50
    if (entities_it == m_group_entities.end()) {
30✔
51
        return std::nullopt;
1✔
52
    }
53

54
    auto name_it = m_group_names.find(group_id);
29✔
55
    auto desc_it = m_group_descriptions.find(group_id);
29✔
56

57
    return GroupDescriptor{
145✔
58
            .id = group_id,
59
            .name = name_it != m_group_names.end() ? name_it->second : "",
58✔
60
            .description = desc_it != m_group_descriptions.end() ? desc_it->second : "",
58✔
61
            .entity_count = entities_it->second.size()};
58✔
62
}
63

64
bool EntityGroupManager::updateGroup(GroupId group_id, std::string const & name, std::string const & description) {
8✔
65
    if (!hasGroup(group_id)) {
8✔
66
        return false;
1✔
67
    }
68

69
    m_group_names[group_id] = name;
7✔
70
    m_group_descriptions[group_id] = description;
7✔
71

72
    return true;
7✔
73
}
74

75
std::vector<GroupId> EntityGroupManager::getAllGroupIds() const {
52✔
76
    std::vector<GroupId> group_ids;
52✔
77
    group_ids.reserve(m_group_entities.size());
52✔
78

79
    for (auto const & [group_id, entities]: m_group_entities) {
62✔
80
        (void) entities;// Suppress unused variable warning
81
        group_ids.push_back(group_id);
10✔
82
    }
83

84
    return group_ids;
52✔
UNCOV
85
}
×
86

87
std::vector<GroupDescriptor> EntityGroupManager::getAllGroupDescriptors() const {
2✔
88
    std::vector<GroupDescriptor> descriptors;
2✔
89
    descriptors.reserve(m_group_entities.size());
2✔
90

91
    for (auto const & [group_id, entities]: m_group_entities) {
5✔
92
        auto name_it = m_group_names.find(group_id);
3✔
93
        auto desc_it = m_group_descriptions.find(group_id);
3✔
94

95
        descriptors.push_back(GroupDescriptor{
15✔
96
                .id = group_id,
3✔
97
                .name = name_it != m_group_names.end() ? name_it->second : "",
6✔
98
                .description = desc_it != m_group_descriptions.end() ? desc_it->second : "",
6✔
99
                .entity_count = entities.size()});
3✔
100
    }
101

102
    return descriptors;
2✔
UNCOV
103
}
×
104

105
// ========== Entity Management ==========
106

107
bool EntityGroupManager::addEntityToGroup(GroupId group_id, EntityId entity_id) {
10,030✔
108
    auto group_it = m_group_entities.find(group_id);
10,030✔
109
    if (group_it == m_group_entities.end()) {
10,030✔
110
        return false;
1✔
111
    }
112

113
    auto & group_set = group_it->second;
10,029✔
114
    auto [ignored, inserted] = group_set.insert(entity_id);
10,029✔
115
    if (!inserted) {
10,029✔
116
        return false;// Already exists
1✔
117
    }
118

119
    auto [rev_it, created] = m_entity_groups.try_emplace(entity_id);
10,028✔
120
    (void) created;
121
    rev_it->second.insert(group_id);
10,028✔
122

123
    return true;
10,028✔
124
}
125

126
std::size_t EntityGroupManager::addEntitiesToGroup(GroupId group_id, std::vector<EntityId> const & entity_ids) {
26✔
127
    auto group_it = m_group_entities.find(group_id);
26✔
128
    if (group_it == m_group_entities.end()) {
26✔
129
        return 0;
1✔
130
    }
131

132
    auto & group_set = group_it->second;
25✔
133
    // Reserve to reduce rehashing when adding many entities
134
    group_set.reserve(group_set.size() + entity_ids.size());
25✔
135
    m_entity_groups.reserve(m_entity_groups.size() + entity_ids.size());
25✔
136

137
    std::size_t added_count = 0;
25✔
138
    for (EntityId const entity_id: entity_ids) {
10,091✔
139
        auto [ignored, inserted] = group_set.insert(entity_id);
10,066✔
140
        if (!inserted) {
10,066✔
141
            continue;
8✔
142
        }
143

144
        auto [rev_it, created] = m_entity_groups.try_emplace(entity_id);
10,058✔
145
        (void) created;
146
        rev_it->second.insert(group_id);
10,058✔
147
        ++added_count;
10,058✔
148
    }
149

150
    return added_count;
25✔
151
}
152

153
bool EntityGroupManager::removeEntityFromGroup(GroupId group_id, EntityId entity_id) {
7✔
154
    auto group_it = m_group_entities.find(group_id);
7✔
155
    if (group_it == m_group_entities.end()) {
7✔
156
        return false;
1✔
157
    }
158

159
    // Check if entity is in group
160
    auto entity_it = group_it->second.find(entity_id);
6✔
161
    if (entity_it == group_it->second.end()) {
6✔
162
        return false;// Not in group
2✔
163
    }
164

165
    // Remove entity from group
166
    group_it->second.erase(entity_it);
4✔
167

168
    // Remove group from entity's reverse lookup
169
    auto reverse_it = m_entity_groups.find(entity_id);
4✔
170
    if (reverse_it != m_entity_groups.end()) {
4✔
171
        reverse_it->second.erase(group_id);
4✔
172
        // Clean up empty entity entries
173
        if (reverse_it->second.empty()) {
4✔
174
            m_entity_groups.erase(reverse_it);
2✔
175
        }
176
    }
177

178
    return true;
4✔
179
}
180

181
std::size_t EntityGroupManager::removeEntitiesFromGroup(GroupId group_id, std::vector<EntityId> const & entity_ids) {
9✔
182
    auto group_it = m_group_entities.find(group_id);
9✔
183
    if (group_it == m_group_entities.end()) {
9✔
184
        return 0;
1✔
185
    }
186

187
    std::size_t removed_count = 0;
8✔
188
    for (EntityId const entity_id: entity_ids) {
5,019✔
189
        // Check if entity is in group
190
        auto entity_it = group_it->second.find(entity_id);
5,011✔
191
        if (entity_it != group_it->second.end()) {
5,011✔
192
            // Remove entity from group
193
            group_it->second.erase(entity_it);
5,008✔
194

195
            // Remove group from entity's reverse lookup
196
            auto reverse_it = m_entity_groups.find(entity_id);
5,008✔
197
            if (reverse_it != m_entity_groups.end()) {
5,008✔
198
                reverse_it->second.erase(group_id);
5,008✔
199
                // Clean up empty entity entries
200
                if (reverse_it->second.empty()) {
5,008✔
201
                    m_entity_groups.erase(reverse_it);
5,007✔
202
                }
203
            }
204

205
            ++removed_count;
5,008✔
206
        }
207
    }
208

209
    return removed_count;
8✔
210
}
211

212
std::vector<EntityId> EntityGroupManager::getEntitiesInGroup(GroupId group_id) const {
11✔
213
    auto it = m_group_entities.find(group_id);
11✔
214
    if (it == m_group_entities.end()) {
11✔
215
        return {};
1✔
216
    }
217

218
    std::vector<EntityId> entities;
10✔
219
    entities.reserve(it->second.size());
10✔
220

221
    for (EntityId const entity_id: it->second) {
5,029✔
222
        entities.push_back(entity_id);
5,019✔
223
    }
224

225
    return entities;
10✔
226
}
10✔
227

228
bool EntityGroupManager::isEntityInGroup(GroupId group_id, EntityId entity_id) const { // NOLINT(bugprone-easily-swappable-parameters)
37✔
229
    auto it = m_group_entities.find(group_id);
37✔
230
    if (it == m_group_entities.end()) {
37✔
UNCOV
231
        return false;
×
232
    }
233

234
    return it->second.find(entity_id) != it->second.end();
37✔
235
}
236

237
std::vector<GroupId> EntityGroupManager::getGroupsContainingEntity(EntityId entity_id) const {
30✔
238
    auto it = m_entity_groups.find(entity_id);
30✔
239
    if (it == m_entity_groups.end()) {
30✔
240
        return {};
14✔
241
    }
242

243
    std::vector<GroupId> groups;
16✔
244
    groups.reserve(it->second.size());
16✔
245

246
    for (GroupId const group_id: it->second) {
44✔
247
        groups.push_back(group_id);
28✔
248
    }
249

250
    return groups;
16✔
251
}
16✔
252

253
std::size_t EntityGroupManager::getGroupSize(GroupId group_id) const {
34✔
254
    auto it = m_group_entities.find(group_id);
34✔
255
    if (it == m_group_entities.end()) {
34✔
256
        return 0;
1✔
257
    }
258

259
    return it->second.size();
33✔
260
}
261

262
bool EntityGroupManager::clearGroup(GroupId group_id) {
3✔
263
    auto group_it = m_group_entities.find(group_id);
3✔
264
    if (group_it == m_group_entities.end()) {
3✔
265
        return false;
1✔
266
    }
267

268
    // Remove this group from all entities' reverse lookup
269
    for (EntityId const entity_id: group_it->second) {
6✔
270
        auto entity_it = m_entity_groups.find(entity_id);
4✔
271
        if (entity_it != m_entity_groups.end()) {
4✔
272
            entity_it->second.erase(group_id);
4✔
273
            // Clean up empty entity entries
274
            if (entity_it->second.empty()) {
4✔
275
                m_entity_groups.erase(entity_it);
4✔
276
            }
277
        }
278
    }
279

280
    // Clear the group's entities
281
    group_it->second.clear();
2✔
282

283
    return true;
2✔
284
}
285

286
void EntityGroupManager::clear() {
2✔
287
    m_group_names.clear();
2✔
288
    m_group_descriptions.clear();
2✔
289
    m_group_entities.clear();
2✔
290
    m_entity_groups.clear();
2✔
291
    m_next_group_id = 1;
2✔
292
}
2✔
293

294
std::size_t EntityGroupManager::getGroupCount() const {
11✔
295
    return m_group_entities.size();
11✔
296
}
297

298
std::size_t EntityGroupManager::getTotalEntityCount() const {
12✔
299
    return m_entity_groups.size();
12✔
300
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc