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