//===- llvm/unittest/Telemetry/TelemetryTest.cpp - Telemetry unittests ---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Telemetry/Telemetry.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include "gtest/gtest.h" #include #include namespace llvm { namespace telemetry { // Testing parameters. // // These are set by each test to force certain outcomes. struct TestContext { // Controlling whether there is vendor plugin. In "real" implementation, the // plugin-registration framework will handle the overrides but for tests, we // just use a bool flag to decide which function to call. bool HasVendorPlugin = false; // This field contains data emitted by the framework for later // verification by the tests. std::string Buffer = ""; // The expected Uuid generated by the fake tool. std::string ExpectedUuid = ""; }; class StringSerializer : public Serializer { public: const std::string &getString() { return Buffer; } Error init() override { if (Started) return createStringError("Serializer already in use"); Started = true; Buffer.clear(); return Error::success(); } void write(StringRef KeyName, bool Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, StringRef Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, int Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, long Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, long long Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, unsigned int Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, unsigned long Value) override { writeHelper(KeyName, Value); } void write(StringRef KeyName, unsigned long long Value) override { writeHelper(KeyName, Value); } void beginObject(StringRef KeyName) override { Children.push_back(std::string("\n")); ChildrenNames.push_back(KeyName.str()); } void endObject() override { assert(!Children.empty() && !ChildrenNames.empty()); std::string ChildBuff = Children.back(); std::string Name = ChildrenNames.back(); Children.pop_back(); ChildrenNames.pop_back(); writeHelper(Name, ChildBuff); } Error finalize() override { assert(Children.empty() && ChildrenNames.empty()); if (!Started) return createStringError("Serializer not currently in use"); Started = false; return Error::success(); } private: template void writeHelper(StringRef Name, T Value) { assert(Started && "serializer not started"); if (Children.empty()) Buffer.append((Name + ":" + Twine(Value) + "\n").str()); else Children.back().append((Name + ":" + Twine(Value) + "\n").str()); } bool Started = false; std::string Buffer; std::vector Children; std::vector ChildrenNames; }; namespace vendor { struct VendorConfig : public Config { VendorConfig(bool Enable) : Config(Enable) {} std::optional makeSessionId() override { static int seed = 0; return std::to_string(seed++); } }; std::shared_ptr getTelemetryConfig(const TestContext &Ctxt) { return std::make_shared(/*EnableTelemetry=*/true); } class TestStorageDestination : public Destination { public: TestStorageDestination(TestContext *Ctxt) : CurrentContext(Ctxt) {} Error receiveEntry(const TelemetryInfo *Entry) override { if (Error Err = serializer.init()) return Err; Entry->serialize(serializer); if (Error Err = serializer.finalize()) return Err; CurrentContext->Buffer.append(serializer.getString()); return Error::success(); } StringLiteral name() const override { return "TestDestination"; } private: TestContext *CurrentContext; StringSerializer serializer; }; struct StartupInfo : public TelemetryInfo { std::string ToolName; std::map MetaData; void serialize(Serializer &serializer) const override { TelemetryInfo::serialize(serializer); serializer.write("ToolName", ToolName); serializer.write("MetaData", MetaData); } }; struct ExitInfo : public TelemetryInfo { int ExitCode; std::string ExitDesc; void serialize(Serializer &serializer) const override { TelemetryInfo::serialize(serializer); serializer.write("ExitCode", ExitCode); serializer.write("ExitDesc", ExitDesc); } }; class TestManager : public Manager { public: static std::unique_ptr createInstance(Config *Config, TestContext *CurrentContext) { if (!Config->EnableTelemetry) return nullptr; CurrentContext->ExpectedUuid = *(Config->makeSessionId()); std::unique_ptr Ret = std::make_unique( CurrentContext, CurrentContext->ExpectedUuid); // Add a destination. Ret->addDestination( std::make_unique(CurrentContext)); return Ret; } TestManager(TestContext *Ctxt, std::string Id) : CurrentContext(Ctxt), SessionId(Id) {} Error preDispatch(TelemetryInfo *Entry) override { Entry->SessionId = SessionId; (void)CurrentContext; return Error::success(); } std::string getSessionId() { return SessionId; } private: TestContext *CurrentContext; const std::string SessionId; }; } // namespace vendor std::shared_ptr getTelemetryConfig(const TestContext &Ctxt) { if (Ctxt.HasVendorPlugin) return vendor::getTelemetryConfig(Ctxt); return std::make_shared(false); } TEST(TelemetryTest, TelemetryDisabled) { TestContext Context; Context.HasVendorPlugin = false; std::shared_ptr Config = getTelemetryConfig(Context); auto Manager = vendor::TestManager::createInstance(Config.get(), &Context); EXPECT_EQ(nullptr, Manager); } TEST(TelemetryTest, TelemetryEnabled) { const std::string ToolName = "TelemetryTestTool"; // Preset some params. TestContext Context; Context.HasVendorPlugin = true; Context.Buffer.clear(); std::shared_ptr Config = getTelemetryConfig(Context); auto Manager = vendor::TestManager::createInstance(Config.get(), &Context); EXPECT_STREQ(Manager->getSessionId().c_str(), Context.ExpectedUuid.c_str()); vendor::StartupInfo S; S.ToolName = ToolName; S.MetaData["a"] = "A"; S.MetaData["b"] = "B"; Error startupEmitStatus = Manager->dispatch(&S); EXPECT_FALSE(startupEmitStatus); std::string ExpectedBuffer = "SessionId:0\nToolName:TelemetryTestTool\nMetaData:\na:A\nb:B\n\n"; EXPECT_EQ(ExpectedBuffer, Context.Buffer); Context.Buffer.clear(); vendor::ExitInfo E; E.ExitCode = 0; E.ExitDesc = "success"; Error exitEmitStatus = Manager->dispatch(&E); EXPECT_FALSE(exitEmitStatus); ExpectedBuffer = "SessionId:0\nExitCode:0\nExitDesc:success\n"; EXPECT_EQ(ExpectedBuffer, Context.Buffer); } } // namespace telemetry } // namespace llvm