blob: 1b590d47a2ef1a587e8180c09f07ccc1aef6b366 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "portier/group.h"
#include <memory>
#include <string>
#include <vector>
#include <base/stl_util.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "portier/mock_group.h"
namespace portier {
using std::shared_ptr;
using std::string;
using std::vector;
namespace {
// Test data.
using MockGroup = MockGroupMember::GroupType;
using MockGroupMemberPtr = shared_ptr<MockGroupMember>;
const vector<string> kInvalidGroupNames = {"",
"not one word",
" leadingspaces",
"trailingspaces ",
"i||eg@|ch@r$",
"contains^badcharacter",
"\n0\np\ri\n\tabl\e",
"nonasci\xf0\x9f\xa4\xa1"};
const vector<string> kValidGroupNames = {"eth0-group", "test_group", "lanparty",
"net0", "othername"};
constexpr char kGroupName1[] = "group1";
constexpr char kGroupName2[] = "group2";
constexpr size_t kManyInterfaceCount = 30;
} // namespace
// Parameterized test for invalid group names.
class GroupInvalidNameTest : public testing::TestWithParam<string> {};
TEST_P(GroupInvalidNameTest, CreationFails) {
const string pg_name = GetParam();
auto pg_ptr = MockGroup::Create(pg_name);
EXPECT_FALSE(pg_ptr);
}
INSTANTIATE_TEST_CASE_P(GroupTest,
GroupInvalidNameTest,
testing::ValuesIn(kInvalidGroupNames));
class GroupValidNameTest : public testing::TestWithParam<string> {};
TEST_P(GroupValidNameTest, CreationSucceeds) {
const string pg_name = GetParam();
auto pg_ptr = MockGroup::Create(pg_name);
EXPECT_TRUE(pg_ptr);
}
INSTANTIATE_TEST_CASE_P(GroupTest,
GroupValidNameTest,
testing::ValuesIn(kValidGroupNames));
TEST(GroupTest, MemberLess) {
auto pg_ptr = MockGroup::Create(kGroupName1);
EXPECT_TRUE(pg_ptr);
EXPECT_EQ(pg_ptr->name(), kGroupName1);
EXPECT_EQ(pg_ptr->size(), 0);
auto members = pg_ptr->GetMembers();
EXPECT_TRUE(members.empty());
}
TEST(GroupTest, SingleMember) {
auto pg_ptr = MockGroup::Create(kGroupName1);
auto mem_ptr = MockGroupMemberPtr(new MockGroupMember());
// Add member.
EXPECT_CALL(*mem_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg_ptr->AddMember(mem_ptr));
// Verify membership.
EXPECT_EQ(pg_ptr->size(), 1);
EXPECT_TRUE(mem_ptr->HasGroup());
auto mem_pg_ptr = mem_ptr->GetGroup();
EXPECT_EQ(pg_ptr.get(), mem_pg_ptr);
auto members = pg_ptr->GetMembers();
EXPECT_EQ(members.size(), 1);
auto list_mem_ptr = members.at(0);
EXPECT_EQ(list_mem_ptr, mem_ptr);
// Add a second time.
EXPECT_TRUE(pg_ptr->AddMember(mem_ptr));
EXPECT_EQ(pg_ptr->size(), 1);
EXPECT_TRUE(mem_ptr->HasGroup());
// Remove
EXPECT_CALL(*mem_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem_ptr, PostLeaveGroup()).Times(1);
EXPECT_TRUE(pg_ptr->RemoveMember(mem_ptr));
// Verify removal.
EXPECT_EQ(pg_ptr->size(), 0);
EXPECT_FALSE(mem_ptr->HasGroup());
mem_pg_ptr = mem_ptr->GetGroup();
EXPECT_FALSE(mem_pg_ptr);
// Try removing again (should fail).
EXPECT_FALSE(pg_ptr->RemoveMember(mem_ptr));
}
TEST(GroupTest, OutOfScopeGroup) {
auto pg_ptr = MockGroup::Create(kGroupName1);
auto mem_ptr = MockGroupMemberPtr(new MockGroupMember());
// Add member.
EXPECT_CALL(*mem_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg_ptr->AddMember(mem_ptr));
EXPECT_TRUE(mem_ptr->HasGroup());
// Deleting group should automatically remove member.
EXPECT_CALL(*mem_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem_ptr, PostLeaveGroup()).Times(1);
pg_ptr.reset();
EXPECT_FALSE(mem_ptr->HasGroup());
}
TEST(GroupTest, MultipleMembers) {
auto pg_ptr = MockGroup::Create(kGroupName1);
auto mem1_ptr = MockGroupMemberPtr(new MockGroupMember());
auto mem2_ptr = MockGroupMemberPtr(new MockGroupMember());
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(0);
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg_ptr->AddMember(mem1_ptr));
EXPECT_TRUE(pg_ptr->AddMember(mem2_ptr));
// Verify members.
EXPECT_EQ(pg_ptr->size(), 2);
EXPECT_TRUE(mem1_ptr->HasGroup());
EXPECT_TRUE(mem2_ptr->HasGroup());
// Remove first.
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(1);
EXPECT_TRUE(pg_ptr->RemoveMember(mem1_ptr));
// Verify.
EXPECT_EQ(pg_ptr->size(), 1);
EXPECT_FALSE(mem1_ptr->HasGroup());
EXPECT_TRUE(mem2_ptr->HasGroup());
// Add it back.
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg_ptr->AddMember(mem1_ptr));
// Verify members.
EXPECT_EQ(pg_ptr->size(), 2);
EXPECT_TRUE(mem1_ptr->HasGroup());
EXPECT_TRUE(mem2_ptr->HasGroup());
// Remove all.
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(1);
pg_ptr->RemoveAllMembers();
// Verify
EXPECT_EQ(pg_ptr->size(), 0);
EXPECT_FALSE(mem1_ptr->HasGroup());
EXPECT_FALSE(mem2_ptr->HasGroup());
}
TEST(GroupTest, ManyMembers) {
auto pg_ptr = MockGroup::Create(kGroupName1);
for (size_t i = 0; i < kManyInterfaceCount; i++) {
auto mem_ptr = MockGroupMemberPtr(new MockGroupMember());
EXPECT_CALL(*mem_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem_ptr, PostLeaveGroup()).Times(1);
EXPECT_TRUE(pg_ptr->AddMember(mem_ptr));
}
EXPECT_EQ(pg_ptr->size(), kManyInterfaceCount);
pg_ptr->RemoveAllMembers();
EXPECT_EQ(pg_ptr->size(), 0);
}
TEST(GroupTest, Upstream) {
auto pg_ptr = MockGroup::Create(kGroupName1);
auto mem1_ptr = MockGroupMemberPtr(new MockGroupMember());
auto mem2_ptr = MockGroupMemberPtr(new MockGroupMember());
// For this test, we dont really care about the calls.
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(1);
EXPECT_FALSE(pg_ptr->GetUpstream());
// Add member.
EXPECT_TRUE(pg_ptr->AddMember(mem1_ptr));
// Verify nothing funky is going on.
EXPECT_FALSE(mem1_ptr->IsUpstream());
EXPECT_FALSE(pg_ptr->GetUpstream());
// Try and fail at setting non-member as upstream.
EXPECT_FALSE(pg_ptr->SetUpstream(mem2_ptr));
EXPECT_FALSE(pg_ptr->GetUpstream());
EXPECT_FALSE(mem2_ptr->HasGroup());
// Set the current member as upstream.
EXPECT_TRUE(pg_ptr->SetUpstream(mem1_ptr));
EXPECT_EQ(pg_ptr->GetUpstream(), mem1_ptr);
EXPECT_TRUE(mem1_ptr->IsUpstream());
// Add another interface.
EXPECT_TRUE(pg_ptr->AddMember(mem2_ptr));
// Nothing else should change.
EXPECT_EQ(pg_ptr->GetUpstream(), mem1_ptr);
EXPECT_TRUE(mem1_ptr->IsUpstream());
EXPECT_FALSE(mem2_ptr->IsUpstream());
// Remove upstream member normally.
pg_ptr->UnsetUpstream();
// Should still be member, but not upsteam.
EXPECT_FALSE(mem1_ptr->IsUpstream());
EXPECT_TRUE(mem1_ptr->HasGroup());
EXPECT_EQ(pg_ptr->size(), 2);
// Set second member as upstream.
EXPECT_TRUE(pg_ptr->SetUpstream(mem2_ptr));
EXPECT_EQ(pg_ptr->GetUpstream(), mem2_ptr);
EXPECT_TRUE(mem2_ptr->IsUpstream());
// Remove second member from group.
EXPECT_TRUE(pg_ptr->RemoveMember(mem2_ptr));
// Verify that is no longer upstream.
EXPECT_FALSE(pg_ptr->GetUpstream());
EXPECT_FALSE(mem2_ptr->HasGroup());
EXPECT_FALSE(mem2_ptr->IsUpstream());
EXPECT_EQ(pg_ptr->size(), 1);
}
TEST(GroupTest, MultipleGroups) {
auto pg1_ptr = MockGroup::Create(kGroupName1);
auto pg2_ptr = MockGroup::Create(kGroupName2);
auto mem1_ptr = MockGroupMemberPtr(new MockGroupMember());
auto mem2_ptr = MockGroupMemberPtr(new MockGroupMember());
EXPECT_CALL(*mem1_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(0);
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg1_ptr->AddMember(mem1_ptr));
EXPECT_TRUE(pg2_ptr->AddMember(mem2_ptr));
// Verify membership.
EXPECT_TRUE(mem1_ptr->HasGroup());
EXPECT_TRUE(mem2_ptr->HasGroup());
EXPECT_EQ(pg1_ptr->size(), 1);
EXPECT_EQ(pg2_ptr->size(), 1);
// Should not be able members to other groups.
EXPECT_FALSE(pg1_ptr->AddMember(mem2_ptr));
EXPECT_FALSE(pg2_ptr->AddMember(mem1_ptr));
// Should not be able to remove members from other groups
EXPECT_FALSE(pg1_ptr->RemoveMember(mem2_ptr));
EXPECT_FALSE(pg2_ptr->RemoveMember(mem1_ptr));
// Remove member 2 from group 2.
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(0);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(1);
EXPECT_TRUE(pg2_ptr->RemoveMember(mem2_ptr));
// Add member 2 to group 1.
EXPECT_CALL(*mem2_ptr, PostJoinGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(0);
EXPECT_TRUE(pg1_ptr->AddMember(mem2_ptr));
EXPECT_EQ(pg1_ptr->size(), 2);
EXPECT_EQ(pg2_ptr->size(), 0);
EXPECT_CALL(*mem1_ptr, PostLeaveGroup()).Times(1);
EXPECT_CALL(*mem2_ptr, PostLeaveGroup()).Times(1);
}
} // namespace portier