// Copyright (c) 2012 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 "base/posix/file_descriptor_shuffle.h" #include "testing/gtest/include/gtest/gtest.h" namespace { // 'Duplicated' file descriptors start at this number const int kDuplicateBase = 1000; } // namespace namespace base { struct Action { enum Type { CLOSE, MOVE, DUPLICATE, }; Action(Type in_type, int in_fd1, int in_fd2 = -1) : type(in_type), fd1(in_fd1), fd2(in_fd2) { } bool operator==(const Action& other) const { return other.type == type && other.fd1 == fd1 && other.fd2 == fd2; } Type type; int fd1; int fd2; }; class InjectionTracer : public InjectionDelegate { public: InjectionTracer() : next_duplicate_(kDuplicateBase) { } bool Duplicate(int* result, int fd) override { *result = next_duplicate_++; actions_.push_back(Action(Action::DUPLICATE, *result, fd)); return true; } bool Move(int src, int dest) override { actions_.push_back(Action(Action::MOVE, src, dest)); return true; } void Close(int fd) override { actions_.push_back(Action(Action::CLOSE, fd)); } const std::vector<Action>& actions() const { return actions_; } private: int next_duplicate_; std::vector<Action> actions_; }; TEST(FileDescriptorShuffleTest, Empty) { InjectiveMultimap map; InjectionTracer tracer; EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); EXPECT_EQ(0u, tracer.actions().size()); } TEST(FileDescriptorShuffleTest, Noop) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 0, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); EXPECT_EQ(0u, tracer.actions().size()); } TEST(FileDescriptorShuffleTest, NoopAndClose) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 0, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); EXPECT_EQ(0u, tracer.actions().size()); } TEST(FileDescriptorShuffleTest, Simple1) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(1u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); } TEST(FileDescriptorShuffleTest, Simple2) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); map.push_back(InjectionArc(2, 3, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(2u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3)); } TEST(FileDescriptorShuffleTest, Simple3) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(2u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0)); } TEST(FileDescriptorShuffleTest, Simple4) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(10, 0, true)); map.push_back(InjectionArc(1, 1, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(2u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10)); } TEST(FileDescriptorShuffleTest, Cycle) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); map.push_back(InjectionArc(1, 0, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(4u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::DUPLICATE, kDuplicateBase, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); } TEST(FileDescriptorShuffleTest, CycleAndClose1) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, true)); map.push_back(InjectionArc(1, 0, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(4u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::DUPLICATE, kDuplicateBase, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); } TEST(FileDescriptorShuffleTest, CycleAndClose2) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); map.push_back(InjectionArc(1, 0, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(4u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::DUPLICATE, kDuplicateBase, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); } TEST(FileDescriptorShuffleTest, CycleAndClose3) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, true)); map.push_back(InjectionArc(1, 0, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(4u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::DUPLICATE, kDuplicateBase, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); } TEST(FileDescriptorShuffleTest, Fanout) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); map.push_back(InjectionArc(0, 2, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(2u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); } TEST(FileDescriptorShuffleTest, FanoutAndClose1) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, true)); map.push_back(InjectionArc(0, 2, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(3u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); } TEST(FileDescriptorShuffleTest, FanoutAndClose2) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, false)); map.push_back(InjectionArc(0, 2, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(3u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); } TEST(FileDescriptorShuffleTest, FanoutAndClose3) { InjectiveMultimap map; InjectionTracer tracer; map.push_back(InjectionArc(0, 1, true)); map.push_back(InjectionArc(0, 2, true)); EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); ASSERT_EQ(3u, tracer.actions().size()); EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); } class FailingDelegate : public InjectionDelegate { public: bool Duplicate(int* result, int fd) override { return false; } bool Move(int src, int dest) override { return false; } void Close(int fd) override {} }; TEST(FileDescriptorShuffleTest, EmptyWithFailure) { InjectiveMultimap map; FailingDelegate failing; EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); } TEST(FileDescriptorShuffleTest, NoopWithFailure) { InjectiveMultimap map; FailingDelegate failing; map.push_back(InjectionArc(0, 0, false)); EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); } TEST(FileDescriptorShuffleTest, Simple1WithFailure) { InjectiveMultimap map; FailingDelegate failing; map.push_back(InjectionArc(0, 1, false)); EXPECT_FALSE(PerformInjectiveMultimap(map, &failing)); } } // namespace base