blob: 69c92026728800f2d8191b0b1d7f6fad9cdf3198 [file] [log] [blame]
// 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 <memory>
#include <optional>
#include "base/base_paths.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/language_detection/language_detection_model_service_factory.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/metrics/content/subprocess_metrics_provider.h"
#include "components/optimization_guide/core/delivery/model_util.h"
#include "components/optimization_guide/core/delivery/test_model_info_builder.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/translate/core/common/translate_util.h"
#include "components/translate/core/language_detection/language_detection_model.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace language_detection {
namespace {
// Fetch and calculate the total number of samples from all the bins for
// |histogram_name|. Note: from some browertests run (such as chromeos) there
// might be two profiles created, and this will return the total sample count
// across profiles.
int GetTotalHistogramSamples(const base::HistogramTester* histogram_tester,
const std::string& histogram_name) {
std::vector<base::Bucket> buckets =
histogram_tester->GetAllSamples(histogram_name);
int total = 0;
for (const auto& bucket : buckets) {
total += bucket.count;
}
return total;
}
// Handles HTTP requests to |path| with |content| as the response body.
// |content| is expected to be JavaScript; the response mime type is always set
// to "text/javascript".
// Invokes |done_callback| after serving the HTTP request.
std::unique_ptr<net::test_server::HttpResponse> RespondWithJS(
const std::string& path,
const std::string& content,
base::OnceClosure done_callback,
const net::test_server::HttpRequest& request) {
GURL request_url = request.GetURL();
if (request_url.path() != path) {
return nullptr;
}
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_content_type("text/javascript");
response->set_content(content);
std::move(done_callback).Run();
return response;
}
// Retries fetching |histogram_name| until it contains at least |count| samples.
int RetryForHistogramUntilCountReached(
const base::HistogramTester* histogram_tester,
const std::string& histogram_name,
int count) {
while (true) {
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
int total = GetTotalHistogramSamples(histogram_tester, histogram_name);
if (total >= count) {
return total;
}
content::FetchHistogramsFromChildProcesses();
metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
base::RunLoop().RunUntilIdle();
}
}
class LanguageDetectionModelServiceDisabledBrowserTest
: public InProcessBrowserTest {
public:
LanguageDetectionModelServiceDisabledBrowserTest() {
scoped_feature_list_.InitAndDisableFeature(
translate::kTFLiteLanguageDetectionEnabled);
}
void SetUp() override {
origin_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
origin_server_->ServeFilesFromSourceDirectory(
"chrome/test/data/optimization_guide");
ASSERT_TRUE(origin_server_->Start());
english_url_ = origin_server_->GetURL("/hello_world.html");
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"LanguageDetectionAPI");
}
std::string EvalJsCatchingError(Browser* browser, std::string_view script) {
return EvalJs(browser->tab_strip_model()->GetActiveWebContents(),
base::StringPrintf(R"(
(async () => {
try {
%s
} catch (e) {
return e.toString();
}
})();
)",
script))
.ExtractString();
}
void TestLanguageDetectionAvailable(Browser* browser,
const std::string_view result) {
ASSERT_EQ(EvalJsCatchingError(
browser, "return await LanguageDetector.availability();"),
result);
}
~LanguageDetectionModelServiceDisabledBrowserTest() override = default;
const GURL& english_url() const { return english_url_; }
private:
GURL english_url_;
std::unique_ptr<net::EmbeddedTestServer> origin_server_;
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceDisabledBrowserTest,
LanguageDetectionModelServiceDisabled) {
EXPECT_FALSE(LanguageDetectionModelServiceFactory::GetForProfile(
browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceDisabledBrowserTest,
Availability_ModelUnavailable) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
// Language detection is not available when the model service is disabled.
TestLanguageDetectionAvailable(browser(), "unavailable");
}
class LanguageDetectionModelServiceWithoutOptimizationGuideBrowserTest
: public LanguageDetectionModelServiceDisabledBrowserTest {
public:
LanguageDetectionModelServiceWithoutOptimizationGuideBrowserTest() {
scoped_feature_list_.InitWithFeatures(
{translate::kTFLiteLanguageDetectionEnabled},
{optimization_guide::features::kOptimizationHints});
}
~LanguageDetectionModelServiceWithoutOptimizationGuideBrowserTest() override =
default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
// This test confirms the translate model service is not available if
// the optimization guide does not exist.
IN_PROC_BROWSER_TEST_F(
LanguageDetectionModelServiceWithoutOptimizationGuideBrowserTest,
LanguageDetectionModelServiceEnabled) {
EXPECT_FALSE(LanguageDetectionModelServiceFactory::GetForProfile(
browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceDisabledBrowserTest,
LanguageDetectionModelNotCreated) {
base::HistogramTester histogram_tester;
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
RetryForHistogramUntilCountReached(
&histogram_tester, "Translate.CLD3.TopLanguageEvaluationDuration", 1);
histogram_tester.ExpectTotalCount(
"LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 0);
}
// Makes requesting and waiting for the model file easy. This can only be used
// once.
class ModelFileGetter {
public:
explicit ModelFileGetter(
LanguageDetectionModelService& language_detection_model_service)
: language_detection_model_service_(language_detection_model_service) {}
// Queues a request to get the model file. Do not call this again.
void RequestModelFile() {
CHECK_EQ(state_, State::kUnused);
language_detection_model_service_->GetLanguageDetectionModelFile(
base::BindOnce(
[](ModelFileGetter* getter, base::File model_file) {
getter->waiter_.OnEvent();
getter->model_file_ = std::move(model_file);
},
base::Unretained(this)));
state_ = State::kWaiting;
}
// Waits for the file and returns it if the request is satisfied. Returns
// `nullopt` if the waiting is interrupted before the request is satisfied.
// `RequestModelFile` must be called first.
[[nodiscard]] std::optional<base::File> WaitForModelFile() {
CHECK_EQ(state_, State::kWaiting);
if (waiter_.Wait()) {
state_ = State::kUsed;
return std::move(model_file_);
} else {
// This should really only happen if the test times out.
return std::nullopt;
}
}
// Wraps requesting and waiting.
[[nodiscard]] std::optional<base::File> RequestAndWaitForModelFile() {
RequestModelFile();
return WaitForModelFile();
}
// Returns whether a file has been received.
bool HasFileBeenReceived() { return model_file_.has_value(); }
private:
raw_ref<LanguageDetectionModelService> language_detection_model_service_;
content::WaiterHelper waiter_;
enum class State {
kUnused = 0,
kWaiting = 1,
kUsed = 2,
};
State state_ = State::kUnused;
std::optional<base::File> model_file_;
};
class LanguageDetectionModelServiceBrowserTest
: public LanguageDetectionModelServiceDisabledBrowserTest {
public:
LanguageDetectionModelServiceBrowserTest() {
scoped_feature_list_.InitWithFeatures(
{translate::kTFLiteLanguageDetectionEnabled,
optimization_guide::features::kOptimizationHints},
{});
}
void SetUp() override {
origin_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
origin_server_->ServeFilesFromSourceDirectory(
"chrome/test/data/optimization_guide");
origin_server_->RegisterRequestHandler(base::BindRepeating(
&LanguageDetectionModelServiceBrowserTest::RequestHandler,
base::Unretained(this)));
ASSERT_TRUE(origin_server_->Start());
english_url_ = origin_server_->GetURL("/hello_world.html");
InProcessBrowserTest::SetUp();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"LanguageDetectionAPI");
}
// Waits for the model file to be resolved. `nullopt` will be returned if the
// waiting is interrupted, e.g. by test timeout.
[[nodiscard]] std::optional<base::File> RequestAndWaitForModelFile() {
ModelFileGetter getter(*language_detection_model_service());
return getter.RequestAndWaitForModelFile();
}
~LanguageDetectionModelServiceBrowserTest() override = default;
LanguageDetectionModelService* language_detection_model_service() {
return LanguageDetectionModelServiceFactory::GetForProfile(
browser()->profile());
}
const GURL& english_url() const { return english_url_; }
private:
std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
const net::test_server::HttpRequest& request) {
std::string path_value;
// This script is render blocking in the HTML, but is intentionally slow.
// This provides important time between commit and first layout for model
// requests to make it to the renderer, reducing flakes.
if (request.GetURL().path() == "/slow-first-layout.js") {
std::unique_ptr<net::test_server::DelayedHttpResponse> resp =
std::make_unique<net::test_server::DelayedHttpResponse>(
base::Milliseconds(500));
resp->set_code(net::HTTP_OK);
resp->set_content_type("application/javascript");
resp->set_content(std::string());
return resp;
}
return nullptr;
}
base::test::ScopedFeatureList scoped_feature_list_;
GURL english_url_;
std::unique_ptr<net::EmbeddedTestServer> origin_server_;
};
base::FilePath model_file_path() {
base::FilePath source_root_dir;
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_root_dir);
return source_root_dir.AppendASCII("components")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("translate")
.AppendASCII("valid_model.tflite");
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
LanguageDetectionModelServiceEnabled) {
EXPECT_TRUE(language_detection_model_service());
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
LanguageDetectionModelServiceEnabled_OffTheRecord) {
EXPECT_TRUE(LanguageDetectionModelServiceFactory::GetForProfile(
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)));
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
LanguageDetectionModelReadyOnRequest) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
LanguageDetectionModelLoadedAfterRequest) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
ModelFileGetter getter(*language_detection_model_service());
getter.RequestModelFile();
ASSERT_FALSE(getter.HasFileBeenReceived());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
auto model_file = getter.WaitForModelFile();
ASSERT_TRUE(model_file.has_value());
EXPECT_TRUE(model_file->IsValid());
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
InvalidModelWhenLoading) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(
base::FilePath(optimization_guide::StringToFilePath(
optimization_guide::kTestAbsoluteFilePath)
.value()))
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", false, 1);
}
// TODO(crbug.com/40836720): Re-enable this test
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
DISABLED_LanguageDetectionModelAvailableForDetection) {
base::HistogramTester histogram_tester;
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
ui_test_utils::NavigateToURLWithDisposition(
browser(), english_url(), WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
RetryForHistogramUntilCountReached(
&histogram_tester,
"LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 1);
histogram_tester.ExpectUniqueSample(
"LanguageDetection.TFLiteModel.WasModelAvailableForDetection", true, 1);
}
// Disabled on linux+ASAN, macOS+ASAN, chromeOS+ASAN and windows due to high
// failure rate: crbug.com/1199854 crbug.com/1297485.
// TODO(crbug.com/40904444): Re-enable this test
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
DISABLED_LanguageDetectionWithBackgroundTab) {
base::HistogramTester histogram_tester;
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
ui_test_utils::NavigateToURLWithDisposition(
browser(), english_url(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_NO_WAIT);
// Opening the browser causes the first model deferral event. The second
// is due to the background tab.
RetryForHistogramUntilCountReached(
&histogram_tester,
"LanguageDetection.TFLiteModel.WasModelRequestDeferred", 2);
histogram_tester.ExpectBucketCount(
"LanguageDetection.TFLiteModel.WasModelRequestDeferred", true, 2);
// Make the background tab the active tab.
browser()->tab_strip_model()->SelectNextTab();
RetryForHistogramUntilCountReached(
&histogram_tester,
"LanguageDetection.TFLiteModel.WasModelAvailableForDetection", 1);
histogram_tester.ExpectBucketCount(
"LanguageDetection.TFLiteModel.WasModelAvailableForDetection", true, 1);
}
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
ModelUpdateFromOptimizationGuide) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 2);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 2);
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
}
// Test that the service correctly handles being notified that there is no
// longer a valid model available and also that it then handles a valid model
// becoming available.
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
ModelUpdateFromOptimizationGuideMissingModelInfo) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 1);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
// Tell the service that there is no longer a model available.
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
nullptr);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 1);
ASSERT_FALSE(RequestAndWaitForModelFile()->IsValid());
// Tell the service that a model is available again.
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
RetryForHistogramUntilCountReached(
&histogram_tester,
"TranslateModelService.LanguageDetectionModel.WasLoaded", 2);
histogram_tester.ExpectUniqueSample(
"TranslateModelService.LanguageDetectionModel.WasLoaded", true, 2);
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
}
// Tests that we immediately reject requests if we exceed the allowed number of
// pending requests.
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
LimitPendingRequests) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
base::HistogramTester histogram_tester;
ASSERT_TRUE(language_detection_model_service());
ASSERT_GE(kMaxPendingRequestsAllowed, 1);
// The intention is to queue `kMaxPendingRequestsAllowed` pending requests and
// then one more that should fail immediately. However sometimes a request is
// already queued due to the renderer process, so we queue 1-less than max,
// expecting them all to remain pending and then be successfully fulfilled and
// then one more which may or may not remain pending.
// TODO(https://crbug.com/364504537): Make this a unittest to avoid this race
// condition.
std::vector<std::unique_ptr<ModelFileGetter>> getters;
for (int i = 0; i < kMaxPendingRequestsAllowed - 1; i++) {
getters.emplace_back(
std::make_unique<ModelFileGetter>(*language_detection_model_service()));
getters.back()->RequestModelFile();
}
// This one might exceed the max depending on whether we received a request
// from the renderer, so we never check its status.
ModelFileGetter maybe_getter(*language_detection_model_service());
maybe_getter.RequestModelFile();
// Requesting one more should definitely give an invalid file immediately.
ASSERT_FALSE(RequestAndWaitForModelFile()->IsValid());
// The first `kMaxPendingRequestsAllowed - 1` pending ones should still be
// pending.
for (auto& getter_good : getters) {
ASSERT_FALSE(getter_good->HasFileBeenReceived());
}
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
// The first `kMaxPendingRequestsAllowed` should get a valid file now.
for (auto& getter_good : getters) {
ASSERT_TRUE(getter_good->WaitForModelFile()->IsValid());
}
// Requesting one more now should give a valid file because the queue has been
// emptied.
ASSERT_TRUE(RequestAndWaitForModelFile()->IsValid());
}
// TODO(crbug.com/410842873): Add test to check behavior for extension
// service workers once supported.
//
// Test the behavior of the Language Detector API accessed from a service worker
// outside of an extension.
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
APIAvailability_NonExtensionWorkers) {
const std::string kWorkerScript =
"try {"
" LanguageDetector;"
" self.postMessage('test');"
"} catch (e) {"
" self.postMessage(e.name);"
"}";
base::RunLoop loop;
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&RespondWithJS, "/js-response", kWorkerScript, loop.QuitClosure()));
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/workers/create_dedicated_worker.html?worker_url=/js-response")));
loop.Run();
EXPECT_EQ(
"ReferenceError",
content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
"waitForMessage();"));
}
// Tests the behavior of availability().
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest, Availability) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
ASSERT_TRUE(language_detection_model_service());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
// Language detection is not readily available until the model is downloaded.
TestLanguageDetectionAvailable(browser(), "downloadable");
ModelFileGetter getter(*language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
getter.RequestModelFile();
auto model_file = getter.WaitForModelFile();
TestLanguageDetectionAvailable(browser(), "available");
}
// Tests the behavior of availability().
IN_PROC_BROWSER_TEST_F(LanguageDetectionModelServiceBrowserTest,
HebrewLanguageTags) {
base::ScopedAllowBlockingForTesting allow_io_for_test_setup;
ASSERT_TRUE(language_detection_model_service());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), english_url()));
ModelFileGetter getter(*language_detection_model_service());
OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
->OverrideTargetModelForTesting(
optimization_guide::proto::OPTIMIZATION_TARGET_LANGUAGE_DETECTION,
optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path())
.Build());
getter.RequestModelFile();
auto model_file = getter.WaitForModelFile();
// Should accept both the "he" and "iw" tag.
ASSERT_EQ(EvalJsCatchingError(browser(),
R"(
await LanguageDetector.create({expectedInputLanguages: ['iw']});
return 'OK';
)"),
"OK");
ASSERT_EQ(EvalJsCatchingError(browser(),
R"(
await LanguageDetector.create({expectedInputLanguages: ['he']});
return 'OK';
)"),
"OK");
// Should transform both the iw and he tag to just the he tag.
ASSERT_EQ(EvalJsCatchingError(browser(),
R"(
const detector = await LanguageDetector.create(
{expectedInputLanguages: ['iw', 'he']});
return detector.expectedInputLanguages.join(',');
)"),
"he");
// The detectedLanguage for hebrew should be he and not iw.
ASSERT_EQ(EvalJsCatchingError(browser(),
R"(
const detector = await LanguageDetector.create(
{expectedInputLanguages: ['iw', 'he']});
const results = await detector.detect('זוהי מחרוזת בעברית');
return results[0].detectedLanguage;
)"),
"he");
}
} // namespace
} // namespace language_detection