Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / mdns / mdns_api_unittest.cc
blobfd8d8daac5beb9f61b443d7d03577ba1d62972ac
1 // Copyright 2015 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.
5 #include <vector>
7 #include "base/memory/linked_ptr.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "chrome/browser/extensions/api/mdns/mdns_api.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/extensions/extension_service_test_base.h"
12 #include "chrome/browser/extensions/test_extension_system.h"
13 #include "chrome/common/extensions/api/mdns.h"
14 #include "content/public/browser/browser_context.h"
15 #include "content/public/test/mock_render_process_host.h"
16 #include "extensions/browser/event_listener_map.h"
17 #include "extensions/browser/event_router_factory.h"
18 #include "extensions/browser/extension_prefs.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/common/manifest_constants.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
24 using testing::_;
25 using testing::Return;
26 using testing::ReturnRef;
28 namespace extensions {
29 namespace {
31 const char kExtId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
32 const char kService1[] = "service1";
33 const char kService2[] = "service2";
35 // Registers a new EventListener for |service_types| in |listener_list|.
36 void AddEventListener(
37 const std::string& extension_id,
38 const std::string& service_type,
39 extensions::EventListenerMap::ListenerList* listener_list) {
40 scoped_ptr<base::DictionaryValue> filter(new base::DictionaryValue);
41 filter->SetString(kEventFilterServiceTypeKey, service_type);
42 listener_list->push_back(make_linked_ptr(
43 EventListener::ForExtension(kEventFilterServiceTypeKey, extension_id,
44 nullptr, filter.Pass()).release()));
47 class NullDelegate : public EventListenerMap::Delegate {
48 public:
49 void OnListenerAdded(const EventListener* listener) override {}
50 void OnListenerRemoved(const EventListener* listener) override {}
53 // Testing subclass of MDnsAPI which replaces calls to core browser components
54 // that we don't want to have to instantiate or mock for these tests.
55 class MockedMDnsAPI : public MDnsAPI {
56 public:
57 explicit MockedMDnsAPI(content::BrowserContext* context) : MDnsAPI(context) {}
59 public:
60 MOCK_CONST_METHOD2(IsMDnsAllowed,
61 bool(const std::string& extension_id,
62 const std::string& service_type));
64 MOCK_METHOD0(GetEventListeners,
65 const extensions::EventListenerMap::ListenerList&());
68 scoped_ptr<KeyedService> MockedMDnsAPITestingFactoryFunction(
69 content::BrowserContext* context) {
70 return make_scoped_ptr(new MockedMDnsAPI(context));
73 scoped_ptr<KeyedService> MDnsAPITestingFactoryFunction(
74 content::BrowserContext* context) {
75 return make_scoped_ptr(new MDnsAPI(context));
78 scoped_ptr<KeyedService> BuildEventRouter(content::BrowserContext* context) {
79 return make_scoped_ptr(
80 new extensions::EventRouter(context, ExtensionPrefs::Get(context)));
83 // For ExtensionService interface when it requires a path that is not used.
84 base::FilePath bogus_file_pathname(const std::string& name) {
85 return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
86 .AppendASCII(name);
89 class MockDnsSdRegistry : public DnsSdRegistry {
90 public:
91 explicit MockDnsSdRegistry(extensions::MDnsAPI* api) : api_(api) {}
92 virtual ~MockDnsSdRegistry() {}
94 MOCK_METHOD1(AddObserver, void(DnsSdObserver* observer));
95 MOCK_METHOD1(RemoveObserver, void(DnsSdObserver* observer));
96 MOCK_METHOD1(RegisterDnsSdListener, void(const std::string& service_type));
97 MOCK_METHOD1(UnregisterDnsSdListener, void(const std::string& service_type));
98 MOCK_METHOD1(Publish, void(const std::string&));
99 MOCK_METHOD0(ForceDiscovery, void(void));
101 void DispatchMDnsEvent(const std::string& service_type,
102 const DnsSdServiceList& services) {
103 api_->OnDnsSdEvent(service_type, services);
106 private:
107 extensions::DnsSdRegistry::DnsSdObserver* api_;
110 class MockEventRouter : public EventRouter {
111 public:
112 explicit MockEventRouter(content::BrowserContext* browser_context,
113 ExtensionPrefs* extension_prefs)
114 : EventRouter(browser_context, extension_prefs) {}
115 virtual ~MockEventRouter() {}
117 virtual void BroadcastEvent(scoped_ptr<Event> event) {
118 BroadcastEventPtr(event.get());
120 MOCK_METHOD1(BroadcastEventPtr, void(Event* event));
123 scoped_ptr<KeyedService> MockEventRouterFactoryFunction(
124 content::BrowserContext* context) {
125 return make_scoped_ptr(
126 new MockEventRouter(context, ExtensionPrefs::Get(context)));
129 class EventServiceListSizeMatcher
130 : public testing::MatcherInterface<const Event&> {
131 public:
132 explicit EventServiceListSizeMatcher(size_t expected_size)
133 : expected_size_(expected_size) {}
135 virtual bool MatchAndExplain(const Event& e,
136 testing::MatchResultListener* listener) const {
137 if (!e.event_args) {
138 *listener << "event.event_arg is null when it shouldn't be";
139 return false;
141 if (e.event_args->GetSize() != 1) {
142 *listener << "event.event_arg.GetSize() should be 1 but is "
143 << e.event_args->GetSize();
144 return false;
146 const base::ListValue* services = nullptr;
148 const base::Value* out;
149 e.event_args->Get(0, &out);
150 services = static_cast<const base::ListValue*>(out);
152 if (!services) {
153 *listener << "event's service list argument is not a ListValue";
154 return false;
156 *listener << "number of services is " << services->GetSize();
157 return static_cast<testing::Matcher<size_t>>(testing::Eq(expected_size_))
158 .MatchAndExplain(services->GetSize(), listener);
161 virtual void DescribeTo(::std::ostream* os) const {
162 *os << "is an onServiceList event where the number of services is "
163 << expected_size_;
166 virtual void DescribeNegationTo(::std::ostream* os) const {
167 *os << "isn't an onServiceList event where the number of services is "
168 << expected_size_;
171 private:
172 size_t expected_size_;
175 inline testing::Matcher<const Event&> EventServiceListSize(
176 size_t expected_size) {
177 return testing::MakeMatcher(new EventServiceListSizeMatcher(expected_size));
180 } // namespace
182 class MDnsAPITest : public extensions::ExtensionServiceTestBase {
183 public:
184 void SetUp() override {
185 extensions::ExtensionServiceTestBase::SetUp();
187 // Set up browser_context().
188 InitializeEmptyExtensionService();
190 // A custom TestingFactoryFunction is required for an MDnsAPI to actually be
191 // constructed.
192 MDnsAPI::GetFactoryInstance()->SetTestingFactory(browser_context(),
193 GetMDnsFactory());
195 EventRouterFactory::GetInstance()->SetTestingFactory(browser_context(),
196 &BuildEventRouter);
198 // Do some sanity checking
199 ASSERT_TRUE(MDnsAPI::Get(browser_context())); // constructs MDnsAPI
200 ASSERT_TRUE(EventRouter::Get(browser_context())); // constructs EventRouter
202 registry_ = new MockDnsSdRegistry(MDnsAPI::Get(browser_context()));
203 EXPECT_CALL(*dns_sd_registry(),
204 AddObserver(MDnsAPI::Get(browser_context())))
205 .Times(1);
206 MDnsAPI::Get(browser_context())->SetDnsSdRegistryForTesting(
207 scoped_ptr<DnsSdRegistry>(registry_));
209 render_process_host_.reset(
210 new content::MockRenderProcessHost(browser_context()));
213 // Returns the mDNS API factory function (mock vs. real) to use for the test.
214 virtual BrowserContextKeyedServiceFactory::TestingFactoryFunction
215 GetMDnsFactory() {
216 return MDnsAPITestingFactoryFunction;
219 void TearDown() override {
220 EXPECT_CALL(*dns_sd_registry(),
221 RemoveObserver(MDnsAPI::Get(browser_context())))
222 .Times(1);
223 render_process_host_.reset();
224 extensions::ExtensionServiceTestBase::TearDown();
227 virtual MockDnsSdRegistry* dns_sd_registry() {
228 return registry_;
231 // Constructs an extension according to the parameters that matter most to
232 // MDnsAPI the local unit tests.
233 const scoped_refptr<extensions::Extension> CreateExtension(
234 std::string name,
235 bool is_platform_app,
236 std::string extension_id) {
237 base::DictionaryValue manifest;
238 manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
239 manifest.SetString(extensions::manifest_keys::kName, name);
240 if (is_platform_app) {
241 // Setting app.background.page = "background.html" is sufficient to make
242 // the extension type TYPE_PLATFORM_APP.
243 manifest.Set(extensions::manifest_keys::kPlatformAppBackgroundPage,
244 new base::StringValue("background.html"));
247 std::string error;
248 return extensions::Extension::Create(
249 bogus_file_pathname(name),
250 extensions::Manifest::INVALID_LOCATION,
251 manifest,
252 Extension::NO_FLAGS,
253 extension_id,
254 &error);
257 content::RenderProcessHost* render_process_host() const {
258 return render_process_host_.get();
261 private:
262 // The registry is owned by MDnsAPI, but MDnsAPI does not have an accessor
263 // for it, so use a private member.
264 MockDnsSdRegistry* registry_;
266 scoped_ptr<content::RenderProcessHost> render_process_host_;
269 class MDnsAPIMaxServicesTest : public MDnsAPITest {
270 public:
271 MockEventRouter* event_router() {
272 return static_cast<MockEventRouter*>(EventRouter::Get(browser_context()));
276 class MDnsAPIDiscoveryTest : public MDnsAPITest {
277 public:
278 BrowserContextKeyedServiceFactory::TestingFactoryFunction GetMDnsFactory()
279 override {
280 return MockedMDnsAPITestingFactoryFunction;
283 void SetUp() override {
284 MDnsAPITest::SetUp();
285 mdns_api_ = static_cast<MockedMDnsAPI*>(MDnsAPI::Get(browser_context()));
286 EXPECT_CALL(*mdns_api_, IsMDnsAllowed(_, _)).WillRepeatedly(Return(true));
289 protected:
290 MockedMDnsAPI* mdns_api_;
293 TEST_F(MDnsAPIDiscoveryTest, ServiceListenersAddedAndRemoved) {
294 EventRouterFactory::GetInstance()->SetTestingFactory(
295 browser_context(), &MockEventRouterFactoryFunction);
296 extensions::EventListenerMap::ListenerList listeners;
298 extensions::EventListenerInfo listener_info(
299 kEventFilterServiceTypeKey, kExtId, GURL(), browser_context());
301 EXPECT_CALL(*mdns_api_, GetEventListeners())
302 .WillRepeatedly(ReturnRef(listeners));
304 // Listener #1 added with kService1.
305 AddEventListener(kExtId, kService1, &listeners);
306 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService1));
307 mdns_api_->OnListenerAdded(listener_info);
309 // Listener #2 added with kService2.
310 AddEventListener(kExtId, kService2, &listeners);
311 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService2));
312 mdns_api_->OnListenerAdded(listener_info);
314 // No-op.
315 mdns_api_->OnListenerAdded(listener_info);
317 // Listener #3 added with kService2. Should trigger a refresh of kService2.
318 AddEventListener(kExtId, kService2, &listeners);
319 EXPECT_CALL(*dns_sd_registry(), Publish(kService2));
320 mdns_api_->OnListenerAdded(listener_info);
322 // Listener #3 removed, should be a no-op since there is still a live
323 // listener for kService2.
324 listeners.pop_back();
325 mdns_api_->OnListenerRemoved(listener_info);
327 // Listener #2 removed, should unregister kService2.
328 listeners.pop_back();
329 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener(kService2));
330 mdns_api_->OnListenerRemoved(listener_info);
332 // Listener #1 removed, should unregister kService1.
333 listeners.pop_back();
334 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener(kService1));
335 mdns_api_->OnListenerRemoved(listener_info);
337 // No-op.
338 mdns_api_->OnListenerAdded(listener_info);
340 // Listener #4 added with kService1.
341 AddEventListener(kExtId, kService1, &listeners);
342 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener(kService1));
343 mdns_api_->OnListenerAdded(listener_info);
346 TEST_F(MDnsAPIMaxServicesTest, OnServiceListDoesNotExceedLimit) {
347 EventRouterFactory::GetInstance()->SetTestingFactory(
348 browser_context(), &MockEventRouterFactoryFunction);
350 // This check should change when the [value=64] changes in the IDL file.
351 EXPECT_EQ(64, api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT);
353 // Dispatch an mDNS event with more service instances than the max, and ensure
354 // that the list is truncated by inspecting the argument to MockEventRouter's
355 // BroadcastEvent method.
356 DnsSdRegistry::DnsSdServiceList services;
357 for (int i = 0; i < api::mdns::MAX_SERVICE_INSTANCES_PER_EVENT + 10; ++i) {
358 services.push_back(DnsSdService());
360 EXPECT_CALL(*event_router(), BroadcastEventPtr(testing::Pointee(
361 EventServiceListSize(size_t(64))))).Times(1);
362 dns_sd_registry()->DispatchMDnsEvent("_testing._tcp.local", services);
365 TEST_F(MDnsAPITest, ExtensionRespectsWhitelist) {
366 scoped_refptr<extensions::Extension> extension =
367 CreateExtension("Dinosaur networker", false, kExtId);
368 ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
369 ASSERT_EQ(Manifest::TYPE_EXTENSION, extension.get()->GetType());
371 // There is a whitelist of mdns service types extensions may access, which
372 // includes "_testing._tcp.local" and exludes "_trex._tcp.local"
374 base::DictionaryValue filter;
375 filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local");
377 ASSERT_TRUE(dns_sd_registry());
378 // Test that the extension is able to listen to a non-whitelisted service
379 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"))
380 .Times(0);
381 EventRouter::Get(browser_context())
382 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
383 render_process_host(), kExtId, filter,
384 false);
386 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"))
387 .Times(0);
388 EventRouter::Get(browser_context())
389 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
390 render_process_host(), kExtId, filter,
391 false);
394 base::DictionaryValue filter;
395 filter.SetString(kEventFilterServiceTypeKey, "_testing._tcp.local");
397 ASSERT_TRUE(dns_sd_registry());
398 // Test that the extension is able to listen to a whitelisted service
399 EXPECT_CALL(*dns_sd_registry(),
400 RegisterDnsSdListener("_testing._tcp.local"));
401 EventRouter::Get(browser_context())
402 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
403 render_process_host(), kExtId, filter,
404 false);
406 EXPECT_CALL(*dns_sd_registry(),
407 UnregisterDnsSdListener("_testing._tcp.local"));
408 EventRouter::Get(browser_context())
409 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
410 render_process_host(), kExtId, filter,
411 false);
415 TEST_F(MDnsAPITest, PlatformAppsNotSubjectToWhitelist) {
416 scoped_refptr<extensions::Extension> extension =
417 CreateExtension("Dinosaur networker", true, kExtId);
418 ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
419 ASSERT_TRUE(extension.get()->is_platform_app());
421 base::DictionaryValue filter;
422 filter.SetString(kEventFilterServiceTypeKey, "_trex._tcp.local");
424 ASSERT_TRUE(dns_sd_registry());
425 // Test that the extension is able to listen to a non-whitelisted service
426 EXPECT_CALL(*dns_sd_registry(), RegisterDnsSdListener("_trex._tcp.local"));
427 EventRouter::Get(browser_context())
428 ->AddFilteredEventListener(api::mdns::OnServiceList::kEventName,
429 render_process_host(), kExtId, filter, false);
431 EXPECT_CALL(*dns_sd_registry(), UnregisterDnsSdListener("_trex._tcp.local"));
432 EventRouter::Get(browser_context())
433 ->RemoveFilteredEventListener(api::mdns::OnServiceList::kEventName,
434 render_process_host(), kExtId, filter,
435 false);
438 } // namespace extensions