1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "googletest.h"
27
28 #include "projectstoragemock.h"
29 #include "sqlitedatabasemock.h"
30
31 #include <projectstorage/sourcepathcache.h>
32
33 namespace {
34
35 using QmlDesigner::SourceContextId;
36 using QmlDesigner::SourceId;
37 using QmlDesigner::SourceIds;
38 using Cache = QmlDesigner::SourcePathCache<NiceMock<ProjectStorageMock>>;
39 using NFP = QmlDesigner::SourcePath;
40 using QmlDesigner::SourcePathView;
41 using QmlDesigner::SourcePathViews;
42 using QmlDesigner::Cache::SourceNameAndSourceContextId;
43
44 class SourcePathCache : public testing::Test
45 {
46 protected:
SourcePathCache()47 SourcePathCache()
48 {
49 ON_CALL(storageMock, fetchSourceContextId(Eq("/path/to")))
50 .WillByDefault(Return(SourceContextId{5}));
51 ON_CALL(storageMock, fetchSourceContextId(Eq("/path2/to")))
52 .WillByDefault(Return(SourceContextId{6}));
53 ON_CALL(storageMock, fetchSourceId(SourceContextId{5}, Eq("file.cpp")))
54 .WillByDefault(Return(SourceId{42}));
55 ON_CALL(storageMock, fetchSourceId(SourceContextId{5}, Eq("file2.cpp")))
56 .WillByDefault(Return(SourceId{63}));
57 ON_CALL(storageMock, fetchSourceId(SourceContextId{6}, Eq("file.cpp")))
58 .WillByDefault(Return(SourceId{72}));
59 ON_CALL(storageMock, fetchSourceContextPath(SourceContextId{5}))
60 .WillByDefault(Return(Utils::PathString("/path/to")));
61 ON_CALL(storageMock, fetchSourceNameAndSourceContextId(SourceId{42}))
62 .WillByDefault(Return(SourceNameAndSourceContextId("file.cpp", SourceContextId{5})));
63 ON_CALL(storageMockFilled, fetchAllSources())
64 .WillByDefault(Return(std::vector<QmlDesigner::Cache::Source>({
65 {"file.cpp", SourceContextId{6}, SourceId{72}},
66 {"file2.cpp", SourceContextId{5}, SourceId{63}},
67 {"file.cpp", SourceContextId{5}, SourceId{42}},
68 })));
69 ON_CALL(storageMockFilled, fetchAllSourceContexts())
70 .WillByDefault(Return(std::vector<QmlDesigner::Cache::SourceContext>(
71 {{"/path2/to", SourceContextId{6}}, {"/path/to", SourceContextId{5}}})));
72 ON_CALL(storageMockFilled, fetchSourceContextId(Eq("/path/to")))
73 .WillByDefault(Return(SourceContextId{5}));
74 ON_CALL(storageMockFilled, fetchSourceId(SourceContextId{5}, Eq("file.cpp")))
75 .WillByDefault(Return(SourceId{42}));
76 }
77
78 protected:
79 NiceMock<SqliteDatabaseMock> databaseMock;
80 NiceMock<ProjectStorageMock> storageMock{databaseMock};
81 Cache cache{storageMock};
82 NiceMock<ProjectStorageMock> storageMockFilled{databaseMock};
83 Cache cacheNotFilled{storageMockFilled};
84 };
85
TEST_F(SourcePathCache,SourceIdWithOutAnyEntryCallSourceContextId)86 TEST_F(SourcePathCache, SourceIdWithOutAnyEntryCallSourceContextId)
87 {
88 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to")));
89
90 cache.sourceId(SourcePathView("/path/to/file.cpp"));
91 }
92
TEST_F(SourcePathCache,SourceIdWithOutAnyEntryCalls)93 TEST_F(SourcePathCache, SourceIdWithOutAnyEntryCalls)
94 {
95 EXPECT_CALL(storageMock, fetchSourceId(SourceContextId{5}, Eq("file.cpp")));
96
97 cache.sourceId(SourcePathView("/path/to/file.cpp"));
98 }
99
TEST_F(SourcePathCache,SourceIdOfSourceIdWithOutAnyEntry)100 TEST_F(SourcePathCache, SourceIdOfSourceIdWithOutAnyEntry)
101 {
102 auto sourceId = cache.sourceId(SourcePathView("/path/to/file.cpp"));
103
104 ASSERT_THAT(sourceId, SourceId{42});
105 }
106
TEST_F(SourcePathCache,IfEntryExistsDontCallInStrorage)107 TEST_F(SourcePathCache, IfEntryExistsDontCallInStrorage)
108 {
109 cache.sourceId(SourcePathView("/path/to/file.cpp"));
110
111 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to"))).Times(0);
112 EXPECT_CALL(storageMock, fetchSourceId(SourceContextId{5}, Eq("file.cpp"))).Times(0);
113
114 cache.sourceId(SourcePathView("/path/to/file.cpp"));
115 }
116
TEST_F(SourcePathCache,IfDirectoryEntryExistsDontCallFetchSourceContextIdButStillCallFetchSourceId)117 TEST_F(SourcePathCache, IfDirectoryEntryExistsDontCallFetchSourceContextIdButStillCallFetchSourceId)
118 {
119 cache.sourceId(SourcePathView("/path/to/file2.cpp"));
120
121 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to"))).Times(0);
122 EXPECT_CALL(storageMock, fetchSourceId(SourceContextId{5}, Eq("file.cpp")));
123
124 cache.sourceId(SourcePathView("/path/to/file.cpp"));
125 }
126
TEST_F(SourcePathCache,GetSourceIdWithCachedValue)127 TEST_F(SourcePathCache, GetSourceIdWithCachedValue)
128 {
129 cache.sourceId(SourcePathView("/path/to/file.cpp"));
130
131 auto sourceId = cache.sourceId(SourcePathView("/path/to/file.cpp"));
132
133 ASSERT_THAT(sourceId, SourceId{42});
134 }
135
TEST_F(SourcePathCache,GetSourceIdWithSourceContextIdCached)136 TEST_F(SourcePathCache, GetSourceIdWithSourceContextIdCached)
137 {
138 cache.sourceId(SourcePathView("/path/to/file.cpp"));
139
140 auto sourceId = cache.sourceId(SourcePathView("/path/to/file2.cpp"));
141
142 ASSERT_THAT(sourceId, SourceId{63});
143 }
144
TEST_F(SourcePathCache,ThrowForGettingAFilePathWithAnInvalidId)145 TEST_F(SourcePathCache, ThrowForGettingAFilePathWithAnInvalidId)
146 {
147 SourceId sourceId;
148
149 ASSERT_THROW(cache.sourcePath(sourceId), QmlDesigner::NoSourcePathForInvalidSourceId);
150 }
151
TEST_F(SourcePathCache,GetAFilePath)152 TEST_F(SourcePathCache, GetAFilePath)
153 {
154 SourceId sourceId = cache.sourceId(SourcePathView("/path/to/file.cpp"));
155
156 auto sourcePath = cache.sourcePath(sourceId);
157
158 ASSERT_THAT(sourcePath, Eq(SourcePathView{"/path/to/file.cpp"}));
159 }
160
TEST_F(SourcePathCache,GetAFilePathWithCachedSourceId)161 TEST_F(SourcePathCache, GetAFilePathWithCachedSourceId)
162 {
163 SourceId sourceId{42};
164
165 auto sourcePath = cache.sourcePath(sourceId);
166
167 ASSERT_THAT(sourcePath, Eq(SourcePathView{"/path/to/file.cpp"}));
168 }
169
TEST_F(SourcePathCache,FileNamesAreUniqueForEveryDirectory)170 TEST_F(SourcePathCache, FileNamesAreUniqueForEveryDirectory)
171 {
172 SourceId sourceId = cache.sourceId(SourcePathView("/path/to/file.cpp"));
173
174 SourceId sourcePath2Id = cache.sourceId(SourcePathView("/path2/to/file.cpp"));
175
176 ASSERT_THAT(sourcePath2Id, Ne(sourceId));
177 }
178
TEST_F(SourcePathCache,DuplicateFilePathsAreEqual)179 TEST_F(SourcePathCache, DuplicateFilePathsAreEqual)
180 {
181 SourceId sourcePath1Id = cache.sourceId(SourcePathView("/path/to/file.cpp"));
182
183 SourceId sourcePath2Id = cache.sourceId(SourcePathView("/path/to/file.cpp"));
184
185 ASSERT_THAT(sourcePath2Id, Eq(sourcePath1Id));
186 }
187
TEST_F(SourcePathCache,SourceContextIdCallsFetchSourceContextId)188 TEST_F(SourcePathCache, SourceContextIdCallsFetchSourceContextId)
189 {
190 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to")));
191
192 cache.sourceContextId(Utils::SmallString("/path/to"));
193 }
194
TEST_F(SourcePathCache,SecondSourceContextIdCallsNotFetchSourceContextId)195 TEST_F(SourcePathCache, SecondSourceContextIdCallsNotFetchSourceContextId)
196 {
197 cache.sourceContextId(Utils::SmallString("/path/to"));
198
199 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to"))).Times(0);
200
201 cache.sourceContextId(Utils::SmallString("/path/to"));
202 }
203
TEST_F(SourcePathCache,SourceContextIdWithTrailingSlash)204 TEST_F(SourcePathCache, SourceContextIdWithTrailingSlash)
205 {
206 EXPECT_CALL(storageMock, fetchSourceContextId(Eq("/path/to")));
207
208 cache.sourceContextId(Utils::SmallString("/path/to/"));
209 }
210
TEST_F(SourcePathCache,SourceContextId)211 TEST_F(SourcePathCache, SourceContextId)
212 {
213 auto id = cache.sourceContextId(Utils::SmallString("/path/to"));
214
215 ASSERT_THAT(id, Eq(SourceContextId{5}));
216 }
217
TEST_F(SourcePathCache,SourceContextIdIsAlreadyInCache)218 TEST_F(SourcePathCache, SourceContextIdIsAlreadyInCache)
219 {
220 auto firstId = cache.sourceContextId(Utils::SmallString("/path/to"));
221
222 auto secondId = cache.sourceContextId(Utils::SmallString("/path/to"));
223
224 ASSERT_THAT(secondId, firstId);
225 }
226
TEST_F(SourcePathCache,SourceContextIdIsAlreadyInCacheWithTrailingSlash)227 TEST_F(SourcePathCache, SourceContextIdIsAlreadyInCacheWithTrailingSlash)
228 {
229 auto firstId = cache.sourceContextId(Utils::SmallString("/path/to/"));
230
231 auto secondId = cache.sourceContextId(Utils::SmallString("/path/to/"));
232
233 ASSERT_THAT(secondId, firstId);
234 }
235
TEST_F(SourcePathCache,SourceContextIdIsAlreadyInCacheWithAndWithoutTrailingSlash)236 TEST_F(SourcePathCache, SourceContextIdIsAlreadyInCacheWithAndWithoutTrailingSlash)
237 {
238 auto firstId = cache.sourceContextId(Utils::SmallString("/path/to/"));
239
240 auto secondId = cache.sourceContextId(Utils::SmallString("/path/to"));
241
242 ASSERT_THAT(secondId, firstId);
243 }
244
TEST_F(SourcePathCache,SourceContextIdIsAlreadyInCacheWithoutAndWithTrailingSlash)245 TEST_F(SourcePathCache, SourceContextIdIsAlreadyInCacheWithoutAndWithTrailingSlash)
246 {
247 auto firstId = cache.sourceContextId(Utils::SmallString("/path/to"));
248
249 auto secondId = cache.sourceContextId(Utils::SmallString("/path/to/"));
250
251 ASSERT_THAT(secondId, firstId);
252 }
253
TEST_F(SourcePathCache,ThrowForGettingADirectoryPathWithAnInvalidId)254 TEST_F(SourcePathCache, ThrowForGettingADirectoryPathWithAnInvalidId)
255 {
256 SourceContextId sourceContextId;
257
258 ASSERT_THROW(cache.sourceContextPath(sourceContextId),
259 QmlDesigner::NoSourceContextPathForInvalidSourceContextId);
260 }
261
TEST_F(SourcePathCache,GetADirectoryPath)262 TEST_F(SourcePathCache, GetADirectoryPath)
263 {
264 SourceContextId sourceContextId{5};
265
266 auto sourceContextPath = cache.sourceContextPath(sourceContextId);
267
268 ASSERT_THAT(sourceContextPath, Eq(Utils::SmallStringView{"/path/to"}));
269 }
270
TEST_F(SourcePathCache,GetADirectoryPathWithCachedSourceContextId)271 TEST_F(SourcePathCache, GetADirectoryPathWithCachedSourceContextId)
272 {
273 SourceContextId sourceContextId{5};
274 cache.sourceContextPath(sourceContextId);
275
276 auto sourceContextPath = cache.sourceContextPath(sourceContextId);
277
278 ASSERT_THAT(sourceContextPath, Eq(Utils::SmallStringView{"/path/to"}));
279 }
280
TEST_F(SourcePathCache,DirectoryPathCallsFetchDirectoryPath)281 TEST_F(SourcePathCache, DirectoryPathCallsFetchDirectoryPath)
282 {
283 EXPECT_CALL(storageMock, fetchSourceContextPath(Eq(SourceContextId{5})));
284
285 cache.sourceContextPath(SourceContextId{5});
286 }
287
TEST_F(SourcePathCache,SecondDirectoryPathCallsNotFetchDirectoryPath)288 TEST_F(SourcePathCache, SecondDirectoryPathCallsNotFetchDirectoryPath)
289 {
290 cache.sourceContextPath(SourceContextId{5});
291
292 EXPECT_CALL(storageMock, fetchSourceContextPath(_)).Times(0);
293
294 cache.sourceContextPath(SourceContextId{5});
295 }
296
TEST_F(SourcePathCache,ThrowForGettingASourceContextIdWithAnInvalidSourceId)297 TEST_F(SourcePathCache, ThrowForGettingASourceContextIdWithAnInvalidSourceId)
298 {
299 SourceId sourceId;
300
301 ASSERT_THROW(cache.sourceContextId(sourceId), QmlDesigner::NoSourcePathForInvalidSourceId);
302 }
303
TEST_F(SourcePathCache,FetchSourceContextIdBySourceId)304 TEST_F(SourcePathCache, FetchSourceContextIdBySourceId)
305 {
306 auto sourceContextId = cache.sourceContextId(SourceId{42});
307
308 ASSERT_THAT(sourceContextId, Eq(SourceContextId{5}));
309 }
310
TEST_F(SourcePathCache,FetchSourceContextIdBySourceIdCached)311 TEST_F(SourcePathCache, FetchSourceContextIdBySourceIdCached)
312 {
313 cache.sourceContextId(SourceId{42});
314
315 auto sourceContextId = cache.sourceContextId(SourceId{42});
316
317 ASSERT_THAT(sourceContextId, Eq(SourceContextId{5}));
318 }
319
TEST_F(SourcePathCache,FetchFilePathAfterFetchingSourceContextIdBySourceId)320 TEST_F(SourcePathCache, FetchFilePathAfterFetchingSourceContextIdBySourceId)
321 {
322 cache.sourceContextId(SourceId{42});
323
324 auto sourcePath = cache.sourcePath(SourceId{42});
325
326 ASSERT_THAT(sourcePath, Eq("/path/to/file.cpp"));
327 }
328
TEST_F(SourcePathCache,FetchSourceContextIdAfterFetchingFilePathBySourceId)329 TEST_F(SourcePathCache, FetchSourceContextIdAfterFetchingFilePathBySourceId)
330 {
331 cache.sourcePath(SourceId{42});
332
333 auto sourceContextId = cache.sourceContextId(SourceId{42});
334
335 ASSERT_THAT(sourceContextId, Eq(SourceContextId{5}));
336 }
337
TEST_F(SourcePathCache,FetchAllSourceContextsAndSourcesAtCreation)338 TEST_F(SourcePathCache, FetchAllSourceContextsAndSourcesAtCreation)
339 {
340 EXPECT_CALL(storageMock, fetchAllSourceContexts());
341 EXPECT_CALL(storageMock, fetchAllSources());
342
343 Cache cache{storageMock};
344 }
345
TEST_F(SourcePathCache,GetFileIdInFilledCache)346 TEST_F(SourcePathCache, GetFileIdInFilledCache)
347 {
348 Cache cacheFilled{storageMockFilled};
349
350 auto id = cacheFilled.sourceId("/path2/to/file.cpp");
351
352 ASSERT_THAT(id, Eq(SourceId{72}));
353 }
354
TEST_F(SourcePathCache,GetSourceContextIdInFilledCache)355 TEST_F(SourcePathCache, GetSourceContextIdInFilledCache)
356 {
357 Cache cacheFilled{storageMockFilled};
358
359 auto id = cacheFilled.sourceContextId(SourceId{42});
360
361 ASSERT_THAT(id, Eq(SourceContextId{5}));
362 }
363
TEST_F(SourcePathCache,GetDirectoryPathInFilledCache)364 TEST_F(SourcePathCache, GetDirectoryPathInFilledCache)
365 {
366 Cache cacheFilled{storageMockFilled};
367
368 auto path = cacheFilled.sourceContextPath(SourceContextId{5});
369
370 ASSERT_THAT(path, Eq("/path/to"));
371 }
372
TEST_F(SourcePathCache,GetFilePathInFilledCache)373 TEST_F(SourcePathCache, GetFilePathInFilledCache)
374 {
375 Cache cacheFilled{storageMockFilled};
376
377 auto path = cacheFilled.sourcePath(SourceId{42});
378
379 ASSERT_THAT(path, Eq("/path/to/file.cpp"));
380 }
381
TEST_F(SourcePathCache,GetFileIdInAfterPopulateIfEmpty)382 TEST_F(SourcePathCache, GetFileIdInAfterPopulateIfEmpty)
383 {
384 cacheNotFilled.populateIfEmpty();
385
386 auto id = cacheNotFilled.sourceId("/path2/to/file.cpp");
387
388 ASSERT_THAT(id, Eq(SourceId{72}));
389 }
390
TEST_F(SourcePathCache,DontPopulateIfNotEmpty)391 TEST_F(SourcePathCache, DontPopulateIfNotEmpty)
392 {
393 cacheNotFilled.sourceId("/path/to/file.cpp");
394
395 EXPECT_CALL(storageMockFilled, fetchAllSourceContexts()).Times(0);
396 EXPECT_CALL(storageMockFilled, fetchAllSources()).Times(0);
397
398 cacheNotFilled.populateIfEmpty();
399 }
400
TEST_F(SourcePathCache,GetSourceContextIdAfterPopulateIfEmpty)401 TEST_F(SourcePathCache, GetSourceContextIdAfterPopulateIfEmpty)
402 {
403 cacheNotFilled.populateIfEmpty();
404
405 auto id = cacheNotFilled.sourceContextId(SourceId{42});
406
407 ASSERT_THAT(id, Eq(SourceContextId{5}));
408 }
409
TEST_F(SourcePathCache,GetDirectoryPathAfterPopulateIfEmpty)410 TEST_F(SourcePathCache, GetDirectoryPathAfterPopulateIfEmpty)
411 {
412 cacheNotFilled.populateIfEmpty();
413
414 auto path = cacheNotFilled.sourceContextPath(SourceContextId{5});
415
416 ASSERT_THAT(path, Eq("/path/to"));
417 }
418
TEST_F(SourcePathCache,GetFilePathAfterPopulateIfEmptye)419 TEST_F(SourcePathCache, GetFilePathAfterPopulateIfEmptye)
420 {
421 cacheNotFilled.populateIfEmpty();
422
423 auto path = cacheNotFilled.sourcePath(SourceId{42});
424
425 ASSERT_THAT(path, Eq("/path/to/file.cpp"));
426 }
427
428 } // namespace
429