/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <general_test/timer_cancel_test.h>
#include <cinttypes>
#include <cstddef>
#include <shared/send_message.h>
#include <chre.h>
using nanoapp_testing::sendFatalFailureToHost;
using nanoapp_testing::sendInternalFailureToHost;
using nanoapp_testing::sendSuccessToHost;
/*
* This test has four stages where we cancel one-shot and recurring timers,
* before and after they're triggered.
*
* See the TimerCancelTest constructor to see which stage tests which setup.
*
* When all of our stages have succeeded, then we send success to the host.
*/
// 10 milliseconds
static uint64_t kDuration = UINT64_C(10000000);
namespace general_test {
void TimerCancelTest::startStages() {
for (uint32_t i = 0; i < kStageCount; i++) {
Stage *stage = &mStages[i];
stage->timerId = chreTimerSet(kDuration, stage, stage->oneShot);
if (stage->timerId == CHRE_TIMER_INVALID) {
sendFatalFailureToHost("Unable to set timer:", &i);
}
if (stage->expectCallback) {
// Go on to the next stage. Note this stage will markSuccess()
// in handleStageEvent().
continue;
}
if (!chreTimerCancel(stage->timerId)) {
sendFatalFailureToHost("Unable to cancel timer:", &i);
}
if (chreTimerCancel(stage->timerId)) {
sendFatalFailureToHost("Claimed success in second cancel:", &i);
}
markSuccess(i);
}
}
TimerCancelTest::TimerCancelTest()
: Test(CHRE_API_VERSION_1_0),
mInMethod(false),
mStages{
// expectCallback:false ==> We're canceling before the timer fires.
// expectCallback:true ==> We'll cancel after the timer fires once.
//
// stage, oneShot, expectCallback
Stage(0, false, false),
Stage(1, true, false),
Stage(2, false, true ),
Stage(3, true, true )},
mFinishedBitmask(0) {
}
void TimerCancelTest::setUp(uint32_t messageSize, const void * /* message */) {
mInMethod = true;
if (messageSize != 0) {
sendFatalFailureToHost(
"TimerCancel message expects 0 additional bytes, got ",
&messageSize);
}
constexpr uint32_t kUnownedTimer = 0;
static_assert((kUnownedTimer != CHRE_TIMER_INVALID), "Bad test");
if (chreTimerCancel(kUnownedTimer)) {
sendFatalFailureToHost("Claimed success canceling timer we don't own");
}
startStages();
// Now we wait for some events from the timers to fire.
mInMethod = false;
}
void TimerCancelTest::handleStageEvent(Stage *stage) {
if (!stage->expectCallback) {
sendFatalFailureToHost("Timer didn't cancel:", &stage->stage);
}
// Now we're going to cancel the timer, so we don't expect an
// additional call.
stage->expectCallback = false;
bool cancelSucceeded = chreTimerCancel(stage->timerId);
if (stage->oneShot) {
if (cancelSucceeded) {
sendFatalFailureToHost("Claimed success canceling one-shot after "
"it fired:", &stage->stage);
}
} else {
if (!cancelSucceeded) {
sendFatalFailureToHost("Unable to cancel recurring timer:",
&stage->stage);
}
}
if (chreTimerCancel(stage->timerId)) {
sendFatalFailureToHost("Claimed success in second cancel:",
&stage->stage);
}
markSuccess(stage->stage);
}
void TimerCancelTest::handleEvent(uint32_t senderInstanceId,
uint16_t eventType, const void* eventData) {
if (mInMethod) {
sendFatalFailureToHost("handleEvent invoked while another nanoapp "
"method is running");
}
mInMethod = true;
if (senderInstanceId != CHRE_INSTANCE_ID) {
sendFatalFailureToHost("handleEvent got event from unexpected sender:",
&senderInstanceId);
}
if (eventType != CHRE_EVENT_TIMER) {
unexpectedEvent(eventType);
}
const Stage *stage = static_cast<const Stage*>(eventData);
if (stage->stage >= kStageCount) {
sendFatalFailureToHost("Invalid handleEvent data:", &stage->stage);
}
handleStageEvent(const_cast<Stage *>(stage));
mInMethod = false;
}
void TimerCancelTest::markSuccess(uint32_t stage) {
chreLog(CHRE_LOG_DEBUG, "Stage %" PRIu32 " succeeded", stage);
uint32_t finishedBit = (1 << stage);
if ((kAllFinished & finishedBit) == 0) {
sendFatalFailureToHost("markSuccess bad stage:", &stage);
}
if ((mFinishedBitmask & finishedBit) != 0) {
sendInternalFailureToHost("markSuccess multiple times:", &stage);
}
mFinishedBitmask |= finishedBit;
if (mFinishedBitmask == kAllFinished) {
sendSuccessToHost();
}
}
} // namespace general_test