blob: 1e53b29af537273c23406d1f499927b257feb110 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/spellcheck/browser/spellcheck_platform.h"
#include <stddef.h>
#include <ostream>
#include "base/bind.h"
#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/win/windows_version.h"
#include "build/build_config.h"
#include "components/spellcheck/browser/windows_spell_checker.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/common/spellcheck_result.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
struct RequestTextCheckTestCase {
const char* text_to_check;
const char* expected_suggestion;
};
std::ostream& operator<<(std::ostream& out,
const RequestTextCheckTestCase& test_case) {
out << "text_to_check=" << test_case.text_to_check
<< ", expected_suggestion=" << test_case.expected_suggestion;
return out;
}
class WindowsSpellCheckerTest : public testing::Test {
public:
WindowsSpellCheckerTest() {
// The WindowsSpellchecker object can be created even on Windows versions
// that don't support platform spellchecking. However, the spellcheck
// factory won't be instantiated and the result returned in the
// CreateSpellChecker callback will be false.
win_spell_checker_ = std::make_unique<WindowsSpellChecker>(
base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()}));
win_spell_checker_->CreateSpellChecker(
"en-US",
base::BindOnce(&WindowsSpellCheckerTest::SetLanguageCompletionCallback,
base::Unretained(this)));
RunUntilResultReceived();
}
void RunUntilResultReceived() {
if (callback_finished_)
return;
base::RunLoop run_loop;
quit_ = run_loop.QuitClosure();
run_loop.Run();
// reset status
callback_finished_ = false;
}
void SetLanguageCompletionCallback(bool result) {
set_language_result_ = result;
callback_finished_ = true;
if (quit_)
std::move(quit_).Run();
}
void TextCheckCompletionCallback(
const std::vector<SpellCheckResult>& results) {
callback_finished_ = true;
spell_check_results_ = results;
if (quit_)
std::move(quit_).Run();
}
void PerLanguageSuggestionsCompletionCallback(
const spellcheck::PerLanguageSuggestions& suggestions) {
callback_finished_ = true;
per_language_suggestions_ = suggestions;
if (quit_)
std::move(quit_).Run();
}
void RetrieveSpellcheckLanguagesCompletionCallback(
const std::vector<std::string>& spellcheck_languages) {
callback_finished_ = true;
spellcheck_languages_ = spellcheck_languages;
DVLOG(2) << "RetrieveSpellcheckLanguagesCompletionCallback: Dictionary "
"found for following language tags: "
<< base::JoinString(spellcheck_languages_, ", ");
if (quit_)
std::move(quit_).Run();
}
protected:
void SetUp() override {
feature_list_.InitWithFeatures(
/*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker,
spellcheck::kWinRetrieveSuggestionsOnlyOnDemand},
/*disabled_features=*/{});
}
void RunRequestTextCheckTest(const RequestTextCheckTestCase& test_case);
std::unique_ptr<WindowsSpellChecker> win_spell_checker_;
bool callback_finished_ = false;
base::OnceClosure quit_;
base::test::ScopedFeatureList feature_list_;
bool set_language_result_;
std::vector<SpellCheckResult> spell_check_results_;
spellcheck::PerLanguageSuggestions per_language_suggestions_;
std::vector<std::string> spellcheck_languages_;
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
};
void WindowsSpellCheckerTest::RunRequestTextCheckTest(
const RequestTextCheckTestCase& test_case) {
ASSERT_EQ(set_language_result_,
spellcheck::WindowsVersionSupportsSpellchecker());
const std::u16string word(base::ASCIIToUTF16(test_case.text_to_check));
// Check if the suggested words occur.
win_spell_checker_->RequestTextCheck(
1, word,
base::BindOnce(&WindowsSpellCheckerTest::TextCheckCompletionCallback,
base::Unretained(this)));
RunUntilResultReceived();
if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
// On Windows versions that don't support platform spellchecking, the
// returned vector of results should be empty.
ASSERT_TRUE(spell_check_results_.empty());
return;
}
ASSERT_EQ(1u, spell_check_results_.size())
<< "RequestTextCheck: Wrong number of results";
const std::vector<std::u16string>& suggestions =
spell_check_results_.front().replacements;
if (base::FeatureList::IsEnabled(
spellcheck::kWinRetrieveSuggestionsOnlyOnDemand)) {
// RequestTextCheck should return no suggestions.
ASSERT_TRUE(suggestions.empty())
<< "RequestTextCheck: No suggestions are expected";
} else {
const std::u16string suggested_word(
base::ASCIIToUTF16(test_case.expected_suggestion));
auto position =
std::find_if(suggestions.begin(), suggestions.end(),
[&](const std::u16string& suggestion) {
return suggestion.compare(suggested_word) == 0;
});
ASSERT_FALSE(position == suggestions.end())
<< "RequestTextCheck: Expected suggestion not found";
}
}
static const RequestTextCheckTestCase kRequestTextCheckTestCases[] = {
{"absense", "absence"}, {"becomeing", "becoming"},
{"cieling", "ceiling"}, {"definate", "definite"},
{"eigth", "eight"}, {"exellent", "excellent"},
{"finaly", "finally"}, {"garantee", "guarantee"},
{"humerous", "humorous"}, {"imediately", "immediately"},
{"jellous", "jealous"}, {"knowlege", "knowledge"},
{"lenght", "length"}, {"manuever", "maneuver"},
{"naturaly", "naturally"}, {"ommision", "omission"},
};
class WindowsSpellCheckerRequestTextCheckTest
: public WindowsSpellCheckerTest,
public testing::WithParamInterface<RequestTextCheckTestCase> {};
INSTANTIATE_TEST_SUITE_P(TestCases,
WindowsSpellCheckerRequestTextCheckTest,
testing::ValuesIn(kRequestTextCheckTestCases));
TEST_P(WindowsSpellCheckerRequestTextCheckTest, RequestTextCheck) {
RunRequestTextCheckTest(GetParam());
}
class WindowsSpellCheckerRequestTextCheckWithSuggestionsTest
: public WindowsSpellCheckerRequestTextCheckTest {
protected:
void SetUp() override {
// Want to maintain test coverage for requesting suggestions on call to
// RequestTextCheck.
feature_list_.InitWithFeatures(
/*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker},
/*disabled_features=*/{
spellcheck::kWinRetrieveSuggestionsOnlyOnDemand});
}
};
INSTANTIATE_TEST_SUITE_P(TestCases,
WindowsSpellCheckerRequestTextCheckWithSuggestionsTest,
testing::ValuesIn(kRequestTextCheckTestCases));
TEST_P(WindowsSpellCheckerRequestTextCheckWithSuggestionsTest,
RequestTextCheck) {
RunRequestTextCheckTest(GetParam());
}
TEST_F(WindowsSpellCheckerTest, RetrieveSpellcheckLanguages) {
// Test retrieval of real dictionary on system (useful for debug logging
// other registered dictionaries).
win_spell_checker_->RetrieveSpellcheckLanguages(base::BindOnce(
&WindowsSpellCheckerTest::RetrieveSpellcheckLanguagesCompletionCallback,
base::Unretained(this)));
RunUntilResultReceived();
if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
// On Windows versions that don't support platform spellchecking, the
// returned vector of results should be empty.
ASSERT_TRUE(spellcheck_languages_.empty());
return;
}
ASSERT_LE(1u, spellcheck_languages_.size());
ASSERT_TRUE(base::Contains(spellcheck_languages_, "en-US"));
}
TEST_F(WindowsSpellCheckerTest, RetrieveSpellcheckLanguagesFakeDictionaries) {
// Test retrieval of fake dictionaries added using
// AddSpellcheckLanguagesForTesting. If fake dictionaries are used,
// instantiation of the spellchecker factory is not required for
// RetrieveSpellcheckLanguages, so the test should pass even on Windows
// versions that don't support platform spellchecking.
std::vector<std::string> spellcheck_languages_for_testing = {
"ar-SA", "es-419", "fr-CA"};
win_spell_checker_->AddSpellcheckLanguagesForTesting(
spellcheck_languages_for_testing);
DVLOG(2) << "Calling RetrieveSpellcheckLanguages after fake dictionaries "
"added using AddSpellcheckLanguagesForTesting...";
win_spell_checker_->RetrieveSpellcheckLanguages(base::BindOnce(
&WindowsSpellCheckerTest::RetrieveSpellcheckLanguagesCompletionCallback,
base::Unretained(this)));
RunUntilResultReceived();
ASSERT_EQ(spellcheck_languages_for_testing, spellcheck_languages_);
}
TEST_F(WindowsSpellCheckerTest, GetPerLanguageSuggestions) {
ASSERT_EQ(set_language_result_,
spellcheck::WindowsVersionSupportsSpellchecker());
win_spell_checker_->GetPerLanguageSuggestions(
u"tihs",
base::BindOnce(
&WindowsSpellCheckerTest::PerLanguageSuggestionsCompletionCallback,
base::Unretained(this)));
RunUntilResultReceived();
if (!spellcheck::WindowsVersionSupportsSpellchecker()) {
// On Windows versions that don't support platform spellchecking, the
// returned vector of results should be empty.
ASSERT_TRUE(per_language_suggestions_.empty());
return;
}
ASSERT_EQ(per_language_suggestions_.size(), 1u);
ASSERT_GT(per_language_suggestions_[0].size(), 0u);
}
} // namespace