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