/* * Copyright (C) 2020 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. */ #define LOG_TAG "VibratorHalControllerTest" #include #include #include #include #include #include #include #include #include #include "test_utils.h" using android::hardware::vibrator::Effect; using android::hardware::vibrator::EffectStrength; using std::chrono::milliseconds; using namespace android; using namespace std::chrono_literals; using namespace testing; static const auto ON_FN = [](vibrator::HalWrapper* hal) { return hal->on(10ms, []() {}); }; static const auto OFF_FN = [](vibrator::HalWrapper* hal) { return hal->off(); }; static const auto PING_FN = [](vibrator::HalWrapper* hal) { return hal->ping(); }; // ------------------------------------------------------------------------------------------------- class MockHalWrapper : public vibrator::HalWrapper { public: MockHalWrapper(std::shared_ptr scheduler) : HalWrapper(scheduler) {} virtual ~MockHalWrapper() = default; MOCK_METHOD(vibrator::HalResult, ping, (), (override)); MOCK_METHOD(void, tryReconnect, (), (override)); MOCK_METHOD(vibrator::HalResult, on, (milliseconds timeout, const std::function& completionCallback), (override)); MOCK_METHOD(vibrator::HalResult, off, (), (override)); MOCK_METHOD(vibrator::HalResult, setAmplitude, (float amplitude), (override)); MOCK_METHOD(vibrator::HalResult, setExternalControl, (bool enabled), (override)); MOCK_METHOD(vibrator::HalResult, alwaysOnEnable, (int32_t id, Effect effect, EffectStrength strength), (override)); MOCK_METHOD(vibrator::HalResult, alwaysOnDisable, (int32_t id), (override)); MOCK_METHOD(vibrator::HalResult, performEffect, (Effect effect, EffectStrength strength, const std::function& completionCallback), (override)); MOCK_METHOD(vibrator::HalResult, getCapabilitiesInternal, (), (override)); vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); } }; // ------------------------------------------------------------------------------------------------- class VibratorHalControllerTest : public Test { public: void SetUp() override { mConnectCounter = 0; auto callbackScheduler = std::make_shared(); mMockHal = std::make_shared>(callbackScheduler); mController = std::make_unique< vibrator::HalController>(std::move(callbackScheduler), [&](std::shared_ptr) { android_atomic_inc(&(this->mConnectCounter)); return this->mMockHal; }); ASSERT_NE(mController, nullptr); } protected: int32_t mConnectCounter; std::shared_ptr mMockHal; std::unique_ptr mController; }; // ------------------------------------------------------------------------------------------------- TEST_F(VibratorHalControllerTest, TestInit) { ASSERT_TRUE(mController->init()); ASSERT_EQ(1, mConnectCounter); // Noop when wrapper was already initialized. ASSERT_TRUE(mController->init()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestGetInfoRetriesOnAnyFailure) { EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), getCapabilitiesInternal()) .Times(Exactly(2)) .WillOnce(Return(vibrator::HalResult::failed("message"))) .WillRepeatedly(Return(vibrator::HalResult::ok( vibrator::Capabilities::ON_CALLBACK))); auto result = mController->getInfo(); ASSERT_FALSE(result.capabilities.isFailed()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) { EXPECT_CALL(*mMockHal.get(), on(_, _)) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::ok())); auto result = mController->doWithRetry(ON_FN, "on"); ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) { EXPECT_CALL(*mMockHal.get(), off()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::unsupported())); ASSERT_EQ(0, mConnectCounter); auto result = mController->doWithRetry(OFF_FN, "off"); ASSERT_TRUE(result.isUnsupported()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) { EXPECT_CALL(*mMockHal.get(), on(_, _)) .Times(Exactly(2)) .WillRepeatedly(Return(vibrator::HalResult::failed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); ASSERT_EQ(0, mConnectCounter); auto result = mController->doWithRetry(ON_FN, "on"); ASSERT_TRUE(result.isFailed()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::failed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::ok())); } ASSERT_EQ(0, mConnectCounter); auto result = mController->doWithRetry(PING_FN, "ping"); ASSERT_TRUE(result.isOk()); ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) { ASSERT_EQ(0, mConnectCounter); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(10)) .WillRepeatedly(Return(vibrator::HalResult::ok())); std::vector threads; for (int i = 0; i < 10; i++) { threads.push_back(std::thread([&]() { auto result = mController->doWithRetry(PING_FN, "ping"); ASSERT_TRUE(result.isOk()); })); } std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); // Connector was called only by the first thread to use the api. ASSERT_EQ(1, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) { mController = std::make_unique< vibrator::HalController>(nullptr, [&](std::shared_ptr) { android_atomic_inc(&(this->mConnectCounter)); return nullptr; }); ASSERT_EQ(0, mConnectCounter); ASSERT_TRUE(mController->doWithRetry(OFF_FN, "off").isUnsupported()); ASSERT_TRUE(mController->doWithRetry(PING_FN, "ping").isUnsupported()); // One connection attempt per api call. ASSERT_EQ(2, mConnectCounter); } TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { { InSequence seq; EXPECT_CALL(*mMockHal.get(), on(_, _)) .Times(Exactly(1)) .WillRepeatedly([&](milliseconds timeout, std::function callback) { mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout); return vibrator::HalResult::ok(); }); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::failed("message"))); EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(1)) .WillRepeatedly(Return(vibrator::HalResult::failed("message"))); } std::unique_ptr callbackCounter = std::make_unique(); auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); auto onFn = [&](vibrator::HalWrapper* hal) { return hal->on(10ms, callback); }; ASSERT_TRUE(mController->doWithRetry(onFn, "on").isOk()); ASSERT_TRUE(mController->doWithRetry(PING_FN, "ping").isFailed()); mMockHal.reset(); ASSERT_EQ(0, *callbackCounter.get()); // Callback triggered even after HalWrapper was reconnected. std::this_thread::sleep_for(15ms); ASSERT_EQ(1, *callbackCounter.get()); }