chromium/chrome/browser/ash/policy/reporting/metrics_reporting/network/https_latency_event_detector_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/policy/reporting/metrics_reporting/network/https_latency_event_detector.h"

#include <optional>
#include <string>
#include <vector>

#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "components/reporting/proto/synced/metric_data.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"

namespace reporting {
namespace {

// Network service constants.
constexpr char kNetworkName[] = "network_name";
constexpr char kServicePath[] = "/service/path";
constexpr char kDeviceName[] = "device_name";
constexpr char kDevicePath[] = "/device/path";
constexpr char kProfilePath[] = "/profile/path";
constexpr char kGuid[] = "guid";
constexpr char kUserHash[] = "user_hash";

void SetNetworkData(
    std::vector<std::string> connection_states,
    ::ash::NetworkHandlerTestHelper* network_handler_test_helper) {
  auto* const service_client = network_handler_test_helper->service_test();
  auto* const device_client = network_handler_test_helper->device_test();
  network_handler_test_helper->profile_test()->AddProfile(kProfilePath,
                                                          kUserHash);
  base::RunLoop().RunUntilIdle();
  network_handler_test_helper->service_test()->ClearServices();
  network_handler_test_helper->device_test()->ClearDevices();
  for (size_t i = 0; i < connection_states.size(); ++i) {
    std::string index_str = base::StrCat({"_", base::NumberToString(i)});
    const std::string device_path = base::StrCat({kDevicePath, index_str});
    const std::string device_name = base::StrCat({kDeviceName, index_str});
    const std::string service_path = base::StrCat({kServicePath, index_str});
    const std::string network_name = base::StrCat({kNetworkName, index_str});
    const std::string guid = base::StrCat({kGuid, index_str});
    device_client->AddDevice(device_path, shill::kTypeEthernet, device_name);
    base::RunLoop().RunUntilIdle();
    service_client->AddService(service_path, guid, network_name,
                               shill::kTypeEthernet, connection_states[i],
                               /*visible=*/true);
    service_client->SetServiceProperty(service_path, shill::kDeviceProperty,
                                       base::Value(device_path));
    service_client->SetServiceProperty(service_path, shill::kProfileProperty,
                                       base::Value(kProfilePath));
  }
  base::RunLoop().RunUntilIdle();
}

struct HttpsLatencyEventDetectorTestCase {
  std::string test_name;
  HttpsLatencyProblem problem;
};

class HttpsLatencyEventDetectorTest
    : public ::testing::TestWithParam<HttpsLatencyEventDetectorTestCase> {
 protected:
  base::test::TaskEnvironment task_environment_;
  ::ash::NetworkHandlerTestHelper network_handler_test_helper_;
};

TEST_F(HttpsLatencyEventDetectorTest, NoEventDetected) {
  MetricData previous_metric_data;
  MetricData current_metric_data;

  HttpsLatencyEventDetector detector;

  auto event_type =
      detector.DetectEvent(previous_metric_data, current_metric_data);

  // No latency data in both current and previous collected data.
  EXPECT_FALSE(event_type.has_value());

  auto* const current_latency_data =
      current_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  current_latency_data->set_verdict(RoutineVerdict::NO_PROBLEM);

  event_type = detector.DetectEvent(std::nullopt, current_metric_data);

  // No previously collected data and no problems found in current latency data.
  EXPECT_FALSE(event_type.has_value());

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // No latency data in previous collected data and no problems found in current
  // latency data.
  EXPECT_FALSE(event_type.has_value());

  auto* const previous_latency_data =
      previous_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  previous_latency_data->set_verdict(RoutineVerdict::NO_PROBLEM);
  current_latency_data->set_verdict(RoutineVerdict::NO_PROBLEM);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // No problem found in both previous and current latency data.
  EXPECT_FALSE(event_type.has_value());

  previous_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  previous_latency_data->set_problem(HttpsLatencyProblem::HIGH_LATENCY);
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  current_latency_data->set_problem(HttpsLatencyProblem::HIGH_LATENCY);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // Same problem found in both previous and current latency data.
  EXPECT_FALSE(event_type.has_value());
}

TEST_F(HttpsLatencyEventDetectorTest, EventDetected) {
  MetricEventType expected_event_type =
      MetricEventType::NETWORK_HTTPS_LATENCY_CHANGE;
  MetricData previous_metric_data;
  MetricData current_metric_data;

  HttpsLatencyEventDetector detector;
  auto* const current_latency_data =
      current_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);

  auto event_type = detector.DetectEvent(std::nullopt, current_metric_data);

  // No data previously collected, and current collected data has problem.
  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), expected_event_type);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // No latency data in previous collected data, and current collected data has
  // problem.
  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), expected_event_type);

  auto* const previous_latency_data =
      previous_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  previous_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  current_latency_data->set_verdict(RoutineVerdict::NO_PROBLEM);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // Problem found in previous latency data and no problem found in current
  // latency data.
  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), expected_event_type);

  previous_latency_data->set_verdict(RoutineVerdict::NO_PROBLEM);
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // No problem found in previous latency data and problem found in current
  // latency data.
  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), expected_event_type);

  previous_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  previous_latency_data->set_problem(HttpsLatencyProblem::HIGH_LATENCY);
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  current_latency_data->set_problem(HttpsLatencyProblem::VERY_HIGH_LATENCY);

  event_type = detector.DetectEvent(previous_metric_data, current_metric_data);

  // Previous and current latency data have different problems.
  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), expected_event_type);
}

TEST_P(HttpsLatencyEventDetectorTest, RequestError_Offline) {
  MetricData previous_metric_data;
  MetricData current_metric_data;

  auto* const current_latency_data =
      current_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  current_latency_data->set_problem(GetParam().problem);

  HttpsLatencyEventDetector detector;

  SetNetworkData({shill::kStateIdle, shill::kStateConfiguration},
                 &network_handler_test_helper_);
  auto event_type =
      detector.DetectEvent(previous_metric_data, current_metric_data);

  // No latency data since the request problem is because that the device is not
  // online.
  EXPECT_FALSE(event_type.has_value());
}

TEST_P(HttpsLatencyEventDetectorTest, RequestError_Online) {
  MetricData previous_metric_data;
  MetricData current_metric_data;

  auto* const current_latency_data =
      current_metric_data.mutable_telemetry_data()
          ->mutable_networks_telemetry()
          ->mutable_https_latency_data();
  current_latency_data->set_verdict(RoutineVerdict::PROBLEM);
  current_latency_data->set_problem(GetParam().problem);

  HttpsLatencyEventDetector detector;

  SetNetworkData({shill::kStateOnline, shill::kStateAssociation},
                 &network_handler_test_helper_);
  auto event_type =
      detector.DetectEvent(previous_metric_data, current_metric_data);

  ASSERT_TRUE(event_type.has_value());
  EXPECT_EQ(event_type.value(), MetricEventType::NETWORK_HTTPS_LATENCY_CHANGE);
}

INSTANTIATE_TEST_SUITE_P(
    HttpsLatencyEventDetectorTests,
    HttpsLatencyEventDetectorTest,
    ::testing::ValuesIn<HttpsLatencyEventDetectorTestCase>(
        {{"FailedDnsResolutions", HttpsLatencyProblem::FAILED_DNS_RESOLUTIONS},
         {"FailedHttpsRequests", HttpsLatencyProblem::FAILED_HTTPS_REQUESTS}}),
    [](const testing::TestParamInfo<HttpsLatencyEventDetectorTest::ParamType>&
           info) { return info.param.test_name; });
}  // namespace
}  // namespace reporting