blob: f2967b96a0f455610a077c9059533da12a4a9819 [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
Tim Kilbournc929d252015-04-29 13:50:17 -070044using InputCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)>;
45using DeviceCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&)>;
Tim Kilbourn73475a42015-02-13 10:35:20 -080046
Tim Kilbournc929d252015-04-29 13:50:17 -070047static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){};
48static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){};
Tim Kilbourn73475a42015-02-13 10:35:20 -080049
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
Tim Kilbournc929d252015-04-29 13:50:17 -070060 virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
Tim Kilbourn73475a42015-02-13 10:35:20 -080061 nsecs_t event_time) override {
62 mInputCb(node, event, event_time);
63 }
Tim Kilbournc929d252015-04-29 13:50:17 -070064 virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override {
Tim Kilbourn73475a42015-02-13 10:35:20 -080065 mDeviceAddedCb(node);
66 }
Tim Kilbournc929d252015-04-29 13:50:17 -070067 virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override {
Tim Kilbourn73475a42015-02-13 10:35:20 -080068 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(
Tim Kilbournc929d252015-04-29 13:50:17 -0700104 [&](const std::shared_ptr<InputDeviceNode>& node) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800105 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(
Tim Kilbournc929d252015-04-29 13:50:17 -0700139 [&](const std::shared_ptr<InputDeviceNode>& node) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800140 tempNode = node;
141 });
142 mCallback->setDeviceRemovedCallback(
Tim Kilbournc929d252015-04-29 13:50:17 -0700143 [&](const std::shared_ptr<InputDeviceNode>& node) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800144 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(
Tim Kilbournc929d252015-04-29 13:50:17 -0700185 [&](const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
186 nsecs_t event_time) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800187 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(
Tim Kilbournc929d252015-04-29 13:50:17 -0700215 [&](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800216 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(
Tim Kilbournc929d252015-04-29 13:50:17 -0700223 [&](const std::shared_ptr<InputDeviceNode>& node) {
Tim Kilbourn73475a42015-02-13 10:35:20 -0800224 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