chromium/ash/capture_mode/capture_audio_mixing_unittests.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_menu_group.h"
#include "ash/capture_mode/capture_mode_session_test_api.h"
#include "ash/capture_mode/capture_mode_settings_test_api.h"
#include "ash/capture_mode/capture_mode_settings_view.h"
#include "ash/capture_mode/capture_mode_test_util.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/test_capture_mode_delegate.h"
#include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
#include "ash/style/icon_button.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_util.h"
#include "base/test/metrics/histogram_tester.h"

namespace ash {

namespace {

CaptureModeController* StartSession() {
  return StartCaptureSession(CaptureModeSource::kFullscreen,
                             CaptureModeType::kVideo);
}

bool IsAudioOptionChecked(int option_id) {
  return CaptureModeSettingsTestApi().GetAudioInputMenuGroup()->IsOptionChecked(
      option_id);
}

}  // namespace

using CaptureAudioMixingTest = AshTestBase;

TEST_F(CaptureAudioMixingTest, AudioSettingsMenu) {
  auto* controller = StartSession();
  auto* event_generator = GetEventGenerator();

  // Open the settings menu, and check the currently selected audio mode.
  ClickOnView(GetSettingsButton(), event_generator);
  EXPECT_EQ(AudioRecordingMode::kOff,
            controller->GetEffectiveAudioRecordingMode());

  {
    CaptureModeSettingsTestApi test_api;
    views::View* microphone_option = test_api.GetMicrophoneOption();
    views::View* system_option = test_api.GetSystemAudioOption();
    views::View* system_and_microphone_option =
        test_api.GetSystemAndMicrophoneAudioOption();

    // All the options should exist, but only the "off" option should be
    // selected.
    EXPECT_TRUE(microphone_option);
    EXPECT_TRUE(system_option);
    EXPECT_TRUE(system_and_microphone_option);
    EXPECT_TRUE(IsAudioOptionChecked(kAudioOff));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioMicrophone));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioSystem));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioSystemAndMicrophone));

    // Clicking on the system audio should select that option and update the
    // controller.
    ClickOnView(system_option, event_generator);
    EXPECT_FALSE(IsAudioOptionChecked(kAudioOff));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioMicrophone));
    EXPECT_TRUE(IsAudioOptionChecked(kAudioSystem));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioSystemAndMicrophone));
    EXPECT_EQ(AudioRecordingMode::kSystem,
              controller->GetEffectiveAudioRecordingMode());

    // Likewise for the system+microphone option.
    ClickOnView(system_and_microphone_option, event_generator);
    EXPECT_FALSE(IsAudioOptionChecked(kAudioOff));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioMicrophone));
    EXPECT_FALSE(IsAudioOptionChecked(kAudioSystem));
    EXPECT_TRUE(IsAudioOptionChecked(kAudioSystemAndMicrophone));
    EXPECT_EQ(AudioRecordingMode::kSystemAndMicrophone,
              controller->GetEffectiveAudioRecordingMode());
  }

  // Exit the session, and start a new one. The most recent audio setting will
  // be remembered.
  SendKey(ui::VKEY_ESCAPE, event_generator);
  StartSession();
  ClickOnView(GetSettingsButton(), event_generator);
  EXPECT_FALSE(IsAudioOptionChecked(kAudioOff));
  EXPECT_FALSE(IsAudioOptionChecked(kAudioMicrophone));
  EXPECT_FALSE(IsAudioOptionChecked(kAudioSystem));
  EXPECT_TRUE(IsAudioOptionChecked(kAudioSystemAndMicrophone));
}

TEST_F(CaptureAudioMixingTest, KeyboardNavigation) {
  auto* controller = StartSession();
  using FocusGroup = CaptureModeSessionFocusCycler::FocusGroup;
  CaptureModeSessionTestApi test_api(controller->capture_mode_session());

  auto* event_generator = GetEventGenerator();
  SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/6);
  EXPECT_EQ(test_api.GetCurrentFocusedView()->GetView(), GetSettingsButton());
  // Press the space bar, and the settings menu should open, ready for keyboard
  // navigation.
  SendKey(ui::VKEY_SPACE, event_generator);
  EXPECT_EQ(FocusGroup::kPendingSettings, test_api.GetCurrentFocusGroup());
  CaptureModeSettingsView* settings_menu =
      test_api.GetCaptureModeSettingsView();
  ASSERT_TRUE(settings_menu);

  CaptureModeSettingsTestApi settings_test_api;
  // Tab twice, the current focused view is the "Off" option.
  SendKey(ui::VKEY_TAB, event_generator, ui::EF_NONE, /*count=*/2);
  EXPECT_EQ(test_api.GetCurrentFocusedView()->GetView(),
            settings_test_api.GetAudioOffOption());
  // Next tabs will go through all the audio options.
  SendKey(ui::VKEY_TAB, event_generator);
  EXPECT_EQ(test_api.GetCurrentFocusedView()->GetView(),
            settings_test_api.GetSystemAudioOption());
  SendKey(ui::VKEY_TAB, event_generator);
  EXPECT_EQ(test_api.GetCurrentFocusedView()->GetView(),
            settings_test_api.GetMicrophoneOption());
  SendKey(ui::VKEY_TAB, event_generator);
  EXPECT_EQ(test_api.GetCurrentFocusedView()->GetView(),
            settings_test_api.GetSystemAndMicrophoneAudioOption());
}

