[android/platform-hardware-interfaces.git] / health / storage / 1.0 / vts / functional / VtsHalHealthStorageV1_0TargetTest.cpp
1 /*
2 * Copyright (C) 2018 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 */
17 #include <VtsHalHidlTargetTestBase.h>
18 #include <VtsHalHidlTargetTestEnvBase.h>
19 #include <android-base/logging.h>
20 #include <android/hardware/health/storage/1.0/IStorage.h>
21 #include <hidl/HidlTransportSupport.h>
22 #include <unistd.h>
23 #include <thread>
25 namespace android {
26 namespace hardware {
27 namespace health {
28 namespace storage {
29 namespace V1_0 {
31 using ::std::literals::chrono_literals::operator""ms;
33 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
35 // Dev GC timeout. This is the timeout used by vold.
36 const uint64_t kDevGcTimeoutSec = 120;
37 const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
38 // Time accounted for RPC calls.
39 const std::chrono::milliseconds kRpcTime{100};
41 template <typename R>
42 std::string toString(std::chrono::duration<R, std::milli> time) {
43 return std::to_string(time.count()) + "ms";
44 }
46 /** An atomic boolean flag that indicates whether a task has finished. */
47 class Flag {
48 public:
49 void onFinish() {
50 std::unique_lock<std::mutex> lock(mMutex);
51 onFinishLocked(&lock);
52 }
53 template <typename R, typename P>
54 bool wait(std::chrono::duration<R, P> duration) {
55 std::unique_lock<std::mutex> lock(mMutex);
56 return waitLocked(&lock, duration);
57 }
59 protected:
60 /** Will unlock. */
61 void onFinishLocked(std::unique_lock<std::mutex>* lock) {
62 mFinished = true;
63 lock->unlock();
64 mCv.notify_all();
65 }
66 template <typename R, typename P>
67 bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
68 mCv.wait_for(*lock, duration, [this] { return mFinished; });
69 return mFinished;
70 }
72 bool mFinished{false};
73 std::mutex mMutex;
74 std::condition_variable mCv;
75 };
77 class GcCallback : public IGarbageCollectCallback, public Flag {
78 public:
79 Return<void> onFinish(Result result) override {
80 std::unique_lock<std::mutex> lock(mMutex);
81 mResult = result;
82 Flag::onFinishLocked(&lock);
83 return Void();
84 }
86 /**
87 * Wait for a specific "timeout". If GC has finished, test that the result
88 * is equal to the "expected" value.
89 */
90 template <typename R, typename P>
91 void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
92 std::unique_lock<std::mutex> lock(mMutex);
93 if (waitLocked(&lock, timeout)) {
94 EXPECT_EQ(expected, mResult);
95 } else {
96 LOG(INFO) << "timeout after " << toString(timeout);
97 }
98 }
100 private:
101 Result mResult{Result::UNKNOWN_ERROR};
102 };
104 /** Test environment for Health Storage HIDL HAL. */
105 class HealthStorageHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
106 public:
107 /** get the test environment singleton */
108 static HealthStorageHidlEnvironment* Instance() {
109 static HealthStorageHidlEnvironment* instance = new HealthStorageHidlEnvironment();
110 return instance;
111 }
112 virtual void registerTestServices() override { registerTestService<IStorage>(); }
114 private:
115 HealthStorageHidlEnvironment() {}
116 };
118 class HealthStorageHidlTest : public ::testing::VtsHalHidlTargetTestBase {
119 public:
120 virtual void SetUp() override {
121 fs = ::testing::VtsHalHidlTargetTestBase::getService<IStorage>(
122 HealthStorageHidlEnvironment::Instance()->getServiceName<IStorage>());
124 ASSERT_NE(fs, nullptr);
125 LOG(INFO) << "Service is remote " << fs->isRemote();
126 }
128 virtual void TearDown() override {
129 EXPECT_TRUE(ping(kRpcTime))
130 << "Service is not responsive; expect subsequent tests to fail.";
131 }
133 /**
134 * Ping the service and expect it to return after "timeout". Return true
135 * iff the service is responsive within "timeout".
136 */
137 template <typename R, typename P>
138 bool ping(std::chrono::duration<R, P> timeout) {
139 // Ensure the service is responsive after the test.
140 sp<IStorage> service = fs;
141 auto pingFlag = std::make_shared<Flag>();
142 std::thread([service, pingFlag] {
143 service->ping();
144 pingFlag->onFinish();
145 })
146 .detach();
147 return pingFlag->wait(timeout);
148 }
150 sp<IStorage> fs;
151 };
153 /**
154 * Ensure garbage collection works on null callback.
155 */
156 TEST_F(HealthStorageHidlTest, GcNullCallback) {
157 auto ret = fs->garbageCollect(kDevGcTimeoutSec, nullptr);
159 ASSERT_OK(ret);
161 // Hold test process because HAL can be single-threaded and doing GC.
162 ASSERT_TRUE(ping(kDevGcTimeout + kRpcTime))
163 << "Service must be available after " << toString(kDevGcTimeout + kRpcTime);
164 }
166 /**
167 * Ensure garbage collection works on non-null callback.
168 */
169 TEST_F(HealthStorageHidlTest, GcNonNullCallback) {
170 sp<GcCallback> cb = new GcCallback();
171 auto ret = fs->garbageCollect(kDevGcTimeoutSec, cb);
172 ASSERT_OK(ret);
173 cb->waitForResult(kDevGcTimeout + kRpcTime, Result::SUCCESS);
174 }
176 } // namespace V1_0
177 } // namespace storage
178 } // namespace health
179 } // namespace hardware
180 } // namespace android
182 int main(int argc, char** argv) {
183 using ::android::hardware::configureRpcThreadpool;
184 using ::android::hardware::health::storage::V1_0::HealthStorageHidlEnvironment;
186 configureRpcThreadpool(1, false /* callerWillJoin*/);
187 ::testing::AddGlobalTestEnvironment(HealthStorageHidlEnvironment::Instance());
188 ::testing::InitGoogleTest(&argc, argv);
189 HealthStorageHidlEnvironment::Instance()->init(&argc, argv);
190 int status = RUN_ALL_TESTS();
191 LOG(INFO) << "Test result = " << status;
192 return status;
193 }