Move the Browsing Topics Annotator fuzz test
Moves the fuzz test into the browsing_topics component.
Verified this is fully working locally.
Bug: b/278162907
Change-Id: I4c26d117436316939145cc3c136f7a51f1df727a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4495631
Reviewed-by: Yao Xiao <[email protected]>
Commit-Queue: Robert Ogden <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1139126}
diff --git a/components/browsing_topics/BUILD.gn b/components/browsing_topics/BUILD.gn
index 8ea72a76..78f94e31a 100644
--- a/components/browsing_topics/BUILD.gn
+++ b/components/browsing_topics/BUILD.gn
@@ -1,6 +1,7 @@
# 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.
+import("//testing/libfuzzer/fuzzer_test.gni")
source_set("browsing_topics") {
sources = [
@@ -106,3 +107,16 @@
"//third_party/zlib/google:compression_utils",
]
}
+
+fuzzer_test("browsing_topics_annotator_fuzzer") {
+ sources = [ "annotator_fuzzer.cc" ]
+ deps = [
+ ":browsing_topics",
+ "//base:base",
+ "//base/test:test_support",
+ "//components/optimization_guide/core:core",
+ "//components/optimization_guide/core:features",
+ "//components/optimization_guide/core:test_support",
+ "//components/optimization_guide/proto:optimization_guide_proto",
+ ]
+}
diff --git a/components/browsing_topics/annotator_fuzzer.cc b/components/browsing_topics/annotator_fuzzer.cc
new file mode 100644
index 0000000..45e13e9
--- /dev/null
+++ b/components/browsing_topics/annotator_fuzzer.cc
@@ -0,0 +1,146 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "base/files/file_util.h"
+#include "base/path_service.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/task/thread_pool.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/browsing_topics/annotator_impl.h"
+#include "components/optimization_guide/core/optimization_guide_features.h"
+#include "components/optimization_guide/core/optimization_guide_model_provider.h"
+#include "components/optimization_guide/core/test_model_info_builder.h"
+#include "components/optimization_guide/core/test_optimization_guide_model_provider.h"
+#include "components/optimization_guide/proto/models.pb.h"
+#include "components/optimization_guide/proto/page_topics_model_metadata.pb.h"
+
+// Sends a fully working model and configuration to the calling observer.
+class ModelProvider
+ : public optimization_guide::TestOptimizationGuideModelProvider {
+ public:
+ ModelProvider() = default;
+ ~ModelProvider() override = default;
+
+ // optimization_guide::TestOptimizationGuideModelProvider:
+ void AddObserverForOptimizationTargetModel(
+ optimization_guide::proto::OptimizationTarget optimization_target,
+ const absl::optional<optimization_guide::proto::Any>& model_metadata,
+ optimization_guide::OptimizationTargetModelObserver* observer) override {
+ optimization_guide::proto::Any any_metadata;
+ any_metadata.set_type_url(
+ "type.googleapis.com/com.foo.PageTopicsModelMetadata");
+ optimization_guide::proto::PageTopicsModelMetadata
+ page_topics_model_metadata;
+ page_topics_model_metadata.set_version(123);
+ page_topics_model_metadata.add_supported_output(
+ optimization_guide::proto::PAGE_TOPICS_SUPPORTED_OUTPUT_CATEGORIES);
+ auto* output_params =
+ page_topics_model_metadata.mutable_output_postprocessing_params();
+ auto* category_params = output_params->mutable_category_params();
+ category_params->set_max_categories(5);
+ category_params->set_min_none_weight(0.8);
+ category_params->set_min_category_weight(0.1);
+ category_params->set_min_normalized_weight_within_top_n(0.1);
+ page_topics_model_metadata.SerializeToString(any_metadata.mutable_value());
+
+ base::FilePath source_root_dir;
+ base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
+ base::FilePath model_file_path =
+ source_root_dir.AppendASCII("components")
+ .AppendASCII("test")
+ .AppendASCII("data")
+ .AppendASCII("browsing_topics")
+ .AppendASCII("golden_data_model.tflite");
+ std::unique_ptr<optimization_guide::ModelInfo> model_info =
+ optimization_guide::TestModelInfoBuilder()
+ .SetModelFilePath(model_file_path)
+ .SetModelMetadata(any_metadata)
+ .Build();
+
+ observer->OnModelUpdated(
+ optimization_guide::proto::OPTIMIZATION_TARGET_PAGE_TOPICS_V2,
+ *model_info);
+ }
+};
+
+// An AnnotatorImpl that never unloads the model, thus keeping the executions
+// per second high.
+class TestAnnotator : public browsing_topics::AnnotatorImpl {
+ public:
+ TestAnnotator(
+ optimization_guide::OptimizationGuideModelProvider* model_provider,
+ scoped_refptr<base::SequencedTaskRunner> background_task_runner,
+ const absl::optional<optimization_guide::proto::Any>& model_metadata)
+ : browsing_topics::AnnotatorImpl(model_provider,
+ std::move(background_task_runner),
+ model_metadata) {}
+
+ protected:
+ // AnnotatorImpl:
+ void UnloadModel() override {
+ // Do nothing so that the model stays loaded.
+ }
+};
+
+// Static test fixture to maintain the state needed to run repeated fuzz tests.
+class AnnotatorFuzzerTest {
+ public:
+ explicit AnnotatorFuzzerTest()
+ : annotator_(std::make_unique<TestAnnotator>(
+ &model_provider_,
+ task_environment_.GetMainThreadTaskRunner(),
+ absl::nullopt)) {
+ scoped_feature_list_.InitAndDisableFeature(
+ optimization_guide::features::kPreventLongRunningPredictionModels);
+ }
+ ~AnnotatorFuzzerTest() { task_environment_.RunUntilIdle(); }
+
+ browsing_topics::Annotator* annotator() { return annotator_.get(); }
+
+ void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
+ private:
+ base::test::TaskEnvironment task_environment_;
+ base::test::ScopedFeatureList scoped_feature_list_;
+ ModelProvider model_provider_;
+ std::unique_ptr<browsing_topics::AnnotatorImpl> annotator_;
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static AnnotatorFuzzerTest test;
+ static bool had_successful_run = false;
+
+ std::string input(reinterpret_cast<const char*>(data), size);
+
+ base::RunLoop run_loop;
+ test.annotator()->BatchAnnotate(
+ base::BindOnce(
+ [](base::RunLoop* run_loop,
+ const std::vector<browsing_topics::Annotation>& annotations) {
+ if (!annotations[0].topics.empty() && !had_successful_run) {
+ had_successful_run = true;
+ // Print a single debug message so that its obvious things are
+ // working (or able to at least once) when running locally.
+ LOG(INFO) << "Congrats! Got a successful model execution. This "
+ "message will not be printed again.";
+ }
+
+ run_loop->Quit();
+ },
+ &run_loop),
+ {input});
+ run_loop.Run();
+
+ // The model executor does some PostTask'ing to manage its state. While these
+ // tasks are not important for fuzzing, we don't want to queue up a ton of
+ // them.
+ test.RunUntilIdle();
+
+ return 0;
+}
diff --git a/components/browsing_topics/annotator_impl.h b/components/browsing_topics/annotator_impl.h
index d958528..87d63b4 100644
--- a/components/browsing_topics/annotator_impl.h
+++ b/components/browsing_topics/annotator_impl.h
@@ -77,6 +77,10 @@
absl::optional<std::vector<int32_t>> ExtractCategoriesFromModelOutput(
const std::vector<tflite::task::core::Category>& model_output) const;
+ protected:
+ // optimization_guide::BertModelHandler:
+ void UnloadModel() override;
+
private:
// Sets the |override_list_| after it was loaded on a background thread and
// calls |StartBatchAnnotate|.
@@ -104,9 +108,6 @@
BatchAnnotationCallback callback,
std::unique_ptr<std::vector<Annotation>> annotations_ptr);
- // optimization_guide::BertModelHandler:
- void UnloadModel() override;
-
// Sets |annotation.topics| from the output of the model, calling
// |ExtractCategoriesFromModelOutput| in the process.
void PostprocessCategoriesToBatchAnnotationResult(
diff --git a/components/optimization_guide/core/BUILD.gn b/components/optimization_guide/core/BUILD.gn
index 494611d..a2d21f6 100644
--- a/components/optimization_guide/core/BUILD.gn
+++ b/components/optimization_guide/core/BUILD.gn
@@ -488,32 +488,6 @@
}
if (build_with_tflite_lib) {
- template("bert_model_fuzzer") {
- forward_variables_from(invoker, "*")
-
- fuzzer_test("optimization_guide_${target_name}_fuzzer") {
- sources = [ "bert_model_fuzzer.cc" ]
- defines = [
- "OPTIMIZATION_TARGET=${optimization_target}",
- "MODEL_FILE_BASENAME=\"${model_file_basename}\"",
- ]
- deps = [
- ":core",
- ":features",
- ":machine_learning",
- ":model_executor",
- "//base:base",
- "//base/test:test_support",
- "//components/optimization_guide/proto:optimization_guide_proto",
- ]
- }
- }
-
- bert_model_fuzzer("page_topics") {
- optimization_target = "OPTIMIZATION_TARGET_PAGE_TOPICS_V2"
- model_file_basename = "bert_page_topics_model.tflite"
- }
-
fuzzer_test("optimization_guide_page_visibility_model_fuzzer") {
sources = [ "page_visibility_model_fuzzer.cc" ]
deps = [
diff --git a/components/optimization_guide/core/bert_model_fuzzer.cc b/components/optimization_guide/core/bert_model_fuzzer.cc
deleted file mode 100644
index 7aa32ef..0000000
--- a/components/optimization_guide/core/bert_model_fuzzer.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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 <string>
-
-#include <fuzzer/FuzzedDataProvider.h>
-
-#include "base/path_service.h"
-#include "base/test/task_environment.h"
-#include "components/optimization_guide/core/bert_model_executor.h"
-
-// This fuzzer relies on two macros so that the test code can be easily reused
-// across multiple model inputs. Make sure these are defined by the build rule.
-//
-// OPTIMIZATION_TARGET should be something like
-// OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD.
-//
-// MODEL_FILE_BASENAME should be something like "model.tflite", quotes included!
-// Given model file base names use the OptGuide test data dir in //components.
-class BertModelExecutorFuzzer {
- public:
- BertModelExecutorFuzzer()
- : model_executor_(optimization_guide::BertModelExecutor(
- optimization_guide::proto::OPTIMIZATION_TARGET)) {
- model_executor_.InitializeAndMoveToExecutionThread(
- // This is an arbitrarily long time since we don't need to test the
- // timeout behavior here, libfuzzer will take care of hangs.
- /*model_inference_timeout=*/base::Minutes(60),
- optimization_guide::proto::OPTIMIZATION_TARGET,
- /*execution_task_runner=*/task_environment_.GetMainThreadTaskRunner(),
- /*reply_task_runner=*/task_environment_.GetMainThreadTaskRunner());
-
- base::FilePath source_root_dir;
- base::PathService::Get(base::DIR_SOURCE_ROOT, &source_root_dir);
- base::FilePath model_file_path = source_root_dir.AppendASCII("components")
- .AppendASCII("test")
- .AppendASCII("data")
- .AppendASCII("optimization_guide")
- .AppendASCII(MODEL_FILE_BASENAME);
-
- model_executor_.UpdateModelFile(model_file_path);
- model_executor_.SetShouldUnloadModelOnComplete(false);
- }
- ~BertModelExecutorFuzzer() { task_environment_.RunUntilIdle(); }
-
- optimization_guide::BertModelExecutor* executor() { return &model_executor_; }
-
- void RunUntilIdle() { task_environment_.RunUntilIdle(); }
-
- private:
- base::test::TaskEnvironment task_environment_;
- optimization_guide::BertModelExecutor model_executor_;
-};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- static BertModelExecutorFuzzer test;
- static bool had_successful_run = false;
-
- std::string input(reinterpret_cast<const char*>(data), size);
- test.executor()->SendForExecution(
- base::BindOnce(
- [](const absl::optional<std::vector<tflite::task::core::Category>>&
- output) {
- if (output && !had_successful_run) {
- had_successful_run = true;
- // Print a single debug message so that its obvious things are
- // working (or able to at least once) when running locally.
- LOG(INFO) << "Congrats! Got a successful model execution. This "
- "message will not be printed again.";
- }
- }),
- base::TimeTicks(), input);
-
- // The model executor does some PostTask'ing to manage its state. While these
- // tasks are not important for fuzzing, we don't want to queue up a ton of
- // them.
- test.RunUntilIdle();
-
- return 0;
-}
\ No newline at end of file