1*766a08dfSBen Barham //===- unittests/Serialization/ModuleCacheTest.cpp - CI tests -------------===//
2*766a08dfSBen Barham //
3*766a08dfSBen Barham // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*766a08dfSBen Barham // See https://llvm.org/LICENSE.txt for license information.
5*766a08dfSBen Barham // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*766a08dfSBen Barham //
7*766a08dfSBen Barham //===----------------------------------------------------------------------===//
8*766a08dfSBen Barham 
9*766a08dfSBen Barham #include "clang/Basic/FileManager.h"
10*766a08dfSBen Barham #include "clang/Frontend/CompilerInstance.h"
11*766a08dfSBen Barham #include "clang/Frontend/CompilerInvocation.h"
12*766a08dfSBen Barham #include "clang/Frontend/FrontendActions.h"
13*766a08dfSBen Barham #include "clang/Lex/HeaderSearch.h"
14*766a08dfSBen Barham #include "llvm/ADT/SmallString.h"
15*766a08dfSBen Barham #include "llvm/Support/FileSystem.h"
16*766a08dfSBen Barham #include "llvm/Support/raw_ostream.h"
17*766a08dfSBen Barham 
18*766a08dfSBen Barham #include "gtest/gtest.h"
19*766a08dfSBen Barham 
20*766a08dfSBen Barham using namespace llvm;
21*766a08dfSBen Barham using namespace clang;
22*766a08dfSBen Barham 
23*766a08dfSBen Barham namespace {
24*766a08dfSBen Barham 
25*766a08dfSBen Barham class ModuleCacheTest : public ::testing::Test {
26*766a08dfSBen Barham   void SetUp() override {
27*766a08dfSBen Barham     ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir));
28*766a08dfSBen Barham 
29*766a08dfSBen Barham     ModuleCachePath = SmallString<256>(TestDir);
30*766a08dfSBen Barham     sys::path::append(ModuleCachePath, "mcp");
31*766a08dfSBen Barham     ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath));
32*766a08dfSBen Barham   }
33*766a08dfSBen Barham 
34*766a08dfSBen Barham   void TearDown() override { sys::fs::remove_directories(TestDir); }
35*766a08dfSBen Barham 
36*766a08dfSBen Barham public:
37*766a08dfSBen Barham   SmallString<256> TestDir;
38*766a08dfSBen Barham   SmallString<256> ModuleCachePath;
39*766a08dfSBen Barham 
40*766a08dfSBen Barham   void addFile(StringRef Path, StringRef Contents) {
41*766a08dfSBen Barham     ASSERT_FALSE(sys::path::is_absolute(Path));
42*766a08dfSBen Barham 
43*766a08dfSBen Barham     SmallString<256> AbsPath(TestDir);
44*766a08dfSBen Barham     sys::path::append(AbsPath, Path);
45*766a08dfSBen Barham 
46*766a08dfSBen Barham     std::error_code EC;
47*766a08dfSBen Barham     ASSERT_FALSE(
48*766a08dfSBen Barham         sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
49*766a08dfSBen Barham     llvm::raw_fd_ostream OS(AbsPath, EC);
50*766a08dfSBen Barham     ASSERT_FALSE(EC);
51*766a08dfSBen Barham     OS << Contents;
52*766a08dfSBen Barham   }
53*766a08dfSBen Barham 
54*766a08dfSBen Barham   void addDuplicateFrameworks() {
55*766a08dfSBen Barham     addFile("test.m", R"cpp(
56*766a08dfSBen Barham         @import Top;
57*766a08dfSBen Barham     )cpp");
58*766a08dfSBen Barham 
59*766a08dfSBen Barham     addFile("frameworks/Top.framework/Headers/top.h", R"cpp(
60*766a08dfSBen Barham         @import M;
61*766a08dfSBen Barham     )cpp");
62*766a08dfSBen Barham     addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp(
63*766a08dfSBen Barham         framework module Top [system] {
64*766a08dfSBen Barham           header "top.h"
65*766a08dfSBen Barham           export *
66*766a08dfSBen Barham         }
67*766a08dfSBen Barham     )cpp");
68*766a08dfSBen Barham 
69*766a08dfSBen Barham     addFile("frameworks/M.framework/Headers/m.h", R"cpp(
70*766a08dfSBen Barham         void foo();
71*766a08dfSBen Barham     )cpp");
72*766a08dfSBen Barham     addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp(
73*766a08dfSBen Barham         framework module M [system] {
74*766a08dfSBen Barham           header "m.h"
75*766a08dfSBen Barham           export *
76*766a08dfSBen Barham         }
77*766a08dfSBen Barham     )cpp");
78*766a08dfSBen Barham 
79*766a08dfSBen Barham     addFile("frameworks2/M.framework/Headers/m.h", R"cpp(
80*766a08dfSBen Barham         void foo();
81*766a08dfSBen Barham     )cpp");
82*766a08dfSBen Barham     addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp(
83*766a08dfSBen Barham         framework module M [system] {
84*766a08dfSBen Barham           header "m.h"
85*766a08dfSBen Barham           export *
86*766a08dfSBen Barham         }
87*766a08dfSBen Barham     )cpp");
88*766a08dfSBen Barham   }
89*766a08dfSBen Barham };
90*766a08dfSBen Barham 
91*766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPath) {
92*766a08dfSBen Barham   addDuplicateFrameworks();
93*766a08dfSBen Barham 
94*766a08dfSBen Barham   SmallString<256> MCPArg("-fmodules-cache-path=");
95*766a08dfSBen Barham   MCPArg.append(ModuleCachePath);
96*766a08dfSBen Barham   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
97*766a08dfSBen Barham       CompilerInstance::createDiagnostics(new DiagnosticOptions());
98*766a08dfSBen Barham 
99*766a08dfSBen Barham   // First run should pass with no errors
100*766a08dfSBen Barham   const char *Args[] = {"clang",        "-fmodules",          "-Fframeworks",
101*766a08dfSBen Barham                         MCPArg.c_str(), "-working-directory", TestDir.c_str(),
102*766a08dfSBen Barham                         "test.m"};
103*766a08dfSBen Barham   std::shared_ptr<CompilerInvocation> Invocation =
104*766a08dfSBen Barham       createInvocationFromCommandLine(Args, Diags);
105*766a08dfSBen Barham   ASSERT_TRUE(Invocation);
106*766a08dfSBen Barham   CompilerInstance Instance;
107*766a08dfSBen Barham   Instance.setDiagnostics(Diags.get());
108*766a08dfSBen Barham   Instance.setInvocation(Invocation);
109*766a08dfSBen Barham   SyntaxOnlyAction Action;
110*766a08dfSBen Barham   ASSERT_TRUE(Instance.ExecuteAction(Action));
111*766a08dfSBen Barham   ASSERT_FALSE(Diags->hasErrorOccurred());
112*766a08dfSBen Barham 
113*766a08dfSBen Barham   // Now add `frameworks2` to the search path. `Top.pcm` will have a reference
114*766a08dfSBen Barham   // to the `M` from `frameworks`, but a search will find the `M` from
115*766a08dfSBen Barham   // `frameworks2` - causing a mismatch and it to be considered out of date.
116*766a08dfSBen Barham   //
117*766a08dfSBen Barham   // Normally this would be fine - `M` and the modules it depends on would be
118*766a08dfSBen Barham   // rebuilt. However, since we have a shared module cache and thus an already
119*766a08dfSBen Barham   // finalized `Top`, recompiling `Top` will cause the existing module to be
120*766a08dfSBen Barham   // removed from the cache, causing possible crashed if it is ever used.
121*766a08dfSBen Barham   //
122*766a08dfSBen Barham   // Make sure that an error occurs instead.
123*766a08dfSBen Barham   const char *Args2[] = {"clang",         "-fmodules",    "-Fframeworks2",
124*766a08dfSBen Barham                          "-Fframeworks",  MCPArg.c_str(), "-working-directory",
125*766a08dfSBen Barham                          TestDir.c_str(), "test.m"};
126*766a08dfSBen Barham   std::shared_ptr<CompilerInvocation> Invocation2 =
127*766a08dfSBen Barham       createInvocationFromCommandLine(Args2, Diags);
128*766a08dfSBen Barham   ASSERT_TRUE(Invocation2);
129*766a08dfSBen Barham   CompilerInstance Instance2(Instance.getPCHContainerOperations(),
130*766a08dfSBen Barham                              &Instance.getModuleCache());
131*766a08dfSBen Barham   Instance2.setDiagnostics(Diags.get());
132*766a08dfSBen Barham   Instance2.setInvocation(Invocation2);
133*766a08dfSBen Barham   SyntaxOnlyAction Action2;
134*766a08dfSBen Barham   ASSERT_FALSE(Instance2.ExecuteAction(Action2));
135*766a08dfSBen Barham   ASSERT_TRUE(Diags->hasErrorOccurred());
136*766a08dfSBen Barham }
137*766a08dfSBen Barham 
138*766a08dfSBen Barham TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) {
139*766a08dfSBen Barham   addDuplicateFrameworks();
140*766a08dfSBen Barham 
141*766a08dfSBen Barham   SmallString<256> MCPArg("-fmodules-cache-path=");
142*766a08dfSBen Barham   MCPArg.append(ModuleCachePath);
143*766a08dfSBen Barham   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
144*766a08dfSBen Barham       CompilerInstance::createDiagnostics(new DiagnosticOptions());
145*766a08dfSBen Barham 
146*766a08dfSBen Barham   // First run should pass with no errors
147*766a08dfSBen Barham   const char *Args[] = {"clang",        "-fmodules",          "-Fframeworks",
148*766a08dfSBen Barham                         MCPArg.c_str(), "-working-directory", TestDir.c_str(),
149*766a08dfSBen Barham                         "test.m"};
150*766a08dfSBen Barham   std::shared_ptr<CompilerInvocation> Invocation =
151*766a08dfSBen Barham       createInvocationFromCommandLine(Args, Diags);
152*766a08dfSBen Barham   ASSERT_TRUE(Invocation);
153*766a08dfSBen Barham   CompilerInstance Instance;
154*766a08dfSBen Barham   Instance.setDiagnostics(Diags.get());
155*766a08dfSBen Barham   Instance.setInvocation(Invocation);
156*766a08dfSBen Barham   SyntaxOnlyAction Action;
157*766a08dfSBen Barham   ASSERT_TRUE(Instance.ExecuteAction(Action));
158*766a08dfSBen Barham   ASSERT_FALSE(Diags->hasErrorOccurred());
159*766a08dfSBen Barham 
160*766a08dfSBen Barham   // Same as `CachedModuleNewPath` but while allowing errors. This is a hard
161*766a08dfSBen Barham   // failure where the module wasn't created, so it should still fail.
162*766a08dfSBen Barham   const char *Args2[] = {
163*766a08dfSBen Barham       "clang",         "-fmodules",    "-Fframeworks2",
164*766a08dfSBen Barham       "-Fframeworks",  MCPArg.c_str(), "-working-directory",
165*766a08dfSBen Barham       TestDir.c_str(), "-Xclang",      "-fallow-pcm-with-compiler-errors",
166*766a08dfSBen Barham       "test.m"};
167*766a08dfSBen Barham   std::shared_ptr<CompilerInvocation> Invocation2 =
168*766a08dfSBen Barham       createInvocationFromCommandLine(Args2, Diags);
169*766a08dfSBen Barham   ASSERT_TRUE(Invocation2);
170*766a08dfSBen Barham   CompilerInstance Instance2(Instance.getPCHContainerOperations(),
171*766a08dfSBen Barham                              &Instance.getModuleCache());
172*766a08dfSBen Barham   Instance2.setDiagnostics(Diags.get());
173*766a08dfSBen Barham   Instance2.setInvocation(Invocation2);
174*766a08dfSBen Barham   SyntaxOnlyAction Action2;
175*766a08dfSBen Barham   ASSERT_FALSE(Instance2.ExecuteAction(Action2));
176*766a08dfSBen Barham   ASSERT_TRUE(Diags->hasErrorOccurred());
177*766a08dfSBen Barham }
178*766a08dfSBen Barham 
179*766a08dfSBen Barham } // anonymous namespace
180