| Index: content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
|
| diff --git a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
|
| index de48b777c97ec88383c41e3d2ce16a1321539ffd..18eda04edb119a4cd3f0159c24911d8f44ff4963 100644
|
| --- a/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
|
| +++ b/content/browser/renderer_host/media/audio_input_device_manager_unittest.cc
|
| @@ -53,13 +53,30 @@ class MockAudioInputDeviceManagerListener
|
| class MockAudioInputDeviceManagerEventHandler
|
| : public AudioInputDeviceManagerEventHandler {
|
| public:
|
| - MockAudioInputDeviceManagerEventHandler() {}
|
| + explicit MockAudioInputDeviceManagerEventHandler(MessageLoop* message_loop)
|
| + : message_loop_(message_loop) {}
|
| virtual ~MockAudioInputDeviceManagerEventHandler() {}
|
|
|
| - MOCK_METHOD2(OnDeviceStarted, void(int, const std::string&));
|
| - MOCK_METHOD1(OnDeviceStopped, void(int));
|
| + MOCK_METHOD2(DeviceStarted, void(int, const std::string&));
|
| + MOCK_METHOD1(DeviceStopped, void(int));
|
| +
|
| + virtual void OnDeviceStarted(int session_id,
|
| + const std::string& device_id) {
|
| + message_loop_->PostTask(
|
| + FROM_HERE, base::Bind(
|
| + &MockAudioInputDeviceManagerEventHandler::DeviceStarted,
|
| + base::Unretained(this), session_id, device_id));
|
| + }
|
| +
|
| + virtual void OnDeviceStopped(int session_id) {
|
| + message_loop_->PostTask(
|
| + FROM_HERE, base::Bind(
|
| + &MockAudioInputDeviceManagerEventHandler::DeviceStopped,
|
| + base::Unretained(this), session_id));
|
| + }
|
|
|
| private:
|
| + MessageLoop* message_loop_;
|
| DISALLOW_COPY_AND_ASSIGN(MockAudioInputDeviceManagerEventHandler);
|
| };
|
|
|
| @@ -94,12 +111,13 @@ class AudioInputDeviceManagerTest: public testing::Test {
|
| audio_input_listener_.reset(new MockAudioInputDeviceManagerListener());
|
| manager_->Register(audio_input_listener_.get());
|
|
|
| - // Get the enumerated device list from the AudioInputDeviceManager.
|
| + // Gets the enumerated device list from the AudioInputDeviceManager.
|
| manager_->EnumerateDevices();
|
| EXPECT_CALL(*audio_input_listener_, DevicesEnumerated(_))
|
| .Times(1);
|
| - // Sync up the threads to make sure we get the list.
|
| - SyncWithAudioInputDeviceManagerThread();
|
| +
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
|
|
| virtual void TearDown() {
|
| @@ -107,29 +125,6 @@ class AudioInputDeviceManagerTest: public testing::Test {
|
| io_thread_.reset();
|
| }
|
|
|
| - // Called on the AudioInputDeviceManager thread.
|
| - static void PostQuitMessageLoop(MessageLoop* message_loop) {
|
| - message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
|
| - }
|
| -
|
| - // Called on the main thread.
|
| - static void PostQuitOnAudioInputDeviceManagerThread(
|
| - MessageLoop* message_loop, AudioInputDeviceManager* manager) {
|
| - manager->message_loop()->PostTask(
|
| - FROM_HERE, base::Bind(&PostQuitMessageLoop, message_loop));
|
| - }
|
| -
|
| - // SyncWithAudioInputDeviceManagerThread() waits until all pending tasks on
|
| - // the audio_input_device_manager thread are executed while also processing
|
| - // pending task in message_loop_ on the current thread.
|
| - void SyncWithAudioInputDeviceManagerThread() {
|
| - message_loop_->PostTask(
|
| - FROM_HERE,
|
| - base::Bind(&PostQuitOnAudioInputDeviceManagerThread,
|
| - message_loop_.get(),
|
| - manager_.get()));
|
| - message_loop_->Run();
|
| - }
|
| scoped_ptr<MessageLoop> message_loop_;
|
| scoped_ptr<BrowserThreadImpl> io_thread_;
|
| scoped_ptr<AudioInputDeviceManager> manager_;
|
| @@ -139,7 +134,7 @@ class AudioInputDeviceManagerTest: public testing::Test {
|
| DISALLOW_COPY_AND_ASSIGN(AudioInputDeviceManagerTest);
|
| };
|
|
|
| -// Test the devices can be opened and closed.
|
| +// Opens and closes the devices.
|
| TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| @@ -148,7 +143,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) {
|
| for (StreamDeviceInfoArray::const_iterator iter =
|
| audio_input_listener_->devices_.begin();
|
| iter != audio_input_listener_->devices_.end(); ++iter) {
|
| - // Open/close the devices.
|
| + // Opens/closes the devices.
|
| int session_id = manager_->Open(*iter);
|
| manager_->Close(session_id);
|
|
|
| @@ -157,11 +152,13 @@ TEST_F(AudioInputDeviceManagerTest, OpenAndCloseDevice) {
|
| .Times(1);
|
| EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| +
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
| }
|
|
|
| -// Open multiple devices at one time and close them later.
|
| +// Opens multiple devices at one time and closes them later.
|
| TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| @@ -171,21 +168,23 @@ TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) {
|
| const int kDeviceSize = audio_input_listener_->devices_.size();
|
| scoped_array<int> session_id(new int[kDeviceSize]);
|
|
|
| - // Open the devices in a loop.
|
| + // Opens the devices in a loop.
|
| for (StreamDeviceInfoArray::const_iterator iter =
|
| audio_input_listener_->devices_.begin();
|
| iter != audio_input_listener_->devices_.end(); ++iter, ++index) {
|
| - // Open the devices.
|
| + // Opens the devices.
|
| session_id[index] = manager_->Open(*iter);
|
|
|
| - // Expected mock call with expected return value.
|
| + // Expected mock call with expected returned value.
|
| EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture,
|
| session_id[index]))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| +
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
|
|
| - // Check if the session_ids are unique
|
| + // Checks if the session_ids are unique.
|
| for (int i = 0; i < kDeviceSize - 1; ++i) {
|
| for (int k = i+1; k < kDeviceSize; ++k) {
|
| EXPECT_TRUE(session_id[i] != session_id[k]);
|
| @@ -193,15 +192,17 @@ TEST_F(AudioInputDeviceManagerTest, OpenMultipleDevices) {
|
| }
|
|
|
| for (int i = 0; i < kDeviceSize; ++i) {
|
| - // Close the devices.
|
| + // Closes the devices.
|
| manager_->Close(session_id[i]);
|
| EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id[i]))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| +
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
| }
|
|
|
| -// Try to open a non-existing device.
|
| +// Opens a non-existing device.
|
| TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| @@ -212,42 +213,21 @@ TEST_F(AudioInputDeviceManagerTest, OpenNotExistingDevice) {
|
| std::string device_id("id_doesnt_exist");
|
| StreamDeviceInfo dummy_device(stream_type, device_name, device_id, false);
|
|
|
| - // This should fail and trigger error code 'kDeviceNotAvailable'.
|
| int session_id = manager_->Open(dummy_device);
|
| -
|
| - EXPECT_CALL(*audio_input_listener_, Error(_, session_id, kDeviceNotAvailable))
|
| + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, session_id))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| -}
|
| -
|
| -// Try open an invalid device.
|
| -TEST_F(AudioInputDeviceManagerTest, OpenInvalidDevice) {
|
| - if (!CanRunAudioInputDeviceTests())
|
| - return;
|
| - InSequence s;
|
| -
|
| - MediaStreamType stream_type = kAudioCapture;
|
| - std::string device_name;
|
| - std::string device_id;
|
| - device_name = audio_input_listener_->devices_.front().name;
|
| - device_id = "wrong_id";
|
| - StreamDeviceInfo invalid_device(stream_type, device_name, device_id, false);
|
| -
|
| - // This should fail and trigger error code 'kDeviceNotAvailable'.
|
| - int session_id = manager_->Open(invalid_device);
|
|
|
| - EXPECT_CALL(*audio_input_listener_, Error(_, session_id, kDeviceNotAvailable))
|
| - .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
|
|
| -// Opening default device twice should work.
|
| +// Opens default device twice.
|
| TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| InSequence s;
|
|
|
| - // Open/close the default device twice.
|
| + // Opens and closes the default device twice.
|
| int first_session_id = manager_->Open(
|
| audio_input_listener_->devices_.front());
|
| int second_session_id = manager_->Open(
|
| @@ -255,7 +235,7 @@ TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) {
|
| manager_->Close(first_session_id);
|
| manager_->Close(second_session_id);
|
|
|
| - // Expected mock calls with expected return values.
|
| + // Expected mock calls with expected returned values.
|
| EXPECT_NE(first_session_id, second_session_id);
|
| EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, first_session_id))
|
| .Times(1);
|
| @@ -265,11 +245,13 @@ TEST_F(AudioInputDeviceManagerTest, OpenDeviceTwice) {
|
| .Times(1);
|
| EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, second_session_id))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| +
|
| + // Waits for the callback.
|
| + message_loop_->RunAllPending();
|
| }
|
|
|
| -// Test the Start and Close function after opening the devices.
|
| -TEST_F(AudioInputDeviceManagerTest, StartAndStopDevice) {
|
| +// Starts and closes the sessions after opening the devices.
|
| +TEST_F(AudioInputDeviceManagerTest, StartAndStopSession) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| InSequence s;
|
| @@ -278,38 +260,41 @@ TEST_F(AudioInputDeviceManagerTest, StartAndStopDevice) {
|
| const int kDeviceSize = audio_input_listener_->devices_.size();
|
| scoped_array<int> session_id(new int[kDeviceSize]);
|
|
|
| - // Create the EventHandler for the sessions.
|
| + // Creates the EventHandler for the sessions.
|
| scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| - audio_input_event_handler(new MockAudioInputDeviceManagerEventHandler());
|
| + audio_input_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
|
|
| - // Loop through the devices, and Open/start/stop/close each device.
|
| + // Loops through the devices and calls Open()/Start()/Stop()/Close() for
|
| + // each device.
|
| for (StreamDeviceInfoArray::const_iterator iter =
|
| audio_input_listener_->devices_.begin();
|
| iter != audio_input_listener_->devices_.end(); ++iter, ++index) {
|
| - // Note that no stop device notification for Event Handler as we have
|
| + // Note that no DeviceStopped() notification for Event Handler as we have
|
| // stopped the device before calling close.
|
| - // Open/start/stop/close the device.
|
| session_id[index] = manager_->Open(*iter);
|
| - manager_->Start(session_id[index], audio_input_event_handler.get());
|
| - manager_->Stop(session_id[index]);
|
| - manager_->Close(session_id[index]);
|
| -
|
| - // Expected mock calls with expected return values.
|
| EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture,
|
| session_id[index]))
|
| .Times(1);
|
| + message_loop_->RunAllPending();
|
| +
|
| + manager_->Start(session_id[index], audio_input_event_handler.get());
|
| EXPECT_CALL(*audio_input_event_handler,
|
| - OnDeviceStarted(session_id[index], iter->device_id))
|
| + DeviceStarted(session_id[index], iter->device_id))
|
| .Times(1);
|
| + message_loop_->RunAllPending();
|
| +
|
| + manager_->Stop(session_id[index]);
|
| + manager_->Close(session_id[index]);
|
| EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture,
|
| session_id[index]))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| + message_loop_->RunAllPending();
|
| }
|
| }
|
|
|
| -// Test the behavior of calling Close without calling Stop.
|
| -TEST_F(AudioInputDeviceManagerTest, CloseWithoutStopDevice) {
|
| +// Tests the behavior of calling Close() without calling Stop().
|
| +TEST_F(AudioInputDeviceManagerTest, CloseWithoutStopSession) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| InSequence s;
|
| @@ -318,40 +303,43 @@ TEST_F(AudioInputDeviceManagerTest, CloseWithoutStopDevice) {
|
| const int kDeviceSize = audio_input_listener_->devices_.size();
|
| scoped_array<int> session_id(new int[kDeviceSize]);
|
|
|
| - // Create the EventHandlers for the sessions.
|
| + // Creates the EventHandlers for the sessions.
|
| scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| - audio_input_event_handler(new MockAudioInputDeviceManagerEventHandler());
|
| + audio_input_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
|
|
| - // Loop through the devices, and open/start/close the devices.
|
| + // Loop through the devices, and calls Open()/Start()/Close() for the devices.
|
| // Note that we do not call stop.
|
| for (StreamDeviceInfoArray::const_iterator iter =
|
| audio_input_listener_->devices_.begin();
|
| iter != audio_input_listener_->devices_.end(); ++iter, ++index) {
|
| - // Open/start/close the device.
|
| + // Calls Open()/Start()/Close() for each device.
|
| session_id[index] = manager_->Open(*iter);
|
| - manager_->Start(session_id[index], audio_input_event_handler.get());
|
| - manager_->Close(session_id[index]);
|
| -
|
| - // Expected mock calls with expected return values.
|
| EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture,
|
| session_id[index]))
|
| .Times(1);
|
| + message_loop_->RunAllPending();
|
| +
|
| + manager_->Start(session_id[index], audio_input_event_handler.get());
|
| EXPECT_CALL(*audio_input_event_handler,
|
| - OnDeviceStarted(session_id[index], iter->device_id))
|
| + DeviceStarted(session_id[index], iter->device_id))
|
| .Times(1);
|
| + message_loop_->RunAllPending();
|
| +
|
| // Event Handler should get a stop device notification as no stop is called
|
| // before closing the device.
|
| + manager_->Close(session_id[index]);
|
| EXPECT_CALL(*audio_input_event_handler,
|
| - OnDeviceStopped(session_id[index]))
|
| + DeviceStopped(session_id[index]))
|
| .Times(1);
|
| EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture,
|
| session_id[index]))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| + message_loop_->RunAllPending();
|
| }
|
| }
|
|
|
| -// Should be able to start the default device twice.
|
| +// Starts the same device twice.
|
| TEST_F(AudioInputDeviceManagerTest, StartDeviceTwice) {
|
| if (!CanRunAudioInputDeviceTests())
|
| return;
|
| @@ -359,45 +347,123 @@ TEST_F(AudioInputDeviceManagerTest, StartDeviceTwice) {
|
|
|
| // Create one EventHandler for each session.
|
| scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| - first_audio_input_event_handler(
|
| - new MockAudioInputDeviceManagerEventHandler());
|
| + first_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
| scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| - second_audio_input_event_handler(
|
| - new MockAudioInputDeviceManagerEventHandler());
|
| + second_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
|
|
| // Open the default device twice.
|
| StreamDeviceInfoArray::const_iterator iter =
|
| audio_input_listener_->devices_.begin();
|
| int first_session_id = manager_->Open(*iter);
|
| int second_session_id = manager_->Open(*iter);
|
| + EXPECT_NE(first_session_id, second_session_id);
|
| + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, first_session_id))
|
| + .Times(1);
|
| + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, second_session_id))
|
| + .Times(1);
|
| + message_loop_->RunAllPending();
|
| +
|
| + // Calls Start()/Stop()/Close() for the default device twice.
|
| + manager_->Start(first_session_id, first_event_handler.get());
|
| + manager_->Start(second_session_id, second_event_handler.get());
|
| + EXPECT_CALL(*first_event_handler,
|
| + DeviceStarted(first_session_id,
|
| + AudioManagerBase::kDefaultDeviceId))
|
| + .Times(1);
|
| + EXPECT_CALL(*second_event_handler,
|
| + DeviceStarted(second_session_id,
|
| + AudioManagerBase::kDefaultDeviceId))
|
| + .Times(1);
|
| + message_loop_->RunAllPending();
|
|
|
| - // Start/stop/close the default device twice.
|
| - manager_->Start(first_session_id, first_audio_input_event_handler.get());
|
| - manager_->Start(second_session_id, second_audio_input_event_handler.get());
|
| manager_->Stop(first_session_id);
|
| manager_->Stop(second_session_id);
|
| manager_->Close(first_session_id);
|
| manager_->Close(second_session_id);
|
| + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, first_session_id))
|
| + .Times(1);
|
| + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, second_session_id))
|
| + .Times(1);
|
| + message_loop_->RunAllPending();
|
| +}
|
|
|
| - // Expected mock calls with expected return values.
|
| - EXPECT_NE(first_session_id, second_session_id);
|
| - EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, first_session_id))
|
| +// Starts an invalid session.
|
| +TEST_F(AudioInputDeviceManagerTest, StartInvalidSession) {
|
| + if (!CanRunAudioInputDeviceTests())
|
| + return;
|
| + InSequence s;
|
| +
|
| + // Creates the EventHandlers for the sessions.
|
| + scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| + audio_input_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
| +
|
| + // Opens the first device.
|
| + StreamDeviceInfoArray::const_iterator iter =
|
| + audio_input_listener_->devices_.begin();
|
| + int session_id = manager_->Open(*iter);
|
| + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, session_id))
|
| .Times(1);
|
| - EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, second_session_id))
|
| + message_loop_->RunAllPending();
|
| +
|
| + // Starts a non-opened device.
|
| + // This should fail and trigger error code 'kDeviceNotAvailable'.
|
| + int invalid_session_id = session_id + 1;
|
| + manager_->Start(invalid_session_id, audio_input_event_handler.get());
|
| + EXPECT_CALL(*audio_input_event_handler,
|
| + DeviceStarted(invalid_session_id,
|
| + AudioInputDeviceManager::kInvalidDeviceId))
|
| .Times(1);
|
| - EXPECT_CALL(*first_audio_input_event_handler,
|
| - OnDeviceStarted(first_session_id,
|
| - AudioManagerBase::kDefaultDeviceId))
|
| + message_loop_->RunAllPending();
|
| +
|
| + manager_->Close(session_id);
|
| + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id))
|
| + .Times(1);
|
| + message_loop_->RunAllPending();
|
| +}
|
| +
|
| +// Starts a session twice, the first time should succeed, while the second
|
| +// time should fail.
|
| +TEST_F(AudioInputDeviceManagerTest, StartSessionTwice) {
|
| + if (!CanRunAudioInputDeviceTests())
|
| + return;
|
| + InSequence s;
|
| +
|
| + // Creates the EventHandlers for the sessions.
|
| + scoped_ptr<MockAudioInputDeviceManagerEventHandler>
|
| + audio_input_event_handler(
|
| + new MockAudioInputDeviceManagerEventHandler(message_loop_.get()));
|
| +
|
| + // Opens the first device.
|
| + StreamDeviceInfoArray::const_iterator iter =
|
| + audio_input_listener_->devices_.begin();
|
| + int session_id = manager_->Open(*iter);
|
| + EXPECT_CALL(*audio_input_listener_, Opened(kAudioCapture, session_id))
|
| .Times(1);
|
| - EXPECT_CALL(*second_audio_input_event_handler,
|
| - OnDeviceStarted(second_session_id,
|
| - AudioManagerBase::kDefaultDeviceId))
|
| + message_loop_->RunAllPending();
|
| +
|
| + // Starts the session, it should succeed.
|
| + manager_->Start(session_id, audio_input_event_handler.get());
|
| + EXPECT_CALL(*audio_input_event_handler,
|
| + DeviceStarted(session_id,
|
| + AudioManagerBase::kDefaultDeviceId))
|
| .Times(1);
|
| - EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, first_session_id))
|
| + message_loop_->RunAllPending();
|
| +
|
| + // Starts the session for the second time, it should fail.
|
| + manager_->Start(session_id, audio_input_event_handler.get());
|
| + EXPECT_CALL(*audio_input_event_handler,
|
| + DeviceStarted(session_id,
|
| + AudioInputDeviceManager::kInvalidDeviceId))
|
| .Times(1);
|
| - EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, second_session_id))
|
| +
|
| + manager_->Stop(session_id);
|
| + manager_->Close(session_id);
|
| + EXPECT_CALL(*audio_input_listener_, Closed(kAudioCapture, session_id))
|
| .Times(1);
|
| - SyncWithAudioInputDeviceManagerThread();
|
| + message_loop_->RunAllPending();
|
| }
|
|
|
| } // namespace media_stream
|
|
|