1 /*
2     This file is part of the KDE project
3     SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "udsentrytest.h"
9 
10 #include <QDataStream>
11 #include <QTemporaryFile>
12 #include <QTest>
13 #include <QVector>
14 #include <qplatformdefs.h>
15 
16 #include <kfileitem.h>
17 #include <udsentry.h>
18 
19 #include "kiotesthelper.h"
20 
21 struct UDSTestField {
UDSTestFieldUDSTestField22     UDSTestField()
23     {
24     }
25 
UDSTestFieldUDSTestField26     UDSTestField(uint uds, const QString &value)
27         : m_uds(uds)
28         , m_string(value)
29     {
30         Q_ASSERT(uds & KIO::UDSEntry::UDS_STRING);
31     }
32 
UDSTestFieldUDSTestField33     UDSTestField(uint uds, long long value)
34         : m_uds(uds)
35         , m_long(value)
36     {
37         Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER);
38     }
39 
40     uint m_uds;
41     QString m_string;
42     long long m_long;
43 };
44 
45 /**
46  * Test that storing UDSEntries to a stream and then re-loading them works.
47  */
testSaveLoad()48 void UDSEntryTest::testSaveLoad()
49 {
50     const QVector<QVector<UDSTestField>> testCases{// Data for 1st UDSEntry.
51                                                    {UDSTestField(KIO::UDSEntry::UDS_SIZE, 1),
52                                                     UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user1")),
53                                                     UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")),
54                                                     UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1")),
55                                                     UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456),
56                                                     UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 12345),
57                                                     UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 2),
58                                                     UDSTestField(KIO::UDSEntry::UDS_INODE, 56)},
59                                                    // 2nd entry: change some of the data.
60                                                    {UDSTestField(KIO::UDSEntry::UDS_SIZE, 2),
61                                                     UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user2")),
62                                                     UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")),
63                                                     UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename2")),
64                                                     UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12345),
65                                                     UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1234),
66                                                     UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87),
67                                                     UDSTestField(KIO::UDSEntry::UDS_INODE, 42)},
68                                                    // 3rd entry: keep the data, but change the order of the entries.
69                                                    {
70                                                        UDSTestField(KIO::UDSEntry::UDS_SIZE, 2),
71                                                        UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")),
72                                                        UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user2")),
73                                                        UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename2")),
74                                                        UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12345),
75                                                        UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87),
76                                                        UDSTestField(KIO::UDSEntry::UDS_INODE, 42),
77                                                        UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1234),
78                                                    },
79                                                    // 4th entry: change some of the data and the order of the entries.
80                                                    {UDSTestField(KIO::UDSEntry::UDS_SIZE, 2),
81                                                     UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user4")),
82                                                     UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")),
83                                                     UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346),
84                                                     UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87),
85                                                     UDSTestField(KIO::UDSEntry::UDS_INODE, 42),
86                                                     UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235),
87                                                     UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename4"))},
88                                                    // 5th entry: remove one field.
89                                                    {UDSTestField(KIO::UDSEntry::UDS_SIZE, 2),
90                                                     UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user4")),
91                                                     UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")),
92                                                     UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346),
93                                                     UDSTestField(KIO::UDSEntry::UDS_INODE, 42),
94                                                     UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235),
95                                                     UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename4"))},
96                                                    // 6th entry: add a new field, and change some others.
97                                                    {UDSTestField(KIO::UDSEntry::UDS_SIZE, 89),
98                                                     UDSTestField(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("icon6")),
99                                                     UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user6")),
100                                                     UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")),
101                                                     UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346),
102                                                     UDSTestField(KIO::UDSEntry::UDS_INODE, 32),
103                                                     UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235),
104                                                     UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename6"))}};
105     // Store the entries in a QByteArray.
106     QByteArray data;
107     {
108         QDataStream stream(&data, QIODevice::WriteOnly);
109         for (const QVector<UDSTestField> &testCase : testCases) {
110             KIO::UDSEntry entry;
111 
112             for (const UDSTestField &field : testCase) {
113                 uint uds = field.m_uds;
114                 if (uds & KIO::UDSEntry::UDS_STRING) {
115                     entry.fastInsert(uds, field.m_string);
116                 } else {
117                     Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER);
118                     entry.fastInsert(uds, field.m_long);
119                 }
120             }
121 
122             QCOMPARE(entry.count(), testCase.count());
123             stream << entry;
124         }
125     }
126 
127     // Re-load the entries and compare with the data in testCases.
128     {
129         QDataStream stream(data);
130         for (const QVector<UDSTestField> &testCase : testCases) {
131             KIO::UDSEntry entry;
132             stream >> entry;
133             QCOMPARE(entry.count(), testCase.count());
134 
135             for (const UDSTestField &field : testCase) {
136                 uint uds = field.m_uds;
137                 QVERIFY(entry.contains(uds));
138 
139                 if (uds & KIO::UDSEntry::UDS_STRING) {
140                     QCOMPARE(entry.stringValue(uds), field.m_string);
141                 } else {
142                     Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER);
143                     QCOMPARE(entry.numberValue(uds), field.m_long);
144                 }
145             }
146         }
147     }
148 
149     // Now: Store the fields manually in the order in which they appear in
150     // testCases, and re-load them. This ensures that loading the entries
151     // works no matter in which order the fields appear in the QByteArray.
152     data.clear();
153 
154     {
155         QDataStream stream(&data, QIODevice::WriteOnly);
156         for (const QVector<UDSTestField> &testCase : testCases) {
157             stream << testCase.count();
158 
159             for (const UDSTestField &field : testCase) {
160                 uint uds = field.m_uds;
161                 stream << uds;
162 
163                 if (uds & KIO::UDSEntry::UDS_STRING) {
164                     stream << field.m_string;
165                 } else {
166                     Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER);
167                     stream << field.m_long;
168                 }
169             }
170         }
171     }
172 
173     {
174         QDataStream stream(data);
175         for (const QVector<UDSTestField> &testCase : testCases) {
176             KIO::UDSEntry entry;
177             stream >> entry;
178             QCOMPARE(entry.count(), testCase.count());
179 
180             for (const UDSTestField &field : testCase) {
181                 uint uds = field.m_uds;
182                 QVERIFY(entry.contains(uds));
183 
184                 if (uds & KIO::UDSEntry::UDS_STRING) {
185                     QCOMPARE(entry.stringValue(uds), field.m_string);
186                 } else {
187                     Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER);
188                     QCOMPARE(entry.numberValue(uds), field.m_long);
189                 }
190             }
191         }
192     }
193 }
194 
195 /**
196  * Test to verify that move semantics work. This is only useful when ran through callgrind.
197  */
testMove()198 void UDSEntryTest::testMove()
199 {
200     // Create a temporary file. Just to make a UDSEntry further down.
201     QTemporaryFile file;
202     QVERIFY(file.open());
203     const QByteArray filePath = file.fileName().toLocal8Bit();
204     const QString fileName = QUrl(file.fileName()).fileName(); // QTemporaryFile::fileName returns the full path.
205     QVERIFY(!fileName.isEmpty());
206 
207     // We have a file now. Get the stat data from it to make the UDSEntry.
208     QT_STATBUF statBuf;
209     QVERIFY(QT_LSTAT(filePath.constData(), &statBuf) == 0);
210     KIO::UDSEntry entry(statBuf, fileName);
211 
212     // Verify that the name in the UDSEntry is the same as we've got from the fileName var.
213     QCOMPARE(fileName, entry.stringValue(KIO::UDSEntry::UDS_NAME));
214 
215     // That was the boilerplate code. Now for move semantics.
216     // First: move assignment.
217     {
218         // First a copy as we need to keep the entry for the next test.
219         KIO::UDSEntry entryCopy = entry;
220 
221         // Now move-assignment (two lines to prevent compiler optimization)
222         KIO::UDSEntry movedEntry;
223         movedEntry = std::move(entryCopy);
224 
225         // And verify that this works.
226         QCOMPARE(fileName, movedEntry.stringValue(KIO::UDSEntry::UDS_NAME));
227     }
228 
229     // Move constructor
230     {
231         // First a copy again
232         KIO::UDSEntry entryCopy = entry;
233 
234         // Now move-assignment
235         KIO::UDSEntry movedEntry(std::move(entryCopy));
236 
237         // And verify that this works.
238         QCOMPARE(fileName, movedEntry.stringValue(KIO::UDSEntry::UDS_NAME));
239     }
240 }
241 
242 /**
243  * Test to verify that equal semantics work.
244  */
testEquality()245 void UDSEntryTest::testEquality()
246 {
247     KIO::UDSEntry entry;
248     entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 1);
249     entry.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("user1"));
250     entry.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1"));
251     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1"));
252     entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456);
253     entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, 12345);
254     entry.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, 2);
255     entry.fastInsert(KIO::UDSEntry::UDS_INODE, 56);
256 
257     // Same as entry
258     KIO::UDSEntry entry2;
259     entry2.fastInsert(KIO::UDSEntry::UDS_SIZE, 1);
260     entry2.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("user1"));
261     entry2.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1"));
262     entry2.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1"));
263     entry2.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456);
264     entry2.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, 12345);
265     entry2.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, 2);
266     entry2.fastInsert(KIO::UDSEntry::UDS_INODE, 56);
267 
268     // 3rd entry: different user.
269     KIO::UDSEntry entry3;
270     entry3.fastInsert(KIO::UDSEntry::UDS_SIZE, 1);
271     entry3.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("other user"));
272     entry3.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1"));
273     entry3.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1"));
274     entry3.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456);
275     entry3.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, 12345);
276     entry3.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, 2);
277     entry3.fastInsert(KIO::UDSEntry::UDS_INODE, 56);
278 
279     // 4th entry : an additional field
280     KIO::UDSEntry entry4;
281     entry4.fastInsert(KIO::UDSEntry::UDS_SIZE, 1);
282     entry4.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("user1"));
283     entry4.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1"));
284     entry4.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1"));
285     entry4.fastInsert(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("home"));
286     entry4.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456);
287     entry4.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, 12345);
288     entry4.fastInsert(KIO::UDSEntry::UDS_DEVICE_ID, 2);
289     entry4.fastInsert(KIO::UDSEntry::UDS_INODE, 56);
290 
291     // ==
292     QVERIFY(entry == entry2);
293     QVERIFY(!(entry == entry3));
294     QVERIFY(!(entry == entry4));
295     QVERIFY(!(entry2 == entry3));
296 
297     // !=
298     QVERIFY(!(entry != entry2));
299     QVERIFY(entry != entry3);
300     QVERIFY(entry != entry4);
301     QVERIFY(entry2 != entry3);
302 
303     // make entry3 == entry
304     entry3.replace(KIO::UDSEntry::UDS_USER, QStringLiteral("user1"));
305 
306     QVERIFY(entry == entry3);
307     QVERIFY(entry2 == entry3);
308     QVERIFY(!(entry != entry3));
309     QVERIFY(!(entry2 != entry3));
310 }
311 
312 QTEST_MAIN(UDSEntryTest)
313