// 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 "chrome/browser/ash/app_list/search/omnibox/open_tab_result.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/strings/strcat.h"
#include "chrome/browser/ash/app_list/search/common/search_result_util.h"
#include "chrome/browser/ash/app_list/test/test_app_list_controller_delegate.h"
#include "chrome/browser/chromeos/launcher_search/search_util.h"
#include "chromeos/ash/components/string_matching/tokenized_string.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/favicon/core/test/mock_favicon_service.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/favicon_cache.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/skia_util.h"
#include "url/gurl.h"
namespace app_list::test {
namespace {
using testing::_;
using testing::Mock;
using ::ash::string_matching::TokenizedString;
// A 16x16 yellow square image.
gfx::ImageSkia TestIcon() {
SkBitmap bitmap;
bitmap.allocN32Pixels(16, 16);
bitmap.eraseColor(SK_ColorYELLOW);
return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
}
// Returns true if the pixels of the two given images are identical.
bool ImageSkiasEqual(const gfx::ImageSkia& a, const gfx::ImageSkia& b) {
return gfx::BitmapsAreEqual(*a.bitmap(), *b.bitmap());
}
} // namespace
class OpenTabResultTest : public testing::Test {
public:
OpenTabResultTest() = default;
OpenTabResultTest(const OpenTabResultTest&) = delete;
OpenTabResultTest& operator=(const OpenTabResultTest&) = delete;
~OpenTabResultTest() override = default;
void SetUp() override {
app_list_controller_delegate_ =
std::make_unique<::test::TestAppListControllerDelegate>();
favicon_cache_ = std::make_unique<FaviconCache>(
/*favicon_service=*/&favicon_service_, /*history_service=*/nullptr);
}
std::unique_ptr<OpenTabResult> MakeResult(const std::u16string& query,
const std::u16string& description,
const std::u16string& url) {
AutocompleteMatch match;
match.description = description;
match.destination_url = GURL(url);
match.relevance = 1000;
match.type = AutocompleteMatchType::OPEN_TAB;
TokenizedString tokenized_query(query, TokenizedString::Mode::kCamelCase);
return std::make_unique<OpenTabResult>(
/*profile=*/nullptr, app_list_controller_delegate_.get(),
crosapi::CreateResult(match, /*controller=*/nullptr,
favicon_cache_.get(),
/*bookmark_model=*/nullptr, AutocompleteInput()),
tokenized_query);
}
const GURL& GetLastOpenedUrl() const {
return app_list_controller_delegate_->last_opened_url();
}
private:
content::BrowserTaskEnvironment task_environment_;
std::unique_ptr<::test::TestAppListControllerDelegate>
app_list_controller_delegate_;
std::unique_ptr<FaviconCache> favicon_cache_;
protected:
testing::NiceMock<favicon::MockFaviconService> favicon_service_;
};
TEST_F(OpenTabResultTest, Basic) {
std::unique_ptr<OpenTabResult> result =
MakeResult(u"query", u"queryabc", u"http://www.website.com");
EXPECT_EQ(result->title(), u"queryabc");
EXPECT_EQ(StringFromTextVector(result->details_text_vector()),
base::StrCat({u"http://www.website.com/",
l10n_util::GetStringFUTF16(IDS_APP_LIST_OPEN_TAB_HINT,
u" - ")}));
EXPECT_EQ(result->accessible_name(),
base::StrCat({u"queryabc, http://www.website.com/",
l10n_util::GetStringFUTF16(IDS_APP_LIST_OPEN_TAB_HINT,
u", ")}));
result->Open(0);
EXPECT_EQ("http://www.website.com/", GetLastOpenedUrl().spec());
EXPECT_EQ(result->DriveId(), std::nullopt);
}
TEST_F(OpenTabResultTest, ManuallyCalculateRelevance) {
std::unique_ptr<OpenTabResult> result1 =
MakeResult(u"query", u"queryabc", u"http://www.website.com");
std::unique_ptr<OpenTabResult> result2 =
MakeResult(u"queryabc", u"queryabc", u"http://www.website.com");
// The results were given the same |match.relevance|, but the closer query
// should have higher score.
EXPECT_GT(result2->relevance(), result1->relevance());
}
TEST_F(OpenTabResultTest, Favicon) {
// Capture the callback used to return the "fetched" icon.
favicon_base::FaviconImageCallback return_icon_callback;
EXPECT_CALL(favicon_service_,
GetFaviconImageForPageURL(GURL("http://www.website.com"),
/*callback=*/_, /*tracker=*/_))
.WillOnce([&](auto, favicon_base::FaviconImageCallback callback, auto) {
return_icon_callback = std::move(callback);
return base::CancelableTaskTracker::kBadTaskId;
});
std::unique_ptr<OpenTabResult> result =
MakeResult(u"query", u"queryabc", u"http://www.website.com");
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&favicon_service_));
base::RunLoop().RunUntilIdle();
// The mock fetch result.
favicon_base::FaviconImageResult mock_icon_result;
mock_icon_result.image = gfx::Image(TestIcon());
// Transmit fetched image back to the Omnibox result.
std::move(return_icon_callback).Run(mock_icon_result);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(
ImageSkiasEqual(TestIcon(), result->icon().icon.Rasterize(nullptr)));
}
} // namespace app_list::test