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 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 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: 57 DummyReproducer() : Reproducer(){}; 58 59 using Reproducer::SetCapture; 60 using Reproducer::SetReplay; 61 }; 62 63 struct YamlData { 64 YamlData() : i(-1) {} 65 YamlData(int i) : i(i) {} 66 int i; 67 }; 68 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> { 78 static void mapping(IO &io, YamlData &Y) { io.mapRequired("i", Y.i); }; 79 }; 80 } // namespace yaml 81 } // namespace llvm 82 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 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 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 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 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 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