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. 4 5#import <Foundation/Foundation.h> 6 7#include "ios/chrome/browser/crash_report/crash_reporter_url_observer.h" 8 9#include "base/strings/sys_string_conversions.h" 10#include "base/test/task_environment.h" 11#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" 12#import "ios/chrome/browser/web_state_list/fake_web_state_list_delegate.h" 13#import "ios/chrome/browser/web_state_list/web_state_list.h" 14#import "ios/chrome/browser/web_state_list/web_state_opener.h" 15#import "ios/web/public/navigation/navigation_item.h" 16#import "ios/web/public/test/fakes/fake_navigation_context.h" 17#import "ios/web/public/test/fakes/test_navigation_manager.h" 18#import "ios/web/public/test/fakes/test_web_state.h" 19#include "testing/gtest/include/gtest/gtest.h" 20#include "testing/gtest_mac.h" 21#include "testing/platform_test.h" 22 23#if !defined(__has_feature) || !__has_feature(objc_arc) 24#error "This file requires ARC support." 25#endif 26 27namespace { 28 29class TestWebState : public web::TestWebState { 30 public: 31 void LoadURL(const GURL& url) { 32 SetCurrentURL(url); 33 web::FakeNavigationContext context; 34 context.SetUrl(url); 35 web::TestNavigationManager* navigation_manager = 36 static_cast<web::TestNavigationManager*>(GetNavigationManager()); 37 navigation_manager->SetPendingItem(nullptr); 38 pending_item_.reset(); 39 OnNavigationFinished(&context); 40 } 41 42 void LoadPendingURL(const GURL& url) { 43 SetCurrentURL(url); 44 web::FakeNavigationContext context; 45 context.SetUrl(url); 46 web::TestNavigationManager* navigation_manager = 47 static_cast<web::TestNavigationManager*>(GetNavigationManager()); 48 DCHECK(!pending_item_); 49 pending_item_ = web::NavigationItem::Create(); 50 pending_item_->SetURL(url); 51 navigation_manager->SetPendingItem(pending_item_.get()); 52 OnNavigationStarted(&context); 53 } 54 55 private: 56 std::unique_ptr<web::NavigationItem> pending_item_; 57}; 58 59} // namespace 60 61@interface DictionaryParameterSetter : NSObject <CrashReporterParameterSetter> 62@property(nonatomic) NSMutableDictionary* params; 63@end 64 65@implementation DictionaryParameterSetter 66 67- (instancetype)init { 68 self = [super init]; 69 if (self) { 70 _params = [[NSMutableDictionary alloc] init]; 71 } 72 return self; 73} 74 75- (void)removeReportParameter:(NSString*)key { 76 [_params removeObjectForKey:key]; 77} 78 79- (void)setReportParameterURL:(const GURL&)URL forKey:(NSString*)key { 80 [_params setObject:base::SysUTF8ToNSString(URL.spec()) forKey:key]; 81} 82 83@end 84 85class CrashReporterURLObserverTest : public PlatformTest { 86 public: 87 CrashReporterURLObserverTest() { 88 TestChromeBrowserState::Builder test_cbs_builder; 89 test_chrome_browser_state_ = test_cbs_builder.Build(); 90 params_ = [[DictionaryParameterSetter alloc] init]; 91 observer_ = std::make_unique<CrashReporterURLObserver>(params_); 92 } 93 94 TestWebState* CreateWebState(WebStateList* web_state_list) { 95 std::unique_ptr<TestWebState> test_web_state = 96 std::make_unique<TestWebState>(); 97 test_web_state->SetBrowserState(test_chrome_browser_state_.get()); 98 test_web_state->SetNavigationManager( 99 std::make_unique<web::TestNavigationManager>()); 100 TestWebState* test_web_state_ptr = test_web_state.get(); 101 web_state_list->InsertWebState(0, std::move(test_web_state), 102 WebStateList::INSERT_NO_FLAGS, 103 WebStateOpener()); 104 return test_web_state_ptr; 105 } 106 107 std::unique_ptr<TestWebState> CreatePreloadWebState() { 108 std::unique_ptr<TestWebState> test_web_state = 109 std::make_unique<TestWebState>(); 110 test_web_state->SetBrowserState(test_chrome_browser_state_.get()); 111 test_web_state->SetNavigationManager( 112 std::make_unique<web::TestNavigationManager>()); 113 observer_->ObservePreloadWebState(test_web_state.get()); 114 return test_web_state; 115 } 116 117 protected: 118 base::test::TaskEnvironment task_environment_; 119 std::unique_ptr<ChromeBrowserState> test_chrome_browser_state_; 120 FakeWebStateListDelegate web_state_list_delegate_; 121 DictionaryParameterSetter* params_; 122 std::unique_ptr<CrashReporterURLObserver> observer_; 123}; 124 125TEST_F(CrashReporterURLObserverTest, TestBasicBehaviors) { 126 EXPECT_NSEQ(@{}, params_.params); 127 128 // Create 5 WebStateLists to have 5 groups 129 WebStateList web_state_list_1(&web_state_list_delegate_); 130 observer_->ObserveWebStateList(&web_state_list_1); 131 WebStateList web_state_list_2(&web_state_list_delegate_); 132 observer_->ObserveWebStateList(&web_state_list_2); 133 WebStateList web_state_list_3(&web_state_list_delegate_); 134 observer_->ObserveWebStateList(&web_state_list_3); 135 WebStateList web_state_list_4(&web_state_list_delegate_); 136 observer_->ObserveWebStateList(&web_state_list_4); 137 WebStateList web_state_list_5(&web_state_list_delegate_); 138 observer_->ObserveWebStateList(&web_state_list_5); 139 140 TestWebState* web_state_11 = CreateWebState(&web_state_list_1); 141 TestWebState* web_state_12 = CreateWebState(&web_state_list_1); 142 TestWebState* web_state_21 = CreateWebState(&web_state_list_2); 143 TestWebState* web_state_31 = CreateWebState(&web_state_list_3); 144 TestWebState* web_state_41 = CreateWebState(&web_state_list_4); 145 TestWebState* web_state_51 = CreateWebState(&web_state_list_5); 146 147 // Load in every group in turn. The last 3 should be reported. 148 web_state_11->LoadURL(GURL("http://example11.test/")); 149 NSDictionary* expected = @{@"url0" : @"http://example11.test/"}; 150 EXPECT_NSEQ(expected, params_.params); 151 152 web_state_21->LoadURL(GURL("http://example21.test/")); 153 expected = @{ 154 @"url0" : @"http://example11.test/", 155 @"url1" : @"http://example21.test/" 156 }; 157 EXPECT_NSEQ(expected, params_.params); 158 159 web_state_31->LoadURL(GURL("http://example31.test/")); 160 expected = @{ 161 @"url0" : @"http://example11.test/", 162 @"url1" : @"http://example21.test/", 163 @"url2" : @"http://example31.test/" 164 }; 165 EXPECT_NSEQ(expected, params_.params); 166 167 web_state_41->LoadURL(GURL("http://example41.test/")); 168 expected = @{ 169 @"url0" : @"http://example41.test/", 170 @"url1" : @"http://example21.test/", 171 @"url2" : @"http://example31.test/" 172 }; 173 EXPECT_NSEQ(expected, params_.params); 174 175 web_state_51->LoadURL(GURL("http://example51.test/")); 176 expected = @{ 177 @"url0" : @"http://example41.test/", 178 @"url1" : @"http://example51.test/", 179 @"url2" : @"http://example31.test/" 180 }; 181 EXPECT_NSEQ(expected, params_.params); 182 183 web_state_11->LoadURL(GURL("http://example12.test/")); 184 expected = @{ 185 @"url0" : @"http://example41.test/", 186 @"url1" : @"http://example51.test/", 187 @"url2" : @"http://example12.test/" 188 }; 189 EXPECT_NSEQ(expected, params_.params); 190 191 // Load again in group 4. URL 0 should be updated. 192 web_state_41->LoadURL(GURL("http://example42.test/")); 193 expected = @{ 194 @"url0" : @"http://example42.test/", 195 @"url1" : @"http://example51.test/", 196 @"url2" : @"http://example12.test/" 197 }; 198 EXPECT_NSEQ(expected, params_.params); 199 200 // Load again in group 2. 201 web_state_21->LoadURL(GURL("http://example22.test/")); 202 expected = @{ 203 @"url0" : @"http://example42.test/", 204 @"url1" : @"http://example22.test/", 205 @"url2" : @"http://example12.test/" 206 }; 207 EXPECT_NSEQ(expected, params_.params); 208 209 // Load again in group 1, on multiple WebState. 210 web_state_11->LoadURL(GURL("http://example13.test/")); 211 expected = @{ 212 @"url0" : @"http://example42.test/", 213 @"url1" : @"http://example22.test/", 214 @"url2" : @"http://example13.test/" 215 }; 216 EXPECT_NSEQ(expected, params_.params); 217 218 web_state_12->LoadURL(GURL("http://example14.test/")); 219 expected = @{ 220 @"url0" : @"http://example42.test/", 221 @"url1" : @"http://example22.test/", 222 @"url2" : @"http://example14.test/" 223 }; 224 EXPECT_NSEQ(expected, params_.params); 225 226 // Activate different WebState 227 web_state_list_1.ActivateWebStateAt(0); 228 expected = @{ 229 @"url0" : @"http://example42.test/", 230 @"url1" : @"http://example22.test/", 231 @"url2" : @"http://example13.test/" 232 }; 233 EXPECT_NSEQ(expected, params_.params); 234 235 web_state_list_1.ActivateWebStateAt(1); 236 expected = @{ 237 @"url0" : @"http://example42.test/", 238 @"url1" : @"http://example22.test/", 239 @"url2" : @"http://example14.test/" 240 }; 241 EXPECT_NSEQ(expected, params_.params); 242 243 // Load a pending URL in a group already reported, then load it. 244 web_state_41->LoadPendingURL(GURL("http://example43.test/")); 245 expected = @{ 246 @"url0" : @"http://example42.test/", 247 @"url0-pending" : @"http://example43.test/", 248 @"url1" : @"http://example22.test/", 249 @"url2" : @"http://example14.test/" 250 }; 251 EXPECT_NSEQ(expected, params_.params); 252 253 web_state_41->LoadURL(GURL("http://example43.test/")); 254 expected = @{ 255 @"url0" : @"http://example43.test/", 256 @"url1" : @"http://example22.test/", 257 @"url2" : @"http://example14.test/" 258 }; 259 EXPECT_NSEQ(expected, params_.params); 260 261 // Load a pending URL in a group not already reported, then load it. 262 web_state_51->LoadPendingURL(GURL("http://example53.test/")); 263 expected = @{ 264 @"url0" : @"http://example43.test/", 265 @"url1-pending" : @"http://example53.test/", 266 @"url2" : @"http://example14.test/" 267 }; 268 EXPECT_NSEQ(expected, params_.params); 269 270 web_state_51->LoadURL(GURL("http://example53.test/")); 271 expected = @{ 272 @"url0" : @"http://example43.test/", 273 @"url1" : @"http://example53.test/", 274 @"url2" : @"http://example14.test/" 275 }; 276 EXPECT_NSEQ(expected, params_.params); 277 278 // Remove a group and some reload URLs 279 observer_->RemoveWebStateList(&web_state_list_5); 280 expected = @{ 281 @"url0" : @"http://example43.test/", 282 @"url2" : @"http://example14.test/" 283 }; 284 EXPECT_NSEQ(expected, params_.params); 285 286 web_state_31->LoadURL(GURL("http://example33.test/")); 287 expected = @{ 288 @"url0" : @"http://example43.test/", 289 @"url1" : @"http://example33.test/", 290 @"url2" : @"http://example14.test/" 291 }; 292 EXPECT_NSEQ(expected, params_.params); 293 294 web_state_51->LoadURL(GURL("http://example54.test/")); 295 expected = @{ 296 @"url0" : @"http://example43.test/", 297 @"url1" : @"http://example33.test/", 298 @"url2" : @"http://example54.test/" 299 }; 300 EXPECT_NSEQ(expected, params_.params); 301 302 // Remove a WebState 303 web_state_12->LoadURL(GURL("http://example14.test/")); 304 expected = @{ 305 @"url0" : @"http://example14.test/", 306 @"url1" : @"http://example33.test/", 307 @"url2" : @"http://example54.test/" 308 }; 309 EXPECT_NSEQ(expected, params_.params); 310 311 // This should activate the other WebState. 312 std::unique_ptr<web::WebState> tmp_web_state = 313 web_state_list_1.DetachWebStateAt(1); 314 expected = @{ 315 @"url0" : @"http://example13.test/", 316 @"url1" : @"http://example33.test/", 317 @"url2" : @"http://example54.test/" 318 }; 319 EXPECT_NSEQ(expected, params_.params); 320 321 web_state_list_1.InsertWebState(0, std::move(tmp_web_state), 322 WebStateList::INSERT_ACTIVATE, 323 WebStateOpener()); 324 expected = @{ 325 @"url0" : @"http://example14.test/", 326 @"url1" : @"http://example33.test/", 327 @"url2" : @"http://example54.test/" 328 }; 329 EXPECT_NSEQ(expected, params_.params); 330 331 std::unique_ptr<web::WebState> tmp_web_state2 = 332 web_state_list_3.DetachWebStateAt(0); 333 expected = @{ 334 @"url0" : @"http://example14.test/", 335 @"url2" : @"http://example54.test/" 336 }; 337 EXPECT_NSEQ(expected, params_.params); 338 339 web_state_list_3.InsertWebState(0, std::move(tmp_web_state2), 340 WebStateList::INSERT_ACTIVATE, 341 WebStateOpener()); 342 expected = @{ 343 @"url0" : @"http://example14.test/", 344 @"url1" : @"http://example33.test/", 345 @"url2" : @"http://example54.test/" 346 }; 347 EXPECT_NSEQ(expected, params_.params); 348 349 std::unique_ptr<TestWebState> preload_web_state = CreatePreloadWebState(); 350 TestWebState* preload_web_state_ptr = preload_web_state.get(); 351 expected = @{ 352 @"url0" : @"http://example14.test/", 353 @"url1" : @"http://example33.test/", 354 @"url2" : @"http://example54.test/" 355 }; 356 EXPECT_NSEQ(expected, params_.params); 357 358 preload_web_state->LoadPendingURL(GURL("http://example-preload.test/")); 359 expected = @{ 360 @"url0" : @"http://example14.test/", 361 @"url1" : @"http://example33.test/", 362 @"url2-pending" : @"http://example-preload.test/" 363 }; 364 EXPECT_NSEQ(expected, params_.params); 365 366 observer_->StopObservingPreloadWebState(preload_web_state.get()); 367 web_state_list_3.ReplaceWebStateAt(0, std::move(preload_web_state)); 368 expected = @{ 369 @"url0" : @"http://example14.test/", 370 @"url1" : @"http://example33.test/", 371 @"url1-pending" : @"http://example-preload.test/" 372 }; 373 EXPECT_NSEQ(expected, params_.params); 374 375 preload_web_state_ptr->LoadURL(GURL("http://example-preload.test/")); 376 expected = @{ 377 @"url0" : @"http://example14.test/", 378 @"url1" : @"http://example-preload.test/" 379 }; 380 EXPECT_NSEQ(expected, params_.params); 381 382 observer_->StopObservingWebStateList(&web_state_list_1); 383 observer_->StopObservingWebStateList(&web_state_list_2); 384 observer_->StopObservingWebStateList(&web_state_list_3); 385 observer_->StopObservingWebStateList(&web_state_list_4); 386 observer_->StopObservingWebStateList(&web_state_list_5); 387} 388