blob: 74921520956d4eb9bb3499634ffb5d93f4023020 [file] [log] [blame]
Brian Duddiecd3a43f2016-12-07 16:53:11 -08001/*
2 * Copyright (C) 2017 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#define LOG_TAG "contexthub_hidl_hal_test"
18
Steven Moreland3eb7df72017-04-06 12:15:23 -070019#include <VtsHalHidlTargetTestBase.h>
Zhuoyao Zhangebae6472018-02-08 20:48:38 -080020#include <VtsHalHidlTargetTestEnvBase.h>
Brian Duddiecd3a43f2016-12-07 16:53:11 -080021#include <android-base/logging.h>
22#include <android/hardware/contexthub/1.0/IContexthub.h>
23#include <android/hardware/contexthub/1.0/IContexthubCallback.h>
24#include <android/hardware/contexthub/1.0/types.h>
25#include <android/log.h>
Steven Moreland3eb7df72017-04-06 12:15:23 -070026#include <log/log.h>
Brian Duddiecd3a43f2016-12-07 16:53:11 -080027
28#include <cinttypes>
29#include <future>
30#include <utility>
31
32using ::android::hardware::Return;
33using ::android::hardware::Void;
34using ::android::hardware::hidl_string;
35using ::android::hardware::hidl_vec;
36using ::android::hardware::contexthub::V1_0::AsyncEventType;
37using ::android::hardware::contexthub::V1_0::ContextHub;
38using ::android::hardware::contexthub::V1_0::ContextHubMsg;
39using ::android::hardware::contexthub::V1_0::HubAppInfo;
40using ::android::hardware::contexthub::V1_0::IContexthub;
41using ::android::hardware::contexthub::V1_0::IContexthubCallback;
42using ::android::hardware::contexthub::V1_0::NanoAppBinary;
43using ::android::hardware::contexthub::V1_0::Result;
44using ::android::hardware::contexthub::V1_0::TransactionResult;
45using ::android::sp;
46
Brian Duddiecd3a43f2016-12-07 16:53:11 -080047#define ASSERT_OK(result) ASSERT_EQ(result, Result::OK)
48#define EXPECT_OK(result) EXPECT_EQ(result, Result::OK)
49
50namespace {
51
52// App ID with vendor "GoogT" (Google Testing), app identifier 0x555555. This
53// app ID is reserved and must never appear in the list of loaded apps.
54constexpr uint64_t kNonExistentAppId = 0x476f6f6754555555;
55
56// Helper that does explicit conversion of an enum class to its underlying/base
57// type. Useful for stream output of enum values.
58template<typename EnumType>
59constexpr typename std::underlying_type<EnumType>::type asBaseType(
60 EnumType value) {
61 return static_cast<typename std::underlying_type<EnumType>::type>(value);
62}
63
64// Synchronously queries IContexthub::getHubs() and returns the result
65hidl_vec<ContextHub> getHubsSync(sp<IContexthub> hubApi) {
66 hidl_vec<ContextHub> hubList;
67 std::promise<void> barrier;
68
69 hubApi->getHubs([&hubList, &barrier](const hidl_vec<ContextHub>& hubs) {
70 hubList = hubs;
71 barrier.set_value();
72 });
73 barrier.get_future().wait_for(std::chrono::seconds(1));
74
75 return hubList;
76}
77
78// Gets a list of valid hub IDs in the system
79std::vector<uint32_t> getHubIds() {
80 static std::vector<uint32_t> hubIds;
81
82 if (hubIds.size() == 0) {
Yuexi Maed2bb4e2017-03-10 00:44:45 -080083 sp<IContexthub> hubApi = ::testing::VtsHalHidlTargetTestBase::getService<IContexthub>();
Brian Duddiecd3a43f2016-12-07 16:53:11 -080084
85 if (hubApi != nullptr) {
86 for (ContextHub hub : getHubsSync(hubApi)) {
87 hubIds.push_back(hub.hubId);
88 }
89 }
90 }
91
92 ALOGD("Running tests against all %zu reported hubs", hubIds.size());
93 return hubIds;
94}
95
Zhuoyao Zhangebae6472018-02-08 20:48:38 -080096// Test environment for Contexthub HIDL HAL.
97class ContexthubHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
98 public:
99 // get the test environment singleton
100 static ContexthubHidlEnvironment* Instance() {
101 static ContexthubHidlEnvironment* instance = new ContexthubHidlEnvironment;
102 return instance;
103 }
104
105 virtual void registerTestServices() override { registerTestService<IContexthub>(); }
106 private:
107 ContexthubHidlEnvironment() {}
108};
109
Brian Duddiecd3a43f2016-12-07 16:53:11 -0800110// Base test fixture that initializes the HAL and makes the context hub API
111// handle available
Yuexi Maed2bb4e2017-03-10 00:44:45 -0800112class ContexthubHidlTestBase : public ::testing::VtsHalHidlTargetTestBase {
Brian Duddiecd3a43f2016-12-07 16:53:11 -0800113 public:
114 virtual void SetUp() override {
Zhuoyao Zhangebae6472018-02-08 20:48:38 -0800115 hubApi = ::testing::VtsHalHidlTargetTestBase::getService<IContexthub>(
116 ContexthubHidlEnvironment::Instance()->getServiceName<IContexthub>());
Brian Duddiecd3a43f2016-12-07 16:53:11 -0800117 ASSERT_NE(hubApi, nullptr);
118
119 // getHubs() must be called at least once for proper initialization of the
120 // HAL implementation
121 getHubsSync(hubApi);
122 }
123
124 virtual void TearDown() override {}
125
126 sp<IContexthub> hubApi;
127};
128
129// Test fixture parameterized by hub ID
130class ContexthubHidlTest : public ContexthubHidlTestBase,
131 public ::testing::WithParamInterface<uint32_t> {
132 public:
133 uint32_t getHubId() {
134 return GetParam();
135 }
136
137 Result registerCallback(sp<IContexthubCallback> cb) {
138 Result result = hubApi->registerCallback(getHubId(), cb);
139 ALOGD("Registered callback, result %" PRIu32, result);
140 return result;
141 }
142};
143
144// Base callback implementation that just logs all callbacks by default
145class ContexthubCallbackBase : public IContexthubCallback {
146 public:
147 virtual Return<void> handleClientMsg(const ContextHubMsg& /*msg*/) override {
148 ALOGD("Got client message callback");
149 return Void();
150 }
151
152 virtual Return<void> handleTxnResult(
153 uint32_t txnId, TransactionResult result) override {
154 ALOGD("Got transaction result callback for txnId %" PRIu32 " with result %"
155 PRId32, txnId, result);
156 return Void();
157 }
158
159 virtual Return<void> handleHubEvent(AsyncEventType evt) override {
160 ALOGD("Got hub event callback for event type %" PRIu32, evt);
161 return Void();
162 }
163
164 virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode)
165 override {
166 ALOGD("Got app abort notification for appId 0x%" PRIx64 " with abort code "
167 "0x%" PRIx32, appId, abortCode);
168 return Void();
169 }
170
171 virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& /*appInfo*/)
172 override {
173 ALOGD("Got app info callback");
174 return Void();
175 }
176};
177
178// Wait for a callback to occur (signaled by the given future) up to the
179// provided timeout. If the future is invalid or the callback does not come
180// within the given time, returns false.
181template<class ReturnType>
182bool waitForCallback(
183 std::future<ReturnType> future,
184 ReturnType *result,
185 std::chrono::milliseconds timeout = std::chrono::seconds(5)) {
186 auto expiration = std::chrono::system_clock::now() + timeout;
187
188 EXPECT_NE(result, nullptr);
189 EXPECT_TRUE(future.valid());
190 if (result != nullptr && future.valid()) {
191 std::future_status status = future.wait_until(expiration);
192 EXPECT_NE(status, std::future_status::timeout)
193 << "Timed out waiting for callback";
194
195 if (status == std::future_status::ready) {
196 *result = future.get();
197 return true;
198 }
199 }
200
201 return false;
202}
203
204// Ensures that the metadata reported in getHubs() is sane
205TEST_F(ContexthubHidlTestBase, TestGetHubs) {
206 hidl_vec<ContextHub> hubs = getHubsSync(hubApi);
207 ALOGD("System reports %zu hubs", hubs.size());
208
209 for (ContextHub hub : hubs) {
210 ALOGD("Checking hub ID %" PRIu32, hub.hubId);
211
212 EXPECT_FALSE(hub.name.empty());
213 EXPECT_FALSE(hub.vendor.empty());
214 EXPECT_FALSE(hub.toolchain.empty());
215 EXPECT_GT(hub.peakMips, 0);
216 EXPECT_GE(hub.stoppedPowerDrawMw, 0);
217 EXPECT_GE(hub.sleepPowerDrawMw, 0);
218 EXPECT_GT(hub.peakPowerDrawMw, 0);
219
220 // Minimum 128 byte MTU as required by CHRE API v1.0
221 EXPECT_GE(hub.maxSupportedMsgLen, UINT32_C(128));
222 }
223}
224
225TEST_P(ContexthubHidlTest, TestRegisterCallback) {
226 ALOGD("TestRegisterCallback called, hubId %" PRIu32, getHubId());
227 ASSERT_OK(registerCallback(new ContexthubCallbackBase()));
228}
229
230TEST_P(ContexthubHidlTest, TestRegisterNullCallback) {
231 ALOGD("TestRegisterNullCallback called, hubId %" PRIu32, getHubId());
232 ASSERT_OK(registerCallback(nullptr));
233}
234
235// Helper callback that puts the async appInfo callback data into a promise
236class QueryAppsCallback : public ContexthubCallbackBase {
237 public:
238 virtual Return<void> handleAppsInfo(const hidl_vec<HubAppInfo>& appInfo)
239 override {
240 ALOGD("Got app info callback with %zu apps", appInfo.size());
241 promise.set_value(appInfo);
242 return Void();
243 }
244
245 std::promise<hidl_vec<HubAppInfo>> promise;
246};
247
248// Calls queryApps() and checks the returned metadata
249TEST_P(ContexthubHidlTest, TestQueryApps) {
250 ALOGD("TestQueryApps called, hubId %u", getHubId());
251 sp<QueryAppsCallback> cb = new QueryAppsCallback();
252 ASSERT_OK(registerCallback(cb));
253
254 Result result = hubApi->queryApps(getHubId());
255 ASSERT_OK(result);
256
257 ALOGD("Waiting for app info callback");
258 hidl_vec<HubAppInfo> appList;
259 ASSERT_TRUE(waitForCallback(cb->promise.get_future(), &appList));
260 for (const HubAppInfo &appInfo : appList) {
261 EXPECT_NE(appInfo.appId, UINT64_C(0));
262 EXPECT_NE(appInfo.appId, kNonExistentAppId);
263 }
264}
265
266// Helper callback that puts the TransactionResult for the expectedTxnId into a
267// promise
268class TxnResultCallback : public ContexthubCallbackBase {
269 public:
270 virtual Return<void> handleTxnResult(
271 uint32_t txnId, TransactionResult result) override {
272 ALOGD("Got transaction result callback for txnId %" PRIu32 " (expecting %"
273 PRIu32 ") with result %" PRId32, txnId, expectedTxnId, result);
274 if (txnId == expectedTxnId) {
275 promise.set_value(result);
276 }
277 return Void();
278 }
279
280 uint32_t expectedTxnId = 0;
281 std::promise<TransactionResult> promise;
282};
283
284// Parameterized fixture that sets the callback to TxnResultCallback
285class ContexthubTxnTest : public ContexthubHidlTest {
286 public:
287 virtual void SetUp() override {
288 ContexthubHidlTest::SetUp();
289 ASSERT_OK(registerCallback(cb));
290 }
291
292 sp<TxnResultCallback> cb = new TxnResultCallback();
293};
294
295
296// Checks cases where the hub implementation is expected to return an error, but
297// that error can be returned either synchronously or in the asynchronous
298// transaction callback. Returns an AssertionResult that can be used in
299// ASSERT/EXPECT_TRUE. Allows checking the sync result against 1 additional
300// allowed error code apart from OK and TRANSACTION_FAILED, which are always
301// allowed.
302::testing::AssertionResult checkFailureSyncOrAsync(
303 Result result, Result allowedSyncResult,
304 std::future<TransactionResult>&& future) {
305 if (result == Result::OK) {
306 // No error reported synchronously - this is OK, but then we should get an
307 // async callback with a failure status
308 TransactionResult asyncResult;
309 if (!waitForCallback(std::forward<std::future<TransactionResult>>(future),
310 &asyncResult)) {
311 return ::testing::AssertionFailure()
312 << "Got successful sync result, then failed to receive async cb";
313 } else if (asyncResult == TransactionResult::SUCCESS) {
314 return ::testing::AssertionFailure()
315 << "Got successful sync result, then unexpected successful async "
316 "result";
317 }
318 } else if (result != allowedSyncResult &&
319 result != Result::TRANSACTION_FAILED) {
320 return ::testing::AssertionFailure() << "Got sync result "
321 << asBaseType(result) << ", expected TRANSACTION_FAILED or "
322 << asBaseType(allowedSyncResult);
323 }
324
325 return ::testing::AssertionSuccess();
326}
327
328TEST_P(ContexthubTxnTest, TestSendMessageToNonExistentNanoApp) {
329 ContextHubMsg msg;
330 msg.appName = kNonExistentAppId;
331 msg.msgType = 1;
332 msg.msg.resize(4);
333 std::fill(msg.msg.begin(), msg.msg.end(), 0);
334
335 ALOGD("Sending message to non-existent nanoapp");
336 Result result = hubApi->sendMessageToHub(getHubId(), msg);
337 if (result != Result::OK &&
338 result != Result::BAD_PARAMS &&
339 result != Result::TRANSACTION_FAILED) {
340 FAIL() << "Got result " << asBaseType(result) << ", expected OK, BAD_PARAMS"
341 << ", or TRANSACTION_FAILED";
342 }
343}
344
345TEST_P(ContexthubTxnTest, TestLoadEmptyNanoApp) {
346 cb->expectedTxnId = 0123;
347 NanoAppBinary emptyApp;
348
349 emptyApp.appId = kNonExistentAppId;
350 emptyApp.appVersion = 1;
351 emptyApp.flags = 0;
352 emptyApp.targetChreApiMajorVersion = 1;
353 emptyApp.targetChreApiMinorVersion = 0;
354
355 ALOGD("Loading empty nanoapp");
356 Result result = hubApi->loadNanoApp(getHubId(), emptyApp, cb->expectedTxnId);
357 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
358 cb->promise.get_future()));
359}
360
361TEST_P(ContexthubTxnTest, TestUnloadNonexistentNanoApp) {
362 cb->expectedTxnId = 1234;
363
364 ALOGD("Unloading nonexistent nanoapp");
365 Result result = hubApi->unloadNanoApp(getHubId(), kNonExistentAppId,
366 cb->expectedTxnId);
367 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
368 cb->promise.get_future()));
369}
370
371TEST_P(ContexthubTxnTest, TestEnableNonexistentNanoApp) {
372 cb->expectedTxnId = 2345;
373
374 ALOGD("Enabling nonexistent nanoapp");
375 Result result = hubApi->enableNanoApp(getHubId(), kNonExistentAppId,
376 cb->expectedTxnId);
377 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
378 cb->promise.get_future()));
379}
380
381TEST_P(ContexthubTxnTest, TestDisableNonexistentNanoApp) {
382 cb->expectedTxnId = 3456;
383
384 ALOGD("Disabling nonexistent nanoapp");
385 Result result = hubApi->disableNanoApp(getHubId(), kNonExistentAppId,
386 cb->expectedTxnId);
387 EXPECT_TRUE(checkFailureSyncOrAsync(result, Result::BAD_PARAMS,
388 cb->promise.get_future()));
389}
390
391// Parameterize all SingleContexthubTest tests against each valid hub ID
392INSTANTIATE_TEST_CASE_P(HubIdSpecificTests, ContexthubHidlTest,
393 ::testing::ValuesIn(getHubIds()));
394INSTANTIATE_TEST_CASE_P(HubIdSpecificTests, ContexthubTxnTest,
395 ::testing::ValuesIn(getHubIds()));
396
397} // anonymous namespace
398
399int main(int argc, char **argv) {
Zhuoyao Zhangebae6472018-02-08 20:48:38 -0800400 ::testing::AddGlobalTestEnvironment(ContexthubHidlEnvironment::Instance());
Brian Duddiecd3a43f2016-12-07 16:53:11 -0800401 ::testing::InitGoogleTest(&argc, argv);
Zhuoyao Zhangebae6472018-02-08 20:48:38 -0800402 ContexthubHidlEnvironment::Instance()->init(&argc, argv);
403 int status = RUN_ALL_TESTS();
404 ALOGI ("Test result = %d", status);
405 return status;
Brian Duddiecd3a43f2016-12-07 16:53:11 -0800406}
407