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/// @brief Testsuite tasks support
29///
30/// A testsuite task is a function that can be called from testsuite by its name.
31/// Only one task with the same name can be run simultaneously.
32///
33class TestsuiteTasks final {
34public:
35 /// Task function type
36 using TaskCallback = std::function<void()>;
37
38 explicit TestsuiteTasks(bool is_enabled);
39 ~TestsuiteTasks();
40
41 /// @brief Are testsuite tasks enabled or not
42 ///
43 /// It is recommended to register tasks only for testsuite environment. Use
44 /// this method to test weather or not you are running under testsuite:
45 /// @code
46 /// if (testsuite_tasks.IsEnabled()) {
47 /// // testsuite code goes here...
48 /// } else {
49 /// // production code goes here...
50 /// }
51 /// @endcode
52 bool IsEnabled() const { return is_enabled_; }
53
54 /// @brief Register new task
55 /// @param name Task name
56 /// @param callback Testsuite task callback function
57 /// @throws std::runtime_error When task with `name` is already registered.
58 void RegisterTask(const std::string& name, TaskCallback callback);
59
60 /// @brief Unregister task
61 /// @param name Task name
62 /// @throws std::runtime_error When task `name` is not known.
63 void UnregisterTask(const std::string& name);
64
65 /// @brief Run task by name and waits for it to finish
66 /// @param name Task name
67 /// @throws TaskAlreadyRunning When task `name` is already running.
68 void RunTask(const std::string& name);
69
70 /// @brief Asynchronously starts the task
71 /// @param name Task name
72 /// @returns Task id
73 /// @throws TaskAlreadyRunning When task `name` is already running.
74 std::string SpawnTask(const std::string& name);
75
76 /// @brief Stop previously spawned task
77 /// @param task_id Task id returned by `SpawnTask()`
78 /// @throws TaskNotFound When task `task_id` is not known.
79 void StopSpawnedTask(const std::string& task_id);
80
81 /// @brief Checks that there are no running tasks left.
82 ///
83 /// Must be called on service shutdown. Aborts process when there are tasks
84 /// running.
85 void CheckNoRunningTasks() noexcept;
86
87 /// @brief Returns list of registered task names
88 std::vector<std::string> GetTaskNames() const;
89
90private:
91 struct Entry {
92 std::atomic<bool> running_flag{false};
93 TaskCallback callback;
94 };
95
96 struct SpawnedTask {
97 std::atomic<bool> busy_flag{false};
98 std::string name;
99 engine::TaskWithResult<void> task;
100 };
101
102 std::shared_ptr<Entry> GetEntryFor(const std::string& name);
103 std::shared_ptr<SpawnedTask> GetSpawnedFor(const std::string& task_id);
104
105 const bool is_enabled_;
106
107 using TasksMap = std::unordered_map<std::string, std::shared_ptr<Entry>>;
108 concurrent::Variable<TasksMap> tasks_;
109
110 using SpawnedMap = std::unordered_map<std::string, std::shared_ptr<SpawnedTask>>;
111 concurrent::Variable<SpawnedMap> spawned_;
112};
113
114/// @brief Get reference to TestsuiteTasks instance
115/// @param component_context
116TestsuiteTasks& GetTestsuiteTasks(const components::ComponentContext& component_context);
117
118} // namespace testsuite
119
120USERVER_NAMESPACE_END