OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 #import <Cocoa/Cocoa.h> | 5 #import <Cocoa/Cocoa.h> |
6 #include <stdint.h> | 6 #include <stdint.h> |
7 | 7 |
8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/callback.h" |
9 #include "base/mac/scoped_nsobject.h" | 10 #include "base/mac/scoped_nsobject.h" |
10 #include "base/macros.h" | 11 #include "base/macros.h" |
11 #include "base/run_loop.h" | 12 #include "base/run_loop.h" |
12 #include "chrome/browser/local_discovery/service_discovery_client.h" | 13 #include "chrome/browser/local_discovery/service_discovery_client.h" |
13 #include "chrome/browser/local_discovery/service_discovery_client_mac.h" | 14 #include "chrome/browser/local_discovery/service_discovery_client_mac.h" |
14 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" | 15 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" |
15 #include "content/public/test/test_browser_thread_bundle.h" | 16 #include "content/public/test/test_browser_thread_bundle.h" |
16 #include "net/base/ip_endpoint.h" | 17 #include "net/base/ip_endpoint.h" |
17 #include "net/base/sockaddr_storage.h" | 18 #include "net/base/sockaddr_storage.h" |
18 #include "testing/gtest_mac.h" | 19 #include "testing/gtest_mac.h" |
19 | 20 |
20 @interface TestNSNetService : NSNetService { | 21 @interface TestNSNetService : NSNetService { |
21 @private | 22 @private |
22 NSData* data_; | 23 base::scoped_nsobject<NSData> data_; |
23 NSArray* addresses_; | 24 base::scoped_nsobject<NSArray> addresses_; |
24 } | 25 } |
25 - (id)initWithData:(NSData*)data; | 26 - (id)initWithData:(NSData*)data; |
26 - (void)setAddresses:(NSArray*)addresses; | 27 - (void)setAddresses:(NSArray*)addresses; |
27 @end | 28 @end |
28 | 29 |
29 @implementation TestNSNetService | 30 @implementation TestNSNetService |
30 | 31 |
31 - (id)initWithData:(NSData*)data { | 32 - (id)initWithData:(NSData*)data { |
32 if ((self = [super init])) { | 33 if ((self = [super init])) { |
33 data_ = data; | 34 data_.reset([data retain]); |
34 } | 35 } |
35 return self; | 36 return self; |
36 } | 37 } |
37 | 38 |
38 - (void)setAddresses:(NSArray*)addresses { | 39 - (void)setAddresses:(NSArray*)addresses { |
39 addresses_ = addresses; | 40 addresses_.reset([addresses copy]); |
40 } | 41 } |
41 | 42 |
42 - (NSArray*)addresses { | 43 - (NSArray*)addresses { |
43 return addresses_; | 44 return addresses_; |
44 } | 45 } |
45 | 46 |
46 - (NSData*)TXTRecordData { | 47 - (NSData*)TXTRecordData { |
47 return data_; | 48 return data_; |
48 } | 49 } |
49 | 50 |
50 @end | 51 @end |
51 | 52 |
52 namespace local_discovery { | 53 namespace local_discovery { |
53 | 54 |
54 class ServiceDiscoveryClientMacTest : public CocoaTest { | 55 class ServiceDiscoveryClientMacTest : public CocoaTest { |
55 public: | 56 public: |
56 ServiceDiscoveryClientMacTest() : num_updates_(0), num_resolves_(0) { | 57 ServiceDiscoveryClientMacTest() |
57 client_mac_ = new ServiceDiscoveryClientMac(); | 58 : client_(new ServiceDiscoveryClientMac()), |
58 client_ = client_mac_.get(); | 59 num_updates_(0), |
| 60 num_resolves_(0) { |
59 } | 61 } |
60 | 62 |
61 void OnServiceUpdated( | 63 void OnServiceUpdated( |
62 ServiceWatcher::UpdateType update, | 64 ServiceWatcher::UpdateType update, |
63 const std::string& service_name) { | 65 const std::string& service_name) { |
64 last_update_ = update; | 66 last_update_ = update; |
65 last_service_name_ = service_name; | 67 last_service_name_ = service_name; |
66 num_updates_++; | 68 num_updates_++; |
67 } | 69 } |
68 | 70 |
69 | |
70 void OnResolveComplete( | 71 void OnResolveComplete( |
71 ServiceResolver::RequestStatus status, | 72 ServiceResolver::RequestStatus status, |
72 const ServiceDescription& service_description) { | 73 const ServiceDescription& service_description) { |
73 last_status_ = status; | 74 last_status_ = status; |
74 last_service_description_ = service_description; | 75 last_service_description_ = service_description; |
75 num_resolves_++; | 76 num_resolves_++; |
76 } | 77 } |
77 | 78 |
| 79 ServiceDiscoveryClient* client() { return client_.get(); } |
| 80 |
78 protected: | 81 protected: |
79 content::TestBrowserThreadBundle thread_bundle_; | 82 content::TestBrowserThreadBundle thread_bundle_; |
80 | 83 |
81 scoped_refptr<ServiceDiscoveryClientMac> client_mac_; | 84 scoped_refptr<ServiceDiscoveryClientMac> client_; |
82 ServiceDiscoveryClient* client_; // weak | |
83 | 85 |
84 ServiceWatcher::UpdateType last_update_; | 86 ServiceWatcher::UpdateType last_update_; |
85 std::string last_service_name_; | 87 std::string last_service_name_; |
86 int num_updates_; | 88 int num_updates_; |
87 ServiceResolver::RequestStatus last_status_; | 89 ServiceResolver::RequestStatus last_status_; |
88 ServiceDescription last_service_description_; | 90 ServiceDescription last_service_description_; |
89 int num_resolves_; | 91 int num_resolves_; |
90 }; | 92 }; |
91 | 93 |
92 TEST_F(ServiceDiscoveryClientMacTest, ServiceWatcher) { | 94 TEST_F(ServiceDiscoveryClientMacTest, ServiceWatcher) { |
93 const std::string test_service_type = "_testing._tcp.local"; | 95 const std::string test_service_type = "_testing._tcp.local"; |
94 const std::string test_service_name = "Test.123"; | 96 const std::string test_service_name = "Test.123"; |
95 | 97 |
96 std::unique_ptr<ServiceWatcher> watcher = client_->CreateServiceWatcher( | 98 std::unique_ptr<ServiceWatcher> watcher = client()->CreateServiceWatcher( |
97 test_service_type, | 99 test_service_type, |
98 base::Bind(&ServiceDiscoveryClientMacTest::OnServiceUpdated, | 100 base::Bind(&ServiceDiscoveryClientMacTest::OnServiceUpdated, |
99 base::Unretained(this))); | 101 base::Unretained(this))); |
100 watcher->Start(); | 102 watcher->Start(); |
101 | 103 |
102 // Weak pointer to implementation class. | 104 // Weak pointer to implementation class. |
103 ServiceWatcherImplMac* watcher_impl = | 105 ServiceWatcherImplMac* watcher_impl = |
104 static_cast<ServiceWatcherImplMac*>(watcher.get()); | 106 static_cast<ServiceWatcherImplMac*>(watcher.get()); |
105 // Simulate service update events. | 107 // Simulate service update events. |
106 watcher_impl->OnServicesUpdate( | 108 watcher_impl->OnServicesUpdate( |
107 ServiceWatcher::UPDATE_ADDED, test_service_name); | 109 ServiceWatcher::UPDATE_ADDED, test_service_name); |
108 watcher_impl->OnServicesUpdate( | 110 watcher_impl->OnServicesUpdate( |
109 ServiceWatcher::UPDATE_CHANGED, test_service_name); | 111 ServiceWatcher::UPDATE_CHANGED, test_service_name); |
110 watcher_impl->OnServicesUpdate( | 112 watcher_impl->OnServicesUpdate( |
111 ServiceWatcher::UPDATE_REMOVED, test_service_name); | 113 ServiceWatcher::UPDATE_REMOVED, test_service_name); |
112 EXPECT_EQ(last_service_name_, test_service_name + "." + test_service_type); | 114 EXPECT_EQ(last_service_name_, test_service_name + "." + test_service_type); |
113 EXPECT_EQ(num_updates_, 3); | 115 EXPECT_EQ(num_updates_, 3); |
114 } | 116 } |
115 | 117 |
116 TEST_F(ServiceDiscoveryClientMacTest, ServiceResolver) { | 118 TEST_F(ServiceDiscoveryClientMacTest, ServiceResolver) { |
117 const std::string test_service_name = "Test.123._testing._tcp.local"; | 119 const std::string test_service_name = "Test.123._testing._tcp.local"; |
118 std::unique_ptr<ServiceResolver> resolver = client_->CreateServiceResolver( | 120 std::unique_ptr<ServiceResolver> resolver = client()->CreateServiceResolver( |
119 test_service_name, | 121 test_service_name, |
120 base::Bind(&ServiceDiscoveryClientMacTest::OnResolveComplete, | 122 base::Bind(&ServiceDiscoveryClientMacTest::OnResolveComplete, |
121 base::Unretained(this))); | 123 base::Unretained(this))); |
122 | 124 |
123 const uint8_t record_bytes[] = {2, 'a', 'b', 3, 'd', '=', 'e'}; | 125 const uint8_t record_bytes[] = {2, 'a', 'b', 3, 'd', '=', 'e'}; |
124 base::scoped_nsobject<TestNSNetService> test_service([[TestNSNetService alloc] | 126 base::scoped_nsobject<TestNSNetService> test_service([[TestNSNetService alloc] |
125 initWithData:[[NSData alloc] initWithBytes:record_bytes | 127 initWithData:[NSData dataWithBytes:record_bytes |
126 length:arraysize(record_bytes)]]); | 128 length:arraysize(record_bytes)]]); |
127 | 129 |
128 const std::string kIp = "2001:4860:4860::8844"; | 130 const std::string kIp = "2001:4860:4860::8844"; |
129 const uint16_t kPort = 4321; | 131 const uint16_t kPort = 4321; |
130 net::IPAddress ip_address; | 132 net::IPAddress ip_address; |
131 ASSERT_TRUE(ip_address.AssignFromIPLiteral(kIp)); | 133 ASSERT_TRUE(ip_address.AssignFromIPLiteral(kIp)); |
132 net::IPEndPoint endpoint(ip_address, kPort); | 134 net::IPEndPoint endpoint(ip_address, kPort); |
133 net::SockaddrStorage storage; | 135 net::SockaddrStorage storage; |
134 ASSERT_TRUE(endpoint.ToSockAddr(storage.addr, &storage.addr_len)); | 136 ASSERT_TRUE(endpoint.ToSockAddr(storage.addr, &storage.addr_len)); |
135 NSData* discoveryHost = | 137 NSData* discoveryHost = |
136 [NSData dataWithBytes:storage.addr length:storage.addr_len]; | 138 [NSData dataWithBytes:storage.addr length:storage.addr_len]; |
137 NSArray* addresses = @[ discoveryHost ]; | 139 NSArray* addresses = @[ discoveryHost ]; |
138 [test_service setAddresses:addresses]; | 140 [test_service setAddresses:addresses]; |
139 | 141 |
140 ServiceResolverImplMac* resolver_impl = | 142 ServiceResolverImplMac* resolver_impl = |
141 static_cast<ServiceResolverImplMac*>(resolver.get()); | 143 static_cast<ServiceResolverImplMac*>(resolver.get()); |
142 resolver_impl->GetContainerForTesting()->SetServiceForTesting( | 144 resolver_impl->GetContainerForTesting()->SetServiceForTesting( |
143 base::scoped_nsobject<NSNetService>(test_service)); | 145 base::scoped_nsobject<NSNetService>(test_service)); |
144 resolver->StartResolving(); | 146 resolver->StartResolving(); |
145 | 147 |
146 resolver_impl->GetContainerForTesting()->OnResolveUpdate( | 148 resolver_impl->GetContainerForTesting()->OnResolveUpdate( |
147 ServiceResolver::STATUS_SUCCESS); | 149 ServiceResolver::STATUS_SUCCESS); |
148 | 150 |
149 base::RunLoop().RunUntilIdle(); | 151 base::RunLoop().RunUntilIdle(); |
150 | 152 |
151 EXPECT_EQ(1, num_resolves_); | 153 EXPECT_EQ(1, num_resolves_); |
152 EXPECT_EQ(2u, last_service_description_.metadata.size()); | 154 |
| 155 const std::vector<std::string>& metadata = |
| 156 last_service_description_.metadata; |
| 157 EXPECT_EQ(2u, metadata.size()); |
| 158 EXPECT_NE(metadata.end(), |
| 159 std::find(metadata.begin(), metadata.end(), "ab")); |
| 160 EXPECT_NE(metadata.end(), |
| 161 std::find(metadata.begin(), metadata.end(), "d=e")); |
| 162 |
153 EXPECT_EQ(ip_address, last_service_description_.ip_address); | 163 EXPECT_EQ(ip_address, last_service_description_.ip_address); |
154 EXPECT_EQ(kPort, last_service_description_.address.port()); | 164 EXPECT_EQ(kPort, last_service_description_.address.port()); |
155 EXPECT_EQ(kIp, last_service_description_.address.host()); | 165 EXPECT_EQ(kIp, last_service_description_.address.host()); |
156 } | 166 } |
157 | 167 |
| 168 // https://crbug.com/586628 |
| 169 TEST_F(ServiceDiscoveryClientMacTest, ResolveInvalidUnicodeRecord) { |
| 170 const std::string test_service_name = "Test.123._testing._tcp.local"; |
| 171 std::unique_ptr<ServiceResolver> resolver = client()->CreateServiceResolver( |
| 172 test_service_name, |
| 173 base::Bind(&ServiceDiscoveryClientMacTest::OnResolveComplete, |
| 174 base::Unretained(this))); |
| 175 |
| 176 const uint8_t record_bytes[] = { |
| 177 3, 'a', '=', 'b', |
| 178 // The bytes after name= are the UTF-8 encoded representation of |
| 179 // U+1F4A9, with the first two bytes of the code unit sequence transposed. |
| 180 9, 'n', 'a', 'm', 'e', '=', 0x9F, 0xF0, 0x92, 0xA9, |
| 181 5, 'c', 'd', '=', 'e', '9', |
| 182 }; |
| 183 base::scoped_nsobject<TestNSNetService> test_service([[TestNSNetService alloc] |
| 184 initWithData:[NSData dataWithBytes:record_bytes |
| 185 length:arraysize(record_bytes)]]); |
| 186 |
| 187 const std::string kIp = "2001:4860:4860::8844"; |
| 188 const uint16_t kPort = 4321; |
| 189 net::IPAddress ip_address; |
| 190 ASSERT_TRUE(ip_address.AssignFromIPLiteral(kIp)); |
| 191 net::IPEndPoint endpoint(ip_address, kPort); |
| 192 net::SockaddrStorage storage; |
| 193 ASSERT_TRUE(endpoint.ToSockAddr(storage.addr, &storage.addr_len)); |
| 194 NSData* discovery_host = |
| 195 [NSData dataWithBytes:storage.addr length:storage.addr_len]; |
| 196 NSArray* addresses = @[ discovery_host ]; |
| 197 [test_service setAddresses:addresses]; |
| 198 |
| 199 ServiceResolverImplMac* resolver_impl = |
| 200 static_cast<ServiceResolverImplMac*>(resolver.get()); |
| 201 resolver_impl->GetContainerForTesting()->SetServiceForTesting( |
| 202 base::scoped_nsobject<NSNetService>(test_service)); |
| 203 resolver->StartResolving(); |
| 204 |
| 205 resolver_impl->GetContainerForTesting()->OnResolveUpdate( |
| 206 ServiceResolver::STATUS_SUCCESS); |
| 207 |
| 208 base::RunLoop().RunUntilIdle(); |
| 209 |
| 210 EXPECT_EQ(1, num_resolves_); |
| 211 |
| 212 const std::vector<std::string>& metadata = |
| 213 last_service_description_.metadata; |
| 214 EXPECT_EQ(2u, metadata.size()); |
| 215 EXPECT_NE(metadata.end(), |
| 216 std::find(metadata.begin(), metadata.end(), "a=b")); |
| 217 EXPECT_NE(metadata.end(), |
| 218 std::find(metadata.begin(), metadata.end(), "cd=e9")); |
| 219 |
| 220 EXPECT_EQ(ip_address, last_service_description_.ip_address); |
| 221 EXPECT_EQ(kPort, last_service_description_.address.port()); |
| 222 EXPECT_EQ(kIp, last_service_description_.address.host()); |
| 223 } |
| 224 |
| 225 TEST_F(ServiceDiscoveryClientMacTest, ResolveInvalidServiceName) { |
| 226 base::RunLoop run_loop; |
| 227 |
| 228 // This is the same invalid U+1F4A9 code unit sequence as in |
| 229 // ResolveInvalidUnicodeRecord. |
| 230 const std::string test_service_name = |
| 231 "Test\x9F\xF0\x92\xA9.123._testing._tcp.local"; |
| 232 std::unique_ptr<ServiceResolver> resolver = client()->CreateServiceResolver( |
| 233 test_service_name, |
| 234 base::Bind( |
| 235 [](ServiceDiscoveryClientMacTest* test, |
| 236 base::Closure quit_closure, |
| 237 ServiceResolver::RequestStatus status, |
| 238 const ServiceDescription& service_description) { |
| 239 test->OnResolveComplete(status, service_description); |
| 240 quit_closure.Run(); |
| 241 }, |
| 242 base::Unretained(this), run_loop.QuitClosure())); |
| 243 resolver->StartResolving(); |
| 244 |
| 245 run_loop.Run(); |
| 246 |
| 247 EXPECT_EQ(1, num_resolves_); |
| 248 EXPECT_EQ(ServiceResolver::STATUS_KNOWN_NONEXISTENT, last_status_); |
| 249 } |
| 250 |
158 } // namespace local_discovery | 251 } // namespace local_discovery |
OLD | NEW |