• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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