text: /*
   *  Copyright 2020 The WebRTC Project Authors. All rights reserved.
   *
   *  Use of this source code is governed by a BSD-style license
   *  that can be found in the LICENSE file in the root of the source
   *  tree. An additional intellectual property rights grant can be found
   *  in the file PATENTS.  All contributing project authors may
   *  be found in the AUTHORS file in the root of the source tree.
   */
  
  #include "video/adaptation/quality_scaler_resource.h"
  
  #include <memory>
  
  #include "absl/types/optional.h"
  #include "api/task_queue/default_task_queue_factory.h"
  #include "api/task_queue/task_queue_factory.h"
  #include "api/video_codecs/video_encoder.h"
  #include "rtc_base/event.h"
  #include "rtc_base/task_queue.h"
  #include "test/gtest.h"
  
  namespace webrtc {
  
  namespace {
  
  const int kDefaultTimeout = 5000;
  
  class FakeQualityScalerQpUsageHandlerCallback
      : public QualityScalerQpUsageHandlerCallbackInterface {
   public:
    explicit FakeQualityScalerQpUsageHandlerCallback(
        rtc::TaskQueue* encoder_queue)
        : QualityScalerQpUsageHandlerCallbackInterface(),
          encoder_queue_(encoder_queue),
          is_handled_(false),
          qp_usage_handled_event_(true /* manual_reset */, false),
          clear_qp_samples_result_(absl::nullopt) {}
    ~FakeQualityScalerQpUsageHandlerCallback() override {
      RTC_DCHECK(is_handled_)
          << "The callback was destroyed without being invoked.";
    }
  
    void OnQpUsageHandled(bool clear_qp_samples) override {
      ASSERT_TRUE(encoder_queue_->IsCurrent());
      RTC_DCHECK(!is_handled_);
      clear_qp_samples_result_ = clear_qp_samples;
      is_handled_ = true;
      qp_usage_handled_event_.Set();
    }
  
    bool is_handled() const { return is_handled_; }
    rtc::Event* qp_usage_handled_event() { return &qp_usage_handled_event_; }
    absl::optional<bool> clear_qp_samples_result() const {
      return clear_qp_samples_result_;
    }
  
   private:
    rtc::TaskQueue* const encoder_queue_;
    bool is_handled_;
    rtc::Event qp_usage_handled_event_;
    absl::optional<bool> clear_qp_samples_result_;
  };
  
  }  // namespace
  
  class QualityScalerResourceTest : public ::testing::Test {
   public:
    QualityScalerResourceTest()
        : task_queue_factory_(CreateDefaultTaskQueueFactory()),
          encoder_queue_(task_queue_factory_->CreateTaskQueue(
              "EncoderQueue",
              TaskQueueFactory::Priority::NORMAL)),
          quality_scaler_resource_(new QualityScalerResource()) {
      quality_scaler_resource_->Initialize(&encoder_queue_);
      rtc::Event event;
      encoder_queue_.PostTask([this, &event] {
        quality_scaler_resource_->StartCheckForOveruse(
            VideoEncoder::QpThresholds());
        event.Set();
      });
      event.Wait(kDefaultTimeout);
    }
  
    ~QualityScalerResourceTest() {
      rtc::Event event;
      encoder_queue_.PostTask([this, &event] {
        quality_scaler_resource_->StopCheckForOveruse();
        event.Set();
      });
      event.Wait(kDefaultTimeout);
    }
  
   protected:
    const std::unique_ptr<TaskQueueFactory> task_queue_factory_;
    rtc::TaskQueue encoder_queue_;
    rtc::scoped_refptr<QualityScalerResource> quality_scaler_resource_;
  };
  
  TEST_F(QualityScalerResourceTest, ReportQpHigh) {
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    encoder_queue_.PostTask([this, callback] {
      quality_scaler_resource_->OnReportQpUsageHigh(callback);
    });
    callback->qp_usage_handled_event()->Wait(kDefaultTimeout);
  }
  
  TEST_F(QualityScalerResourceTest, ReportQpLow) {
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    encoder_queue_.PostTask([this, callback] {
      quality_scaler_resource_->OnReportQpUsageLow(callback);
    });
    callback->qp_usage_handled_event()->Wait(kDefaultTimeout);
  }
  
  // TODO(https://crbug.com/webrtc/11542): Callbacks are currently resolved
  // immediately, but when we have an adaptation queue this test will ensure we
  // can have multiple callbacks pending at the same time.
  TEST_F(QualityScalerResourceTest, MultipleCallbacksInFlight) {
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback1 =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback2 =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback3 =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    encoder_queue_.PostTask([this, callback1, callback2, callback3] {
      quality_scaler_resource_->OnReportQpUsageHigh(callback1);
      quality_scaler_resource_->OnReportQpUsageLow(callback2);
      quality_scaler_resource_->OnReportQpUsageHigh(callback3);
    });
    callback1->qp_usage_handled_event()->Wait(kDefaultTimeout);
    callback2->qp_usage_handled_event()->Wait(kDefaultTimeout);
    callback3->qp_usage_handled_event()->Wait(kDefaultTimeout);
  }
  
  // TODO(https://crbug.com/webrtc/11542): Callbacks are currently resolved
  // immediately, but when we have an adaptation queue this test will ensure we
  // can abort pending callbacks.
  TEST_F(QualityScalerResourceTest, AbortPendingCallbacksAndStartAgain) {
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback1 =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    rtc::scoped_refptr<FakeQualityScalerQpUsageHandlerCallback> callback2 =
        new FakeQualityScalerQpUsageHandlerCallback(&encoder_queue_);
    encoder_queue_.PostTask([this, callback1, callback2] {
      quality_scaler_resource_->OnReportQpUsageHigh(callback1);
      quality_scaler_resource_->StopCheckForOveruse();
      EXPECT_TRUE(callback1->qp_usage_handled_event()->Wait(0));
      quality_scaler_resource_->StartCheckForOveruse(
          VideoEncoder::QpThresholds());
      quality_scaler_resource_->OnReportQpUsageHigh(callback2);
    });
    callback1->qp_usage_handled_event()->Wait(kDefaultTimeout);
    callback2->qp_usage_handled_event()->Wait(kDefaultTimeout);
  }
  
  }  // namespace webrtc
  