chromium/chrome/browser/enterprise/util/android_enterprise_info_unittest.cc

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

#include "chrome/browser/enterprise/util/android_enterprise_info.h"

#include <optional>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "testing/gtest/include/gtest/gtest.h"

class AndroidEnterpriseInfoTest : public ::testing::Test {
 protected:
  AndroidEnterpriseInfoTest() {
    instance_ = enterprise_util::AndroidEnterpriseInfo::GetInstance();
    // Java side isn't running, so we need to skip the call to it.
    instance_->set_skip_jni_call_for_testing(true);
  }

  raw_ptr<enterprise_util::AndroidEnterpriseInfo> instance_;
};

class EnterpriseInfoCallbackHelper {
 public:
  std::optional<bool> is_profile_owned, is_device_owned;
  int num_times_called = 0;

  void OnResult(bool profile_owned, bool device_owned) {
    is_profile_owned = profile_owned;
    is_device_owned = device_owned;
    num_times_called++;
  }
};

// Test that a callback can be queued and receives the correct results when
// serviced.
TEST_F(AndroidEnterpriseInfoTest, CallbackGetsResult) {
  EnterpriseInfoCallbackHelper helper;

  // Add a callback to the queue.
  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));

  bool profile_value = true;
  bool device_value = false;
  // Now pretend we got a result.
  instance_->ServiceCallbacksForTesting(profile_value, device_value);

  ASSERT_TRUE(helper.is_profile_owned.has_value());
  ASSERT_TRUE(helper.is_device_owned.has_value());
  EXPECT_EQ(helper.is_profile_owned, profile_value);
  EXPECT_EQ(helper.is_device_owned, device_value);
}

// Test that multiple queued callbacks are all serviced.
TEST_F(AndroidEnterpriseInfoTest, MultipleCallbacksServiced) {
  EnterpriseInfoCallbackHelper helper;

  const int kCOUNT = 6;
  for (int i = 0; i < kCOUNT; i++) {
    instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
        &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));
  }

  // Nothing should get called until the callbacks are serviced.
  EXPECT_EQ(helper.num_times_called, 0);
  // Now pretend we got a result. Value is irrelevant in this test.
  instance_->ServiceCallbacksForTesting(false, false);

  EXPECT_EQ(helper.num_times_called, kCOUNT);
}

// Test that callbacks are still serviced after prior callbacks have already
// been serviced.
TEST_F(AndroidEnterpriseInfoTest, MultipleCallbacksNonconcurrentServiced) {
  EnterpriseInfoCallbackHelper prehelper;

  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&prehelper)));
  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&prehelper)));

  // Now pretend we got a result. Value is irrelevant in this test.
  instance_->ServiceCallbacksForTesting(false, false);

  // After servicing, load more callbacks.
  EnterpriseInfoCallbackHelper helper;

  const int kCOUNT = 4;
  for (int i = 0; i < kCOUNT; i++) {
    instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
        &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));
  }

  EXPECT_EQ(helper.num_times_called, 0);
  // Now pretend we got a result. Value is irrelevant in this test, but let's
  // change this one just to highlight that you can't assume it stays the same
  // between servicings.
  instance_->ServiceCallbacksForTesting(true, true);

  EXPECT_EQ(helper.num_times_called, kCOUNT);
}

// Test that multiple callbacks all receive the same result.
TEST_F(AndroidEnterpriseInfoTest, MultipleCallbacksSameResult) {
  EnterpriseInfoCallbackHelper helper1, helper2;

  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper1)));
  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper2)));

  bool profile_value = false;
  bool device_value = true;
  // Now pretend we got a result.
  instance_->ServiceCallbacksForTesting(profile_value, device_value);

  // If at least one optional has a value, then the other optional can only be
  // equivalent if it has the same value.
  ASSERT_TRUE(helper2.is_profile_owned.has_value());
  ASSERT_TRUE(helper2.is_device_owned.has_value());
  EXPECT_EQ(helper1.is_profile_owned, helper2.is_profile_owned);
  EXPECT_EQ(helper1.is_device_owned, helper2.is_device_owned);
}

// Test that a reentrant callback doesn't cause a loop.
TEST_F(AndroidEnterpriseInfoTest, ReentrantCallback) {
  EnterpriseInfoCallbackHelper helper;

  // Insert 4 callbacks into the instance, the 2nd callback will attempt to
  // insert another callback when it's serviced.
  enterprise_util::AndroidEnterpriseInfo::EnterpriseInfoCallback
      reentrant_callback =
          base::BindLambdaForTesting([&](bool one, bool two) -> void {
            // Do nothing with the arguments, just enter the function again.
            instance_->GetAndroidEnterpriseInfoState(
                base::BindOnce(&EnterpriseInfoCallbackHelper::OnResult,
                               base::Unretained(&helper)));
          });

  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));
  instance_->GetAndroidEnterpriseInfoState(std::move(reentrant_callback));
  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));
  instance_->GetAndroidEnterpriseInfoState(base::BindOnce(
      &EnterpriseInfoCallbackHelper::OnResult, base::Unretained(&helper)));

  // Nothing should get called until the callbacks are serviced.
  EXPECT_EQ(helper.num_times_called, 0);
  // Now pretend we got a result. Value is irrelevant in this test.
  instance_->ServiceCallbacksForTesting(false, false);

  // Only the first 3 |helper| calls should have been serviced at this point.
  // The last one should be waiting in the (new) callback queue.
  EXPECT_EQ(helper.num_times_called, 3);

  // Service callbacks again to get the fourth one.
  // Value is irrelevant, but mix it up anyway.
  instance_->ServiceCallbacksForTesting(true, false);
  EXPECT_EQ(helper.num_times_called, 4);
}