Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / ios / chrome / browser / installation_notifier_unittest.mm
blob374a4c2768ecacdfcdded50d616a71563e08fbc4
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 "ios/chrome/browser/installation_notifier.h"
7 #import <UIKit/UIKit.h>
9 #include "base/ios/block_types.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/test/histogram_tester.h"
13 #include "ios/web/public/test/test_web_thread.h"
14 #include "net/base/backoff_entry.h"
15 #include "testing/platform_test.h"
17 @interface MockDispatcher : NSObject<DispatcherProtocol>
18 - (int64_t)lastDelayInNSec;
19 @end
21 @implementation MockDispatcher {
22   int _dispatchCount;
23   int64_t _lastDelayInNSec;
24   base::scoped_nsobject<NSMutableDictionary> _blocks;
27 - (instancetype)init {
28   if ((self = [super init]))
29     _blocks.reset([[NSMutableDictionary alloc] init]);
30   return self;
33 #pragma mark -
34 #pragma mark Testing methods
36 - (void)executeAfter:(int)dispatchCount block:(ProceduralBlock)block {
37   [_blocks setObject:[[block copy] autorelease]
38               forKey:[NSNumber numberWithInt:dispatchCount]];
41 - (int64_t)lastDelayInNSec {
42   return _lastDelayInNSec;
45 #pragma mark -
46 #pragma mark DispatcherProtocol
48 - (void)dispatchAfter:(int64_t)delayInNSec withBlock:(dispatch_block_t)block {
49   _lastDelayInNSec = delayInNSec;
50   void (^blockToCallForThisIteration)(void) =
51       [_blocks objectForKey:[NSNumber numberWithInt:_dispatchCount]];
52   if (blockToCallForThisIteration)
53     blockToCallForThisIteration();
54   _dispatchCount++;
55   block();
58 @end
60 @interface MockNotificationReceiver : NSObject
61 @end
63 @implementation MockNotificationReceiver {
64   int notificationCount_;
67 - (int)notificationCount {
68   return notificationCount_;
71 - (void)receivedNotification {
72   notificationCount_++;
75 @end
77 @interface MockUIApplication : NSObject
78 // Mocks UIApplication's canOpenURL.
79 @end
81 @implementation MockUIApplication {
82   BOOL canOpen_;
85 - (void)setCanOpenURL:(BOOL)canOpen {
86   canOpen_ = canOpen;
89 - (BOOL)canOpenURL:(NSURL*)url {
90   return canOpen_;
93 @end
95 @interface InstallationNotifier (Testing)
96 - (void)setDispatcher:(id<DispatcherProtocol>)dispatcher;
97 - (void)setSharedApplication:(id)sharedApplication;
98 - (void)dispatchInstallationNotifierBlock;
99 - (void)registerForInstallationNotifications:(id)observer
100                                 withSelector:(SEL)notificationSelector
101                                    forScheme:(NSString*)scheme
102                                 startPolling:(BOOL)poll;
103 - (net::BackoffEntry::Policy const*)backOffPolicy;
104 @end
106 namespace {
108 class InstallationNotifierTest : public PlatformTest {
109  public:
110   InstallationNotifierTest() : ui_thread_(web::WebThread::UI, &message_loop_) {}
112  protected:
113   void SetUp() override {
114     installationNotifier_ = [InstallationNotifier sharedInstance];
115     dispatcher_ = [[MockDispatcher alloc] init];
116     notificationReceiver1_.reset(([[MockNotificationReceiver alloc] init]));
117     notificationReceiver2_.reset(([[MockNotificationReceiver alloc] init]));
118     sharedApplication_.reset([[MockUIApplication alloc] init]);
119     [installationNotifier_ setSharedApplication:sharedApplication_];
120     [installationNotifier_ setDispatcher:dispatcher_];
121     histogramTester_.reset(new base::HistogramTester());
122   }
124   void VerifyHistogramValidity(int expectedYes, int expectedNo) {
125     histogramTester_->ExpectTotalCount("NativeAppLauncher.InstallationDetected",
126                                        expectedYes + expectedNo);
127     histogramTester_->ExpectBucketCount(
128         "NativeAppLauncher.InstallationDetected", YES, expectedYes);
129     histogramTester_->ExpectBucketCount(
130         "NativeAppLauncher.InstallationDetected", NO, expectedNo);
131   }
133   void VerifyDelay(int pollingIteration) {
134     double delayInMSec = [dispatcher_ lastDelayInNSec] / NSEC_PER_MSEC;
135     double initialDelayInMSec =
136         [installationNotifier_ backOffPolicy]->initial_delay_ms;
137     double multiplyFactor =
138         [installationNotifier_ backOffPolicy]->multiply_factor;
139     double expectedDelayInMSec =
140         initialDelayInMSec * pow(multiplyFactor, pollingIteration);
141     double jitter = [installationNotifier_ backOffPolicy]->jitter_factor;
142     EXPECT_NEAR(delayInMSec, expectedDelayInMSec,
143                 50 + jitter * expectedDelayInMSec);
144   }
146   base::MessageLoopForUI message_loop_;
147   web::TestWebThread ui_thread_;
148   InstallationNotifier* installationNotifier_;  // Weak pointer to singleton.
149   MockDispatcher* dispatcher_;  // Weak. installationNotifier_ owns it.
150   base::scoped_nsobject<MockNotificationReceiver> notificationReceiver1_;
151   base::scoped_nsobject<MockNotificationReceiver> notificationReceiver2_;
152   base::scoped_nsobject<MockUIApplication> sharedApplication_;
153   scoped_ptr<base::HistogramTester> histogramTester_;
156 TEST_F(InstallationNotifierTest, RegisterWithAppAlreadyInstalled) {
157   [sharedApplication_ setCanOpenURL:YES];
158   [installationNotifier_
159       registerForInstallationNotifications:notificationReceiver1_
160                               withSelector:@selector(receivedNotification)
161                                  forScheme:@"foo-scheme"];
162   EXPECT_EQ(1, [notificationReceiver1_ notificationCount]);
163   [installationNotifier_
164       registerForInstallationNotifications:notificationReceiver1_
165                               withSelector:@selector(receivedNotification)
166                                  forScheme:@"foo-scheme"];
167   EXPECT_EQ(2, [notificationReceiver1_ notificationCount]);
168   VerifyHistogramValidity(2, 0);
171 TEST_F(InstallationNotifierTest, RegisterWithAppInstalledAfterSomeTime) {
172   [sharedApplication_ setCanOpenURL:NO];
173   [dispatcher_ executeAfter:10
174                       block:^{
175                         [sharedApplication_ setCanOpenURL:YES];
176                       }];
177   [installationNotifier_
178       registerForInstallationNotifications:notificationReceiver1_
179                               withSelector:@selector(receivedNotification)
180                                  forScheme:@"foo-scheme"];
181   EXPECT_EQ(1, [notificationReceiver1_ notificationCount]);
182   VerifyHistogramValidity(1, 0);
185 TEST_F(InstallationNotifierTest, RegisterForTwoInstallations) {
186   [sharedApplication_ setCanOpenURL:NO];
187   [dispatcher_ executeAfter:10
188                       block:^{
189                         [sharedApplication_ setCanOpenURL:YES];
190                       }];
191   [installationNotifier_
192       registerForInstallationNotifications:notificationReceiver1_
193                               withSelector:@selector(receivedNotification)
194                                  forScheme:@"foo-scheme"
195                               startPolling:NO];
196   [installationNotifier_
197       registerForInstallationNotifications:notificationReceiver2_
198                               withSelector:@selector(receivedNotification)
199                                  forScheme:@"foo-scheme"
200                               startPolling:NO];
201   [installationNotifier_
202       registerForInstallationNotifications:notificationReceiver2_
203                               withSelector:@selector(receivedNotification)
204                                  forScheme:@"bar-scheme"
205                               startPolling:NO];
206   [installationNotifier_ dispatchInstallationNotifierBlock];
207   EXPECT_EQ(1, [notificationReceiver1_ notificationCount]);
208   EXPECT_EQ(2, [notificationReceiver2_ notificationCount]);
209   VerifyHistogramValidity(2, 0);
212 TEST_F(InstallationNotifierTest, RegisterAndThenUnregister) {
213   [sharedApplication_ setCanOpenURL:NO];
214   [dispatcher_ executeAfter:10
215                       block:^{
216                         [installationNotifier_
217                             unregisterForNotifications:notificationReceiver1_];
218                       }];
219   [installationNotifier_
220       registerForInstallationNotifications:notificationReceiver1_
221                               withSelector:@selector(receivedNotification)
222                                  forScheme:@"foo-scheme"];
223   EXPECT_EQ(0, [notificationReceiver1_ notificationCount]);
224   VerifyHistogramValidity(0, 1);
227 TEST_F(InstallationNotifierTest, TestExponentialBackoff) {
228   [sharedApplication_ setCanOpenURL:NO];
229   // Making sure that delay is multiplied by |multiplyFactor| every time.
230   [dispatcher_ executeAfter:0
231                       block:^{
232                         VerifyDelay(0);
233                       }];
234   [dispatcher_ executeAfter:1
235                       block:^{
236                         VerifyDelay(1);
237                       }];
238   [dispatcher_ executeAfter:2
239                       block:^{
240                         VerifyDelay(2);
241                       }];
242   // Registering for the installation of another application and making sure
243   // that the delay is reset to the initial delay.
244   [dispatcher_ executeAfter:
245                    3 block:^{
246     VerifyDelay(3);
247     [installationNotifier_
248         registerForInstallationNotifications:notificationReceiver1_
249                                 withSelector:@selector(receivedNotification)
250                                    forScheme:@"bar-scheme"
251                                 startPolling:NO];
252   }];
253   [dispatcher_ executeAfter:4
254                       block:^{
255                         VerifyDelay(0);
256                       }];
257   [dispatcher_ executeAfter:5
258                       block:^{
259                         VerifyDelay(1);
260                         [installationNotifier_
261                             unregisterForNotifications:notificationReceiver1_];
262                       }];
264   [installationNotifier_
265       registerForInstallationNotifications:notificationReceiver1_
266                               withSelector:@selector(receivedNotification)
267                                  forScheme:@"foo-scheme"];
268   VerifyHistogramValidity(0, 2);
271 TEST_F(InstallationNotifierTest, TestThatEmptySchemeDoesntCrashChrome) {
272   [installationNotifier_
273       registerForInstallationNotifications:notificationReceiver1_
274                               withSelector:@selector(receivedNotification)
275                                  forScheme:nil];
276   [installationNotifier_ unregisterForNotifications:notificationReceiver1_];
279 }  // namespace