blob: c1b9f38bc1eecb7f9661600247e989b30b761592 [file] [log] [blame]
//===- 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 <optional>
#include <vector>
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 <typename T> 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<std::string> Children;
std::vector<std::string> ChildrenNames;
};
namespace vendor {
struct VendorConfig : public Config {
VendorConfig(bool Enable) : Config(Enable) {}
std::optional<std::string> makeSessionId() override {
static int seed = 0;
return std::to_string(seed++);
}
};
std::shared_ptr<Config> getTelemetryConfig(const TestContext &Ctxt) {
return std::make_shared<VendorConfig>(/*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<std::string, std::string> 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<TestManager>
createInstance(Config *Config, TestContext *CurrentContext) {
if (!Config->EnableTelemetry)
return nullptr;
CurrentContext->ExpectedUuid = *(Config->makeSessionId());
std::unique_ptr<TestManager> Ret = std::make_unique<TestManager>(
CurrentContext, CurrentContext->ExpectedUuid);
// Add a destination.
Ret->addDestination(
std::make_unique<TestStorageDestination>(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<Config> getTelemetryConfig(const TestContext &Ctxt) {
if (Ctxt.HasVendorPlugin)
return vendor::getTelemetryConfig(Ctxt);
return std::make_shared<Config>(false);
}
#if LLVM_ENABLE_TELEMETRY
#define TELEMETRY_TEST(suite, test) TEST(suite, test)
#else
#define TELEMETRY_TEST(suite, test) TEST(DISABLED_##suite, test)
#endif
TELEMETRY_TEST(TelemetryTest, TelemetryDisabled) {
TestContext Context;
Context.HasVendorPlugin = false;
std::shared_ptr<Config> Config = getTelemetryConfig(Context);
auto Manager = vendor::TestManager::createInstance(Config.get(), &Context);
EXPECT_EQ(nullptr, Manager);
}
TELEMETRY_TEST(TelemetryTest, TelemetryEnabled) {
const std::string ToolName = "TelemetryTestTool";
// Preset some params.
TestContext Context;
Context.HasVendorPlugin = true;
Context.Buffer.clear();
std::shared_ptr<Config> 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