#include "components/spellcheck/renderer/spellcheck_provider_test.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "components/spellcheck/common/spellcheck_features.h"
#include "components/spellcheck/renderer/spellcheck.h"
#include "components/spellcheck/spellcheck_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_text_checking_result.h"
#include "third_party/blink/public/web/web_text_decoration_type.h"
namespace {
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
struct HybridSpellCheckTestCase {
size_t language_count;
size_t enabled_language_count;
size_t expected_spelling_service_calls_count;
size_t expected_text_check_requests_count;
};
struct CombineSpellCheckResultsTestCase {
std::string browser_locale;
std::string renderer_locale;
const wchar_t* text;
std::vector<SpellCheckResult> browser_results;
bool use_spelling_service;
blink::WebVector<blink::WebTextCheckingResult> expected_results;
};
std::ostream& operator<<(std::ostream& out,
const CombineSpellCheckResultsTestCase& test_case) {
out << "browser_locale=" << test_case.browser_locale
<< ", renderer_locale=" << test_case.renderer_locale << ", text=\""
<< test_case.text
<< "\", use_spelling_service=" << test_case.use_spelling_service;
return out;
}
#endif
class SpellCheckProviderCacheTest : public SpellCheckProviderTest { … };
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
class HybridSpellCheckTest
: public testing::TestWithParam<HybridSpellCheckTestCase> {
public:
HybridSpellCheckTest() : provider_(&embedder_provider_) {}
~HybridSpellCheckTest() override {}
void SetUp() override {
feature_list_.InitAndDisableFeature(
spellcheck::kWinDelaySpellcheckServiceInit);
}
void RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
protected:
base::test::ScopedFeatureList feature_list_;
base::test::SingleThreadTaskEnvironment task_environment_;
spellcheck::EmptyLocalInterfaceProvider embedder_provider_;
TestingSpellCheckProvider provider_;
};
class HybridSpellCheckTestDelayInit : public HybridSpellCheckTest {
public:
HybridSpellCheckTestDelayInit() = default;
void SetUp() override {
feature_list_.InitAndEnableFeature(
spellcheck::kWinDelaySpellcheckServiceInit);
}
};
class CombineSpellCheckResultsTest
: public testing::TestWithParam<CombineSpellCheckResultsTestCase> {
public:
CombineSpellCheckResultsTest() : provider_(&embedder_provider_) {}
~CombineSpellCheckResultsTest() override {}
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
spellcheck::EmptyLocalInterfaceProvider embedder_provider_;
TestingSpellCheckProvider provider_;
};
#endif
TEST_F(SpellCheckProviderCacheTest, SubstringWithoutMisspellings) { … }
TEST_F(SpellCheckProviderCacheTest, SubstringWithMisspellings) { … }
TEST_F(SpellCheckProviderCacheTest, ShorterTextNotSubstring) { … }
TEST_F(SpellCheckProviderCacheTest, ResetCacheOnCustomDictionaryUpdate) { … }
#if BUILDFLAG(IS_WIN) && BUILDFLAG(USE_BROWSER_SPELLCHECKER)
TEST_F(SpellCheckProviderTest, ShouldNotUseBrowserSpellCheck) {
spellcheck::ScopedDisableBrowserSpellCheckerForTesting
disable_browser_spell_checker;
FakeTextCheckingResult completion;
std::u16string text = u"This is a test";
provider_.RequestTextChecking(
text, std::make_unique<FakeTextCheckingCompletion>(&completion));
EXPECT_EQ(provider_.spelling_service_call_count_, 1U);
EXPECT_EQ(provider_.text_check_requests_.size(), 0U);
EXPECT_EQ(completion.completion_count_, 1U);
EXPECT_EQ(completion.cancellation_count_, 0U);
}
static const HybridSpellCheckTestCase kSpellCheckProviderHybridTestsParams[] = {
HybridSpellCheckTestCase{0U, 0U, 0U, 0U},
HybridSpellCheckTestCase{1U, 0U, 0U, 1U},
HybridSpellCheckTestCase{1U, 1U, 1U, 0U},
HybridSpellCheckTestCase{2U, 1U, 0U, 1U},
HybridSpellCheckTestCase{3U, 3U, 1U, 0U},
HybridSpellCheckTestCase{3U, 0U, 0U, 1U},
HybridSpellCheckTestCase{6U, 3U, 0U, 1U}};
INSTANTIATE_TEST_SUITE_P(
SpellCheckProviderHybridTests,
HybridSpellCheckTest,
testing::ValuesIn(kSpellCheckProviderHybridTestsParams));
TEST_P(HybridSpellCheckTest, ShouldUseBrowserSpellCheckOnlyWhenNeeded) {
RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
}
void HybridSpellCheckTest::RunShouldUseBrowserSpellCheckOnlyWhenNeededTest() {
const auto& test_case = GetParam();
FakeTextCheckingResult completion;
provider_.spellcheck()->SetFakeLanguageCounts(
test_case.language_count, test_case.enabled_language_count);
provider_.RequestTextChecking(
u"This is a test",
std::make_unique<FakeTextCheckingCompletion>(&completion));
EXPECT_EQ(provider_.spelling_service_call_count_,
test_case.expected_spelling_service_calls_count);
EXPECT_EQ(provider_.text_check_requests_.size(),
test_case.expected_text_check_requests_count);
EXPECT_EQ(completion.completion_count_,
test_case.expected_text_check_requests_count > 0u ? 0u : 1u);
EXPECT_EQ(completion.cancellation_count_, 0U);
}
INSTANTIATE_TEST_SUITE_P(
SpellCheckProviderHybridTests,
HybridSpellCheckTestDelayInit,
testing::ValuesIn(kSpellCheckProviderHybridTestsParams));
TEST_P(HybridSpellCheckTestDelayInit,
ShouldUseBrowserSpellCheckOnlyWhenNeeded) {
RunShouldUseBrowserSpellCheckOnlyWhenNeededTest();
}
INSTANTIATE_TEST_SUITE_P(
SpellCheckProviderCombineResultsTests,
CombineSpellCheckResultsTest,
testing::Values(
CombineSpellCheckResultsTestCase{"en-US",
"",
L"This has no misspellings",
{},
false,
{}},
CombineSpellCheckResultsTestCase{
"en-US",
"",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"",
L"Tihs has soem \x3053\x3093\x306B\x3061\x306F \x4F60\x597D "
L"\xC548\xB155\xD558\xC138\xC694 "
L"\x0930\x093E\x091C\x0927\x093E\x0928 words in different "
L"character sets "
L"(Japanese, Chinese, Korean, Hindi)",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
14,
5,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
20,
2,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
23,
5,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
29,
6,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")})},
true,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::GRAMMAR,
9,
4,
{std::u16string(u"foo")})},
true,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(blink::WebTextDecorationType::
kWebTextDecorationTypeGrammar,
9,
4)})},
CombineSpellCheckResultsTestCase{"en-US",
"en-US",
L"This has no misspellings",
{},
false,
{}},
CombineSpellCheckResultsTestCase{
"en-US",
"en-US",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"en-US",
L"Tihs has soem misspellings",
{
SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
5,
3,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
14,
12,
{std::u16string(u"foo")}),
},
false,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"en-US",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
5,
3,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
14,
12,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>()},
CombineSpellCheckResultsTestCase{
"en-US",
"fr",
L"Tihs mot is misspelled in Russian: "
L"\x043C\x0438\x0440\x0432\x043E\x0439",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
5,
3,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
35,
6,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>(
std::vector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4)}))},
CombineSpellCheckResultsTestCase{
"en-US",
"ru",
L"Tihs \x0432\x0441\x0435\x0445 is misspelled in Russian: "
L"\x043C\x0438\x0440\x0432\x043E\x0439",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
5,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
36,
6,
{std::u16string(u"foo")})},
false,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
36,
6)})},
CombineSpellCheckResultsTestCase{
"en-US",
"en-US",
L"Tihs has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::GRAMMAR,
9,
4,
{std::u16string(u"foo")})},
true,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
0,
4),
blink::WebTextCheckingResult(blink::WebTextDecorationType::
kWebTextDecorationTypeGrammar,
9,
4)})},
CombineSpellCheckResultsTestCase{
"en-US",
"en-US",
L"This has soem misspellings",
{SpellCheckResult(SpellCheckResult::SPELLING,
0,
4,
{std::u16string(u"foo")}),
SpellCheckResult(SpellCheckResult::SPELLING,
9,
4,
{std::u16string(u"foo")})},
true,
blink::WebVector<blink::WebTextCheckingResult>(
{blink::WebTextCheckingResult(blink::WebTextDecorationType::
kWebTextDecorationTypeGrammar,
0,
4),
blink::WebTextCheckingResult(
blink::WebTextDecorationType::
kWebTextDecorationTypeSpelling,
9,
4)})}));
TEST_P(CombineSpellCheckResultsTest, ShouldCorrectlyCombineHybridResults) {
const auto& test_case = GetParam();
const bool has_browser_check = !test_case.browser_locale.empty();
const bool has_renderer_check = !test_case.renderer_locale.empty();
if (has_browser_check) {
provider_.spellcheck()->InitializeSpellCheckForLocale(
test_case.browser_locale, false);
}
if (has_renderer_check) {
provider_.spellcheck()->InitializeSpellCheckForLocale(
test_case.renderer_locale, true);
}
if (test_case.use_spelling_service) {
for (auto& result : test_case.browser_results) {
const_cast<SpellCheckResult&>(result).spelling_service_used = true;
}
}
FakeTextCheckingResult completion;
SpellCheckProvider::HybridSpellCheckRequestInfo request_info = {
has_renderer_check,
has_browser_check, base::TimeTicks::Now()};
int check_id = provider_.AddCompletionForTest(
std::make_unique<FakeTextCheckingCompletion>(&completion), request_info);
provider_.OnRespondTextCheck(check_id, base::WideToUTF16(test_case.text),
test_case.browser_results);
ASSERT_EQ(completion.completion_count_, 1u);
ASSERT_EQ(completion.cancellation_count_, 0U);
ASSERT_EQ(provider_.spelling_service_call_count_, 0u);
ASSERT_EQ(provider_.text_check_requests_.size(), 0u);
ASSERT_EQ(test_case.expected_results.size(), completion.results_.size());
for (size_t i = 0; i < test_case.expected_results.size(); ++i) {
const auto& expected = test_case.expected_results[i];
const auto& actual = completion.results_[i];
ASSERT_EQ(expected.decoration, actual.decoration);
ASSERT_EQ(expected.location, actual.location);
ASSERT_EQ(expected.length, actual.length);
if (has_renderer_check) {
ASSERT_EQ(actual.replacements.size(), 0u);
} else {
ASSERT_GT(actual.replacements.size(), 0u);
}
}
}
#endif
}