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