Add walrus provider to manta
Bug: b:370476808
Test: autoninja -C out_volteer/Release chrome
Change-Id: Iadc5c92a1724ead33c1f6716ec017c59c0061262
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5898137
Reviewed-by: Chris Mullins <[email protected]>
Reviewed-by: Tony Yeoman <[email protected]>
Commit-Queue: MD Nayeem Jahan Rafi <[email protected]>
Reviewed-by: Xinglong Luan <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1364724}
diff --git a/components/manta/BUILD.gn b/components/manta/BUILD.gn
index 1d09cc5f..131c008 100644
--- a/components/manta/BUILD.gn
+++ b/components/manta/BUILD.gn
@@ -56,6 +56,8 @@
"sparky/sparky_util.h",
"sparky/system_info_delegate.cc",
"sparky/system_info_delegate.h",
+ "walrus_provider.cc",
+ "walrus_provider.h",
]
deps += [ "//chromeos/constants:constants" ]
}
@@ -75,6 +77,7 @@
"snapper_provider_unittest.cc",
"sparky/sparky_provider_unittest.cc",
"sparky/sparky_util_unittest.cc",
+ "walrus_provider_unittest.cc",
]
}
diff --git a/components/manta/features.cc b/components/manta/features.cc
index a2e2b92f..c54bcdf 100644
--- a/components/manta/features.cc
+++ b/components/manta/features.cc
@@ -25,6 +25,11 @@
"MahiUseProdServer",
base::FEATURE_ENABLED_BY_DEFAULT);
+// Enables Walrus Prod Server
+BASE_FEATURE(kWalrusUseProdServer,
+ "WalrusUseProdServer",
+ base::FEATURE_ENABLED_BY_DEFAULT);
+
bool IsMantaServiceEnabled() {
return base::FeatureList::IsEnabled(kMantaService);
}
@@ -41,4 +46,8 @@
return base::FeatureList::IsEnabled(kMahiUseProdServer);
}
+bool IsWalrusUseProdServerEnabled() {
+ return base::FeatureList::IsEnabled(kWalrusUseProdServer);
+}
+
} // namespace manta::features
diff --git a/components/manta/features.h b/components/manta/features.h
index d104af8..701db835 100644
--- a/components/manta/features.h
+++ b/components/manta/features.h
@@ -18,6 +18,8 @@
COMPONENT_EXPORT(MANTA) BASE_DECLARE_FEATURE(kSeaPenUseProdServer);
+COMPONENT_EXPORT(MANTA) BASE_DECLARE_FEATURE(kWalrusUseProdServer);
+
COMPONENT_EXPORT(MANTA) bool IsMantaServiceEnabled();
COMPONENT_EXPORT(MANTA) bool IsOrcaUseProdServerEnabled();
@@ -26,6 +28,8 @@
COMPONENT_EXPORT(MANTA) bool IsMahiUseProdServerEnabled();
+COMPONENT_EXPORT(MANTA) bool IsWalrusUseProdServerEnabled();
+
} // namespace manta::features
#endif // COMPONENTS_MANTA_FEATURES_H_
diff --git a/components/manta/manta_service.cc b/components/manta/manta_service.cc
index 189f382c..e309afeb 100644
--- a/components/manta/manta_service.cc
+++ b/components/manta/manta_service.cc
@@ -25,6 +25,7 @@
#include "components/manta/orca_provider.h"
#include "components/manta/snapper_provider.h"
#include "components/manta/sparky/sparky_provider.h"
+#include "components/manta/walrus_provider.h"
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
namespace manta {
@@ -161,6 +162,16 @@
std::move(sparky_delegate), std::move(system_info_delegate));
}
+std::unique_ptr<WalrusProvider> MantaService::CreateWalrusProvider() {
+ if (!identity_manager_) {
+ return nullptr;
+ }
+ const ProviderParams provider_params = {/*use_api_key=*/is_demo_mode_,
+ chrome_version_, chrome_channel_};
+ return std::make_unique<WalrusProvider>(shared_url_loader_factory_,
+ identity_manager_, provider_params);
+}
+
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
void MantaService::Shutdown() {
diff --git a/components/manta/manta_service.h b/components/manta/manta_service.h
index 1887c89..413e0ac1 100644
--- a/components/manta/manta_service.h
+++ b/components/manta/manta_service.h
@@ -37,6 +37,7 @@
class OrcaProvider;
class SnapperProvider;
class SparkyProvider;
+class WalrusProvider;
// The MantaService class is a KeyedService for the Chrome/ChromeOS Manta
// project. It serves two main functions:
@@ -68,6 +69,7 @@
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Virtual for testing.
virtual std::unique_ptr<MahiProvider> CreateMahiProvider();
+ virtual std::unique_ptr<WalrusProvider> CreateWalrusProvider();
std::unique_ptr<OrcaProvider> CreateOrcaProvider();
virtual std::unique_ptr<SnapperProvider> CreateSnapperProvider();
diff --git a/components/manta/manta_service_callbacks.cc b/components/manta/manta_service_callbacks.cc
index 7b2ac71..be7cf64 100644
--- a/components/manta/manta_service_callbacks.cc
+++ b/components/manta/manta_service_callbacks.cc
@@ -88,6 +88,10 @@
base::UmaHistogramTimes("Ash.MantaService.AnchovyProvider.TimeCost",
time_cost);
break;
+ case MantaMetricType::kWalrus:
+ base::UmaHistogramTimes("Ash.MantaService.WalrusProvider.TimeCost",
+ time_cost);
+ break;
}
}
@@ -118,6 +122,10 @@
base::UmaHistogramEnumeration(
"Ash.MantaService.AnchovyProvider.StatusCode", status_code);
break;
+ case MantaMetricType::kWalrus:
+ base::UmaHistogramEnumeration(
+ "Ash.MantaService.WalrusProvider.StatusCode", status_code);
+ break;
}
}
} // namespace
diff --git a/components/manta/manta_service_callbacks.h b/components/manta/manta_service_callbacks.h
index 4fb1419..64f2c68b3 100644
--- a/components/manta/manta_service_callbacks.h
+++ b/components/manta/manta_service_callbacks.h
@@ -28,6 +28,7 @@
kMahiSummary,
kMahiQA,
kSparky,
+ kWalrus,
};
// Manta service uses this callback to return a Response proto parsed
diff --git a/components/manta/proto/manta.proto b/components/manta/proto/manta.proto
index 067302e..ad59963 100644
--- a/components/manta/proto/manta.proto
+++ b/components/manta/proto/manta.proto
@@ -22,6 +22,7 @@
CHROMEOS_SPARKY = 314;
CHROMEOS_LOBSTER = 315;
CHROMEOS_SCANNER = 316;
+ CHROMEOS_WALRUS = 318;
reserved 304 to 311;
ACCESSIBILITY_IMAGE_DESCRIPTION = 700;
diff --git a/components/manta/walrus_provider.cc b/components/manta/walrus_provider.cc
new file mode 100644
index 0000000..1ea36c0
--- /dev/null
+++ b/components/manta/walrus_provider.cc
@@ -0,0 +1,106 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/manta/walrus_provider.h"
+
+#include "base/strings/stringprintf.h"
+#include "components/manta/features.h"
+
+namespace manta {
+
+namespace {
+
+constexpr char kOauthConsumerName[] = "manta_walrus";
+constexpr base::TimeDelta kTimeout = base::Seconds(30);
+
+void OnServerResponseOrErrorReceived(
+ MantaGenericCallback callback,
+ std::unique_ptr<proto::Response> manta_response,
+ MantaStatus manta_status) {
+ if (manta_response == nullptr || !manta_response->filtered_data_size()) {
+ // Return the status if the text/images are not blocked.
+ std::move(callback).Run(base::Value::Dict(), std::move(manta_status));
+ return;
+ }
+
+ CHECK(manta_response != nullptr);
+
+ // Add extra information for the invalid inputs.
+ auto output_data = base::Value::Dict();
+ for (const auto& filtered_data : manta_response->filtered_data()) {
+ auto filtered_reason = filtered_data.reason();
+ switch (filtered_reason) {
+ case manta::proto::FilteredReason::IMAGE_SAFETY:
+ output_data.Set("image_blocked", true);
+ break;
+ case manta::proto::FilteredReason::TEXT_SAFETY:
+ output_data.Set("text_blocked", true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ std::move(callback).Run(std::move(output_data),
+ {manta::MantaStatusCode::kBlockedOutputs});
+}
+
+} // namespace
+
+WalrusProvider::WalrusProvider(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ signin::IdentityManager* identity_manager,
+ const ProviderParams& provider_params)
+ : BaseProvider(url_loader_factory, identity_manager, provider_params) {}
+
+WalrusProvider::WalrusProvider(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ signin::IdentityManager* identity_manager)
+ : BaseProvider(url_loader_factory, identity_manager) {}
+
+WalrusProvider::~WalrusProvider() = default;
+
+void WalrusProvider::Filter(std::string text_prompt,
+ MantaGenericCallback done_callback) {
+ std::vector<std::vector<uint8_t>> empty_images;
+ Filter(text_prompt, empty_images, std::move(done_callback));
+}
+
+void WalrusProvider::Filter(const std::optional<std::string>& text_prompt,
+ const std::vector<std::vector<uint8_t>>& images,
+ MantaGenericCallback done_callback) {
+ proto::Request request;
+ request.set_feature_name(proto::FeatureName::CHROMEOS_WALRUS);
+
+ if (text_prompt.has_value() && !text_prompt->empty()) {
+ auto* input_data = request.add_input_data();
+ input_data->set_tag("input_text");
+ input_data->set_text(text_prompt.value());
+ }
+
+ for (auto& image : images) {
+ auto* input_data = request.add_input_data();
+ input_data->set_tag("input_image");
+ input_data->mutable_image()->set_serialized_bytes(
+ std::string(image.begin(), image.end()));
+ }
+
+ if (!request.input_data_size()) {
+ std::move(done_callback)
+ .Run(base::Value::Dict(), {MantaStatusCode::kInvalidInput});
+ return;
+ }
+
+ // TODO(b:370476808): MISSING_TRAFFIC_ANNOTATION should be resolved before
+ // launch.
+ RequestInternal(
+ GURL{GetProviderEndpoint(features::IsWalrusUseProdServerEnabled())},
+ kOauthConsumerName, MISSING_TRAFFIC_ANNOTATION, request,
+ MantaMetricType::kWalrus,
+ base::BindOnce(&OnServerResponseOrErrorReceived,
+ std::move(done_callback)),
+ kTimeout);
+}
+
+} // namespace manta
diff --git a/components/manta/walrus_provider.h b/components/manta/walrus_provider.h
new file mode 100644
index 0000000..aacf803
--- /dev/null
+++ b/components/manta/walrus_provider.h
@@ -0,0 +1,72 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_MANTA_WALRUS_PROVIDER_H_
+#define COMPONENTS_MANTA_WALRUS_PROVIDER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "components/endpoint_fetcher/endpoint_fetcher.h"
+#include "components/manta/base_provider.h"
+#include "components/manta/manta_service_callbacks.h"
+#include "components/manta/provider_params.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+namespace manta {
+
+// The Walrus provider for the Manta project. Provides a method for clients to
+// call the relevant google API, handling OAuth and http fetching.
+// IMPORTANT: This class depends on `IdentityManager`.
+// `WalrusProvider::Filter` will return an empty response after
+// `IdentityManager` destruction.
+class COMPONENT_EXPORT(MANTA) WalrusProvider : virtual public BaseProvider {
+ public:
+ // Returns a `WalrusProvider` instance tied to the profile of the passed
+ // arguments.
+ WalrusProvider(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ signin::IdentityManager* identity_manager,
+ const ProviderParams& provider_params);
+
+ WalrusProvider(const WalrusProvider&) = delete;
+ WalrusProvider& operator=(const WalrusProvider&) = delete;
+
+ ~WalrusProvider() override;
+
+ // Filters the given `text_prompt` and `images` by calling the google service
+ // endpoint with the http POST request payload populated with the `input`. The
+ // fetched response is processed and returned to the caller via an
+ // `MantaGenericCallback` callback.
+ // Will give an empty response if `IdentityManager` is no longer valid.
+ virtual void Filter(const std::optional<std::string>& text_prompt,
+ const std::vector<std::vector<uint8_t>>& images,
+ MantaGenericCallback done_callback);
+
+ // Filters the given `text_prompt`.
+ virtual void Filter(const std::string text_prompt,
+ MantaGenericCallback done_callback);
+
+ protected:
+ WalrusProvider(
+ scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+ signin::IdentityManager* identity_manager);
+
+ private:
+ friend class FakeWalrusProvider;
+
+ base::WeakPtrFactory<WalrusProvider> weak_ptr_factory_{this};
+};
+
+} // namespace manta
+
+#endif // COMPONENTS_MANTA_WALRUS_PROVIDER_H_
diff --git a/components/manta/walrus_provider_unittest.cc b/components/manta/walrus_provider_unittest.cc
new file mode 100644
index 0000000..cc5e25b
--- /dev/null
+++ b/components/manta/walrus_provider_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/manta/walrus_provider.h"
+
+#include <memory>
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/time/time.h"
+#include "components/manta/base_provider.h"
+#include "components/manta/base_provider_test_helper.h"
+#include "components/manta/manta_status.h"
+#include "components/manta/proto/manta.pb.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace manta {
+
+namespace {
+constexpr char kMockEndpoint[] = "https://my-endpoint.com";
+}
+
+class FakeWalrusProvider : public WalrusProvider, public FakeBaseProvider {
+ public:
+ FakeWalrusProvider(
+ scoped_refptr<network::SharedURLLoaderFactory> test_url_loader_factory,
+ signin::IdentityManager* identity_manager)
+ : BaseProvider(test_url_loader_factory, identity_manager),
+ WalrusProvider(test_url_loader_factory,
+ identity_manager,
+ ProviderParams()),
+ FakeBaseProvider(test_url_loader_factory, identity_manager) {}
+};
+
+class WalrusProviderTest : public BaseProviderTest {
+ public:
+ WalrusProviderTest() = default;
+
+ WalrusProviderTest(const WalrusProviderTest&) = delete;
+ WalrusProviderTest& operator=(const WalrusProviderTest&) = delete;
+
+ ~WalrusProviderTest() override = default;
+
+ std::unique_ptr<FakeWalrusProvider> CreateWalrusProvider() {
+ return std::make_unique<FakeWalrusProvider>(
+ base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+ &test_url_loader_factory_),
+ identity_test_env_->identity_manager());
+ }
+};
+
+// Test that responses with http_status_code != net::HTTP_OK are captured.
+TEST_F(WalrusProviderTest, CaptureUnexcpetedStatusCode) {
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+ net::HTTP_BAD_REQUEST, net::OK);
+ std::optional<std::string> text_prompt = "text pompt";
+ std::vector<std::vector<uint8_t>> images;
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting(
+ [quit_closure = task_environment_.QuitClosure()](
+ base::Value::Dict response, MantaStatus manta_status) {
+ EXPECT_EQ(manta_status.status_code,
+ MantaStatusCode::kBackendFailure);
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+}
+
+// Test that responses with network errors are captured.
+TEST_F(WalrusProviderTest, CaptureNetError) {
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+ net::HTTP_OK, net::ERR_FAILED);
+ std::optional<std::string> text_prompt = "text pompt";
+ std::vector<std::vector<uint8_t>> images;
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting(
+ [quit_closure = task_environment_.QuitClosure()](
+ base::Value::Dict response, MantaStatus manta_status) {
+ EXPECT_EQ(manta_status.status_code,
+ MantaStatusCode::kNoInternetConnection);
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+}
+
+// Test Manta Provider rejects invalid input data. Currently we require the
+// input must contain a valid text prompt or an image.
+TEST_F(WalrusProviderTest, InvalidInput) {
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, /*response_data=*/"",
+ net::HTTP_OK, net::OK);
+ std::optional<std::string> text_prompt;
+ std::vector<std::vector<uint8_t>> images;
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting(
+ [quit_closure = task_environment_.QuitClosure()](
+ base::Value::Dict response, MantaStatus manta_status) {
+ EXPECT_EQ(manta_status.status_code, MantaStatusCode::kInvalidInput);
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+}
+
+// Test the response when the text prompt / image is safe.
+TEST_F(WalrusProviderTest, SuccessfulResponse) {
+ std::string image_bytes = "image_bytes";
+ base::HistogramTester histogram_tester;
+ manta::proto::Response response;
+ auto* output_data = response.add_output_data();
+ output_data->set_text("text pompt");
+ output_data = response.add_output_data();
+ output_data->mutable_image()->set_serialized_bytes(image_bytes);
+
+ std::string response_data;
+ response.SerializeToString(&response_data);
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+ net::OK);
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+ auto quit_closure = task_environment_.QuitClosure();
+ std::optional<std::string> text_prompt = "text pompt";
+ std::vector<std::vector<uint8_t>> images = {
+ std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+ MantaStatus manta_status) {
+ // Even though the response has text and image, walrus just
+ // returns the status code
+ ASSERT_EQ(MantaStatusCode::kOk, manta_status.status_code);
+ ASSERT_TRUE(response.empty());
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+
+ // Metric is logged when response is successfully parsed.
+ histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+ 1);
+}
+
+// Test the response when the text prompt is blocked.
+TEST_F(WalrusProviderTest, TextBlocked) {
+ std::string image_bytes = "image_bytes";
+ base::HistogramTester histogram_tester;
+ manta::proto::Response response;
+ manta::proto::FilteredData& filtered_data = *response.add_filtered_data();
+ filtered_data.set_reason(manta::proto::FilteredReason::TEXT_SAFETY);
+ std::string response_data;
+ response.SerializeToString(&response_data);
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+ net::OK);
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+ auto quit_closure = task_environment_.QuitClosure();
+ std::optional<std::string> text_prompt = "text pompt";
+ std::vector<std::vector<uint8_t>> images = {
+ std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+ MantaStatus manta_status) {
+ // Even though the response has text and image, walrus just
+ // returns the status code.
+ ASSERT_EQ(MantaStatusCode::kBlockedOutputs, manta_status.status_code);
+ ASSERT_EQ(response.size(), 1u);
+ ASSERT_TRUE(response.FindBool("text_blocked"));
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+
+ // Metric is logged when response is successfully parsed.
+ histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+ 1);
+}
+
+// Test the response when the text prompt and images is blocked.
+TEST_F(WalrusProviderTest, TextImageBothBlocked) {
+ std::string image_bytes = "image_bytes";
+ base::HistogramTester histogram_tester;
+ manta::proto::Response response;
+ auto* filtered_data = response.add_filtered_data();
+ filtered_data->set_reason(manta::proto::FilteredReason::TEXT_SAFETY);
+ filtered_data = response.add_filtered_data();
+ filtered_data->set_reason(manta::proto::FilteredReason::IMAGE_SAFETY);
+ filtered_data = response.add_filtered_data();
+ filtered_data->set_reason(manta::proto::FilteredReason::IMAGE_SAFETY);
+ std::string response_data;
+ response.SerializeToString(&response_data);
+
+ SetEndpointMockResponse(GURL{kMockEndpoint}, response_data, net::HTTP_OK,
+ net::OK);
+ std::unique_ptr<FakeWalrusProvider> walrus_provider = CreateWalrusProvider();
+ auto quit_closure = task_environment_.QuitClosure();
+ std::optional<std::string> text_prompt = "text pompt";
+ std::vector<std::vector<uint8_t>> images = {
+ std::vector<uint8_t>(image_bytes.begin(), image_bytes.end()),
+ std::vector<uint8_t>(image_bytes.begin(), image_bytes.end())};
+
+ walrus_provider->Filter(
+ text_prompt, images,
+ base::BindLambdaForTesting([&quit_closure](base::Value::Dict response,
+ MantaStatus manta_status) {
+ // Even though the response has text and image, walrus just
+ // returns the status code
+ ASSERT_EQ(MantaStatusCode::kBlockedOutputs, manta_status.status_code);
+ ASSERT_EQ(response.size(), 2u);
+ ASSERT_TRUE(response.FindBool("text_blocked"));
+ ASSERT_TRUE(response.FindBool("image_blocked"));
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+
+ // Metric is logged when response is successfully parsed.
+ histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+ 1);
+}
+
+TEST_F(WalrusProviderTest, EmptyResponseAfterIdentityManagerShutdown) {
+ base::HistogramTester histogram_tester;
+ std::unique_ptr<FakeWalrusProvider> wallrus_provider = CreateWalrusProvider();
+
+ identity_test_env_.reset();
+
+ std::string text_prompt = "text pompt";
+ wallrus_provider->Filter(
+ text_prompt, base::BindLambdaForTesting(
+ [quit_closure = task_environment_.QuitClosure()](
+ base::Value::Dict dict, MantaStatus manta_status) {
+ ASSERT_TRUE(dict.empty());
+ ASSERT_EQ(MantaStatusCode::kNoIdentityManager,
+ manta_status.status_code);
+ quit_closure.Run();
+ }));
+ task_environment_.RunUntilQuit();
+
+ // No metric logged.
+ histogram_tester.ExpectTotalCount("Ash.MantaService.WalrusProvider.TimeCost",
+ 0);
+}
+
+} // namespace manta