blob: e286bff8d6bf50673d4bb9949bbdbdf7626ac3b9 [file] [log] [blame]
Jordan Baylesb0c191e2019-03-26 15:49:57 -07001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <thread> // NOLINT
6
7#include "api/impl/task_runner_impl.h"
8#include "api/public/task_runner_factory.h"
9#include "platform/api/time.h"
10#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
11
12namespace {
13using openscreen::platform::Clock;
14using std::chrono::milliseconds;
15
16const auto kTaskRunnerSleepTime = milliseconds(1);
17
18void WaitUntilCondition(std::function<bool()> predicate) {
19 while (!predicate()) {
20 std::this_thread::sleep_for(kTaskRunnerSleepTime);
21 }
22}
23} // anonymous namespace
24
25namespace openscreen {
26namespace platform {
27
28TEST(TaskRunnerTest, TaskRunnerFromFactoryExecutesTask) {
29 auto runner = TaskRunnerFactory::Create();
30
31 std::thread t([&runner] {
32 static_cast<TaskRunnerImpl*>(runner.get())->RunUntilStopped();
33 });
34
35 std::string ran_tasks = "";
36 const auto task = [&ran_tasks] { ran_tasks += "1"; };
37 EXPECT_EQ(ran_tasks, "");
38
39 runner->PostTask(task);
40
41 WaitUntilCondition([&ran_tasks] { return ran_tasks == "1"; });
42 EXPECT_EQ(ran_tasks, "1");
43
44 static_cast<TaskRunnerImpl*>(runner.get())->RequestStopSoon();
45 t.join();
46}
47
48TEST(TaskRunnerTest, TaskRunnerRunsDelayedTasksInOrder) {
49 auto runner = TaskRunnerFactory::Create();
50
51 std::thread t([&runner] {
52 static_cast<TaskRunnerImpl*>(runner.get())->RunUntilStopped();
53 });
54
55 std::string ran_tasks = "";
56
57 const auto kDelayTimeTaskOne = milliseconds(5);
58 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
59 runner->PostTaskWithDelay(task_one, kDelayTimeTaskOne);
60
61 const auto kDelayTimeTaskTwo = milliseconds(10);
62 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
63 runner->PostTaskWithDelay(task_two, kDelayTimeTaskTwo);
64
65 const auto now = platform::Clock::now();
66 const auto kMinimumClockFirstTaskShouldRunAt = now + kDelayTimeTaskOne;
67 const auto kMinimumClockSecondTaskShouldRunAt = now + kDelayTimeTaskTwo;
68 while (platform::Clock::now() < kMinimumClockFirstTaskShouldRunAt) {
69 EXPECT_EQ(ran_tasks, "");
70 std::this_thread::sleep_for(kTaskRunnerSleepTime);
71 }
72
73 WaitUntilCondition([&ran_tasks] { return ran_tasks == "1"; });
74 EXPECT_EQ(ran_tasks, "1");
75
76 while (platform::Clock::now() < kMinimumClockSecondTaskShouldRunAt) {
77 EXPECT_EQ(ran_tasks, "1");
78 std::this_thread::sleep_for(kTaskRunnerSleepTime);
79 }
80
81 WaitUntilCondition([&ran_tasks] { return ran_tasks == "12"; });
82 EXPECT_EQ(ran_tasks, "12");
83
84 static_cast<TaskRunnerImpl*>(runner.get())->RequestStopSoon();
85 t.join();
86}
87
88TEST(TaskRunnerTest, SingleThreadedTaskRunnerRunsSequentially) {
89 TaskRunnerImpl runner(platform::Clock::now);
90
91 std::string ran_tasks;
92 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
93 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
94 const auto task_three = [&ran_tasks] { ran_tasks += "3"; };
95 const auto task_four = [&ran_tasks] { ran_tasks += "4"; };
96 const auto task_five = [&ran_tasks] { ran_tasks += "5"; };
97
98 runner.PostTask(task_one);
99 runner.PostTask(task_two);
100 runner.PostTask(task_three);
101 runner.PostTask(task_four);
102 runner.PostTask(task_five);
103 EXPECT_EQ(ran_tasks, "");
104
105 runner.RunUntilIdleForTesting();
106 EXPECT_EQ(ran_tasks, "12345");
107}
108
109TEST(TaskRunnerTest, TaskRunnerCanStopRunning) {
110 TaskRunnerImpl runner(platform::Clock::now);
111
112 std::string ran_tasks;
113 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
114 const auto task_two = [&ran_tasks] { ran_tasks += "2"; };
115
116 runner.PostTask(task_one);
117 EXPECT_EQ(ran_tasks, "");
118
119 std::thread start_thread([&runner] { runner.RunUntilStopped(); });
120
121 WaitUntilCondition([&ran_tasks] { return !ran_tasks.empty(); });
122 EXPECT_EQ(ran_tasks, "1");
123
124 // Since Stop is called first, and the single threaded task
125 // runner should honor the queue, we know the task runner is not running
126 // since task two doesn't get ran.
127 runner.RequestStopSoon();
128 runner.PostTask(task_two);
129 EXPECT_EQ(ran_tasks, "1");
130
131 start_thread.join();
132}
133
134TEST(TaskRunnerTest, StoppingDoesNotDeleteTasks) {
135 TaskRunnerImpl runner(platform::Clock::now);
136
137 std::string ran_tasks;
138 const auto task_one = [&ran_tasks] { ran_tasks += "1"; };
139
140 runner.PostTask(task_one);
141 runner.RequestStopSoon();
142
143 EXPECT_EQ(ran_tasks, "");
144 runner.RunUntilIdleForTesting();
145
146 EXPECT_EQ(ran_tasks, "1");
147}
148
149TEST(TaskRunnerTest, TaskRunnerIsStableWithLotsOfTasks) {
150 TaskRunnerImpl runner(platform::Clock::now);
151
152 const int kNumberOfTasks = 500;
153 std::string expected_ran_tasks;
154 expected_ran_tasks.append(kNumberOfTasks, '1');
155
156 std::string ran_tasks;
157 for (int i = 0; i < kNumberOfTasks; ++i) {
158 const auto task = [&ran_tasks] { ran_tasks += "1"; };
159 runner.PostTask(task);
160 }
161
162 runner.RunUntilIdleForTesting();
163 EXPECT_EQ(ran_tasks, expected_ran_tasks);
164}
Jordan Baylesa8e96772019-04-08 10:53:54 -0700165
166TEST(TaskRunnerTest, TaskRunnerDelayedTasksDontBlockImmediateTasks) {
167 TaskRunnerImpl runner(platform::Clock::now);
168
169 std::string ran_tasks;
170 const auto task = [&ran_tasks] { ran_tasks += "1"; };
171 const auto delayed_task = [&ran_tasks] { ran_tasks += "A"; };
172
173 runner.PostTaskWithDelay(delayed_task, milliseconds(10000));
174 runner.PostTask(task);
175
176 runner.RunUntilIdleForTesting();
177 // The immediate task should have run, even though the delayed task
178 // was added first.
179
180 EXPECT_EQ(ran_tasks, "1");
181}
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700182} // namespace platform
183} // namespace openscreen