text: // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <cmath>
  
  #include "base/logging.h"
  #include "base/memory/scoped_ptr.h"
  #include "media/audio/audio_io.h"
  #include "media/audio/audio_manager_base.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  #if defined(OS_WIN)
  #include "base/win/scoped_com_initializer.h"
  #include "media/audio/win/core_audio_util_win.h"
  #endif
  
  namespace media {
  
  double GetVolumeAfterSetVolumeOnLinux(AudioInputStream* ais,
                                        double target_volume) {
    // SetVolume() is asynchronous on Linux, we need to keep trying until
    // the SetVolume() operation is done.
    static const int kTimesToRun = 10;
    double volume = 0.0;
    for (int i = 0; i < kTimesToRun; ++i) {
      volume = ais->GetVolume();
      if (volume == target_volume)
        break;
  
      // Sleep 100ms to wait for the operation.
      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
    }
  
    return volume;
  }
  
  class AudioInputVolumeTest : public ::testing::Test {
   protected:
    AudioInputVolumeTest()
        : audio_manager_(AudioManager::Create())
  #if defined(OS_WIN)
         , com_init_(base::win::ScopedCOMInitializer::kMTA)
  #endif
    {
    }
  
    bool CanRunAudioTests() {
  #if defined(OS_WIN)
      // TODO(henrika): add support for volume control on Windows XP as well.
      // For now, we might as well signal false already here to avoid running
      // these tests on Windows XP.
      if (!CoreAudioUtil::IsSupported())
        return false;
  #endif
      if (!audio_manager_)
        return false;
  
      return audio_manager_->HasAudioInputDevices();
    }
  
    // Helper method which checks if the stream has volume support.
    bool HasDeviceVolumeControl(AudioInputStream* stream) {
      if (!stream)
        return false;
  
      return (stream->GetMaxVolume() != 0.0);
    }
  
    AudioInputStream* CreateAndOpenStream(const std::string& device_id) {
      const AudioParameters& params =
          audio_manager_->GetInputStreamParameters(device_id);
      AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
          params, device_id);
      EXPECT_TRUE(NULL != ais);
  
  #if defined(OS_LINUX) || defined(OS_OPENBSD)
      // Some linux devices do not support our settings, we may fail to open
      // those devices.
      if (!ais->Open()) {
        // Default device should always be able to be opened.
        EXPECT_TRUE(AudioManagerBase::kDefaultDeviceId != device_id);
        ais->Close();
        ais = NULL;
      }
  #elif defined(OS_WIN) || defined(OS_MACOSX)
      EXPECT_TRUE(ais->Open());
  #endif
  
      return ais;
    }
  
    scoped_ptr<AudioManager> audio_manager_;
  
  #if defined(OS_WIN)
    base::win::ScopedCOMInitializer com_init_;
  #endif
  };
  
  #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(ARCH_CPU_ARM_FAMILY)
  // Currently failing on linux ARM bot: http://crbug/238490
  #define MAYBE_InputVolumeTest DISABLED_InputVolumeTest
  #else
  #define MAYBE_InputVolumeTest InputVolumeTest
  #endif
  
  TEST_F(AudioInputVolumeTest, MAYBE_InputVolumeTest) {
    if (!CanRunAudioTests())
      return;
  
    // Retrieve a list of all available input devices.
    AudioDeviceNames device_names;
    audio_manager_->GetAudioInputDeviceNames(&device_names);
    if (device_names.empty()) {
      LOG(WARNING) << "Could not find any available input device";
      return;
    }
  
    // Scan all available input devices and repeat the same test for all of them.
    for (AudioDeviceNames::const_iterator it = device_names.begin();
         it != device_names.end();
         ++it) {
      AudioInputStream* ais = CreateAndOpenStream(it->unique_id);
      if (!ais) {
        DLOG(WARNING) << "Failed to open stream for device " << it->unique_id;
        continue;
      }
  
      if (!HasDeviceVolumeControl(ais)) {
        DLOG(WARNING) << "Device: " << it->unique_id
                      << ", does not have volume control.";
        ais->Close();
        continue;
      }
  
      double max_volume = ais->GetMaxVolume();
      EXPECT_GT(max_volume, 0.0);
  
      // Store the current input-device volume level.
      double original_volume = ais->GetVolume();
      EXPECT_GE(original_volume, 0.0);
  #if defined(OS_WIN) || defined(OS_MACOSX)
      // Note that |original_volume| can be higher than |max_volume| on Linux.
      EXPECT_LE(original_volume, max_volume);
  #endif
  
      // Set the volume to the maxiumum level..
      ais->SetVolume(max_volume);
      double current_volume = ais->GetVolume();
      EXPECT_EQ(max_volume, current_volume);
  
      // Set the volume to the mininum level (=0).
      double new_volume = 0.0;
      ais->SetVolume(new_volume);
  #if defined(OS_LINUX)
      current_volume = GetVolumeAfterSetVolumeOnLinux(ais, new_volume);
  #else
      current_volume = ais->GetVolume();
  #endif
      EXPECT_EQ(new_volume, current_volume);
  
      // Set the volume to the mid level (50% of max).
      // Verify that the absolute error is small enough.
      new_volume = max_volume / 2;
      ais->SetVolume(new_volume);
  #if defined(OS_LINUX)
      current_volume = GetVolumeAfterSetVolumeOnLinux(ais, new_volume);
  #else
      current_volume = ais->GetVolume();
  #endif
      EXPECT_LT(current_volume, max_volume);
      EXPECT_GT(current_volume, 0);
      EXPECT_NEAR(current_volume, new_volume, 0.25 * max_volume);
  
      // Restores the volume to the original value.
      ais->SetVolume(original_volume);
      current_volume = ais->GetVolume();
      EXPECT_EQ(original_volume, current_volume);
  
      ais->Close();
    }
  }
  
  }  // namespace media
  