text: // Copyright 2020 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 "chrome/browser/policy/messaging_layer/public/report_client.h"
  
  #include "base/memory/singleton.h"
  #include "base/task/post_task.h"
  #include "base/test/scoped_feature_list.h"
  #include "base/test/task_environment.h"
  #include "build/build_config.h"
  #include "build/chromeos_buildflags.h"
  #include "chrome/browser/policy/messaging_layer/public/report_queue_impl.h"
  #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
  #include "components/reporting/client/report_queue_configuration.h"
  #include "components/reporting/proto/record_constants.pb.h"
  #include "components/reporting/util/status.h"
  #include "components/reporting/util/status_macros.h"
  #include "components/reporting/util/statusor.h"
  #include "content/public/browser/browser_task_traits.h"
  #include "content/public/browser/browser_thread.h"
  #include "content/public/test/browser_task_environment.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
  #include "chrome/test/base/testing_profile.h"
  #include "components/user_manager/scoped_user_manager.h"
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  
  namespace reporting {
  namespace {
  
  // Usage (in tests only):
  //
  //   TestEvent<ResType> e;
  //   ... Do some async work passing e.cb() as a completion callback of
  //   base::OnceCallback<void(ResType* res)> type which also may perform some
  //   other action specified by |done| callback provided by the caller.
  //   ... = e.result();  // Will wait for e.cb() to be called and return the
  //   collected result.
  //
  template <typename ResType>
  class TestEvent {
   public:
    TestEvent() : run_loop_(std::make_unique<base::RunLoop>()) {}
    ~TestEvent() = default;
    TestEvent(const TestEvent& other) = delete;
    TestEvent& operator=(const TestEvent& other) = delete;
    ResType result() {
      run_loop_->Run();
      return std::forward<ResType>(result_);
    }
  
    // Completion callback to hand over to the processing method.
    base::OnceCallback<void(ResType res)> cb() {
      return base::BindOnce(
          [](base::RunLoop* run_loop, ResType* result, ResType res) {
            *result = std::forward<ResType>(res);
            run_loop->Quit();
          },
          base::Unretained(run_loop_.get()), base::Unretained(&result_));
    }
  
   private:
    std::unique_ptr<base::RunLoop> run_loop_;
    ResType result_;
  };
  
  class ReportClientTest : public testing::Test {
   public:
    void SetUp() override {
  #if BUILDFLAG(IS_CHROMEOS_ASH)
      // Set up fake primary profile.
      auto mock_user_manager =
          std::make_unique<testing::NiceMock<chromeos::FakeChromeUserManager>>();
      profile_ = std::make_unique<TestingProfile>(
          base::FilePath(FILE_PATH_LITERAL("/home/chronos/u-0123456789abcdef")));
      const AccountId account_id(AccountId::FromUserEmailGaiaId(
          profile_->GetProfileUserName(), "12345"));
      const user_manager::User* user =
          mock_user_manager->AddPublicAccountUser(account_id);
      mock_user_manager->UserLoggedIn(account_id, user->username_hash(),
                                      /*browser_restart=*/false,
                                      /*is_child=*/false);
      user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
          std::move(mock_user_manager));
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
      // Provide a mock cloud policy client.
      client_ = std::make_unique<policy::MockCloudPolicyClient>();
      client_->SetDMToken("FAKE_DM_TOKEN");
      test_reporting_ =
          std::make_unique<ReportingClient::TestEnvironment>(client_.get());
  
      scoped_feature_list_.InitAndEnableFeature(
          ReportingClient::kEncryptedReportingPipeline);
    }
  
    void TearDown() override {
  #if BUILDFLAG(IS_CHROMEOS_ASH)
      user_manager_.reset();
      profile_.reset();
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
    }
  
   protected:
    base::test::ScopedFeatureList scoped_feature_list_;
    std::unique_ptr<ReportingClient::TestEnvironment> test_reporting_;
    // BrowserTaskEnvironment needs to be destroyed before TestEnvironment
    // and ScopedFeatureList, so that tasks on other threads don't run after
    // they are destroyed.
    content::BrowserTaskEnvironment task_environment_;
  #if BUILDFLAG(IS_CHROMEOS_ASH)
    std::unique_ptr<TestingProfile> profile_;
    std::unique_ptr<user_manager::ScopedUserManager> user_manager_;
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
    std::unique_ptr<policy::MockCloudPolicyClient> client_;
    const std::string dm_token_ = "TOKEN";
    const Destination destination_ = Destination::UPLOAD_EVENTS;
    ReportQueueConfiguration::PolicyCheckCallback policy_checker_callback_ =
        base::BindRepeating([]() { return Status::StatusOK(); });
  };
  
  // Tests that a ReportQueue can be created using the ReportingClient.
  TEST_F(ReportClientTest, CreatesReportQueue) {
    auto config_result = ReportQueueConfiguration::Create(
        dm_token_, destination_, policy_checker_callback_);
    ASSERT_OK(config_result);
  
    TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a;
    ReportingClient::CreateReportQueueImpl(std::move(config_result.ValueOrDie()),
                                           a.cb());
    ASSERT_OK(a.result());
  }
  
  // Ensures that created ReportQueues are actually different.
  TEST_F(ReportClientTest, CreatesTwoDifferentReportQueues) {
    auto config_result = ReportQueueConfiguration::Create(
        dm_token_, destination_, policy_checker_callback_);
    EXPECT_TRUE(config_result.ok());
  
    TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a1;
    ReportingClient::CreateReportQueueImpl(std::move(config_result.ValueOrDie()),
                                           a1.cb());
    auto result = a1.result();
    ASSERT_OK(result);
    auto report_queue_1 = std::move(result.ValueOrDie());
  
    TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a2;
    config_result = ReportQueueConfiguration::Create(dm_token_, destination_,
                                                     policy_checker_callback_);
    ReportingClient::CreateReportQueueImpl(std::move(config_result.ValueOrDie()),
                                           a2.cb());
    result = a2.result();
    ASSERT_OK(result);
    auto report_queue_2 = std::move(result.ValueOrDie());
  
    EXPECT_NE(report_queue_1.get(), report_queue_2.get());
  }
  }  // namespace
  }  // namespace reporting
  