1 //===-- ReproducerTest.cpp ------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
11
12 #include "lldb/Utility/FileSpec.h"
13 #include "lldb/Utility/Reproducer.h"
14 #include "lldb/Utility/ReproducerProvider.h"
15 #include "llvm/ADT/ScopeExit.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Testing/Support/Error.h"
18
19 using namespace llvm;
20 using namespace lldb_private;
21 using namespace lldb_private::repro;
22
23 class DummyProvider : public repro::Provider<DummyProvider> {
24 public:
25 struct Info {
26 static const char *name;
27 static const char *file;
28 };
29
DummyProvider(const FileSpec & directory)30 DummyProvider(const FileSpec &directory) : Provider(directory) {}
31
32 static char ID;
33 };
34
35 class YamlMultiProvider
36 : public MultiProvider<YamlRecorder, YamlMultiProvider> {
37 public:
38 struct Info {
39 static const char *name;
40 static const char *file;
41 };
42
YamlMultiProvider(const FileSpec & directory)43 YamlMultiProvider(const FileSpec &directory) : MultiProvider(directory) {}
44
45 static char ID;
46 };
47
48 const char *DummyProvider::Info::name = "dummy";
49 const char *DummyProvider::Info::file = "dummy.yaml";
50 const char *YamlMultiProvider::Info::name = "mutli";
51 const char *YamlMultiProvider::Info::file = "mutli.yaml";
52 char DummyProvider::ID = 0;
53 char YamlMultiProvider::ID = 0;
54
55 class DummyReproducer : public Reproducer {
56 public:
DummyReproducer()57 DummyReproducer() : Reproducer(){};
58
59 using Reproducer::SetCapture;
60 using Reproducer::SetReplay;
61 };
62
63 struct YamlData {
YamlDataYamlData64 YamlData() : i(-1) {}
YamlDataYamlData65 YamlData(int i) : i(i) {}
66 int i;
67 };
68
operator ==(const YamlData & LHS,const YamlData & RHS)69 inline bool operator==(const YamlData &LHS, const YamlData &RHS) {
70 return LHS.i == RHS.i;
71 }
72
73 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(YamlData)
74
75 namespace llvm {
76 namespace yaml {
77 template <> struct MappingTraits<YamlData> {
mappingllvm::yaml::MappingTraits78 static void mapping(IO &io, YamlData &Y) { io.mapRequired("i", Y.i); };
79 };
80 } // namespace yaml
81 } // namespace llvm
82
TEST(ReproducerTest,SetCapture)83 TEST(ReproducerTest, SetCapture) {
84 DummyReproducer reproducer;
85
86 // Initially both generator and loader are unset.
87 EXPECT_EQ(nullptr, reproducer.GetGenerator());
88 EXPECT_EQ(nullptr, reproducer.GetLoader());
89
90 // Enable capture and check that means we have a generator.
91 EXPECT_THAT_ERROR(
92 reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
93 Succeeded());
94 EXPECT_NE(nullptr, reproducer.GetGenerator());
95 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
96 reproducer.GetGenerator()->GetRoot());
97 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
98 reproducer.GetReproducerPath());
99
100 // Ensure that we cannot enable replay.
101 EXPECT_THAT_ERROR(
102 reproducer.SetReplay(FileSpec("//bogus/path", FileSpec::Style::posix)),
103 Failed());
104 EXPECT_EQ(nullptr, reproducer.GetLoader());
105
106 // Ensure we can disable the generator again.
107 EXPECT_THAT_ERROR(reproducer.SetCapture(llvm::None), Succeeded());
108 EXPECT_EQ(nullptr, reproducer.GetGenerator());
109 EXPECT_EQ(nullptr, reproducer.GetLoader());
110 }
111
TEST(ReproducerTest,SetReplay)112 TEST(ReproducerTest, SetReplay) {
113 DummyReproducer reproducer;
114
115 // Initially both generator and loader are unset.
116 EXPECT_EQ(nullptr, reproducer.GetGenerator());
117 EXPECT_EQ(nullptr, reproducer.GetLoader());
118
119 // Expected to fail because we can't load the index.
120 EXPECT_THAT_ERROR(
121 reproducer.SetReplay(FileSpec("//bogus/path", FileSpec::Style::posix)),
122 Failed());
123 // However the loader should still be set, which we check here.
124 EXPECT_NE(nullptr, reproducer.GetLoader());
125
126 // Make sure the bogus path is correctly set.
127 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
128 reproducer.GetLoader()->GetRoot());
129 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
130 reproducer.GetReproducerPath());
131
132 // Ensure that we cannot enable replay.
133 EXPECT_THAT_ERROR(
134 reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
135 Failed());
136 EXPECT_EQ(nullptr, reproducer.GetGenerator());
137 }
138
TEST(GeneratorTest,Create)139 TEST(GeneratorTest, Create) {
140 DummyReproducer reproducer;
141
142 EXPECT_THAT_ERROR(
143 reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
144 Succeeded());
145 auto &generator = *reproducer.GetGenerator();
146
147 auto *provider = generator.Create<DummyProvider>();
148 EXPECT_NE(nullptr, provider);
149 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
150 provider->GetRoot());
151 }
152
TEST(GeneratorTest,Get)153 TEST(GeneratorTest, Get) {
154 DummyReproducer reproducer;
155
156 EXPECT_THAT_ERROR(
157 reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
158 Succeeded());
159 auto &generator = *reproducer.GetGenerator();
160
161 auto *provider = generator.Create<DummyProvider>();
162 EXPECT_NE(nullptr, provider);
163
164 auto *provider_alt = generator.Get<DummyProvider>();
165 EXPECT_EQ(provider, provider_alt);
166 }
167
TEST(GeneratorTest,GetOrCreate)168 TEST(GeneratorTest, GetOrCreate) {
169 DummyReproducer reproducer;
170
171 EXPECT_THAT_ERROR(
172 reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
173 Succeeded());
174 auto &generator = *reproducer.GetGenerator();
175
176 auto &provider = generator.GetOrCreate<DummyProvider>();
177 EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
178 provider.GetRoot());
179
180 auto &provider_alt = generator.GetOrCreate<DummyProvider>();
181 EXPECT_EQ(&provider, &provider_alt);
182 }
183
TEST(GeneratorTest,YamlMultiProvider)184 TEST(GeneratorTest, YamlMultiProvider) {
185 SmallString<128> root;
186 std::error_code ec = llvm::sys::fs::createUniqueDirectory("reproducer", root);
187 ASSERT_FALSE(static_cast<bool>(ec));
188
189 auto cleanup = llvm::make_scope_exit(
190 [&] { EXPECT_FALSE(llvm::sys::fs::remove_directories(root.str())); });
191
192 YamlData data0(0);
193 YamlData data1(1);
194 YamlData data2(2);
195 YamlData data3(3);
196
197 {
198 DummyReproducer reproducer;
199 EXPECT_THAT_ERROR(reproducer.SetCapture(FileSpec(root.str())), Succeeded());
200
201 auto &generator = *reproducer.GetGenerator();
202 auto *provider = generator.Create<YamlMultiProvider>();
203 ASSERT_NE(nullptr, provider);
204
205 auto *recorder = provider->GetNewRecorder();
206 ASSERT_NE(nullptr, recorder);
207 recorder->Record(data0);
208 recorder->Record(data1);
209
210 recorder = provider->GetNewRecorder();
211 ASSERT_NE(nullptr, recorder);
212 recorder->Record(data2);
213 recorder->Record(data3);
214
215 generator.Keep();
216 }
217
218 {
219 DummyReproducer reproducer;
220 EXPECT_THAT_ERROR(reproducer.SetReplay(FileSpec(root.str())), Succeeded());
221
222 auto &loader = *reproducer.GetLoader();
223 std::unique_ptr<repro::MultiLoader<YamlMultiProvider>> multi_loader =
224 repro::MultiLoader<YamlMultiProvider>::Create(&loader);
225
226 // Read the first file.
227 {
228 llvm::Optional<std::string> file = multi_loader->GetNextFile();
229 EXPECT_TRUE(static_cast<bool>(file));
230
231 auto buffer = llvm::MemoryBuffer::getFile(*file);
232 EXPECT_TRUE(static_cast<bool>(buffer));
233
234 yaml::Input yin((*buffer)->getBuffer());
235 std::vector<YamlData> data;
236 yin >> data;
237
238 ASSERT_EQ(data.size(), 2U);
239 EXPECT_THAT(data, testing::ElementsAre(data0, data1));
240 }
241
242 // Read the second file.
243 {
244 llvm::Optional<std::string> file = multi_loader->GetNextFile();
245 EXPECT_TRUE(static_cast<bool>(file));
246
247 auto buffer = llvm::MemoryBuffer::getFile(*file);
248 EXPECT_TRUE(static_cast<bool>(buffer));
249
250 yaml::Input yin((*buffer)->getBuffer());
251 std::vector<YamlData> data;
252 yin >> data;
253
254 ASSERT_EQ(data.size(), 2U);
255 EXPECT_THAT(data, testing::ElementsAre(data2, data3));
256 }
257
258 // There is no third file.
259 llvm::Optional<std::string> file = multi_loader->GetNextFile();
260 EXPECT_FALSE(static_cast<bool>(file));
261 }
262 }
263