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 }; 61 62 struct YamlData { 63 YamlData() : i(-1) {} 64 YamlData(int i) : i(i) {} 65 int i; 66 }; 67 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> { 77 static void mapping(IO &io, YamlData &Y) { io.mapRequired("i", Y.i); }; 78 }; 79 } // namespace yaml 80 } // namespace llvm 81 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 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 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 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 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