| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <string.h> | 5 #include <string.h> |
| 6 | 6 |
| 7 #include "base/strings/stringprintf.h" | 7 #include "base/strings/stringprintf.h" |
| 8 #include "chrome/browser/extensions/api/bluetooth/bluetooth_api.h" | 8 #include "chrome/browser/extensions/api/bluetooth/bluetooth_api.h" |
| 9 #include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h" | 9 #include "chrome/browser/extensions/api/bluetooth/bluetooth_event_router.h" |
| 10 #include "chrome/browser/extensions/extension_apitest.h" | 10 #include "chrome/browser/extensions/extension_apitest.h" |
| 11 #include "chrome/browser/extensions/extension_function_test_utils.h" | 11 #include "chrome/browser/extensions/extension_function_test_utils.h" |
| 12 #include "chrome/browser/extensions/extension_service.h" | 12 #include "chrome/browser/extensions/extension_service.h" |
| 13 #include "chrome/browser/extensions/extension_test_message_listener.h" | 13 #include "chrome/browser/extensions/extension_test_message_listener.h" |
| 14 #include "chrome/browser/ui/browser.h" | 14 #include "chrome/browser/ui/browser.h" |
| 15 #include "chrome/test/base/ui_test_utils.h" | 15 #include "chrome/test/base/ui_test_utils.h" |
| 16 #include "device/bluetooth/bluetooth_adapter.h" | 16 #include "device/bluetooth/bluetooth_adapter.h" |
| 17 #include "device/bluetooth/bluetooth_out_of_band_pairing_data.h" | 17 #include "device/bluetooth/bluetooth_out_of_band_pairing_data.h" |
| 18 #include "device/bluetooth/test/mock_bluetooth_adapter.h" | 18 #include "device/bluetooth/test/mock_bluetooth_adapter.h" |
| 19 #include "device/bluetooth/test/mock_bluetooth_device.h" | 19 #include "device/bluetooth/test/mock_bluetooth_device.h" |
| 20 #include "device/bluetooth/test/mock_bluetooth_discovery_session.h" | |
| 21 #include "device/bluetooth/test/mock_bluetooth_profile.h" | 20 #include "device/bluetooth/test/mock_bluetooth_profile.h" |
| 22 #include "device/bluetooth/test/mock_bluetooth_socket.h" | 21 #include "device/bluetooth/test/mock_bluetooth_socket.h" |
| 23 #include "testing/gmock/include/gmock/gmock.h" | 22 #include "testing/gmock/include/gmock/gmock.h" |
| 24 | 23 |
| 25 using device::BluetoothAdapter; | 24 using device::BluetoothAdapter; |
| 26 using device::BluetoothDevice; | 25 using device::BluetoothDevice; |
| 27 using device::BluetoothDiscoverySession; | |
| 28 using device::BluetoothOutOfBandPairingData; | 26 using device::BluetoothOutOfBandPairingData; |
| 29 using device::BluetoothProfile; | 27 using device::BluetoothProfile; |
| 30 using device::MockBluetoothAdapter; | 28 using device::MockBluetoothAdapter; |
| 31 using device::MockBluetoothDevice; | 29 using device::MockBluetoothDevice; |
| 32 using device::MockBluetoothDiscoverySession; | |
| 33 using device::MockBluetoothProfile; | 30 using device::MockBluetoothProfile; |
| 34 using extensions::Extension; | 31 using extensions::Extension; |
| 35 | 32 |
| 36 namespace utils = extension_function_test_utils; | 33 namespace utils = extension_function_test_utils; |
| 37 namespace api = extensions::api; | 34 namespace api = extensions::api; |
| 38 | 35 |
| 39 namespace { | 36 namespace { |
| 40 | 37 |
| 41 static const char* kAdapterAddress = "A1:A2:A3:A4:A5:A6"; | 38 static const char* kAdapterAddress = "A1:A2:A3:A4:A5:A6"; |
| 42 static const char* kName = "whatsinaname"; | 39 static const char* kName = "whatsinaname"; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 61 event_router()->SetAdapterForTest(mock_adapter_); | 58 event_router()->SetAdapterForTest(mock_adapter_); |
| 62 | 59 |
| 63 device1_.reset(new testing::NiceMock<MockBluetoothDevice>( | 60 device1_.reset(new testing::NiceMock<MockBluetoothDevice>( |
| 64 mock_adapter_, 0, "d1", "11:12:13:14:15:16", | 61 mock_adapter_, 0, "d1", "11:12:13:14:15:16", |
| 65 true /* paired */, true /* connected */)); | 62 true /* paired */, true /* connected */)); |
| 66 device2_.reset(new testing::NiceMock<MockBluetoothDevice>( | 63 device2_.reset(new testing::NiceMock<MockBluetoothDevice>( |
| 67 mock_adapter_, 0, "d2", "21:22:23:24:25:26", | 64 mock_adapter_, 0, "d2", "21:22:23:24:25:26", |
| 68 false /* paired */, false /* connected */)); | 65 false /* paired */, false /* connected */)); |
| 69 } | 66 } |
| 70 | 67 |
| 71 void DiscoverySessionCallback( | |
| 72 const BluetoothAdapter::DiscoverySessionCallback& callback, | |
| 73 const BluetoothAdapter::ErrorCallback& error_callback) { | |
| 74 if (mock_session_.get()) { | |
| 75 callback.Run( | |
| 76 scoped_ptr<BluetoothDiscoverySession>(mock_session_.release())); | |
| 77 return; | |
| 78 } | |
| 79 error_callback.Run(); | |
| 80 } | |
| 81 | |
| 82 template <class T> | 68 template <class T> |
| 83 T* setupFunction(T* function) { | 69 T* setupFunction(T* function) { |
| 84 function->set_extension(empty_extension_.get()); | 70 function->set_extension(empty_extension_.get()); |
| 85 function->set_has_callback(true); | 71 function->set_has_callback(true); |
| 86 return function; | 72 return function; |
| 87 } | 73 } |
| 88 | 74 |
| 89 protected: | 75 protected: |
| 90 testing::StrictMock<MockBluetoothAdapter>* mock_adapter_; | 76 testing::StrictMock<MockBluetoothAdapter>* mock_adapter_; |
| 91 scoped_ptr<testing::NiceMock<MockBluetoothDiscoverySession> > mock_session_; | |
| 92 scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device1_; | 77 scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device1_; |
| 93 scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device2_; | 78 scoped_ptr<testing::NiceMock<MockBluetoothDevice> > device2_; |
| 94 scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile1_; | 79 scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile1_; |
| 95 scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile2_; | 80 scoped_ptr<testing::NiceMock<MockBluetoothProfile> > profile2_; |
| 96 | 81 |
| 97 extensions::ExtensionBluetoothEventRouter* event_router() { | 82 extensions::ExtensionBluetoothEventRouter* event_router() { |
| 98 return extensions::BluetoothAPI::Get(browser()->profile()) | 83 return extensions::BluetoothAPI::Get(browser()->profile()) |
| 99 ->bluetooth_event_router(); | 84 ->bluetooth_event_router(); |
| 100 } | 85 } |
| 101 | 86 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 memcpy(&(data.randomizer), kOutOfBandPairingDataRandomizer, | 122 memcpy(&(data.randomizer), kOutOfBandPairingDataRandomizer, |
| 138 device::kBluetoothOutOfBandPairingDataSize); | 123 device::kBluetoothOutOfBandPairingDataSize); |
| 139 return data; | 124 return data; |
| 140 } | 125 } |
| 141 | 126 |
| 142 static bool CallClosure(const base::Closure& callback) { | 127 static bool CallClosure(const base::Closure& callback) { |
| 143 callback.Run(); | 128 callback.Run(); |
| 144 return true; | 129 return true; |
| 145 } | 130 } |
| 146 | 131 |
| 147 static void StopDiscoverySessionCallback(const base::Closure& callback, | 132 static void CallDiscoveryCallback( |
| 148 const base::Closure& error_callback) { | 133 const base::Closure& callback, |
| 134 const BluetoothAdapter::ErrorCallback& error_callback) { |
| 149 callback.Run(); | 135 callback.Run(); |
| 150 } | 136 } |
| 151 | 137 |
| 138 static void CallDiscoveryErrorCallback( |
| 139 const base::Closure& callback, |
| 140 const BluetoothAdapter::ErrorCallback& error_callback) { |
| 141 error_callback.Run(); |
| 142 } |
| 143 |
| 152 static void CallOutOfBandPairingDataCallback( | 144 static void CallOutOfBandPairingDataCallback( |
| 153 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, | 145 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, |
| 154 const BluetoothAdapter::ErrorCallback& error_callback) { | 146 const BluetoothAdapter::ErrorCallback& error_callback) { |
| 155 callback.Run(GetOutOfBandPairingData()); | 147 callback.Run(GetOutOfBandPairingData()); |
| 156 } | 148 } |
| 157 | 149 |
| 158 } // namespace | 150 } // namespace |
| 159 | 151 |
| 160 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Profiles) { | 152 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Profiles) { |
| 161 // Run in context of an extension that has permissions for the profiles | 153 // Run in context of an extension that has permissions for the profiles |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 317 std::string error(utils::RunFunctionAndReturnError( | 309 std::string error(utils::RunFunctionAndReturnError( |
| 318 set_oob_function.get(), params, browser())); | 310 set_oob_function.get(), params, browser())); |
| 319 EXPECT_FALSE(error.empty()); | 311 EXPECT_FALSE(error.empty()); |
| 320 | 312 |
| 321 // TODO(bryeung): Also test setting the data when there is support for | 313 // TODO(bryeung): Also test setting the data when there is support for |
| 322 // ArrayBuffers in the arguments to the RunFunctionAnd* methods. | 314 // ArrayBuffers in the arguments to the RunFunctionAnd* methods. |
| 323 // crbug.com/132796 | 315 // crbug.com/132796 |
| 324 } | 316 } |
| 325 | 317 |
| 326 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { | 318 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, Discovery) { |
| 327 // Try with a failure to start. This will return an error as we haven't | 319 // Try with a failure to start |
| 328 // initialied a session object. | 320 EXPECT_CALL(*mock_adapter_, StartDiscovering(testing::_, testing::_)) |
| 329 EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_)) | 321 .WillOnce(testing::Invoke(CallDiscoveryErrorCallback)); |
| 330 .WillOnce( | |
| 331 testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback)); | |
| 332 | |
| 333 // StartDiscovery failure will remove the adapter that is no longer used. | 322 // StartDiscovery failure will remove the adapter that is no longer used. |
| 334 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); | 323 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); |
| 335 scoped_refptr<api::BluetoothStartDiscoveryFunction> start_function; | 324 scoped_refptr<api::BluetoothStartDiscoveryFunction> start_function; |
| 336 start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); | 325 start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); |
| 337 std::string error( | 326 std::string error( |
| 338 utils::RunFunctionAndReturnError(start_function.get(), "[]", browser())); | 327 utils::RunFunctionAndReturnError(start_function.get(), "[]", browser())); |
| 339 ASSERT_FALSE(error.empty()); | 328 ASSERT_FALSE(error.empty()); |
| 340 | 329 |
| 341 // Reset the adapter and initiate a discovery session. The ownership of the | 330 // Reset for a successful start |
| 342 // mock session will be passed to the event router. | |
| 343 ASSERT_FALSE(mock_session_.get()); | |
| 344 SetUpMockAdapter(); | 331 SetUpMockAdapter(); |
| 332 EXPECT_CALL(*mock_adapter_, StartDiscovering(testing::_, testing::_)) |
| 333 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 345 | 334 |
| 346 // Create a mock session to be returned as a result. Get a handle to it as | |
| 347 // its ownership will be passed and |mock_session_| will be reset. | |
| 348 mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>()); | |
| 349 MockBluetoothDiscoverySession* session = mock_session_.get(); | |
| 350 EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_)) | |
| 351 .WillOnce( | |
| 352 testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback)); | |
| 353 start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); | 335 start_function = setupFunction(new api::BluetoothStartDiscoveryFunction); |
| 354 (void) | 336 (void) |
| 355 utils::RunFunctionAndReturnError(start_function.get(), "[]", browser()); | 337 utils::RunFunctionAndReturnError(start_function.get(), "[]", browser()); |
| 356 | 338 |
| 357 // End the discovery session. The StopDiscovery function should succeed. | 339 // Reset to try stopping |
| 358 testing::Mock::VerifyAndClearExpectations(mock_adapter_); | 340 testing::Mock::VerifyAndClearExpectations(mock_adapter_); |
| 359 EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true)); | 341 EXPECT_CALL(*mock_adapter_, StopDiscovering(testing::_, testing::_)) |
| 360 EXPECT_CALL(*session, Stop(testing::_, testing::_)) | 342 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 361 .WillOnce(testing::Invoke(StopDiscoverySessionCallback)); | |
| 362 | |
| 363 // StopDiscovery success will remove the adapter that is no longer used. | 343 // StopDiscovery success will remove the adapter that is no longer used. |
| 364 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); | 344 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); |
| 365 scoped_refptr<api::BluetoothStopDiscoveryFunction> stop_function; | 345 scoped_refptr<api::BluetoothStopDiscoveryFunction> stop_function; |
| 366 stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); | 346 stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); |
| 367 (void) utils::RunFunctionAndReturnSingleResult( | 347 (void) utils::RunFunctionAndReturnSingleResult( |
| 368 stop_function.get(), "[]", browser()); | 348 stop_function.get(), "[]", browser()); |
| 369 | 349 |
| 370 // Reset the adapter. Simulate failure for stop discovery. The event router | 350 // Reset to try stopping with an error |
| 371 // still owns the session. Make it appear inactive. | |
| 372 SetUpMockAdapter(); | 351 SetUpMockAdapter(); |
| 373 EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(false)); | 352 EXPECT_CALL(*mock_adapter_, StopDiscovering(testing::_, testing::_)) |
| 353 .WillOnce(testing::Invoke(CallDiscoveryErrorCallback)); |
| 374 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); | 354 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); |
| 375 stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); | 355 stop_function = setupFunction(new api::BluetoothStopDiscoveryFunction); |
| 376 error = | 356 error = |
| 377 utils::RunFunctionAndReturnError(stop_function.get(), "[]", browser()); | 357 utils::RunFunctionAndReturnError(stop_function.get(), "[]", browser()); |
| 378 ASSERT_FALSE(error.empty()); | 358 ASSERT_FALSE(error.empty()); |
| 379 SetUpMockAdapter(); | 359 SetUpMockAdapter(); |
| 380 } | 360 } |
| 381 | 361 |
| 382 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DiscoveryCallback) { | 362 IN_PROC_BROWSER_TEST_F(BluetoothApiTest, DiscoveryCallback) { |
| 383 mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>()); | 363 EXPECT_CALL(*mock_adapter_, StartDiscovering(testing::_, testing::_)) |
| 384 MockBluetoothDiscoverySession* session = mock_session_.get(); | 364 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 385 EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_)) | 365 EXPECT_CALL(*mock_adapter_, StopDiscovering(testing::_, testing::_)) |
| 386 .WillOnce( | 366 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 387 testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback)); | |
| 388 EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true)); | |
| 389 EXPECT_CALL(*session, Stop(testing::_, testing::_)) | |
| 390 .WillOnce(testing::Invoke(StopDiscoverySessionCallback)); | |
| 391 | 367 |
| 392 ResultCatcher catcher; | 368 ResultCatcher catcher; |
| 393 catcher.RestrictToProfile(browser()->profile()); | 369 catcher.RestrictToProfile(browser()->profile()); |
| 394 | 370 |
| 395 ExtensionTestMessageListener discovery_started("ready", true); | 371 ExtensionTestMessageListener discovery_started("ready", true); |
| 396 ASSERT_TRUE(LoadExtension( | 372 ASSERT_TRUE(LoadExtension( |
| 397 test_data_dir_.AppendASCII("bluetooth/discovery_callback"))); | 373 test_data_dir_.AppendASCII("bluetooth/discovery_callback"))); |
| 398 EXPECT_TRUE(discovery_started.WaitUntilSatisfied()); | 374 EXPECT_TRUE(discovery_started.WaitUntilSatisfied()); |
| 399 | 375 |
| 400 event_router()->DeviceAdded(mock_adapter_, device1_.get()); | 376 event_router()->DeviceAdded(mock_adapter_, device1_.get()); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 425 EXPECT_CALL(*mock_adapter_, IsDiscovering()) | 401 EXPECT_CALL(*mock_adapter_, IsDiscovering()) |
| 426 .WillOnce(testing::Return(true)); | 402 .WillOnce(testing::Return(true)); |
| 427 event_router()->AdapterDiscoveringChanged(mock_adapter_, true); | 403 event_router()->AdapterDiscoveringChanged(mock_adapter_, true); |
| 428 | 404 |
| 429 // Cache a device before the extension starts discovering | 405 // Cache a device before the extension starts discovering |
| 430 event_router()->DeviceAdded(mock_adapter_, device1_.get()); | 406 event_router()->DeviceAdded(mock_adapter_, device1_.get()); |
| 431 | 407 |
| 432 ResultCatcher catcher; | 408 ResultCatcher catcher; |
| 433 catcher.RestrictToProfile(browser()->profile()); | 409 catcher.RestrictToProfile(browser()->profile()); |
| 434 | 410 |
| 435 mock_session_.reset(new testing::NiceMock<MockBluetoothDiscoverySession>()); | 411 EXPECT_CALL(*mock_adapter_, StartDiscovering(testing::_, testing::_)) |
| 436 MockBluetoothDiscoverySession* session = mock_session_.get(); | 412 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 437 EXPECT_CALL(*mock_adapter_, StartDiscoverySession(testing::_, testing::_)) | 413 EXPECT_CALL(*mock_adapter_, StopDiscovering(testing::_, testing::_)) |
| 438 .WillOnce( | 414 .WillOnce(testing::Invoke(CallDiscoveryCallback)); |
| 439 testing::Invoke(this, &BluetoothApiTest::DiscoverySessionCallback)); | |
| 440 EXPECT_CALL(*session, IsActive()).WillOnce(testing::Return(true)); | |
| 441 EXPECT_CALL(*session, Stop(testing::_, testing::_)) | |
| 442 .WillOnce(testing::Invoke(StopDiscoverySessionCallback)); | |
| 443 | 415 |
| 444 ExtensionTestMessageListener discovery_started("ready", true); | 416 ExtensionTestMessageListener discovery_started("ready", true); |
| 445 ASSERT_TRUE(LoadExtension( | 417 ASSERT_TRUE(LoadExtension( |
| 446 test_data_dir_.AppendASCII("bluetooth/discovery_in_progress"))); | 418 test_data_dir_.AppendASCII("bluetooth/discovery_in_progress"))); |
| 447 EXPECT_TRUE(discovery_started.WaitUntilSatisfied()); | 419 EXPECT_TRUE(discovery_started.WaitUntilSatisfied()); |
| 448 | 420 |
| 449 // Only this should be received. No additional notification should be sent for | 421 // This should be received in addition to the cached device above. |
| 450 // devices discovered before the discovery session started. | |
| 451 event_router()->DeviceAdded(mock_adapter_, device2_.get()); | 422 event_router()->DeviceAdded(mock_adapter_, device2_.get()); |
| 452 | 423 |
| 453 discovery_started.Reply("go"); | 424 discovery_started.Reply("go"); |
| 454 ExtensionTestMessageListener discovery_stopped("ready", true); | 425 ExtensionTestMessageListener discovery_stopped("ready", true); |
| 455 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); | 426 EXPECT_CALL(*mock_adapter_, RemoveObserver(testing::_)); |
| 456 EXPECT_TRUE(discovery_stopped.WaitUntilSatisfied()); | 427 EXPECT_TRUE(discovery_stopped.WaitUntilSatisfied()); |
| 457 | 428 |
| 458 SetUpMockAdapter(); | 429 SetUpMockAdapter(); |
| 459 // This should never be received. | 430 // This should never be received. |
| 460 event_router()->DeviceAdded(mock_adapter_, device2_.get()); | 431 event_router()->DeviceAdded(mock_adapter_, device2_.get()); |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 // Load and wait for setup | 570 // Load and wait for setup |
| 600 ExtensionTestMessageListener listener("ready", true); | 571 ExtensionTestMessageListener listener("ready", true); |
| 601 ASSERT_TRUE(LoadExtension( | 572 ASSERT_TRUE(LoadExtension( |
| 602 test_data_dir_.AppendASCII("bluetooth/get_devices_error"))); | 573 test_data_dir_.AppendASCII("bluetooth/get_devices_error"))); |
| 603 EXPECT_TRUE(listener.WaitUntilSatisfied()); | 574 EXPECT_TRUE(listener.WaitUntilSatisfied()); |
| 604 | 575 |
| 605 listener.Reply("go"); | 576 listener.Reply("go"); |
| 606 | 577 |
| 607 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); | 578 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| 608 } | 579 } |
| OLD | NEW |