blob: 2885ab4073959bbc4e285cdf40ae032fba2f3c03 [file] [log] [blame] [edit]
// Copyright 2025 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/tab/web_contents_state.h"
#include "base/android/jni_android.h"
#include "base/android/jni_bytebuffer.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "components/sessions/core/serialized_navigation_entry.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
namespace {
sessions::SerializedNavigationEntry CreateNavigation(
int index,
const std::string& url,
const std::u16string& title) {
sessions::SerializedNavigationEntry entry;
entry.set_index(index);
entry.set_virtual_url(GURL(url));
entry.set_title(title);
entry.set_transition_type(ui::PAGE_TRANSITION_LINK);
return entry;
}
void ExpectNavigationsEqual(const sessions::SerializedNavigationEntry& expected,
const sessions::SerializedNavigationEntry& actual) {
EXPECT_EQ(expected.index(), actual.index());
EXPECT_EQ(expected.virtual_url(), actual.virtual_url());
EXPECT_EQ(expected.title(), actual.title());
}
size_t CalculateUnpackedSize(const WebContentsStateUnpacked& unpacked) {
size_t size = sizeof(WebContentsStateUnpacked);
size += unpacked.navigations().capacity() *
sizeof(sessions::SerializedNavigationEntry);
for (const auto& nav : unpacked.navigations()) {
size += nav.virtual_url().spec().size();
size += nav.title().size() * sizeof(char16_t);
size += nav.encoded_page_state().size();
}
return size;
}
} // namespace
class WebContentsStateUnpackedTest : public ::testing::Test {
public:
WebContentsStateUnpackedTest() = default;
private:
content::BrowserTaskEnvironment task_environment_;
};
TEST_F(WebContentsStateUnpackedTest, PackAndUnpack) {
const bool kIsOffTheRecord = false;
const int kCurrentEntryIndex = 1;
const int kSavedStateVersion = 2;
std::vector<sessions::SerializedNavigationEntry> navigations;
navigations.push_back(
CreateNavigation(0, "https://example.com/0", u"Title 0"));
navigations.push_back(
CreateNavigation(1, "https://example.com/1", u"Title 1"));
navigations.push_back(
CreateNavigation(2, "https://example.com/2", u"Title 2"));
const auto navigations_for_comparison = navigations;
WebContentsStateUnpacked original_unpacked(
kIsOffTheRecord, kCurrentEntryIndex, std::move(navigations));
JNIEnv* env = base::android::AttachCurrentThread();
base::android::ScopedJavaLocalRef<jobject> buffer_obj =
original_unpacked.Pack(env);
ASSERT_TRUE(buffer_obj);
base::span<const uint8_t> buffer_span =
base::android::JavaByteBufferToSpan(env, buffer_obj);
ASSERT_FALSE(buffer_span.empty());
base::TimeTicks start_time = base::TimeTicks::Now();
std::unique_ptr<WebContentsStateUnpacked> unpacked =
WebContentsState::Unpack(buffer_span, kSavedStateVersion);
base::TimeDelta unpack_duration = base::TimeTicks::Now() - start_time;
LOG(INFO) << "Unpack duration: " << unpack_duration.InMicroseconds() << "us";
ASSERT_TRUE(unpacked);
EXPECT_EQ(unpacked->is_off_the_record(), kIsOffTheRecord);
EXPECT_EQ(unpacked->current_entry_index(), kCurrentEntryIndex);
ASSERT_EQ(unpacked->navigations().size(), navigations_for_comparison.size());
for (size_t i = 0; i < unpacked->navigations().size(); ++i) {
ExpectNavigationsEqual(navigations_for_comparison[i],
unpacked->navigations()[i]);
}
size_t unpacked_size = CalculateUnpackedSize(*unpacked);
LOG(INFO) << "Buffer size (bytes): " << buffer_span.size()
<< ", Unpacked size (bytes): " << unpacked_size;
}