Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc

Issue 494573002: A change for the setPause() api in chrome.sockets.tcp: Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cosmetics and commentary. Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <sched.h>
6 #include <unistd.h>
7
5 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/run_loop.h"
6 #include "base/strings/stringprintf.h" 10 #include "base/strings/stringprintf.h"
11 #include "base/synchronization/lock.h"
7 #include "extensions/browser/api/dns/host_resolver_wrapper.h" 12 #include "extensions/browser/api/dns/host_resolver_wrapper.h"
8 #include "extensions/browser/api/dns/mock_host_resolver_creator.h" 13 #include "extensions/browser/api/dns/mock_host_resolver_creator.h"
9 #include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" 14 #include "extensions/browser/api/sockets_tcp/sockets_tcp_api.h"
10 #include "extensions/browser/api_test_utils.h" 15 #include "extensions/browser/api_test_utils.h"
16 #include "extensions/browser/event_router.h"
17 #include "extensions/browser/event_router_factory.h"
18 #include "extensions/browser/extension_function_dispatcher.h"
19 #include "extensions/browser/extension_prefs.h"
20 #include "extensions/common/api/sockets/sockets_manifest_data.h"
11 #include "extensions/common/extension.h" 21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_builder.h"
23 #include "extensions/common/manifest_constants.h"
12 #include "extensions/common/test_util.h" 24 #include "extensions/common/test_util.h"
25 #include "extensions/common/value_builder.h"
13 #include "extensions/shell/test/shell_apitest.h" 26 #include "extensions/shell/test/shell_apitest.h"
14 #include "extensions/shell/test/shell_test.h" 27 #include "extensions/shell/test/shell_test.h"
15 #include "extensions/test/extension_test_message_listener.h" 28 #include "extensions/test/extension_test_message_listener.h"
16 #include "extensions/test/result_catcher.h" 29 #include "extensions/test/result_catcher.h"
30 #include "net/base/io_buffer.h"
31 #include "net/base/net_errors.h"
17 #include "net/dns/mock_host_resolver.h" 32 #include "net/dns/mock_host_resolver.h"
33 #include "net/socket/tcp_server_socket.h"
18 #include "net/test/spawned_test_server/spawned_test_server.h" 34 #include "net/test/spawned_test_server/spawned_test_server.h"
35 #include "testing/gmock/include/gmock/gmock.h"
19 36
20 namespace extensions { 37 namespace extensions {
38 using ::testing::_;
21 39
22 const std::string kHostname = "127.0.0.1"; 40 const std::string kHostname = "127.0.0.1";
23 41
42 // Runs a single-connection TCP echo server on a separate thread.
43 class LocalServer : public base::RefCountedThreadSafe<LocalServer> {
44 public:
45 LocalServer()
46 : echo_socket_(nullptr, net::NetLog::Source()),
47 thread_("localserver"),
48 active_(false),
49 issued_write_count_(0),
50 finished_write_count_(0),
51 port_(-1),
52 sock_(nullptr),
53 state_cvar_(&state_lock_) {}
54
55 void Start() {
56 // Should run in an IO thread (or at least one with a MessageLoopForIO)
57 base::Thread::Options thread_opts(base::MessageLoop::TYPE_IO, 0);
58 thread_.StartWithOptions(thread_opts);
59 thread_.WaitUntilThreadStarted();
60 thread_.message_loop()->PostTask(
61 FROM_HERE, base::Bind(&LocalServer::IOStart, base::Unretained(this)));
62 }
63
64 void Close() {
65 thread_.message_loop()->PostTask(FROM_HERE,
66 base::Bind(&LocalServer::DoClose, this));
67 }
68
69 // This is -1 until IOStart() has run on this LocalServer's IO thread
70 int port() {
71 int ret = -1;
72 do {
73 {
74 base::AutoLock l(state_lock_);
75 ret = port_;
76 }
77 if (port_ < 0) {
78 LOG(INFO) << "port(): waiting for thread to finish doing whatever it's "
79 "doing.";
80 usleep(25000);
81 }
82 } while (ret < 0);
83 return ret;
84 }
85
86 // Whether Accept() completed, making sock_ valid.
87 bool accepted() {
88 base::AutoLock l(state_lock_);
89 return active_;
90 }
91
92 // Returns the total number of issued writes. If this call returns N, then
93 // you can verify that the underlying write finished with
94 // wait_for_n_finished_writes(N).
95 int Write(const char* message) { return Write(std::string(message)); }
96
97 int Write(const std::string& message) {
98 thread_.message_loop()->PostTask(
99 FROM_HERE,
100 base::Bind(&LocalServer::DoWrite, base::Unretained(this), message));
101 return ++issued_write_count_;
102 }
103
104 int issued_write_count() {
105 base::AutoLock l(state_lock_);
106 return issued_write_count_;
107 }
108
109 int finished_write_count() {
110 base::AutoLock l(state_lock_);
111 return finished_write_count_;
112 }
113
114 void WaitForNFinishedWrites(int nr) {
115 base::AutoLock l(state_lock_);
116 while (finished_write_count_ < nr) {
117 state_cvar_.Wait();
118 }
119 }
120
121 private:
122 net::TCPServerSocket echo_socket_;
123 base::Thread thread_; // Our IO thread.
124 bool active_;
125 int issued_write_count_; // How many calls to Write() have been made?
126 int finished_write_count_; // How many calls to FinishWrite() have been made?
127 int port_;
128 scoped_ptr<net::StreamSocket> sock_; // not for use on the primary thread!
129 base::Lock state_lock_;
130 base::ConditionVariable state_cvar_;
131
132 friend class base::RefCountedThreadSafe<LocalServer>;
133 virtual ~LocalServer() {
134 bool active;
135 {
136 base::AutoLock l(state_lock_);
137 active = active_;
138 }
139 DCHECK(!active);
140 }
141
142 void FinishWrite(int result) {
143 base::AutoLock l(state_lock_);
144 ASSERT_GT(result, 0);
145 LOG(INFO) << "FinishWrite(result=" << result << ")";
146 finished_write_count_++;
147 state_cvar_.Broadcast();
148 }
149
150 void DoWrite(const std::string& message) {
151 {
152 base::AutoLock l(state_lock_);
153 DCHECK(active_);
154 issued_write_count_++;
155 }
156 scoped_refptr<net::StringIOBuffer> buf(new net::StringIOBuffer(message));
157 LOG(INFO) << "Write(" << message << ")";
158 int result = sock_->Write(
159 buf.get(), buf->size(),
160 base::Bind(&LocalServer::FinishWrite, base::Unretained(this)));
161 if (result != net::ERR_IO_PENDING) {
162 FinishWrite(result);
163 }
164 }
165
166 void IOStart() {
167 base::AutoLock l(state_lock_);
168 net::IPAddressNumber localhost;
169 net::IPEndPoint local;
170 net::ParseIPLiteralToNumber("127.0.0.1", &localhost);
171 // setup echo_socket_.
172 echo_socket_.Listen(net::IPEndPoint(localhost, 0), 5);
173 echo_socket_.GetLocalAddress(&local);
174 port_ = local.port();
175 echo_socket_.Accept(
176 &sock_, base::Bind(&LocalServer::AcceptedSock, base::Unretained(this)));
177 }
178
179 void AcceptedSock(int result) {
180 base::AutoLock l(state_lock_);
181 ASSERT_EQ(net::OK, result);
182 active_ = true;
183 }
184
185 void DoClose() {
186 base::AutoLock l(state_lock_);
187 // Repeatedly calling this is harmless.
188 sock_->Disconnect();
189 active_ = false;
190 }
191 };
192
193 class MockEventRouter : public EventRouter {
194 public:
195 MockEventRouter(content::BrowserContext* ctx, ExtensionPrefs* prefs)
196 : EventRouter(ctx, prefs), do_dispatch_(true), call_count_(0) {}
197
198 MOCK_METHOD3(DoDispatchEventToExtension,
199 void(const std::string&, const std::string&, Event*));
200
201 void set_dispatch(bool v) { do_dispatch_ = v; }
202 int call_count() { return call_count_; }
203
204 // Trampoline method to work around GMOCK/scoped_ptr problems.
205 void DispatchEventToExtension(const std::string& str,
206 scoped_ptr<Event> evt) override {
207 LOG(INFO) << "MockEventRouter: Got event: " << evt->event_name;
208 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
209 call_count_++;
210 DoDispatchEventToExtension(str, evt->event_name, evt.get());
211 if (do_dispatch_) {
212 EventRouter::DispatchEventToExtension(str,
213 scoped_ptr<Event>(evt.release()));
214 }
215 }
216
217 private:
218 bool do_dispatch_;
219 int call_count_;
220 };
221
24 class SocketsTcpApiTest : public ShellApiTest { 222 class SocketsTcpApiTest : public ShellApiTest {
25 public: 223 public:
26 SocketsTcpApiTest() 224 SocketsTcpApiTest()
27 : resolver_event_(true, false), 225 : resolver_event_(true, false),
28 resolver_creator_(new MockHostResolverCreator()) {} 226 resolver_creator_(new MockHostResolverCreator()) {}
29 227
228 void EnableMockRouter() {
229 // Setup a Mock EventRouter to catch output from the
230 // TCPSocketEventDispatcher
231 EventRouterFactory::GetInstance()->SetTestingFactoryAndUse(
232 browser_context(), &BuildMockRouter);
233 }
234
30 void SetUpOnMainThread() override { 235 void SetUpOnMainThread() override {
31 ShellApiTest::SetUpOnMainThread(); 236 ShellApiTest::SetUpOnMainThread();
32 237
33 HostResolverWrapper::GetInstance()->SetHostResolverForTesting( 238 HostResolverWrapper::GetInstance()->SetHostResolverForTesting(
34 resolver_creator_->CreateMockHostResolver()); 239 resolver_creator_->CreateMockHostResolver());
35 } 240 }
36 241
37 void TearDownOnMainThread() override { 242 void TearDownOnMainThread() override {
243 LOG(INFO) << "TearDown starting";
38 HostResolverWrapper::GetInstance()->SetHostResolverForTesting(NULL); 244 HostResolverWrapper::GetInstance()->SetHostResolverForTesting(NULL);
39 resolver_creator_->DeleteMockHostResolver(); 245 resolver_creator_->DeleteMockHostResolver();
40 246
247 LOG(INFO) << "super TearDown starting";
41 ShellApiTest::TearDownOnMainThread(); 248 ShellApiTest::TearDownOnMainThread();
249 LOG(INFO) << "TearDown done";
250 }
251
252 protected:
253 // Makes the extension if we haven't built it yet.
254 scoped_refptr<Extension> extension() {
255 if (!extension_.get()) {
256 DictionaryBuilder app;
257 app.Set("background",
258 DictionaryBuilder().Set("scripts",
259 ListBuilder().Append("background.J's")));
260 manifest_builder_.Set("name", "Test")
261 .Set("version", "1.0")
262 .Set(manifest_keys::kApp, app);
263 extension_ = ExtensionBuilder().SetManifest(manifest_builder_).Build();
264 }
265 return extension_;
266 }
267
268 static scoped_ptr<KeyedService> BuildMockRouter(
269 content::BrowserContext* ctx) {
270 return scoped_ptr<KeyedService>(
271 new MockEventRouter(ctx, ExtensionPrefs::Get(ctx)));
272 }
273
274 MockEventRouter& mock_router() {
275 return *static_cast<MockEventRouter*>(EventRouter::Get(browser_context()));
276 }
277
278 template <class F>
279 scoped_ptr<base::Value> CallFunction(std::string args) {
280 scoped_refptr<F> function = new F();
281 function->set_extension(extension());
282 return scoped_ptr<base::Value>(
283 api_test_utils::RunFunctionAndReturnSingleResult(function.get(), args,
284 browser_context()));
285 }
286
287 // Adds parts to the currently-building manifest. Call |extension()| to
288 // create the extension with the manifest.
289 void SetManifestPermissions(const std::string& perms) {
290 base::string16 error;
291 scoped_ptr<base::DictionaryValue> permissionsDict =
292 api_test_utils::ParseDictionary(perms);
293 // Validate the permissions.
294 DCHECK(extension_.get() == nullptr);
295 if (SocketsManifestData::FromValue(*permissionsDict.get(), &error).get()) {
296 DictionaryBuilder dbuilder(*permissionsDict.get());
297 manifest_builder_.Set(manifest_keys::kSockets, dbuilder);
298 } else {
299 LOG(ERROR) << "Failed to setup socket manifest permissions: " << error
300 << " for value: " << perms;
301 }
42 } 302 }
43 303
44 private: 304 private:
45 base::WaitableEvent resolver_event_; 305 base::WaitableEvent resolver_event_;
306 DictionaryBuilder manifest_builder_;
307 scoped_refptr<Extension> extension_;
46 308
47 // The MockHostResolver asserts that it's used on the same thread on which 309 // The MockHostResolver asserts that it's used on the same thread on which
48 // it's created, which is actually a stronger rule than its real counterpart. 310 // it's created, which is actually a stronger rule than its real counterpart.
49 // But that's fine; it's good practice. 311 // But that's fine; it's good practice.
50 scoped_refptr<MockHostResolverCreator> resolver_creator_; 312 scoped_refptr<MockHostResolverCreator> resolver_creator_;
51 }; 313 };
52 314
53 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateGood) { 315 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateIsGood) {
54 scoped_refptr<api::SocketsTcpCreateFunction> socket_create_function(
55 new api::SocketsTcpCreateFunction());
56 scoped_refptr<Extension> empty_extension = test_util::CreateEmptyExtension();
57
58 socket_create_function->set_extension(empty_extension.get());
59 socket_create_function->set_has_callback(true);
60
61 scoped_ptr<base::Value> result( 316 scoped_ptr<base::Value> result(
62 api_test_utils::RunFunctionAndReturnSingleResult( 317 CallFunction<api::SocketsTcpCreateFunction>("[]"));
63 socket_create_function.get(), "[]", browser_context()));
64 318
65 ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); 319 ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType());
66 base::DictionaryValue* value = 320 base::DictionaryValue* value =
67 static_cast<base::DictionaryValue*>(result.get()); 321 static_cast<base::DictionaryValue*>(result.get());
68 int socketId = -1; 322 int socketId = -1;
69 EXPECT_TRUE(value->GetInteger("socketId", &socketId)); 323 EXPECT_TRUE(value->GetInteger("socketId", &socketId));
70 ASSERT_TRUE(socketId > 0); 324 ASSERT_GT(socketId, 0);
71 } 325 }
72 326
73 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpExtension) { 327 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketTcpExtension) {
74 scoped_ptr<net::SpawnedTestServer> test_server(new net::SpawnedTestServer( 328 scoped_ptr<net::SpawnedTestServer> test_server(new net::SpawnedTestServer(
75 net::SpawnedTestServer::TYPE_TCP_ECHO, net::SpawnedTestServer::kLocalhost, 329 net::SpawnedTestServer::TYPE_TCP_ECHO, net::SpawnedTestServer::kLocalhost,
76 base::FilePath(FILE_PATH_LITERAL("net/data")))); 330 base::FilePath(FILE_PATH_LITERAL("net/data"))));
77 EXPECT_TRUE(test_server->Start()); 331 EXPECT_TRUE(test_server->Start());
78 332
79 net::HostPortPair host_port_pair = test_server->host_port_pair(); 333 net::HostPortPair host_port_pair = test_server->host_port_pair();
80 int port = host_port_pair.port(); 334 int port = host_port_pair.port();
81 ASSERT_TRUE(port > 0); 335 ASSERT_GT(port, 0);
82 336
83 // Test that connect() is properly resolving hostnames. 337 // Test that connect() is properly resolving hostnames.
84 host_port_pair.set_host("lOcAlHoSt"); 338 host_port_pair.set_host("lOcAlHoSt");
85 339
86 ResultCatcher catcher; 340 ResultCatcher catcher;
87 catcher.RestrictToBrowserContext(browser_context()); 341 catcher.RestrictToBrowserContext(browser_context());
88 342
89 ExtensionTestMessageListener listener("info_please", true); 343 ExtensionTestMessageListener listener("info_please", true);
90 344
91 ASSERT_TRUE(LoadApp("sockets_tcp/api")); 345 ASSERT_TRUE(LoadApp("sockets_tcp/api"));
(...skipping 21 matching lines...) Expand all
113 ExtensionTestMessageListener listener("info_please", true); 367 ExtensionTestMessageListener listener("info_please", true);
114 368
115 ASSERT_TRUE(LoadApp("sockets_tcp/api")); 369 ASSERT_TRUE(LoadApp("sockets_tcp/api"));
116 EXPECT_TRUE(listener.WaitUntilSatisfied()); 370 EXPECT_TRUE(listener.WaitUntilSatisfied());
117 listener.Reply(base::StringPrintf( 371 listener.Reply(base::StringPrintf(
118 "https:%s:%d", https_host_port_pair.host().c_str(), https_port)); 372 "https:%s:%d", https_host_port_pair.host().c_str(), https_port));
119 373
120 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); 374 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
121 } 375 }
122 376
377 IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpPauseIsGood) {
378 EnableMockRouter();
379
380 scoped_refptr<LocalServer> server(new LocalServer);
381 server->Start();
382 int port = server->port();
383
384 LOG(INFO) << "Port " << port;
385 SetManifestPermissions(base::StringPrintf(
386 "{ \"tcp\": { \"connect\": \"localhost:%d\" } }", port));
387
388 scoped_ptr<base::Value> create(
389 CallFunction<api::SocketsTcpCreateFunction>("[{\"persistent\":true}]"));
390 int sockfd;
391 base::DictionaryValue* create_val =
392 static_cast<base::DictionaryValue*>(create.get());
393 EXPECT_TRUE(create_val->GetInteger("socketId", &sockfd));
394 ASSERT_GT(sockfd, 0);
395
396 scoped_ptr<base::Value> conn_result =
397 CallFunction<api::SocketsTcpConnectFunction>(
398 base::StringPrintf("[%d, \"localhost\", %d]", sockfd, port));
399 LOG(INFO) << "Finished running connect()";
400
401 // Require that we receive two OnReceive events, with a "MARK" event in the
402 // middle. So we can make sure that we don't receive an OnReceive while
403 // paused.
404 {
405 ::testing::InSequence seq;
406 EXPECT_CALL(mock_router(),
407 DoDispatchEventToExtension(_, "sockets.tcp.onReceive", _))
408 .Times(1);
409 EXPECT_CALL(mock_router(), DoDispatchEventToExtension(_, "MARK", _))
410 .Times(1);
411 EXPECT_CALL(mock_router(),
412 DoDispatchEventToExtension(_, "sockets.tcp.onReceive", _))
413 .Times(1)
414 .RetiresOnSaturation();
415 }
416
417 // Write the first message, and wait for it to have been issued.
418 server->Write("first message\n");
419 server->WaitForNFinishedWrites(1);
420 // Make sure that the onReceive comes before the MARK gets sent over.
421 while (mock_router().call_count() < 1) {
422 LOG(INFO) << "RunLoop call {";
423 base::RunLoop().RunUntilIdle();
424 LOG(INFO) << "} RunLoop call finished.";
425 }
426
427 CallFunction<api::SocketsTcpSetPausedFunction>(
428 base::StringPrintf("[%d, true]", sockfd));
429
430 server->Write("second message\n");
431 server->WaitForNFinishedWrites(2);
432 // This will result in a message before the Write() above, as it's paused.
433 mock_router().DoDispatchEventToExtension("", "MARK", nullptr);
434
435 // Let the Write() get delivered.
436 CallFunction<api::SocketsTcpSetPausedFunction>(
437 base::StringPrintf("[%d, false]", sockfd));
438
439 while (mock_router().call_count() < 2) {
440 // Let event queues drain.
441 base::RunLoop().RunUntilIdle();
442 }
443
444 server->Close();
445 }
446
123 } // namespace extensions 447 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698