1 /*
2 * Copyright (C) 2022 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 <pthread.h>
18 #include <sched.h>
19 #include <sys/resource.h>
20 #include <unistd.h>
21
22 #include <atomic>
23
24 #include <StreamWorker.h>
25
26 #include <gtest/gtest.h>
27 #define LOG_TAG "StreamWorker_Test"
28 #include <log/log.h>
29
30 using android::hardware::audio::common::StreamLogic;
31 using android::hardware::audio::common::StreamWorker;
32
33 class TestWorkerLogic : public StreamLogic {
34 public:
35 struct Stream {
setErrorStatusTestWorkerLogic::Stream36 void setErrorStatus() { status = Status::ABORT; }
setStopStatusTestWorkerLogic::Stream37 void setStopStatus() { status = Status::EXIT; }
38 std::atomic<Status> status = Status::CONTINUE;
39 };
40
41 // Use nullptr to test error reporting from the worker thread.
TestWorkerLogic(Stream * stream)42 explicit TestWorkerLogic(Stream* stream) : mStream(stream) {}
43
getWorkerCycles() const44 size_t getWorkerCycles() const { return mWorkerCycles; }
getPriority() const45 int getPriority() const { return mPriority; }
hasWorkerCycleCalled() const46 bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
hasNoWorkerCycleCalled(useconds_t usec)47 bool hasNoWorkerCycleCalled(useconds_t usec) {
48 const size_t cyclesBefore = mWorkerCycles;
49 usleep(usec);
50 return mWorkerCycles == cyclesBefore;
51 }
52
53 protected:
54 // StreamLogic implementation
init()55 std::string init() override { return mStream != nullptr ? "" : "Expected error"; }
cycle()56 Status cycle() override {
57 mPriority = getpriority(PRIO_PROCESS, 0);
58 do {
59 mWorkerCycles++;
60 } while (mWorkerCycles == 0);
61 return mStream->status;
62 }
63
64 private:
65 Stream* const mStream;
66 std::atomic<size_t> mWorkerCycles = 0;
67 std::atomic<int> mPriority = ANDROID_PRIORITY_DEFAULT;
68 };
69 using TestWorker = StreamWorker<TestWorkerLogic>;
70
71 // The parameter specifies whether an extra call to 'stop' is made at the end.
72 class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
73 public:
StreamWorkerInvalidTest()74 StreamWorkerInvalidTest() : StreamWorkerInvalidTest(nullptr) {}
TearDown()75 void TearDown() override {
76 if (GetParam()) {
77 worker.stop();
78 }
79 }
80
81 protected:
StreamWorkerInvalidTest(TestWorker::Stream * stream)82 StreamWorkerInvalidTest(TestWorker::Stream* stream)
83 : testing::TestWithParam<bool>(), worker(stream) {}
84 TestWorker worker;
85 };
86
TEST_P(StreamWorkerInvalidTest,Uninitialized)87 TEST_P(StreamWorkerInvalidTest, Uninitialized) {
88 EXPECT_FALSE(worker.hasWorkerCycleCalled());
89 EXPECT_FALSE(worker.hasError());
90 EXPECT_LE(worker.getTid(), 0);
91 }
92
TEST_P(StreamWorkerInvalidTest,UninitializedPauseIgnored)93 TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
94 EXPECT_FALSE(worker.hasError());
95 worker.pause();
96 EXPECT_FALSE(worker.hasError());
97 }
98
TEST_P(StreamWorkerInvalidTest,UninitializedResumeIgnored)99 TEST_P(StreamWorkerInvalidTest, UninitializedResumeIgnored) {
100 EXPECT_FALSE(worker.hasError());
101 worker.resume();
102 EXPECT_FALSE(worker.hasError());
103 }
104
TEST_P(StreamWorkerInvalidTest,Start)105 TEST_P(StreamWorkerInvalidTest, Start) {
106 EXPECT_FALSE(worker.start());
107 EXPECT_FALSE(worker.hasWorkerCycleCalled());
108 EXPECT_TRUE(worker.hasError());
109 #if defined(__ANDROID__)
110 EXPECT_GT(worker.getTid(), 0);
111 #endif
112 }
113
TEST_P(StreamWorkerInvalidTest,PauseIgnored)114 TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
115 EXPECT_FALSE(worker.start());
116 EXPECT_TRUE(worker.hasError());
117 worker.pause();
118 EXPECT_TRUE(worker.hasError());
119 }
120
TEST_P(StreamWorkerInvalidTest,ResumeIgnored)121 TEST_P(StreamWorkerInvalidTest, ResumeIgnored) {
122 EXPECT_FALSE(worker.start());
123 EXPECT_TRUE(worker.hasError());
124 worker.resume();
125 EXPECT_TRUE(worker.hasError());
126 }
127
128 INSTANTIATE_TEST_SUITE_P(StreamWorkerInvalid, StreamWorkerInvalidTest, testing::Bool());
129
130 class StreamWorkerTest : public StreamWorkerInvalidTest {
131 public:
StreamWorkerTest()132 StreamWorkerTest() : StreamWorkerInvalidTest(&stream) {}
133
134 protected:
135 TestWorker::Stream stream;
136 };
137
138 static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
139
TEST_P(StreamWorkerTest,Uninitialized)140 TEST_P(StreamWorkerTest, Uninitialized) {
141 EXPECT_FALSE(worker.hasWorkerCycleCalled());
142 EXPECT_FALSE(worker.hasError());
143 EXPECT_LE(worker.getTid(), 0);
144 }
145
TEST_P(StreamWorkerTest,Start)146 TEST_P(StreamWorkerTest, Start) {
147 ASSERT_TRUE(worker.start());
148 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
149 EXPECT_FALSE(worker.hasError());
150 #if defined(__ANDROID__)
151 EXPECT_GT(worker.getTid(), 0);
152 #endif
153 }
154
TEST_P(StreamWorkerTest,StartStop)155 TEST_P(StreamWorkerTest, StartStop) {
156 ASSERT_TRUE(worker.start());
157 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
158 EXPECT_FALSE(worker.hasError());
159 worker.stop();
160 EXPECT_FALSE(worker.hasError());
161 }
162
TEST_P(StreamWorkerTest,WorkerExit)163 TEST_P(StreamWorkerTest, WorkerExit) {
164 ASSERT_TRUE(worker.start());
165 stream.setStopStatus();
166 worker.waitForAtLeastOneCycle();
167 EXPECT_FALSE(worker.hasError());
168 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
169 }
170
TEST_P(StreamWorkerTest,WorkerJoin)171 TEST_P(StreamWorkerTest, WorkerJoin) {
172 ASSERT_TRUE(worker.start());
173 stream.setStopStatus();
174 worker.join();
175 EXPECT_FALSE(worker.hasError());
176 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
177 }
178
TEST_P(StreamWorkerTest,WorkerError)179 TEST_P(StreamWorkerTest, WorkerError) {
180 ASSERT_TRUE(worker.start());
181 stream.setErrorStatus();
182 worker.waitForAtLeastOneCycle();
183 EXPECT_TRUE(worker.hasError());
184 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
185 }
186
TEST_P(StreamWorkerTest,StopAfterError)187 TEST_P(StreamWorkerTest, StopAfterError) {
188 ASSERT_TRUE(worker.start());
189 stream.setErrorStatus();
190 worker.waitForAtLeastOneCycle();
191 EXPECT_TRUE(worker.hasError());
192 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
193 worker.stop();
194 EXPECT_TRUE(worker.hasError());
195 }
196
TEST_P(StreamWorkerTest,PauseResume)197 TEST_P(StreamWorkerTest, PauseResume) {
198 ASSERT_TRUE(worker.start());
199 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
200 EXPECT_FALSE(worker.hasError());
201 worker.pause();
202 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
203 EXPECT_FALSE(worker.hasError());
204 const size_t workerCyclesBefore = worker.getWorkerCycles();
205 worker.resume();
206 // 'resume' is synchronous and returns after the worker has looped at least once.
207 EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
208 EXPECT_FALSE(worker.hasError());
209 }
210
TEST_P(StreamWorkerTest,StopPaused)211 TEST_P(StreamWorkerTest, StopPaused) {
212 ASSERT_TRUE(worker.start());
213 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
214 EXPECT_FALSE(worker.hasError());
215 worker.pause();
216 worker.stop();
217 EXPECT_FALSE(worker.hasError());
218 }
219
TEST_P(StreamWorkerTest,PauseAfterErrorIgnored)220 TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
221 ASSERT_TRUE(worker.start());
222 stream.setErrorStatus();
223 worker.waitForAtLeastOneCycle();
224 EXPECT_TRUE(worker.hasError());
225 worker.pause();
226 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
227 EXPECT_TRUE(worker.hasError());
228 }
229
TEST_P(StreamWorkerTest,ResumeAfterErrorIgnored)230 TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
231 ASSERT_TRUE(worker.start());
232 stream.setErrorStatus();
233 worker.waitForAtLeastOneCycle();
234 EXPECT_TRUE(worker.hasError());
235 worker.resume();
236 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
237 EXPECT_TRUE(worker.hasError());
238 }
239
TEST_P(StreamWorkerTest,WorkerErrorOnResume)240 TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
241 ASSERT_TRUE(worker.start());
242 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
243 EXPECT_FALSE(worker.hasError());
244 worker.pause();
245 EXPECT_FALSE(worker.hasError());
246 stream.setErrorStatus();
247 EXPECT_FALSE(worker.hasError());
248 worker.resume();
249 worker.waitForAtLeastOneCycle();
250 EXPECT_TRUE(worker.hasError());
251 EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
252 }
253
TEST_P(StreamWorkerTest,WaitForAtLeastOneCycle)254 TEST_P(StreamWorkerTest, WaitForAtLeastOneCycle) {
255 ASSERT_TRUE(worker.start());
256 const size_t workerCyclesBefore = worker.getWorkerCycles();
257 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
258 EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
259 }
260
TEST_P(StreamWorkerTest,WaitForAtLeastOneCycleError)261 TEST_P(StreamWorkerTest, WaitForAtLeastOneCycleError) {
262 ASSERT_TRUE(worker.start());
263 stream.setErrorStatus();
264 EXPECT_FALSE(worker.waitForAtLeastOneCycle());
265 }
266
TEST_P(StreamWorkerTest,MutexDoesNotBlockWorker)267 TEST_P(StreamWorkerTest, MutexDoesNotBlockWorker) {
268 ASSERT_TRUE(worker.start());
269 const size_t workerCyclesBefore = worker.getWorkerCycles();
270 worker.testLockUnlockMutex(true);
271 while (worker.getWorkerCycles() == workerCyclesBefore) {
272 usleep(kWorkerIdleCheckTime);
273 }
274 worker.testLockUnlockMutex(false);
275 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
276 EXPECT_FALSE(worker.hasError());
277 }
278
TEST_P(StreamWorkerTest,ThreadName)279 TEST_P(StreamWorkerTest, ThreadName) {
280 const std::string workerName = "TestWorker";
281 ASSERT_TRUE(worker.start(workerName)) << worker.getError();
282 char nameBuf[128];
283 ASSERT_EQ(0, pthread_getname_np(worker.testGetThreadNativeHandle(), nameBuf, sizeof(nameBuf)));
284 EXPECT_EQ(workerName, nameBuf);
285 }
286
TEST_P(StreamWorkerTest,ThreadPriority)287 TEST_P(StreamWorkerTest, ThreadPriority) {
288 const int priority = ANDROID_PRIORITY_LOWEST;
289 ASSERT_TRUE(worker.start("", priority)) << worker.getError();
290 EXPECT_TRUE(worker.waitForAtLeastOneCycle());
291 EXPECT_EQ(priority, worker.getPriority());
292 }
293
TEST_P(StreamWorkerTest,DeferredStartCheckNoError)294 TEST_P(StreamWorkerTest, DeferredStartCheckNoError) {
295 stream.setStopStatus();
296 EXPECT_TRUE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
297 EXPECT_FALSE(worker.hasError());
298 }
299
TEST_P(StreamWorkerTest,DeferredStartCheckWithError)300 TEST_P(StreamWorkerTest, DeferredStartCheckWithError) {
301 stream.setErrorStatus();
302 EXPECT_FALSE(worker.start(android::hardware::audio::common::internal::kTestSingleThread));
303 EXPECT_TRUE(worker.hasError());
304 }
305
306 INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
307