普通文本  |  343行  |  12.21 KB

//
// Copyright (C) 2011 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "shill/async_connection.h"

#include <netinet/in.h>

#include <vector>

#include <base/bind.h>
#include <gtest/gtest.h>

#include "shill/mock_event_dispatcher.h"
#include "shill/net/ip_address.h"
#include "shill/net/mock_sockets.h"

using base::Bind;
using base::Callback;
using base::Unretained;
using std::string;
using ::testing::_;
using ::testing::Return;
using ::testing::ReturnNew;
using ::testing::StrEq;
using ::testing::StrictMock;
using ::testing::Test;

namespace shill {

namespace {
const char kInterfaceName[] = "int0";
const char kIPv4Address[] = "10.11.12.13";
const char kIPv6Address[] = "2001:db8::1";
const int kConnectPort = 10203;
const int kErrorNumber = 30405;
const int kSocketFD = 60708;
}  // namespace

class AsyncConnectionTest : public Test {
 public:
  AsyncConnectionTest()
      : async_connection_(
            new AsyncConnection(kInterfaceName, &dispatcher_, &sockets_,
                                callback_target_.callback())),
        ipv4_address_(IPAddress::kFamilyIPv4),
        ipv6_address_(IPAddress::kFamilyIPv6) { }

  virtual void SetUp() {
    EXPECT_TRUE(ipv4_address_.SetAddressFromString(kIPv4Address));
    EXPECT_TRUE(ipv6_address_.SetAddressFromString(kIPv6Address));
  }
  virtual void TearDown() {
    if (async_connection_.get() && async_connection_->fd_ >= 0) {
      EXPECT_CALL(sockets(), Close(kSocketFD))
          .WillOnce(Return(0));
    }
  }
  void InvokeFreeConnection(bool /*success*/, int /*fd*/) {
    async_connection_.reset();
  }

 protected:
  class ConnectCallbackTarget {
   public:
    ConnectCallbackTarget()
        : callback_(Bind(&ConnectCallbackTarget::CallTarget,
                         Unretained(this))) {}

    MOCK_METHOD2(CallTarget, void(bool success, int fd));
    const Callback<void(bool, int)>& callback() { return callback_; }

   private:
    Callback<void(bool, int)> callback_;
  };

  void ExpectReset() {
    EXPECT_STREQ(kInterfaceName, async_connection_->interface_name_.c_str());
    EXPECT_EQ(&dispatcher_, async_connection_->dispatcher_);
    EXPECT_EQ(&sockets_, async_connection_->sockets_);
    EXPECT_TRUE(callback_target_.callback().
                Equals(async_connection_->callback_));
    EXPECT_EQ(-1, async_connection_->fd_);
    EXPECT_FALSE(async_connection_->connect_completion_callback_.is_null());
    EXPECT_FALSE(async_connection_->connect_completion_handler_.get());
  }

  void StartConnection() {
    EXPECT_CALL(sockets_, Socket(_, _, _))
        .WillOnce(Return(kSocketFD));
    EXPECT_CALL(sockets_, SetNonBlocking(kSocketFD))
        .WillOnce(Return(0));
    EXPECT_CALL(sockets_, BindToDevice(kSocketFD, StrEq(kInterfaceName)))
        .WillOnce(Return(0));
    EXPECT_CALL(sockets(), Connect(kSocketFD, _, _))
        .WillOnce(Return(-1));
    EXPECT_CALL(sockets_, Error())
        .WillOnce(Return(EINPROGRESS));
    EXPECT_CALL(dispatcher(),
                CreateReadyHandler(kSocketFD, IOHandler::kModeOutput, _))
        .WillOnce(ReturnNew<IOHandler>());
    EXPECT_TRUE(async_connection().Start(ipv4_address_, kConnectPort));
  }

