device_google_felix/vibrator/cs40l26/tests/test-vibrator.cpp
Chase Wu 07468b074a [DO NOT MERGE] cs40l26: single HAL for dual haptics
We need to sync both haptics on the single HAL
architecture to reduce the time overhead and prevent the
mismatch behavior from vibrator manager HAL implementation.

Bug: 260090235
Bug: 261687849
Bug: 261841035
Bug: 261832151
Test: atest PtsVibratorHalTestSuite \
PtsHapticsTestCases \
PtsHapticsFeatureTestCases \
VibratorHalCs40l26TestSuite \
VtsHalVibratorManagerTargetTest \
VtsHalVibratorTargetTest \
android.os.cts.VibratorTest \
android.os.cts.VibratorManagerTest \
android.os.cts.VibrationEffectTest \
android.os.cts.VibrationAttributesTest \
android.os.cts.CombinedVibrationTest \
Change-Id: I084e1da952ab2f63e5c217a7f708ac3d34635336
Signed-off-by: Chase Wu <chasewu@google.com>
2022-12-12 17:34:18 +00:00

694 lines
28 KiB
C++

/*
* Copyright (C) 2022 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 <aidl/android/hardware/vibrator/BnVibratorCallback.h>
#include <android-base/logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <future>
#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::SetArgReferee;
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 <typename T, typename U, typename... Args>
static EffectQueue Queue(const T &first, const U &second, Args... rest);
static EffectLevel Level(float intensity, float levelLow, float levelHigh);
static EffectScale Scale(float intensity, float levelLow, float levelHigh);
// Constants With Arbitrary Values
static constexpr uint32_t CAL_VERSION = 2;
static constexpr std::array<EffectLevel, 2> V_TICK_DEFAULT = {1, 100};
static constexpr std::array<EffectLevel, 2> V_CLICK_DEFAULT{1, 100};
static constexpr std::array<EffectLevel, 2> V_LONG_DEFAULT{1, 100};
static constexpr std::array<EffectDuration, 14> EFFECT_DURATIONS{
0, 100, 30, 1000, 300, 130, 150, 500, 100, 15, 20, 1000, 1000, 1000};
// Constants With Prescribed Values
static const std::map<Effect, EffectIndex> EFFECT_INDEX{
{Effect::CLICK, 2},
{Effect::TICK, 2},
{Effect::HEAVY_CLICK, 2},
{Effect::TEXTURE_TICK, 9},
};
static constexpr uint32_t MIN_ON_OFF_INTERVAL_US = 8500;
static constexpr uint8_t VOLTAGE_SCALE_MAX = 100;
static constexpr int8_t MAX_COLD_START_LATENCY_MS = 6; // I2C Transaction + DSP Return-From-Standby
static constexpr auto POLLING_TIMEOUT = 20;
enum WaveformIndex : uint16_t {
/* Physical waveform */
WAVEFORM_LONG_VIBRATION_EFFECT_INDEX = 0,
WAVEFORM_RESERVED_INDEX_1 = 1,
WAVEFORM_CLICK_INDEX = 2,
WAVEFORM_SHORT_VIBRATION_EFFECT_INDEX = 3,
WAVEFORM_THUD_INDEX = 4,
WAVEFORM_SPIN_INDEX = 5,
WAVEFORM_QUICK_RISE_INDEX = 6,
WAVEFORM_SLOW_RISE_INDEX = 7,
WAVEFORM_QUICK_FALL_INDEX = 8,
WAVEFORM_LIGHT_TICK_INDEX = 9,
WAVEFORM_LOW_TICK_INDEX = 10,
WAVEFORM_RESERVED_MFG_1,
WAVEFORM_RESERVED_MFG_2,
WAVEFORM_RESERVED_MFG_3,
WAVEFORM_MAX_PHYSICAL_INDEX,
/* OWT waveform */
WAVEFORM_COMPOSE = WAVEFORM_MAX_PHYSICAL_INDEX,
WAVEFORM_PWLE,
/*
* Refer to <linux/input.h>, the WAVEFORM_MAX_INDEX must not exceed 96.
* #define FF_GAIN 0x60 // 96 in decimal
* #define FF_MAX_EFFECTS FF_GAIN
*/
WAVEFORM_MAX_INDEX,
};
static const EffectScale ON_GLOBAL_SCALE{levelToScale(V_LONG_DEFAULT[1])};
static const EffectIndex ON_EFFECT_INDEX{0};
static const std::map<EffectTuple, EffectScale> EFFECT_SCALE{
{{Effect::TICK, EffectStrength::LIGHT},
Scale(0.5f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::TICK, EffectStrength::MEDIUM},
Scale(0.5f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::TICK, EffectStrength::STRONG},
Scale(0.5f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::CLICK, EffectStrength::LIGHT},
Scale(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::CLICK, EffectStrength::MEDIUM},
Scale(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::CLICK, EffectStrength::STRONG},
Scale(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::HEAVY_CLICK, EffectStrength::LIGHT},
Scale(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::HEAVY_CLICK, EffectStrength::MEDIUM},
Scale(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::HEAVY_CLICK, EffectStrength::STRONG},
Scale(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
{{Effect::TEXTURE_TICK, EffectStrength::LIGHT},
Scale(0.5f * 0.5f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
{{Effect::TEXTURE_TICK, EffectStrength::MEDIUM},
Scale(0.5f * 0.7f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
{{Effect::TEXTURE_TICK, EffectStrength::STRONG},
Scale(0.5f * 1.0f, V_TICK_DEFAULT[0], V_TICK_DEFAULT[1])},
};
static const std::map<EffectTuple, EffectQueue> EFFECT_QUEUE{
{{Effect::DOUBLE_CLICK, EffectStrength::LIGHT},
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(0.7f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
100,
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(1.0f * 0.5f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
{{Effect::DOUBLE_CLICK, EffectStrength::MEDIUM},
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(0.7f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
100,
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(1.0f * 0.7f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
{{Effect::DOUBLE_CLICK, EffectStrength::STRONG},
Queue(QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(0.7f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])},
100,
QueueEffect{EFFECT_INDEX.at(Effect::CLICK),
Level(1.0f * 1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])})},
};
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 <typename T, typename U, typename... Args>
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, float levelLow, float levelHigh) {
return std::lround(intensity * (levelHigh - levelLow)) + levelLow;
}
static EffectScale Scale(float intensity, float levelLow, float levelHigh) {
return levelToScale(Level(intensity, levelLow, levelHigh));
}
class VibratorTest : public Test {
public:
void SetUp() override {
setenv("INPUT_EVENT_NAME", "CS40L26TestSuite", true);
std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
createMock(&mockapi, &mockcal, &mockgpio);
createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio));
}
void TearDown() override { deleteVibrator(); }
protected:
void createMock(std::unique_ptr<MockApi> *mockapi, std::unique_ptr<MockCal> *mockcal,
std::unique_ptr<MockGPIO> *mockgpio) {
*mockapi = std::make_unique<MockApi>();
*mockcal = std::make_unique<MockCal>();
*mockgpio = std::make_unique<MockGPIO>();
mMockApi = mockapi->get();
mMockCal = mockcal->get();
mMockGpio = mockgpio->get();
ON_CALL(*mMockApi, destructor()).WillByDefault(Assign(&mMockApi, nullptr));
ON_CALL(*mMockApi, setFFGain(_, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, setFFEffect(_, _, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, setFFPlay(_, _, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, pollVibeState(_, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, eraseOwtEffect(_, _, _)).WillByDefault(Return(true));
ON_CALL(*mMockApi, getOwtFreeSpace(_))
.WillByDefault(DoAll(SetArgPointee<0>(11504), Return(true)));
ON_CALL(*mMockCal, destructor()).WillByDefault(Assign(&mMockCal, nullptr));
ON_CALL(*mMockCal, getVersion(_))
.WillByDefault(DoAll(SetArgPointee<0>(CAL_VERSION), Return(true)));
ON_CALL(*mMockCal, getTickVolLevels(_))
.WillByDefault(DoAll(SetArgPointee<0>(V_TICK_DEFAULT), Return(true)));
ON_CALL(*mMockCal, getClickVolLevels(_))
.WillByDefault(DoAll(SetArgPointee<0>(V_CLICK_DEFAULT), Return(true)));
ON_CALL(*mMockCal, getLongVolLevels(_))
.WillByDefault(DoAll(SetArgPointee<0>(V_LONG_DEFAULT), Return(true)));
ON_CALL(*mMockGpio, destructor()).WillByDefault(Assign(&mMockGpio, nullptr));
relaxMock(false);
}
void createVibrator(std::unique_ptr<MockApi> mockapi, std::unique_ptr<MockCal> mockcal,
std::unique_ptr<MockGPIO> mockgpio, bool relaxed = true) {
if (relaxed) {
relaxMock(true);
}
// TODO(b/261415845): Need to add dual parameters to test the vibrator HAL's code in haptics
// mock test
mVibrator = ndk::SharedRefBase::make<Vibrator>(std::move(mockapi), std::move(mockcal),
nullptr, nullptr, std::move(mockgpio));
if (relaxed) {
relaxMock(false);
}
}
void deleteVibrator(bool relaxed = true) {
if (relaxed) {
relaxMock(true);
}
mVibrator.reset();
}
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, setF0Offset(_)).Times(times);
EXPECT_CALL(*mMockApi, setRedc(_)).Times(times);
EXPECT_CALL(*mMockApi, setQ(_)).Times(times);
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).Times(times);
EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).Times(times);
EXPECT_CALL(*mMockApi, setF0CompEnable(_)).Times(times);
EXPECT_CALL(*mMockApi, setRedcCompEnable(_)).Times(times);
EXPECT_CALL(*mMockApi, pollVibeState(_, _)).Times(times);
EXPECT_CALL(*mMockApi, setFFGain(_, _)).Times(times);
EXPECT_CALL(*mMockApi, setFFEffect(_, _, _)).Times(times);
EXPECT_CALL(*mMockApi, setFFPlay(_, _, _)).Times(times);
EXPECT_CALL(*mMockApi, setMinOnOffInterval(_)).Times(times);
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).Times(times);
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, _, _, _)).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, getTickVolLevels(_)).Times(times);
EXPECT_CALL(*mMockCal, getClickVolLevels(_)).Times(times);
EXPECT_CALL(*mMockCal, getLongVolLevels(_)).Times(times);
EXPECT_CALL(*mMockCal, isChirpEnabled()).Times(times);
EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).Times(times);
EXPECT_CALL(*mMockCal, isF0CompEnabled()).Times(times);
EXPECT_CALL(*mMockCal, isRedcCompEnabled()).Times(times);
EXPECT_CALL(*mMockCal, debug(_)).Times(times);
}
protected:
MockApi *mMockApi;
MockCal *mMockCal;
MockGPIO *mMockGpio;
std::shared_ptr<IVibrator> mVibrator;
uint32_t mEffectIndex;
};
TEST_F(VibratorTest, Constructor) {
std::unique_ptr<MockApi> mockapi;
std::unique_ptr<MockCal> mockcal;
std::unique_ptr<MockGPIO> mockgpio;
std::string f0Val = std::to_string(std::rand());
std::string redcVal = std::to_string(std::rand());
std::string qVal = std::to_string(std::rand());
uint32_t calVer;
uint32_t supportedPrimitivesBits = 0x0;
Expectation volGet;
Sequence f0Seq, redcSeq, qSeq, supportedPrimitivesSeq;
EXPECT_CALL(*mMockApi, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockCal, destructor()).WillOnce(DoDefault());
EXPECT_CALL(*mMockGpio, destructor()).WillOnce(DoDefault());
deleteVibrator(false);
createMock(&mockapi, &mockcal, &mockgpio);
EXPECT_CALL(*mMockCal, getF0(_))
.InSequence(f0Seq)
.WillOnce(DoAll(SetArgReferee<0>(f0Val), Return(true)));
EXPECT_CALL(*mMockApi, setF0(f0Val)).InSequence(f0Seq).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, getRedc(_))
.InSequence(redcSeq)
.WillOnce(DoAll(SetArgReferee<0>(redcVal), Return(true)));
EXPECT_CALL(*mMockApi, setRedc(redcVal)).InSequence(redcSeq).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, getQ(_))
.InSequence(qSeq)
.WillOnce(DoAll(SetArgReferee<0>(qVal), Return(true)));
EXPECT_CALL(*mMockApi, setQ(qVal)).InSequence(qSeq).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, getLongFrequencyShift(_)).WillOnce(Return(true));
mMockCal->getVersion(&calVer);
if (calVer == 2) {
volGet = EXPECT_CALL(*mMockCal, getTickVolLevels(_)).WillOnce(DoDefault());
volGet = EXPECT_CALL(*mMockCal, getClickVolLevels(_)).WillOnce(DoDefault());
volGet = EXPECT_CALL(*mMockCal, getLongVolLevels(_)).WillOnce(DoDefault());
}
EXPECT_CALL(*mMockCal, isF0CompEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, setF0CompEnable(true)).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, isRedcCompEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, setRedcCompEnable(true)).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, isChirpEnabled()).WillOnce(Return(true));
EXPECT_CALL(*mMockCal, getSupportedPrimitives(_))
.InSequence(supportedPrimitivesSeq)
.WillOnce(DoAll(SetArgPointee<0>(supportedPrimitivesBits), Return(true)));
EXPECT_CALL(*mMockApi, setMinOnOffInterval(MIN_ON_OFF_INTERVAL_US)).WillOnce(Return(true));
createVibrator(std::move(mockapi), std::move(mockcal), std::move(mockgpio), false);
}
TEST_F(VibratorTest, on) {
Sequence s1, s2;
uint16_t duration = std::rand() + 1;
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
EXPECT_CALL(*mMockApi, setFFEffect(_, _, duration + MAX_COLD_START_LATENCY_MS))
.InSequence(s2)
.WillOnce(DoDefault());
EXPECT_CALL(*mMockApi, setFFPlay(_, ON_EFFECT_INDEX, true))
.InSequence(s1, s2)
.WillOnce(DoDefault());
EXPECT_TRUE(mVibrator->on(duration, nullptr).isOk());
}
TEST_F(VibratorTest, off) {
Sequence s1;
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
EXPECT_TRUE(mVibrator->off().isOk());
}
TEST_F(VibratorTest, supportsAmplitudeControl_supported) {
int32_t capabilities;
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
EXPECT_GT(capabilities & IVibrator::CAP_AMPLITUDE_CONTROL, 0);
}
TEST_F(VibratorTest, supportsExternalAmplitudeControl_unsupported) {
int32_t capabilities;
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, 0);
}
TEST_F(VibratorTest, setAmplitude_supported) {
EffectAmplitude amplitude = static_cast<float>(std::rand()) / RAND_MAX ?: 1.0f;
EXPECT_CALL(*mMockApi, setFFGain(_, amplitudeToScale(amplitude))).WillOnce(Return(true));
EXPECT_TRUE(mVibrator->setAmplitude(amplitude).isOk());
}
TEST_F(VibratorTest, supportsExternalControl_supported) {
int32_t capabilities;
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(true));
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
EXPECT_GT(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
}
TEST_F(VibratorTest, supportsExternalControl_unsupported) {
int32_t capabilities;
EXPECT_CALL(*mMockApi, hasOwtFreeSpace()).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).WillOnce(Return(false));
EXPECT_TRUE(mVibrator->getCapabilities(&capabilities).isOk());
EXPECT_EQ(capabilities & IVibrator::CAP_EXTERNAL_CONTROL, 0);
}
TEST_F(VibratorTest, setExternalControl_enable) {
Sequence s1, s2;
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE)).InSequence(s1).WillOnce(DoDefault());
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _))
.InSequence(s1, s2)
.WillOnce(Return(true));
EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
}
TEST_F(VibratorTest, setExternalControl_disable) {
Sequence s1, s2, s3, s4;
// The default mIsUnderExternalControl is false, so it needs to turn on the External Control
// to make mIsUnderExternalControl become true.
EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
.InSequence(s1)
.InSequence(s1)
.WillOnce(DoDefault());
EXPECT_CALL(*mMockApi, getHapticAlsaDevice(_, _)).InSequence(s2).WillOnce(Return(true));
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, true, _, _)).InSequence(s3).WillOnce(Return(true));
EXPECT_TRUE(mVibrator->setExternalControl(true).isOk());
EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(VOLTAGE_SCALE_MAX)))
.InSequence(s4)
.WillOnce(DoDefault());
EXPECT_CALL(*mMockApi, setHapticPcmAmp(_, false, _, _))
.InSequence(s1, s2, s3, s4)
.WillOnce(Return(true));
EXPECT_TRUE(mVibrator->setExternalControl(false).isOk());
}
class EffectsTest : public VibratorTest, public WithParamInterface<EffectTuple> {
public:
static auto PrintParam(const TestParamInfo<ParamType> &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<MockVibratorCallback>();
std::promise<void> promise;
std::future<void> future{promise.get_future()};
auto complete = [&promise] {
promise.set_value();
return ndk::ScopedAStatus::ok();
};
bool composeEffect;
ExpectationSet eSetup;
Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
if (scale != EFFECT_SCALE.end()) {
EffectIndex index = EFFECT_INDEX.at(effect);
duration = EFFECT_DURATIONS[index];
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, levelToScale(scale->second)))
.WillOnce(DoDefault());
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, index, true))
.After(eSetup)
.WillOnce(DoDefault());
} else if (queue != EFFECT_QUEUE.end()) {
duration = std::get<1>(queue->second);
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
.After(eSetup)
.WillOnce(DoDefault());
eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
.After(eSetup)
.WillOnce(DoDefault());
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
.After(eSetup)
.WillOnce(DoDefault());
composeEffect = true;
} else {
duration = 0;
}
if (duration) {
ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
.After(eActivate)
.WillOnce(DoDefault());
ePollStop = EXPECT_CALL(*mMockApi, pollVibeState(0, -1))
.After(ePollHaptics)
.WillOnce(DoDefault());
if (composeEffect) {
eEraseDone = EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _))
.After(ePollStop)
.WillOnce(DoDefault());
EXPECT_CALL(*callback, onComplete()).After(eEraseDone).WillOnce(complete);
} else {
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);
}
}
const std::vector<Effect> kEffects{ndk::enum_range<Effect>().begin(),
ndk::enum_range<Effect>().end()};
const std::vector<EffectStrength> kEffectStrengths{ndk::enum_range<EffectStrength>().begin(),
ndk::enum_range<EffectStrength>().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<PrimitiveParam> {
public:
static auto PrintParam(const TestParamInfo<ParamType> &info) {
return toString(info.param.primitive);
}
};
const std::vector<PrimitiveParam> kPrimitiveParams = {
{CompositePrimitive::CLICK, 2}, {CompositePrimitive::THUD, 4},
{CompositePrimitive::SPIN, 5}, {CompositePrimitive::QUICK_RISE, 6},
{CompositePrimitive::SLOW_RISE, 7}, {CompositePrimitive::QUICK_FALL, 8},
{CompositePrimitive::LIGHT_TICK, 9}, {CompositePrimitive::LOW_TICK, 10},
};
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<CompositeEffect> composite;
EffectQueue queue;
};
class ComposeTest : public VibratorTest, public WithParamInterface<ComposeParam> {
public:
static auto PrintParam(const TestParamInfo<ParamType> &info) { return info.param.name; }
};
TEST_P(ComposeTest, compose) {
auto param = GetParam();
auto composite = param.composite;
auto queue = std::get<0>(param.queue);
ExpectationSet eSetup;
Expectation eActivate, ePollHaptics, ePollStop, eEraseDone;
auto callback = ndk::SharedRefBase::make<MockVibratorCallback>();
std::promise<void> promise;
std::future<void> future{promise.get_future()};
auto complete = [&promise] {
promise.set_value();
return ndk::ScopedAStatus::ok();
};
eSetup += EXPECT_CALL(*mMockApi, setFFGain(_, ON_GLOBAL_SCALE))
.After(eSetup)
.WillOnce(DoDefault());
eSetup += EXPECT_CALL(*mMockApi, getOwtFreeSpace(_)).WillOnce(DoDefault());
eSetup += EXPECT_CALL(*mMockApi, uploadOwtEffect(_, _, _, _, _, _))
.After(eSetup)
.WillOnce(DoDefault());
eActivate = EXPECT_CALL(*mMockApi, setFFPlay(_, WAVEFORM_COMPOSE, true))
.After(eSetup)
.WillOnce(DoDefault());
ePollHaptics = EXPECT_CALL(*mMockApi, pollVibeState(1, POLLING_TIMEOUT))
.After(eActivate)
.WillOnce(DoDefault());
ePollStop =
EXPECT_CALL(*mMockApi, pollVibeState(0, -1)).After(ePollHaptics).WillOnce(DoDefault());
eEraseDone =
EXPECT_CALL(*mMockApi, eraseOwtEffect(_, _, _)).After(ePollStop).WillOnce(DoDefault());
EXPECT_CALL(*callback, onComplete()).After(eEraseDone).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<ComposeParam> kComposeParams = {
{"click",
{{0, CompositePrimitive::CLICK, 1.0f}},
Queue(QueueEffect(2, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
{"thud",
{{1, CompositePrimitive::THUD, 0.8f}},
Queue(1, QueueEffect(4, Level(0.8f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
{"spin",
{{2, CompositePrimitive::SPIN, 0.6f}},
Queue(2, QueueEffect(5, Level(0.6f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
{"quick_rise",
{{3, CompositePrimitive::QUICK_RISE, 0.4f}},
Queue(3, QueueEffect(6, Level(0.4f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
{"slow_rise",
{{4, CompositePrimitive::SLOW_RISE, 0.0f}},
Queue(4, QueueEffect(7, Level(0.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
{"quick_fall",
{{5, CompositePrimitive::QUICK_FALL, 1.0f}},
Queue(5, QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
{"pop",
{{6, CompositePrimitive::SLOW_RISE, 1.0f}, {50, CompositePrimitive::THUD, 1.0f}},
Queue(6, QueueEffect(7, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 50,
QueueEffect(4, Level(1.0f, V_CLICK_DEFAULT[0], V_CLICK_DEFAULT[1])), 0)},
{"snap",
{{7, CompositePrimitive::QUICK_RISE, 1.0f}, {0, CompositePrimitive::QUICK_FALL, 1.0f}},
Queue(7, QueueEffect(6, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])),
QueueEffect(8, Level(1.0f, V_LONG_DEFAULT[0], V_LONG_DEFAULT[1])), 0)},
};
INSTANTIATE_TEST_CASE_P(VibratorTests, ComposeTest,
ValuesIn(kComposeParams.begin(), kComposeParams.end()),
ComposeTest::PrintParam);
} // namespace vibrator
} // namespace hardware
} // namespace android
} // namespace aidl