Files
hardware_interfaces/automotive/remoteaccess/test_grpc_server/impl/test/TestWakeupClientServiceImplUnitTest.cpp
Yu Shan 27068fb383 Fix flaky TestWakeupClientServiceImplUnitTest.
This CL fixed two possible issues: 1. mServerStarted is not reset
to false for each test case. 2. pollOnce should be used instead of
pollAll since pollAll might swallow wake.

Test: atest --host TestWakeupClientServerHostUnitTest
Bug: 333673876
Change-Id: Id705d6257c54a2e2711aff6018abf2ea65b0f691
2024-04-23 10:59:25 -07:00

360 lines
14 KiB
C++

/*
* Copyright (C) 2023 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 "TestWakeupClientServiceImpl.h"
#include <grpcpp/channel.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <gtest/gtest.h>
#include <chrono>
namespace android::hardware::automotive::remoteaccess::test {
using ::android::base::ScopedLockAssertion;
using ::grpc::Channel;
using ::grpc::ClientContext;
using ::grpc::Server;
using ::grpc::ServerBuilder;
using ::grpc::Status;
const std::string kTestClientId = "test client id";
const std::string kTestScheduleId = "test schedule id";
const std::vector<uint8_t> kTestData = {0xde, 0xad, 0xbe, 0xef};
constexpr int32_t kTestCount = 1234;
constexpr int64_t kTestStartTimeInEpochSeconds = 2345;
constexpr int64_t kTestPeriodicInSeconds = 123;
class MyTestWakeupClientServiceImpl final : public ServiceImpl {
public:
void wakeupApplicationProcessor([[maybe_unused]] int32_t bootupReason) override {
// Do nothing.
}
};
class TestWakeupClientServiceImplUnitTest : public ::testing::Test {
public:
virtual void SetUp() override {
int selectedPort = 0;
mServerStarted = false;
mService.reset();
mServer.reset();
mServerThread = std::thread([this, &selectedPort] {
mService = std::make_unique<MyTestWakeupClientServiceImpl>();
ServerBuilder builder;
builder.AddListeningPort("localhost:0", grpc::InsecureServerCredentials(),
&selectedPort);
WakeupClientServiceImpl wakeupClientService(mService.get());
builder.RegisterService(&wakeupClientService);
mServer = builder.BuildAndStart();
mServerStarted = true;
{
std::unique_lock<std::mutex> lock(mLock);
mServerStartCv.notify_one();
}
if (mServer != nullptr) {
mServer->Wait();
}
});
{
std::unique_lock<std::mutex> lock(mLock);
bool serverStarted = mServerStartCv.wait_for(lock, std::chrono::seconds(10), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mServerStarted.load();
});
ASSERT_TRUE(serverStarted) << "Failed to wait for mServerStarted to be set within 10s";
}
if (mServer == nullptr) {
GTEST_SKIP() << "Failed to start the test grpc server";
}
std::string address = "localhost:" + std::to_string(selectedPort);
std::cout << "Test grpc server started at: " << address << std::endl;
mChannel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials());
mStub = WakeupClient::NewStub(mChannel);
}
virtual void TearDown() override {
printf("Start server shutdown\n");
mService->stopServer();
mServer->Shutdown();
printf("Server shutdown complete\n");
mServerThread.join();
printf("Server thread exits\n");
mServer.reset();
mService.reset();
printf("Server and service classes reset\n");
}
WakeupClient::Stub* getStub() { return mStub.get(); }
size_t waitForRemoteTasks(size_t count) {
ClientContext context = {};
GetRemoteTasksResponse response;
auto reader = mStub->GetRemoteTasks(&context, GetRemoteTasksRequest{});
size_t got = 0;
while (reader->Read(&response)) {
got++;
mRemoteTaskResponses.push_back(response);
if (got == count) {
break;
}
}
// If there is more messages to be read in the reader, cancel them all so that we can
// finish.
context.TryCancel();
reader->Finish();
return got;
}
std::vector<GetRemoteTasksResponse> getRemoteTaskResponses() { return mRemoteTaskResponses; }
Status scheduleTask(int32_t count, int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
return scheduleTask(kTestScheduleId, count, startTimeInEpochSeconds, periodicInSeconds);
}
Status scheduleTask(const std::string& scheduleId, int32_t count,
int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
ClientContext context;
ScheduleTaskRequest request;
ScheduleTaskResponse response;
int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_scheduleid(scheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(count);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(startTimeInEpochSeconds);
request.mutable_scheduleinfo()->set_periodicinseconds(periodicInSeconds);
return getStub()->ScheduleTask(&context, request, &response);
}
int64_t getNow() {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
private:
std::condition_variable mServerStartCv;
std::mutex mLock;
std::thread mServerThread;
std::unique_ptr<MyTestWakeupClientServiceImpl> mService;
std::unique_ptr<Server> mServer;
std::atomic<bool> mServerStarted = false;
std::shared_ptr<Channel> mChannel;
std::unique_ptr<WakeupClient::Stub> mStub;
std::vector<GetRemoteTasksResponse> mRemoteTaskResponses;
};
TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask) {
ClientContext context = {};
ScheduleTaskRequest request = {};
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
// Schedule the task to be executed 1s later.
request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
request.mutable_scheduleinfo()->set_periodicinseconds(1);
Status status = getStub()->ScheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response.errorcode(), ErrorCode::OK);
size_t gotTaskCount = waitForRemoteTasks(/*count=*/2);
EXPECT_EQ(gotTaskCount, 2);
auto responses = getRemoteTaskResponses();
for (const auto& response : responses) {
EXPECT_EQ(response.clientid(), kTestClientId);
EXPECT_EQ(response.data(), std::string(kTestData.begin(), kTestData.end()));
}
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask_conflictScheduleId) {
Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
/*periodicInSeconds=*/1);
ASSERT_TRUE(status.ok());
// Schedule the same task again.
ClientContext context = {};
ScheduleTaskRequest request = {};
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
request.mutable_scheduleinfo()->set_tasktype(ScheduleTaskType::CUSTOM);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
request.mutable_scheduleinfo()->set_periodicinseconds(1);
status = getStub()->ScheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response.errorcode(), ErrorCode::INVALID_ARG);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleTask) {
Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
/*periodicInSeconds=*/1);
ASSERT_TRUE(status.ok());
ClientContext context;
UnscheduleTaskRequest request;
UnscheduleTaskResponse response;
request.set_clientid(kTestClientId);
request.set_scheduleid(kTestScheduleId);
status = getStub()->UnscheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
sleep(2);
// There should be no remote tasks received after 2s because the task was unscheduled.
EXPECT_EQ(getRemoteTaskResponses().size(), 0);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestIsTaskScheduled) {
int64_t startTimeInEpochSeconds = getNow() + 1;
int64_t periodicInSeconds = 1234;
Status status = scheduleTask(/*count=*/2, startTimeInEpochSeconds, periodicInSeconds);
ASSERT_TRUE(status.ok());
ClientContext context;
IsTaskScheduledRequest request;
IsTaskScheduledResponse response;
request.set_clientid(kTestClientId);
request.set_scheduleid(kTestScheduleId);
status = getStub()->IsTaskScheduled(&context, request, &response);
ASSERT_TRUE(status.ok());
EXPECT_TRUE(response.istaskscheduled());
ClientContext context2;
IsTaskScheduledRequest request2;
IsTaskScheduledResponse response2;
request.set_clientid(kTestClientId);
request.set_scheduleid("invalid id");
status = getStub()->IsTaskScheduled(&context2, request2, &response2);
ASSERT_TRUE(status.ok());
EXPECT_FALSE(response2.istaskscheduled());
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleAllTasks) {
std::string scheduleId1 = "scheduleId1";
std::string scheduleId2 = "scheduleId2";
int64_t time1 = getNow();
int64_t time2 = getNow() + 1;
int64_t periodicInSeconds1 = 1;
int64_t periodicInSeconds2 = 1;
int32_t count1 = 2;
int64_t count2 = 5;
Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
ASSERT_TRUE(status.ok());
status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
ASSERT_TRUE(status.ok());
ClientContext context;
UnscheduleAllTasksRequest request;
UnscheduleAllTasksResponse response;
request.set_clientid(kTestClientId);
status = getStub()->UnscheduleAllTasks(&context, request, &response);
ASSERT_TRUE(status.ok());
sleep(2);
// There should be no remote tasks received after 2s because the tasks were unscheduled.
EXPECT_EQ(getRemoteTaskResponses().size(), 0);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestGetAllPendingScheduledTasks) {
std::string scheduleId1 = "scheduleId1";
std::string scheduleId2 = "scheduleId2";
int64_t time1 = getNow();
int64_t time2 = getNow() + 1;
int64_t periodicInSeconds1 = 1;
int64_t periodicInSeconds2 = 1;
int32_t count1 = 2;
int64_t count2 = 5;
Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
ASSERT_TRUE(status.ok());
status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
ASSERT_TRUE(status.ok());
ClientContext context;
GetAllPendingScheduledTasksRequest request;
GetAllPendingScheduledTasksResponse response;
request.set_clientid("invalid client Id");
status = getStub()->GetAllPendingScheduledTasks(&context, request, &response);
ASSERT_TRUE(status.ok());
EXPECT_EQ(response.allscheduledtasks_size(), 0);
ClientContext context2;
GetAllPendingScheduledTasksRequest request2;
GetAllPendingScheduledTasksResponse response2;
request2.set_clientid(kTestClientId);
status = getStub()->GetAllPendingScheduledTasks(&context2, request2, &response2);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response2.allscheduledtasks_size(), 2);
for (int i = 0; i < 2; i++) {
EXPECT_EQ(response2.allscheduledtasks(i).clientid(), kTestClientId);
if (response2.allscheduledtasks(i).scheduleid() == scheduleId1) {
EXPECT_EQ(response2.allscheduledtasks(i).tasktype(), ScheduleTaskType::CUSTOM);
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count1);
EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time1);
EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds1);
} else {
EXPECT_EQ(response2.allscheduledtasks(i).scheduleid(), scheduleId2);
EXPECT_EQ(response2.allscheduledtasks(i).tasktype(), ScheduleTaskType::CUSTOM);
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count2);
EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time2);
EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds2);
}
}
}
} // namespace android::hardware::automotive::remoteaccess::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}