TEST_F(CaptureAudioMixingTest, ServiceWillRecordAudio) {
  constexpr char kHistogramNameBase[] = "AudioRecordingMode";
  const std::string histogram_name = BuildHistogramName(
      kHistogramNameBase, /*behavior=*/nullptr, /*append_ui_mode_suffix=*/true);

  struct {
    const char* const scope_name;
    AudioRecordingMode audio_mode;
    int expected_number_of_audio_capturers;
  } kTestCases[] = {
      {"Off", AudioRecordingMode::kOff,
       /*expected_number_of_audio_capturers=*/0},
      {"Microphone", AudioRecordingMode::kMicrophone,
       /*expected_number_of_audio_capturers=*/1},
      {"System audio", AudioRecordingMode::kSystem,
       /*expected_number_of_audio_capturers=*/1},
      {"System and microphone audio", AudioRecordingMode::kSystemAndMicrophone,
       /*expected_number_of_audio_capturers=*/2},
  };

  for (const auto& test_case : kTestCases) {
    SCOPED_TRACE(test_case.scope_name);

    base::HistogramTester histogram_tester;
    histogram_tester.ExpectBucketCount(histogram_name, test_case.audio_mode, 0);

    auto* controller = StartSession();
    controller->SetAudioRecordingMode(test_case.audio_mode);

    StartVideoRecordingImmediately();

    EXPECT_TRUE(controller->is_recording_in_progress());
    auto* test_delegate = static_cast<TestCaptureModeDelegate*>(
        controller->delegate_for_testing());
    CaptureModeTestApi().FlushRecordingServiceForTesting();
    EXPECT_EQ(test_case.expected_number_of_audio_capturers,
              test_delegate->GetNumberOfAudioCapturers());
    controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton);

    WaitForCaptureFileToBeSaved();

    histogram_tester.ExpectBucketCount(histogram_name, test_case.audio_mode, 1);
  }
}

// -----------------------------------------------------------------------------
// ProjectorAudioMixingTest:

class ProjectorAudioMixingTest : public CaptureAudioMixingTest {
 public:
  ProjectorAudioMixingTest() = default;
  ~ProjectorAudioMixingTest() override = default;

  // CaptureAudioMixingTest:
  void SetUp() override {
    CaptureAudioMixingTest::SetUp();
    projector_helper_.SetUp();
  }

  void StartProjectorModeSession() {
    projector_helper_.StartProjectorModeSession();
  }

 private:
  ProjectorCaptureModeIntegrationHelper projector_helper_;
};

TEST_F(ProjectorAudioMixingTest, AudioSettingsMenu) {
  constexpr char kHistogramNameBase[] = "AudioRecordingMode";
  const std::string histogram_name = BuildHistogramName(
      kHistogramNameBase,
      CaptureModeTestApi().GetBehavior(BehaviorType::kProjector),
      /*append_ui_mode_suffix=*/true);
  base::HistogramTester histogram_tester;

  StartProjectorModeSession();
  auto* event_generator = GetEventGenerator();

  // Open the settings menu, and check that only microphone and
  // system+microphone options are available.
  ClickOnView(GetSettingsButton(), event_generator);

  CaptureModeSettingsTestApi test_api;
  views::View* off_option = test_api.GetAudioOffOption();
  views::View* microphone_option = test_api.GetMicrophoneOption();
  views::View* system_option = test_api.GetSystemAudioOption();
  views::View* system_and_microphone_option =
      test_api.GetSystemAndMicrophoneAudioOption();

  EXPECT_FALSE(off_option);
  EXPECT_FALSE(system_option);
  EXPECT_TRUE(microphone_option);
  EXPECT_TRUE(system_and_microphone_option);

  // Microphone should still be selected by default.
  EXPECT_TRUE(IsAudioOptionChecked(kAudioMicrophone));
  EXPECT_FALSE(IsAudioOptionChecked(kAudioSystemAndMicrophone));

  // End the session and expect the correct audio mode was recorded.
  auto* controller = CaptureModeController::Get();
  controller->Stop();
  histogram_tester.ExpectBucketCount(histogram_name,
                                     AudioRecordingMode::kMicrophone, 1);
  histogram_tester.ExpectBucketCount(
      histogram_name, AudioRecordingMode::kSystemAndMicrophone, 0);

  // Start a new session and select `kSystemAndMicrophone`, and expect the
  // correct metrics will be recorded when the session ends.
  StartProjectorModeSession();
  controller->SetAudioRecordingMode(AudioRecordingMode::kSystemAndMicrophone);
  controller->Stop();

  histogram_tester.ExpectBucketCount(histogram_name,
                                     AudioRecordingMode::kMicrophone, 1);
  histogram_tester.ExpectBucketCount(
      histogram_name, AudioRecordingMode::kSystemAndMicrophone, 1);
}

}  // namespace ash