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)scrollStateFromDictionary:(NSDictionary*)dictionary;
23 + (NSDictionary*)dictionaryFromScrollState:
24 (const web::PageScrollState&)scrollState;
27 static NSString* const kHTTPHeaderKey1 = @"key1";
28 static NSString* const kHTTPHeaderKey2 = @"key2";
29 static NSString* const kHTTPHeaderValue1 = @"value1";
30 static NSString* const kHTTPHeaderValue2 = @"value2";
32 class CRWSessionEntryTest : public PlatformTest {
34 static void expectEqualSessionEntries(CRWSessionEntry* entry1,
35 CRWSessionEntry* entry2,
36 ui::PageTransition transition);
39 void SetUp() override {
40 GURL url("http://init.test");
41 ui::PageTransition transition =
42 ui::PAGE_TRANSITION_AUTO_BOOKMARK;
43 sessionEntry_.reset([[CRWSessionEntry alloc] initWithUrl:url
44 referrer:web::Referrer()
46 useDesktopUserAgent:NO
47 rendererInitiated:NO]);
48 [sessionEntry_ navigationItem]->SetTimestamp(base::Time::Now());
49 [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey1 : kHTTPHeaderValue1 }];
51 setPOSTData:[@"Test data" dataUsingEncoding:NSUTF8StringEncoding]];
53 void TearDown() override { sessionEntry_.reset(); }
56 base::scoped_nsobject<CRWSessionEntry> sessionEntry_;
59 @implementation OCMockComplexTypeHelper (CRWSessionEntryTest)
60 typedef void (^encodeBytes_length_forKey_block)(
61 const uint8_t*, NSUInteger, NSString*);
62 - (void)encodeBytes:(const uint8_t*)bytes
63 length:(NSUInteger)length
64 forKey:(NSString*)key {
65 static_cast<encodeBytes_length_forKey_block>([self blockForSelector:_cmd])(
69 typedef const uint8_t* (^decodeBytesForKeyBlock)(NSString*, NSUInteger*);
70 - (const uint8_t*)decodeBytesForKey:(NSString*)key
71 returnedLength:(NSUInteger*)lengthp {
72 return static_cast<decodeBytesForKeyBlock>([self blockForSelector:_cmd])(
77 void CRWSessionEntryTest::expectEqualSessionEntries(
78 CRWSessionEntry* entry1,
79 CRWSessionEntry* entry2,
80 ui::PageTransition transition) {
81 EXPECT_EQ(entry1.index, entry2.index);
82 web::NavigationItem* navItem1 = entry1.navigationItem;
83 web::NavigationItem* navItem2 = entry2.navigationItem;
84 // url is not compared because it could differ after copy or archive.
85 EXPECT_EQ(navItem1->GetVirtualURL(), navItem2->GetVirtualURL());
86 EXPECT_EQ(navItem1->GetReferrer().url, navItem2->GetReferrer().url);
87 EXPECT_EQ(navItem1->GetTimestamp(), navItem2->GetTimestamp());
88 EXPECT_EQ(navItem1->GetTitle(), navItem2->GetTitle());
89 EXPECT_EQ(navItem1->GetPageScrollState(), navItem2->GetPageScrollState());
90 EXPECT_EQ(entry1.useDesktopUserAgent, entry2.useDesktopUserAgent);
91 EXPECT_EQ(entry1.usedDataReductionProxy, entry2.usedDataReductionProxy);
92 EXPECT_EQ(navItem2->GetTransitionType(), transition);
93 EXPECT_NSEQ(entry1.httpHeaders, entry2.httpHeaders);
94 EXPECT_TRUE((!entry1.POSTData && !entry2.POSTData) ||
95 [entry1.POSTData isEqualToData:entry2.POSTData]);
96 EXPECT_EQ(entry1.skipResubmitDataConfirmation,
97 entry2.skipResubmitDataConfirmation);
100 TEST_F(CRWSessionEntryTest, Description) {
101 [sessionEntry_ navigationItem]->SetTitle(base::SysNSStringToUTF16(@"Title"));
102 EXPECT_NSEQ([sessionEntry_ description], @"url:http://init.test/ "
103 @"originalurl:http://init.test/ " @"title:Title " @"transition:2 "
104 @"scrollState:{ scrollOffset:(nan, nan), zoomScaleRange:(nan, "
105 @"nan), zoomScale:nan } " @"desktopUA:0 " @"proxy:0");
108 TEST_F(CRWSessionEntryTest, InitWithCoder) {
109 web::NavigationItem* item = [sessionEntry_ navigationItem];
110 item->SetVirtualURL(GURL("http://user.friendly"));
111 item->SetTitle(base::SysNSStringToUTF16(@"Title"));
112 int index = sessionEntry_.get().index;
113 // Old serialized entries have no timestamp.
114 item->SetTimestamp(base::Time::FromInternalValue(0));
116 NSURL* virtualUrl = net::NSURLWithGURL(item->GetVirtualURL());
117 NSURL* referrer = net::NSURLWithGURL(item->GetReferrer().url);
118 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
119 base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
120 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
122 decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
123 NSUInteger* length) {
128 [[[decoder stub] andReturnValue:[NSNumber numberWithBool:NO]]
129 containsValueForKey:[OCMArg any]];
131 [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
132 callBlockExpectation:block];
133 [[[decoder expect] andReturnValue:OCMOCK_VALUE(index)]
134 decodeIntForKey:@"index"];
135 [[[decoder expect] andReturn:virtualUrl]
136 decodeObjectForKey:@"virtualUrl"];
137 [[[decoder expect] andReturn:referrer]
138 decodeObjectForKey:@"referrer"];
139 [[[decoder expect] andReturn:title]
140 decodeObjectForKey:@"title"];
141 const web::PageScrollState& scrollState =
142 [sessionEntry_ navigationItem]->GetPageScrollState();
143 NSDictionary* serializedScrollState =
144 [CRWSessionEntry dictionaryFromScrollState:scrollState];
145 [[[decoder expect] andReturn:serializedScrollState]
146 decodeObjectForKey:@"state"];
147 BOOL useDesktopUserAgent = sessionEntry_.get().useDesktopUserAgent;
148 [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
149 decodeBoolForKey:@"useDesktopUserAgent"];
150 BOOL usedDataReductionProxy = sessionEntry_.get().usedDataReductionProxy;
151 [[[decoder expect] andReturnValue:OCMOCK_VALUE(usedDataReductionProxy)]
152 decodeBoolForKey:@"usedDataReductionProxy"];
153 [[[decoder expect] andReturn:sessionEntry_.get().httpHeaders]
154 decodeObjectForKey:@"httpHeaders"];
155 [[[decoder expect] andReturn:sessionEntry_.get().POSTData]
156 decodeObjectForKey:@"POSTData"];
157 BOOL skipResubmitDataConfirmation =
158 sessionEntry_.get().skipResubmitDataConfirmation;
159 [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
160 decodeBoolForKey:@"skipResubmitDataConfirmation"];
162 base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
163 [[CRWSessionEntry alloc] initWithCoder:decoder]);
164 web::NavigationItem* newItem = [newSessionEntry navigationItem];
166 EXPECT_OCMOCK_VERIFY(decoder);
167 expectEqualSessionEntries(sessionEntry_, newSessionEntry,
168 ui::PAGE_TRANSITION_RELOAD);
169 EXPECT_NE(item->GetURL(), newItem->GetURL());
170 EXPECT_EQ(item->GetVirtualURL(), newItem->GetURL());
173 TEST_F(CRWSessionEntryTest, InitWithCoderNewStyle) {
174 web::NavigationItem* item = [sessionEntry_ navigationItem];
175 item->SetVirtualURL(GURL("http://user.friendly"));
176 item->SetTitle(base::SysNSStringToUTF16(@"Title"));
177 int index = sessionEntry_.get().index;
178 int64 timestamp = item->GetTimestamp().ToInternalValue();
180 std::string virtualUrl = item->GetVirtualURL().spec();
181 std::string referrerUrl = item->GetReferrer().url.spec();
182 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
183 base::scoped_nsobject<id> decoder([[OCMockComplexTypeHelper alloc]
184 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
186 const std::string emptyString;
187 decodeBytesForKeyBlock block = ^ const uint8_t* (NSString* key,
188 NSUInteger* length) {
189 const std::string *value = &emptyString;
190 if ([key isEqualToString:@"virtualUrlString"])
192 else if ([key isEqualToString:@"referrerUrlString"])
193 value = &referrerUrl;
197 *length = value->size();
198 return reinterpret_cast<const uint8_t*>(value->data());
201 [decoder onSelector:@selector(decodeBytesForKey:returnedLength:)
202 callBlockExpectation:block];
203 [[[decoder stub] andReturnValue:[NSNumber numberWithBool:YES]]
204 containsValueForKey:[OCMArg any]];
205 [[[decoder expect] andReturnValue:OCMOCK_VALUE(index)]
206 decodeIntForKey:@"index"];
207 web::ReferrerPolicy expectedPolicy = item->GetReferrer().policy;
209 andReturnValue:OCMOCK_VALUE(expectedPolicy)]
210 decodeIntForKey:@"referrerPolicy"];
211 [[[decoder expect] andReturnValue:OCMOCK_VALUE(timestamp)]
212 decodeInt64ForKey:@"timestamp"];
213 [[[decoder expect] andReturn:title]
214 decodeObjectForKey:@"title"];
215 const web::PageScrollState& scrollState =
216 [sessionEntry_ navigationItem]->GetPageScrollState();
217 NSDictionary* serializedScrollState =
218 [CRWSessionEntry dictionaryFromScrollState:scrollState];
219 [[[decoder expect] andReturn:serializedScrollState]
220 decodeObjectForKey:@"state"];
221 BOOL useDesktopUserAgent = sessionEntry_.get().useDesktopUserAgent;
222 [[[decoder expect] andReturnValue:OCMOCK_VALUE(useDesktopUserAgent)]
223 decodeBoolForKey:@"useDesktopUserAgent"];
224 BOOL usedDataReductionProxy = sessionEntry_.get().usedDataReductionProxy;
225 [[[decoder expect] andReturnValue:OCMOCK_VALUE(usedDataReductionProxy)]
226 decodeBoolForKey:@"usedDataReductionProxy"];
227 [[[decoder expect] andReturn:sessionEntry_.get().httpHeaders]
228 decodeObjectForKey:@"httpHeaders"];
229 [[[decoder expect] andReturn:sessionEntry_.get().POSTData]
230 decodeObjectForKey:@"POSTData"];
231 BOOL skipResubmitDataConfirmation =
232 sessionEntry_.get().skipResubmitDataConfirmation;
233 [[[decoder expect] andReturnValue:OCMOCK_VALUE(skipResubmitDataConfirmation)]
234 decodeBoolForKey:@"skipResubmitDataConfirmation"];
236 base::scoped_nsobject<CRWSessionEntry> newSessionEntry(
237 [[CRWSessionEntry alloc] initWithCoder:decoder]);
238 web::NavigationItem* newItem = [newSessionEntry navigationItem];
240 EXPECT_OCMOCK_VERIFY(decoder);
241 expectEqualSessionEntries(sessionEntry_, newSessionEntry,
242 ui::PAGE_TRANSITION_RELOAD);
243 EXPECT_NE(item->GetURL(), newItem->GetURL());
244 EXPECT_EQ(item->GetVirtualURL(), newItem->GetVirtualURL());
247 TEST_F(CRWSessionEntryTest, EncodeDecode) {
249 [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
250 id decoded = [NSKeyedUnarchiver unarchiveObjectWithData:data];
252 expectEqualSessionEntries(sessionEntry_, decoded,
253 ui::PAGE_TRANSITION_RELOAD);
256 TEST_F(CRWSessionEntryTest, EncodeWithCoder) {
257 web::NavigationItem* item = [sessionEntry_ navigationItem];
258 NSString* title = base::SysUTF16ToNSString(item->GetTitle());
260 base::scoped_nsobject<id> coder([[OCMockComplexTypeHelper alloc]
261 initWithRepresentedObject:[OCMockObject mockForClass:[NSCoder class]]]);
263 encodeBytes_length_forKey_block block = ^(const uint8_t* bytes,
266 if ([key isEqualToString:@"virtualUrlString"]) {
267 ASSERT_EQ(item->GetVirtualURL().spec(),
268 std::string(reinterpret_cast<const char*>(bytes), length));
270 } else if ([key isEqualToString:@"referrerUrlString"]) {
271 ASSERT_EQ(item->GetReferrer().url.spec(),
272 std::string(reinterpret_cast<const char*>(bytes), length));
277 [coder onSelector:@selector(encodeBytes:length:forKey:)
278 callBlockExpectation:block];
279 [[coder expect] encodeInt:[sessionEntry_ index] forKey:@"index"];
280 [[coder expect] encodeInt:item->GetReferrer().policy
281 forKey:@"referrerPolicy"];
282 [[coder expect] encodeInt64:item->GetTimestamp().ToInternalValue()
283 forKey:@"timestamp"];
284 [[coder expect] encodeObject:title forKey:@"title"];
285 const web::PageScrollState& scrollState =
286 [sessionEntry_ navigationItem]->GetPageScrollState();
287 NSDictionary* serializedScrollState =
288 [CRWSessionEntry dictionaryFromScrollState:scrollState];
289 [[coder expect] encodeObject:serializedScrollState forKey:@"state"];
290 [[coder expect] encodeBool:[sessionEntry_ useDesktopUserAgent]
291 forKey:@"useDesktopUserAgent"];
292 [[coder expect] encodeBool:[sessionEntry_ usedDataReductionProxy]
293 forKey:@"usedDataReductionProxy"];
294 [[coder expect] encodeObject:[sessionEntry_ httpHeaders]
295 forKey:@"httpHeaders"];
296 [[coder expect] encodeObject:[sessionEntry_ POSTData] forKey:@"POSTData"];
297 [[coder expect] encodeBool:[sessionEntry_ skipResubmitDataConfirmation]
298 forKey:@"skipResubmitDataConfirmation"];
299 [sessionEntry_ encodeWithCoder:coder];
300 EXPECT_OCMOCK_VERIFY(coder);
303 TEST_F(CRWSessionEntryTest, CodingEncoding) {
304 web::NavigationItem* item = [sessionEntry_ navigationItem];
305 item->SetVirtualURL(GURL("http://user.friendly"));
306 NSData* data = [NSKeyedArchiver archivedDataWithRootObject:sessionEntry_];
307 EXPECT_TRUE(data != nil);
308 CRWSessionEntry* unarchivedSessionEntry =
309 [NSKeyedUnarchiver unarchiveObjectWithData:data];
310 ASSERT_TRUE(unarchivedSessionEntry != nil);
311 web::NavigationItem* unarchivedItem = [unarchivedSessionEntry navigationItem];
312 expectEqualSessionEntries(sessionEntry_, unarchivedSessionEntry,
313 ui::PAGE_TRANSITION_RELOAD);
314 EXPECT_EQ(unarchivedItem->GetURL(), item->GetVirtualURL());
315 EXPECT_NE(unarchivedItem->GetURL(), item->GetURL());
318 TEST_F(CRWSessionEntryTest, CopyWithZone) {
319 CRWSessionEntry* sessionEntry2 = [sessionEntry_ copy];
320 EXPECT_NE(sessionEntry_, sessionEntry2);
321 expectEqualSessionEntries(
322 sessionEntry_, sessionEntry2,
323 [sessionEntry_ navigationItem]->GetTransitionType());
326 TEST_F(CRWSessionEntryTest, EmptyVirtualUrl) {
327 EXPECT_EQ(GURL("http://init.test/"),
328 [sessionEntry_ navigationItem]->GetURL());
331 TEST_F(CRWSessionEntryTest, NonEmptyVirtualUrl) {
332 web::NavigationItem* item = [sessionEntry_ navigationItem];
333 item->SetVirtualURL(GURL("http://user.friendly"));
334 EXPECT_EQ(GURL("http://user.friendly/"), item->GetVirtualURL());
335 EXPECT_EQ(GURL("http://init.test/"), item->GetURL());
338 TEST_F(CRWSessionEntryTest, EmptyDescription) {
339 EXPECT_GT([[sessionEntry_ description] length], 0U);
342 TEST_F(CRWSessionEntryTest, CreateWithNavigationItem) {
343 int index = 5; // Just pick something non-zero.
344 GURL url("http://www.virtualurl.com");
345 web::Referrer referrer(GURL("http://www.referrer.com"),
346 web::ReferrerPolicyDefault);
347 base::string16 title = base::SysNSStringToUTF16(@"Title");
349 ui::PageTransition transition = ui::PAGE_TRANSITION_GENERATED;
351 scoped_ptr<web::NavigationItem> navigation_item(
352 new web::NavigationItemImpl());
353 navigation_item->SetURL(url);
354 navigation_item->SetReferrer(referrer);
355 navigation_item->SetTitle(title);
356 navigation_item->SetTransitionType(transition);
358 base::scoped_nsobject<CRWSessionEntry> sessionEntry(
359 [[CRWSessionEntry alloc] initWithNavigationItem:navigation_item.Pass()
361 web::NavigationItem* item = [sessionEntry navigationItem];
362 // Validate everything was set correctly.
363 EXPECT_EQ(sessionEntry.get().index, index);
364 // Desktop only persists the virtual url, all three fields are initialized
366 EXPECT_EQ(item->GetURL(), url);
367 EXPECT_EQ(item->GetVirtualURL(), url);
368 EXPECT_EQ(sessionEntry.get().originalUrl, url);
369 EXPECT_EQ(item->GetReferrer().url, referrer.url);
370 EXPECT_EQ(item->GetTitle(), title);
371 EXPECT_EQ(item->GetTransitionType(), transition);
374 TEST_F(CRWSessionEntryTest, AddHTTPHeaders) {
375 EXPECT_NSEQ(@{ kHTTPHeaderKey1 : kHTTPHeaderValue1 },
376 [sessionEntry_ httpHeaders]);
378 [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey1 : kHTTPHeaderValue2 }];
379 EXPECT_NSEQ(@{ kHTTPHeaderKey1 : kHTTPHeaderValue2 },
380 [sessionEntry_ httpHeaders]);
382 [sessionEntry_ addHTTPHeaders:@{ kHTTPHeaderKey2 : kHTTPHeaderValue1 }];
383 NSDictionary* expected = @{ kHTTPHeaderKey1 : kHTTPHeaderValue2,
384 kHTTPHeaderKey2 : kHTTPHeaderValue1 };
385 EXPECT_NSEQ(expected, [sessionEntry_ httpHeaders]);
388 TEST_F(CRWSessionEntryTest, RemoveHTTPHeaderForKey) {
389 NSDictionary* httpHeaders = @{ kHTTPHeaderKey1 : kHTTPHeaderValue1,
390 kHTTPHeaderKey2 : kHTTPHeaderValue2 };
391 [sessionEntry_ addHTTPHeaders:httpHeaders];
392 EXPECT_NSEQ(httpHeaders, [sessionEntry_ httpHeaders]);
394 [sessionEntry_ removeHTTPHeaderForKey:kHTTPHeaderKey1];
395 EXPECT_NSEQ(@{ kHTTPHeaderKey2 : kHTTPHeaderValue2 },
396 [sessionEntry_ httpHeaders]);
398 [sessionEntry_ removeHTTPHeaderForKey:kHTTPHeaderKey2];
399 EXPECT_TRUE([sessionEntry_ httpHeaders] == nil);