  void OnConnectCompletion(int fd) {
    async_connection_->OnConnectCompletion(fd);
  }
  AsyncConnection& async_connection() { return *async_connection_.get(); }
  StrictMock<MockSockets>& sockets() { return sockets_; }
  MockEventDispatcher& dispatcher() { return dispatcher_; }
  const IPAddress& ipv4_address() { return ipv4_address_; }
  const IPAddress& ipv6_address() { return ipv6_address_; }
  int fd() { return async_connection_->fd_; }
  void set_fd(int fd) { async_connection_->fd_ = fd; }
  StrictMock<ConnectCallbackTarget>& callback_target() {
    return callback_target_;
  }

 private:
  MockEventDispatcher dispatcher_;
  StrictMock<MockSockets> sockets_;
  StrictMock<ConnectCallbackTarget> callback_target_;
  std::unique_ptr<AsyncConnection> async_connection_;
  IPAddress ipv4_address_;
  IPAddress ipv6_address_;
};

TEST_F(AsyncConnectionTest, InitState) {
  ExpectReset();
  EXPECT_EQ(string(), async_connection().error());
}

TEST_F(AsyncConnectionTest, StartSocketFailure) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(-1));
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(kErrorNumber));
  EXPECT_FALSE(async_connection().Start(ipv4_address(), kConnectPort));
  ExpectReset();
  EXPECT_STREQ(strerror(kErrorNumber), async_connection().error().c_str());
}

TEST_F(AsyncConnectionTest, StartNonBlockingFailure) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(-1));
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(kErrorNumber));
  EXPECT_CALL(sockets(), Close(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_FALSE(async_connection().Start(ipv4_address(), kConnectPort));
  ExpectReset();
  EXPECT_STREQ(strerror(kErrorNumber), async_connection().error().c_str());
}

TEST_F(AsyncConnectionTest, StartBindToDeviceFailure) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(-1));
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(kErrorNumber));
  EXPECT_CALL(sockets(), Close(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_FALSE(async_connection().Start(ipv4_address(), kConnectPort));
  ExpectReset();
  EXPECT_STREQ(strerror(kErrorNumber), async_connection().error().c_str());
}

TEST_F(AsyncConnectionTest, SynchronousFailure) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), Connect(kSocketFD, _, _))
      .WillOnce(Return(-1));
  EXPECT_CALL(sockets(), Error())
      .Times(2)
      .WillRepeatedly(Return(0));
  EXPECT_CALL(sockets(), Close(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_FALSE(async_connection().Start(ipv4_address(), kConnectPort));
  ExpectReset();
}

MATCHER_P2(IsSocketAddress, address, port, "") {
  const struct sockaddr_in* arg_saddr =
      reinterpret_cast<const struct sockaddr_in*>(arg);
  IPAddress arg_addr(IPAddress::kFamilyIPv4,
                     ByteString(reinterpret_cast<const unsigned char*>(
                         &arg_saddr->sin_addr.s_addr),
                                sizeof(arg_saddr->sin_addr.s_addr)));
  return address.Equals(arg_addr) && arg_saddr->sin_port == htons(port);
}

MATCHER_P2(IsSocketIpv6Address, ipv6_address, port, "") {
  const struct sockaddr_in6* arg_saddr =
      reinterpret_cast<const struct sockaddr_in6*>(arg);
  IPAddress arg_addr(IPAddress::kFamilyIPv6,
                     ByteString(reinterpret_cast<const unsigned char*>(
                         &arg_saddr->sin6_addr.s6_addr),
                                sizeof(arg_saddr->sin6_addr.s6_addr)));
  return ipv6_address.Equals(arg_addr) && arg_saddr->sin6_port == htons(port);
}

TEST_F(AsyncConnectionTest, SynchronousStart) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), Connect(kSocketFD,
                                  IsSocketAddress(ipv4_address(), kConnectPort),
                                  sizeof(struct sockaddr_in)))
      .WillOnce(Return(-1));
  EXPECT_CALL(dispatcher(),
              CreateReadyHandler(kSocketFD, IOHandler::kModeOutput, _))
        .WillOnce(ReturnNew<IOHandler>());
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(EINPROGRESS));
  EXPECT_TRUE(async_connection().Start(ipv4_address(), kConnectPort));
  EXPECT_EQ(kSocketFD, fd());
}

