xref: /aosp_15_r20/external/perfetto/src/base/periodic_task_unittest.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/ext/base/periodic_task.h"
18 
19 #include "perfetto/ext/base/file_utils.h"
20 #include "src/base/test/test_task_runner.h"
21 #include "test/gtest_and_gmock.h"
22 
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
24     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
25 #include <unistd.h>
26 #endif
27 
28 #include <chrono>
29 #include <thread>
30 
31 namespace perfetto {
32 namespace base {
33 
34 namespace {
35 
TEST(PeriodicTaskTest,PostDelayedTaskMode)36 TEST(PeriodicTaskTest, PostDelayedTaskMode) {
37   TestTaskRunner task_runner;
38   PeriodicTask pt(&task_runner);
39   uint32_t num_callbacks = 0;
40   auto quit_closure = task_runner.CreateCheckpoint("all_timers_done");
41 
42   PeriodicTask::Args args;
43   args.task = [&] {
44     if (++num_callbacks == 3)
45       quit_closure();
46   };
47   args.period_ms = 1;
48   args.start_first_task_immediately = true;
49   pt.Start(std::move(args));
50   EXPECT_EQ(num_callbacks, 1u);
51   task_runner.RunUntilCheckpoint("all_timers_done");
52   EXPECT_EQ(num_callbacks, 3u);
53 }
54 
TEST(PeriodicTaskTest,OneShot)55 TEST(PeriodicTaskTest, OneShot) {
56   TestTaskRunner task_runner;
57   PeriodicTask pt(&task_runner);
58   uint32_t num_callbacks = 0;
59   auto quit_closure = task_runner.CreateCheckpoint("one_shot_done");
60 
61   PeriodicTask::Args args;
62   args.use_suspend_aware_timer = true;
63   args.one_shot = true;
64   args.period_ms = 1;
65   args.task = [&] {
66     ASSERT_EQ(++num_callbacks, 1u);
67     quit_closure();
68   };
69   pt.Start(std::move(args));
70   std::this_thread::sleep_for(std::chrono::milliseconds(3));
71   task_runner.RunUntilCheckpoint("one_shot_done");
72   EXPECT_EQ(num_callbacks, 1u);
73 }
74 
75 // Call Reset() from a callback, ensure no further calls are made.
TEST(PeriodicTaskTest,ResetFromCallback)76 TEST(PeriodicTaskTest, ResetFromCallback) {
77   TestTaskRunner task_runner;
78   PeriodicTask pt(&task_runner);
79   uint32_t num_callbacks = 0;
80   PeriodicTask::Args args;
81   auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
82   args.task = [&] {
83     ++num_callbacks;
84     pt.Reset();
85     task_runner.PostDelayedTask(quit_closure, 5);
86   };
87   args.period_ms = 1;
88   pt.Start(std::move(args));
89   EXPECT_EQ(num_callbacks, 0u);  // No immediate execution.
90 
91   task_runner.RunUntilCheckpoint("quit_closure");
92   EXPECT_EQ(num_callbacks, 1u);
93 }
94 
95 // Invalidates the timerfd, by replacing it with /dev/null, in the middle of
96 // the periodic ticks. That causes the next read() to fail and fall back on
97 // PostDelayedTask().
98 // On Mac and other systems where timerfd is not supported this will fall back
99 // on PostDelayedTask() immediately (and work).
TEST(PeriodicTaskTest,FallbackIfTimerfdFails)100 TEST(PeriodicTaskTest, FallbackIfTimerfdFails) {
101   TestTaskRunner task_runner;
102   PeriodicTask pt(&task_runner);
103   uint32_t num_callbacks = 0;
104   auto quit_closure = task_runner.CreateCheckpoint("all_timers_done");
105 
106   PeriodicTask::Args args;
107   args.task = [&] {
108     ++num_callbacks;
109 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
110     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
111     if (num_callbacks == 3 && pt.timer_fd_for_testing() > 0) {
112       ScopedFile dev_null = OpenFile("/dev/null", O_RDONLY);
113       dup2(*dev_null, pt.timer_fd_for_testing());
114     }
115 #else
116     EXPECT_FALSE(base::ScopedPlatformHandle::ValidityChecker::IsValid(
117         pt.timer_fd_for_testing()));
118 #endif
119     if (num_callbacks == 6)
120       quit_closure();
121   };
122   args.period_ms = 1;
123   args.use_suspend_aware_timer = true;
124   pt.Start(std::move(args));
125   task_runner.RunUntilCheckpoint("all_timers_done");
126   EXPECT_EQ(num_callbacks, 6u);
127 }
128 
TEST(PeriodicTaskTest,DestroyedFromCallback)129 TEST(PeriodicTaskTest, DestroyedFromCallback) {
130   TestTaskRunner task_runner;
131   std::unique_ptr<PeriodicTask> pt(new PeriodicTask(&task_runner));
132   uint32_t num_callbacks = 0;
133   PeriodicTask::Args args;
134   auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
135   args.task = [&] {
136     ++num_callbacks;
137     pt.reset();
138     task_runner.PostDelayedTask(quit_closure, 5);
139   };
140   args.period_ms = 1;
141   args.use_suspend_aware_timer = true;
142   pt->Start(std::move(args));
143 
144   task_runner.RunUntilCheckpoint("quit_closure");
145   EXPECT_EQ(num_callbacks, 1u);
146   EXPECT_FALSE(pt);
147 }
148 
TEST(PeriodicTaskTest,DestroyedFromAnotherTask)149 TEST(PeriodicTaskTest, DestroyedFromAnotherTask) {
150   TestTaskRunner task_runner;
151   std::unique_ptr<PeriodicTask> pt(new PeriodicTask(&task_runner));
152   uint32_t num_callbacks = 0;
153   PeriodicTask::Args args;
154   auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
155   args.task = [&] {
156     if (++num_callbacks == 2) {
157       task_runner.PostTask([&] {
158         pt.reset();
159         task_runner.PostDelayedTask(quit_closure, 5);
160       });
161     }
162   };
163   args.period_ms = 1;
164   args.use_suspend_aware_timer = true;
165   pt->Start(std::move(args));
166 
167   task_runner.RunUntilCheckpoint("quit_closure");
168   EXPECT_EQ(num_callbacks, 2u);
169   EXPECT_FALSE(pt);
170 }
171 
172 // Checks the generation logic.
TEST(PeriodicTaskTest,RestartWhileRunning)173 TEST(PeriodicTaskTest, RestartWhileRunning) {
174   TestTaskRunner task_runner;
175   PeriodicTask pt(&task_runner);
176   uint32_t num_callbacks_a = 0;
177   uint32_t num_callbacks_b = 0;
178   auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
179 
180   auto reuse = [&] {
181     PeriodicTask::Args args;
182     args.period_ms = 1;
183     args.task = [&] {
184       if (++num_callbacks_b == 3)
185         quit_closure();
186     };
187     pt.Start(std::move(args));
188   };
189 
190   PeriodicTask::Args args;
191   args.task = [&] {
192     if (++num_callbacks_a == 2)
193       task_runner.PostTask(reuse);
194   };
195   args.period_ms = 1;
196   args.use_suspend_aware_timer = true;
197   pt.Start(std::move(args));
198 
199   task_runner.RunUntilCheckpoint("quit_closure");
200   EXPECT_EQ(num_callbacks_a, 2u);
201   EXPECT_EQ(num_callbacks_b, 3u);
202 }
203 
TEST(PeriodicTaskTest,ImmediateExecution)204 TEST(PeriodicTaskTest, ImmediateExecution) {
205   TestTaskRunner task_runner;
206   PeriodicTask pt(&task_runner);
207   uint32_t num_callbacks = 0;
208 
209   PeriodicTask::Args args;
210   args.task = [&] { ++num_callbacks; };
211   args.period_ms = 1;
212   pt.Start(args);
213   EXPECT_EQ(num_callbacks, 0u);  // No immediate execution.
214 
215   args.start_first_task_immediately = true;
216   pt.Start(args);
217   EXPECT_EQ(num_callbacks, 1u);
218 }
219 
220 }  // namespace
221 }  // namespace base
222 }  // namespace perfetto
223