blob: ec1a8bb0067286dcadcd15319120833b8506674b [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web/navigation/crw_session_entry.h"
#import <Foundation/Foundation.h>
#include <stdint.h>
#include <utility>
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/testing/ocmock_complex_type_helper.h"
#include "ios/web/navigation/navigation_item_impl.h"
#include "ios/web/public/referrer.h"
#import "net/base/mac/url_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#include "ui/base/page_transition_types.h"
@interface CRWSessionEntry (ExposedForTesting)
+ (web::PageScrollState)pageStateFromDictionary:(NSDictionary*)dictionary;
+ (NSDictionary*)dictionaryFromPageDisplayState:
(const web::PageDisplayState&)displayState;
@end
class CRWSessionEntryTest : public PlatformTest {
public:
static void expectEqualSessionEntries(CRWSessionEntry* entry1,
CRWSessionEntry* entry2,
ui::PageTransition transition);
protected:
void SetUp() override {
GURL url("http://init.test");
ui::PageTransition transition =
ui::PAGE_TRANSITION_AUTO_BOOKMARK;
std::unique_ptr<web::NavigationItemImpl> item(
new web::NavigationItemImpl());
item->SetURL(url);
item->SetTransitionType(transition);
item->SetTimestamp(base::Time::Now());
item->SetPostData([@"Test data" dataUsingEncoding:NSUTF8StringEncoding]);
sessionEntry_.reset(
[[CRWSessionEntry alloc] initWithNavigationItem:std::move(item)]);
}
void TearDown() override { sessionEntry_.reset(); }
protected:
base::scoped_nsobject<CRWSessionEntry> sessionEntry_;
};
@implementation OCMockComplexTypeHelper (CRWSessionEntryTest)
typedef void (^encodeBytes_length_forKey_block)(
const uint8_t*, NSUInteger, NSString*);
- (void)encodeBytes:(const uint8_t*)bytes
length:(NSUInteger)length
forKey:(NSString*)key {
static_cast<encodeBytes_length_forKey_block>([self blockForSelector:_cmd])(
bytes, length, key);
}
typedef const uint8_t* (^decodeBytesForKeyBlock)(NSString*, NSUInteger*);
- (const uint8_t*)decodeBytesForKey:(NSString*)key
returnedLength:(NSUInteger*)lengthp {
return static_cast<decodeBytesForKeyBlock>([self blockForSelector:_cmd])(
key, lengthp);
}
@end
void CRWSessionEntryTest::expectEqualSessionEntries(
CRWSessionEntry* entry1,
CRWSessionEntry* entry2,
ui::PageTransition transition) {
web::NavigationItemImpl* navItem1 = entry1.navigationItemImpl;
web::NavigationItemImpl* navItem2 = entry2.navigationItemImpl;
// url is not compared because it could differ after copy or archive.
EXPECT_EQ(navItem1->GetVirtualURL(), navItem2->GetVirtualURL());
EXPECT_EQ(navItem1->GetReferrer().url, navItem2->GetReferrer().url);
EXPECT_EQ(navItem1->GetTimestamp(), navItem2->GetTimestamp());
EXPECT_EQ(navItem1->GetTitle(), navItem2->GetTitle());
EXPECT_EQ(navItem1->GetPageDisplayState(), navItem2->GetPageDisplayState());
EXPECT_EQ(navItem1->ShouldSkipResubmitDataConfirmation(),
navItem2->ShouldSkipResubmitDataConfirmation());
EXPECT_EQ(navItem1->IsOverridingUserAgent(),
navItem2->IsOverridingUserAgent());
EXPECT_TRUE((!navItem1->HasPostData() && !navItem2->HasPostData()) ||
[navItem1->GetPostData() isEqualToData:navItem2->GetPostData()]);
EXPECT_TRUE(ui::PageTransitionTypeIncludingQualifiersIs(
navItem2->GetTransitionType(), transition));
EXPECT_NSEQ(navItem1->GetHttpRequestHeaders(),
navItem2->GetHttpRequestHeaders());
}
TEST_F(CRWSessionEntryTest, Description) {
[sessionEntry_ navigationItem]->SetTitle(base::SysNSStringToUTF16(@"Title"));
EXPECT_NSEQ([sessionEntry_ description],
@"url:http://init.test/ originalurl:http://init.test/ "
@"title:Title transition:2 displayState:{ scrollOffset:(nan, "
@"nan), zoomScaleRange:(nan, nan), zoomScale:nan } desktopUA:0");
}
TEST_F(CRWSessionEntryTest, InitWithCoder) {
web::NavigationItem* item = [sessionEntry_ navigationItem];
item->SetVirtualURL(GURL("http://user.friendly"));
item->SetTitle(base::SysNSStringToUTF16(@"Title"));
// Old serialized entries have no timestamp.
item->SetTimestamp(base::Time::FromInternalValue(0));
NSURL* virtualUrl = net::NSURLWithGURL(item->GetVirtualURL());
NSURL* referrer = net::NSURLWithGURL(item->GetReferrer().url);
NSString* title = base::SysUTF16ToNSString(item->GetTitle());
base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
NSUInteger* length) {
*length = 0;
return NULL;
};
[[[decoder stub] andReturnValue:[NSNumber numberWithBool:NO]]
containsValueForKey:[OCMArg any]];
[decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
callBlockExpectation:block];
[[[decoder expect] andReturn:virtualUrl]
decodeObjectForKey:web::kSessionEntryURLDeperecatedKey];
[[[decoder expect] andReturn:referrer]
decodeObjectForKey:web::kSessionEntryReferrerURLDeprecatedKey];
[[[decoder expect] andReturn:title]
decodeObjectForKey:web::kSessionEntryTitleKey];
const web::PageDisplayState& pageState =
[sessionEntry_ navigationItem]->GetPageDisplayState();
NSDictionary* serializedPageDisplayState =
[CRWSessionEntry dictionaryFromPageDisplayState:pageState];
[[[decoder expect] andReturn:serializedPageDisplayState]
decodeObjectForKey:web::kSessionEntryPageScrollStateKey];
BOOL useDesktopUserAgent =
[sessionEntry_ navigationItem]->IsOverridingUserAgent();
[[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey];
NSDictionary* requestHeaders =
[sessionEntry_ navigationItem]->GetHttpRequestHeaders();
[[[decoder expect] andReturn:requestHeaders]
decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey];
[[[decoder expect]
andReturn:[sessionEntry_ navigationItemImpl]->GetPostData()]
decodeObjectForKey:web::kSessionEntryPOSTDataKey];
BOOL skipResubmitDataConfirmation =
[sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
[[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey];
base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
[[CRWSessionEntry alloc] initWithCoder:decoder]);
web::NavigationItem* newItem = [newSessionEntry navigationItem];
EXPECT_OCMOCK_VERIFY(decoder);
expectEqualSessionEntries(sessionEntry_, newSessionEntry,
ui::PAGE_TRANSITION_RELOAD);
EXPECT_NE(item->GetURL(), newItem->GetURL());
EXPECT_EQ(item->GetVirtualURL(), newItem->GetURL());
}
TEST_F(CRWSessionEntryTest, InitWithCoderNewStyle) {
web::NavigationItem* item = [sessionEntry_ navigationItem];
item->SetVirtualURL(GURL("http://user.friendly"));
item->SetTitle(base::SysNSStringToUTF16(@"Title"));
int64_t timestamp = item->GetTimestamp().ToInternalValue();
std::string virtualUrl = item->GetVirtualURL().spec();
std::string referrerUrl = item->GetReferrer().url.spec();
NSString* title = base::SysUTF16ToNSString(item->GetTitle());
base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
const std::string emptyString;
decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
NSUInteger* length) {
const std::string *value = &emptyString;
if ([key isEqualToString:web::kSessionEntryURLKey])
value = &virtualUrl;
else if ([key isEqualToString:web::kSessionEntryReferrerURLKey])
value = &referrerUrl;
else
EXPECT_TRUE(false);
*length = value->size();
return reinterpret_cast<const uint8_t*>(value->data());
};
[decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
callBlockExpectation:block];
[[[decoder stub] andReturnValue:[NSNumber numberWithBool:YES]]
containsValueForKey:[OCMArg any]];
web::ReferrerPolicy expectedPolicy = item->GetReferrer().policy;
[[[decoder expect] andReturnValue:OCMOCK_VALUE(expectedPolicy)]
decodeIntForKey:web::kSessionEntryReferrerPolicyKey];
[[[decoder expect] andReturnValue:OCMOCK_VALUE(timestamp)]
decodeInt64ForKey:web::kSessionEntryTimestampKey];
[[[decoder expect] andReturn:title]
decodeObjectForKey:web::kSessionEntryTitleKey];
const web::PageDisplayState& pageState =
[sessionEntry_ navigationItem]->GetPageDisplayState();
NSDictionary* serializedPageDisplayState =
[CRWSessionEntry dictionaryFromPageDisplayState:pageState];
[[[decoder expect] andReturn:serializedPageDisplayState]
decodeObjectForKey:web::kSessionEntryPageScrollStateKey];
BOOL useDesktopUserAgent =
[sessionEntry_ navigationItem]->IsOverridingUserAgent();
[[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey];
NSDictionary* requestHeaders =
[sessionEntry_ navigationItem]->GetHttpRequestHeaders();
[[[decoder expect] andReturn:requestHeaders]
decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey];
NSData* POSTData = [sessionEntry_ navigationItemImpl]->GetPostData();
[[[decoder expect] andReturn:POSTData]
decodeObjectForKey:web::kSessionEntryPOSTDataKey];
BOOL skipResubmitDataConfirmation =
[sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
[[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey];
base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
[[CRWSessionEntry alloc] initWithCoder:decoder]);
web::NavigationItem* newItem = [newSessionEntry navigationItem];
EXPECT_OCMOCK_VERIFY(decoder);
expectEqualSessionEntries(sessionEntry_, newSessionEntry,
ui::PAGE_TRANSITION_RELOAD);
EXPECT_NE(item->GetURL(), newItem->GetURL());
EXPECT_EQ(item->GetVirtualURL(), newItem->GetVirtualURL());
}
TEST_F(CRWSessionEntryTest, EncodeDecode) {
NSData *data =
[NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
expectEqualSessionEntries(sessionEntry_, decoded,
ui::PAGE_TRANSITION_RELOAD);
}
TEST_F(CRWSessionEntryTest, EncodeWithCoder) {
web::NavigationItem* item = [sessionEntry_ navigationItem];
NSString* title = base::SysUTF16ToNSString(item->GetTitle());
base::scoped_nsobject<id> coder([[OCMockComplexTypeHelper alloc]
initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
encodeBytes_length_forKey_block block =
^(const uint8_t* bytes, NSUInteger length, NSString* key) {
if ([key isEqualToString:web::kSessionEntryURLKey]) {
ASSERT_EQ(item->GetVirtualURL().spec(),
std::string(reinterpret_cast<const char*>(bytes), length));
return;
} else if ([key isEqualToString:web::kSessionEntryReferrerURLKey]) {
ASSERT_EQ(item->GetReferrer().url.spec(),
std::string(reinterpret_cast<const char*>(bytes), length));
return;
}
FAIL();
};
[coder onSelector:@selector(encodeBytes:length:forKey:)
callBlockExpectation:block];
[[coder expect] encodeInt:item->GetReferrer().policy
forKey:web::kSessionEntryReferrerPolicyKey];
[[coder expect] encodeInt64:item->GetTimestamp().ToInternalValue()
forKey:web::kSessionEntryTimestampKey];
[[coder expect] encodeObject:title forKey:web::kSessionEntryTitleKey];
const web::PageDisplayState& pageState =
[sessionEntry_ navigationItem]->GetPageDisplayState();
NSDictionary* serializedPageDisplayState =
[CRWSessionEntry dictionaryFromPageDisplayState:pageState];
[[coder expect] encodeObject:serializedPageDisplayState
forKey:web::kSessionEntryPageScrollStateKey];
BOOL useDesktopUserAgent =
[sessionEntry_ navigationItem]->IsOverridingUserAgent();
[[coder expect] encodeBool:useDesktopUserAgent
forKey:web::kSessionEntryUseDesktopUserAgentKey];
NSDictionary* requestHeaders =
[sessionEntry_ navigationItem]->GetHttpRequestHeaders();
[[coder expect] encodeObject:requestHeaders
forKey:web::kSessionEntryHTTPRequestHeadersKey];
[[coder expect] encodeObject:[sessionEntry_ navigationItemImpl]->GetPostData()
forKey:web::kSessionEntryPOSTDataKey];
BOOL skipResubmitDataConfirmation =
[sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
[[coder expect] encodeBool:skipResubmitDataConfirmation
forKey:web::kSessionEntrySkipResubmitConfirmationKey];
[sessionEntry_ encodeWithCoder:coder];
EXPECT_OCMOCK_VERIFY(coder);
}
TEST_F(CRWSessionEntryTest, CodingEncoding) {
web::NavigationItem* item = [sessionEntry_ navigationItem];
item->SetVirtualURL(GURL("http://user.friendly"));
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
EXPECT_TRUE(data != nil);
CRWSessionEntry* unarchivedSessionEntry =
[NSKeyedUnarchiver unarchiveObjectWithData:data];
ASSERT_TRUE(unarchivedSessionEntry != nil);
web::NavigationItem* unarchivedItem = [unarchivedSessionEntry navigationItem];
expectEqualSessionEntries(sessionEntry_, unarchivedSessionEntry,
ui::PAGE_TRANSITION_RELOAD);
EXPECT_EQ(unarchivedItem->GetURL(), item->GetVirtualURL());
EXPECT_NE(unarchivedItem->GetURL(), item->GetURL());
}
TEST_F(CRWSessionEntryTest, CopyWithZone) {
CRWSessionEntry* sessionEntry2 = [sessionEntry_ copy];
EXPECT_NE(sessionEntry_, sessionEntry2);
expectEqualSessionEntries(
sessionEntry_, sessionEntry2,
[sessionEntry_ navigationItem]->GetTransitionType());
}
TEST_F(CRWSessionEntryTest, EmptyVirtualUrl) {
EXPECT_EQ(GURL("http://init.test/"),
[sessionEntry_ navigationItem]->GetURL());
}
TEST_F(CRWSessionEntryTest, NonEmptyVirtualUrl) {
web::NavigationItem* item = [sessionEntry_ navigationItem];
item->SetVirtualURL(GURL("http://user.friendly"));
EXPECT_EQ(GURL("http://user.friendly/"), item->GetVirtualURL());
EXPECT_EQ(GURL("http://init.test/"), item->GetURL());
}
TEST_F(CRWSessionEntryTest, EmptyDescription) {
EXPECT_GT([[sessionEntry_ description] length], 0U);
}