• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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