| 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 |