blob: d011ff617e501c22cb6bd08c705b66286a3bc578 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "database.h"
#include <zircon/assert.h>
#include "gtest/gtest.h"
namespace btlib {
namespace att {
namespace {
constexpr Handle kTestRangeStart = 1;
constexpr Handle kTestRangeEnd = 10;
constexpr common::UUID kTestType1((uint16_t)1);
constexpr common::UUID kTestType2((uint16_t)2);
constexpr common::UUID kTestType3((uint16_t)3);
// Values with different lengths
const auto kTestValue1 = common::CreateStaticByteBuffer('x', 'x');
const auto kTestValue2 = common::CreateStaticByteBuffer('x', 'x', 'x');
// Returns the handles of each attribute visited by advancing |iter| until the
// end.
std::vector<Handle> IterHandles(Database::Iterator* iter) {
ZX_DEBUG_ASSERT(iter);
std::vector<Handle> handles;
for (; !iter->AtEnd(); iter->Advance()) {
handles.push_back(iter->get()->handle());
}
return handles;
}
TEST(ATT_DatabaseTest, NewGroupingWhileEmptyError) {
constexpr size_t kTooLarge = kTestRangeEnd - kTestRangeStart + 1;
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
EXPECT_FALSE(db->NewGrouping(kTestType1, kTooLarge, kTestValue1));
}
TEST(ATT_DatabaseTest, NewGroupingWhileEmptyFill) {
constexpr size_t kExact = kTestRangeEnd - kTestRangeStart;
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto* grp = db->NewGrouping(kTestType1, kExact, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(kTestType1, grp->group_type());
EXPECT_EQ(kTestRangeStart, grp->start_handle());
EXPECT_EQ(kTestRangeEnd, grp->end_handle());
// Ran out of space.
EXPECT_FALSE(db->NewGrouping(kTestType1, 0, kTestValue1));
}
// This test case performs multiple insertions and removals on the same
// database.
TEST(ATT_DatabaseTest, NewGroupingMultipleInsertions) {
// [__________]
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
// Insert to empty db
// [XXX_______] (insert X)
auto* grp = db->NewGrouping(kTestType1, 2, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(1, grp->start_handle());
EXPECT_EQ(3, grp->end_handle());
// Not enough space
grp = db->NewGrouping(kTestType1, 7, kTestValue1);
EXPECT_FALSE(grp);
// Insert back
// [XXXYYYYY__] (insert Y)
grp = db->NewGrouping(kTestType1, 4, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(4, grp->start_handle());
EXPECT_EQ(8, grp->end_handle());
// Not enough space
grp = db->NewGrouping(kTestType1, 2, kTestValue1);
EXPECT_FALSE(grp);
// Insert back
// [XXXYYYYYZZ] (insert Z)
grp = db->NewGrouping(kTestType1, 1, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(9, grp->start_handle());
EXPECT_EQ(10, grp->end_handle());
// Out of space
EXPECT_FALSE(db->NewGrouping(kTestType1, 0, kTestValue1));
// Remove first grouping. It should be possible to reinsert a smaller group.
// [___YYYYYZZ]
EXPECT_TRUE(db->RemoveGrouping(1));
// Not enough space
grp = db->NewGrouping(kTestType1, 3, kTestValue1);
EXPECT_FALSE(grp);
// Insert front
// [XX_YYYYYZZ] (insert X)
grp = db->NewGrouping(kTestType1, 1, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(1, grp->start_handle());
EXPECT_EQ(2, grp->end_handle());
// Handle doesn't exist.
EXPECT_FALSE(db->RemoveGrouping(3));
// Insert in the middle
// [XXWYYYYYZZ] (insert W)
grp = db->NewGrouping(kTestType1, 0, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(3, grp->start_handle());
EXPECT_EQ(3, grp->end_handle());
// [XXW_____ZZ] (remove Y)
EXPECT_TRUE(db->RemoveGrouping(4));
// Insert in the middle
// [XXWAAA__ZZ] (insert A)
grp = db->NewGrouping(kTestType1, 2, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(4, grp->start_handle());
EXPECT_EQ(6, grp->end_handle());
// Insert in the middle
// [XXWAAABBZZ] (insert B)
grp = db->NewGrouping(kTestType1, 1, kTestValue1);
ASSERT_TRUE(grp);
EXPECT_EQ(7, grp->start_handle());
EXPECT_EQ(8, grp->end_handle());
// Out of space
EXPECT_FALSE(db->NewGrouping(kTestType1, 0, kTestValue1));
}
TEST(ATT_DatabaseTest, RemoveWhileEmpty) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
EXPECT_FALSE(db->RemoveGrouping(kTestRangeStart));
}
TEST(ATT_DatabaseTest, FindAttributeInvalidHandle) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
EXPECT_EQ(nullptr, db->FindAttribute(kInvalidHandle));
}
TEST(ATT_DatabaseTest, FindAttributeGroupingNotFound) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
// Create the following layout:
//
// handle 0x0001: occupied
// handle 0x0002: empty
// handle 0x0003: occupied
db->NewGrouping(kTestType1, 0, kTestValue1)->set_active(true);
auto* grp = db->NewGrouping(kTestType1, 0, kTestValue1);
grp->set_active(true);
db->NewGrouping(kTestType1, 0, kTestValue1)->set_active(true);
db->RemoveGrouping(grp->start_handle());
EXPECT_EQ(nullptr, db->FindAttribute(0xFFFF));
EXPECT_EQ(nullptr, db->FindAttribute(0x0002));
EXPECT_NE(nullptr, db->FindAttribute(0x0001));
EXPECT_NE(nullptr, db->FindAttribute(0x0003));
}
TEST(ATT_DatabaseTest, FindAttributeIncompleteGrouping) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto* grp = db->NewGrouping(kTestType1, 1, kTestValue1);
EXPECT_EQ(nullptr, db->FindAttribute(grp->start_handle()));
}
TEST(ATT_DatabaseTest, FindAttributeInactiveGrouping) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto* grp = db->NewGrouping(kTestType1, 0, kTestValue1);
EXPECT_EQ(nullptr, db->FindAttribute(grp->start_handle()));
}
TEST(ATT_DatabaseTest, FindAttributeOnePerGrouping) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto* grp1 = db->NewGrouping(kTestType1, 0, kTestValue1);
grp1->set_active(true);
auto* grp2 = db->NewGrouping(kTestType1, 0, kTestValue1);
grp2->set_active(true);
EXPECT_EQ(&grp1->attributes()[0], db->FindAttribute(grp1->start_handle()));
EXPECT_EQ(&grp2->attributes()[0], db->FindAttribute(grp2->start_handle()));
}
TEST(ATT_DatabaseTest, FindAttributeIndexIntoGrouping) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto* grp = db->NewGrouping(kTestType1, 1, kTestValue1);
auto* attr =
grp->AddAttribute(kTestType2, AccessRequirements(), AccessRequirements());
grp->set_active(true);
EXPECT_EQ(attr, db->FindAttribute(grp->end_handle()));
}
TEST(ATT_DatabaseTest, IteratorEmpty) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
// Advance should have no effect.
iter.Advance();
EXPECT_TRUE(iter.AtEnd());
}
TEST(ATT_DatabaseTest, IteratorGroupOnlySingleInactive) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
db->NewGrouping(kTestType1, 0, kTestValue1);
// |grp| is not active
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, nullptr,
true /* groups_only */);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
}
TEST(ATT_DatabaseTest, IteratorGroupOnlySingle) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp = db->NewGrouping(kTestType1, 0, kTestValue1);
grp->set_active(true);
// Not within range.
auto iter = db->GetIterator(grp->start_handle() + 1, kTestRangeEnd, nullptr,
true /* groups_only */);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, nullptr,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(grp->start_handle(), handles[0]);
}
TEST(ATT_DatabaseTest, IteratorGroupOnlyMultiple) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp1 = db->NewGrouping(kTestType1, 0, kTestValue1);
auto grp2 = db->NewGrouping(kTestType1, 0, kTestValue1);
auto grp3 = db->NewGrouping(kTestType1, 0, kTestValue1);
auto grp4 = db->NewGrouping(kTestType1, 0, kTestValue1);
// Leave |grp2| as inactive.
grp1->set_active(true);
grp3->set_active(true);
grp4->set_active(true);
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, nullptr,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
// |grp2| should be omitted.
auto handles = IterHandles(&iter);
ASSERT_EQ(3u, handles.size());
EXPECT_EQ(grp1->start_handle(), handles[0]);
EXPECT_EQ(grp3->start_handle(), handles[1]);
EXPECT_EQ(grp4->start_handle(), handles[2]);
grp2->set_active(true);
// Pick a narrow range that excludes |grp1| and |grp4|.
iter = db->GetIterator(grp2->start_handle(), grp3->end_handle(), nullptr,
true /* groups_only */);
handles = IterHandles(&iter);
ASSERT_EQ(2u, handles.size());
EXPECT_EQ(grp2->start_handle(), handles[0]);
EXPECT_EQ(grp3->start_handle(), handles[1]);
}
TEST(ATT_DatabaseTest, IteratorGroupOnlySingleWithFilter) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp = db->NewGrouping(kTestType1, 0, kTestValue1);
grp->set_active(true);
// No match.
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType2,
true /* groups_only */);
EXPECT_TRUE(iter.AtEnd());
iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType1,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(grp->start_handle(), handles[0]);
}
TEST(ATT_DatabaseTest, IteratorGroupOnlyManyWithFilter) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp1 = db->NewGrouping(kTestType1, 1, kTestValue1); // match
grp1->AddAttribute(kTestType1); // match but skipped - not group decl.
grp1->set_active(true);
// Matching but inactive.
db->NewGrouping(kTestType1, 0, kTestValue1);
auto grp2 = db->NewGrouping(kTestType2, 0, kTestValue1);
grp2->set_active(true);
auto grp3 = db->NewGrouping(kTestType1, 0, kTestValue1);
grp3->set_active(true);
auto grp4 = db->NewGrouping(kTestType2, 0, kTestValue1);
grp4->set_active(true);
auto grp5 = db->NewGrouping(kTestType2, 0, kTestValue1);
grp5->set_active(true);
auto grp6 = db->NewGrouping(kTestType1, 0, kTestValue1);
grp6->set_active(true);
// Filter by |kTestType1|
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType1,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
ASSERT_EQ(3u, handles.size());
EXPECT_EQ(grp1->start_handle(), handles[0]);
EXPECT_EQ(grp3->start_handle(), handles[1]);
EXPECT_EQ(grp6->start_handle(), handles[2]);
// Filter by |kTestType2|
iter = db->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType2,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
handles = IterHandles(&iter);
ASSERT_EQ(3u, handles.size());
EXPECT_EQ(grp2->start_handle(), handles[0]);
EXPECT_EQ(grp4->start_handle(), handles[1]);
EXPECT_EQ(grp5->start_handle(), handles[2]);
// Search narrower range.
iter = db->GetIterator(grp1->end_handle(), grp5->end_handle(), &kTestType1,
true /* groups_only */);
EXPECT_FALSE(iter.AtEnd());
handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(grp3->start_handle(), handles[0]);
}
TEST(ATT_DatabaseTest, IteratorSingleInactive) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp = db->NewGrouping(kTestType1, 1, kTestValue1);
auto iter = db->GetIterator(kTestRangeStart, kTestRangeEnd);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
// Complete but still inactive.
grp->AddAttribute(kTestType1);
iter = db->GetIterator(kTestRangeStart, kTestRangeEnd);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
}
TEST(ATT_DatabaseTest, IteratorSingle) {
auto db = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp = db->NewGrouping(kTestType1, 0, kTestValue1);
grp->set_active(true);
// Not within range.
auto iter = db->GetIterator(grp->start_handle() + 1, kTestRangeEnd);
EXPECT_TRUE(iter.AtEnd());
EXPECT_FALSE(iter.get());
iter = db->GetIterator(kTestRangeStart, kTestRangeEnd);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(grp->start_handle(), handles[0]);
}
class ATT_DatabaseIteratorManyTest : public ::testing::Test {
public:
ATT_DatabaseIteratorManyTest() = default;
~ATT_DatabaseIteratorManyTest() override = default;
protected:
static constexpr size_t kActiveAttrCount = 8;
void SetUp() override {
db_ = Database::Create(kTestRangeStart, kTestRangeEnd);
auto grp1 = db()->NewGrouping(kTestType1, 3, kTestValue1); // 1
grp1->AddAttribute(kTestType2); // 2
grp1->AddAttribute(kTestType2); // 3
grp1->AddAttribute(kTestType1); // 4
grp1->set_active(true);
auto grp2 = db()->NewGrouping(kTestType2, 2, kTestValue1); // 5
grp2->AddAttribute(kTestType1); // 6
grp2->AddAttribute(kTestType2); // 7
grp2->set_active(true);
auto grp3 = db()->NewGrouping(kTestType1, 1, kTestValue1); // 8 (inactive)
grp3->AddAttribute(kTestType2); // 9 (inactive)
auto grp4 = db()->NewGrouping(kTestType1, 0, kTestValue1); // 10
grp4->set_active(true);
}
Database* db() const { return db_.get(); }
private:
fxl::RefPtr<Database> db_;
FXL_DISALLOW_COPY_AND_ASSIGN(ATT_DatabaseIteratorManyTest);
};
// static
const size_t ATT_DatabaseIteratorManyTest::kActiveAttrCount;
TEST_F(ATT_DatabaseIteratorManyTest, NoFilter) {
auto iter = db()->GetIterator(kTestRangeStart, kTestRangeEnd);
EXPECT_FALSE(iter.AtEnd());
// Should cover all but the inactive attribute.
auto handles = IterHandles(&iter);
// All active attribute handles.
const std::array<Handle, kActiveAttrCount> kExpected = {1, 2, 3, 4,
5, 6, 7, 10};
ASSERT_EQ(kExpected.size(), handles.size());
for (size_t i = 0; i < handles.size(); i++) {
EXPECT_EQ(kExpected[i], handles[i]);
}
}
TEST_F(ATT_DatabaseIteratorManyTest, FilterTestType1) {
// Filter by |kTestType1|.
auto iter = db()->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType1);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
// Handles of attributes with type |kTestType1|.
const std::array<Handle, 4u> kExpected = {1, 4, 6, 10};
ASSERT_EQ(kExpected.size(), handles.size());
for (size_t i = 0; i < handles.size(); i++) {
EXPECT_EQ(kExpected[i], handles[i]);
}
}
TEST_F(ATT_DatabaseIteratorManyTest, FilterTestType2) {
// Filter by |kTestType2|.
auto iter = db()->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType2);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
// Handles of attributes with type |kTestType2|.
const std::array<Handle, 4u> kExpected = {2, 3, 5, 7};
ASSERT_EQ(kExpected.size(), handles.size());
for (size_t i = 0; i < handles.size(); i++) {
EXPECT_EQ(kExpected[i], handles[i]);
}
}
TEST_F(ATT_DatabaseIteratorManyTest, FilterTestType3) {
// Filter by |kTestType3|.
auto iter = db()->GetIterator(kTestRangeStart, kTestRangeEnd, &kTestType3);
EXPECT_TRUE(iter.AtEnd());
}
TEST_F(ATT_DatabaseIteratorManyTest, UnaryRange) {
// Test ranges with a single attribute. Test group begin, middle, and end
// cases.
constexpr Handle kBegin = 5;
constexpr Handle kMiddle = 6;
constexpr Handle kEnd = 7;
auto iter = db()->GetIterator(kBegin, kBegin);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(kBegin, handles[0]);
iter = db()->GetIterator(kMiddle, kMiddle);
EXPECT_FALSE(iter.AtEnd());
handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(kMiddle, handles[0]);
iter = db()->GetIterator(kEnd, kEnd);
EXPECT_FALSE(iter.AtEnd());
handles = IterHandles(&iter);
ASSERT_EQ(1u, handles.size());
EXPECT_EQ(kEnd, handles[0]);
}
TEST_F(ATT_DatabaseIteratorManyTest, Range) {
auto iter = db()->GetIterator(4, 6);
EXPECT_FALSE(iter.AtEnd());
auto handles = IterHandles(&iter);
// All active attribute handles.
const std::array<Handle, 3> kExpected = {4, 5, 6};
ASSERT_EQ(kExpected.size(), handles.size());
for (size_t i = 0; i < handles.size(); i++) {
EXPECT_EQ(kExpected[i], handles[i]);
}
}
} // namespace
} // namespace att
} // namespace btlib