1 /*
2 * Copyright (C) 2016 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 <general_test/timer_set_test.h>
18
19 #include <cinttypes>
20 #include <cstddef>
21 #include <new>
22
23 #include <shared/send_message.h>
24 #include <shared/time_util.h>
25
26 #include <chre/util/nanoapp/log.h>
27
28 #include "chre_api/chre.h"
29
30 #define LOG_TAG "[TimerSetTest]"
31
32 using nanoapp_testing::kOneMillisecondInNanoseconds;
33 using nanoapp_testing::kOneSecondInNanoseconds;
34 using nanoapp_testing::sendFatalFailureToHost;
35 using nanoapp_testing::sendInternalFailureToHost;
36 using nanoapp_testing::sendSuccessToHost;
37
38 /*
39 * We have various "stages" for different timer setups we want to test.
40 * To speed up the test, we run all our stages simultaneously. That
41 * requires having 6 timers available, but with a 32 timer minimum
42 * and a presumption that tests aren't going to be run alongside a lot
43 * of other nanoapps, this should be fine.
44 *
45 * See initStages() for the description of each stage. Since these all
46 * happen in parallel, we leave it to each stage to mark itself has having
47 * succeeded, and have markSuccess() tell the Host when all stages have
48 * reported in.
49 *
50 * Note that we intentionally place the one-shot timers first, to give
51 * us more time to notice them (incorrectly) firing multiple times.
52 */
53
54 static uint64_t kShortDuration = 10 * kOneMillisecondInNanoseconds;
55 static uint64_t kLongDuration = kOneSecondInNanoseconds;
56
57 namespace general_test {
58
Stage(uint32_t stage,uint64_t duration,const void * cookie,bool oneShot)59 TimerSetTest::Stage::Stage(uint32_t stage, uint64_t duration,
60 const void *cookie, bool oneShot)
61 : mSetTime(0),
62 mDuration(duration),
63 mStage(stage),
64 mEventCount(0),
65 mCookie(cookie),
66 mOneShot(oneShot) {}
67
start()68 void TimerSetTest::Stage::start() {
69 mSetTime = chreGetTime();
70 mTimerHandle = chreTimerSet(mDuration, mCookie, mOneShot);
71 if (mTimerHandle == CHRE_TIMER_INVALID) {
72 sendFatalFailureToHost("Unable to set timer ", &mStage);
73 }
74 if (mSetTime == 0) {
75 sendFatalFailureToHost("chreGetTime() gave 0");
76 }
77 }
78
processEvent(uint64_t timestamp,TimerSetTest * test)79 void TimerSetTest::Stage::processEvent(uint64_t timestamp, TimerSetTest *test) {
80 if (mSetTime == 0) {
81 sendInternalFailureToHost("Didn't initialize mSetTime");
82 }
83 mEventCount++;
84
85 uint64_t expectedTime = mSetTime + (mEventCount * mDuration);
86 if (timestamp < expectedTime) {
87 sendFatalFailureToHost("Timer triggered too soon ", &mStage);
88 }
89 // TODO(b/32179037): Make this check much stricter.
90 if (timestamp > (expectedTime + kOneSecondInNanoseconds)) {
91 sendFatalFailureToHost("Timer triggered over a second late ", &mStage);
92 }
93
94 if (mOneShot) {
95 if (mEventCount > 1) {
96 sendFatalFailureToHost("One shot timer called multiple times ", &mStage);
97 } else {
98 test->markSuccess(mStage);
99 }
100 } else if (mEventCount == 3) {
101 // We mark recurring timers as successful on their third firing, if we
102 // can cancel it.
103 if (chreTimerCancel(mTimerHandle)) {
104 test->markSuccess(mStage);
105 } else {
106 sendFatalFailureToHost("Could not cancel recurring timer", &mStage);
107 }
108 }
109 }
110
initStages()111 void TimerSetTest::initStages() {
112 // To avoid fragmentation, we do one large allocation, and use
113 // placement new to initialize it.
114 mStages = static_cast<Stage *>(chreHeapAlloc(sizeof(*mStages) * kStageCount));
115 if (mStages == nullptr) {
116 sendFatalFailureToHost("Insufficient heap");
117 }
118
119 #define COOKIE(num) reinterpret_cast<const void *>(num)
120
121 // Stage 0: Test NULL cookie
122 new (&mStages[0]) Stage(0, kShortDuration, nullptr, true);
123 // Stage 1: Test (void*)-1 cookie
124 new (&mStages[1]) Stage(1, kShortDuration, COOKIE(-1), true);
125 // Stage 2: Test one shot with short duration
126 new (&mStages[2]) Stage(2, kShortDuration, COOKIE(2), true);
127 // Stage 3: Test one shot with long duration
128 new (&mStages[3]) Stage(3, kLongDuration, COOKIE(3), true);
129 // Stage 4: Test recurring with long duration
130 new (&mStages[4]) Stage(4, kLongDuration, COOKIE(4), false);
131 // Stage 5: Test recurring with short duration
132 new (&mStages[5]) Stage(5, kShortDuration, COOKIE(5), false);
133 static_assert((5 + 1) == kStageCount, "Missized array");
134
135 #undef COOKIE
136 }
137
TimerSetTest()138 TimerSetTest::TimerSetTest()
139 : Test(CHRE_API_VERSION_1_0), mInMethod(false), mFinishedBitmask(0) {}
140
setUp(uint32_t messageSize,const void *)141 void TimerSetTest::setUp(uint32_t messageSize, const void * /* message */) {
142 mInMethod = true;
143
144 if (messageSize != 0) {
145 sendFatalFailureToHost("TimerSet message expects 0 additional bytes, got ",
146 &messageSize);
147 }
148
149 initStages();
150 for (size_t i = 0; i < kStageCount; i++) {
151 mStages[i].start();
152 }
153
154 mInMethod = false;
155 }
156
~TimerSetTest()157 TimerSetTest::~TimerSetTest() {
158 chreHeapFree(mStages);
159 }
160
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)161 void TimerSetTest::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
162 const void *eventData) {
163 uint64_t timestamp = chreGetTime();
164 if (mInMethod) {
165 sendFatalFailureToHost(
166 "handleEvent invoked while another nanoapp method is running");
167 }
168 mInMethod = true;
169 if (senderInstanceId != CHRE_INSTANCE_ID) {
170 sendFatalFailureToHost("handleEvent got event from unexpected sender:",
171 &senderInstanceId);
172 }
173 if (eventType != CHRE_EVENT_TIMER) {
174 unexpectedEvent(eventType);
175 }
176 Stage *stage = getStageFromCookie(eventData);
177 if (stage == nullptr) {
178 sendFatalFailureToHost("handleEvent got invalid eventData");
179 }
180 stage->processEvent(timestamp, this);
181
182 mInMethod = false;
183 }
184
markSuccess(uint32_t stage)185 void TimerSetTest::markSuccess(uint32_t stage) {
186 LOGD("Stage %" PRIu32 " succeeded", stage);
187 uint32_t finishedBit = (1 << stage);
188 if ((kAllFinished & finishedBit) == 0) {
189 sendFatalFailureToHost("markSuccess bad stage", &stage);
190 }
191 mFinishedBitmask |= finishedBit;
192 if (mFinishedBitmask == kAllFinished) {
193 sendSuccessToHost();
194 }
195 }
196
getStageFromCookie(const void * cookie)197 TimerSetTest::Stage *TimerSetTest::getStageFromCookie(const void *cookie) {
198 Stage *ret = nullptr;
199 for (size_t i = 0; i < kStageCount; i++) {
200 if (mStages[i].getCookie() == cookie) {
201 if (ret != nullptr) {
202 sendInternalFailureToHost("Multiple stages with the same cookie");
203 }
204 ret = &mStages[i];
205 // It's cheap enough to go through the whole array, and will
206 // catch if we screw up this test setup by duplicating a cookie.
207 }
208 }
209 return ret;
210 }
211
212 } // namespace general_test
213