/* * Copyright (C) 2019 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. */ #include #include #include #include #include #include "Vibrator.h" #include "mocks.h" #include "types.h" #include "utils.h" namespace aidl { namespace android { namespace hardware { namespace vibrator { using ::testing::_; using ::testing::AnyNumber; using ::testing::Assign; using ::testing::AtLeast; using ::testing::AtMost; using ::testing::Combine; using ::testing::DoAll; using ::testing::DoDefault; using ::testing::Exactly; using ::testing::Expectation; using ::testing::ExpectationSet; using ::testing::Ge; using ::testing::Mock; using ::testing::MockFunction; using ::testing::Range; using ::testing::Return; using ::testing::Sequence; using ::testing::SetArgPointee; using ::testing::Test; using ::testing::TestParamInfo; using ::testing::ValuesIn; using ::testing::WithParamInterface; // Forward Declarations static EffectQueue Queue(const QueueEffect &effect); static EffectQueue Queue(const QueueDelay &delay); template static EffectQueue Queue(const T &first, const U &second, Args... rest); static EffectLevel Level(float intensity); static EffectScale Scale(float intensity); // Constants With Arbitrary Values static constexpr uint32_t CAL_VERSION = 1; static constexpr std::array V_LEVELS{40, 50, 60, 70, 80, 90}; static constexpr std::array EFFECT_DURATIONS{0, 0, 11, 0, 300, 132, 150, 500, 101, 5}; // Constants With Prescribed Values static const std::map EFFECT_INDEX{ {Effect::CLICK, 2}, {Effect::TICK, 2}, {Effect::HEAVY_CLICK, 2}, {Effect::TEXTURE_TICK, 9}, }; static constexpr EffectIndex QUEUE_INDEX{65534}; static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LEVELS[5])}; static const EffectIndex ON_EFFECT_INDEX{0}; static constexpr uint32_t WAVEFORM_DOUBLE_CLICK_SILENCE_MS = 100; static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby static constexpr int8_t MAX_PAUSE_TIMING_ERROR_MS = 1; // ALERT Irq Handling static constexpr auto POLLING_TIMEOUT = 20; static const std::map EFFECT_SCALE{ {{Effect::CLICK, EffectStrength::LIGHT}, Scale(0.7f * 0.5f)}, {{Effect::CLICK, EffectStrength::MEDIUM}, Scale(0.7f * 0.7f)}, {{Effect::CLICK, EffectStrength::STRONG}, Scale(0.7f * 1.0f)}, {{Effect::TICK, EffectStrength::LIGHT}, Scale(0.5f * 0.5f)}, {{Effect::TICK, EffectStrength::MEDIUM}, Scale(0.5f * 0.7f)}, {{Effect::TICK, EffectStrength::STRONG}, Scale(0.5f * 1.0f)}, {{Effect::HEAVY_CLICK, EffectStrength::LIGHT}, Scale(1.0f * 0.5f)}, {{Effect::HEAVY_CLICK, EffectStrength::MEDIUM}, Scale(1.0f * 0.7f)}, {{Effect::HEAVY_CLICK, EffectStrength::STRONG}, Scale(1.0f * 1.0f)}, {{Effect::TEXTURE_TICK, EffectStrength::LIGHT}, Scale(0.5f * 0.5f)}, {{Effect::TEXTURE_TICK, EffectStrength::MEDIUM}, Scale(0.5f * 0.7f)}, {{Effect::TEXTURE_TICK, EffectStrength::STRONG}, Scale(0.5f * 1.0f)}, }; static const std::map EFFECT_QUEUE{ {{Effect::DOUBLE_CLICK, EffectStrength::LIGHT}, Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(0.7f * 0.5f)}, WAVEFORM_DOUBLE_CLICK_SILENCE_MS, QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(1.0f * 0.5f)})}, {{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM}, Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(0.7f * 0.7f)}, WAVEFORM_DOUBLE_CLICK_SILENCE_MS, QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(1.0f * 0.7f)})}, {{Effect::DOUBLE_CLICK, EffectStrength::STRONG}, Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(0.7f * 1.0f)}, WAVEFORM_DOUBLE_CLICK_SILENCE_MS, QueueEffect{EFFECT_INDEX.at(Effect::CLICK), Level(1.0f * 1.0f)})}, }; EffectQueue Queue(const QueueEffect &effect) { auto index = std::get<0>(effect); auto level = std::get<1>(effect); auto string = std::to_string(index) + "." + std::to_string(level); auto duration = EFFECT_DURATIONS[index]; return {string, duration}; } EffectQueue Queue(const QueueDelay &delay) { auto string = std::to_string(delay); return {string, delay}; } template EffectQueue Queue(const T &first, const U &second, Args... rest) { auto head = Queue(first); auto tail = Queue(second, rest...); auto string = std::get<0>(head) + "," + std::get<0>(tail); auto duration = std::get<1>(head) + std::get<1>(tail); return {string, duration}; } static EffectLevel Level(float intensity) { auto vMin = std::max(V_LEVELS[0] - (V_LEVELS[4] - V_LEVELS[0]) / 4.0f, 4.0f); auto vMax = V_LEVELS[4]; return std::lround(intensity * (vMax - vMin)) + vMin; } static EffectScale Scale(float intensity) { return levelToScale(Level(intensity)); } class VibratorTest : public Test { public: void SetUp() override { std::unique_ptr mockapi; std::unique_ptr mockcal; createMock(&mockapi, &mockcal); createVibrator(std::move(mockapi), std::move(mockcal)); } void TearDown() override { deleteVibrator(); } protected: void createMock(std::unique_ptr *mockapi, std::unique_ptr *mockcal) { *mockapi = std::make_unique(); *mockcal = std::make_unique(); mMockApi = mockapi->get(); mMockCal = mockcal->get(); ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr)); ON_CALL(*mMockApi, getEffectCount(_)) .WillByDefault(DoAll(SetArgPointee<0>(EFFECT_DURATIONS.size()), Return(true))); ON_CALL(*mMockApi, setEffectIndex(_)) .WillByDefault(Invoke(this, &VibratorTest::setEffectIndex)); ON_CALL(*mMockApi, getEffectDuration(_)) .WillByDefault(Invoke(this, &VibratorTest::getEffectDuration)); ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr)); ON_CALL(*mMockCal, getVersion(_)) .WillByDefault(DoAll(SetArgPointee<0>(CAL_VERSION), Return(true))); ON_CALL(*mMockCal, getVolLevels(_)) .WillByDefault(DoAll(SetArgPointee<0>(V_LEVELS), Return(true))); relaxMock(false); } void createVibrator(std::unique_ptr mockapi, std::unique_ptr mockcal, bool relaxed = true) { if (relaxed) { relaxMock(true); } mVibrator = ndk::SharedRefBase::make(std::move(mockapi), std::move(mockcal)); if (relaxed) { relaxMock(false); } } void deleteVibrator(bool relaxed = true) { if (relaxed) { relaxMock(true); } mVibrator.reset(); } bool setEffectIndex(EffectIndex index) { mEffectIndex = index; return true; } bool getEffectDuration(EffectDuration *duration) { if (mEffectIndex < EFFECT_DURATIONS.size()) { *duration = msToCycles(EFFECT_DURATIONS[mEffectIndex]); return true; } else { return false; } } private: void relaxMock(bool relax) { auto times = relax ? AnyNumber() : Exactly(0); Mock::VerifyAndClearExpectations(mMockApi); Mock::VerifyAndClearExpectations(mMockCal); EXPECT_CALL(*mMockApi, destructor()).Times(times); EXPECT_CALL(*mMockApi, setF0(_)).Times(times); EXPECT_CALL(*mMockApi, setRedc(_)).Times(times); EXPECT_CALL(*mMockApi, setQ(_)).Times(times); EXPECT_CALL(*mMockApi, setActivate(_)).Times(times); EXPECT_CALL(*mMockApi, setDuration(_)).Times(times); EXPECT_CALL(*mMockApi, getEffectCount(_)).Times(times); EXPECT_CALL(*mMockApi, getEffectDuration(_)).Times(times); EXPECT_CALL(*mMockApi, setEffectIndex(_)).Times(times); EXPECT_CALL(*mMockApi, setEffectQueue(_)).Times(times); EXPECT_CALL(*mMockApi, hasEffectScale()).Times(times); EXPECT_CALL(*mMockApi, setEffectScale(_)).Times(times); EXPECT_CALL(*mMockApi, setGlobalScale(_)).Times(times); EXPECT_CALL(*mMockApi, setState(_)).Times(times); EXPECT_CALL(*mMockApi, hasAspEnable()).Times(times); EXPECT_CALL(*mMockApi, getAspEnable(_)).Times(times); EXPECT_CALL(*mMockApi, setAspEnable(_)).Times(times); EXPECT_CALL(*mMockApi, setGpioFallIndex(_)).Times(times); EXPECT_CALL(*mMockApi, setGpioFallScale(_)).Times(times); EXPECT_CALL(*mMockApi, setGpioRiseIndex(_)).Times(times); EXPECT_CALL(*mMockApi, setGpioRiseScale(_)).Times(times); EXPECT_CALL(*mMockApi, debug(_)).Times(times); EXPECT_CALL(*mMockCal, destructor()).Times(times); EXPECT_CALL(*mMockCal, getF0(_)).Times(times); EXPECT_CALL(*mMockCal, getRedc(_)).Times(times); EXPECT_CALL(*mMockCal, getQ(_)).Times(times); EXPECT_CALL(*mMockCal, getVolLevels(_)).Times(times); EXPECT_CALL(*mMockCal, debug(_)).Times(times); } protected: MockApi *mMockApi; MockCal *mMockCal; std::shared_ptr mVibrator; uint32_t mEffectIndex; }; TEST_F(VibratorTest, Constructor) { std::unique_ptr mockapi; std::unique_ptr mockcal; uint32_t f0Val = std::rand(); uint32_t redcVal = std::rand(); uint32_t qVal = std::rand(); uint32_t calVer; Expectation volGet; Sequence f0Seq, redcSeq, qSeq, volSeq, durSeq; EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault()); EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault()); deleteVibrator(false); createMock(&mockapi, &mockcal); EXPECT_CALL(*mMockCal, getF0(_)) .InSequence(f0Seq) .WillOnce(DoAll(SetArgPointee<0>(f0Val), Return(true))); EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(Return(true)); EXPECT_CALL(*mMockCal, getRedc(_)) .InSequence(redcSeq) .WillOnce(DoAll(SetArgPointee<0>(redcVal), Return(true))); EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(Return(true)); EXPECT_CALL(*mMockCal, getQ(_)) .InSequence(qSeq) .WillOnce(DoAll(SetArgPointee<0>(qVal), Return(true))); EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(Return(true)); if (mMockCal->getVersion(&calVer) == 1) { volGet = EXPECT_CALL(*mMockCal, getVolLevels(_)).WillOnce(DoDefault()); } else { volGet = EXPECT_CALL(*mMockCal, getTickVolLevels(_)).WillOnce(DoDefault()); volGet = EXPECT_CALL(*mMockCal, getClickVolLevels(_)).WillOnce(DoDefault()); volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault()); } EXPECT_CALL(*mMockApi, setState(true)).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, getEffectCount(_)).InSequence(durSeq).WillOnce(DoDefault()); for (auto &d : EFFECT_DURATIONS) { EXPECT_CALL(*mMockApi, setEffectIndex(&d - &EFFECT_DURATIONS[0])) .InSequence(durSeq) .WillOnce(DoDefault()); EXPECT_CALL(*mMockApi, getEffectDuration(_)).InSequence(durSeq).WillOnce(DoDefault()); } EXPECT_CALL(*mMockApi, hasEffectScale()).WillRepeatedly(Return(true)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillRepeatedly(Return(true)); createVibrator(std::move(mockapi), std::move(mockcal), false); } TEST_F(VibratorTest, on) { Sequence s1, s2, s3; uint16_t duration = std::rand() + 1; EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setEffectIndex(ON_EFFECT_INDEX)).InSequence(s2).WillOnce(DoDefault()); EXPECT_CALL(*mMockApi, setDuration(Ge(duration))).InSequence(s3).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setActivate(true)).InSequence(s1, s2, s3).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->on(duration, nullptr).isOk()); } TEST_F(VibratorTest, off) { EXPECT_CALL(*mMockApi, setActivate(false)).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGlobalScale(0)).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->off().isOk()); } TEST_F(VibratorTest, supportsAmplitudeControl_supported) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_GT(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0); } TEST_F(VibratorTest, supportsAmplitudeControl_unsupported1) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(false)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_EQ(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0); } TEST_F(VibratorTest, supportsAmplitudeControl_unsupported2) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(false)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(false)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_EQ(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0); } TEST_F(VibratorTest, supportsExternalAmplitudeControl_unsupported) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, 0); } TEST_F(VibratorTest, setAmplitude_supported) { Sequence s; EffectAmplitude amplitude = static_cast(std::rand()) / RAND_MAX ?: 1.0f; // The default mIsUnderExternalControl is false, no need to turn off the External Control EXPECT_CALL(*mMockApi, setEffectScale(amplitudeToScale(amplitude))) .InSequence(s) .WillOnce(Return(true)); EXPECT_TRUE(mVibrator->setAmplitude(amplitude).isOk()); } TEST_F(VibratorTest, setAmplitude_unsupported) { // Turn on the External Control and make mIsUnderExternalControl true Sequence s; EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)).InSequence(s).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setAspEnable(true)).InSequence(s).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->setExternalControl(true).isOk()); EXPECT_EQ(EX_UNSUPPORTED_OPERATION, mVibrator->setAmplitude(1).getExceptionCode()); } TEST_F(VibratorTest, supportsExternalControl_supported) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_GT(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0); } TEST_F(VibratorTest, supportsExternalControl_unsupported) { EXPECT_CALL(*mMockApi, hasEffectScale()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(false)); int32_t capabilities; EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk()); EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0); } TEST_F(VibratorTest, setExternalControl_enable) { Sequence s; EXPECT_CALL(*mMockApi, hasAspEnable()).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)).InSequence(s).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setAspEnable(true)).InSequence(s).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->setExternalControl(true).isOk()); } TEST_F(VibratorTest, setExternalControl_disable) { Sequence s; EXPECT_CALL(*mMockApi, hasAspEnable()).WillRepeatedly(Return(true)); // The default mIsUnderExternalControl is false, so it needs to turn on the External Control // to make mIsUnderExternalControl become true. EXPECT_CALL(*mMockApi, setGlobalScale(ON_GLOBAL_SCALE)).InSequence(s).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setAspEnable(true)).InSequence(s).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->setExternalControl(true).isOk()); EXPECT_CALL(*mMockApi, setAspEnable(false)).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGlobalScale(0)).WillOnce(Return(true)); EXPECT_TRUE(mVibrator->setExternalControl(false).isOk()); } class EffectsTest : public VibratorTest, public WithParamInterface { public: static auto PrintParam(const TestParamInfo &info) { auto param = info.param; auto effect = std::get<0>(param); auto strength = std::get<1>(param); return toString(effect) + "_" + toString(strength); } }; TEST_P(EffectsTest, perform) { auto param = GetParam(); auto effect = std::get<0>(param); auto strength = std::get<1>(param); auto scale = EFFECT_SCALE.find(param); auto queue = EFFECT_QUEUE.find(param); EffectDuration duration; auto callback = ndk::SharedRefBase::make(); std::promise promise; std::future future{promise.get_future()}; auto complete = [&promise] { promise.set_value(); return ndk::ScopedAStatus::ok(); }; ExpectationSet eSetup; Expectation eActivate, ePollStop; if (scale != EFFECT_SCALE.end()) { EffectIndex index = EFFECT_INDEX.at(effect); duration = EFFECT_DURATIONS[index] + MAX_COLD_START_LATENCY_MS; eSetup += EXPECT_CALL(*mMockApi, setEffectIndex(index)).WillOnce(DoDefault()); eSetup += EXPECT_CALL(*mMockApi, setEffectScale(scale->second)).WillOnce(Return(true)); } else if (queue != EFFECT_QUEUE.end()) { duration = std::get<1>(queue->second) + MAX_COLD_START_LATENCY_MS * 2 + MAX_PAUSE_TIMING_ERROR_MS; eSetup += EXPECT_CALL(*mMockApi, setEffectIndex(QUEUE_INDEX)).WillOnce(DoDefault()); eSetup += EXPECT_CALL(*mMockApi, setEffectQueue(std::get<0>(queue->second))) .WillOnce(Return(true)); eSetup += EXPECT_CALL(*mMockApi, setEffectScale(0)).WillOnce(Return(true)); } else { duration = 0; } if (duration) { eSetup += EXPECT_CALL(*mMockApi, setDuration(Ge(duration))).WillOnce(Return(true)); eActivate = EXPECT_CALL(*mMockApi, setActivate(true)).After(eSetup).WillOnce(Return(true)); ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(false, duration + POLLING_TIMEOUT)) .After(eActivate) .WillOnce(DoDefault()); EXPECT_CALL(*mMockApi, setActivate(false)).After(ePollStop).WillOnce(Return(true)); EXPECT_CALL(*callback, onComplete()).After(ePollStop).WillOnce(complete); } int32_t lengthMs; ndk::ScopedAStatus status = mVibrator->perform(effect, strength, callback, &lengthMs); if (status.isOk()) { EXPECT_LE(duration, lengthMs); } else { EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode()); EXPECT_EQ(0, lengthMs); } if (duration) { EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready); } } TEST_P(EffectsTest, alwaysOnEnable) { auto param = GetParam(); auto effect = std::get<0>(param); auto strength = std::get<1>(param); auto scale = EFFECT_SCALE.find(param); bool supported = (scale != EFFECT_SCALE.end()); if (supported) { EXPECT_CALL(*mMockApi, setGpioRiseIndex(EFFECT_INDEX.at(effect))).WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGpioRiseScale(scale->second)).WillOnce(Return(true)); } ndk::ScopedAStatus status = mVibrator->alwaysOnEnable(0, effect, strength); if (supported) { EXPECT_EQ(EX_NONE, status.getExceptionCode()); } else { EXPECT_EQ(EX_UNSUPPORTED_OPERATION, status.getExceptionCode()); } } const std::vector kEffects{ndk::enum_range().begin(), ndk::enum_range().end()}; const std::vector kEffectStrengths{ndk::enum_range().begin(), ndk::enum_range().end()}; INSTANTIATE_TEST_CASE_P(VibratorTests, EffectsTest, Combine(ValuesIn(kEffects.begin(), kEffects.end()), ValuesIn(kEffectStrengths.begin(), kEffectStrengths.end())), EffectsTest::PrintParam); struct PrimitiveParam { CompositePrimitive primitive; EffectIndex index; }; class PrimitiveTest : public VibratorTest, public WithParamInterface { public: static auto PrintParam(const TestParamInfo &info) { return toString(info.param.primitive); } }; const std::vector kPrimitiveParams = { {CompositePrimitive::NOOP, 0}, {CompositePrimitive::CLICK, 2}, {CompositePrimitive::THUD, 4}, {CompositePrimitive::SPIN, 5}, {CompositePrimitive::QUICK_RISE, 6}, {CompositePrimitive::SLOW_RISE, 7}, {CompositePrimitive::QUICK_FALL, 8}, }; TEST_P(PrimitiveTest, getPrimitiveDuration) { auto param = GetParam(); auto primitive = param.primitive; auto index = param.index; int32_t duration; EXPECT_EQ(EX_NONE, mVibrator->getPrimitiveDuration(primitive, &duration).getExceptionCode()); EXPECT_EQ(EFFECT_DURATIONS[index], duration); } INSTANTIATE_TEST_CASE_P(VibratorTests, PrimitiveTest, ValuesIn(kPrimitiveParams.begin(), kPrimitiveParams.end()), PrimitiveTest::PrintParam); struct ComposeParam { std::string name; std::vector composite; EffectQueue queue; }; class ComposeTest : public VibratorTest, public WithParamInterface { public: static auto PrintParam(const TestParamInfo &info) { return info.param.name; } }; TEST_P(ComposeTest, compose) { auto param = GetParam(); auto composite = param.composite; auto queue = std::get<0>(param.queue); auto duration = std::get<1>(param.queue); ExpectationSet eSetup; Expectation eActivate, ePollStop; auto callback = ndk::SharedRefBase::make(); std::promise promise; std::future future{promise.get_future()}; auto complete = [&promise] { promise.set_value(); return ndk::ScopedAStatus::ok(); }; eSetup += EXPECT_CALL(*mMockApi, setEffectIndex(QUEUE_INDEX)).WillOnce(DoDefault()); eSetup += EXPECT_CALL(*mMockApi, setEffectQueue(queue)).WillOnce(Return(true)); eSetup += EXPECT_CALL(*mMockApi, setEffectScale(0)).WillOnce(Return(true)); eSetup += EXPECT_CALL(*mMockApi, setDuration(UINT32_MAX)).WillOnce(Return(true)); eActivate = EXPECT_CALL(*mMockApi, setActivate(true)).After(eSetup).WillOnce(Return(true)); ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(false, duration + POLLING_TIMEOUT)) .After(eActivate) .WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setActivate(false)).After(ePollStop).WillOnce(Return(true)); EXPECT_CALL(*callback, onComplete()).After(ePollStop).WillOnce(complete); EXPECT_EQ(EX_NONE, mVibrator->compose(composite, callback).getExceptionCode()); EXPECT_EQ(future.wait_for(std::chrono::milliseconds(100)), std::future_status::ready); } const std::vector kComposeParams = { {"click", {{0, CompositePrimitive::CLICK, 1.0f}}, Queue(QueueEffect(2, Level(1.0f)), 0)}, {"thud", {{1, CompositePrimitive::THUD, 0.8f}}, Queue(1, QueueEffect(4, Level(0.8f)), 0)}, {"spin", {{2, CompositePrimitive::SPIN, 0.6f}}, Queue(2, QueueEffect(5, Level(0.6f)), 0)}, {"quick_rise", {{3, CompositePrimitive::QUICK_RISE, 0.4f}}, Queue(3, QueueEffect(6, 0.4f * V_LEVELS[5]), 0)}, {"slow_rise", {{4, CompositePrimitive::SLOW_RISE, 0.0f}}, Queue(4, QueueEffect(7, Level(0.0f)), 0)}, {"quick_fall", {{5, CompositePrimitive::QUICK_FALL, 1.0f}}, Queue(5, QueueEffect(8, 1.0f * V_LEVELS[5]), 0)}, {"pop", {{6, CompositePrimitive::SLOW_RISE, 1.0f}, {50, CompositePrimitive::THUD, 1.0f}}, Queue(6, QueueEffect(7, Level(1.0f)), 50, QueueEffect(4, Level(1.0f)), 0)}, {"snap", {{7, CompositePrimitive::QUICK_RISE, 1.0f}, {0, CompositePrimitive::QUICK_FALL, 1.0f}}, Queue(7, QueueEffect(6, 1.0f * V_LEVELS[5]), QueueEffect(8, 1.0f * V_LEVELS[5]), 0)}, }; INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest, ValuesIn(kComposeParams.begin(), kComposeParams.end()), ComposeTest::PrintParam); class AlwaysOnTest : public VibratorTest, public WithParamInterface { public: static auto PrintParam(const TestParamInfo &info) { return std::to_string(info.param); } }; TEST_P(AlwaysOnTest, alwaysOnEnable) { auto param = GetParam(); auto scale = EFFECT_SCALE.begin(); std::advance(scale, std::rand() % EFFECT_SCALE.size()); auto effect = std::get<0>(scale->first); auto strength = std::get<1>(scale->first); switch (param) { case 0: EXPECT_CALL(*mMockApi, setGpioRiseIndex(EFFECT_INDEX.at(effect))) .WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGpioRiseScale(scale->second)).WillOnce(Return(true)); break; case 1: EXPECT_CALL(*mMockApi, setGpioFallIndex(EFFECT_INDEX.at(effect))) .WillOnce(Return(true)); EXPECT_CALL(*mMockApi, setGpioFallScale(scale->second)).WillOnce(Return(true)); break; } ndk::ScopedAStatus status = mVibrator->alwaysOnEnable(param, effect, strength); EXPECT_EQ(EX_NONE, status.getExceptionCode()); } TEST_P(AlwaysOnTest, alwaysOnDisable) { auto param = GetParam(); switch (param) { case 0: EXPECT_CALL(*mMockApi, setGpioRiseIndex(0)).WillOnce(Return(true)); break; case 1: EXPECT_CALL(*mMockApi, setGpioFallIndex(0)).WillOnce(Return(true)); break; } ndk::ScopedAStatus status = mVibrator->alwaysOnDisable(param); EXPECT_EQ(EX_NONE, status.getExceptionCode()); } INSTANTIATE_TEST_CASE_P(VibratorTests, AlwaysOnTest, Range(0, 1), AlwaysOnTest::PrintParam); } // namespace vibrator } // namespace hardware } // namespace android } // namespace aidl