blob: 16b6e5ba093213decea53f5c1511ba141f9a2744 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <list>
#include <string>
#include <utility>
#include "base/test/scoped_task_environment.h"
#include "device/u2f/mock_u2f_device.h"
#include "device/u2f/mock_u2f_discovery.h"
#include "device/u2f/u2f_request.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace device {
namespace {
constexpr char kTestRelyingPartyId[] = "google.com";
class FakeU2fRequest : public U2fRequest {
public:
explicit FakeU2fRequest(std::string relying_party_id)
: U2fRequest(std::move(relying_party_id),
nullptr /* connector */,
base::flat_set<U2fTransportProtocol>()) {}
~FakeU2fRequest() override = default;
void TryDevice() override {
// Do nothing.
}
};
// Convenience functions for setting one and two mock discoveries, respectively.
MockU2fDiscovery* SetMockDiscovery(
U2fRequest* request,
std::unique_ptr<MockU2fDiscovery> discovery) {
auto* raw_discovery = discovery.get();
std::vector<std::unique_ptr<U2fDiscovery>> discoveries;
discoveries.push_back(std::move(discovery));
request->SetDiscoveriesForTesting(std::move(discoveries));
return raw_discovery;
}
std::pair<MockU2fDiscovery*, MockU2fDiscovery*> SetMockDiscoveries(
U2fRequest* request,
std::unique_ptr<MockU2fDiscovery> discovery_1,
std::unique_ptr<MockU2fDiscovery> discovery_2) {
auto* raw_discovery_1 = discovery_1.get();
auto* raw_discovery_2 = discovery_2.get();
std::vector<std::unique_ptr<U2fDiscovery>> discoveries;
discoveries.push_back(std::move(discovery_1));
discoveries.push_back(std::move(discovery_2));
request->SetDiscoveriesForTesting(std::move(discoveries));
return {raw_discovery_1, raw_discovery_2};
}
} // namespace
class U2fRequestTest : public testing::Test {
protected:
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
};
TEST_F(U2fRequestTest, TestIterateDevice) {
FakeU2fRequest request(kTestRelyingPartyId);
auto* discovery =
SetMockDiscovery(&request, std::make_unique<MockU2fDiscovery>());
auto device0 = std::make_unique<MockU2fDevice>();
auto device1 = std::make_unique<MockU2fDevice>();
EXPECT_CALL(*device0, GetId()).WillRepeatedly(testing::Return("device0"));
EXPECT_CALL(*device1, GetId()).WillRepeatedly(testing::Return("device1"));
// Add two U2F devices
discovery->AddDevice(std::move(device0));
discovery->AddDevice(std::move(device1));
// Move first device to current
request.IterateDevice();
ASSERT_NE(nullptr, request.current_device_);
EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
// Move second device to current, first to attempted
request.IterateDevice();
ASSERT_NE(nullptr, request.current_device_);
EXPECT_EQ(static_cast<size_t>(1), request.attempted_devices_.size());
// Move second device from current to attempted, move attempted to devices as
// all devices have been attempted
request.IterateDevice();
ASSERT_EQ(nullptr, request.current_device_);
EXPECT_EQ(static_cast<size_t>(2), request.devices_.size());
EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size());
// Moving attempted devices results in a delayed retry, after which the first
// device will be tried again. Check for the expected behavior here.
auto* mock_device = static_cast<MockU2fDevice*>(request.devices_.front());
EXPECT_CALL(*mock_device, TryWinkRef(_));
scoped_task_environment_.FastForwardUntilNoTasksRemain();
EXPECT_EQ(mock_device, request.current_device_);
EXPECT_EQ(static_cast<size_t>(1), request.devices_.size());
EXPECT_EQ(static_cast<size_t>(0), request.attempted_devices_.size());
}
TEST_F(U2fRequestTest, TestBasicMachine) {
FakeU2fRequest request(kTestRelyingPartyId);
auto* discovery =
SetMockDiscovery(&request, std::make_unique<MockU2fDiscovery>());
EXPECT_CALL(*discovery, Start())
.WillOnce(testing::Invoke(discovery, &MockU2fDiscovery::StartSuccess));
request.Start();
// Add one U2F device
auto device = std::make_unique<MockU2fDevice>();
EXPECT_CALL(*device, GetId());
EXPECT_CALL(*device, TryWinkRef(_))
.WillOnce(testing::Invoke(MockU2fDevice::WinkDoNothing));
discovery->AddDevice(std::move(device));
EXPECT_EQ(U2fRequest::State::BUSY, request.state_);
}
TEST_F(U2fRequestTest, TestAlreadyPresentDevice) {
auto discovery = std::make_unique<MockU2fDiscovery>();
auto device = std::make_unique<MockU2fDevice>();
EXPECT_CALL(*device, GetId());
discovery->AddDevice(std::move(device));
FakeU2fRequest request(kTestRelyingPartyId);
EXPECT_CALL(*discovery, Start())
.WillOnce(
testing::Invoke(discovery.get(), &MockU2fDiscovery::StartSuccess));
SetMockDiscovery(&request, std::move(discovery));
request.Start();
EXPECT_NE(nullptr, request.current_device_);
}
TEST_F(U2fRequestTest, TestMultipleDiscoveries) {
// Create a fake request with two different discoveries that both start up
// successfully.
FakeU2fRequest request(kTestRelyingPartyId);
MockU2fDiscovery* discoveries[2];
std::tie(discoveries[0], discoveries[1]) =
SetMockDiscoveries(&request, std::make_unique<MockU2fDiscovery>(),
std::make_unique<MockU2fDiscovery>());
EXPECT_CALL(*discoveries[0], Start())
.WillOnce(
testing::Invoke(discoveries[0], &MockU2fDiscovery::StartSuccess));
EXPECT_CALL(*discoveries[1], Start())
.WillOnce(
testing::Invoke(discoveries[1], &MockU2fDiscovery::StartSuccess));
request.Start();
// Let each discovery find a device.
auto device_1 = std::make_unique<MockU2fDevice>();
auto device_2 = std::make_unique<MockU2fDevice>();
EXPECT_CALL(*device_1, GetId()).WillRepeatedly(testing::Return("device_1"));
EXPECT_CALL(*device_2, GetId()).WillRepeatedly(testing::Return("device_2"));
auto* device_1_ptr = device_1.get();
auto* device_2_ptr = device_2.get();
discoveries[0]->AddDevice(std::move(device_1));
discoveries[1]->AddDevice(std::move(device_2));
// Iterate through the devices and make sure they are considered in the same
// order as they were added.
EXPECT_EQ(device_1_ptr, request.current_device_);
request.IterateDevice();
EXPECT_EQ(device_2_ptr, request.current_device_);
request.IterateDevice();
EXPECT_EQ(nullptr, request.current_device_);
EXPECT_EQ(2u, request.devices_.size());
// Add a third device.
auto device_3 = std::make_unique<MockU2fDevice>();
EXPECT_CALL(*device_3, GetId()).WillRepeatedly(testing::Return("device_3"));
auto* device_3_ptr = device_3.get();
discoveries[0]->AddDevice(std::move(device_3));
// Exhaust the timeout and remove the first two devices, making sure the just
// added one is the only device considered.
scoped_task_environment_.FastForwardUntilNoTasksRemain();
discoveries[1]->RemoveDevice("device_2");
discoveries[0]->RemoveDevice("device_1");
EXPECT_EQ(device_3_ptr, request.current_device_);
// Finally remove the last remaining device.
discoveries[0]->RemoveDevice("device_3");
EXPECT_EQ(nullptr, request.current_device_);
}
} // namespace device