| /* | 
 |  * libjingle | 
 |  * Copyright 2004--2011, Google Inc. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions are met: | 
 |  * | 
 |  *  1. Redistributions of source code must retain the above copyright notice, | 
 |  *     this list of conditions and the following disclaimer. | 
 |  *  2. Redistributions in binary form must reproduce the above copyright notice, | 
 |  *     this list of conditions and the following disclaimer in the documentation | 
 |  *     and/or other materials provided with the distribution. | 
 |  *  3. The name of the author may not be used to endorse or promote products | 
 |  *     derived from this software without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | 
 |  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 
 |  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | 
 |  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 |  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
 |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | 
 |  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 
 |  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | 
 |  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | 
 |  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <signal.h> | 
 | #include <stdarg.h> | 
 |  | 
 | #include "talk/base/gunit.h" | 
 | #include "talk/base/logging.h" | 
 | #include "talk/base/physicalsocketserver.h" | 
 | #include "talk/base/scoped_ptr.h" | 
 | #include "talk/base/socket_unittest.h" | 
 | #include "talk/base/testutils.h" | 
 | #include "talk/base/thread.h" | 
 |  | 
 | namespace talk_base { | 
 |  | 
 | class PhysicalSocketTest : public SocketTest { | 
 | }; | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectIPv4) { | 
 |   SocketTest::TestConnectIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectIPv6) { | 
 |   SocketTest::TestConnectIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv4) { | 
 |   SocketTest::TestConnectWithDnsLookupIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupIPv6) { | 
 |   SocketTest::TestConnectWithDnsLookupIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectFailIPv4) { | 
 |   SocketTest::TestConnectFailIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectFailIPv6) { | 
 |   SocketTest::TestConnectFailIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv4) { | 
 |   SocketTest::TestConnectWithDnsLookupFailIPv4(); | 
 | } | 
 |  | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithDnsLookupFailIPv6) { | 
 |   SocketTest::TestConnectWithDnsLookupFailIPv6(); | 
 | } | 
 |  | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv4) { | 
 |   SocketTest::TestConnectWithClosedSocketIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWithClosedSocketIPv6) { | 
 |   SocketTest::TestConnectWithClosedSocketIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv4) { | 
 |   SocketTest::TestConnectWhileNotClosedIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestConnectWhileNotClosedIPv6) { | 
 |   SocketTest::TestConnectWhileNotClosedIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv4) { | 
 |   SocketTest::TestServerCloseDuringConnectIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestServerCloseDuringConnectIPv6) { | 
 |   SocketTest::TestServerCloseDuringConnectIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv4) { | 
 |   SocketTest::TestClientCloseDuringConnectIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestClientCloseDuringConnectIPv6) { | 
 |   SocketTest::TestClientCloseDuringConnectIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestServerCloseIPv4) { | 
 |   SocketTest::TestServerCloseIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestServerCloseIPv6) { | 
 |   SocketTest::TestServerCloseIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv4) { | 
 |   SocketTest::TestCloseInClosedCallbackIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestCloseInClosedCallbackIPv6) { | 
 |   SocketTest::TestCloseInClosedCallbackIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv4) { | 
 |   SocketTest::TestSocketServerWaitIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestSocketServerWaitIPv6) { | 
 |   SocketTest::TestSocketServerWaitIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestTcpIPv4) { | 
 |   SocketTest::TestTcpIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestTcpIPv6) { | 
 |   SocketTest::TestTcpIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestUdpIPv4) { | 
 |   SocketTest::TestUdpIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestUdpIPv6) { | 
 |   SocketTest::TestUdpIPv6(); | 
 | } | 
 |  | 
 | // Disable for TSan v2, see | 
 | // https://code.google.com/p/webrtc/issues/detail?id=3498 for details. | 
 | #if !defined(THREAD_SANITIZER) | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv4) { | 
 |   SocketTest::TestUdpReadyToSendIPv4(); | 
 | } | 
 |  | 
 | #endif // if !defined(THREAD_SANITIZER) | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestUdpReadyToSendIPv6) { | 
 |   SocketTest::TestUdpReadyToSendIPv6(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv4) { | 
 |   SocketTest::TestGetSetOptionsIPv4(); | 
 | } | 
 |  | 
 | TEST_F(PhysicalSocketTest, TestGetSetOptionsIPv6) { | 
 |   SocketTest::TestGetSetOptionsIPv6(); | 
 | } | 
 |  | 
 | #ifdef POSIX | 
 |  | 
 | class PosixSignalDeliveryTest : public testing::Test { | 
 |  public: | 
 |   static void RecordSignal(int signum) { | 
 |     signals_received_.push_back(signum); | 
 |     signaled_thread_ = Thread::Current(); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void SetUp() { | 
 |     ss_.reset(new PhysicalSocketServer()); | 
 |   } | 
 |  | 
 |   void TearDown() { | 
 |     ss_.reset(NULL); | 
 |     signals_received_.clear(); | 
 |     signaled_thread_ = NULL; | 
 |   } | 
 |  | 
 |   bool ExpectSignal(int signum) { | 
 |     if (signals_received_.empty()) { | 
 |       LOG(LS_ERROR) << "ExpectSignal(): No signal received"; | 
 |       return false; | 
 |     } | 
 |     if (signals_received_[0] != signum) { | 
 |       LOG(LS_ERROR) << "ExpectSignal(): Received signal " << | 
 |           signals_received_[0] << ", expected " << signum; | 
 |       return false; | 
 |     } | 
 |     signals_received_.erase(signals_received_.begin()); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool ExpectNone() { | 
 |     bool ret = signals_received_.empty(); | 
 |     if (!ret) { | 
 |       LOG(LS_ERROR) << "ExpectNone(): Received signal " << signals_received_[0] | 
 |           << ", expected none"; | 
 |     } | 
 |     return ret; | 
 |   } | 
 |  | 
 |   static std::vector<int> signals_received_; | 
 |   static Thread *signaled_thread_; | 
 |  | 
 |   scoped_ptr<PhysicalSocketServer> ss_; | 
 | }; | 
 |  | 
 | std::vector<int> PosixSignalDeliveryTest::signals_received_; | 
 | Thread *PosixSignalDeliveryTest::signaled_thread_ = NULL; | 
 |  | 
 | // Test receiving a synchronous signal while not in Wait() and then entering | 
 | // Wait() afterwards. | 
 | TEST_F(PosixSignalDeliveryTest, RaiseThenWait) { | 
 |   ASSERT_TRUE(ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal)); | 
 |   raise(SIGTERM); | 
 |   EXPECT_TRUE(ss_->Wait(0, true)); | 
 |   EXPECT_TRUE(ExpectSignal(SIGTERM)); | 
 |   EXPECT_TRUE(ExpectNone()); | 
 | } | 
 |  | 
 | // Test that we can handle getting tons of repeated signals and that we see all | 
 | // the different ones. | 
 | TEST_F(PosixSignalDeliveryTest, InsanelyManySignals) { | 
 |   ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal); | 
 |   ss_->SetPosixSignalHandler(SIGINT, &RecordSignal); | 
 |   for (int i = 0; i < 10000; ++i) { | 
 |     raise(SIGTERM); | 
 |   } | 
 |   raise(SIGINT); | 
 |   EXPECT_TRUE(ss_->Wait(0, true)); | 
 |   // Order will be lowest signal numbers first. | 
 |   EXPECT_TRUE(ExpectSignal(SIGINT)); | 
 |   EXPECT_TRUE(ExpectSignal(SIGTERM)); | 
 |   EXPECT_TRUE(ExpectNone()); | 
 | } | 
 |  | 
 | // Test that a signal during a Wait() call is detected. | 
 | TEST_F(PosixSignalDeliveryTest, SignalDuringWait) { | 
 |   ss_->SetPosixSignalHandler(SIGALRM, &RecordSignal); | 
 |   alarm(1); | 
 |   EXPECT_TRUE(ss_->Wait(1500, true)); | 
 |   EXPECT_TRUE(ExpectSignal(SIGALRM)); | 
 |   EXPECT_TRUE(ExpectNone()); | 
 | } | 
 |  | 
 | class RaiseSigTermRunnable : public Runnable { | 
 |   void Run(Thread *thread) { | 
 |     thread->socketserver()->Wait(1000, false); | 
 |  | 
 |     // Allow SIGTERM. This will be the only thread with it not masked so it will | 
 |     // be delivered to us. | 
 |     sigset_t mask; | 
 |     sigemptyset(&mask); | 
 |     pthread_sigmask(SIG_SETMASK, &mask, NULL); | 
 |  | 
 |     // Raise it. | 
 |     raise(SIGTERM); | 
 |   } | 
 | }; | 
 |  | 
 | // Test that it works no matter what thread the kernel chooses to give the | 
 | // signal to (since it's not guaranteed to be the one that Wait() runs on). | 
 | TEST_F(PosixSignalDeliveryTest, SignalOnDifferentThread) { | 
 |   ss_->SetPosixSignalHandler(SIGTERM, &RecordSignal); | 
 |   // Mask out SIGTERM so that it can't be delivered to this thread. | 
 |   sigset_t mask; | 
 |   sigemptyset(&mask); | 
 |   sigaddset(&mask, SIGTERM); | 
 |   EXPECT_EQ(0, pthread_sigmask(SIG_SETMASK, &mask, NULL)); | 
 |   // Start a new thread that raises it. It will have to be delivered to that | 
 |   // thread. Our implementation should safely handle it and dispatch | 
 |   // RecordSignal() on this thread. | 
 |   scoped_ptr<Thread> thread(new Thread()); | 
 |   scoped_ptr<RaiseSigTermRunnable> runnable(new RaiseSigTermRunnable()); | 
 |   thread->Start(runnable.get()); | 
 |   EXPECT_TRUE(ss_->Wait(1500, true)); | 
 |   EXPECT_TRUE(ExpectSignal(SIGTERM)); | 
 |   EXPECT_EQ(Thread::Current(), signaled_thread_); | 
 |   EXPECT_TRUE(ExpectNone()); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | }  // namespace talk_base |