chromium/chrome/browser/ash/dbus/chrome_features_service_provider_unittest.cc

// Copyright 2022 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/ash/dbus/chrome_features_service_provider.h"

#include "base/feature_list.h"
#include "base/test/scoped_feature_list.h"
#include "dbus/message.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash {

namespace {
void ResponseSenderCallback(const std::string& expected_message,
                            std::unique_ptr<dbus::Response> response) {
  EXPECT_EQ(expected_message, response->ToString());
}
}  // namespace

class ChromeFeaturesServiceProviderTest : public testing::Test {
 protected:
  void GetFeatureParams(dbus::MethodCall* method_call, std::string expected) {
    provider_->GetFeatureParams(
        method_call, base::BindOnce(&ResponseSenderCallback, expected));
  }
  void IsFeatureEnabled(dbus::MethodCall* method_call, std::string expected) {
    provider_->IsFeatureEnabled(
        method_call, base::BindOnce(&ResponseSenderCallback, expected));
  }
  std::unique_ptr<ChromeFeaturesServiceProvider> provider_;
};

TEST_F(ChromeFeaturesServiceProviderTest, IsFeatureEnabled_Success) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  const char enabled[] = "CrOSLateBootA";
  const char disabled[] = "";
  feature_list->InitFromCommandLine(enabled, disabled);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  const char kExpectedMessage[] =
      R"--(message_type: MESSAGE_METHOD_RETURN
signature: b
reply_serial: 123

bool true
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  writer.AppendString("CrOSLateBootA");

  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  IsFeatureEnabled(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, IsFeatureEnabled_UnknownFeature) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  const char enabled[] = "CrOSLateBootA";
  const char disabled[] = "";
  feature_list->InitFromCommandLine(enabled, disabled);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  const char kExpectedMessage[] =
      R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Chrome can't get state for 'CrOSLateBootB'; feature_library will decide"
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  writer.AppendString("CrOSLateBootB");

  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  IsFeatureEnabled(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, IsFeatureEnabled_InvalidPrefix) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  const char enabled[] = "CrOSLateBootA";
  const char disabled[] = "";
  feature_list->InitFromCommandLine(enabled, disabled);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  const char kExpectedMessage[] =
      R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Invalid prefix for feature name: 'B'. Want CrOSLateBoot"
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  writer.AppendString("B");

  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  IsFeatureEnabled(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, IsFeatureEnabled_InvalidInput) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  const char enabled[] = "";
  const char disabled[] = "";
  feature_list->InitFromCommandLine(enabled, disabled);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  const char kExpectedMessage[] =
      R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Missing or invalid feature_name string arg."
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  writer.AppendBool(true);

  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  IsFeatureEnabled(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, GetFeatureParams_Success) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  const char enabled[] = "CrOSLateBootA:key1/value1/key2/value2,CrOSLateBootB";
  const char disabled[] = "CrOSLateBootC";
  feature_list->InitFromCommandLine(enabled, disabled);
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  const char kExpectedMessage[] =
      R"--(message_type: MESSAGE_METHOD_RETURN
signature: a{s(bba{ss})}
reply_serial: 123

array [
  dict entry {
    string "CrOSLateBootA"
    struct {
      bool true
      bool true
      array [
        dict entry {
          string "key1"
          string "value1"
        }
        dict entry {
          string "key2"
          string "value2"
        }
      ]
    }
  }
  dict entry {
    string "CrOSLateBootB"
    struct {
      bool true
      bool true
      array [
      ]
    }
  }
  dict entry {
    string "CrOSLateBootC"
    struct {
      bool true
      bool false
      array [
      ]
    }
  }
  dict entry {
    string "CrOSLateBootD"
    struct {
      bool false
      bool false
      array [
      ]
    }
  }
]
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  dbus::MessageWriter array_writer(nullptr);
  writer.OpenArray("s", &array_writer);
  array_writer.AppendString("CrOSLateBootA");
  array_writer.AppendString("CrOSLateBootB");
  array_writer.AppendString("CrOSLateBootC");
  array_writer.AppendString("CrOSLateBootD");
  writer.CloseContainer(&array_writer);

  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  GetFeatureParams(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, GetFeatureParams_NoInput) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  feature_list->InitFromCommandLine("", "");
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  constexpr char kExpectedMessage[] = R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Could not pop string array of feature names"
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  GetFeatureParams(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, GetFeatureParams_BadInput) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  feature_list->InitFromCommandLine("", "");
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  constexpr char kExpectedMessage[] = R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Could not pop string array of feature names"
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  writer.AppendString("CrOSLateBootA");  // not in an array!
  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  GetFeatureParams(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, GetFeatureParams_BadArrayEntry) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  feature_list->InitFromCommandLine("", "");
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  constexpr char kExpectedMessage[] = R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Missing or invalid feature_name string arg in array."
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  dbus::MessageWriter array_writer(nullptr);
  writer.OpenArray("b", &array_writer);
  array_writer.AppendBool(true);  // wrong type
  writer.CloseContainer(&array_writer);
  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  GetFeatureParams(&method_call, kExpectedMessage);
}

TEST_F(ChromeFeaturesServiceProviderTest, GetFeatureParams_BadNameFormat) {
  auto feature_list = std::make_unique<base::FeatureList>();
  auto feature_list_accessor = feature_list->ConstructAccessor();
  feature_list->InitFromCommandLine("", "");
  base::test::ScopedFeatureList scoped_feature_list;
  scoped_feature_list.InitWithFeatureList(std::move(feature_list));

  provider_ = std::make_unique<ChromeFeaturesServiceProvider>(
      std::move(feature_list_accessor));

  constexpr char kExpectedMessage[] = R"--(message_type: MESSAGE_ERROR
error_name: org.freedesktop.DBus.Error.InvalidArgs
signature: s
reply_serial: 123

string "Invalid prefix for feature name: 'B'. Want CrOSLateBoot"
)--";

  dbus::MethodCall method_call("com.example.Interface", "SomeMethod");
  dbus::MessageWriter writer(&method_call);
  dbus::MessageWriter array_writer(nullptr);
  writer.OpenArray("s", &array_writer);
  array_writer.AppendString("CrOSLateBootA");
  array_writer.AppendString("B");  // missing prefix!
  writer.CloseContainer(&array_writer);
  // Not setting the serial causes a crash.
  method_call.SetSerial(123);
  GetFeatureParams(&method_call, kExpectedMessage);
}

}  // namespace ash