InputHub_test.cpp revision c929d2509530b0262681c8e6619609f44bfceea4
1/* 2 * Copyright (C) 2015 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#define LOG_TAG "InputHub_test" 18//#define LOG_NDEBUG 0 19 20#include <linux/input.h> 21 22#include <chrono> 23#include <memory> 24#include <mutex> 25 26#include <gtest/gtest.h> 27 28#include <utils/Log.h> 29#include <utils/StopWatch.h> 30#include <utils/Timers.h> 31 32#include "InputHub.h" 33#include "TestHelpers.h" 34 35// # of milliseconds to fudge stopwatch measurements 36#define TIMING_TOLERANCE_MS 25 37#define NO_TIMEOUT (-1) 38 39namespace android { 40namespace tests { 41 42using namespace std::literals::chrono_literals; 43 44using InputCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)>; 45using DeviceCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&)>; 46 47static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){}; 48static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){}; 49 50class TestInputCallback : public InputCallbackInterface { 51public: 52 TestInputCallback() : 53 mInputCb(kNoopInputCb), mDeviceAddedCb(kNoopDeviceCb), mDeviceRemovedCb(kNoopDeviceCb) {} 54 virtual ~TestInputCallback() = default; 55 56 void setInputCallback(InputCbFunc cb) { mInputCb = cb; } 57 void setDeviceAddedCallback(DeviceCbFunc cb) { mDeviceAddedCb = cb; } 58 void setDeviceRemovedCallback(DeviceCbFunc cb) { mDeviceRemovedCb = cb; } 59 60 virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event, 61 nsecs_t event_time) override { 62 mInputCb(node, event, event_time); 63 } 64 virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override { 65 mDeviceAddedCb(node); 66 } 67 virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override { 68 mDeviceRemovedCb(node); 69 } 70 71private: 72 InputCbFunc mInputCb; 73 DeviceCbFunc mDeviceAddedCb; 74 DeviceCbFunc mDeviceRemovedCb; 75}; 76 77class InputHubTest : public ::testing::Test { 78 protected: 79 virtual void SetUp() { 80 mCallback = std::make_shared<TestInputCallback>(); 81 mInputHub = std::make_shared<InputHub>(mCallback); 82 } 83 84 std::shared_ptr<TestInputCallback> mCallback; 85 std::shared_ptr<InputHub> mInputHub; 86}; 87 88TEST_F(InputHubTest, testWake) { 89 // Call wake() after 100ms. 90 auto f = delay_async(100ms, [&]() { EXPECT_EQ(OK, mInputHub->wake()); }); 91 92 StopWatch stopWatch("poll"); 93 EXPECT_EQ(OK, mInputHub->poll()); 94 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 95 96 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 97} 98 99TEST_F(InputHubTest, DISABLED_testDeviceAdded) { 100 auto tempDir = std::make_shared<TempDir>(); 101 std::string pathname; 102 // Expect that this callback will run and set handle and pathname. 103 mCallback->setDeviceAddedCallback( 104 [&](const std::shared_ptr<InputDeviceNode>& node) { 105 pathname = node->getPath(); 106 }); 107 108 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 109 110 // Create a new file in tempDir after 100ms. 111 std::unique_ptr<TempFile> tempFile; 112 std::mutex tempFileMutex; 113 auto f = delay_async(100ms, 114 [&]() { 115 std::lock_guard<std::mutex> lock(tempFileMutex); 116 tempFile.reset(tempDir->newTempFile()); 117 }); 118 119 StopWatch stopWatch("poll"); 120 EXPECT_EQ(OK, mInputHub->poll()); 121 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 122 123 124 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 125 std::lock_guard<std::mutex> lock(tempFileMutex); 126 EXPECT_EQ(tempFile->getName(), pathname); 127} 128 129TEST_F(InputHubTest, DISABLED_testDeviceRemoved) { 130 // Create a temp dir and file. Save its name and handle (to be filled in 131 // once InputHub scans the dir). 132 auto tempDir = std::make_unique<TempDir>(); 133 auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); 134 std::string tempFileName(deviceFile->getName()); 135 136 std::shared_ptr<InputDeviceNode> tempNode; 137 // Expect that these callbacks will run for the above device file. 138 mCallback->setDeviceAddedCallback( 139 [&](const std::shared_ptr<InputDeviceNode>& node) { 140 tempNode = node; 141 }); 142 mCallback->setDeviceRemovedCallback( 143 [&](const std::shared_ptr<InputDeviceNode>& node) { 144 EXPECT_EQ(tempNode, node); 145 }); 146 147 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 148 // Ensure that tempDir was scanned to find the device. 149 ASSERT_TRUE(tempNode != nullptr); 150 151 auto f = delay_async(100ms, [&]() { deviceFile.reset(); }); 152 153 StopWatch stopWatch("poll"); 154 EXPECT_EQ(OK, mInputHub->poll()); 155 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 156 157 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 158} 159 160TEST_F(InputHubTest, DISABLED_testInputEvent) { 161 // Create a temp dir and file. Save its name and handle (to be filled in 162 // once InputHub scans the dir.) 163 auto tempDir = std::make_unique<TempDir>(); 164 auto deviceFile = std::unique_ptr<TempFile>(tempDir->newTempFile()); 165 std::string tempFileName(deviceFile->getName()); 166 167 // Send a key event corresponding to HOME. 168 struct input_event iev; 169 iev.time = { 1, 0 }; 170 iev.type = EV_KEY; 171 iev.code = KEY_HOME; 172 iev.value = 0x01; 173 174 auto inputDelayMs = 100ms; 175 auto f = delay_async(inputDelayMs, [&] { 176 ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile->getFd(), &iev, sizeof(iev))); 177 178 ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " 179 << deviceFile->getFd() << ". errno: " << errno; 180 }); 181 182 // Expect this callback to run when the input event is read. 183 nsecs_t expectedWhen = systemTime(CLOCK_MONOTONIC) + ms2ns(inputDelayMs.count()); 184 mCallback->setInputCallback( 185 [&](const std::shared_ptr<InputDeviceNode>& node, InputEvent& event, 186 nsecs_t event_time) { 187 EXPECT_NEAR(expectedWhen, event_time, ms2ns(TIMING_TOLERANCE_MS)); 188 EXPECT_EQ(s2ns(1), event.when); 189 EXPECT_EQ(tempFileName, node->getPath()); 190 EXPECT_EQ(EV_KEY, event.type); 191 EXPECT_EQ(KEY_HOME, event.code); 192 EXPECT_EQ(0x01, event.value); 193 }); 194 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 195 196 StopWatch stopWatch("poll"); 197 EXPECT_EQ(OK, mInputHub->poll()); 198 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 199 200 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 201} 202 203TEST_F(InputHubTest, DISABLED_testCallbackOrder) { 204 // Create two "devices": one to receive input and the other to go away. 205 auto tempDir = std::make_unique<TempDir>(); 206 auto deviceFile1 = std::unique_ptr<TempFile>(tempDir->newTempFile()); 207 auto deviceFile2 = std::unique_ptr<TempFile>(tempDir->newTempFile()); 208 std::string tempFileName(deviceFile2->getName()); 209 210 bool inputCallbackFinished = false, deviceCallbackFinished = false; 211 212 // Setup the callback for input events. Should run before the device 213 // callback. 214 mCallback->setInputCallback( 215 [&](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t) { 216 ASSERT_FALSE(deviceCallbackFinished); 217 inputCallbackFinished = true; 218 }); 219 220 // Setup the callback for device removal. Should run after the input 221 // callback. 222 mCallback->setDeviceRemovedCallback( 223 [&](const std::shared_ptr<InputDeviceNode>& node) { 224 ASSERT_TRUE(inputCallbackFinished) 225 << "input callback did not run before device changed callback"; 226 // Make sure the correct device was removed. 227 EXPECT_EQ(tempFileName, node->getPath()); 228 deviceCallbackFinished = true; 229 }); 230 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName())); 231 232 auto f = delay_async(100ms, 233 [&]() { 234 // Delete the second device file first. 235 deviceFile2.reset(); 236 237 // Then inject an input event into the first device. 238 struct input_event iev; 239 iev.time = { 1, 0 }; 240 iev.type = EV_KEY; 241 iev.code = KEY_HOME; 242 iev.value = 0x01; 243 244 ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile1->getFd(), &iev, sizeof(iev))); 245 246 ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to " 247 << deviceFile1->getFd() << ". errno: " << errno; 248 }); 249 250 StopWatch stopWatch("poll"); 251 EXPECT_EQ(OK, mInputHub->poll()); 252 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); 253 254 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS); 255 EXPECT_TRUE(inputCallbackFinished); 256 EXPECT_TRUE(deviceCallbackFinished); 257} 258 259} // namespace tests 260} // namespace android 261