You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.2 KiB

/*
* Copyright 2020 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 "LooperStub.h"
#include <android-base/chrono_utils.h>
#include <utils/Looper.h>
#include <future> // NOLINT(build/c++11)
namespace android {
namespace automotive {
namespace watchdog {
namespace testing {
using ::android::Message;
using ::android::base::Error;
using ::android::base::Result;
// As the messages, which are to be polled immediately, are enqueued in the underlying looper
// handler before calling its poll method, the looper handler doesn't have to wait for any new
// messages.
const std::chrono::milliseconds kLooperPollTimeout = 0ms;
// Maximum timeout before giving up on the underlying looper handler. This doesn't block the test
// as long as the underlying looper handler processes the enqueued messages quickly and updates
// |mShouldPoll|.
const std::chrono::milliseconds kStubPollCheckTimeout = 3min;
int LooperStub::pollAll(int /*timeoutMillis*/) {
{
Mutex::Autolock lock(mMutex);
if (!mShouldPoll) {
return 0;
}
mElapsedTime = mTimer;
while (!mCache.empty() && mCache.front().empty()) {
mTimer += 1s; // Each empty entry in the cache is a second elapsed.
mCache.erase(mCache.begin());
}
mElapsedTime = mTimer - mElapsedTime;
if (mCache.empty()) {
mShouldPoll = false;
return 0;
}
// Send messages from the top of the cache and poll them immediately.
const auto messages = mCache.front();
for (const auto& m : messages) {
mLooper->sendMessage(mHandler, m);
}
mCache.front().clear();
}
int result = mLooper->pollAll(kLooperPollTimeout.count());
Mutex::Autolock lock(mMutex);
mShouldPoll = false;
return result;
}
void LooperStub::sendMessage(const android::sp<MessageHandler>& handler, const Message& message) {
sendMessageAtTime(now(), handler, message);
}
void LooperStub::sendMessageAtTime(nsecs_t uptime, const android::sp<MessageHandler>& handler,
const Message& message) {
Mutex::Autolock lock(mMutex);
mHandler = handler;
nsecs_t uptimeDelay = uptime - now();
size_t pos = static_cast<size_t>(ns2s(uptimeDelay));
while (mCache.size() < pos + 1) {
mCache.emplace_back(LooperStub::CacheEntry());
}
mCache[pos].emplace_back(message);
}
void LooperStub::removeMessages(const android::sp<MessageHandler>& handler) {
Mutex::Autolock lock(mMutex);
mCache.clear();
mLooper->removeMessages(handler);
}
void LooperStub::removeMessages(const android::sp<MessageHandler>& handler, int what) {
Mutex::Autolock lock(mMutex);
for (auto& entry : mCache) {
for (auto it = entry.begin(); it != entry.end();) {
if (it->what == what) {
entry.erase(it);
} else {
++it;
}
}
}
mLooper->removeMessages(handler, what);
}
Result<void> LooperStub::pollCache() {
{
Mutex::Autolock lock(mMutex);
mShouldPoll = true;
}
auto checkPollCompleted = std::async([&]() {
bool shouldPoll = true;
while (shouldPoll) {
Mutex::Autolock lock(mMutex);
shouldPoll = mShouldPoll;
}
});
if (checkPollCompleted.wait_for(kStubPollCheckTimeout) != std::future_status::ready) {
mShouldPoll = false;
return Error() << "Poll didn't complete before " << kStubPollCheckTimeout.count()
<< " milliseconds";
}
return {};
}
} // namespace testing
} // namespace watchdog
} // namespace automotive
} // namespace android