// 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 "content/browser/media/media_devices_util.h"

#include "media/audio/audio_device_description.h"
#include "media/capture/video/video_capture_device_descriptor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/mediastream/media_devices.h"
#include "url/gurl.h"

namespace content {

using ::blink::mojom::MediaDeviceType;
using ::blink::mojom::MediaStreamType;
using ::testing::Eq;
using ::testing::Optional;

namespace {

MediaDeviceSaltAndOrigin ExampleSaltAndOrigin() {
  return MediaDeviceSaltAndOrigin(
      "Device ID Salt", url::Origin::Create(GURL("https://example.com")),
      "Group ID Salt");
}

blink::WebMediaDeviceInfo ExampleWebMediaDeviceInfo() {
  return blink::WebMediaDeviceInfo(
      /*device_id=*/"example device id",
      /*label=*/"example label",
      /*group_id=*/"example_group_id",
      /*video_control_support=*/{.pan = true, .tilt = true, .zoom = true},
      blink::mojom::FacingMode::kEnvironment,
      media::CameraAvailability::kAvailable);
}

}  // namespace

TEST(MediaDevicesUtilTest, TranslateEmptyMediaDeviceInfoArray) {
  MediaDeviceSaltAndOrigin salt_and_origin = ExampleSaltAndOrigin();
  blink::WebMediaDeviceInfoArray web_media_device_infos =
      TranslateMediaDeviceInfoArray(/*has_permission=*/false, salt_and_origin,
                                    {});
  EXPECT_TRUE(web_media_device_infos.empty());
  web_media_device_infos = TranslateMediaDeviceInfoArray(
      /*has_permission=*/true, salt_and_origin, {});
  EXPECT_TRUE(web_media_device_infos.empty());
}

TEST(MediaDevicesUtilBrowserTest, TranslateMediaDeviceInfoWithPermission) {
  MediaDeviceSaltAndOrigin salt_and_origin = ExampleSaltAndOrigin();
  blink::WebMediaDeviceInfo original_info = ExampleWebMediaDeviceInfo();
  blink::WebMediaDeviceInfo translated_info = TranslateMediaDeviceInfo(
      /*has_permission=*/true, salt_and_origin, original_info);
  EXPECT_EQ(
      translated_info.device_id,
      GetHMACForRawMediaDeviceID(salt_and_origin, original_info.device_id));
  EXPECT_EQ(translated_info.label, original_info.label);
  EXPECT_EQ(translated_info.group_id,
            GetHMACForRawMediaDeviceID(salt_and_origin, original_info.group_id,
                                       /*use_group_salt=*/true));
  EXPECT_EQ(translated_info.video_facing, original_info.video_facing);
  EXPECT_EQ(translated_info.video_control_support.pan,
            original_info.video_control_support.pan);
  EXPECT_EQ(translated_info.video_control_support.tilt,
            original_info.video_control_support.tilt);
  EXPECT_EQ(translated_info.video_control_support.zoom,
            original_info.video_control_support.zoom);
  EXPECT_EQ(translated_info.availability, original_info.availability);
}

TEST(MediaDevicesUtilBrowserTest, TranslateMediaDeviceInfoWithoutPermission) {
  MediaDeviceSaltAndOrigin salt_and_origin = ExampleSaltAndOrigin();
  blink::WebMediaDeviceInfo original_info = ExampleWebMediaDeviceInfo();
  blink::WebMediaDeviceInfo translated_info = TranslateMediaDeviceInfo(
      /*has_permission=*/false, salt_and_origin, original_info);
  EXPECT_TRUE(translated_info.device_id.empty());
  EXPECT_TRUE(translated_info.label.empty());
  EXPECT_TRUE(translated_info.group_id.empty());
  EXPECT_EQ(translated_info.video_facing, blink::mojom::FacingMode::kNone);
  EXPECT_FALSE(translated_info.video_control_support.pan);
  EXPECT_FALSE(translated_info.video_control_support.tilt);
  EXPECT_FALSE(translated_info.video_control_support.zoom);
  EXPECT_FALSE(translated_info.availability.has_value());
}

TEST(MediaDevicesUtilTest, TranslateSpecialDeviceIDs) {
  MediaDeviceSaltAndOrigin salt_and_origin = ExampleSaltAndOrigin();
  const std::string raw_default_id(
      media::AudioDeviceDescription::kDefaultDeviceId);
  const std::string hashed_default_id =
      GetHMACForRawMediaDeviceID(salt_and_origin, raw_default_id);
  EXPECT_TRUE(DoesRawMediaDeviceIDMatchHMAC(salt_and_origin, hashed_default_id,
                                            raw_default_id));
  EXPECT_EQ(raw_default_id, hashed_default_id);

  const std::string raw_communications_id(
      media::AudioDeviceDescription::kCommunicationsDeviceId);
  const std::string hashed_communications_id =
      GetHMACForRawMediaDeviceID(salt_and_origin, raw_communications_id);
  EXPECT_TRUE(DoesRawMediaDeviceIDMatchHMAC(
      salt_and_origin, hashed_communications_id, raw_communications_id));
  EXPECT_EQ(raw_communications_id, hashed_communications_id);

  const std::string raw_empty_id;
  const std::string hashed_empty_id =
      GetHMACForRawMediaDeviceID(salt_and_origin, raw_empty_id);
  EXPECT_TRUE(DoesRawMediaDeviceIDMatchHMAC(salt_and_origin, hashed_empty_id,
                                            raw_empty_id));
  EXPECT_EQ(raw_empty_id, hashed_empty_id);
}

TEST(MediaDevicesUtilTest, TranslateNonSpecialDeviceID) {
  MediaDeviceSaltAndOrigin salt_and_origin = ExampleSaltAndOrigin();
  const std::string raw_other_id("other-unique-id");
  const std::string hashed_other_id =
      GetHMACForRawMediaDeviceID(salt_and_origin, raw_other_id);
  EXPECT_TRUE(DoesRawMediaDeviceIDMatchHMAC(salt_and_origin, hashed_other_id,
                                            raw_other_id));
  EXPECT_FALSE(DoesRawMediaDeviceIDMatchHMAC(
      salt_and_origin, hashed_other_id,
      media::AudioDeviceDescription::kDefaultDeviceId));
  EXPECT_FALSE(DoesRawMediaDeviceIDMatchHMAC(
      salt_and_origin, hashed_other_id,
      media::AudioDeviceDescription::kCommunicationsDeviceId));
  EXPECT_NE(raw_other_id, hashed_other_id);
  EXPECT_EQ(hashed_other_id.size(), 64U);
  for (const char& c : hashed_other_id) {
    EXPECT_TRUE(base::IsAsciiDigit(c) || (c >= 'a' && c <= 'f'));
  }
}

}  // namespace content
