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 */
16
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>
24
25 namespace android {
26 namespace hardware {
27 namespace health {
28 namespace storage {
29 namespace V1_0 {
30
31 using ::std::literals::chrono_literals::operator""ms;
32
33 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
34
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 // Dev GC timeout tolerance. The HAL may not immediately return after the
39 // timeout, so include an acceptable tolerance.
40 const std::chrono::seconds kDevGcTolerance{3};
41 // Time accounted for RPC calls.
42 const std::chrono::milliseconds kRpcTime{1000};
43
44 template <typename R>
toString(std::chrono::duration<R,std::milli> time)45 std::string toString(std::chrono::duration<R, std::milli> time) {
46 return std::to_string(time.count()) + "ms";
47 }
48
49 /** An atomic boolean flag that indicates whether a task has finished. */
50 class Flag {
51 public:
onFinish()52 void onFinish() {
53 std::unique_lock<std::mutex> lock(mMutex);
54 onFinishLocked(&lock);
55 }
56 template <typename R, typename P>
wait(std::chrono::duration<R,P> duration)57 bool wait(std::chrono::duration<R, P> duration) {
58 std::unique_lock<std::mutex> lock(mMutex);
59 return waitLocked(&lock, duration);
60 }
61
62 protected:
63 /** Will unlock. */
onFinishLocked(std::unique_lock<std::mutex> * lock)64 void onFinishLocked(std::unique_lock<std::mutex>* lock) {
65 mFinished = true;
66 lock->unlock();
67 mCv.notify_all();
68 }
69 template <typename R, typename P>
waitLocked(std::unique_lock<std::mutex> * lock,std::chrono::duration<R,P> duration)70 bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
71 mCv.wait_for(*lock, duration, [this] { return mFinished; });
72 return mFinished;
73 }
74
75 bool mFinished{false};
76 std::mutex mMutex;
77 std::condition_variable mCv;
78 };
79
80 class GcCallback : public IGarbageCollectCallback, public Flag {
81 public:
onFinish(Result result)82 Return<void> onFinish(Result result) override {
83 std::unique_lock<std::mutex> lock(mMutex);
84 mResult = result;
85 Flag::onFinishLocked(&lock);
86 return Void();
87 }
88
89 /**
90 * Wait for a specific "timeout". If GC has finished, test that the result
91 * is equal to the "expected" value.
92 */
93 template <typename R, typename P>
waitForResult(std::chrono::duration<R,P> timeout,Result expected)94 void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
95 std::unique_lock<std::mutex> lock(mMutex);
96 ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
97 EXPECT_EQ(expected, mResult);
98 }
99
100 private:
101 Result mResult{Result::UNKNOWN_ERROR};
102 };
103
104 /** Test environment for Health Storage HIDL HAL. */
105 class HealthStorageHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
106 public:
107 /** get the test environment singleton */
Instance()108 static HealthStorageHidlEnvironment* Instance() {
109 static HealthStorageHidlEnvironment* instance = new HealthStorageHidlEnvironment();
110 return instance;
111 }
registerTestServices()112 virtual void registerTestServices() override { registerTestService<IStorage>(); }
113
114 private:
HealthStorageHidlEnvironment()115 HealthStorageHidlEnvironment() {}
116 };
117
118 class HealthStorageHidlTest : public ::testing::VtsHalHidlTargetTestBase {
119 public:
SetUp()120 virtual void SetUp() override {
121 fs = ::testing::VtsHalHidlTargetTestBase::getService<IStorage>(
122 HealthStorageHidlEnvironment::Instance()->getServiceName<IStorage>());
123
124 ASSERT_NE(fs, nullptr);
125 LOG(INFO) << "Service is remote " << fs->isRemote();
126 }
127
TearDown()128 virtual void TearDown() override {
129 EXPECT_TRUE(ping(kRpcTime))
130 << "Service is not responsive; expect subsequent tests to fail.";
131 }
132
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>
ping(std::chrono::duration<R,P> timeout)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 }
149
150 sp<IStorage> fs;
151 };
152
153 /**
154 * Ensure garbage collection works on null callback.
155 */
TEST_F(HealthStorageHidlTest,GcNullCallback)156 TEST_F(HealthStorageHidlTest, GcNullCallback) {
157 auto ret = fs->garbageCollect(kDevGcTimeoutSec, nullptr);
158
159 ASSERT_OK(ret);
160
161 // Hold test process because HAL can be single-threaded and doing GC.
162 ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
163 << "Service must be available after "
164 << toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
165 }
166
167 /**
168 * Ensure garbage collection works on non-null callback.
169 */
TEST_F(HealthStorageHidlTest,GcNonNullCallback)170 TEST_F(HealthStorageHidlTest, GcNonNullCallback) {
171 sp<GcCallback> cb = new GcCallback();
172 auto ret = fs->garbageCollect(kDevGcTimeoutSec, cb);
173 ASSERT_OK(ret);
174 cb->waitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
175 }
176
177 } // namespace V1_0
178 } // namespace storage
179 } // namespace health
180 } // namespace hardware
181 } // namespace android
182
main(int argc,char ** argv)183 int main(int argc, char** argv) {
184 using ::android::hardware::configureRpcThreadpool;
185 using ::android::hardware::health::storage::V1_0::HealthStorageHidlEnvironment;
186
187 configureRpcThreadpool(1, false /* callerWillJoin*/);
188 ::testing::AddGlobalTestEnvironment(HealthStorageHidlEnvironment::Instance());
189 ::testing::InitGoogleTest(&argc, argv);
190 HealthStorageHidlEnvironment::Instance()->init(&argc, argv);
191 int status = RUN_ALL_TESTS();
192 LOG(INFO) << "Test result = " << status;
193 return status;
194 }
195