| Index: net/socket/websocket_endpoint_lock_manager_unittest.cc
 | 
| diff --git a/net/socket/websocket_endpoint_lock_manager_unittest.cc b/net/socket/websocket_endpoint_lock_manager_unittest.cc
 | 
| index 1626aa9020185e7e71929910e89d658c8718a73a..1603a3bf5cfef0dcfae83260e6b1eff0aa636f72 100644
 | 
| --- a/net/socket/websocket_endpoint_lock_manager_unittest.cc
 | 
| +++ b/net/socket/websocket_endpoint_lock_manager_unittest.cc
 | 
| @@ -4,6 +4,9 @@
 | 
|  
 | 
|  #include "net/socket/websocket_endpoint_lock_manager.h"
 | 
|  
 | 
| +#include "base/message_loop/message_loop.h"
 | 
| +#include "base/run_loop.h"
 | 
| +#include "base/time/time.h"
 | 
|  #include "net/base/net_errors.h"
 | 
|  #include "net/socket/next_proto.h"
 | 
|  #include "net/socket/socket_test_util.h"
 | 
| @@ -89,11 +92,30 @@ class FakeWaiter : public WebSocketEndpointLockManager::Waiter {
 | 
|    bool called_;
 | 
|  };
 | 
|  
 | 
| +class BlockingWaiter : public FakeWaiter {
 | 
| + public:
 | 
| +  void WaitForLock() {
 | 
| +    while (!called()) {
 | 
| +      run_loop_.Run();
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  void GotEndpointLock() override {
 | 
| +    FakeWaiter::GotEndpointLock();
 | 
| +    run_loop_.Quit();
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  base::RunLoop run_loop_;
 | 
| +};
 | 
| +
 | 
|  class WebSocketEndpointLockManagerTest : public ::testing::Test {
 | 
|   protected:
 | 
|    WebSocketEndpointLockManagerTest()
 | 
|        : instance_(WebSocketEndpointLockManager::GetInstance()) {}
 | 
|    ~WebSocketEndpointLockManagerTest() override {
 | 
| +    // Permit any pending asynchronous unlock operations to complete.
 | 
| +    RunUntilIdle();
 | 
|      // If this check fails then subsequent tests may fail.
 | 
|      CHECK(instance_->IsEmpty());
 | 
|    }
 | 
| @@ -109,10 +131,14 @@ class WebSocketEndpointLockManagerTest : public ::testing::Test {
 | 
|    void UnlockDummyEndpoint(int times) {
 | 
|      for (int i = 0; i < times; ++i) {
 | 
|        instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +      RunUntilIdle();
 | 
|      }
 | 
|    }
 | 
|  
 | 
| +  static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }
 | 
| +
 | 
|    WebSocketEndpointLockManager* const instance_;
 | 
| +  ScopedWebSocketEndpointZeroUnlockDelay zero_unlock_delay_;
 | 
|  };
 | 
|  
 | 
|  TEST_F(WebSocketEndpointLockManagerTest, GetInstanceWorks) {
 | 
| @@ -131,6 +157,7 @@ TEST_F(WebSocketEndpointLockManagerTest, LockEndpointReturnsOkOnce) {
 | 
|  TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockNotCalledOnOk) {
 | 
|    FakeWaiter waiter;
 | 
|    EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &waiter));
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_FALSE(waiter.called());
 | 
|  
 | 
|    UnlockDummyEndpoint(1);
 | 
| @@ -141,6 +168,7 @@ TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockNotCalledImmediately) {
 | 
|    EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &waiters[0]));
 | 
|    EXPECT_EQ(ERR_IO_PENDING,
 | 
|              instance()->LockEndpoint(DummyEndpoint(), &waiters[1]));
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_FALSE(waiters[1].called());
 | 
|  
 | 
|    UnlockDummyEndpoint(2);
 | 
| @@ -152,6 +180,7 @@ TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockCalledWhenUnlocked) {
 | 
|    EXPECT_EQ(ERR_IO_PENDING,
 | 
|              instance()->LockEndpoint(DummyEndpoint(), &waiters[1]));
 | 
|    instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_TRUE(waiters[1].called());
 | 
|  
 | 
|    UnlockDummyEndpoint(1);
 | 
| @@ -169,6 +198,7 @@ TEST_F(WebSocketEndpointLockManagerTest,
 | 
|    }
 | 
|  
 | 
|    instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  RunUntilIdle();
 | 
|  
 | 
|    FakeWaiter second_lock_holder;
 | 
|    EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &second_lock_holder));
 | 
