diff --git a/api/include/opentelemetry/context/TBD b/api/include/opentelemetry/context/TBD deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/include/opentelemetry/context/context.h b/api/include/opentelemetry/context/context.h new file mode 100644 index 0000000000..35b7920c09 --- /dev/null +++ b/api/include/opentelemetry/context/context.h @@ -0,0 +1,92 @@ +#pragma once + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/context/context_value.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/key_value_iterable_view.h" + +#include +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace context +{ + +// The context class provides a context identifier. +// This is a dummy class that is meant to be overridden, +// the methods return default values. +class Context +{ + +public: + Context() = default; + + // Contructor, creates a context object from a map of keys + // and identifiers. + template ::value> * = nullptr> + Context(const T &keys_and_values) + { + trace::KeyValueIterableView iterable{keys_and_values}; + iterable.ForEachKeyValue([&](nostd::string_view key, context::ContextValue value) noexcept { + context_map_[std::string(key)] = value; + return true; + }); + } + + // Accepts a key and a value and then returns a new context that + // contains both the original pairs and the new pair. + template + Context SetValue(nostd::string_view key, T &value) noexcept + { + std::map context_map_copy; + trace::KeyValueIterableView> context_map_iterable{ + context_map_}; + + context_map_iterable.ForEachKeyValue([&](nostd::string_view key, + context::ContextValue value) noexcept { + context_map_copy[std::string(key)] = value; + return true; + }); + + context_map_copy[std::string(key)] = value; + + return Context(context_map_copy); + } + + // Accepts a new iterable and then returns a new context that + // contains both the original pairs and the new pair. + template ::value> * = nullptr> + Context SetValues(T &keys_and_values) noexcept + { + std::map context_map_copy; + trace::KeyValueIterableView> context_map_iterable{ + context_map_}; + + context_map_iterable.ForEachKeyValue([&](nostd::string_view key, + context::ContextValue value) noexcept { + context_map_copy[std::string(key)] = value; + return true; + }); + + trace::KeyValueIterableView iterable{keys_and_values}; + + iterable.ForEachKeyValue([&](nostd::string_view key, context::ContextValue value) noexcept { + context_map_copy[std::string(key)] = value; + return true; + }); + + return Context(context_map_copy); + } + + // Returns the value associated with the passed in key. + context::ContextValue GetValue(nostd::string_view key) { return context_map_[std::string(key)]; } + + // Copy Constructors. + Context(const Context &other) = default; + Context &operator=(const Context &other) = default; + +private: + std::map context_map_; +}; +} // namespace context +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/context_value.h b/api/include/opentelemetry/context/context_value.h new file mode 100644 index 0000000000..bccdac46d1 --- /dev/null +++ b/api/include/opentelemetry/context/context_value.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace context +{ +using ContextValue = nostd::variant, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span, + nostd::span>; +} // namespace context +OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/context/BUILD b/api/test/context/BUILD new file mode 100644 index 0000000000..26728542b8 --- /dev/null +++ b/api/test/context/BUILD @@ -0,0 +1,10 @@ +cc_test( + name = "context_test", + srcs = [ + "context_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/api/test/context/CMakeLists.txt b/api/test/context/CMakeLists.txt new file mode 100644 index 0000000000..38d48ef0e8 --- /dev/null +++ b/api/test/context/CMakeLists.txt @@ -0,0 +1,8 @@ +include(GoogleTest) + +foreach(testname context_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests(TARGET ${testname} TEST_PREFIX context. TEST_LIST ${testname}) +endforeach() diff --git a/api/test/context/context_test.cc b/api/test/context/context_test.cc new file mode 100644 index 0000000000..ee366d1a1a --- /dev/null +++ b/api/test/context/context_test.cc @@ -0,0 +1,104 @@ +#include "opentelemetry/context/context.h" + +#include + +#include + +using namespace opentelemetry; + +// Tests that the context constructor accepts an std::map. +TEST(ContextTest, ContextIterableAcceptsMap) +{ + std::map map_test = {{"test_key", "123"}}; + context::Context context_test = context::Context(map_test); +} + +// Tests that the GetValue method returns the expected value. +TEST(ContextTest, ContextGetValueReturnsExpectedValue) +{ + std::map map_test = {{"test_key", "123"}, {"foo_key", "456"}}; + + context::Context context_test = context::Context(map_test); + EXPECT_EQ(nostd::get(context_test.GetValue("test_key")), "123"); + EXPECT_EQ(nostd::get(context_test.GetValue("foo_key")), "456"); +} + +// Tests that the SetValues method accepts an std::map. +TEST(ContextTest, ContextSetValuesAcceptsMap) +{ + std::map map_test = {{"test_key", "123"}}; + std::map map_test_write = {{"foo_key", "456"}}; + context::Context context_test = context::Context(map_test); + context::Context foo_test = context_test.SetValues(map_test_write); + EXPECT_EQ(nostd::get(foo_test.GetValue("test_key")), "123"); + EXPECT_EQ(nostd::get(foo_test.GetValue("foo_key")), "456"); +} + +// Tests that the SetValues method accepts a nostd::string_view and +// context::ContextValue. +TEST(ContextTest, ContextSetValuesAcceptsStringViewContextValue) +{ + nostd::string_view string_view_test = "string_view"; + context::ContextValue context_value_test = "123"; + context::Context context_test = context::Context(); + context::Context context_foo = context_test.SetValue(string_view_test, context_value_test); + EXPECT_EQ(nostd::get(context_foo.GetValue(string_view_test)), "123"); +} + +// Tests that the original context does not change when a value is +// written to it. +TEST(ContextTest, ContextImmutability) +{ + std::map map_test = {{"test_key", "123"}}; + context::Context context_test = context::Context(map_test); + + context::Context context_foo = context_test.SetValue("foo_key", "456"); +#if __EXCEPTIONS + EXPECT_THROW(nostd::get(context_test.GetValue("foo_key")), + nostd::bad_variant_access); +#else + EXPECT_DEATH({ nostd::get(context_test.GetValue("foo_key")); }, ""); +#endif +} + +// Tests that writing the same to a context overwrites the original value. +TEST(ContextTest, ContextKeyOverwrite) +{ + std::map map_test = {{"test_key", "123"}}; + context::Context context_test = context::Context(map_test); + context::Context context_foo = context_test.SetValue("test_key", "456"); + + EXPECT_EQ(nostd::get(context_foo.GetValue("test_key")), "456"); +} + +// Tests that the new Context Objects inherits the keys and values +// of the original context object. +TEST(ContextTest, ContextInheritance) +{ + + using M = std::map; + context::Context test_context = context::Context(); + + M m1 = {{"test_key", "123"}, {"foo_key", "456"}}; + M m2 = {{"other_key", "789"}}; + + context::Context foo_context = test_context.SetValues(m1); + context::Context other_context = foo_context.SetValues(m2); + + EXPECT_EQ(nostd::get(other_context.GetValue("test_key")), "123"); + EXPECT_EQ(nostd::get(other_context.GetValue("foo_key")), "456"); +} + +// Tests that copying a context copies the key value pairs as expected. +TEST(ContextTest, ContextCopyOperator) +{ + std::map test_map = { + {"test_key", "123"}, {"foo_key", "456"}, {"other_key", "789"}}; + + context::Context test_context = context::Context(test_map); + context::Context copied_context = test_context; + + EXPECT_EQ(nostd::get(copied_context.GetValue("test_key")), "123"); + EXPECT_EQ(nostd::get(copied_context.GetValue("foo_key")), "456"); + EXPECT_EQ(nostd::get(copied_context.GetValue("other_key")), "789"); +}