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