Home | History | Annotate | Download | only in cast_channel
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/bind.h"
      6 #include "base/command_line.h"
      7 #include "base/files/file_path.h"
      8 #include "chrome/browser/extensions/extension_apitest.h"
      9 #include "chrome/browser/extensions/extension_function_test_utils.h"
     10 #include "chrome/browser/extensions/extension_service.h"
     11 #include "chrome/browser/ui/browser.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "extensions/browser/api/cast_channel/cast_channel_api.h"
     14 #include "extensions/browser/api/cast_channel/cast_socket.h"
     15 #include "extensions/browser/api/cast_channel/logger.h"
     16 #include "extensions/common/api/cast_channel.h"
     17 #include "extensions/common/switches.h"
     18 #include "extensions/common/test_util.h"
     19 #include "extensions/test/result_catcher.h"
     20 #include "net/base/capturing_net_log.h"
     21 #include "net/base/completion_callback.h"
     22 #include "net/base/net_errors.h"
     23 #include "testing/gmock/include/gmock/gmock.h"
     24 #include "testing/gmock_mutant.h"
     25 
     26 // TODO(mfoltz): Mock out the ApiResourceManager to resolve threading issues
     27 // (crbug.com/398242) and simulate unloading of the extension.
     28 
     29 namespace cast_channel = extensions::core_api::cast_channel;
     30 using cast_channel::CastSocket;
     31 using cast_channel::ChannelError;
     32 using cast_channel::ErrorInfo;
     33 using cast_channel::Logger;
     34 using cast_channel::MessageInfo;
     35 using cast_channel::ReadyState;
     36 using extensions::Extension;
     37 
     38 namespace utils = extension_function_test_utils;
     39 
     40 using ::testing::_;
     41 using ::testing::A;
     42 using ::testing::DoAll;
     43 using ::testing::Invoke;
     44 using ::testing::InSequence;
     45 using ::testing::Return;
     46 
     47 namespace {
     48 
     49 const char kTestExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf";
     50 const int64 kTimeoutMs = 10000;
     51 
     52 static void FillMessageInfo(MessageInfo* message_info,
     53                             const std::string& message) {
     54   message_info->namespace_ = "foo";
     55   message_info->source_id = "src";
     56   message_info->destination_id = "dest";
     57   message_info->data.reset(new base::StringValue(message));
     58 }
     59 
     60 ACTION_TEMPLATE(InvokeCompletionCallback,
     61                 HAS_1_TEMPLATE_PARAMS(int, k),
     62                 AND_1_VALUE_PARAMS(result)) {
     63   ::std::tr1::get<k>(args).Run(result);
     64 }
     65 
     66 ACTION_P2(InvokeDelegateOnError, api_test, api) {
     67   api_test->CallOnError(api);
     68 }
     69 
     70 class MockCastSocket : public CastSocket {
     71  public:
     72   explicit MockCastSocket(CastSocket::Delegate* delegate,
     73                           net::IPEndPoint ip_endpoint,
     74                           net::NetLog* net_log,
     75                           const scoped_refptr<Logger>& logger)
     76       : CastSocket(kTestExtensionId,
     77                    ip_endpoint,
     78                    cast_channel::CHANNEL_AUTH_TYPE_SSL,
     79                    delegate,
     80                    net_log,
     81                    base::TimeDelta::FromMilliseconds(kTimeoutMs),
     82                    logger) {}
     83   virtual ~MockCastSocket() {}
     84 
     85   MOCK_METHOD1(Connect, void(const net::CompletionCallback& callback));
     86   MOCK_METHOD2(SendMessage, void(const MessageInfo& message,
     87                                  const net::CompletionCallback& callback));
     88   MOCK_METHOD1(Close, void(const net::CompletionCallback& callback));
     89   MOCK_CONST_METHOD0(ready_state, cast_channel::ReadyState());
     90   MOCK_CONST_METHOD0(error_state, cast_channel::ChannelError());
     91 };
     92 
     93 }  // namespace
     94 
     95 class CastChannelAPITest : public ExtensionApiTest {
     96  public:
     97   CastChannelAPITest() {}
     98 
     99   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    100     ExtensionApiTest::SetUpCommandLine(command_line);
    101     command_line->AppendSwitchASCII(
    102         extensions::switches::kWhitelistedExtensionID,
    103         kTestExtensionId);
    104   }
    105 
    106   void SetUpMockCastSocket() {
    107     extensions::CastChannelAPI* api = GetApi();
    108     net::IPAddressNumber ip_number;
    109     net::ParseIPLiteralToNumber("192.168.1.1", &ip_number);
    110     net::IPEndPoint ip_endpoint(ip_number, 8009);
    111     mock_cast_socket_ = new MockCastSocket(
    112         api, ip_endpoint, &capturing_net_log_, api->GetLogger());
    113     // Transfers ownership of the socket.
    114     api->SetSocketForTest(
    115         make_scoped_ptr<CastSocket>(mock_cast_socket_).Pass());
    116   }
    117 
    118   void SetUpOpenSendClose() {
    119     SetUpMockCastSocket();
    120     EXPECT_CALL(*mock_cast_socket_, error_state())
    121         .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE));
    122     {
    123       InSequence sequence;
    124       EXPECT_CALL(*mock_cast_socket_, Connect(_))
    125           .WillOnce(InvokeCompletionCallback<0>(net::OK));
    126       EXPECT_CALL(*mock_cast_socket_, ready_state())
    127           .WillOnce(Return(cast_channel::READY_STATE_OPEN));
    128       EXPECT_CALL(*mock_cast_socket_, SendMessage(A<const MessageInfo&>(), _))
    129           .WillOnce(InvokeCompletionCallback<1>(net::OK));
    130       EXPECT_CALL(*mock_cast_socket_, ready_state())
    131           .WillOnce(Return(cast_channel::READY_STATE_OPEN));
    132       EXPECT_CALL(*mock_cast_socket_, Close(_))
    133           .WillOnce(InvokeCompletionCallback<0>(net::OK));
    134       EXPECT_CALL(*mock_cast_socket_, ready_state())
    135           .WillOnce(Return(cast_channel::READY_STATE_CLOSED));
    136     }
    137   }
    138 
    139   extensions::CastChannelAPI* GetApi() {
    140     return extensions::CastChannelAPI::Get(profile());
    141   }
    142 
    143   void CallOnError(extensions::CastChannelAPI* api) {
    144     cast_channel::LastErrors last_errors;
    145     last_errors.challenge_reply_error_type =
    146         cast_channel::proto::CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED;
    147     last_errors.nss_error_code = -8164;
    148     api->OnError(mock_cast_socket_,
    149                  cast_channel::CHANNEL_ERROR_CONNECT_ERROR,
    150                  last_errors);
    151   }
    152 
    153  protected:
    154   void CallOnMessage(const std::string& message) {
    155     content::BrowserThread::PostTask(
    156         content::BrowserThread::IO,
    157         FROM_HERE,
    158         base::Bind(&CastChannelAPITest::DoCallOnMessage, this,
    159                    GetApi(), mock_cast_socket_, message));
    160   }
    161 
    162   void DoCallOnMessage(extensions::CastChannelAPI* api,
    163                        MockCastSocket* cast_socket,
    164                        const std::string& message) {
    165     MessageInfo message_info;
    166     FillMessageInfo(&message_info, message);
    167     api->OnMessage(cast_socket, message_info);
    168   }
    169 
    170   extensions::CastChannelOpenFunction* CreateOpenFunction(
    171         scoped_refptr<Extension> extension) {
    172     extensions::CastChannelOpenFunction* cast_channel_open_function =
    173       new extensions::CastChannelOpenFunction;
    174     cast_channel_open_function->set_extension(extension.get());
    175     return cast_channel_open_function;
    176   }
    177 
    178   extensions::CastChannelSendFunction* CreateSendFunction(
    179         scoped_refptr<Extension> extension) {
    180     extensions::CastChannelSendFunction* cast_channel_send_function =
    181       new extensions::CastChannelSendFunction;
    182     cast_channel_send_function->set_extension(extension.get());
    183     return cast_channel_send_function;
    184   }
    185 
    186   MockCastSocket* mock_cast_socket_;
    187   net::CapturingNetLog capturing_net_log_;
    188 };
    189 
    190 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest
    191 // always return true without actually running the test. Remove when fixed.
    192 #if defined(OS_WIN) && !defined(NDEBUG)
    193 #define MAYBE_TestOpenSendClose DISABLED_TestOpenSendClose
    194 #else
    195 #define MAYBE_TestOpenSendClose TestOpenSendClose
    196 #endif
    197 // Test loading extension, opening a channel with ConnectInfo, adding a
    198 // listener, writing, reading, and closing.
    199 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendClose) {
    200   SetUpOpenSendClose();
    201 
    202   EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
    203                                   "test_open_send_close.html"));
    204 }
    205 
    206 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest
    207 // always return true without actually running the test. Remove when fixed.
    208 #if defined(OS_WIN) && !defined(NDEBUG)
    209 #define MAYBE_TestOpenSendCloseWithUrl DISABLED_TestOpenSendCloseWithUrl
    210 #else
    211 #define MAYBE_TestOpenSendCloseWithUrl TestOpenSendCloseWithUrl
    212 #endif
    213 // Test loading extension, opening a channel with a URL, adding a listener,
    214 // writing, reading, and closing.
    215 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendCloseWithUrl) {
    216   SetUpOpenSendClose();
    217 
    218   EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
    219                                   "test_open_send_close_url.html"));
    220 }
    221 
    222 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest
    223 // always return true without actually running the test. Remove when fixed.
    224 #if defined(OS_WIN) && !defined(NDEBUG)
    225 #define MAYBE_TestOpenReceiveClose DISABLED_TestOpenReceiveClose
    226 #else
    227 #define MAYBE_TestOpenReceiveClose TestOpenReceiveClose
    228 #endif
    229 // Test loading extension, opening a channel, adding a listener,
    230 // writing, reading, and closing.
    231 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenReceiveClose) {
    232   SetUpMockCastSocket();
    233   EXPECT_CALL(*mock_cast_socket_, error_state())
    234       .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE));
    235 
    236   {
    237     InSequence sequence;
    238     EXPECT_CALL(*mock_cast_socket_, Connect(_))
    239         .WillOnce(InvokeCompletionCallback<0>(net::OK));
    240     EXPECT_CALL(*mock_cast_socket_, ready_state())
    241         .Times(3)
    242         .WillRepeatedly(Return(cast_channel::READY_STATE_OPEN));
    243     EXPECT_CALL(*mock_cast_socket_, Close(_))
    244         .WillOnce(InvokeCompletionCallback<0>(net::OK));
    245     EXPECT_CALL(*mock_cast_socket_, ready_state())
    246         .WillOnce(Return(cast_channel::READY_STATE_CLOSED));
    247   }
    248 
    249   EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
    250                                   "test_open_receive_close.html"));
    251 
    252   extensions::ResultCatcher catcher;
    253   CallOnMessage("some-message");
    254   CallOnMessage("some-message");
    255   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
    256 }
    257 
    258 // TODO(imcheng): Win Dbg has a workaround that makes RunExtensionSubtest
    259 // always return true without actually running the test. Remove when fixed.
    260 #if defined(OS_WIN) && !defined(NDEBUG)
    261 #define MAYBE_TestGetLogs DISABLED_TestGetLogs
    262 #else
    263 #define MAYBE_TestGetLogs TestGetLogs
    264 #endif
    265 // Test loading extension, execute a open-send-close sequence, then get logs.
    266 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestGetLogs) {
    267   SetUpOpenSendClose();
    268 
    269   EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_get_logs.html"));
    270 }
    271 
    272 // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest
    273 // always return true without actually running the test. Remove when fixed.
    274 #if defined(OS_WIN) && !defined(NDEBUG)
    275 #define MAYBE_TestOpenError DISABLED_TestOpenError
    276 #else
    277 #define MAYBE_TestOpenError TestOpenError
    278 #endif
    279 // Test the case when socket open results in an error.
    280 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenError) {
    281   SetUpMockCastSocket();
    282 
    283   EXPECT_CALL(*mock_cast_socket_, Connect(_))
    284       .WillOnce(DoAll(InvokeDelegateOnError(this, GetApi()),
    285                       InvokeCompletionCallback<0>(net::ERR_CONNECTION_FAILED)));
    286   EXPECT_CALL(*mock_cast_socket_, error_state())
    287       .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_CONNECT_ERROR));
    288   EXPECT_CALL(*mock_cast_socket_, ready_state())
    289       .WillRepeatedly(Return(cast_channel::READY_STATE_CLOSED));
    290   EXPECT_CALL(*mock_cast_socket_, Close(_))
    291       .WillOnce(InvokeCompletionCallback<0>(net::OK));
    292 
    293   EXPECT_TRUE(RunExtensionSubtest("cast_channel/api",
    294                                   "test_open_error.html"));
    295 }
    296 
    297 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestOpenInvalidConnectInfo) {
    298   scoped_refptr<Extension> empty_extension =
    299       extensions::test_util::CreateEmptyExtension();
    300   scoped_refptr<extensions::CastChannelOpenFunction> cast_channel_open_function;
    301 
    302   // Invalid URL
    303   // TODO(mfoltz): Remove this test case when fixing crbug.com/331905
    304   cast_channel_open_function = CreateOpenFunction(empty_extension);
    305   std::string error(utils::RunFunctionAndReturnError(
    306       cast_channel_open_function.get(), "[\"blargh\"]", browser()));
    307   EXPECT_EQ(error, "Invalid connect_info (invalid Cast URL blargh)");
    308 
    309   // Wrong type
    310   // TODO(mfoltz): Remove this test case when fixing crbug.com/331905
    311   cast_channel_open_function = CreateOpenFunction(empty_extension);
    312   error = utils::RunFunctionAndReturnError(
    313       cast_channel_open_function.get(),
    314       "[123]", browser());
    315   EXPECT_EQ(error, "Invalid connect_info (unknown type)");
    316 
    317   // Invalid IP address
    318   cast_channel_open_function = CreateOpenFunction(empty_extension);
    319   error = utils::RunFunctionAndReturnError(
    320       cast_channel_open_function.get(),
    321       "[{\"ipAddress\": \"invalid_ip\", \"port\": 8009, \"auth\": \"ssl\"}]",
    322       browser());
    323   EXPECT_EQ(error, "Invalid connect_info (invalid IP address)");
    324 
    325   // Invalid port
    326   cast_channel_open_function = CreateOpenFunction(empty_extension);
    327   error = utils::RunFunctionAndReturnError(
    328       cast_channel_open_function.get(),
    329       "[{\"ipAddress\": \"127.0.0.1\", \"port\": -200, \"auth\": \"ssl\"}]",
    330       browser());
    331   EXPECT_EQ(error, "Invalid connect_info (invalid port)");
    332 
    333   // Auth not set
    334   cast_channel_open_function = CreateOpenFunction(empty_extension);
    335   error = utils::RunFunctionAndReturnError(
    336       cast_channel_open_function.get(),
    337       "[{\"ipAddress\": \"127.0.0.1\", \"port\": 8009}]",
    338       browser());
    339   EXPECT_EQ(error, "connect_info.auth is required");
    340 }
    341 
    342 IN_PROC_BROWSER_TEST_F(CastChannelAPITest, TestSendInvalidMessageInfo) {
    343   scoped_refptr<Extension> empty_extension(
    344       extensions::test_util::CreateEmptyExtension());
    345   scoped_refptr<extensions::CastChannelSendFunction> cast_channel_send_function;
    346 
    347   // Numbers are not supported
    348   cast_channel_send_function = CreateSendFunction(empty_extension);
    349   std::string error(utils::RunFunctionAndReturnError(
    350       cast_channel_send_function.get(),
    351       "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
    352       "\"connectInfo\": "
    353       "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
    354       "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
    355       "{\"namespace_\": \"foo\", \"sourceId\": \"src\", "
    356       "\"destinationId\": \"dest\", \"data\": 1235}]",
    357       browser()));
    358   EXPECT_EQ(error, "Invalid type of message_info.data");
    359 
    360   // Missing namespace_
    361   cast_channel_send_function = CreateSendFunction(empty_extension);
    362   error = utils::RunFunctionAndReturnError(
    363       cast_channel_send_function.get(),
    364       "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
    365       "\"connectInfo\": "
    366       "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
    367       "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
    368       "{\"namespace_\": \"\", \"sourceId\": \"src\", "
    369       "\"destinationId\": \"dest\", \"data\": \"data\"}]",
    370       browser());
    371   EXPECT_EQ(error, "message_info.namespace_ is required");
    372 
    373   // Missing source_id
    374   cast_channel_send_function = CreateSendFunction(empty_extension);
    375   error = utils::RunFunctionAndReturnError(
    376       cast_channel_send_function.get(),
    377       "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
    378       "\"connectInfo\": "
    379       "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
    380       "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
    381       "{\"namespace_\": \"foo\", \"sourceId\": \"\", "
    382       "\"destinationId\": \"dest\", \"data\": \"data\"}]",
    383       browser());
    384   EXPECT_EQ(error, "message_info.source_id is required");
    385 
    386   // Missing destination_id
    387   cast_channel_send_function = CreateSendFunction(empty_extension);
    388   error = utils::RunFunctionAndReturnError(
    389       cast_channel_send_function.get(),
    390       "[{\"channelId\": 1, \"url\": \"cast://127.0.0.1:8009\", "
    391       "\"connectInfo\": "
    392       "{\"ipAddress\": \"127.0.0.1\", \"port\": 8009, "
    393       "\"auth\": \"ssl\"}, \"readyState\": \"open\"}, "
    394       "{\"namespace_\": \"foo\", \"sourceId\": \"src\", "
    395       "\"destinationId\": \"\", \"data\": \"data\"}]",
    396       browser());
    397   EXPECT_EQ(error, "message_info.destination_id is required");
    398 }
    399