xref: /llvm-project/llvm/unittests/Telemetry/TelemetryTest.cpp (revision 2c95e60df53ba1a5765b3fad9e8ddaff70f21994)
1 //===- llvm/unittest/Telemetry/TelemetryTest.cpp - Telemetry unittests ---===//
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 "llvm/Telemetry/Telemetry.h"
10 #include "llvm/ADT/StringRef.h"
11 #include "llvm/Support/Casting.h"
12 #include "llvm/Support/Error.h"
13 #include "gtest/gtest.h"
14 #include <optional>
15 #include <vector>
16 
17 namespace llvm {
18 namespace telemetry {
19 // Testing parameters.
20 //
21 // These are set by each test to force certain outcomes.
22 struct TestContext {
23   // Controlling whether there is vendor plugin.  In "real" implementation, the
24   // plugin-registration framework will handle the overrides but for tests, we
25   // just use a bool flag to decide which function to call.
26   bool HasVendorPlugin = false;
27 
28   // This field contains data emitted by the framework for later
29   // verification by the tests.
30   std::string Buffer = "";
31 
32   // The expected Uuid generated by the fake tool.
33   std::string ExpectedUuid = "";
34 };
35 
36 class StringSerializer : public Serializer {
37 public:
38   const std::string &getString() { return Buffer; }
39 
40   Error init() override {
41     if (Started)
42       return createStringError("Serializer already in use");
43     Started = true;
44     Buffer.clear();
45     return Error::success();
46   }
47 
48   void write(StringRef KeyName, bool Value) override {
49     writeHelper(KeyName, Value);
50   }
51 
52   void write(StringRef KeyName, StringRef Value) override {
53     writeHelper(KeyName, Value);
54   }
55 
56   void write(StringRef KeyName, int Value) override {
57     writeHelper(KeyName, Value);
58   }
59 
60   void write(StringRef KeyName, long Value) override {
61     writeHelper(KeyName, Value);
62   }
63 
64   void write(StringRef KeyName, long long Value) override {
65     writeHelper(KeyName, Value);
66   }
67 
68   void write(StringRef KeyName, unsigned int Value) override {
69     writeHelper(KeyName, Value);
70   }
71 
72   void write(StringRef KeyName, unsigned long Value) override {
73     writeHelper(KeyName, Value);
74   }
75 
76   void write(StringRef KeyName, unsigned long long Value) override {
77     writeHelper(KeyName, Value);
78   }
79 
80   void beginObject(StringRef KeyName) override {
81     Children.push_back(std::string("\n"));
82     ChildrenNames.push_back(KeyName.str());
83   }
84 
85   void endObject() override {
86     assert(!Children.empty() && !ChildrenNames.empty());
87     std::string ChildBuff = Children.back();
88     std::string Name = ChildrenNames.back();
89     Children.pop_back();
90     ChildrenNames.pop_back();
91     writeHelper(Name, ChildBuff);
92   }
93 
94   Error finalize() override {
95     assert(Children.empty() && ChildrenNames.empty());
96     if (!Started)
97       return createStringError("Serializer not currently in use");
98     Started = false;
99     return Error::success();
100   }
101 
102 private:
103   template <typename T> void writeHelper(StringRef Name, T Value) {
104     assert(Started && "serializer not started");
105     if (Children.empty())
106       Buffer.append((Name + ":" + Twine(Value) + "\n").str());
107     else
108       Children.back().append((Name + ":" + Twine(Value) + "\n").str());
109   }
110 
111   bool Started = false;
112   std::string Buffer;
113   std::vector<std::string> Children;
114   std::vector<std::string> ChildrenNames;
115 };
116 
117 namespace vendor {
118 struct VendorConfig : public Config {
119   VendorConfig(bool Enable) : Config(Enable) {}
120   std::optional<std::string> makeSessionId() override {
121     static int seed = 0;
122     return std::to_string(seed++);
123   }
124 };
125 
126 std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
127   return std::make_shared<VendorConfig>(/*EnableTelemetry=*/true);
128 }
129 
130 class TestStorageDestination : public Destination {
131 public:
132   TestStorageDestination(TestContext *Ctxt) : CurrentContext(Ctxt) {}
133 
134   Error receiveEntry(const TelemetryInfo *Entry) override {
135     if (Error Err = serializer.init())
136       return Err;
137 
138     Entry->serialize(serializer);
139     if (Error Err = serializer.finalize())
140       return Err;
141 
142     CurrentContext->Buffer.append(serializer.getString());
143     return Error::success();
144   }
145 
146   StringLiteral name() const override { return "TestDestination"; }
147 
148 private:
149   TestContext *CurrentContext;
150   StringSerializer serializer;
151 };
152 
153 struct StartupInfo : public TelemetryInfo {
154   std::string ToolName;
155   std::map<std::string, std::string> MetaData;
156 
157   void serialize(Serializer &serializer) const override {
158     TelemetryInfo::serialize(serializer);
159     serializer.write("ToolName", ToolName);
160     serializer.write("MetaData", MetaData);
161   }
162 };
163 
164 struct ExitInfo : public TelemetryInfo {
165   int ExitCode;
166   std::string ExitDesc;
167   void serialize(Serializer &serializer) const override {
168     TelemetryInfo::serialize(serializer);
169     serializer.write("ExitCode", ExitCode);
170     serializer.write("ExitDesc", ExitDesc);
171   }
172 };
173 
174 class TestManager : public Manager {
175 public:
176   static std::unique_ptr<TestManager>
177   createInstance(Config *Config, TestContext *CurrentContext) {
178     if (!Config->EnableTelemetry)
179       return nullptr;
180     CurrentContext->ExpectedUuid = *(Config->makeSessionId());
181     std::unique_ptr<TestManager> Ret = std::make_unique<TestManager>(
182         CurrentContext, CurrentContext->ExpectedUuid);
183 
184     // Add a destination.
185     Ret->addDestination(
186         std::make_unique<TestStorageDestination>(CurrentContext));
187 
188     return Ret;
189   }
190 
191   TestManager(TestContext *Ctxt, std::string Id)
192       : CurrentContext(Ctxt), SessionId(Id) {}
193 
194   Error preDispatch(TelemetryInfo *Entry) override {
195     Entry->SessionId = SessionId;
196     (void)CurrentContext;
197     return Error::success();
198   }
199 
200   std::string getSessionId() { return SessionId; }
201 
202 private:
203   TestContext *CurrentContext;
204   const std::string SessionId;
205 };
206 } // namespace vendor
207 
208 std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
209   if (Ctxt.HasVendorPlugin)
210     return vendor::getTelemetryConfig(Ctxt);
211 
212   return std::make_shared<Config>(false);
213 }
214 
215 TEST(TelemetryTest, TelemetryDisabled) {
216   TestContext Context;
217   Context.HasVendorPlugin = false;
218 
219   std::shared_ptr<Config> Config = getTelemetryConfig(Context);
220   auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
221   EXPECT_EQ(nullptr, Manager);
222 }
223 
224 TEST(TelemetryTest, TelemetryEnabled) {
225   const std::string ToolName = "TelemetryTestTool";
226 
227   // Preset some params.
228   TestContext Context;
229   Context.HasVendorPlugin = true;
230   Context.Buffer.clear();
231 
232   std::shared_ptr<Config> Config = getTelemetryConfig(Context);
233   auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
234 
235   EXPECT_STREQ(Manager->getSessionId().c_str(), Context.ExpectedUuid.c_str());
236 
237   vendor::StartupInfo S;
238   S.ToolName = ToolName;
239   S.MetaData["a"] = "A";
240   S.MetaData["b"] = "B";
241 
242   Error startupEmitStatus = Manager->dispatch(&S);
243   EXPECT_FALSE(startupEmitStatus);
244   std::string ExpectedBuffer =
245       "SessionId:0\nToolName:TelemetryTestTool\nMetaData:\na:A\nb:B\n\n";
246   EXPECT_EQ(ExpectedBuffer, Context.Buffer);
247   Context.Buffer.clear();
248 
249   vendor::ExitInfo E;
250   E.ExitCode = 0;
251   E.ExitDesc = "success";
252   Error exitEmitStatus = Manager->dispatch(&E);
253   EXPECT_FALSE(exitEmitStatus);
254   ExpectedBuffer = "SessionId:0\nExitCode:0\nExitDesc:success\n";
255   EXPECT_EQ(ExpectedBuffer, Context.Buffer);
256 }
257 
258 } // namespace telemetry
259 } // namespace llvm
260