/proc/self/cwd/test/test_common/global.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include "source/common/common/lock_guard.h" |
4 | | #include "source/common/common/thread.h" |
5 | | |
6 | | #include "absl/container/flat_hash_map.h" |
7 | | |
8 | | namespace Envoy { |
9 | | namespace Test { |
10 | | |
11 | | /** |
12 | | * Helper class for managing Global<Type>s. |
13 | | * |
14 | | * This class is instantiated as a process-scoped singleton. It manages a map |
15 | | * from type-name to weak_ptr<GlobalHelper::Singleton>. That map accumulates |
16 | | * over a process lifetime and never shrinks. However, the weak_ptr will |
17 | | * generally be cleared after each test, as all shared_ptr references held in |
18 | | * Global<Type> instances are destroyed. This way, each unit-test gets a fresh |
19 | | * start. |
20 | | */ |
21 | | class Globals { |
22 | | public: |
23 | | /** |
24 | | * Walks through all global singletons and ensures that none of them are |
25 | | * active. No singletons should be allocated at the end of unit tests, so |
26 | | * this is called at the end of Envoy::TestRunner::runTests(). |
27 | | * |
28 | | * @return std::string empty string if quiescent, otherwise newline-separated |
29 | | * error messages. |
30 | | */ |
31 | 0 | static std::string describeActiveSingletons() { |
32 | 0 | return instance().describeActiveSingletonsHelper(); |
33 | 0 | } |
34 | | |
35 | | /** |
36 | | * Manages Singleton objects that are cleaned up after all references are |
37 | | * dropped. This class must not be templatized because as a map value where |
38 | | * every singleton in the map represents a different type. Instead we |
39 | | * templatize the ptr() and ref() methods. |
40 | | */ |
41 | | struct Singleton { |
42 | 169k | virtual ~Singleton() = default; |
43 | | virtual void* ptrHelper() PURE; |
44 | 9.37M | template <class Type> Type* ptr() { return static_cast<Type*>(ptrHelper()); } Envoy::Event::SingletonTimeSystemHelper* Envoy::Test::Globals::Singleton::ptr<Envoy::Event::SingletonTimeSystemHelper>() Line | Count | Source | 44 | 5.60M | template <class Type> Type* ptr() { return static_cast<Type*>(ptrHelper()); } |
Envoy::Stats::TestUtil::TestSymbolTableHelper* Envoy::Test::Globals::Singleton::ptr<Envoy::Stats::TestUtil::TestSymbolTableHelper>() Line | Count | Source | 44 | 3.77M | template <class Type> Type* ptr() { return static_cast<Type*>(ptrHelper()); } |
|
45 | 3.77M | template <class Type> Type& ref() { return *ptr<Type>(); } |
46 | | }; |
47 | | using SingletonSharedPtr = std::shared_ptr<Singleton>; |
48 | | |
49 | | /** |
50 | | * @return Type a singleton instance of Type. T must be default-constructible. |
51 | | */ |
52 | 4.98M | template <class Type> static SingletonSharedPtr get() { return instance().getHelper<Type>(); } std::__1::shared_ptr<Envoy::Test::Globals::Singleton> Envoy::Test::Globals::get<Envoy::Event::SingletonTimeSystemHelper>() Line | Count | Source | 52 | 644k | template <class Type> static SingletonSharedPtr get() { return instance().getHelper<Type>(); } |
std::__1::shared_ptr<Envoy::Test::Globals::Singleton> Envoy::Test::Globals::get<Envoy::Stats::TestUtil::TestSymbolTableHelper>() Line | Count | Source | 52 | 4.34M | template <class Type> static SingletonSharedPtr get() { return instance().getHelper<Type>(); } |
|
53 | | |
54 | | ~Globals() = delete; // Globals is constructed once and never destroyed. |
55 | | |
56 | | private: |
57 | | /** |
58 | | * Templatized derived class of Singleton which holds the Type object and is |
59 | | * responsible for deleting it using the correct destructor. |
60 | | */ |
61 | | template <class Type> struct TypedSingleton : public Singleton { |
62 | 169k | ~TypedSingleton() override = default; Envoy::Test::Globals::TypedSingleton<Envoy::Event::SingletonTimeSystemHelper>::~TypedSingleton() Line | Count | Source | 62 | 89.8k | ~TypedSingleton() override = default; |
Envoy::Test::Globals::TypedSingleton<Envoy::Stats::TestUtil::TestSymbolTableHelper>::~TypedSingleton() Line | Count | Source | 62 | 79.4k | ~TypedSingleton() override = default; |
|
63 | 9.37M | void* ptrHelper() override { return ptr_.get(); } Envoy::Test::Globals::TypedSingleton<Envoy::Event::SingletonTimeSystemHelper>::ptrHelper() Line | Count | Source | 63 | 5.60M | void* ptrHelper() override { return ptr_.get(); } |
Envoy::Test::Globals::TypedSingleton<Envoy::Stats::TestUtil::TestSymbolTableHelper>::ptrHelper() Line | Count | Source | 63 | 3.77M | void* ptrHelper() override { return ptr_.get(); } |
|
64 | | |
65 | | private: |
66 | | std::unique_ptr<Type> ptr_{std::make_unique<Type>()}; |
67 | | }; |
68 | | |
69 | 46 | Globals() = default; // Construct via Globals::instance(). |
70 | | |
71 | | /** |
72 | | * @return Globals& a singleton for Globals. |
73 | | */ |
74 | | static Globals& instance(); |
75 | | |
76 | 4.98M | template <class Type> SingletonSharedPtr getHelper() { |
77 | 4.98M | Thread::LockGuard map_lock(map_mutex_); |
78 | 4.98M | std::weak_ptr<Singleton>& weak_singleton_ref = singleton_map_[typeid(Type).name()]; |
79 | 4.98M | SingletonSharedPtr singleton = weak_singleton_ref.lock(); |
80 | | |
81 | 4.98M | if (singleton == nullptr) { |
82 | 169k | singleton = std::make_shared<TypedSingleton<Type>>(); |
83 | 169k | weak_singleton_ref = singleton; |
84 | 169k | } |
85 | 4.98M | return singleton; |
86 | 4.98M | } std::__1::shared_ptr<Envoy::Test::Globals::Singleton> Envoy::Test::Globals::getHelper<Envoy::Event::SingletonTimeSystemHelper>() Line | Count | Source | 76 | 644k | template <class Type> SingletonSharedPtr getHelper() { | 77 | 644k | Thread::LockGuard map_lock(map_mutex_); | 78 | 644k | std::weak_ptr<Singleton>& weak_singleton_ref = singleton_map_[typeid(Type).name()]; | 79 | 644k | SingletonSharedPtr singleton = weak_singleton_ref.lock(); | 80 | | | 81 | 644k | if (singleton == nullptr) { | 82 | 89.8k | singleton = std::make_shared<TypedSingleton<Type>>(); | 83 | 89.8k | weak_singleton_ref = singleton; | 84 | 89.8k | } | 85 | 644k | return singleton; | 86 | 644k | } |
std::__1::shared_ptr<Envoy::Test::Globals::Singleton> Envoy::Test::Globals::getHelper<Envoy::Stats::TestUtil::TestSymbolTableHelper>() Line | Count | Source | 76 | 4.34M | template <class Type> SingletonSharedPtr getHelper() { | 77 | 4.34M | Thread::LockGuard map_lock(map_mutex_); | 78 | 4.34M | std::weak_ptr<Singleton>& weak_singleton_ref = singleton_map_[typeid(Type).name()]; | 79 | 4.34M | SingletonSharedPtr singleton = weak_singleton_ref.lock(); | 80 | | | 81 | 4.34M | if (singleton == nullptr) { | 82 | 79.4k | singleton = std::make_shared<TypedSingleton<Type>>(); | 83 | 79.4k | weak_singleton_ref = singleton; | 84 | 79.4k | } | 85 | 4.34M | return singleton; | 86 | 4.34M | } |
|
87 | | |
88 | | std::string describeActiveSingletonsHelper(); |
89 | | |
90 | | Thread::MutexBasicLockable map_mutex_; |
91 | | absl::flat_hash_map<std::string, std::weak_ptr<Singleton>> |
92 | | singleton_map_ ABSL_GUARDED_BY(map_mutex_); |
93 | | }; |
94 | | |
95 | | /** |
96 | | * Helps manage classes that need to be instantiated once per server. In |
97 | | * production they must be plumbed through call/class hierarchy, but |
98 | | * in test-code the zero-arg-constructor Mock pattern makes this impractical. |
99 | | * Instead we use self-cleaning singletons. |
100 | | * |
101 | | * Say for example you need a FooImpl plumbed through the system. In production |
102 | | * code you must propagate a FooImpl through constructors to provide access |
103 | | * where needed. For tests, everywhere a common FooImpl is required, |
104 | | * instantiate: |
105 | | * |
106 | | * Global<FooImpl> foo; |
107 | | * |
108 | | * You can then access the singleton FooImpl via foo.get(). The underlying |
109 | | * FooImpl is ref-counted, and when the last TestGlobal is freed, the singleton |
110 | | * FooImpl will be destructed and the singleton pointer nulled. |
111 | | * |
112 | | * The templated type must have a zero-arg constructor. Templatizing this on an |
113 | | * int will compile, but will be hard to use as the memory will be uninitialized |
114 | | * and you will not know when instantiating it whether it needs to be |
115 | | * initialized. |
116 | | */ |
117 | | template <class Type> class Global { |
118 | | public: |
119 | 4.98M | Global() : singleton_(Globals::get<Type>()) {} Envoy::Test::Global<Envoy::Event::SingletonTimeSystemHelper>::Global() Line | Count | Source | 119 | 644k | Global() : singleton_(Globals::get<Type>()) {} |
Envoy::Test::Global<Envoy::Stats::TestUtil::TestSymbolTableHelper>::Global() Line | Count | Source | 119 | 4.34M | Global() : singleton_(Globals::get<Type>()) {} |
|
120 | 3.77M | Type& get() { return singleton_->ref<Type>(); } |
121 | 0 | const Type& get() const { return singleton_->ref<Type>(); } |
122 | 5.60M | Type* operator->() { return singleton_->ptr<Type>(); } |
123 | | Type& operator*() { return singleton_->ref<Type>(); } |
124 | | |
125 | | private: |
126 | | Globals::SingletonSharedPtr singleton_; |
127 | | }; |
128 | | |
129 | | } // namespace Test |
130 | | } // namespace Envoy |