userver: userver/testsuite/tasks.hpp Source File
Loading...
Searching...
No Matches
tasks.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/testsuite/tasks.hpp
4/// @brief @copybrief testsuite::TestsuiteTasks
5
6#include <atomic>
7#include <functional>
8#include <memory>
9#include <string>
10#include <unordered_map>
11
12#include <userver/components/component_fwd.hpp>
13#include <userver/concurrent/variable.hpp>
14#include <userver/engine/task/task_with_result.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace testsuite {
19
20class TaskNotFound final : public std::runtime_error {
21 using std::runtime_error::runtime_error;
22};
23
24class TaskAlreadyRunning final : public std::runtime_error {
25 using std::runtime_error::runtime_error;
26};
27
28// clang-format off
29
30/// @brief Testsuite tasks support
31///
32/// A testsuite task is a function that can be called from testsuite by its name.
33/// Only one task with the same name can be run simultaneously.
34///
35
36// clang-format on
37class TestsuiteTasks final {
38 public:
39 /// Task function type
40 using TaskCallback = std::function<void()>;
41
42 explicit TestsuiteTasks(bool is_enabled);
43 ~TestsuiteTasks();
44
45 /// @brief Are testsuite tasks enabled or not
46 ///
47 /// It is recommended to register tasks only for testsuite environment. Use
48 /// this method to test weather or not you are running under testsuite:
49 /// @code
50 /// if (testsuite_tasks.IsEnabled()) {
51 /// // testsuite code goes here...
52 /// } else {
53 /// // production code goes here...
54 /// }
55 /// @endcode
56 bool IsEnabled() const { return is_enabled_; }
57
58 /// @brief Register new task
59 /// @param name Task name
60 /// @param callback Testsuite task callback function
61 /// @throws std::runtime_error When task with `name` is already registered.
62 void RegisterTask(const std::string& name, TaskCallback callback);
63
64 /// @brief Unregister task
65 /// @param name Task name
66 /// @throws std::runtime_error When task `name` is not known.
67 void UnregisterTask(const std::string& name);
68
69 /// @brief Run task by name and waits for it to finish
70 /// @param name Task name
71 /// @throws TaskAlreadyRunning When task `name` is already running.
72 void RunTask(const std::string& name);
73
74 /// @brief Asynchronously starts the task
75 /// @param name Task name
76 /// @returns Task id
77 /// @throws TaskAlreadyRunning When task `name` is already running.
78 std::string SpawnTask(const std::string& name);
79
80 /// @brief Stop previously spawned task
81 /// @param task_id Task id returned by `SpawnTask()`
82 /// @throws TaskNotFound When task `task_id` is not known.
83 void StopSpawnedTask(const std::string& task_id);
84
85 /// @brief Checks that there are no running tasks left.
86 ///
87 /// Must be called on service shutdown. Aborts process when there are tasks
88 /// running.
89 void CheckNoRunningTasks() noexcept;
90
91 /// @brief Returns list of registered task names
93
94 private:
95 struct Entry {
96 std::atomic<bool> running_flag{false};
97 TaskCallback callback;
98 };
99
100 struct SpawnedTask {
101 std::atomic<bool> busy_flag{false};
102 std::string name;
103 engine::TaskWithResult<void> task;
104 };
105
106 std::shared_ptr<Entry> GetEntryFor(const std::string& name);
107 std::shared_ptr<SpawnedTask> GetSpawnedFor(const std::string& task_id);
108
109 const bool is_enabled_;
110
111 using TasksMap = std::unordered_map<std::string, std::shared_ptr<Entry>>;
112 concurrent::Variable<TasksMap> tasks_;
113
114 using SpawnedMap =
115 std::unordered_map<std::string, std::shared_ptr<SpawnedTask>>;
116 concurrent::Variable<SpawnedMap> spawned_;
117};
118
119/// @brief Get reference to TestsuiteTasks instance
120/// @param component_context
121TestsuiteTasks& GetTestsuiteTasks(
122 const components::ComponentContext& component_context);
123
124} // namespace testsuite
125
126USERVER_NAMESPACE_END