blob: 61a149f8a1e30ea6f9657ba01a47fd587dc8e068 [file] [log] [blame]
// Copyright 2018 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 "chromeos/services/secure_channel/ble_advertiser_impl.h"
#include <memory>
#include <sstream>
#include <vector>
#include "base/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "chromeos/services/secure_channel/error_tolerant_ble_advertisement_impl.h"
#include "chromeos/services/secure_channel/fake_ble_advertiser.h"
#include "chromeos/services/secure_channel/fake_ble_service_data_helper.h"
#include "chromeos/services/secure_channel/fake_ble_synchronizer.h"
#include "chromeos/services/secure_channel/fake_error_tolerant_ble_advertisement.h"
#include "chromeos/services/secure_channel/fake_one_shot_timer.h"
#include "chromeos/services/secure_channel/fake_timer_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromeos {
namespace secure_channel {
namespace {
class FakeErrorTolerantBleAdvertisementFactory
: public ErrorTolerantBleAdvertisementImpl::Factory {
public:
FakeErrorTolerantBleAdvertisementFactory(
BleServiceDataHelper* ble_service_data_helper,
BleSynchronizerBase* ble_synchronizer_base)
: ble_service_data_helper_(ble_service_data_helper),
ble_synchronizer_base_(ble_synchronizer_base) {}
~FakeErrorTolerantBleAdvertisementFactory() override = default;
const base::Optional<DeviceIdPair>& last_created_device_id_pair() const {
return last_created_device_id_pair_;
}
base::flat_map<DeviceIdPair, FakeErrorTolerantBleAdvertisement*>&
device_id_pair_to_active_advertisement_map() {
return device_id_pair_to_active_advertisement_map_;
}
size_t num_instances_created() const { return num_instances_created_; }
private:
// ErrorTolerantBleAdvertisementImpl::Factory:
std::unique_ptr<ErrorTolerantBleAdvertisement> BuildInstance(
const DeviceIdPair& device_id_pair,
std::unique_ptr<DataWithTimestamp> advertisement_data,
BleSynchronizerBase* ble_synchronizer) override {
EXPECT_EQ(*ble_service_data_helper_->GenerateForegroundAdvertisement(
device_id_pair),
*advertisement_data);
EXPECT_EQ(ble_synchronizer_base_, ble_synchronizer);
++num_instances_created_;
auto fake_advertisement =
std::make_unique<FakeErrorTolerantBleAdvertisement>(
device_id_pair,
base::BindOnce(
&FakeErrorTolerantBleAdvertisementFactory::OnInstanceDeleted,
base::Unretained(this)));
device_id_pair_to_active_advertisement_map_[fake_advertisement
->device_id_pair()] =
fake_advertisement.get();
last_created_device_id_pair_ = fake_advertisement->device_id_pair();
return fake_advertisement;
}
void OnInstanceDeleted(const DeviceIdPair& device_id_pair) {
size_t num_deleted =
device_id_pair_to_active_advertisement_map_.erase(device_id_pair);
EXPECT_EQ(1u, num_deleted);
}
BleServiceDataHelper* ble_service_data_helper_;
BleSynchronizerBase* ble_synchronizer_base_;
base::Optional<DeviceIdPair> last_created_device_id_pair_;
base::flat_map<DeviceIdPair, FakeErrorTolerantBleAdvertisement*>
device_id_pair_to_active_advertisement_map_;
size_t num_instances_created_ = 0u;
DISALLOW_COPY_AND_ASSIGN(FakeErrorTolerantBleAdvertisementFactory);
};
const int64_t kDefaultStartTimestamp = 1337;
const int64_t kDefaultEndTimestamp = 13337;
} // namespace
class SecureChannelBleAdvertiserImplTest : public testing::Test {
protected:
SecureChannelBleAdvertiserImplTest() = default;
~SecureChannelBleAdvertiserImplTest() override = default;
// testing::Test:
void SetUp() override {
fake_delegate_ = std::make_unique<FakeBleAdvertiserDelegate>();
fake_ble_service_data_helper_ =
std::make_unique<FakeBleServiceDataHelper>();
fake_ble_synchronizer_ = std::make_unique<FakeBleSynchronizer>();
fake_timer_factory_ = std::make_unique<FakeTimerFactory>();
fake_advertisement_factory_ =
std::make_unique<FakeErrorTolerantBleAdvertisementFactory>(
fake_ble_service_data_helper_.get(), fake_ble_synchronizer_.get());
ErrorTolerantBleAdvertisementImpl::Factory::SetFactoryForTesting(
fake_advertisement_factory_.get());
test_runner_ = base::MakeRefCounted<base::TestSimpleTaskRunner>();
advertiser_ = BleAdvertiserImpl::Factory::Get()->BuildInstance(
fake_delegate_.get(), fake_ble_service_data_helper_.get(),
fake_ble_synchronizer_.get(), fake_timer_factory_.get(), test_runner_);
}
void TearDown() override {
ErrorTolerantBleAdvertisementImpl::Factory::SetFactoryForTesting(nullptr);
// Ensure that all slot ended delegate callbacks were verified.
if (highest_slot_ended_delegate_index_verified_) {
EXPECT_EQ(*highest_slot_ended_delegate_index_verified_ + 1u,
fake_delegate_->slot_ended_events().size());
} else {
EXPECT_TRUE(fake_delegate_->slot_ended_events().empty());
}
// Ensure that all failed advertisement delegate callbacks were verified.
if (highest_failed_advertisement_delegate_index_verified_) {
EXPECT_EQ(*highest_failed_advertisement_delegate_index_verified_ + 1u,
fake_delegate_->advertisement_generation_failures().size());
} else {
EXPECT_TRUE(fake_delegate_->advertisement_generation_failures().empty());
}
// Ensure that there are no more active timers/advertisements.
EXPECT_TRUE(fake_timer_factory_->id_to_active_one_shot_timer_map().empty());
EXPECT_TRUE(fake_advertisement_factory_
->device_id_pair_to_active_advertisement_map()
.empty());
}
FakeErrorTolerantBleAdvertisement* GetLastCreatedAdvertisement(
const DeviceIdPair& expected_device_id_pair) {
EXPECT_EQ(expected_device_id_pair,
*fake_advertisement_factory_->last_created_device_id_pair());
auto* fake_advertisement =
fake_advertisement_factory_
->device_id_pair_to_active_advertisement_map()
[expected_device_id_pair];
EXPECT_NE(fake_advertisement->id(), last_fetched_advertisement_id_);
last_fetched_advertisement_id_ = fake_advertisement->id();
// The advertisement should not yet be stopped.
EXPECT_FALSE(fake_advertisement->HasBeenStopped());
return fake_advertisement;
}
FakeOneShotTimer* GetLastCreatedTimer() {
EXPECT_FALSE(
fake_timer_factory_->id_for_last_created_one_shot_timer().is_empty());
auto* fake_timer =
fake_timer_factory_->id_to_active_one_shot_timer_map()
[fake_timer_factory_->id_for_last_created_one_shot_timer()];
EXPECT_NE(fake_timer->id(), last_fetched_timer_id_);
last_fetched_timer_id_ = fake_timer->id();
// The timer should have been started.
EXPECT_TRUE(fake_timer->IsRunning());
EXPECT_EQ(base::TimeDelta::FromSeconds(
BleAdvertiserImpl::kNumSecondsPerAdvertisementTimeslot),
fake_timer->GetCurrentDelay());
return fake_timer;
}
void AddAdvertisementRequest(const DeviceIdPair& request,
ConnectionPriority connection_priority,
bool should_advertisement_succeed = true) {
if (should_advertisement_succeed) {
// Generate fake service data using the two device IDs.
std::stringstream ss;
ss << request.remote_device_id() << "+" << request.local_device_id();
fake_ble_service_data_helper_->SetAdvertisement(
request,
DataWithTimestamp(ss.str() /* data */, kDefaultStartTimestamp,
kDefaultEndTimestamp));
}
advertiser_->AddAdvertisementRequest(request, connection_priority);
}
void VerifyDelegateNotifiedOnAdvertisingSlotEnded(
const DeviceIdPair& expected_request,
bool expected_replaced_by_higher_priority_advertisement,
size_t expected_index) {
const std::vector<FakeBleAdvertiserDelegate::SlotEndedEvent>&
slot_ended_events = fake_delegate_->slot_ended_events();
EXPECT_EQ(expected_index + 1, slot_ended_events.size());
EXPECT_EQ(expected_request, slot_ended_events.back().first);
EXPECT_EQ(expected_replaced_by_higher_priority_advertisement,
slot_ended_events.back().second);
highest_slot_ended_delegate_index_verified_ = expected_index;
}
void VerifyDelegateNotifiedOnFailureToGenerateAdvertisement(
const DeviceIdPair& expected_request,
size_t expected_index) {
const std::vector<DeviceIdPair>& advertisement_generation_failures =
fake_delegate_->advertisement_generation_failures();
EXPECT_EQ(expected_index + 1, advertisement_generation_failures.size());
EXPECT_EQ(expected_request, advertisement_generation_failures.back());
highest_failed_advertisement_delegate_index_verified_ = expected_index;
}
size_t GetNumAdvertisementsCreated() {
return fake_advertisement_factory_->num_instances_created();
}
size_t GetNumTimersCreated() {
return fake_timer_factory_->num_instances_created();
}
size_t GetNumSlotEndedDelegateCallbacks() {
return fake_delegate_->slot_ended_events().size();
}
size_t GetNumFailedAdvertisementDelegateCallbacks() {
return fake_delegate_->advertisement_generation_failures().size();
}
void VerifyAdvertisementStopped(
FakeErrorTolerantBleAdvertisement* advertisement,
bool should_finish_stopping) {
EXPECT_TRUE(advertisement->HasBeenStopped());
if (should_finish_stopping)
advertisement->InvokeStopCallback();
}
base::TestSimpleTaskRunner* test_runner() { return test_runner_.get(); }
BleAdvertiser* advertiser() { return advertiser_.get(); }
FakeBleServiceDataHelper* fake_ble_service_data_helper() {
return fake_ble_service_data_helper_.get();
}
private:
base::test::ScopedTaskEnvironment scoped_task_environment_;
std::unique_ptr<FakeBleAdvertiserDelegate> fake_delegate_;
std::unique_ptr<FakeBleServiceDataHelper> fake_ble_service_data_helper_;
std::unique_ptr<FakeBleSynchronizer> fake_ble_synchronizer_;
std::unique_ptr<FakeTimerFactory> fake_timer_factory_;
std::unique_ptr<FakeErrorTolerantBleAdvertisementFactory>
fake_advertisement_factory_;
base::UnguessableToken last_fetched_advertisement_id_;
base::UnguessableToken last_fetched_timer_id_;
base::Optional<size_t> highest_slot_ended_delegate_index_verified_;
base::Optional<size_t> highest_failed_advertisement_delegate_index_verified_;
scoped_refptr<base::TestSimpleTaskRunner> test_runner_;
std::unique_ptr<BleAdvertiser> advertiser_;
DISALLOW_COPY_AND_ASSIGN(SecureChannelBleAdvertiserImplTest);
};
TEST_F(SecureChannelBleAdvertiserImplTest, OneAdvertisement_TimerFires) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement;
FakeOneShotTimer* timer;
// Simulate 5 consecutive timeouts, followed by removing the advertisement.
const size_t kNumTimerFires = 5;
for (size_t i = 0; i < kNumTimerFires; ++i) {
advertisement = GetLastCreatedAdvertisement(pair);
timer = GetLastCreatedTimer();
timer->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair, false /* expected_replaced_by_higher_priority_advertisement */,
i /* expected_index */);
VerifyAdvertisementStopped(advertisement,
true /* should_finish_stopping */);
}
advertisement = GetLastCreatedAdvertisement(pair);
advertiser()->RemoveAdvertisementRequest(pair);
advertisement->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest, OneAdvertisement_UpdatePriority) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement =
GetLastCreatedAdvertisement(pair);
// No delegate callbacks yet.
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
// Updating the priority should not trigger any delegate callbacks.
advertiser()->UpdateAdvertisementRequestPriority(pair,
ConnectionPriority::kMedium);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->UpdateAdvertisementRequestPriority(pair,
ConnectionPriority::kHigh);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->RemoveAdvertisementRequest(pair);
advertisement->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest,
OneAdvertisement_AsyncAdvertisement) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement =
GetLastCreatedAdvertisement(pair);
FakeOneShotTimer* timer = GetLastCreatedTimer();
// Fire the timer and verify that the advertisement was stopped, but do not
// complete the asynchronous stopping flow.
timer->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair, false /* expected_replaced_by_higher_priority_advertisement */,
0u /* expected_index */);
VerifyAdvertisementStopped(advertisement, false /* should_finish_stopping */);
// A new timer should have been created for the next timeslot, but the
// original advertisement is still in the process of stopping.
timer = GetLastCreatedTimer();
// Simulate another timeout before the first advertisement has stopped.
timer->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair, false /* expected_replaced_by_higher_priority_advertisement */,
1u /* expected_index */);
VerifyAdvertisementStopped(advertisement, false /* should_finish_stopping */);
advertiser()->RemoveAdvertisementRequest(pair);
advertisement->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest, TwoAdvertisements_TimerFires) {
DeviceIdPair pair_1("remoteDeviceId1", "localDeviceId1");
DeviceIdPair pair_2("remoteDeviceId2", "localDeviceId2");
AddAdvertisementRequest(pair_1, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_1 =
GetLastCreatedAdvertisement(pair_1);
FakeOneShotTimer* timer_1 = GetLastCreatedTimer();
AddAdvertisementRequest(pair_2, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_2 =
GetLastCreatedAdvertisement(pair_2);
FakeOneShotTimer* timer_2 = GetLastCreatedTimer();
// Simulate 5 consecutive timeouts, followed by removing the advertisement.
const size_t kNumTimerFires = 5;
for (size_t i = 0; i < kNumTimerFires; ++i) {
timer_1->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_1, false /* expected_replaced_by_higher_priority_advertisement */,
2u * i /* expected_index */);
VerifyAdvertisementStopped(advertisement_1,
true /* should_finish_stopping */);
advertisement_1 = GetLastCreatedAdvertisement(pair_1);
timer_1 = GetLastCreatedTimer();
timer_2->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_2, false /* expected_replaced_by_higher_priority_advertisement */,
2u * i + 1u /* expected_index */);
VerifyAdvertisementStopped(advertisement_2,
true /* should_finish_stopping */);
advertisement_2 = GetLastCreatedAdvertisement(pair_2);
timer_2 = GetLastCreatedTimer();
}
advertiser()->RemoveAdvertisementRequest(pair_1);
advertisement_1->InvokeStopCallback();
advertiser()->RemoveAdvertisementRequest(pair_2);
advertisement_2->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest, TwoAdvertisements_UpdatePriority) {
DeviceIdPair pair_1("remoteDeviceId1", "localDeviceId1");
DeviceIdPair pair_2("remoteDeviceId2", "localDeviceId2");
AddAdvertisementRequest(pair_1, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_1 =
GetLastCreatedAdvertisement(pair_1);
AddAdvertisementRequest(pair_2, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_2 =
GetLastCreatedAdvertisement(pair_2);
// No delegate callbacks yet.
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
// Updating the priority should not trigger any delegate callbacks.
advertiser()->UpdateAdvertisementRequestPriority(pair_1,
ConnectionPriority::kMedium);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->UpdateAdvertisementRequestPriority(pair_1,
ConnectionPriority::kHigh);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->UpdateAdvertisementRequestPriority(pair_2,
ConnectionPriority::kMedium);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->UpdateAdvertisementRequestPriority(pair_2,
ConnectionPriority::kHigh);
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
advertiser()->RemoveAdvertisementRequest(pair_1);
advertisement_1->InvokeStopCallback();
advertiser()->RemoveAdvertisementRequest(pair_2);
advertisement_2->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest,
TwoAdvertisements_AsyncAdvertisement) {
DeviceIdPair pair_1("remoteDeviceId1", "localDeviceId1");
DeviceIdPair pair_2("remoteDeviceId2", "localDeviceId2");
AddAdvertisementRequest(pair_1, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_1 =
GetLastCreatedAdvertisement(pair_1);
FakeOneShotTimer* timer_1 = GetLastCreatedTimer();
AddAdvertisementRequest(pair_2, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_2 =
GetLastCreatedAdvertisement(pair_2);
FakeOneShotTimer* timer_2 = GetLastCreatedTimer();
// Fire the timer and verify that the advertisement was stopped, but do not
// complete the asynchronous stopping flow.
timer_1->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_1, false /* expected_replaced_by_higher_priority_advertisement */,
0u /* expected_index */);
VerifyAdvertisementStopped(advertisement_1,
false /* should_finish_stopping */);
// A new timer should have been created for the next timeslot, but the
// original advertisement is still in the process of stopping.
timer_1 = GetLastCreatedTimer();
// Same thing for pair_2.
timer_2->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_2, false /* expected_replaced_by_higher_priority_advertisement */,
1u /* expected_index */);
VerifyAdvertisementStopped(advertisement_2,
false /* should_finish_stopping */);
advertiser()->RemoveAdvertisementRequest(pair_1);
advertisement_1->InvokeStopCallback();
advertiser()->RemoveAdvertisementRequest(pair_2);
advertisement_2->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest,
ManyAdvertisements_ComprehensiveTest) {
DeviceIdPair pair_1("remoteDeviceId1", "localDeviceId1");
DeviceIdPair pair_2("remoteDeviceId2", "localDeviceId2");
DeviceIdPair pair_3("remoteDeviceId3", "localDeviceId3");
DeviceIdPair pair_4("remoteDeviceId4", "localDeviceId4");
AddAdvertisementRequest(pair_1, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_1 =
GetLastCreatedAdvertisement(pair_1);
FakeOneShotTimer* timer_1 = GetLastCreatedTimer();
AddAdvertisementRequest(pair_2, ConnectionPriority::kLow);
FakeErrorTolerantBleAdvertisement* advertisement_2 =
GetLastCreatedAdvertisement(pair_2);
FakeOneShotTimer* timer_2 = GetLastCreatedTimer();
// Status: pair_1 - low (active, slot 1)
// pair_2 - low (active, slot 2)
// Add pair_3 as a low-priority request. Since this priority is not higher
// than the two which occupy the active advertisements, this should not result
// in any change.
AddAdvertisementRequest(pair_3, ConnectionPriority::kLow);
EXPECT_EQ(2u, GetNumAdvertisementsCreated());
EXPECT_EQ(2u, GetNumTimersCreated());
EXPECT_EQ(0u, GetNumSlotEndedDelegateCallbacks());
// Now, update pair_3's priority to medium. This should trigger pair_3 to take
// pair_1's spot in the active advertisements list.
advertiser()->UpdateAdvertisementRequestPriority(pair_3,
ConnectionPriority::kMedium);
// A new timer should have been created, but no new advertisement, since
// stopping is asynchronous.
EXPECT_EQ(2u, GetNumAdvertisementsCreated());
EXPECT_EQ(3u, GetNumTimersCreated());
timer_1 = GetLastCreatedTimer();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_1, true /* expected_replaced_by_higher_priority_advertisement */,
0u /* expected_index */);
// Status: pair_1 - low
// pair_2 - low (active, slot 2)
// pair_3 - medium (active, slot 1)
// Finish stopping the advertisement which previously belonged to pair_1. This
// should cause a new advertisement for pair_3 to be created.
advertisement_1->InvokeStopCallback();
EXPECT_EQ(3u, GetNumAdvertisementsCreated());
EXPECT_EQ(3u, GetNumTimersCreated());
advertisement_1 = GetLastCreatedAdvertisement(pair_3);
// Simulate pair_2's timeslot ending; since pair_1 is also low-priority, it
// is expected to take pair_2's place.
timer_2->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_2, false /* expected_replaced_by_higher_priority_advertisement */,
1u /* expected_index */);
VerifyAdvertisementStopped(advertisement_2,
true /* should_finish_stopping */);
EXPECT_EQ(4u, GetNumAdvertisementsCreated());
EXPECT_EQ(4u, GetNumTimersCreated());
advertisement_2 = GetLastCreatedAdvertisement(pair_1);
timer_2 = GetLastCreatedTimer();
// Status: pair_1 - low (active, slot 2)
// pair_2 - low
// pair_3 - medium (active, slot 1)
// Add pair_4 as a high-priority request. This should trigger pair_4 to take
// pair_1's spot, since pair_1 is only low-priority (the other active request
// is pair_3, which has medium priority).
AddAdvertisementRequest(pair_4, ConnectionPriority::kHigh);
EXPECT_EQ(4u, GetNumAdvertisementsCreated());
EXPECT_EQ(5u, GetNumTimersCreated());
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_1, true /* expected_replaced_by_higher_priority_advertisement */,
2u /* expected_index */);
timer_2 = GetLastCreatedTimer();
advertisement_2->InvokeStopCallback();
EXPECT_EQ(5u, GetNumAdvertisementsCreated());
advertisement_2 = GetLastCreatedAdvertisement(pair_4);
// Status: pair_1 - low
// pair_2 - low
// pair_3 - medium (active, slot 1)
// pair_4 - high (active, slot 2)
// Update pair_1's priority to medium. Since the two active requests (pair_3
// with medium priority and pair_4 with high priority) are both >= pair_1's
// new priority, this should not cause any changes.
advertiser()->UpdateAdvertisementRequestPriority(pair_1,
ConnectionPriority::kMedium);
EXPECT_EQ(5u, GetNumAdvertisementsCreated());
EXPECT_EQ(5u, GetNumTimersCreated());
EXPECT_EQ(3u, GetNumSlotEndedDelegateCallbacks());
// Status: pair_1 - medium
// pair_2 - low
// pair_3 - medium (active, slot 1)
// pair_4 - high (active, slot 2)
// Remove pair_4. This should trigger pair_1 to take its place. Note that the
// delegate is not triggered, since the request was removed by the client.
advertiser()->RemoveAdvertisementRequest(pair_4);
EXPECT_EQ(5u, GetNumAdvertisementsCreated());
EXPECT_EQ(6u, GetNumTimersCreated());
EXPECT_EQ(3u, GetNumSlotEndedDelegateCallbacks());
timer_2 = GetLastCreatedTimer();
advertisement_2->InvokeStopCallback();
EXPECT_EQ(6u, GetNumAdvertisementsCreated());
advertisement_2 = GetLastCreatedAdvertisement(pair_1);
// Status: pair_1 - medium (active, slot 2)
// pair_2 - low
// pair_3 - medium (active, slot 1)
// Update pair_3's priority to high; this should not cause any changes.
advertiser()->UpdateAdvertisementRequestPriority(pair_3,
ConnectionPriority::kHigh);
EXPECT_EQ(6u, GetNumAdvertisementsCreated());
EXPECT_EQ(6u, GetNumTimersCreated());
EXPECT_EQ(3u, GetNumSlotEndedDelegateCallbacks());
// Status: pair_1 - medium (active, slot 2)
// pair_2 - low
// pair_3 - high (active, slot 1)
// Simulate pair_1's timeslot ending; since there are not pending requets with
// a medium-or-higher priority, pair_1 should start up again.
timer_2->Fire();
VerifyDelegateNotifiedOnAdvertisingSlotEnded(
pair_1, false /* expected_replaced_by_higher_priority_advertisement */,
3u /* expected_index */);
VerifyAdvertisementStopped(advertisement_2,
true /* should_finish_stopping */);
EXPECT_EQ(7u, GetNumAdvertisementsCreated());
EXPECT_EQ(7u, GetNumTimersCreated());
advertisement_2 = GetLastCreatedAdvertisement(pair_1);
timer_2 = GetLastCreatedTimer();
// Status: pair_1 - medium (active, slot 2)
// pair_2 - low
// pair_3 - high (active, slot 1)
// Remove pair_3. This should trigger pair_2 to take its place.
advertiser()->RemoveAdvertisementRequest(pair_3);
EXPECT_EQ(7u, GetNumAdvertisementsCreated());
EXPECT_EQ(8u, GetNumTimersCreated());
EXPECT_EQ(4u, GetNumSlotEndedDelegateCallbacks());
timer_1 = GetLastCreatedTimer();
advertisement_1->InvokeStopCallback();
EXPECT_EQ(8u, GetNumAdvertisementsCreated());
advertisement_1 = GetLastCreatedAdvertisement(pair_2);
// Status: pair_1 - medium (active, slot 2)
// pair_2 - low (active, slot 1)
// Remove pair_2. Since only pair_1 remains (and it already has an associated
// timer and advertisement), no new timers/advertisements should be created.
advertiser()->RemoveAdvertisementRequest(pair_2);
EXPECT_EQ(8u, GetNumAdvertisementsCreated());
EXPECT_EQ(8u, GetNumTimersCreated());
EXPECT_EQ(4u, GetNumSlotEndedDelegateCallbacks());
advertisement_1->InvokeStopCallback();
EXPECT_EQ(8u, GetNumAdvertisementsCreated());
// Status: pair_1 - medium (active, slot 2)
// Remove pair_1. Nothing else remains.
advertiser()->RemoveAdvertisementRequest(pair_1);
EXPECT_EQ(8u, GetNumAdvertisementsCreated());
EXPECT_EQ(8u, GetNumTimersCreated());
advertisement_2->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest, EdgeCases) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
// Cannot update or remove an advertisement which was not added.
EXPECT_DCHECK_DEATH(advertiser()->UpdateAdvertisementRequestPriority(
pair, ConnectionPriority::kMedium));
EXPECT_DCHECK_DEATH(advertiser()->RemoveAdvertisementRequest(pair));
}
TEST_F(SecureChannelBleAdvertiserImplTest, FailToGenerateAdvertisement_Simple) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow,
false /* should_advertisement_succeed */);
test_runner()->RunUntilIdle();
EXPECT_EQ(0u, GetNumAdvertisementsCreated());
EXPECT_EQ(1u, GetNumTimersCreated());
EXPECT_EQ(1u, GetNumFailedAdvertisementDelegateCallbacks());
VerifyDelegateNotifiedOnFailureToGenerateAdvertisement(
pair, 0u /* expected_index */);
}
TEST_F(SecureChannelBleAdvertiserImplTest,
FailToGenerateAdvertisement_RerequestBeforeCallbackExecutes) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow,
false /* should_advertisement_succeed */);
AddAdvertisementRequest(pair, ConnectionPriority::kLow,
true /* should_advertisement_succeed */);
FakeErrorTolerantBleAdvertisement* advertisement =
GetLastCreatedAdvertisement(pair);
// Since another (valid) AddAdvertisementRequest() executes before the failure
// delegate callback could go through, the delegate is never called.
test_runner()->RunUntilIdle();
EXPECT_EQ(1u, GetNumAdvertisementsCreated());
EXPECT_EQ(2u, GetNumTimersCreated());
EXPECT_EQ(0u, GetNumFailedAdvertisementDelegateCallbacks());
advertiser()->RemoveAdvertisementRequest(pair);
advertisement->InvokeStopCallback();
}
TEST_F(SecureChannelBleAdvertiserImplTest,
FailToGenerateAdvertisement_UpdateBeforeCallbackExecutes) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow,
false /* should_advertisement_succeed */);
advertiser()->UpdateAdvertisementRequestPriority(pair,
ConnectionPriority::kMedium);
// Should not DCHECK since UpdateAdvertisementRequestPriority() should have
// been a no-op.
advertiser()->RemoveAdvertisementRequest(pair);
// Should not notify the delegate since RemoveAdvertisementRequest() was
// called.
test_runner()->RunUntilIdle();
EXPECT_EQ(0u, GetNumAdvertisementsCreated());
EXPECT_EQ(1u, GetNumTimersCreated());
EXPECT_EQ(0u, GetNumFailedAdvertisementDelegateCallbacks());
}
TEST_F(SecureChannelBleAdvertiserImplTest,
FailToGenerateAdvertisement_RemoveAgainBeforeCallbackExecutes) {
DeviceIdPair pair("remoteDeviceId", "localDeviceId");
AddAdvertisementRequest(pair, ConnectionPriority::kLow,
false /* should_advertisement_succeed */);
advertiser()->RemoveAdvertisementRequest(pair);
// Should not notify the delegate since RemoveAdvertisementRequest() was
// called.
test_runner()->RunUntilIdle();
EXPECT_EQ(0u, GetNumAdvertisementsCreated());
EXPECT_EQ(1u, GetNumTimersCreated());
EXPECT_EQ(0u, GetNumFailedAdvertisementDelegateCallbacks());
}
} // namespace secure_channel
} // namespace chromeos