Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(286)

Side by Side Diff: ios/chrome/app/application_delegate/url_opener_unittest.mm

Issue 2580363002: Upstream Chrome on iOS source code [1/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 #include "ios/chrome/app/application_delegate/url_opener.h"
6
7 #import <Foundation/Foundation.h>
8
9 #include "base/mac/scoped_nsobject.h"
10 #include "ios/chrome/app/application_delegate/app_state.h"
11 #include "ios/chrome/app/application_delegate/app_state_testing.h"
12 #include "ios/chrome/app/application_delegate/mock_tab_opener.h"
13 #include "ios/chrome/app/chrome_app_startup_parameters.h"
14 #include "ios/chrome/app/main_application_delegate.h"
15 #include "ios/chrome/app/main_controller.h"
16 #include "ios/chrome/app/main_controller_private.h"
17 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
18 #import "ios/chrome/browser/tabs/tab.h"
19 #import "ios/chrome/browser/ui/browser_view_controller.h"
20 #import "ios/chrome/test/base/scoped_block_swizzler.h"
21 #import "ios/testing/ocmock_complex_type_helper.h"
22 #import "net/base/mac/url_conversions.h"
23 #include "testing/platform_test.h"
24 #import "third_party/ocmock/OCMock/OCMock.h"
25 #include "third_party/ocmock/gtest_support.h"
26
27 #pragma mark - Tab Switcher Mock
28
29 // This mocks either a iPad tab switcher controller or a iPhone stack view
30 // controller.
31 @interface URLOpenerOCMockComplexTypeHandler : OCMockComplexTypeHelper
32 @end
33
34 @implementation URLOpenerOCMockComplexTypeHandler
35
36 typedef Tab* (^mock_gurl_nsuinteger_pagetransition)(const GURL&,
37 NSUInteger,
38 ui::PageTransition);
39
40 - (Tab*)dismissWithNewTabAnimationToModel:(TabModel*)targetModel
41 withURL:(const GURL&)url
42 atIndex:(NSUInteger)position
43 transition:(ui::PageTransition)transition {
44 static_cast<mock_gurl_nsuinteger_pagetransition>(
45 [self blockForSelector:_cmd])(url, position, transition);
46 id mockTab = [OCMockObject mockForClass:[Tab class]];
47 return mockTab;
48 }
49
50 - (Tab*)addSelectedTabWithURL:(const GURL&)url
51 atIndex:(NSUInteger)position
52 transition:(ui::PageTransition)transition {
53 static_cast<mock_gurl_nsuinteger_pagetransition>(
54 [self blockForSelector:_cmd])(url, position, transition);
55 id mockTab = [OCMockObject mockForClass:[Tab class]];
56 return mockTab;
57 }
58
59 @end
60
61 #pragma mark - BrowserViewController Mock
62
63 // Mock BVC class to use for test cases where OCMock gets handled incorrectly
64 // by UIViewController.
65 @interface URLOpenerMockBVC : UIViewController
66 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
67 @property(nonatomic, assign) GURL tabURL;
68 @property(nonatomic, assign) NSUInteger position;
69 @property(nonatomic, assign) ui::PageTransition transition;
70
71 - (Tab*)addSelectedTabWithURL:(const GURL&)url
72 atIndex:(NSUInteger)position
73 transition:(ui::PageTransition)transition;
74 - (void)expectNewForegroundTab;
75 - (void)setActive:(BOOL)active;
76 - (TabModel*)tabModel;
77 @end
78
79 @implementation URLOpenerMockBVC
80 @synthesize browserState = _browserState;
81 @synthesize tabURL = _tabURL;
82 @synthesize position = _position;
83 @synthesize transition = _transition;
84
85 - (Tab*)addSelectedTabWithURL:(const GURL&)url
86 atIndex:(NSUInteger)position
87 transition:(ui::PageTransition)transition {
88 self.tabURL = url;
89 self.position = position;
90 self.transition = transition;
91 return nil;
92 }
93
94 - (void)expectNewForegroundTab {
95 // no-op.
96 }
97
98 - (void)setActive:(BOOL)active {
99 // no-op
100 }
101
102 - (void)setPrimary:(BOOL)primary {
103 // no-op
104 }
105
106 - (TabModel*)tabModel {
107 return nil;
108 }
109
110 @end
111
112 class URLOpenerTest : public PlatformTest {
113 protected:
114 MainController* GetMainController() {
115 if (!main_controller_.get()) {
116 main_controller_.reset([[MainController alloc] init]);
117 [main_controller_ setUpAsForegrounded];
118 id mainTabModel = [OCMockObject mockForClass:[TabModel class]];
119 [[mainTabModel stub] resetSessionMetrics];
120 [[mainTabModel stub] browserStateDestroyed];
121 [[mainTabModel stub] addObserver:[OCMArg any]];
122 [[mainTabModel stub] removeObserver:[OCMArg any]];
123 [[main_controller_ browserViewInformation] setMainTabModel:mainTabModel];
124 }
125 return main_controller_.get();
126 }
127
128 private:
129 base::scoped_nsobject<MainController> main_controller_;
130 };
131
132 TEST_F(URLOpenerTest, HandleOpenURLWithNoOpenTab) {
133 // The tab switcher controller should be dismissed with a new tab containing
134 // the external URL.
135 NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
136 base::scoped_nsobject<ChromeAppStartupParameters> params(
137 [ChromeAppStartupParameters newChromeAppStartupParametersWithURL:url
138 fromSourceApplication:nil]);
139
140 base::scoped_nsobject<id> bvcMock([[URLOpenerMockBVC alloc] init]);
141
142 base::scoped_nsobject<id> tabSwitcherController;
143 tabSwitcherController.reset([[URLOpenerOCMockComplexTypeHandler alloc]
144 initWithRepresentedObject:[OCMockObject
145 mockForProtocol:@protocol(UrlLoader)]]);
146
147 base::scoped_nsobject<id> block([(id) ^ (const GURL& url, NSUInteger position,
148 ui::PageTransition transition) {
149 EXPECT_EQ(url, [params externalURL]);
150 EXPECT_EQ(NSNotFound, static_cast<NSInteger>(position));
151 EXPECT_TRUE(PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_LINK));
152 } copy]);
153 SEL dismissSelector =
154 @selector(dismissWithNewTabAnimationToModel:withURL:atIndex:transition:);
155 [tabSwitcherController onSelector:dismissSelector callBlockExpectation:block];
156
157 // Setup main controller.
158 MainController* controller = GetMainController();
159 controller.browserViewInformation.mainBVC = bvcMock;
160 controller.tabSwitcherController = tabSwitcherController;
161 controller.tabSwitcherActive = YES;
162
163 id mainApplicationDelegate =
164 [OCMockObject mockForClass:[MainApplicationDelegate class]];
165
166 AppState* appState =
167 [[[AppState alloc] initWithBrowserLauncher:controller
168 startupInformation:controller
169 applicationDelegate:mainApplicationDelegate
170 window:controller.window
171 shouldOpenNTP:YES] autorelease];
172 controller.appState = appState;
173
174 NSDictionary<NSString*, id>* options = nil;
175 [URLOpener openURL:url
176 applicationActive:YES
177 options:options
178 tabOpener:controller
179 startupInformation:controller];
180
181 EXPECT_OCMOCK_VERIFY(tabSwitcherController);
182 }
183
184 TEST_F(URLOpenerTest, HandleOpenURLWithOpenTabs) {
185 // The URL should be routed to the main BVC; the OTR BVC shouldn't get calls.
186 NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
187 id otrBVCMock = [OCMockObject mockForClass:[BrowserViewController class]];
188 base::scoped_nsobject<id> bvcMock([[URLOpenerMockBVC alloc] init]);
189 TestChromeBrowserState::Builder mainBrowserStateBuilder;
190 std::unique_ptr<TestChromeBrowserState> chrome_browser_state =
191 mainBrowserStateBuilder.Build();
192 ((URLOpenerMockBVC*)bvcMock).browserState = chrome_browser_state.get();
193
194 // Setup main controller.
195 MainController* controller = GetMainController();
196 controller.browserViewInformation.mainBVC = bvcMock;
197 controller.browserViewInformation.otrBVC = otrBVCMock;
198
199 NSDictionary<NSString*, id>* options = nil;
200 [URLOpener openURL:url
201 applicationActive:YES
202 options:options
203 tabOpener:controller
204 startupInformation:controller];
205
206 EXPECT_EQ(GURL("http://www.google.com/"),
207 [(URLOpenerMockBVC*)bvcMock tabURL]);
208 EXPECT_EQ(NSNotFound,
209 static_cast<NSInteger>([(URLOpenerMockBVC*)bvcMock position]));
210 EXPECT_TRUE(PageTransitionCoreTypeIs([(URLOpenerMockBVC*)bvcMock transition],
211 ui::PAGE_TRANSITION_LINK));
212 EXPECT_OCMOCK_VERIFY(otrBVCMock);
213 }
214
215 TEST_F(URLOpenerTest, HandleOpenURL) {
216 // A set of tests for robustness of
217 // application:openURL:options:tabOpener:startupInformation:
218 // It verifies that the function handles correctly differents URL parsed by
219 // ChromeAppStartupParameters.
220 MainController* controller = GetMainController();
221
222 // The array with the different states to tests (active, not active).
223 NSArray* applicationStatesToTest = @[ @YES, @NO ];
224
225 // Mock of TabOpening, preventing the creation of a new tab.
226 base::scoped_nsobject<MockTabOpener> tabOpener([[MockTabOpener alloc] init]);
227
228 // The keys for this dictionary is the URL to call openURL:. The value
229 // from the key is either YES or NO to indicate if this is a valid URL
230 // or not.
231 NSNumber* kIsValid = [NSNumber numberWithBool:YES];
232 NSNumber* kNotValid = [NSNumber numberWithBool:NO];
233 NSDictionary* urlsToTest = [NSDictionary
234 dictionaryWithObjectsAndKeys:
235 kNotValid, [NSNull null],
236 // Tests for http, googlechrome, and chromium scheme URLs.
237 kNotValid, @"", kIsValid, @"http://www.google.com/", kIsValid,
238 @"https://www.google.com/settings/account/", kIsValid,
239 @"googlechrome://www.google.com/", kIsValid,
240 @"googlechromes://www.google.com/settings/account/", kIsValid,
241 @"chromium://www.google.com/", kIsValid,
242 @"chromiums://www.google.com/settings/account/",
243 // Google search results page URLs.
244 kIsValid, @"https://www.google.com/search?q=pony&"
245 "sugexp=chrome,mod=7&sourceid=chrome&ie=UTF-8",
246 kIsValid, @"googlechromes://www.google.com/search?q=pony&"
247 "sugexp=chrome,mod=7&sourceid=chrome&ie=UTF-8",
248 // Other protocols.
249 kIsValid, @"chromium-x-callback://x-callback-url/open?url=https://"
250 "www.google.com&x-success=http://success",
251 kIsValid, @"file://localhost/path/to/file.pdf",
252 // Invalid format input URL will be ignored.
253 kNotValid, @"this.is.not.a.valid.url",
254 // Valid format but invalid data.
255 kIsValid, @"this://is/garbage/but/valid", nil];
256 NSArray* sourcesToTest = [NSArray
257 arrayWithObjects:[NSNull null], @"", @"com.google.GoogleMobile",
258 @"com.google.GooglePlus", @"com.google.SomeOtherProduct",
259 @"com.apple.mobilesafari",
260 @"com.othercompany.otherproduct", nil];
261 // See documentation for |annotation| property in
262 // UIDocumentInteractionController Class Reference. The following values are
263 // mostly to detect garbage-in situations and ensure that the app won't crash
264 // or garbage out.
265 NSArray* annotationsToTest = [NSArray
266 arrayWithObjects:[NSNull null],
267 [NSArray arrayWithObjects:@"foo", @"bar", nil],
268 [NSDictionary dictionaryWithObject:@"bar" forKey:@"foo"],
269 @"a string annotation object", nil];
270 for (id urlString in [urlsToTest allKeys]) {
271 for (id source in sourcesToTest) {
272 for (id annotation in annotationsToTest) {
273 for (NSNumber* applicationActive in applicationStatesToTest) {
274 BOOL applicationIsActive = [applicationActive boolValue];
275
276 controller.startupParameters = nil;
277 [tabOpener resetURL];
278 NSURL* testUrl = urlString == [NSNull null]
279 ? nil
280 : [NSURL URLWithString:urlString];
281 BOOL isValid = [[urlsToTest objectForKey:urlString] boolValue];
282 base::scoped_nsobject<NSMutableDictionary> options(
283 [[NSMutableDictionary alloc] init]);
284 if (source != [NSNull null]) {
285 [options setObject:source
286 forKey:UIApplicationOpenURLOptionsSourceApplicationKey];
287 }
288 if (annotation != [NSNull null]) {
289 [options setObject:annotation
290 forKey:UIApplicationOpenURLOptionsAnnotationKey];
291 }
292 base::scoped_nsobject<ChromeAppStartupParameters> params(
293 [ChromeAppStartupParameters
294 newChromeAppStartupParametersWithURL:testUrl
295 fromSourceApplication:nil]);
296
297 // Action.
298 BOOL result = [URLOpener openURL:testUrl
299 applicationActive:applicationIsActive
300 options:options
301 tabOpener:tabOpener
302 startupInformation:controller];
303
304 // Tests.
305 EXPECT_EQ(isValid, result);
306 if (!applicationIsActive) {
307 if (result)
308 EXPECT_EQ([params externalURL],
309 controller.startupParameters.externalURL);
310 else
311 EXPECT_EQ(nil, controller.startupParameters);
312 } else if (result) {
313 EXPECT_EQ(nil, controller.startupParameters);
314 EXPECT_EQ([params externalURL], [tabOpener url]);
315 }
316 }
317 }
318 }
319 }
320 }
321
322 // Tests that -handleApplication set startup parameters as expected.
323 TEST_F(URLOpenerTest, VerifyLaunchOptions) {
324 // Setup.
325 NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
326 NSDictionary* launchOptions = @{
327 UIApplicationLaunchOptionsURLKey : url,
328 UIApplicationLaunchOptionsSourceApplicationKey : @"com.apple.mobilesafari"
329 };
330
331 id tabOpenerMock = [OCMockObject mockForProtocol:@protocol(TabOpening)];
332
333 id startupInformationMock =
334 [OCMockObject mockForProtocol:@protocol(StartupInformation)];
335
336 id appStateMock = [OCMockObject mockForClass:[AppState class]];
337 [[appStateMock expect] launchFromURLHandled:YES];
338
339 __block BOOL hasBeenCalled = NO;
340
341 id implementation_block = ^BOOL(
342 id self, NSURL* urlArg, BOOL applicationActive, NSDictionary* options,
343 id<TabOpening> tabOpener, id<StartupInformation> startupInformation) {
344 hasBeenCalled = YES;
345 EXPECT_EQ([url absoluteString], [urlArg absoluteString]);
346 EXPECT_EQ(@"com.apple.mobilesafari",
347 options[UIApplicationOpenURLOptionsSourceApplicationKey]);
348 EXPECT_EQ(startupInformationMock, startupInformation);
349 EXPECT_EQ(tabOpenerMock, tabOpener);
350 return YES;
351 };
352 ScopedBlockSwizzler URL_opening_openURL_swizzler([URLOpener class],
353 @selector(openURL:
354 applicationActive:
355 options:
356 tabOpener:
357 startupInformation:),
358 implementation_block);
359
360 // Action.
361 [URLOpener handleLaunchOptions:launchOptions
362 applicationActive:NO
363 tabOpener:tabOpenerMock
364 startupInformation:startupInformationMock
365 appState:appStateMock];
366
367 // Test.
368 EXPECT_TRUE(hasBeenCalled);
369 EXPECT_OCMOCK_VERIFY(startupInformationMock);
370 }
371
372 // Tests that -handleApplication set startup parameters as expected with options
373 // as nil.
374 TEST_F(URLOpenerTest, VerifyLaunchOptionsNil) {
375 // Creates a mock with no stub. This test will pass only if we don't use these
376 // objects.
377 id startupInformationMock =
378 [OCMockObject mockForProtocol:@protocol(StartupInformation)];
379 id appStateMock = [OCMockObject mockForClass:[AppState class]];
380
381 // Action.
382 [URLOpener handleLaunchOptions:nil
383 applicationActive:YES
384 tabOpener:nil
385 startupInformation:startupInformationMock
386 appState:appStateMock];
387 }
388
389 // Tests that -handleApplication set startup parameters as expected with no
390 // source application.
391 TEST_F(URLOpenerTest, VerifyLaunchOptionsWithNoSourceApplication) {
392 // Setup.
393 NSURL* url = [NSURL URLWithString:@"chromium://www.google.com"];
394 NSDictionary* launchOptions = @{
395 UIApplicationLaunchOptionsURLKey : url,
396 };
397
398 // Creates a mock with no stub. This test will pass only if we don't use these
399 // objects.
400 id startupInformationMock =
401 [OCMockObject mockForProtocol:@protocol(StartupInformation)];
402 id appStateMock = [OCMockObject mockForClass:[AppState class]];
403
404 // Action.
405 [URLOpener handleLaunchOptions:launchOptions
406 applicationActive:YES
407 tabOpener:nil
408 startupInformation:startupInformationMock
409 appState:appStateMock];
410 }
411
412 // Tests that -handleApplication set startup parameters as expected with no url.
413 TEST_F(URLOpenerTest, VerifyLaunchOptionsWithNoURL) {
414 // Setup.
415 NSDictionary* launchOptions = @{
416 UIApplicationLaunchOptionsSourceApplicationKey : @"com.apple.mobilesafari"
417 };
418
419 // Creates a mock with no stub. This test will pass only if we don't use these
420 // objects.
421 id startupInformationMock =
422 [OCMockObject mockForProtocol:@protocol(StartupInformation)];
423 id appStateMock = [OCMockObject mockForClass:[AppState class]];
424
425 // Action.
426 [URLOpener handleLaunchOptions:launchOptions
427 applicationActive:YES
428 tabOpener:nil
429 startupInformation:startupInformationMock
430 appState:appStateMock];
431 }
432
433 // Tests that -handleApplication set startup parameters as expected with a bad
434 // url.
435 TEST_F(URLOpenerTest, VerifyLaunchOptionsWithBadURL) {
436 // Setup.
437 NSURL* url = [NSURL URLWithString:@"chromium.www.google.com"];
438 NSDictionary* launchOptions = @{
439 UIApplicationLaunchOptionsURLKey : url,
440 UIApplicationLaunchOptionsSourceApplicationKey : @"com.apple.mobilesafari"
441 };
442
443 id tabOpenerMock = [OCMockObject mockForProtocol:@protocol(TabOpening)];
444
445 id startupInformationMock =
446 [OCMockObject mockForProtocol:@protocol(StartupInformation)];
447
448 id appStateMock = [OCMockObject mockForClass:[AppState class]];
449 [[appStateMock expect] launchFromURLHandled:YES];
450
451 __block BOOL hasBeenCalled = NO;
452
453 id implementation_block = ^BOOL(
454 id self, NSURL* urlArg, BOOL applicationActive, NSDictionary* options,
455 id<TabOpening> tabOpener, id<StartupInformation> startupInformation) {
456 hasBeenCalled = YES;
457 EXPECT_EQ([url absoluteString], [urlArg absoluteString]);
458 EXPECT_EQ(@"com.apple.mobilesafari",
459 options[UIApplicationOpenURLOptionsSourceApplicationKey]);
460 EXPECT_EQ(startupInformationMock, startupInformation);
461 EXPECT_EQ(tabOpenerMock, tabOpener);
462 return YES;
463 };
464 ScopedBlockSwizzler URL_opening_openURL_swizzler([URLOpener class],
465 @selector(openURL:
466 applicationActive:
467 options:
468 tabOpener:
469 startupInformation:),
470 implementation_block);
471
472 // Action.
473 [URLOpener handleLaunchOptions:launchOptions
474 applicationActive:NO
475 tabOpener:tabOpenerMock
476 startupInformation:startupInformationMock
477 appState:appStateMock];
478
479 // Test.
480 EXPECT_TRUE(hasBeenCalled);
481 EXPECT_OCMOCK_VERIFY(startupInformationMock);
482 }
OLDNEW
« no previous file with comments | « ios/chrome/app/application_delegate/url_opener.mm ('k') | ios/chrome/app/application_delegate/user_activity_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698