OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <list> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "base/run_loop.h" | |
10 #include "base/test/test_io_thread.h" | |
11 #include "device/hid/hid_connection.h" | |
12 #include "device/hid/hid_device_filter.h" | |
13 #include "device/test/test_device_client.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 #include "u2f_apdu_command.h" | |
16 #include "u2f_apdu_response.h" | |
17 #include "u2f_hid_device.h" | |
18 | |
19 namespace device { | |
20 | |
21 class U2fDeviceEnumerate { | |
22 public: | |
23 U2fDeviceEnumerate() | |
24 : closure_(), | |
25 callback_(base::Bind(&U2fDeviceEnumerate::ReceivedCallback, | |
26 base::Unretained(this))), | |
27 run_loop_() {} | |
28 ~U2fDeviceEnumerate() {} | |
29 | |
30 void ReceivedCallback( | |
31 const std::vector<scoped_refptr<HidDeviceInfo>>& devices) { | |
32 std::list<std::unique_ptr<U2fHidDevice>> u2f_devices; | |
33 filter_.SetUsagePage(0xf1d0); | |
34 for (auto device_info : devices) { | |
35 if (filter_.Matches(device_info)) | |
36 u2f_devices.push_front(base::MakeUnique<U2fHidDevice>(device_info)); | |
37 } | |
38 devices_ = std::move(u2f_devices); | |
39 closure_.Run(); | |
40 } | |
41 | |
42 std::list<std::unique_ptr<U2fHidDevice>>& WaitForCallback() { | |
43 closure_ = run_loop_.QuitClosure(); | |
44 run_loop_.Run(); | |
45 return devices_; | |
46 } | |
47 | |
48 const HidService::GetDevicesCallback& callback() { return callback_; } | |
49 | |
50 private: | |
51 HidDeviceFilter filter_; | |
52 std::list<std::unique_ptr<U2fHidDevice>> devices_; | |
53 base::Closure closure_; | |
54 HidService::GetDevicesCallback callback_; | |
55 base::RunLoop run_loop_; | |
56 }; | |
57 | |
58 class TestVersionCallback { | |
59 public: | |
60 TestVersionCallback() | |
61 : closure_(), | |
62 callback_(base::Bind(&TestVersionCallback::ReceivedCallback, | |
63 base::Unretained(this))), | |
64 run_loop_() {} | |
65 ~TestVersionCallback() {} | |
66 | |
67 void ReceivedCallback(bool success, U2fDevice::ProtocolVersion version) { | |
68 version_ = version; | |
69 closure_.Run(); | |
70 } | |
71 | |
72 U2fDevice::ProtocolVersion WaitForCallback() { | |
73 closure_ = run_loop_.QuitClosure(); | |
74 run_loop_.Run(); | |
75 return version_; | |
76 } | |
77 | |
78 const U2fDevice::VersionCallback& callback() { return callback_; } | |
79 | |
80 private: | |
81 U2fDevice::ProtocolVersion version_; | |
82 base::Closure closure_; | |
83 U2fDevice::VersionCallback callback_; | |
84 base::RunLoop run_loop_; | |
85 }; | |
86 | |
87 class TestDeviceCallback { | |
88 public: | |
89 TestDeviceCallback() | |
90 : closure_(), | |
91 callback_(base::Bind(&TestDeviceCallback::ReceivedCallback, | |
92 base::Unretained(this))), | |
93 run_loop_() {} | |
94 ~TestDeviceCallback() {} | |
95 | |
96 void ReceivedCallback(bool success, scoped_refptr<U2fApduResponse> response) { | |
97 response_ = response; | |
98 closure_.Run(); | |
99 } | |
100 | |
101 scoped_refptr<U2fApduResponse> WaitForCallback() { | |
102 closure_ = run_loop_.QuitClosure(); | |
103 run_loop_.Run(); | |
104 return response_; | |
105 } | |
106 | |
107 const U2fDevice::DeviceCallback& callback() { return callback_; } | |
108 | |
109 private: | |
110 scoped_refptr<U2fApduResponse> response_; | |
111 base::Closure closure_; | |
112 U2fDevice::DeviceCallback callback_; | |
113 base::RunLoop run_loop_; | |
114 }; | |
115 | |
116 class TestFailureCallback { | |
Reilly Grant (use Gerrit)
2017/03/09 00:37:03
There's a simpler way to do this:
void ResponseCa
Casey Piper
2017/03/10 00:32:10
Done.
| |
117 public: | |
118 TestFailureCallback() | |
119 : callback_(base::Bind(&TestFailureCallback::ReceivedCallback, | |
120 base::Unretained(this))) {} | |
121 ~TestFailureCallback() {} | |
122 | |
123 void ReceivedCallback(bool success, scoped_refptr<U2fApduResponse> response) { | |
124 response_ = response; | |
125 } | |
126 | |
127 scoped_refptr<U2fApduResponse> GetCallbackResult() { return response_; } | |
128 | |
129 const U2fDevice::DeviceCallback& callback() { return callback_; } | |
130 | |
131 private: | |
132 scoped_refptr<U2fApduResponse> response_; | |
133 U2fDevice::DeviceCallback callback_; | |
134 }; | |
135 | |
136 class U2fHidDeviceTest : public testing::Test { | |
137 public: | |
138 void SetUp() override { | |
139 if (!U2fHidDevice::IsTestEnabled()) | |
140 return; | |
141 message_loop_.reset(new base::MessageLoopForUI()); | |
142 io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart)); | |
143 device_client_.reset( | |
144 new device::TestDeviceClient(io_thread_->task_runner())); | |
145 } | |
146 | |
147 protected: | |
148 std::unique_ptr<base::MessageLoopForUI> message_loop_; | |
149 std::unique_ptr<base::TestIOThread> io_thread_; | |
150 std::unique_ptr<device::TestDeviceClient> device_client_; | |
151 }; | |
152 | |
153 TEST_F(U2fHidDeviceTest, TestHidDeviceVersion) { | |
154 if (!U2fHidDevice::IsTestEnabled()) | |
155 return; | |
156 | |
157 U2fDeviceEnumerate callback; | |
158 HidService* hid_service = DeviceClient::Get()->GetHidService(); | |
159 hid_service->GetDevices(callback.callback()); | |
160 std::list<std::unique_ptr<U2fHidDevice>>& u2f_devices = | |
161 callback.WaitForCallback(); | |
162 | |
163 for (auto& device : u2f_devices) { | |
164 TestVersionCallback vc; | |
165 device->Version(vc.callback()); | |
166 U2fDevice::ProtocolVersion version = vc.WaitForCallback(); | |
167 EXPECT_EQ(version, U2fDevice::ProtocolVersion::U2F_V2); | |
168 } | |
169 }; | |
170 | |
171 TEST_F(U2fHidDeviceTest, TestMultipleRequests) { | |
172 if (!U2fHidDevice::IsTestEnabled()) | |
173 return; | |
174 | |
175 U2fDeviceEnumerate callback; | |
176 HidService* hid_service = DeviceClient::Get()->GetHidService(); | |
177 hid_service->GetDevices(callback.callback()); | |
178 std::list<std::unique_ptr<U2fHidDevice>>& u2f_devices = | |
179 callback.WaitForCallback(); | |
180 | |
181 for (auto& device : u2f_devices) { | |
182 TestVersionCallback vc; | |
183 TestVersionCallback vc2; | |
184 // Call version twice to check message queueing | |
185 device->Version(vc.callback()); | |
186 device->Version(vc2.callback()); | |
187 U2fDevice::ProtocolVersion version = vc.WaitForCallback(); | |
188 EXPECT_EQ(version, U2fDevice::ProtocolVersion::U2F_V2); | |
189 version = vc2.WaitForCallback(); | |
190 EXPECT_EQ(version, U2fDevice::ProtocolVersion::U2F_V2); | |
191 } | |
192 }; | |
193 | |
194 TEST_F(U2fHidDeviceTest, TestConnectionFailure) { | |
195 if (!U2fHidDevice::IsTestEnabled()) | |
196 return; | |
197 | |
198 U2fDeviceEnumerate callback; | |
199 HidService* hid_service = DeviceClient::Get()->GetHidService(); | |
Reilly Grant (use Gerrit)
2017/03/09 00:37:03
Since this logic is handling failure cases can you
Casey Piper
2017/03/10 00:32:10
Switched to using mock device client and device co
| |
200 hid_service->GetDevices(callback.callback()); | |
201 std::list<std::unique_ptr<U2fHidDevice>>& u2f_devices = | |
202 callback.WaitForCallback(); | |
203 | |
204 if (!u2f_devices.empty()) { | |
205 auto& device = u2f_devices.front(); | |
206 // Write a successful message to put device in IDLE state | |
207 TestDeviceCallback cb0; | |
208 device->DeviceTransact(U2fApduCommand::CreateVersion(), cb0.callback()); | |
209 scoped_refptr<U2fApduResponse> response = cb0.WaitForCallback(); | |
210 EXPECT_NE(nullptr, response); | |
211 // Manually delete connection | |
212 device->connection_->Close(); | |
213 device->connection_ = nullptr; | |
214 // Add pending transactions manually and ensure they are processed | |
215 TestFailureCallback cb1; | |
216 TestFailureCallback cb2; | |
217 device->pending_transactions_.push_back( | |
218 {U2fApduCommand::CreateVersion(), cb1.callback()}); | |
219 device->pending_transactions_.push_back( | |
220 {U2fApduCommand::CreateVersion(), cb2.callback()}); | |
221 TestFailureCallback cb3; | |
222 device->DeviceTransact(U2fApduCommand::CreateVersion(), cb3.callback()); | |
223 response = cb3.GetCallbackResult(); | |
224 EXPECT_EQ(nullptr, response); | |
225 response = cb1.GetCallbackResult(); | |
226 EXPECT_EQ(nullptr, response); | |
227 response = cb2.GetCallbackResult(); | |
228 EXPECT_EQ(nullptr, response); | |
229 } | |
230 }; | |
231 | |
232 TEST_F(U2fHidDeviceTest, TestDeviceError) { | |
233 if (!U2fHidDevice::IsTestEnabled()) | |
234 return; | |
235 | |
236 U2fDeviceEnumerate callback; | |
237 HidService* hid_service = DeviceClient::Get()->GetHidService(); | |
238 hid_service->GetDevices(callback.callback()); | |
239 std::list<std::unique_ptr<U2fHidDevice>>& u2f_devices = | |
240 callback.WaitForCallback(); | |
241 | |
242 if (!u2f_devices.empty()) { | |
243 auto& device = u2f_devices.front(); | |
244 // Write a successful message to put device in IDLE state | |
245 TestDeviceCallback cb0; | |
246 device->DeviceTransact(U2fApduCommand::CreateVersion(), cb0.callback()); | |
247 scoped_refptr<U2fApduResponse> response = cb0.WaitForCallback(); | |
248 EXPECT_NE(nullptr, response); | |
249 // Put the device into an error state | |
250 device->state_ = U2fHidDevice::State::DEVICE_ERROR; | |
251 // Add pending transactions manually and ensure they are processed | |
252 TestFailureCallback cb1; | |
253 TestFailureCallback cb2; | |
254 device->pending_transactions_.push_back( | |
255 {U2fApduCommand::CreateVersion(), cb1.callback()}); | |
256 device->pending_transactions_.push_back( | |
257 {U2fApduCommand::CreateVersion(), cb2.callback()}); | |
258 TestFailureCallback cb3; | |
259 device->DeviceTransact(U2fApduCommand::CreateVersion(), cb3.callback()); | |
260 response = cb3.GetCallbackResult(); | |
261 EXPECT_EQ(nullptr, response); | |
262 response = cb1.GetCallbackResult(); | |
263 EXPECT_EQ(nullptr, response); | |
264 response = cb2.GetCallbackResult(); | |
265 EXPECT_EQ(nullptr, response); | |
266 } | |
267 }; | |
268 | |
269 } // namespace device | |
OLD | NEW |