TEST_F(AsyncConnectionTest, SynchronousStartIpv6) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), Connect(kSocketFD,
                                  IsSocketIpv6Address(ipv6_address(),
                                                      kConnectPort),
                                  sizeof(struct sockaddr_in6)))
      .WillOnce(Return(-1));
  EXPECT_CALL(dispatcher(),
              CreateReadyHandler(kSocketFD, IOHandler::kModeOutput, _))
        .WillOnce(ReturnNew<IOHandler>());
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(EINPROGRESS));
  EXPECT_TRUE(async_connection().Start(ipv6_address(), kConnectPort));
  EXPECT_EQ(kSocketFD, fd());
}

TEST_F(AsyncConnectionTest, AsynchronousFailure) {
  StartConnection();
  EXPECT_CALL(sockets(), GetSocketError(kSocketFD))
      .WillOnce(Return(1));
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(kErrorNumber));
  EXPECT_CALL(callback_target(), CallTarget(false, -1));
  EXPECT_CALL(sockets(), Close(kSocketFD))
      .WillOnce(Return(0));
  OnConnectCompletion(kSocketFD);
  ExpectReset();
  EXPECT_STREQ(strerror(kErrorNumber), async_connection().error().c_str());
}

TEST_F(AsyncConnectionTest, AsynchronousSuccess) {
  StartConnection();
  EXPECT_CALL(sockets(), GetSocketError(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(callback_target(), CallTarget(true, kSocketFD));
  OnConnectCompletion(kSocketFD);
  ExpectReset();
}

TEST_F(AsyncConnectionTest, SynchronousSuccess) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), Connect(kSocketFD,
                                  IsSocketAddress(ipv4_address(), kConnectPort),
                                  sizeof(struct sockaddr_in)))
      .WillOnce(Return(0));
  EXPECT_CALL(callback_target(), CallTarget(true, kSocketFD));
  EXPECT_TRUE(async_connection().Start(ipv4_address(), kConnectPort));
  ExpectReset();
}

TEST_F(AsyncConnectionTest, SynchronousSuccessIpv6) {
  EXPECT_CALL(sockets(), Socket(_, _, _))
      .WillOnce(Return(kSocketFD));
  EXPECT_CALL(sockets(), SetNonBlocking(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), BindToDevice(kSocketFD, StrEq(kInterfaceName)))
      .WillOnce(Return(0));
  EXPECT_CALL(sockets(), Connect(kSocketFD,
                                  IsSocketIpv6Address(ipv6_address(),
                                                      kConnectPort),
                                  sizeof(struct sockaddr_in6)))
      .WillOnce(Return(0));
  EXPECT_CALL(callback_target(), CallTarget(true, kSocketFD));
  EXPECT_TRUE(async_connection().Start(ipv6_address(), kConnectPort));
  ExpectReset();
}

TEST_F(AsyncConnectionTest, FreeOnSuccessCallback) {
  StartConnection();
  EXPECT_CALL(sockets(), GetSocketError(kSocketFD))
      .WillOnce(Return(0));
  EXPECT_CALL(callback_target(), CallTarget(true, kSocketFD))
      .WillOnce(Invoke(this, &AsyncConnectionTest::InvokeFreeConnection));
  OnConnectCompletion(kSocketFD);
}

TEST_F(AsyncConnectionTest, FreeOnFailureCallback) {
  StartConnection();
  EXPECT_CALL(sockets(), GetSocketError(kSocketFD))
      .WillOnce(Return(1));
  EXPECT_CALL(callback_target(), CallTarget(false, -1))
      .WillOnce(Invoke(this, &AsyncConnectionTest::InvokeFreeConnection));
  EXPECT_CALL(sockets(), Error())
      .WillOnce(Return(kErrorNumber));
  EXPECT_CALL(sockets(), Close(kSocketFD))
      .WillOnce(Return(0));
  OnConnectCompletion(kSocketFD);
}

}  // namespace shill