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 };
61 
62 struct YamlData {
YamlDataYamlData63   YamlData() : i(-1) {}
YamlDataYamlData64   YamlData(int i) : i(i) {}
65   int i;
66 };
67 
operator ==(const YamlData & LHS,const YamlData & RHS)68 inline bool operator==(const YamlData &LHS, const YamlData &RHS) {
69   return LHS.i == RHS.i;
70 }
71 
72 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(YamlData)
73 
74 namespace llvm {
75 namespace yaml {
76 template <> struct MappingTraits<YamlData> {
mappingllvm::yaml::MappingTraits77   static void mapping(IO &io, YamlData &Y) { io.mapRequired("i", Y.i); };
78 };
79 } // namespace yaml
80 } // namespace llvm
81 
TEST(ReproducerTest,SetCapture)82 TEST(ReproducerTest, SetCapture) {
83   DummyReproducer reproducer;
84 
85   // Initially both generator and loader are unset.
86   EXPECT_EQ(nullptr, reproducer.GetGenerator());
87   EXPECT_EQ(nullptr, reproducer.GetLoader());
88 
89   // Enable capture and check that means we have a generator.
90   EXPECT_THAT_ERROR(
91       reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
92       Succeeded());
93   EXPECT_NE(nullptr, reproducer.GetGenerator());
94   EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
95             reproducer.GetGenerator()->GetRoot());
96   EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
97             reproducer.GetReproducerPath());
98 
99   // Ensure that we cannot enable replay.
100   EXPECT_EQ(nullptr, reproducer.GetLoader());
101 
102   // Ensure we can disable the generator again.
103   EXPECT_THAT_ERROR(reproducer.SetCapture(llvm::None), Succeeded());
104   EXPECT_EQ(nullptr, reproducer.GetGenerator());
105   EXPECT_EQ(nullptr, reproducer.GetLoader());
106 }
107 
TEST(GeneratorTest,Create)108 TEST(GeneratorTest, Create) {
109   DummyReproducer reproducer;
110 
111   EXPECT_THAT_ERROR(
112       reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
113       Succeeded());
114   auto &generator = *reproducer.GetGenerator();
115 
116   auto *provider = generator.Create<DummyProvider>();
117   EXPECT_NE(nullptr, provider);
118   EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
119             provider->GetRoot());
120 }
121 
TEST(GeneratorTest,Get)122 TEST(GeneratorTest, Get) {
123   DummyReproducer reproducer;
124 
125   EXPECT_THAT_ERROR(
126       reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
127       Succeeded());
128   auto &generator = *reproducer.GetGenerator();
129 
130   auto *provider = generator.Create<DummyProvider>();
131   EXPECT_NE(nullptr, provider);
132 
133   auto *provider_alt = generator.Get<DummyProvider>();
134   EXPECT_EQ(provider, provider_alt);
135 }
136 
TEST(GeneratorTest,GetOrCreate)137 TEST(GeneratorTest, GetOrCreate) {
138   DummyReproducer reproducer;
139 
140   EXPECT_THAT_ERROR(
141       reproducer.SetCapture(FileSpec("//bogus/path", FileSpec::Style::posix)),
142       Succeeded());
143   auto &generator = *reproducer.GetGenerator();
144 
145   auto &provider = generator.GetOrCreate<DummyProvider>();
146   EXPECT_EQ(FileSpec("//bogus/path", FileSpec::Style::posix),
147             provider.GetRoot());
148 
149   auto &provider_alt = generator.GetOrCreate<DummyProvider>();
150   EXPECT_EQ(&provider, &provider_alt);
151 }
152 
TEST(GeneratorTest,YamlMultiProvider)153 TEST(GeneratorTest, YamlMultiProvider) {
154   SmallString<128> root;
155   std::error_code ec = llvm::sys::fs::createUniqueDirectory("reproducer", root);
156   ASSERT_FALSE(static_cast<bool>(ec));
157 
158   auto cleanup = llvm::make_scope_exit(
159       [&] { EXPECT_FALSE(llvm::sys::fs::remove_directories(root.str())); });
160 
161   YamlData data0(0);
162   YamlData data1(1);
163   YamlData data2(2);
164   YamlData data3(3);
165 
166   {
167     DummyReproducer reproducer;
168     EXPECT_THAT_ERROR(reproducer.SetCapture(FileSpec(root.str())), Succeeded());
169 
170     auto &generator = *reproducer.GetGenerator();
171     auto *provider = generator.Create<YamlMultiProvider>();
172     ASSERT_NE(nullptr, provider);
173 
174     auto *recorder = provider->GetNewRecorder();
175     ASSERT_NE(nullptr, recorder);
176     recorder->Record(data0);
177     recorder->Record(data1);
178 
179     recorder = provider->GetNewRecorder();
180     ASSERT_NE(nullptr, recorder);
181     recorder->Record(data2);
182     recorder->Record(data3);
183 
184     generator.Keep();
185   }
186 }
187