| @@ -185,6 +215,7 @@ TEST_F(WebSocketEndpointLockManagerTest, RememberSocketWorks) {
 | 
|  
 | 
|    instance()->RememberSocket(&dummy_socket, DummyEndpoint());
 | 
|    instance()->UnlockSocket(&dummy_socket);
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_TRUE(waiters[1].called());
 | 
|  
 | 
|    UnlockDummyEndpoint(1);
 | 
| @@ -199,6 +230,7 @@ TEST_F(WebSocketEndpointLockManagerTest, SocketAssociationForgottenOnUnlock) {
 | 
|    EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &waiter));
 | 
|    instance()->RememberSocket(&dummy_socket, DummyEndpoint());
 | 
|    instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_TRUE(instance()->IsEmpty());
 | 
|  }
 | 
|  
 | 
| @@ -213,12 +245,72 @@ TEST_F(WebSocketEndpointLockManagerTest, NextWaiterCanCallRememberSocketAgain) {
 | 
|  
 | 
|    instance()->RememberSocket(&dummy_sockets[0], DummyEndpoint());
 | 
|    instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  RunUntilIdle();
 | 
|    EXPECT_TRUE(waiters[1].called());
 | 
|    instance()->RememberSocket(&dummy_sockets[1], DummyEndpoint());
 | 
|  
 | 
|    UnlockDummyEndpoint(1);
 | 
|  }
 | 
|  
 | 
| +// Calling UnlockSocket() after UnlockEndpoint() does nothing.
 | 
| +TEST_F(WebSocketEndpointLockManagerTest,
 | 
| +       UnlockSocketAfterUnlockEndpointDoesNothing) {
 | 
| +  FakeWaiter waiters[3];
 | 
| +  FakeStreamSocket dummy_socket;
 | 
| +
 | 
| +  EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &waiters[0]));
 | 
| +  EXPECT_EQ(ERR_IO_PENDING,
 | 
| +            instance()->LockEndpoint(DummyEndpoint(), &waiters[1]));
 | 
| +  EXPECT_EQ(ERR_IO_PENDING,
 | 
| +            instance()->LockEndpoint(DummyEndpoint(), &waiters[2]));
 | 
| +  instance()->RememberSocket(&dummy_socket, DummyEndpoint());
 | 
| +  instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  instance()->UnlockSocket(&dummy_socket);
 | 
| +  RunUntilIdle();
 | 
| +  EXPECT_TRUE(waiters[1].called());
 | 
| +  EXPECT_FALSE(waiters[2].called());
 | 
| +
 | 
| +  UnlockDummyEndpoint(2);
 | 
| +}
 | 
| +
 | 
| +// UnlockEndpoint() should always be asynchronous.
 | 
| +TEST_F(WebSocketEndpointLockManagerTest, UnlockEndpointIsAsynchronous) {
 | 
| +  FakeWaiter waiters[2];
 | 
| +  EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &waiters[0]));
 | 
| +  EXPECT_EQ(ERR_IO_PENDING,
 | 
| +            instance()->LockEndpoint(DummyEndpoint(), &waiters[1]));
 | 
| +
 | 
| +  instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  EXPECT_FALSE(waiters[1].called());
 | 
| +  RunUntilIdle();
 | 
| +  EXPECT_TRUE(waiters[1].called());
 | 
| +
 | 
| +  UnlockDummyEndpoint(1);
 | 
| +}
 | 
| +
 | 
| +// UnlockEndpoint() should normally have a delay.
 | 
| +TEST_F(WebSocketEndpointLockManagerTest, UnlockEndpointIsDelayed) {
 | 
| +  using base::TimeTicks;
 | 
| +
 | 
| +  const base::TimeDelta unlock_delay = base::TimeDelta::FromMilliseconds(1);
 | 
| +  instance()->SetUnlockDelayForTesting(unlock_delay);
 | 
| +  FakeWaiter fake_waiter;
 | 
| +  BlockingWaiter blocking_waiter;
 | 
| +  EXPECT_EQ(OK, instance()->LockEndpoint(DummyEndpoint(), &fake_waiter));
 | 
| +  EXPECT_EQ(ERR_IO_PENDING,
 | 
| +            instance()->LockEndpoint(DummyEndpoint(), &blocking_waiter));
 | 
| +
 | 
| +  TimeTicks before_unlock = TimeTicks::Now();
 | 
| +  instance()->UnlockEndpoint(DummyEndpoint());
 | 
| +  RunUntilIdle();
 | 
| +  EXPECT_FALSE(blocking_waiter.called());
 | 
| +  blocking_waiter.WaitForLock();
 | 
| +  TimeTicks after_unlock = TimeTicks::Now();
 | 
| +  EXPECT_GE(after_unlock - before_unlock, unlock_delay);
 | 
| +  instance()->SetUnlockDelayForTesting(base::TimeDelta());
 | 
| +  UnlockDummyEndpoint(1);
 | 
| +}
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  }  // namespace net
 | 
| 
 |