// Copyright 2024 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/ui/ash/birch/birch_calendar_provider.h"
#include <vector>
#include "ash/birch/birch_model.h"
#include "ash/calendar/calendar_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/shell.h"
#include "ash/system/time/calendar_unittest_utils.h"
#include "base/check.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/ash/birch/birch_calendar_fetcher.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "google_apis/calendar/calendar_api_response_types.h"
#include "google_apis/common/api_error_codes.h"
namespace ash {
namespace {
using google_apis::ApiErrorCode;
base::Time TimeFromString(const char* time_string) {
base::Time time;
CHECK(base::Time::FromString(time_string, &time));
return time;
}
// A fake fetcher that provides arbitrary error codes and events.
class TestCalendarFetcher : public BirchCalendarFetcher {
public:
explicit TestCalendarFetcher(Profile* profile)
: BirchCalendarFetcher(profile) {}
~TestCalendarFetcher() override = default;
// BirchCalendarFetcher:
void GetCalendarEvents(
base::Time start_time,
base::Time end_time,
google_apis::calendar::CalendarEventListCallback callback) override {
std::move(callback).Run(error_code_, std::move(events_));
}
ApiErrorCode error_code_ = ApiErrorCode::HTTP_SUCCESS;
std::unique_ptr<google_apis::calendar::EventList> events_;
};
// A fetcher that counts how many times GetCalendarEvents() was called.
class CountingCalendarFetcher : public BirchCalendarFetcher {
public:
explicit CountingCalendarFetcher(Profile* profile)
: BirchCalendarFetcher(profile) {}
~CountingCalendarFetcher() override = default;
// BirchCalendarFetcher:
void GetCalendarEvents(
base::Time start_time,
base::Time end_time,
google_apis::calendar::CalendarEventListCallback callback) override {
++get_calendar_events_count_;
// Intentionally don't run the callback.
}
int get_calendar_events_count_ = 0;
};
// BrowserWithTestWindowTest provides a Profile and ash::Shell (which provides
// a BirchModel) needed by the test.
class BirchCalendarProviderTest : public BrowserWithTestWindowTest {
public:
BirchCalendarProviderTest() = default;
~BirchCalendarProviderTest() override = default;
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
calendar_client_ =
std::make_unique<calendar_test_utils::CalendarClientTestImpl>();
AccountId account_id = AccountId::FromUserEmail("[email protected]");
Shell::Get()->calendar_controller()->SetActiveUserAccountIdForTesting(
account_id);
Shell::Get()->calendar_controller()->RegisterClientForUser(
account_id, calendar_client_.get());
}
private:
base::test::ScopedFeatureList feature_list_{features::kForestFeature};
std::unique_ptr<calendar_test_utils::CalendarClientTestImpl> calendar_client_;
};
TEST_F(BirchCalendarProviderTest, GetCalendarEvents) {
BirchCalendarProvider provider(profile());
// Set up a custom fetcher with known events.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
auto events = std::make_unique<google_apis::calendar::EventList>();
events->set_time_zone("Greenwich Mean Time");
events->InjectItemForTesting(calendar_test_utils::CreateEvent(
"id_0", "title_0", "10 Jan 2010 10:00 GMT", "10 Jan 2010 11:00 GMT"));
events->InjectItemForTesting(calendar_test_utils::CreateEvent(
"id_1", "title_1", "11 Jan 2010 10:00 GMT", "11 Jan 2010 11:00 GMT"));
fetcher->events_ = std::move(events);
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// Verify the events were inserted into the birch model.
const auto& items = Shell::Get()->birch_model()->GetCalendarItemsForTest();
ASSERT_EQ(2u, items.size());
EXPECT_EQ(items[0].title(), u"title_0");
EXPECT_EQ(items[0].start_time(), TimeFromString("10 Jan 2010 10:00 GMT"));
EXPECT_EQ(items[0].end_time(), TimeFromString("10 Jan 2010 11:00 GMT"));
EXPECT_EQ(items[1].title(), u"title_1");
EXPECT_EQ(items[1].start_time(), TimeFromString("11 Jan 2010 10:00 GMT"));
EXPECT_EQ(items[1].end_time(), TimeFromString("11 Jan 2010 11:00 GMT"));
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_WithNoSummary) {
BirchCalendarProvider provider(profile());
// Set up a custom fetcher with known events.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
auto events = std::make_unique<google_apis::calendar::EventList>();
events->set_time_zone("Greenwich Mean Time");
events->InjectItemForTesting(calendar_test_utils::CreateEvent(
"id_0", /*summary=*/"", "10 Jan 2010 10:00 GMT",
"10 Jan 2010 11:00 GMT"));
fetcher->events_ = std::move(events);
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// The title contains "(No title)".
const auto& items = Shell::Get()->birch_model()->GetCalendarItemsForTest();
ASSERT_EQ(1u, items.size());
EXPECT_EQ(items[0].title(), u"(No title)");
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_WithAttachments) {
BirchCalendarProvider provider(profile());
// Set up a custom fetcher with an event with attachments.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
auto events = std::make_unique<google_apis::calendar::EventList>();
events->set_time_zone("Greenwich Mean Time");
auto event = calendar_test_utils::CreateEvent(
"id_0", "title_0", "10 Jan 2010 10:00 GMT", "10 Jan 2010 11:00 GMT");
event->set_conference_data_uri(GURL("http://meet.com/"));
google_apis::calendar::Attachment attachment0;
attachment0.set_title("attachment0");
attachment0.set_file_url(GURL("http://file0.com/"));
attachment0.set_icon_link(GURL("http://icon0.com/"));
attachment0.set_file_id("file_id_0");
google_apis::calendar::Attachment attachment1;
attachment1.set_title("attachment1");
attachment1.set_file_url(GURL("http://file1.com/"));
attachment1.set_icon_link(GURL("http://icon1.com/"));
attachment1.set_file_id("file_id_1");
event->set_attachments({attachment0, attachment1});
events->InjectItemForTesting(std::move(event));
fetcher->events_ = std::move(events);
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// Verify the event was converted correctly to Birch data types.
auto* birch_model = Shell::Get()->birch_model();
const auto& items = birch_model->GetCalendarItemsForTest();
ASSERT_EQ(1u, items.size());
EXPECT_EQ(items[0].title(), u"title_0");
EXPECT_EQ(items[0].start_time(), TimeFromString("10 Jan 2010 10:00 GMT"));
EXPECT_EQ(items[0].end_time(), TimeFromString("10 Jan 2010 11:00 GMT"));
EXPECT_EQ(items[0].conference_url().spec(), "http://meet.com/");
// Verify the attachments were converted correctly to Birch data types.
const auto& attachments = birch_model->GetAttachmentItemsForTest();
EXPECT_EQ(attachments[0].title(), u"attachment0");
EXPECT_EQ(attachments[0].file_url().spec(), "http://file0.com/");
EXPECT_EQ(attachments[0].icon_url().spec(), "http://icon0.com/");
EXPECT_EQ(attachments[0].file_id(), "file_id_0");
EXPECT_EQ(attachments[1].title(), u"attachment1");
EXPECT_EQ(attachments[1].file_url().spec(), "http://file1.com/");
EXPECT_EQ(attachments[1].icon_url().spec(), "http://icon1.com/");
EXPECT_EQ(attachments[1].file_id(), "file_id_1");
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_DeclinedEventAttachment) {
BirchCalendarProvider provider(profile());
// Set up a custom fetcher with an event with attachments.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
auto events = std::make_unique<google_apis::calendar::EventList>();
events->set_time_zone("Greenwich Mean Time");
// Create a declined event with an attachment.
auto event = calendar_test_utils::CreateEvent(
"id_0", "title_0", "10 Jan 2010 10:00 GMT", "10 Jan 2010 11:00 GMT",
google_apis::calendar::CalendarEvent::EventStatus::kConfirmed,
google_apis::calendar::CalendarEvent::ResponseStatus::kDeclined);
event->set_conference_data_uri(GURL("http://meet.com/"));
google_apis::calendar::Attachment attachment0;
attachment0.set_title("attachment0");
attachment0.set_file_url(GURL("http://file0.com/"));
attachment0.set_icon_link(GURL("http://icon0.com/"));
attachment0.set_file_id("file_id_0");
event->set_attachments({attachment0});
events->InjectItemForTesting(std::move(event));
fetcher->events_ = std::move(events);
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// Verify the declined event is not added to the model.
auto* birch_model = Shell::Get()->birch_model();
const auto& items = birch_model->GetCalendarItemsForTest();
ASSERT_EQ(0u, items.size());
// Verify the declined event attachment is not added to the model.
const auto& attachments = birch_model->GetAttachmentItemsForTest();
ASSERT_EQ(0u, attachments.size());
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_HttpError) {
BirchCalendarProvider provider(profile());
// Populate the birch model with an event so the test can sense when the
// model is cleared later.
std::vector<BirchCalendarItem> items;
items.emplace_back(u"Event 1", /*start_time=*/base::Time(),
/*end_time=*/base::Time(), /*calendar_url=*/GURL(),
/*conference_url=*/GURL(), /*event_id=*/"",
/*all_day_event=*/false);
Shell::Get()->birch_model()->SetCalendarItems(std::move(items));
// Set up a customer fetcher that returns an error.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
fetcher->error_code_ = ApiErrorCode::HTTP_INTERNAL_SERVER_ERROR;
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// Verify the birch model is empty.
EXPECT_TRUE(Shell::Get()->birch_model()->GetCalendarItemsForTest().empty());
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_NullEventList) {
BirchCalendarProvider provider(profile());
// Populate the birch model with an event so the test can sense when the
// model is cleared later.
std::vector<BirchCalendarItem> items;
items.emplace_back(u"Event 1", /*start_time=*/base::Time(),
/*end_time=*/base::Time(), /*calendar_url=*/GURL(),
/*conference_url=*/GURL(), /*event_id=*/"",
/*all_day_event=*/false);
Shell::Get()->birch_model()->SetCalendarItems(std::move(items));
// Set up a customer fetcher that returns a null event list.
auto fetcher = std::make_unique<TestCalendarFetcher>(profile());
fetcher->events_ = nullptr;
provider.SetFetcherForTest(std::move(fetcher));
// Get the calendar events.
provider.RequestBirchDataFetch();
// Verify the birch model is empty.
EXPECT_TRUE(Shell::Get()->birch_model()->GetCalendarItemsForTest().empty());
}
TEST_F(BirchCalendarProviderTest, GetCalendarEvents_MultipleRequests) {
BirchCalendarProvider provider(profile());
// Set up a customer fetcher.
auto fetcher = std::make_unique<CountingCalendarFetcher>(profile());
auto* fetcher_ptr = fetcher.get();
provider.SetFetcherForTest(std::move(fetcher));
ASSERT_EQ(fetcher_ptr->get_calendar_events_count_, 0);
// Request calendar events twice in a row.
provider.RequestBirchDataFetch();
provider.RequestBirchDataFetch();
// The fetcher was only triggered once.
EXPECT_EQ(fetcher_ptr->get_calendar_events_count_, 1);
}
} // namespace
} // namespace ash