| // 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 "chromeos/components/quick_answers/understanding/intent_generator.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/functional/bind.h" |
| #include "base/test/task_environment.h" |
| #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h" |
| #include "chromeos/components/quick_answers/quick_answers_model.h" |
| #include "chromeos/components/quick_answers/test/quick_answers_test_base.h" |
| #include "chromeos/components/quick_answers/utils/quick_answers_utils.h" |
| #include "chromeos/components/quick_answers/utils/spell_checker.h" |
| #include "chromeos/services/machine_learning/public/cpp/fake_service_connection.h" |
| #include "chromeos/services/machine_learning/public/mojom/machine_learning_service.mojom.h" |
| #include "chromeos/services/machine_learning/public/mojom/text_classifier.mojom.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace quick_answers { |
| namespace { |
| |
| using ::chromeos::machine_learning::FakeServiceConnectionImpl; |
| using ::chromeos::machine_learning::mojom::TextAnnotation; |
| using ::chromeos::machine_learning::mojom::TextAnnotationPtr; |
| using ::chromeos::machine_learning::mojom::TextEntity; |
| using ::chromeos::machine_learning::mojom::TextEntityData; |
| using ::chromeos::machine_learning::mojom::TextEntityPtr; |
| using ::chromeos::machine_learning::mojom::TextLanguage; |
| using ::chromeos::machine_learning::mojom::TextLanguagePtr; |
| |
| TextLanguagePtr DefaultLanguage() { |
| return TextLanguage::New("en", /* confidence */ 1); |
| } |
| |
| std::vector<TextLanguagePtr> DefaultLanguages() { |
| std::vector<TextLanguagePtr> languages; |
| languages.push_back(DefaultLanguage()); |
| return languages; |
| } |
| |
| class FakeSpellChecker : public SpellChecker { |
| public: |
| FakeSpellChecker( |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) |
| : SpellChecker(url_loader_factory) {} |
| ~FakeSpellChecker() override = default; |
| |
| // SpellChecker: |
| void CheckSpelling(const std::string& word, |
| CheckSpellingCallback callback) override { |
| std::move(callback).Run(dictionary_.find(word) != dictionary_.end(), |
| dictionary_[word]); |
| } |
| |
| void AddWordToDictionary(const std::string& word, |
| const std::string& language = "en") { |
| dictionary_.insert({word, language}); |
| } |
| |
| private: |
| std::map<std::string, std::string> dictionary_; |
| }; |
| |
| } // namespace |
| |
| class IntentGeneratorTest : public QuickAnswersTestBase { |
| public: |
| IntentGeneratorTest() = default; |
| |
| IntentGeneratorTest(const IntentGeneratorTest&) = delete; |
| IntentGeneratorTest& operator=(const IntentGeneratorTest&) = delete; |
| |
| void SetUp() override { |
| QuickAnswersTestBase::SetUp(); |
| |
| test_shared_loader_factory_ = |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_); |
| spell_checker_ = |
| std::make_unique<FakeSpellChecker>(test_shared_loader_factory_); |
| intent_generator_ = std::make_unique<IntentGenerator>( |
| spell_checker_->GetWeakPtr(), |
| base::BindOnce(&IntentGeneratorTest::IntentGeneratorTestCallback, |
| base::Unretained(this))); |
| |
| fake_quick_answers_state()->set_use_text_annotator_for_testing(); |
| fake_quick_answers_state()->SetApplicationLocale("en"); |
| fake_quick_answers_state()->SetPreferredLanguages("en"); |
| } |
| |
| void TearDown() override { |
| intent_generator_.reset(); |
| spell_checker_.reset(); |
| QuickAnswersTestBase::TearDown(); |
| } |
| |
| void IntentGeneratorTestCallback(const IntentInfo& intent_info) { |
| intent_info_ = intent_info; |
| } |
| |
| // Flush all relevant Mojo pipes. |
| void FlushForTesting() { |
| intent_generator_->FlushForTesting(); |
| fake_service_connection_.FlushForTesting(); |
| } |
| |
| protected: |
| void UseFakeServiceConnection( |
| const std::vector<TextAnnotationPtr>& annotations = |
| std::vector<TextAnnotationPtr>(), |
| const std::vector<TextLanguagePtr>& languages = DefaultLanguages()) { |
| chromeos::machine_learning::ServiceConnection:: |
| UseFakeServiceConnectionForTesting(&fake_service_connection_); |
| chromeos::machine_learning::ServiceConnection::GetInstance()->Initialize(); |
| |
| fake_service_connection_.SetOutputAnnotation(annotations); |
| fake_service_connection_.SetOutputLanguages(languages); |
| } |
| |
| FakeSpellChecker* spell_checker() { return spell_checker_.get(); } |
| |
| base::test::TaskEnvironment task_environment_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; |
| std::unique_ptr<FakeSpellChecker> spell_checker_; |
| std::unique_ptr<IntentGenerator> intent_generator_; |
| IntentInfo intent_info_; |
| FakeServiceConnectionImpl fake_service_connection_; |
| }; |
| |
| TEST_F(IntentGeneratorTest, TranslationIntent) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should generate translation intent. |
| EXPECT_EQ(IntentType::kTranslation, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| EXPECT_EQ("es", intent_info_.device_language); |
| EXPECT_EQ("en", intent_info_.source_language); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentWithSubtag) { |
| std::vector<TextLanguagePtr> languages; |
| languages.push_back(TextLanguage::New("en-US", /* confidence */ 1)); |
| UseFakeServiceConnection({}, languages); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should generate translation intent. |
| EXPECT_EQ(IntentType::kTranslation, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| EXPECT_EQ("es", intent_info_.device_language); |
| // Should drop substag for source language. |
| EXPECT_EQ("en", intent_info_.source_language); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentSameLanguage) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("en"); |
| fake_quick_answers_state()->SetPreferredLanguages("en"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the detected language is the |
| // same as system language. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentPreferredLocale) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es,en,zh"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the detected language is in |
| // the preferred languages list. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentPreferredLanguage) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es-MX,en-US,zh-CN"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the detected language is in |
| // the preferred languages list. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentTextLengthAboveThreshold) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = |
| "Search the world's information, including webpages, images, videos and " |
| "more. Google has many special features to help you find exactly what " |
| "you're looking ..."; |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the length of the selected |
| // text is above the threshold. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ( |
| "Search the world's information, including webpages, images, videos and " |
| "more. Google has many special features to help you find exactly what " |
| "you're looking ...", |
| intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentWithAnnotation) { |
| QuickAnswersRequest request; |
| request.selected_text = "prueba"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 6, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| std::vector<TextLanguagePtr> languages; |
| languages.push_back(TextLanguage::New("es", /* confidence */ 1)); |
| UseFakeServiceConnection(annotations, languages); |
| |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should generate dictionary intent which is prioritized against |
| // translation. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ("prueba", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentDeviceLanguageNotSet) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the device language is not |
| // set. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentUnsupportedDeviceLanguage) { |
| UseFakeServiceConnection(); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("unk"); |
| fake_quick_answers_state()->SetPreferredLanguages("unk"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the device language is |
| // not in the supported languages list. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TranslationIntentUnsupportedSourceLanguage) { |
| std::vector<TextLanguagePtr> languages; |
| languages.push_back(TextLanguage::New("unk", /* confidence */ 1)); |
| UseFakeServiceConnection({}, languages); |
| |
| QuickAnswersRequest request; |
| request.selected_text = "quick answers"; |
| fake_quick_answers_state()->SetApplicationLocale("en"); |
| fake_quick_answers_state()->SetPreferredLanguages("en"); |
| intent_generator_->GenerateIntent(request); |
| |
| FlushForTesting(); |
| |
| // Should not generate translation intent since the detected source |
| // language is not in the supported languages list. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("quick answers", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationDefinitionIntent) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "unfathomable"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 12, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should generate dictionary intent. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ("unfathomable", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, |
| TextAnnotationDefinitionIntentExtraCharsBelowThreshold) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "“unfathomable”"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(1, // Start offset. |
| 13, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should generate dictionary intent since the extra characters is below the |
| // threshold. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ("unfathomable", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, |
| TextAnnotationDefinitionIntentExtraCharsAboveThreshold) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "the unfathomable"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(4, // Start offset. |
| 16, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should not generate dictionary intent since the extra characters is above |
| // the threshold. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("unfathomable", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationUnitIntentExtraChars) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "23 cm to"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "unit", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 5, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should generate unit conversion intent. |
| EXPECT_EQ(IntentType::kUnit, intent_info_.intent_type); |
| EXPECT_EQ("23 cm", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationUnitIntentUtf16Char) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "350°F"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "unit", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 5, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should generate unit conversion intent. |
| EXPECT_EQ(IntentType::kUnit, intent_info_.intent_type); |
| EXPECT_EQ("350°F", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationUnitIntentExtraCharsAboveThreshold) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "23 cm is equal to 9.06 inches"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "unit", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 5, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should not generate unit conversion intent since the extra characters is |
| // above the threshold. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("23 cm", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationNonEnglishLanguage) { |
| fake_quick_answers_state()->SetApplicationLocale("es"); |
| fake_quick_answers_state()->SetPreferredLanguages("es"); |
| |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "unfathomable"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 12, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| std::vector<TextLanguagePtr> languages; |
| languages.push_back(TextLanguage::New("en", /* confidence */ 1)); |
| UseFakeServiceConnection(annotations, languages); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| |
| FlushForTesting(); |
| |
| // Should not generate dictionary intent since English is not device language |
| // or preferred language. Should fallback to translation intent. |
| EXPECT_EQ(IntentType::kTranslation, intent_info_.intent_type); |
| EXPECT_EQ("unfathomable", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationIntentNoAnnotation) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "the unfathomable reaches of space"; |
| |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| FlushForTesting(); |
| |
| // Should generate unknown intent since no annotation found. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("the unfathomable reaches of space", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationIntentNoEntity) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "the unfathomable reaches of space"; |
| |
| std::vector<TextEntityPtr> entities; |
| auto dictionary_annotation = TextAnnotation::New(4, // Start offset. |
| 16, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| FlushForTesting(); |
| |
| // Should generate unknown intent since no entity found. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("the unfathomable reaches of space", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, TextAnnotationIntentUnSupportedEntity) { |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "the unfathomable reaches of space"; |
| |
| // Create the test annotations. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "something_else", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto some_annotation = TextAnnotation::New(4, // Start offset. |
| 16, // End offset. |
| std::move(entities)); |
| |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(some_annotation->Clone()); |
| UseFakeServiceConnection(annotations); |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| FlushForTesting(); |
| |
| // Should generate unknown intent unsupported entity is provided. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("the unfathomable reaches of space", intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, ShouldTriggerForSingleWordInDictionary) { |
| const std::string kWord = "single"; |
| |
| // No Annotation provided. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Add word to the dictionary. |
| spell_checker()->AddWordToDictionary(kWord); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should generate dictionary intent. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, ShouldTriggerForNonEnglishWordInDictionary) { |
| const std::string kWord = "palabra"; |
| const std::string kLanguage = "es"; |
| |
| // No Annotation provided. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Add word to the dictionary. |
| spell_checker()->AddWordToDictionary(kWord, kLanguage); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should generate dictionary intent. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| EXPECT_EQ(kLanguage, intent_info_.source_language); |
| } |
| |
| TEST_F(IntentGeneratorTest, |
| ShouldNotTriggerForSingleWordInDictionaryWithDigits) { |
| const std::string kWord = "1st"; |
| |
| // No Annotation provided. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Add word to the dictionary. |
| spell_checker()->AddWordToDictionary(kWord); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should not generate dictionary intent if the word contains digits even if |
| // it is in the dictionary. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, ShouldNotTriggerForProperNounInDictionary) { |
| const std::string kWord = "Amy"; |
| |
| // No Annotation provided. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Add word to the dictionary. |
| spell_checker()->AddWordToDictionary(kWord); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should not generate dictionary intent if the word contains digits even if |
| // it is in the dictionary. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, |
| ShouldFallbackToAnnotationsForWordNotInDictionaryNoAnnotation) { |
| const std::string kWord = "single"; |
| |
| // No Annotation provided, and not add the word to the dictionary. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should not generate dictionary intent if the word is not in the dictionary |
| // and no annotation provided. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| } |
| |
| TEST_F( |
| IntentGeneratorTest, |
| ShouldFallbackToAnnotationsForWordNotInDictionaryWithDictionaryAnnotation) { |
| const std::string kWord = "unfathomable"; |
| |
| // Annotation provided, and not add the word to the dictionary. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "dictionary", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 12, // End offset. |
| std::move(entities)); |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| UseFakeServiceConnection(annotations); |
| |
| // Word selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kWord; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should generate dictionary intent for the word. |
| EXPECT_EQ(IntentType::kDictionary, intent_info_.intent_type); |
| EXPECT_EQ(kWord, intent_info_.intent_text); |
| } |
| |
| TEST_F( |
| IntentGeneratorTest, |
| ShouldFallbackToAnnotationsForWordNotInDictionaryWithUnitConversionAnnotation) { |
| const std::string kText = "50kg"; |
| |
| // Annotation provided, and not add the text to the dictionary. |
| std::vector<TextEntityPtr> entities; |
| entities.emplace_back(TextEntity::New( |
| "unit", // Entity name. |
| 1.0, // Confidence score. |
| TextEntityData::NewNumericValue(0.0))); // Data extracted. |
| |
| auto dictionary_annotation = TextAnnotation::New(0, // Start offset. |
| 4, // End offset. |
| std::move(entities)); |
| std::vector<TextAnnotationPtr> annotations; |
| annotations.push_back(dictionary_annotation->Clone()); |
| UseFakeServiceConnection(annotations); |
| |
| // Text selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = kText; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should generate unit conversion intent. |
| EXPECT_EQ(IntentType::kUnit, intent_info_.intent_type); |
| EXPECT_EQ(kText, intent_info_.intent_text); |
| } |
| |
| TEST_F(IntentGeneratorTest, ShouldNotTriggerForMultipleWords) { |
| // No Annotation provided. |
| std::vector<TextAnnotationPtr> annotations; |
| UseFakeServiceConnection(annotations); |
| |
| // Multiple words selected. |
| std::unique_ptr<QuickAnswersRequest> quick_answers_request = |
| std::make_unique<QuickAnswersRequest>(); |
| quick_answers_request->selected_text = "multiple words"; |
| |
| intent_generator_->GenerateIntent(*quick_answers_request); |
| task_environment_.RunUntilIdle(); |
| |
| // Should fallback to unknown intent. |
| EXPECT_EQ(IntentType::kUnknown, intent_info_.intent_type); |
| EXPECT_EQ("multiple words", intent_info_.intent_text); |
| } |
| |
| } // namespace quick_answers |