1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Foundation/Foundation.h>
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/strings/sys_string_conversions.h"
9 #import "ios/testing/ocmock_complex_type_helper.h"
10 #import "ios/web/navigation/crw_session_entry.h"
11 #include "ios/web/navigation/navigation_item_impl.h"
12 #include "ios/web/public/referrer.h"
13 #import "net/base/mac/url_conversions.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/gtest_mac.h"
16 #include "testing/platform_test.h"
17 #import "third_party/ocmock/OCMock/OCMock.h"
18 #include "third_party/ocmock/gtest_support.h"
19 #include "ui/base/page_transition_types.h"
21 @interface CRWSessionEntry (ExposedForTesting)
22 + (web::PageScrollState)pageStateFromDictionary:(NSDictionary*)dictionary;
23 + (NSDictionary*)dictionaryFromPageDisplayState:
24 (const web::PageDisplayState&)displayState;
27 class CRWSessionEntryTest : public PlatformTest {
29 static void expectEqualSessionEntries(CRWSessionEntry* entry1,
30 CRWSessionEntry* entry2,
31 ui::PageTransition transition);
34 void SetUp() override {
35 GURL url("http://init.test");
36 ui::PageTransition transition =
37 ui::PAGE_TRANSITION_AUTO_BOOKMARK;
38 scoped_ptr<web::NavigationItemImpl> item(new web::NavigationItemImpl());
40 item->SetTransitionType(transition);
41 item->SetTimestamp(base::Time::Now());
42 item->SetPostData([@"Test data" dataUsingEncoding:NSUTF8StringEncoding]);
44 [[CRWSessionEntry alloc] initWithNavigationItem:item.Pass()]);
46 void TearDown() override { sessionEntry_.reset(); }
49 base::scoped_nsobject<CRWSessionEntry> sessionEntry_;
52 @implementation OCMockComplexTypeHelper (CRWSessionEntryTest)
53 typedef void (^encodeBytes_length_forKey_block)(
54 const uint8_t*, NSUInteger, NSString*);
55 - (void)encodeBytes:(const uint8_t*)bytes
56 length:(NSUInteger)length
57 forKey:(NSString*)key {
58 static_cast<encodeBytes_length_forKey_block>([self blockForSelector:_cmd])(
62 typedef const uint8_t* (^decodeBytesForKeyBlock)(NSString*, NSUInteger*);
63 - (const uint8_t*)decodeBytesForKey:(NSString*)key
64 returnedLength:(NSUInteger*)lengthp {
65 return static_cast<decodeBytesForKeyBlock>([self blockForSelector:_cmd])(
70 void CRWSessionEntryTest::expectEqualSessionEntries(
71 CRWSessionEntry* entry1,
72 CRWSessionEntry* entry2,
73 ui::PageTransition transition) {
74 web::NavigationItemImpl* navItem1 = entry1.navigationItemImpl;
75 web::NavigationItemImpl* navItem2 = entry2.navigationItemImpl;
76 // url is not compared because it could differ after copy or archive.
77 EXPECT_EQ(navItem1->GetVirtualURL(), navItem2->GetVirtualURL());
78 EXPECT_EQ(navItem1->GetReferrer().url, navItem2->GetReferrer().url);
79 EXPECT_EQ(navItem1->GetTimestamp(), navItem2->GetTimestamp());
80 EXPECT_EQ(navItem1->GetTitle(), navItem2->GetTitle());
81 EXPECT_EQ(navItem1->GetPageDisplayState(), navItem2->GetPageDisplayState());
82 EXPECT_EQ(navItem1->ShouldSkipResubmitDataConfirmation(),
83 navItem2->ShouldSkipResubmitDataConfirmation());
84 EXPECT_EQ(navItem1->IsOverridingUserAgent(),
85 navItem2->IsOverridingUserAgent());
86 EXPECT_TRUE((!navItem1->HasPostData() && !navItem2->HasPostData()) ||
87 [navItem1->GetPostData() isEqualToData:navItem2->GetPostData()]);
88 EXPECT_EQ(navItem2->GetTransitionType(), transition);
89 EXPECT_NSEQ(navItem1->GetHttpRequestHeaders(),
90 navItem2->GetHttpRequestHeaders());
93 TEST_F(CRWSessionEntryTest, Description) {
94 [sessionEntry_ navigationItem]->SetTitle(base::SysNSStringToUTF16(@"Title"));
95 EXPECT_NSEQ([sessionEntry_ description],
96 @"url:http://init.test/ originalurl:http://init.test/ "
97 @"title:Title transition:2 displayState:{ scrollOffset:(nan, "
98 @"nan), zoomScaleRange:(nan, nan), zoomScale:nan } desktopUA:0");
101 TEST_F(CRWSessionEntryTest, InitWithCoder) {
102 web::NavigationItem* item = [sessionEntry_ navigationItem];
103 item->SetVirtualURL(GURL("http://user.friendly"));
104 item->SetTitle(base::SysNSStringToUTF16(@"Title"));
105 // Old serialized entries have no timestamp.
106 item->SetTimestamp(base::Time::FromInternalValue(0));
108 NSURL* virtualUrl = net::NSURLWithGURL(item->GetVirtualURL());
109 NSURL* referrer = net::NSURLWithGURL(item->GetReferrer().url);
110 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
111 base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
112 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
114 decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
115 NSUInteger* length) {
120 [[[decoder stub] andReturnValue:[NSNumber numberWithBool:NO]]
121 containsValueForKey:[OCMArg any]];
123 [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
124 callBlockExpectation:block];
125 [[[decoder expect] andReturn:virtualUrl]
126 decodeObjectForKey:web::kSessionEntryURLDeperecatedKey];
127 [[[decoder expect] andReturn:referrer]
128 decodeObjectForKey:web::kSessionEntryReferrerURLDeprecatedKey];
129 [[[decoder expect] andReturn:title]
130 decodeObjectForKey:web::kSessionEntryTitleKey];
131 const web::PageDisplayState& pageState =
132 [sessionEntry_ navigationItem]->GetPageDisplayState();
133 NSDictionary* serializedPageDisplayState =
134 [CRWSessionEntry dictionaryFromPageDisplayState:pageState];
135 [[[decoder expect] andReturn:serializedPageDisplayState]
136 decodeObjectForKey:web::kSessionEntryPageScrollStateKey];
137 BOOL useDesktopUserAgent =
138 [sessionEntry_ navigationItem]->IsOverridingUserAgent();
139 [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
140 decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey];
141 NSDictionary* requestHeaders =
142 [sessionEntry_ navigationItem]->GetHttpRequestHeaders();
143 [[[decoder expect] andReturn:requestHeaders]
144 decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey];
146 andReturn:[sessionEntry_ navigationItemImpl]->GetPostData()]
147 decodeObjectForKey:web::kSessionEntryPOSTDataKey];
148 BOOL skipResubmitDataConfirmation =
149 [sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
150 [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
151 decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey];
153 base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
154 [[CRWSessionEntry alloc] initWithCoder:decoder]);
155 web::NavigationItem* newItem = [newSessionEntry navigationItem];
157 EXPECT_OCMOCK_VERIFY(decoder);
158 expectEqualSessionEntries(sessionEntry_, newSessionEntry,
159 ui::PAGE_TRANSITION_RELOAD);
160 EXPECT_NE(item->GetURL(), newItem->GetURL());
161 EXPECT_EQ(item->GetVirtualURL(), newItem->GetURL());
164 TEST_F(CRWSessionEntryTest, InitWithCoderNewStyle) {
165 web::NavigationItem* item = [sessionEntry_ navigationItem];
166 item->SetVirtualURL(GURL("http://user.friendly"));
167 item->SetTitle(base::SysNSStringToUTF16(@"Title"));
168 int64 timestamp = item->GetTimestamp().ToInternalValue();
170 std::string virtualUrl = item->GetVirtualURL().spec();
171 std::string referrerUrl = item->GetReferrer().url.spec();
172 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
173 base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
174 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
176 const std::string emptyString;
177 decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
178 NSUInteger* length) {
179 const std::string *value = &emptyString;
180 if ([key isEqualToString:web::kSessionEntryURLKey])
182 else if ([key isEqualToString:web::kSessionEntryReferrerURLKey])
183 value = &referrerUrl;
187 *length = value->size();
188 return reinterpret_cast<const uint8_t*>(value->data());
191 [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
192 callBlockExpectation:block];
193 [[[decoder stub] andReturnValue:[NSNumber numberWithBool:YES]]
194 containsValueForKey:[OCMArg any]];
195 web::ReferrerPolicy expectedPolicy = item->GetReferrer().policy;
196 [[[decoder expect] andReturnValue:OCMOCK_VALUE(expectedPolicy)]
197 decodeIntForKey:web::kSessionEntryReferrerPolicyKey];
198 [[[decoder expect] andReturnValue:OCMOCK_VALUE(timestamp)]
199 decodeInt64ForKey:web::kSessionEntryTimestampKey];
200 [[[decoder expect] andReturn:title]
201 decodeObjectForKey:web::kSessionEntryTitleKey];
202 const web::PageDisplayState& pageState =
203 [sessionEntry_ navigationItem]->GetPageDisplayState();
204 NSDictionary* serializedPageDisplayState =
205 [CRWSessionEntry dictionaryFromPageDisplayState:pageState];
206 [[[decoder expect] andReturn:serializedPageDisplayState]
207 decodeObjectForKey:web::kSessionEntryPageScrollStateKey];
208 BOOL useDesktopUserAgent =
209 [sessionEntry_ navigationItem]->IsOverridingUserAgent();
210 [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
211 decodeBoolForKey:web::kSessionEntryUseDesktopUserAgentKey];
212 NSDictionary* requestHeaders =
213 [sessionEntry_ navigationItem]->GetHttpRequestHeaders();
214 [[[decoder expect] andReturn:requestHeaders]
215 decodeObjectForKey:web::kSessionEntryHTTPRequestHeadersKey];
216 NSData* POSTData = [sessionEntry_ navigationItemImpl]->GetPostData();
217 [[[decoder expect] andReturn:POSTData]
218 decodeObjectForKey:web::kSessionEntryPOSTDataKey];
219 BOOL skipResubmitDataConfirmation =
220 [sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
221 [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
222 decodeBoolForKey:web::kSessionEntrySkipResubmitConfirmationKey];
224 base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
225 [[CRWSessionEntry alloc] initWithCoder:decoder]);
226 web::NavigationItem* newItem = [newSessionEntry navigationItem];
228 EXPECT_OCMOCK_VERIFY(decoder);
229 expectEqualSessionEntries(sessionEntry_, newSessionEntry,
230 ui::PAGE_TRANSITION_RELOAD);
231 EXPECT_NE(item->GetURL(), newItem->GetURL());
232 EXPECT_EQ(item->GetVirtualURL(), newItem->GetVirtualURL());
235 TEST_F(CRWSessionEntryTest, EncodeDecode) {
237 [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
238 id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
240 expectEqualSessionEntries(sessionEntry_, decoded,
241 ui::PAGE_TRANSITION_RELOAD);
244 TEST_F(CRWSessionEntryTest, EncodeWithCoder) {
245 web::NavigationItem* item = [sessionEntry_ navigationItem];
246 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
248 base::scoped_nsobject<id> coder([[OCMockComplexTypeHelper alloc]
249 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
251 encodeBytes_length_forKey_block block =
252 ^(const uint8_t* bytes, NSUInteger length, NSString* key) {
253 if ([key isEqualToString:web::kSessionEntryURLKey]) {
254 ASSERT_EQ(item->GetVirtualURL().spec(),
255 std::string(reinterpret_cast<const char*>(bytes), length));
257 } else if ([key isEqualToString:web::kSessionEntryReferrerURLKey]) {
258 ASSERT_EQ(item->GetReferrer().url.spec(),
259 std::string(reinterpret_cast<const char*>(bytes), length));
264 [coder onSelector:@selector(encodeBytes:length:forKey:)
265 callBlockExpectation:block];
266 [[coder expect] encodeInt:item->GetReferrer().policy
267 forKey:web::kSessionEntryReferrerPolicyKey];
268 [[coder expect] encodeInt64:item->GetTimestamp().ToInternalValue()
269 forKey:web::kSessionEntryTimestampKey];
270 [[coder expect] encodeObject:title forKey:web::kSessionEntryTitleKey];
271 const web::PageDisplayState& pageState =
272 [sessionEntry_ navigationItem]->GetPageDisplayState();
273 NSDictionary* serializedPageDisplayState =
274 [CRWSessionEntry dictionaryFromPageDisplayState:pageState];
275 [[coder expect] encodeObject:serializedPageDisplayState
276 forKey:web::kSessionEntryPageScrollStateKey];
277 BOOL useDesktopUserAgent =
278 [sessionEntry_ navigationItem]->IsOverridingUserAgent();
279 [[coder expect] encodeBool:useDesktopUserAgent
280 forKey:web::kSessionEntryUseDesktopUserAgentKey];
281 NSDictionary* requestHeaders =
282 [sessionEntry_ navigationItem]->GetHttpRequestHeaders();
283 [[coder expect] encodeObject:requestHeaders
284 forKey:web::kSessionEntryHTTPRequestHeadersKey];
285 [[coder expect] encodeObject:[sessionEntry_ navigationItemImpl]->GetPostData()
286 forKey:web::kSessionEntryPOSTDataKey];
287 BOOL skipResubmitDataConfirmation =
288 [sessionEntry_ navigationItemImpl]->ShouldSkipResubmitDataConfirmation();
289 [[coder expect] encodeBool:skipResubmitDataConfirmation
290 forKey:web::kSessionEntrySkipResubmitConfirmationKey];
291 [sessionEntry_ encodeWithCoder:coder];
292 EXPECT_OCMOCK_VERIFY(coder);
295 TEST_F(CRWSessionEntryTest, CodingEncoding) {
296 web::NavigationItem* item = [sessionEntry_ navigationItem];
297 item->SetVirtualURL(GURL("http://user.friendly"));
298 NSData* data = [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
299 EXPECT_TRUE(data != nil);
300 CRWSessionEntry* unarchivedSessionEntry =
301 [NSKeyedUnarchiver unarchiveObjectWithData:data];
302 ASSERT_TRUE(unarchivedSessionEntry != nil);
303 web::NavigationItem* unarchivedItem = [unarchivedSessionEntry navigationItem];
304 expectEqualSessionEntries(sessionEntry_, unarchivedSessionEntry,
305 ui::PAGE_TRANSITION_RELOAD);
306 EXPECT_EQ(unarchivedItem->GetURL(), item->GetVirtualURL());
307 EXPECT_NE(unarchivedItem->GetURL(), item->GetURL());
310 TEST_F(CRWSessionEntryTest, CopyWithZone) {
311 CRWSessionEntry* sessionEntry2 = [sessionEntry_ copy];
312 EXPECT_NE(sessionEntry_, sessionEntry2);
313 expectEqualSessionEntries(
314 sessionEntry_, sessionEntry2,
315 [sessionEntry_ navigationItem]->GetTransitionType());
318 TEST_F(CRWSessionEntryTest, EmptyVirtualUrl) {
319 EXPECT_EQ(GURL("http://init.test/"),
320 [sessionEntry_ navigationItem]->GetURL());
323 TEST_F(CRWSessionEntryTest, NonEmptyVirtualUrl) {
324 web::NavigationItem* item = [sessionEntry_ navigationItem];
325 item->SetVirtualURL(GURL("http://user.friendly"));
326 EXPECT_EQ(GURL("http://user.friendly/"), item->GetVirtualURL());
327 EXPECT_EQ(GURL("http://init.test/"), item->GetURL());
330 TEST_F(CRWSessionEntryTest, EmptyDescription) {
331 EXPECT_GT([[sessionEntry_ description] length], 0U);