blob: f4d092bba23a7312f5ac8ad5a62b52f2f89fe4f6 [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 "device/fido/fido_device_discovery.h"
#include <utility>
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "device/fido/fido_authenticator.h"
#include "device/fido/fido_device_authenticator.h"
#include "device/fido/fido_test_data.h"
#include "device/fido/mock_fido_device.h"
#include "device/fido/mock_fido_discovery_observer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace device {
namespace {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::UnorderedElementsAre;
// A minimal implementation of FidoDeviceDiscovery that is no longer abstract.
class ConcreteFidoDiscovery : public FidoDeviceDiscovery {
public:
explicit ConcreteFidoDiscovery(FidoTransportProtocol transport)
: FidoDeviceDiscovery(transport) {}
~ConcreteFidoDiscovery() override = default;
MOCK_METHOD0(StartInternal, void());
using FidoDeviceDiscovery::AddDevice;
using FidoDeviceDiscovery::NotifyDiscoveryStarted;
using FidoDeviceDiscovery::RemoveDevice;
private:
DISALLOW_COPY_AND_ASSIGN(ConcreteFidoDiscovery);
};
} // namespace
TEST(FidoDiscoveryTest, TestAddAndRemoveObserver) {
ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
MockFidoDiscoveryObserver observer;
EXPECT_EQ(nullptr, discovery.observer());
discovery.set_observer(&observer);
EXPECT_EQ(&observer, discovery.observer());
discovery.set_observer(nullptr);
EXPECT_EQ(nullptr, discovery.observer());
}
TEST(FidoDiscoveryTest, TestNotificationsOnSuccessfulStart) {
base::test::ScopedTaskEnvironment scoped_task_environment_;
ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
MockFidoDiscoveryObserver observer;
discovery.set_observer(&observer);
EXPECT_FALSE(discovery.is_start_requested());
EXPECT_FALSE(discovery.is_running());
EXPECT_CALL(discovery, StartInternal());
discovery.Start();
scoped_task_environment_.RunUntilIdle();
EXPECT_TRUE(discovery.is_start_requested());
EXPECT_FALSE(discovery.is_running());
::testing::Mock::VerifyAndClearExpectations(&discovery);
EXPECT_CALL(observer, DiscoveryStarted(&discovery, true));
discovery.NotifyDiscoveryStarted(true);
EXPECT_TRUE(discovery.is_start_requested());
EXPECT_TRUE(discovery.is_running());
::testing::Mock::VerifyAndClearExpectations(&observer);
}
TEST(FidoDiscoveryTest, TestNotificationsOnFailedStart) {
base::test::ScopedTaskEnvironment scoped_task_environment_;
ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
MockFidoDiscoveryObserver observer;
discovery.set_observer(&observer);
discovery.Start();
scoped_task_environment_.RunUntilIdle();
EXPECT_CALL(observer, DiscoveryStarted(&discovery, false));
discovery.NotifyDiscoveryStarted(false);
EXPECT_TRUE(discovery.is_start_requested());
EXPECT_FALSE(discovery.is_running());
::testing::Mock::VerifyAndClearExpectations(&observer);
}
TEST(FidoDiscoveryTest, TestAddRemoveDevices) {
base::test::ScopedTaskEnvironment scoped_task_environment_;
ConcreteFidoDiscovery discovery(FidoTransportProtocol::kBluetoothLowEnergy);
MockFidoDiscoveryObserver observer;
discovery.set_observer(&observer);
discovery.Start();
// Expect successful insertion.
auto device0 = std::make_unique<MockFidoDevice>();
auto* device0_raw = device0.get();
FidoAuthenticator* authenticator0 = nullptr;
base::RunLoop device0_done;
EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _))
.WillOnce(DoAll(SaveArg<1>(&authenticator0),
testing::InvokeWithoutArgs(
[&device0_done]() { device0_done.Quit(); })));
EXPECT_CALL(*device0, GetId()).WillRepeatedly(Return("device0"));
EXPECT_TRUE(discovery.AddDevice(std::move(device0)));
EXPECT_EQ("device0", authenticator0->GetId());
EXPECT_EQ(device0_raw,
static_cast<FidoDeviceAuthenticator*>(authenticator0)->device());
device0_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
// Expect successful insertion.
base::RunLoop device1_done;
auto device1 = std::make_unique<MockFidoDevice>();
auto* device1_raw = device1.get();
FidoAuthenticator* authenticator1 = nullptr;
EXPECT_CALL(observer, AuthenticatorAdded(&discovery, _))
.WillOnce(DoAll(SaveArg<1>(&authenticator1),
testing::InvokeWithoutArgs(
[&device1_done]() { device1_done.Quit(); })));
EXPECT_CALL(*device1, GetId()).WillRepeatedly(Return("device1"));
EXPECT_TRUE(discovery.AddDevice(std::move(device1)));
EXPECT_EQ("device1", authenticator1->GetId());
EXPECT_EQ(device1_raw,
static_cast<FidoDeviceAuthenticator*>(authenticator1)->device());
device1_done.Run();
::testing::Mock::VerifyAndClearExpectations(&observer);
// Inserting a device with an already present id should be prevented.
auto device1_dup = std::make_unique<MockFidoDevice>();
EXPECT_CALL(observer, AuthenticatorAdded(_, _)).Times(0);
EXPECT_CALL(*device1_dup, GetId()).WillOnce(Return("device1"));
EXPECT_FALSE(discovery.AddDevice(std::move(device1_dup)));
::testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_EQ(authenticator0, discovery.GetAuthenticatorForTesting("device0"));
EXPECT_EQ(authenticator1, discovery.GetAuthenticatorForTesting("device1"));
EXPECT_THAT(discovery.GetAuthenticatorsForTesting(),
UnorderedElementsAre(authenticator0, authenticator1));
const FidoDeviceDiscovery& const_discovery = discovery;
EXPECT_THAT(const_discovery.GetAuthenticatorsForTesting(),
UnorderedElementsAre(authenticator0, authenticator1));
// Trying to remove a non-present device should fail.
EXPECT_CALL(observer, AuthenticatorRemoved(_, _)).Times(0);
EXPECT_FALSE(discovery.RemoveDevice("device2"));
::testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, authenticator1));
EXPECT_TRUE(discovery.RemoveDevice("device1"));
::testing::Mock::VerifyAndClearExpectations(&observer);
EXPECT_CALL(observer, AuthenticatorRemoved(&discovery, authenticator0));
EXPECT_TRUE(discovery.RemoveDevice("device0"));
::testing::Mock::VerifyAndClearExpectations(&observer);
}
} // namespace device