QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
tst_storemodel.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include <QDir>
4#include <QFileSystemModel>
5#include <QtTest>
6
8
9class tst_storemodel : public QObject {
10 Q_OBJECT
11
12private Q_SLOTS:
13 void dataRemovesGpgExtension();
14 void dataWithInvalidIndex();
15 void flagsWithValidIndex();
16 void flagsWithInvalidIndex();
17 void mimeTypes();
18 void mimeData();
19 void lessThan();
20 void lessThanDirsFirst();
21 void supportedDropActions();
22 void supportedDragActions();
23 void filterAcceptsRowHidden();
24 void filterExcludesGitDirectory();
25 void filterAcceptsRowVisible();
26 void setModelAndStore();
27 void showThisWithNullFs();
28 void getStoreBasic();
29 void filterRegularExpression();
30};
31
32void tst_storemodel::dataRemovesGpgExtension() {
33 QTemporaryDir tempDir;
34 QFile f(tempDir.path() + "/test.gpg");
35 QVERIFY(f.open(QFile::WriteOnly));
36 f.close();
37
38 QFileSystemModel fsm;
39 fsm.setRootPath(tempDir.path());
40
41 StoreModel sm;
42 sm.setModelAndStore(&fsm, tempDir.path());
43
44 QModelIndex sourceIndex = fsm.index(tempDir.path() + "/test.gpg");
45 QModelIndex proxyIndex = sm.mapFromSource(sourceIndex);
46 QVariant displayData = sm.data(proxyIndex, Qt::DisplayRole);
47 QVERIFY(displayData.isValid());
48 QVERIFY(displayData.canConvert<QString>());
49 QString name = displayData.toString();
50 QVERIFY(!name.endsWith(".gpg"));
51}
52
53void tst_storemodel::flagsWithValidIndex() {
54 QTemporaryDir tempDir;
55 QFile f(tempDir.path() + "/test.gpg");
56 QVERIFY(f.open(QFile::WriteOnly));
57 f.close();
58
59 QFileSystemModel fsm;
60 fsm.setRootPath(tempDir.path());
61
62 StoreModel sm;
63 sm.setModelAndStore(&fsm, tempDir.path());
64
65 QModelIndex sourceIndex = fsm.index(tempDir.path() + "/test.gpg");
66 QModelIndex proxyIndex = sm.mapFromSource(sourceIndex);
67 Qt::ItemFlags flags = sm.flags(proxyIndex);
68 QVERIFY(flags & Qt::ItemIsDragEnabled);
69 QVERIFY(flags & Qt::ItemIsDropEnabled);
70}
71
72void tst_storemodel::flagsWithInvalidIndex() {
73 QTemporaryDir tempDir;
74 QFileSystemModel fsm;
75 StoreModel sm;
76 sm.setModelAndStore(&fsm, tempDir.path());
77
78 QModelIndex invalidIndex;
79 Qt::ItemFlags flags = sm.flags(invalidIndex);
80 QVERIFY(flags & Qt::ItemIsDropEnabled);
81}
82
83void tst_storemodel::mimeTypes() {
84 StoreModel sm;
85 QStringList types = sm.mimeTypes();
86 QVERIFY(
87 types.contains("application/vnd+qtpass.dragAndDropInfoPasswordStore"));
88}
89
90void tst_storemodel::lessThan() {
91 QTemporaryDir tempDir;
92 QFile passwordA(tempDir.path() + "/aaa.gpg");
93 QVERIFY(passwordA.open(QFile::WriteOnly));
94 passwordA.close();
95 QFile passwordB(tempDir.path() + "/bbb.gpg");
96 QVERIFY(passwordB.open(QFile::WriteOnly));
97 passwordB.close();
98
99 QFileSystemModel fsm;
100 fsm.setRootPath(tempDir.path());
101
102 StoreModel sm;
103 sm.setModelAndStore(&fsm, tempDir.path());
104
105 QModelIndex indexA = fsm.index(tempDir.path() + "/aaa.gpg");
106 QModelIndex indexB = fsm.index(tempDir.path() + "/bbb.gpg");
107
108 QVERIFY(sm.lessThan(indexA, indexB));
109}
110
111void tst_storemodel::supportedDropActions() {
112 StoreModel sm;
113 Qt::DropActions actions = sm.supportedDropActions();
114 QVERIFY(actions & Qt::CopyAction);
115 QVERIFY(actions & Qt::MoveAction);
116}
117
118void tst_storemodel::supportedDragActions() {
119 StoreModel sm;
120 Qt::DropActions actions = sm.supportedDragActions();
121 QVERIFY(actions & Qt::CopyAction);
122 QVERIFY(actions & Qt::MoveAction);
123}
124
125void tst_storemodel::filterAcceptsRowHidden() {
126 QTemporaryDir tempDir;
127 QFile f(tempDir.path() + "/secret.gpg");
128 QVERIFY(f.open(QFile::WriteOnly));
129 f.close();
130
131 QFileSystemModel fsm;
132 fsm.setRootPath(tempDir.path());
133
134 StoreModel sm;
135 sm.setModelAndStore(&fsm, tempDir.path());
136
137 sm.setFilterRegularExpression("nothing-matches-this");
138 QModelIndex index = fsm.index(tempDir.path() + "/secret.gpg");
139 bool result = sm.filterAcceptsRow(index.row(), index.parent());
140 QVERIFY(!result);
141}
142
143void tst_storemodel::filterExcludesGitDirectory() {
144 QTemporaryDir tempDir;
145 QDir dir(tempDir.path());
146 QVERIFY(dir.mkdir(".git"));
147
148 QFileSystemModel fsm;
149 fsm.setRootPath(tempDir.path());
150
151 StoreModel sm;
152 sm.setModelAndStore(&fsm, tempDir.path());
153
154 QModelIndex gitIndex = fsm.index(tempDir.path() + "/.git");
155 bool result = sm.filterAcceptsRow(gitIndex.row(), gitIndex.parent());
156 QVERIFY2(!result, ".git directory should be hidden");
157}
158
159void tst_storemodel::filterAcceptsRowVisible() {
160 QTemporaryDir tempDir;
161 QFile passwordFile(tempDir.path() + "/mypassword.gpg");
162 QVERIFY(passwordFile.open(QFile::WriteOnly));
163 passwordFile.close();
164
165 QFileSystemModel fsm;
166 fsm.setRootPath(tempDir.path());
167
168 StoreModel sm;
169 sm.setModelAndStore(&fsm, tempDir.path());
170
171 sm.setFilterRegularExpression("password");
172 QModelIndex index = fsm.index(tempDir.path() + "/mypassword.gpg");
173 bool result = sm.filterAcceptsRow(index.row(), index.parent());
174 QVERIFY(result);
175}
176
177void tst_storemodel::dataWithInvalidIndex() {
178 QFileSystemModel fsm;
179 QTemporaryDir tempDir;
180 StoreModel sm;
181 sm.setModelAndStore(&fsm, tempDir.path());
182
183 QModelIndex invalidIndex;
184 QVariant result = sm.data(invalidIndex, Qt::DisplayRole);
185 QVERIFY(result.isNull());
186}
187
188void tst_storemodel::mimeData() {
189 QTemporaryDir tempDir;
190 QFile testFile(tempDir.path() + "/testfile.gpg");
191 QVERIFY(testFile.open(QFile::WriteOnly));
192 testFile.close();
193
194 QFileSystemModel fsm;
195 fsm.setRootPath(tempDir.path());
196
197 StoreModel sm;
198 sm.setModelAndStore(&fsm, tempDir.path());
199
200 QModelIndex sourceIndex = fsm.index(tempDir.path() + "/testfile.gpg");
201 QModelIndex proxyIndex = sm.mapFromSource(sourceIndex);
202 QMimeData *data = sm.mimeData(QModelIndexList() << proxyIndex);
203 QVERIFY(data != nullptr);
204 QVERIFY(
205 data->hasFormat("application/vnd+qtpass.dragAndDropInfoPasswordStore"));
206 delete data;
207}
208
209void tst_storemodel::lessThanDirsFirst() {
210#ifdef Q_OS_MAC
211 QSKIP("Directory sorting differs on macOS");
212#else
213 QTemporaryDir tempDir;
214 QVERIFY(QDir(tempDir.path()).mkdir("folder"));
215 QVERIFY(QDir(tempDir.path()).exists("folder"));
216 QFile file(tempDir.path() + "/file.gpg");
217 QVERIFY(file.open(QFile::WriteOnly));
218 file.close();
219
220 QFileSystemModel fsm;
221 fsm.setRootPath(tempDir.path());
222
223 StoreModel sm;
224 sm.setModelAndStore(&fsm, tempDir.path());
225
226 QCoreApplication::processEvents();
227 QTRY_VERIFY(fsm.index(tempDir.path() + "/folder").isValid());
228 QTRY_VERIFY(fsm.index(tempDir.path() + "/file.gpg").isValid());
229
230 QModelIndex sourceFolderIdx = fsm.index(tempDir.path() + "/folder");
231 QModelIndex sourceFileIdx = fsm.index(tempDir.path() + "/file.gpg");
232
233 QVERIFY(sm.lessThan(sourceFolderIdx, sourceFileIdx));
234#endif
235}
236
237void tst_storemodel::setModelAndStore() {
238 QFileSystemModel fsm;
239 StoreModel sm;
240
241 QString storePath = "/test/store";
242 sm.setModelAndStore(&fsm, storePath);
243
244 QVERIFY(sm.sourceModel() == &fsm);
245}
246
247void tst_storemodel::showThisWithNullFs() {
248 StoreModel sm;
249 QModelIndex index;
250 bool result = sm.showThis(index);
251 QVERIFY(!result);
252}
253
254void tst_storemodel::getStoreBasic() {
255 QTemporaryDir tempDir;
256 QVERIFY2(tempDir.isValid(), "Temporary directory should be valid");
257 QFileSystemModel fsm;
258 StoreModel sm;
259 sm.setModelAndStore(&fsm, tempDir.path());
260 QString store = sm.getStore();
261 QVERIFY2(store == tempDir.path(), "Store path should match");
262}
263
264void tst_storemodel::filterRegularExpression() {
265 StoreModel sm;
266 sm.setFilterRegularExpression(QRegularExpression("test"));
267 QRegularExpression result = sm.filterRegularExpression();
268 QVERIFY2(result.pattern() == "test", "Filter should match test");
269}
270
271QTEST_MAIN(tst_storemodel)
272#include "tst_storemodel.moc"
QSortFilterProxyModel for filtering and displaying password store.
Definition storemodel.h:31
auto getStore() const -> QString
Get the password store root path.
Definition storemodel.h:90
auto data(const QModelIndex &index, int role) const -> QVariant override
Get display data for index.
auto mimeTypes() const -> QStringList override
Get supported MIME types for drag/drop.
auto filterAcceptsRow(int, const QModelIndex &) const -> bool override
Filter whether a row should be displayed.
auto mimeData(const QModelIndexList &indexes) const -> QMimeData *override
Create MIME data from indexes.
auto supportedDragActions() const -> Qt::DropActions override
Get supported drag actions.
auto showThis(const QModelIndex) const -> bool
Check if a specific index should be shown.
auto flags(const QModelIndex &index) const -> Qt::ItemFlags override
Get item flags for index.
auto lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const -> bool override
Compare two indices for sorting.
void setModelAndStore(QFileSystemModel *sourceModel, QString passStore)
Initialize model with source model and store path.
auto supportedDropActions() const -> Qt::DropActions override
Get supported drop actions.