2
* Copyright (C) 2014 Florian Geyer <blueice@fobos.de>
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 2 or (at your option)
7
* version 3 of the License.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
#include "TestEntrySearcher.h"
19
#include "core/Group.h"
20
#include "core/Tools.h"
24
QTEST_GUILESS_MAIN(TestEntrySearcher)
26
void TestEntrySearcher::init()
28
m_rootGroup = new Group();
29
m_entrySearcher = EntrySearcher();
32
void TestEntrySearcher::cleanup()
37
void TestEntrySearcher::testSearch()
41
* - group1 (search disabled)
48
auto group1 = new Group();
49
auto group2 = new Group();
50
auto group3 = new Group();
52
group1->setParent(m_rootGroup);
53
group2->setParent(m_rootGroup);
54
group3->setParent(m_rootGroup);
56
auto group11 = new Group();
58
group11->setParent(group1);
60
auto group21 = new Group();
61
auto group211 = new Group();
62
auto group2111 = new Group();
64
group21->setParent(group2);
65
group211->setParent(group21);
66
group2111->setParent(group211);
68
group1->setSearchingEnabled(Group::Disable);
70
auto eRoot = new Entry();
71
eRoot->setTitle("test search term test");
72
eRoot->setGroup(m_rootGroup);
74
auto eRoot2 = new Entry();
75
eRoot2->setNotes("test term test");
76
eRoot2->setGroup(m_rootGroup);
78
// Searching is disabled for these
79
auto e1 = new Entry();
80
e1->setUsername("test search term test");
83
auto e11 = new Entry();
84
e11->setNotes("test search term test");
85
e11->setGroup(group11);
86
// End searching disabled
88
auto e2111 = new Entry();
89
e2111->setTitle("test search term test");
90
e2111->setGroup(group2111);
92
auto e2111b = new Entry();
93
e2111b->setNotes("test search test");
94
e2111b->setUsername("user123");
95
e2111b->setPassword("testpass");
96
e2111b->setGroup(group2111);
98
auto e3 = new Entry();
99
e3->setUrl("test search term test");
100
e3->setGroup(group3);
102
auto e3b = new Entry();
103
e3b->setTitle("test search test 123");
104
e3b->setUsername("test@email.com");
105
e3b->setPassword("realpass");
106
e3b->setGroup(group3);
108
// Simple search term testing
109
m_searchResult = m_entrySearcher.search("search", m_rootGroup);
110
QCOMPARE(m_searchResult.count(), 5);
112
m_searchResult = m_entrySearcher.search("search term", m_rootGroup);
113
QCOMPARE(m_searchResult.count(), 3);
115
m_searchResult = m_entrySearcher.search("123", m_rootGroup);
116
QCOMPARE(m_searchResult.count(), 2);
118
m_searchResult = m_entrySearcher.search("search term", group211);
119
QCOMPARE(m_searchResult.count(), 1);
121
// Test advanced search terms
122
m_searchResult = m_entrySearcher.search("title:123", m_rootGroup);
123
QCOMPARE(m_searchResult.count(), 1);
125
m_searchResult = m_entrySearcher.search("t:123", m_rootGroup);
126
QCOMPARE(m_searchResult.count(), 1);
128
m_searchResult = m_entrySearcher.search("password:testpass", m_rootGroup);
129
QCOMPARE(m_searchResult.count(), 1);
131
m_searchResult = m_entrySearcher.search("pw:testpass", m_rootGroup);
132
QCOMPARE(m_searchResult.count(), 1);
134
m_searchResult = m_entrySearcher.search("!user:email.com", m_rootGroup);
135
QCOMPARE(m_searchResult.count(), 5);
137
m_searchResult = m_entrySearcher.search("!u:email.com", m_rootGroup);
138
QCOMPARE(m_searchResult.count(), 5);
140
m_searchResult = m_entrySearcher.search("*user:\".*@.*\\.com\"", m_rootGroup);
141
QCOMPARE(m_searchResult.count(), 1);
143
m_searchResult = m_entrySearcher.search("+user:email", m_rootGroup);
144
QCOMPARE(m_searchResult.count(), 0);
146
// Terms are logical AND together
147
m_searchResult = m_entrySearcher.search("password:pass user:user", m_rootGroup);
148
QCOMPARE(m_searchResult.count(), 1);
150
// Parent group has search disabled
151
m_searchResult = m_entrySearcher.search("search term", group11);
152
QCOMPARE(m_searchResult.count(), 0);
155
void TestEntrySearcher::testAndConcatenationInSearch()
157
auto entry = new Entry();
158
entry->setNotes("abc def ghi");
159
entry->setTitle("jkl");
160
entry->setGroup(m_rootGroup);
162
m_searchResult = m_entrySearcher.search("", m_rootGroup);
163
QCOMPARE(m_searchResult.count(), 1);
165
m_searchResult = m_entrySearcher.search("def", m_rootGroup);
166
QCOMPARE(m_searchResult.count(), 1);
168
m_searchResult = m_entrySearcher.search(" abc ghi ", m_rootGroup);
169
QCOMPARE(m_searchResult.count(), 1);
171
m_searchResult = m_entrySearcher.search("ghi ef", m_rootGroup);
172
QCOMPARE(m_searchResult.count(), 1);
174
m_searchResult = m_entrySearcher.search("abc ef xyz", m_rootGroup);
175
QCOMPARE(m_searchResult.count(), 0);
177
m_searchResult = m_entrySearcher.search("abc kl", m_rootGroup);
178
QCOMPARE(m_searchResult.count(), 1);
181
void TestEntrySearcher::testAllAttributesAreSearched()
183
auto entry = new Entry();
184
entry->setGroup(m_rootGroup);
186
entry->setTitle("testTitle");
187
entry->setUsername("testUsername");
188
entry->setUrl("testUrl");
189
entry->setNotes("testNote");
191
// Default is to AND all terms together
192
m_searchResult = m_entrySearcher.search("testTitle testUsername testUrl testNote", m_rootGroup);
193
QCOMPARE(m_searchResult.count(), 1);
196
void TestEntrySearcher::testSearchTermParser()
198
// Test standard search terms
199
m_entrySearcher.parseSearchTerms("-test \"quoted \\\"string\\\"\" user:user pass:\"test me\" noquote ");
200
auto terms = m_entrySearcher.m_searchTerms;
202
QCOMPARE(terms.length(), 5);
204
QCOMPARE(terms[0].field, EntrySearcher::Field::Undefined);
205
QCOMPARE(terms[0].word, QString("test"));
206
QCOMPARE(terms[0].exclude, true);
208
QCOMPARE(terms[1].field, EntrySearcher::Field::Undefined);
209
QCOMPARE(terms[1].word, QString("quoted \"string\""));
210
QCOMPARE(terms[1].exclude, false);
212
QCOMPARE(terms[2].field, EntrySearcher::Field::Username);
213
QCOMPARE(terms[2].word, QString("user"));
215
QCOMPARE(terms[3].field, EntrySearcher::Field::Password);
216
QCOMPARE(terms[3].word, QString("test me"));
218
QCOMPARE(terms[4].field, EntrySearcher::Field::Undefined);
219
QCOMPARE(terms[4].word, QString("noquote"));
221
// Test wildcard and regex search terms
222
m_entrySearcher.parseSearchTerms("+url:*.google.com *user:\\d+\\w{2}");
223
terms = m_entrySearcher.m_searchTerms;
225
QCOMPARE(terms.length(), 2);
227
QCOMPARE(terms[0].field, EntrySearcher::Field::Url);
228
QCOMPARE(terms[0].regex.pattern(), QString("^(?:.*\\.google\\.com)$"));
230
QCOMPARE(terms[1].field, EntrySearcher::Field::Username);
231
QCOMPARE(terms[1].regex.pattern(), QString("\\d+\\w{2}"));
233
// Test custom attribute search terms
234
m_entrySearcher.parseSearchTerms("+_abc:efg _def:\"ddd\"");
235
terms = m_entrySearcher.m_searchTerms;
237
QCOMPARE(terms.length(), 2);
239
QCOMPARE(terms[0].field, EntrySearcher::Field::AttributeValue);
240
QCOMPARE(terms[0].word, QString("abc"));
241
QCOMPARE(terms[0].regex.pattern(), QString("^(?:efg)$"));
243
QCOMPARE(terms[1].field, EntrySearcher::Field::AttributeValue);
244
QCOMPARE(terms[1].word, QString("def"));
245
QCOMPARE(terms[1].regex.pattern(), QString("ddd"));
248
void TestEntrySearcher::testCustomAttributesAreSearched()
250
QScopedPointer<Entry> e1(new Entry());
251
e1->setGroup(m_rootGroup);
253
e1->attributes()->set("testAttribute", "testE1");
254
e1->attributes()->set("testProtected", "testP", true);
256
QScopedPointer<Entry> e2(new Entry());
257
e2->setGroup(m_rootGroup);
258
e2->attributes()->set("testAttribute", "testE2");
259
e2->attributes()->set("testProtected", "testP2", true);
261
// search for custom entries
262
m_searchResult = m_entrySearcher.search("_testAttribute:test", m_rootGroup);
263
QCOMPARE(m_searchResult.count(), 2);
265
// protected attributes are ignored
266
m_entrySearcher = EntrySearcher(false, true);
267
m_searchResult = m_entrySearcher.search("_testAttribute:test _testProtected:testP2", m_rootGroup);
268
QCOMPARE(m_searchResult.count(), 2);
271
void TestEntrySearcher::testGroup()
276
* - subgroup1 (2 entries)
278
* - subgroup2 (1 entry)
280
auto group1 = new Group();
281
auto group2 = new Group();
283
group1->setParent(m_rootGroup);
284
group1->setName("group1");
285
group2->setParent(m_rootGroup);
286
group2->setName("group2");
288
auto subgroup1 = new Group();
289
subgroup1->setName("subgroup1");
290
subgroup1->setParent(group1);
292
auto subgroup2 = new Group();
293
subgroup2->setName("subgroup2");
294
subgroup2->setParent(group2);
296
auto eGroup1 = new Entry();
297
eGroup1->setTitle("Entry Group 1");
298
eGroup1->setGroup(group1);
300
auto eSub1 = new Entry();
301
eSub1->setTitle("test search term test");
302
eSub1->setGroup(subgroup1);
304
auto eSub2 = new Entry();
305
eSub2->setNotes("test test");
306
eSub2->setGroup(subgroup1);
308
auto eSub3 = new Entry();
309
eSub3->setNotes("test term test");
310
eSub3->setGroup(subgroup2);
312
m_searchResult = m_entrySearcher.search("group:subgroup", m_rootGroup);
313
QCOMPARE(m_searchResult.count(), 3);
315
m_searchResult = m_entrySearcher.search("g:subgroup1", m_rootGroup);
316
QCOMPARE(m_searchResult.count(), 2);
318
m_searchResult = m_entrySearcher.search("g:subgroup1 search", m_rootGroup);
319
QCOMPARE(m_searchResult.count(), 1);
321
m_searchResult = m_entrySearcher.search("g:*1/sub*1", m_rootGroup);
322
QCOMPARE(m_searchResult.count(), 2);
324
m_searchResult = m_entrySearcher.search("g:/group1 search", m_rootGroup);
325
QCOMPARE(m_searchResult.count(), 1);
328
void TestEntrySearcher::testSkipProtected()
330
QScopedPointer<Entry> e1(new Entry());
331
e1->setGroup(m_rootGroup);
333
e1->attributes()->set("testAttribute", "testE1");
334
e1->attributes()->set("testProtected", "apple", true);
336
QScopedPointer<Entry> e2(new Entry());
337
e2->setGroup(m_rootGroup);
338
e2->attributes()->set("testAttribute", "testE2");
339
e2->attributes()->set("testProtected", "banana", true);
341
const QList<Entry*> expectE1{e1.data()};
342
const QList<Entry*> expectE2{e2.data()};
343
const QList<Entry*> expectBoth{e1.data(), e2.data()};
345
// when not skipping protected, empty term matches everything
346
m_searchResult = m_entrySearcher.search("", m_rootGroup);
347
QCOMPARE(m_searchResult, expectBoth);
349
// now test the searcher with skipProtected = true
350
m_entrySearcher = EntrySearcher(false, true);
352
// when skipping protected, empty term matches nothing
353
m_searchResult = m_entrySearcher.search("", m_rootGroup);
354
QCOMPARE(m_searchResult, {});
356
// having a protected entry in terms should not affect the results in anyways
357
m_searchResult = m_entrySearcher.search("_testProtected:apple", m_rootGroup);
358
QCOMPARE(m_searchResult, {});
359
m_searchResult = m_entrySearcher.search("_testProtected:apple _testAttribute:testE2", m_rootGroup);
360
QCOMPARE(m_searchResult, expectE2);
361
m_searchResult = m_entrySearcher.search("_testProtected:apple _testAttribute:testE1", m_rootGroup);
362
QCOMPARE(m_searchResult, expectE1);
364
m_entrySearcher.search("_testProtected:apple _testAttribute:testE1 _testAttribute:testE2", m_rootGroup);
365
QCOMPARE(m_searchResult, {});
367
// also move the protected term around to exercise the short-circuit logic
368
m_searchResult = m_entrySearcher.search("_testAttribute:testE2 _testProtected:apple", m_rootGroup);
369
QCOMPARE(m_searchResult, expectE2);
370
m_searchResult = m_entrySearcher.search("_testAttribute:testE1 _testProtected:apple", m_rootGroup);
371
QCOMPARE(m_searchResult, expectE1);
373
m_entrySearcher.search("_testAttribute:testE1 _testProtected:apple _testAttribute:testE2", m_rootGroup);
374
QCOMPARE(m_searchResult, {});
377
void TestEntrySearcher::testUUIDSearch()
379
auto entry1 = new Entry();
380
entry1->setGroup(m_rootGroup);
381
entry1->setTitle("testTitle");
382
auto uuid1 = QUuid::createUuid();
383
entry1->setUuid(uuid1);
385
auto entry2 = new Entry();
386
entry2->setGroup(m_rootGroup);
387
entry2->setTitle("testTitle2");
388
auto uuid2 = QUuid::createUuid();
389
entry2->setUuid(uuid2);
391
m_searchResult = m_entrySearcher.search("uuid:", m_rootGroup);
392
QCOMPARE(m_searchResult.count(), 2);
394
m_searchResult = m_entrySearcher.search("uuid:" + Tools::uuidToHex(uuid1), m_rootGroup);
395
QCOMPARE(m_searchResult.count(), 1);