blob: f2c8edf9915c2c6e5cd11d0e3697907cea1ece66 [file] [log] [blame]
Tim Kilbourn73475a42015-02-13 10:35:20 -08001/*
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(std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t)>;
45using DeviceCbFunc = std::function<void(std::shared_ptr<InputDeviceNode>)>;
46
47static const InputCbFunc kNoopInputCb = [](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t){};
48static const DeviceCbFunc kNoopDeviceCb = [](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(std::shared_ptr<InputDeviceNode> node, InputEvent& event,
61 nsecs_t event_time) override {
62 mInputCb(node, event, event_time);
63 }
64 virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) override {
65 mDeviceAddedCb(node);
66 }
67 virtual void onDeviceRemoved(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 [&](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 [&](std::shared_ptr<InputDeviceNode> node) {
140 tempNode = node;
141 });
142 mCallback->setDeviceRemovedCallback(
143 [&](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 [&](std::shared_ptr<InputDeviceNode> node, InputEvent& event, nsecs_t event_time) {
186 EXPECT_NEAR(expectedWhen, event_time, ms2ns(TIMING_TOLERANCE_MS));
187 EXPECT_EQ(s2ns(1), event.when);
188 EXPECT_EQ(tempFileName, node->getPath());
189 EXPECT_EQ(EV_KEY, event.type);
190 EXPECT_EQ(KEY_HOME, event.code);
191 EXPECT_EQ(0x01, event.value);
192 });
193 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName()));
194
195 StopWatch stopWatch("poll");
196 EXPECT_EQ(OK, mInputHub->poll());
197 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
198
199 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS);
200}
201
202TEST_F(InputHubTest, DISABLED_testCallbackOrder) {
203 // Create two "devices": one to receive input and the other to go away.
204 auto tempDir = std::make_unique<TempDir>();
205 auto deviceFile1 = std::unique_ptr<TempFile>(tempDir->newTempFile());
206 auto deviceFile2 = std::unique_ptr<TempFile>(tempDir->newTempFile());
207 std::string tempFileName(deviceFile2->getName());
208
209 bool inputCallbackFinished = false, deviceCallbackFinished = false;
210
211 // Setup the callback for input events. Should run before the device
212 // callback.
213 mCallback->setInputCallback(
214 [&](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t) {
215 ASSERT_FALSE(deviceCallbackFinished);
216 inputCallbackFinished = true;
217 });
218
219 // Setup the callback for device removal. Should run after the input
220 // callback.
221 mCallback->setDeviceRemovedCallback(
222 [&](std::shared_ptr<InputDeviceNode> node) {
223 ASSERT_TRUE(inputCallbackFinished)
224 << "input callback did not run before device changed callback";
225 // Make sure the correct device was removed.
226 EXPECT_EQ(tempFileName, node->getPath());
227 deviceCallbackFinished = true;
228 });
229 ASSERT_EQ(OK, mInputHub->registerDevicePath(tempDir->getName()));
230
231 auto f = delay_async(100ms,
232 [&]() {
233 // Delete the second device file first.
234 deviceFile2.reset();
235
236 // Then inject an input event into the first device.
237 struct input_event iev;
238 iev.time = { 1, 0 };
239 iev.type = EV_KEY;
240 iev.code = KEY_HOME;
241 iev.value = 0x01;
242
243 ssize_t nWrite = TEMP_FAILURE_RETRY(write(deviceFile1->getFd(), &iev, sizeof(iev)));
244
245 ASSERT_EQ(static_cast<ssize_t>(sizeof(iev)), nWrite) << "could not write to "
246 << deviceFile1->getFd() << ". errno: " << errno;
247 });
248
249 StopWatch stopWatch("poll");
250 EXPECT_EQ(OK, mInputHub->poll());
251 int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
252
253 EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS);
254 EXPECT_TRUE(inputCallbackFinished);
255 EXPECT_TRUE(deviceCallbackFinished);
256}
257
258} // namespace tests
259} // namespace android