12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <CoreAudio/AudioHardware.h>
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind_helpers.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/audio/mac/audio_device_listener_mac.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "media/base/bind_to_current_loop.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "testing/gmock/include/gmock/gmock.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace media {
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class AudioDeviceListenerMacTest : public testing::Test {
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AudioDeviceListenerMacTest() {
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // It's important to create the device listener from the message loop in
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // order to ensure we don't end up with unbalanced TaskObserver calls.
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    message_loop_.PostTask(FROM_HERE, base::Bind(
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        &AudioDeviceListenerMacTest::CreateDeviceListener,
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Unretained(this)));
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    message_loop_.RunUntilIdle();
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~AudioDeviceListenerMacTest() {
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // It's important to destroy the device listener from the message loop in
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // order to ensure we don't end up with unbalanced TaskObserver calls.
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    message_loop_.PostTask(FROM_HERE, base::Bind(
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        &AudioDeviceListenerMacTest::DestroyDeviceListener,
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Unretained(this)));
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    message_loop_.RunUntilIdle();
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void CreateDeviceListener() {
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Force a post task using BindToCurrentLoop() to ensure device listener
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // internals are working correctly.
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    output_device_listener_.reset(new AudioDeviceListenerMac(BindToCurrentLoop(
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        base::Bind(&AudioDeviceListenerMacTest::OnDeviceChange,
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                   base::Unretained(this)))));
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void DestroyDeviceListener() {
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    output_device_listener_.reset();
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool ListenerIsValid() {
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return !output_device_listener_->listener_cb_.is_null();
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Simulate a device change where no output devices are available.
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool SimulateDefaultOutputDeviceChange() {
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Include multiple addresses to ensure only a single device change event
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // occurs.
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const AudioObjectPropertyAddress addresses[] = {
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      AudioDeviceListenerMac::kDeviceChangePropertyAddress,
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      { kAudioHardwarePropertyDevices,
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kAudioObjectPropertyScopeGlobal,
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kAudioObjectPropertyElementMaster }
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    };
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return noErr == output_device_listener_->OnDefaultDeviceChanged(
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kAudioObjectSystemObject, 1, addresses, output_device_listener_.get());
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  MOCK_METHOD0(OnDeviceChange, void());
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected:
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::MessageLoop message_loop_;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<AudioDeviceListenerMac> output_device_listener_;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(AudioDeviceListenerMacTest);
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Simulate a device change events and ensure we get the right callbacks.
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(AudioDeviceListenerMacTest, OutputDeviceChange) {
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(ListenerIsValid());
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  EXPECT_CALL(*this, OnDeviceChange()).Times(1);
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(SimulateDefaultOutputDeviceChange());
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message_loop_.RunUntilIdle();
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace media
87