| |
1 /* Any copyright is dedicated to the Public Domain. |
| |
2 * http://creativecommons.org/publicdomain/zero/1.0/ */ |
| |
3 |
| |
4 "use strict"; |
| |
5 |
| |
6 const {utils: Cu} = Components; |
| |
7 |
| |
8 Cu.import("resource://gre/modules/Preferences.jsm"); |
| |
9 Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); |
| |
10 Cu.import("resource://testing-common/services/datareporting/mocks.jsm"); |
| |
11 Cu.import("resource://gre/modules/UpdateChannel.jsm"); |
| |
12 |
| |
13 function getPolicy(name, |
| |
14 aCurrentPolicyVersion = 1, |
| |
15 aMinimumPolicyVersion = 1, |
| |
16 aBranchMinimumVersionOverride) { |
| |
17 let branch = "testing.datareporting." + name; |
| |
18 |
| |
19 // The version prefs should not be removed on reset, so set them in the |
| |
20 // default branch. |
| |
21 let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy." |
| |
22 , defaultBranch: true }); |
| |
23 defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion); |
| |
24 defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion); |
| |
25 let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false); |
| |
26 if (aBranchMinimumVersionOverride !== undefined) |
| |
27 defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride); |
| |
28 else |
| |
29 defaultPolicyPrefs.reset(branchOverridePrefName); |
| |
30 |
| |
31 let policyPrefs = new Preferences(branch + ".policy."); |
| |
32 let healthReportPrefs = new Preferences(branch + ".healthreport."); |
| |
33 |
| |
34 let listener = new MockPolicyListener(); |
| |
35 let policy = new DataReportingPolicy(policyPrefs, healthReportPrefs, listener); |
| |
36 |
| |
37 return [policy, policyPrefs, healthReportPrefs, listener]; |
| |
38 } |
| |
39 |
| |
40 function defineNow(policy, now) { |
| |
41 print("Adjusting fake system clock to " + now); |
| |
42 Object.defineProperty(policy, "now", { |
| |
43 value: function customNow() { |
| |
44 return now; |
| |
45 }, |
| |
46 writable: true, |
| |
47 }); |
| |
48 } |
| |
49 |
| |
50 function run_test() { |
| |
51 run_next_test(); |
| |
52 } |
| |
53 |
| |
54 add_test(function test_constructor() { |
| |
55 let policyPrefs = new Preferences("foo.bar.policy."); |
| |
56 let hrPrefs = new Preferences("foo.bar.healthreport."); |
| |
57 let listener = { |
| |
58 onRequestDataUpload: function() {}, |
| |
59 onRequestRemoteDelete: function() {}, |
| |
60 onNotifyDataPolicy: function() {}, |
| |
61 }; |
| |
62 |
| |
63 let policy = new DataReportingPolicy(policyPrefs, hrPrefs, listener); |
| |
64 do_check_true(Date.now() - policy.firstRunDate.getTime() < 1000); |
| |
65 |
| |
66 let tomorrow = Date.now() + 24 * 60 * 60 * 1000; |
| |
67 do_check_true(tomorrow - policy.nextDataSubmissionDate.getTime() < 1000); |
| |
68 |
| |
69 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); |
| |
70 |
| |
71 run_next_test(); |
| |
72 }); |
| |
73 |
| |
74 add_test(function test_prefs() { |
| |
75 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("prefs"); |
| |
76 |
| |
77 let now = new Date(); |
| |
78 let nowT = now.getTime(); |
| |
79 |
| |
80 policy.firstRunDate = now; |
| |
81 do_check_eq(policyPrefs.get("firstRunTime"), nowT); |
| |
82 do_check_eq(policy.firstRunDate.getTime(), nowT); |
| |
83 |
| |
84 policy.dataSubmissionPolicyNotifiedDate= now; |
| |
85 do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT); |
| |
86 do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT); |
| |
87 |
| |
88 policy.dataSubmissionPolicyResponseDate = now; |
| |
89 do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseTime"), nowT); |
| |
90 do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), nowT); |
| |
91 |
| |
92 policy.dataSubmissionPolicyResponseType = "type-1"; |
| |
93 do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseType"), "type-1"); |
| |
94 do_check_eq(policy.dataSubmissionPolicyResponseType, "type-1"); |
| |
95 |
| |
96 policy.dataSubmissionEnabled = false; |
| |
97 do_check_false(policyPrefs.get("dataSubmissionEnabled", true)); |
| |
98 do_check_false(policy.dataSubmissionEnabled); |
| |
99 |
| |
100 policy.dataSubmissionPolicyAccepted = false; |
| |
101 do_check_false(policyPrefs.get("dataSubmissionPolicyAccepted", true)); |
| |
102 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
103 |
| |
104 do_check_false(policy.dataSubmissionPolicyBypassAcceptance); |
| |
105 policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); |
| |
106 do_check_true(policy.dataSubmissionPolicyBypassAcceptance); |
| |
107 |
| |
108 policy.lastDataSubmissionRequestedDate = now; |
| |
109 do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT); |
| |
110 do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), nowT); |
| |
111 |
| |
112 policy.lastDataSubmissionSuccessfulDate = now; |
| |
113 do_check_eq(hrPrefs.get("lastDataSubmissionSuccessfulTime"), nowT); |
| |
114 do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), nowT); |
| |
115 |
| |
116 policy.lastDataSubmissionFailureDate = now; |
| |
117 do_check_eq(hrPrefs.get("lastDataSubmissionFailureTime"), nowT); |
| |
118 do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), nowT); |
| |
119 |
| |
120 policy.nextDataSubmissionDate = now; |
| |
121 do_check_eq(hrPrefs.get("nextDataSubmissionTime"), nowT); |
| |
122 do_check_eq(policy.nextDataSubmissionDate.getTime(), nowT); |
| |
123 |
| |
124 policy.currentDaySubmissionFailureCount = 2; |
| |
125 do_check_eq(hrPrefs.get("currentDaySubmissionFailureCount", 0), 2); |
| |
126 do_check_eq(policy.currentDaySubmissionFailureCount, 2); |
| |
127 |
| |
128 policy.pendingDeleteRemoteData = true; |
| |
129 do_check_true(hrPrefs.get("pendingDeleteRemoteData")); |
| |
130 do_check_true(policy.pendingDeleteRemoteData); |
| |
131 |
| |
132 policy.healthReportUploadEnabled = false; |
| |
133 do_check_false(hrPrefs.get("uploadEnabled")); |
| |
134 do_check_false(policy.healthReportUploadEnabled); |
| |
135 |
| |
136 do_check_false(policy.healthReportUploadLocked); |
| |
137 hrPrefs.lock("uploadEnabled"); |
| |
138 do_check_true(policy.healthReportUploadLocked); |
| |
139 hrPrefs.unlock("uploadEnabled"); |
| |
140 do_check_false(policy.healthReportUploadLocked); |
| |
141 |
| |
142 run_next_test(); |
| |
143 }); |
| |
144 |
| |
145 add_test(function test_notify_state_prefs() { |
| |
146 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notify_state_prefs"); |
| |
147 |
| |
148 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); |
| |
149 |
| |
150 policy._dataSubmissionPolicyNotifiedDate = new Date(); |
| |
151 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); |
| |
152 |
| |
153 policy.dataSubmissionPolicyResponseDate = new Date(); |
| |
154 policy._dataSubmissionPolicyNotifiedDate = null; |
| |
155 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); |
| |
156 |
| |
157 run_next_test(); |
| |
158 }); |
| |
159 |
| |
160 add_task(function test_initial_submission_notification() { |
| |
161 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification"); |
| |
162 |
| |
163 do_check_eq(listener.notifyUserCount, 0); |
| |
164 |
| |
165 // Fresh instances should not do anything initially. |
| |
166 policy.checkStateAndTrigger(); |
| |
167 do_check_eq(listener.notifyUserCount, 0); |
| |
168 |
| |
169 // We still shouldn't notify up to the millisecond before the barrier. |
| |
170 defineNow(policy, new Date(policy.firstRunDate.getTime() + |
| |
171 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC - 1)); |
| |
172 policy.checkStateAndTrigger(); |
| |
173 do_check_eq(listener.notifyUserCount, 0); |
| |
174 do_check_null(policy._dataSubmissionPolicyNotifiedDate); |
| |
175 do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); |
| |
176 |
| |
177 // We have crossed the threshold. We should see notification. |
| |
178 defineNow(policy, new Date(policy.firstRunDate.getTime() + |
| |
179 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC)); |
| |
180 policy.checkStateAndTrigger(); |
| |
181 do_check_eq(listener.notifyUserCount, 1); |
| |
182 yield listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
183 do_check_true(policy._dataSubmissionPolicyNotifiedDate instanceof Date); |
| |
184 do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0); |
| |
185 do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), |
| |
186 policy._dataSubmissionPolicyNotifiedDate.getTime()); |
| |
187 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); |
| |
188 }); |
| |
189 |
| |
190 add_test(function test_bypass_acceptance() { |
| |
191 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("bypass_acceptance"); |
| |
192 |
| |
193 policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); |
| |
194 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
195 do_check_true(policy.dataSubmissionPolicyBypassAcceptance); |
| |
196 defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime())); |
| |
197 policy.checkStateAndTrigger(); |
| |
198 do_check_eq(listener.requestDataUploadCount, 1); |
| |
199 |
| |
200 run_next_test(); |
| |
201 }); |
| |
202 |
| |
203 add_task(function test_notification_implicit_acceptance() { |
| |
204 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_implicit_acceptance"); |
| |
205 |
| |
206 let now = new Date(policy.nextDataSubmissionDate.getTime() - |
| |
207 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); |
| |
208 defineNow(policy, now); |
| |
209 policy.checkStateAndTrigger(); |
| |
210 do_check_eq(listener.notifyUserCount, 1); |
| |
211 yield listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
212 do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded"); |
| |
213 |
| |
214 do_check_true(5000 < policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC); |
| |
215 defineNow(policy, new Date(now.getTime() + 5000)); |
| |
216 policy.checkStateAndTrigger(); |
| |
217 do_check_eq(listener.notifyUserCount, 1); |
| |
218 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); |
| |
219 do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), 0); |
| |
220 do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded"); |
| |
221 |
| |
222 defineNow(policy, new Date(now.getTime() + policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + 1)); |
| |
223 policy.checkStateAndTrigger(); |
| |
224 do_check_eq(listener.notifyUserCount, 1); |
| |
225 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); |
| |
226 do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), policy.now().getTime()); |
| |
227 do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-implicit-time-elapsed"); |
| |
228 }); |
| |
229 |
| |
230 add_task(function test_notification_rejected() { |
| |
231 // User notification failed. We should not record it as being presented. |
| |
232 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_failed"); |
| |
233 |
| |
234 let now = new Date(policy.nextDataSubmissionDate.getTime() - |
| |
235 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); |
| |
236 defineNow(policy, now); |
| |
237 policy.checkStateAndTrigger(); |
| |
238 do_check_eq(listener.notifyUserCount, 1); |
| |
239 yield listener.lastNotifyRequest.onUserNotifyFailed(new Error("testing failed.")); |
| |
240 do_check_null(policy._dataSubmissionPolicyNotifiedDate); |
| |
241 do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); |
| |
242 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); |
| |
243 }); |
| |
244 |
| |
245 add_task(function test_notification_accepted() { |
| |
246 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accepted"); |
| |
247 |
| |
248 let now = new Date(policy.nextDataSubmissionDate.getTime() - |
| |
249 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); |
| |
250 defineNow(policy, now); |
| |
251 policy.checkStateAndTrigger(); |
| |
252 yield listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
253 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); |
| |
254 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
255 listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
256 listener.lastNotifyRequest.onUserAccept("foo-bar"); |
| |
257 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); |
| |
258 do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-foo-bar"); |
| |
259 do_check_true(policy.dataSubmissionPolicyAccepted); |
| |
260 do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), now.getTime()); |
| |
261 }); |
| |
262 |
| |
263 add_task(function test_notification_rejected() { |
| |
264 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_rejected"); |
| |
265 |
| |
266 let now = new Date(policy.nextDataSubmissionDate.getTime() - |
| |
267 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); |
| |
268 defineNow(policy, now); |
| |
269 policy.checkStateAndTrigger(); |
| |
270 yield listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
271 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); |
| |
272 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
273 listener.lastNotifyRequest.onUserReject(); |
| |
274 do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); |
| |
275 do_check_eq(policy.dataSubmissionPolicyResponseType, "rejected-no-reason"); |
| |
276 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
277 |
| |
278 // No requests for submission should occur if user has rejected. |
| |
279 defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime() + 10000)); |
| |
280 policy.checkStateAndTrigger(); |
| |
281 do_check_eq(listener.requestDataUploadCount, 0); |
| |
282 }); |
| |
283 |
| |
284 add_test(function test_submission_kill_switch() { |
| |
285 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch"); |
| |
286 |
| |
287 policy.firstRunDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); |
| |
288 policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
289 policy.recordUserAcceptance("accept-old-ack"); |
| |
290 do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); |
| |
291 policy.checkStateAndTrigger(); |
| |
292 do_check_eq(listener.requestDataUploadCount, 1); |
| |
293 |
| |
294 defineNow(policy, |
| |
295 new Date(Date.now() + policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC + 100)); |
| |
296 policy.dataSubmissionEnabled = false; |
| |
297 policy.checkStateAndTrigger(); |
| |
298 do_check_eq(listener.requestDataUploadCount, 1); |
| |
299 |
| |
300 run_next_test(); |
| |
301 }); |
| |
302 |
| |
303 add_test(function test_upload_kill_switch() { |
| |
304 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch"); |
| |
305 |
| |
306 defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); |
| |
307 policy.recordUserAcceptance(); |
| |
308 defineNow(policy, policy.nextDataSubmissionDate); |
| |
309 |
| |
310 // So that we don't trigger deletions, which cause uploads to be delayed. |
| |
311 hrPrefs.ignore("uploadEnabled", policy.uploadEnabledObserver); |
| |
312 |
| |
313 policy.healthReportUploadEnabled = false; |
| |
314 policy.checkStateAndTrigger(); |
| |
315 do_check_eq(listener.requestDataUploadCount, 0); |
| |
316 policy.healthReportUploadEnabled = true; |
| |
317 policy.checkStateAndTrigger(); |
| |
318 do_check_eq(listener.requestDataUploadCount, 1); |
| |
319 |
| |
320 run_next_test(); |
| |
321 }); |
| |
322 |
| |
323 add_test(function test_data_submission_no_data() { |
| |
324 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data"); |
| |
325 |
| |
326 policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
327 policy.dataSubmissionPolicyAccepted = true; |
| |
328 let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); |
| |
329 defineNow(policy, now); |
| |
330 do_check_eq(listener.requestDataUploadCount, 0); |
| |
331 policy.checkStateAndTrigger(); |
| |
332 do_check_eq(listener.requestDataUploadCount, 1); |
| |
333 listener.lastDataRequest.onNoDataAvailable(); |
| |
334 |
| |
335 // The next trigger should try again. |
| |
336 defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000)); |
| |
337 policy.checkStateAndTrigger(); |
| |
338 do_check_eq(listener.requestDataUploadCount, 2); |
| |
339 |
| |
340 run_next_test(); |
| |
341 }); |
| |
342 |
| |
343 add_task(function test_data_submission_submit_failure_hard() { |
| |
344 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard"); |
| |
345 |
| |
346 policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
347 policy.dataSubmissionPolicyAccepted = true; |
| |
348 let nextDataSubmissionDate = policy.nextDataSubmissionDate; |
| |
349 let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); |
| |
350 defineNow(policy, now); |
| |
351 |
| |
352 policy.checkStateAndTrigger(); |
| |
353 do_check_eq(listener.requestDataUploadCount, 1); |
| |
354 yield listener.lastDataRequest.onSubmissionFailureHard(); |
| |
355 do_check_eq(listener.lastDataRequest.state, |
| |
356 listener.lastDataRequest.SUBMISSION_FAILURE_HARD); |
| |
357 |
| |
358 let expected = new Date(now.getTime() + 24 * 60 * 60 * 1000); |
| |
359 do_check_eq(policy.nextDataSubmissionDate.getTime(), expected.getTime()); |
| |
360 |
| |
361 defineNow(policy, new Date(now.getTime() + 10)); |
| |
362 policy.checkStateAndTrigger(); |
| |
363 do_check_eq(listener.requestDataUploadCount, 1); |
| |
364 }); |
| |
365 |
| |
366 add_task(function test_data_submission_submit_try_again() { |
| |
367 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft"); |
| |
368 |
| |
369 policy.recordUserAcceptance(); |
| |
370 let nextDataSubmissionDate = policy.nextDataSubmissionDate; |
| |
371 let now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
372 defineNow(policy, now); |
| |
373 policy.checkStateAndTrigger(); |
| |
374 yield listener.lastDataRequest.onSubmissionFailureSoft(); |
| |
375 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
376 nextDataSubmissionDate.getTime() + 15 * 60 * 1000); |
| |
377 }); |
| |
378 |
| |
379 add_task(function test_submission_daily_scheduling() { |
| |
380 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling"); |
| |
381 |
| |
382 policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
383 policy.dataSubmissionPolicyAccepted = true; |
| |
384 let nextDataSubmissionDate = policy.nextDataSubmissionDate; |
| |
385 |
| |
386 // Skip ahead to next submission date. We should get a submission request. |
| |
387 let now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
388 defineNow(policy, now); |
| |
389 policy.checkStateAndTrigger(); |
| |
390 do_check_eq(listener.requestDataUploadCount, 1); |
| |
391 do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime()); |
| |
392 |
| |
393 let finishedDate = new Date(now.getTime() + 250); |
| |
394 defineNow(policy, new Date(finishedDate.getTime() + 50)); |
| |
395 yield listener.lastDataRequest.onSubmissionSuccess(finishedDate); |
| |
396 do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), finishedDate.getTime()); |
| |
397 |
| |
398 // Next scheduled submission should be exactly 1 day after the reported |
| |
399 // submission success. |
| |
400 |
| |
401 let nextScheduled = new Date(finishedDate.getTime() + 24 * 60 * 60 * 1000); |
| |
402 do_check_eq(policy.nextDataSubmissionDate.getTime(), nextScheduled.getTime()); |
| |
403 |
| |
404 // Fast forward some arbitrary time. We shouldn't do any work yet. |
| |
405 defineNow(policy, new Date(now.getTime() + 40000)); |
| |
406 policy.checkStateAndTrigger(); |
| |
407 do_check_eq(listener.requestDataUploadCount, 1); |
| |
408 |
| |
409 defineNow(policy, nextScheduled); |
| |
410 policy.checkStateAndTrigger(); |
| |
411 do_check_eq(listener.requestDataUploadCount, 2); |
| |
412 yield listener.lastDataRequest.onSubmissionSuccess(new Date(nextScheduled.getTime() + 200)); |
| |
413 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
414 new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime()); |
| |
415 }); |
| |
416 |
| |
417 add_test(function test_submission_far_future_scheduling() { |
| |
418 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling"); |
| |
419 |
| |
420 let now = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
421 defineNow(policy, now); |
| |
422 policy.recordUserAcceptance(); |
| |
423 now = new Date(); |
| |
424 defineNow(policy, now); |
| |
425 |
| |
426 let nextDate = policy._futureDate(3 * 24 * 60 * 60 * 1000 - 1); |
| |
427 policy.nextDataSubmissionDate = nextDate; |
| |
428 policy.checkStateAndTrigger(); |
| |
429 do_check_eq(listener.requestDataUploadCount, 0); |
| |
430 do_check_eq(policy.nextDataSubmissionDate.getTime(), nextDate.getTime()); |
| |
431 |
| |
432 policy.nextDataSubmissionDate = new Date(nextDate.getTime() + 1); |
| |
433 policy.checkStateAndTrigger(); |
| |
434 do_check_eq(listener.requestDataUploadCount, 0); |
| |
435 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
436 policy._futureDate(24 * 60 * 60 * 1000).getTime()); |
| |
437 |
| |
438 run_next_test(); |
| |
439 }); |
| |
440 |
| |
441 add_task(function test_submission_backoff() { |
| |
442 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff"); |
| |
443 |
| |
444 do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2); |
| |
445 |
| |
446 policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
447 policy.dataSubmissionPolicyAccepted = true; |
| |
448 |
| |
449 let now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
450 defineNow(policy, now); |
| |
451 policy.checkStateAndTrigger(); |
| |
452 do_check_eq(listener.requestDataUploadCount, 1); |
| |
453 do_check_eq(policy.currentDaySubmissionFailureCount, 0); |
| |
454 |
| |
455 now = new Date(now.getTime() + 5000); |
| |
456 defineNow(policy, now); |
| |
457 |
| |
458 // On first soft failure we should back off by scheduled interval. |
| |
459 yield listener.lastDataRequest.onSubmissionFailureSoft(); |
| |
460 do_check_eq(policy.currentDaySubmissionFailureCount, 1); |
| |
461 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
462 new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[0]).getTime()); |
| |
463 do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), now.getTime()); |
| |
464 |
| |
465 // Should not request submission until scheduled. |
| |
466 now = new Date(policy.nextDataSubmissionDate.getTime() - 1); |
| |
467 defineNow(policy, now); |
| |
468 policy.checkStateAndTrigger(); |
| |
469 do_check_eq(listener.requestDataUploadCount, 1); |
| |
470 |
| |
471 // 2nd request for submission. |
| |
472 now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
473 defineNow(policy, now); |
| |
474 policy.checkStateAndTrigger(); |
| |
475 do_check_eq(listener.requestDataUploadCount, 2); |
| |
476 |
| |
477 now = new Date(now.getTime() + 5000); |
| |
478 defineNow(policy, now); |
| |
479 |
| |
480 // On second failure we should back off by more. |
| |
481 yield listener.lastDataRequest.onSubmissionFailureSoft(); |
| |
482 do_check_eq(policy.currentDaySubmissionFailureCount, 2); |
| |
483 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
484 new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[1]).getTime()); |
| |
485 |
| |
486 now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
487 defineNow(policy, now); |
| |
488 policy.checkStateAndTrigger(); |
| |
489 do_check_eq(listener.requestDataUploadCount, 3); |
| |
490 |
| |
491 now = new Date(now.getTime() + 5000); |
| |
492 defineNow(policy, now); |
| |
493 |
| |
494 // On 3rd failure we should back off by a whole day. |
| |
495 yield listener.lastDataRequest.onSubmissionFailureSoft(); |
| |
496 do_check_eq(policy.currentDaySubmissionFailureCount, 0); |
| |
497 do_check_eq(policy.nextDataSubmissionDate.getTime(), |
| |
498 new Date(now.getTime() + 24 * 60 * 60 * 1000).getTime()); |
| |
499 }); |
| |
500 |
| |
501 // Ensure that only one submission request can be active at a time. |
| |
502 add_test(function test_submission_expiring() { |
| |
503 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring"); |
| |
504 |
| |
505 policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); |
| |
506 policy.dataSubmissionPolicyAccepted = true; |
| |
507 let nextDataSubmission = policy.nextDataSubmissionDate; |
| |
508 let now = new Date(policy.nextDataSubmissionDate.getTime()); |
| |
509 defineNow(policy, now); |
| |
510 policy.checkStateAndTrigger(); |
| |
511 do_check_eq(listener.requestDataUploadCount, 1); |
| |
512 defineNow(policy, new Date(now.getTime() + 500)); |
| |
513 policy.checkStateAndTrigger(); |
| |
514 do_check_eq(listener.requestDataUploadCount, 1); |
| |
515 |
| |
516 defineNow(policy, new Date(policy.now().getTime() + |
| |
517 policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC)); |
| |
518 |
| |
519 policy.checkStateAndTrigger(); |
| |
520 do_check_eq(listener.requestDataUploadCount, 2); |
| |
521 |
| |
522 run_next_test(); |
| |
523 }); |
| |
524 |
| |
525 add_task(function test_delete_remote_data() { |
| |
526 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data"); |
| |
527 |
| |
528 do_check_false(policy.pendingDeleteRemoteData); |
| |
529 let nextSubmissionDate = policy.nextDataSubmissionDate; |
| |
530 |
| |
531 let now = new Date(); |
| |
532 defineNow(policy, now); |
| |
533 |
| |
534 policy.deleteRemoteData(); |
| |
535 do_check_true(policy.pendingDeleteRemoteData); |
| |
536 do_check_neq(nextSubmissionDate.getTime(), |
| |
537 policy.nextDataSubmissionDate.getTime()); |
| |
538 do_check_eq(now.getTime(), policy.nextDataSubmissionDate.getTime()); |
| |
539 |
| |
540 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
541 do_check_true(listener.lastRemoteDeleteRequest.isDelete); |
| |
542 defineNow(policy, policy._futureDate(1000)); |
| |
543 |
| |
544 yield listener.lastRemoteDeleteRequest.onSubmissionSuccess(policy.now()); |
| |
545 do_check_false(policy.pendingDeleteRemoteData); |
| |
546 }); |
| |
547 |
| |
548 // Ensure that deletion requests take priority over regular data submission. |
| |
549 add_test(function test_delete_remote_data_priority() { |
| |
550 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority"); |
| |
551 |
| |
552 let now = new Date(); |
| |
553 defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); |
| |
554 policy.recordUserAcceptance(); |
| |
555 defineNow(policy, new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000)); |
| |
556 |
| |
557 policy.checkStateAndTrigger(); |
| |
558 do_check_eq(listener.requestDataUploadCount, 1); |
| |
559 policy._inProgressSubmissionRequest = null; |
| |
560 |
| |
561 policy.deleteRemoteData(); |
| |
562 policy.checkStateAndTrigger(); |
| |
563 |
| |
564 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
565 do_check_eq(listener.requestDataUploadCount, 1); |
| |
566 |
| |
567 run_next_test(); |
| |
568 }); |
| |
569 |
| |
570 add_test(function test_delete_remote_data_backoff() { |
| |
571 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff"); |
| |
572 |
| |
573 let now = new Date(); |
| |
574 defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); |
| |
575 policy.recordUserAcceptance(); |
| |
576 defineNow(policy, now); |
| |
577 policy.nextDataSubmissionDate = now; |
| |
578 policy.deleteRemoteData(); |
| |
579 |
| |
580 policy.checkStateAndTrigger(); |
| |
581 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
582 defineNow(policy, policy._futureDate(1000)); |
| |
583 policy.checkStateAndTrigger(); |
| |
584 do_check_eq(listener.requestDataUploadCount, 0); |
| |
585 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
586 |
| |
587 defineNow(policy, policy._futureDate(500)); |
| |
588 listener.lastRemoteDeleteRequest.onSubmissionFailureSoft(); |
| |
589 defineNow(policy, policy._futureDate(50)); |
| |
590 |
| |
591 policy.checkStateAndTrigger(); |
| |
592 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
593 |
| |
594 defineNow(policy, policy._futureDate(policy.FAILURE_BACKOFF_INTERVALS[0] - 50)); |
| |
595 policy.checkStateAndTrigger(); |
| |
596 do_check_eq(listener.requestRemoteDeleteCount, 2); |
| |
597 |
| |
598 run_next_test(); |
| |
599 }); |
| |
600 |
| |
601 // If we request delete while an upload is in progress, delete should be |
| |
602 // scheduled immediately after upload. |
| |
603 add_task(function test_delete_remote_data_in_progress_upload() { |
| |
604 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload"); |
| |
605 |
| |
606 let now = new Date(); |
| |
607 defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); |
| |
608 policy.recordUserAcceptance(); |
| |
609 defineNow(policy, policy.nextDataSubmissionDate); |
| |
610 |
| |
611 policy.checkStateAndTrigger(); |
| |
612 do_check_eq(listener.requestDataUploadCount, 1); |
| |
613 defineNow(policy, policy._futureDate(50 * 1000)); |
| |
614 |
| |
615 // If we request a delete during a pending request, nothing should be done. |
| |
616 policy.deleteRemoteData(); |
| |
617 policy.checkStateAndTrigger(); |
| |
618 do_check_eq(listener.requestDataUploadCount, 1); |
| |
619 do_check_eq(listener.requestRemoteDeleteCount, 0); |
| |
620 |
| |
621 // Now wait a little bit and finish the request. |
| |
622 defineNow(policy, policy._futureDate(10 * 1000)); |
| |
623 yield listener.lastDataRequest.onSubmissionSuccess(policy._futureDate(1000)); |
| |
624 defineNow(policy, policy._futureDate(5000)); |
| |
625 |
| |
626 policy.checkStateAndTrigger(); |
| |
627 do_check_eq(listener.requestDataUploadCount, 1); |
| |
628 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
629 }); |
| |
630 |
| |
631 add_test(function test_polling() { |
| |
632 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling"); |
| |
633 let intended = 500; |
| |
634 let acceptable = 250; // Because nsITimer doesn't guarantee times. |
| |
635 |
| |
636 // Ensure checkStateAndTrigger is called at a regular interval. |
| |
637 let then = Date.now(); |
| |
638 print("Starting run: " + then); |
| |
639 Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { |
| |
640 value: intended, |
| |
641 }); |
| |
642 let count = 0; |
| |
643 |
| |
644 Object.defineProperty(policy, "checkStateAndTrigger", { |
| |
645 value: function fakeCheckStateAndTrigger() { |
| |
646 let now = Date.now(); |
| |
647 let after = now - then; |
| |
648 count++; |
| |
649 |
| |
650 print("Polled at " + now + " after " + after + "ms, intended " + intended); |
| |
651 do_check_true(after >= acceptable); |
| |
652 DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); |
| |
653 |
| |
654 if (count >= 2) { |
| |
655 policy.stopPolling(); |
| |
656 |
| |
657 do_check_eq(listener.notifyUserCount, 0); |
| |
658 do_check_eq(listener.requestDataUploadCount, 0); |
| |
659 |
| |
660 run_next_test(); |
| |
661 } |
| |
662 |
| |
663 // "Specified timer period will be at least the time between when |
| |
664 // processing for last firing the callback completes and when the next |
| |
665 // firing occurs." |
| |
666 // |
| |
667 // That means we should set 'then' at the *end* of our handler, not |
| |
668 // earlier. |
| |
669 then = Date.now(); |
| |
670 } |
| |
671 }); |
| |
672 policy.startPolling(); |
| |
673 }); |
| |
674 |
| |
675 // Ensure that implicit acceptance of policy is resolved through polling. |
| |
676 // |
| |
677 // This is probably covered by other tests. But, it's best to have explicit |
| |
678 // coverage from a higher-level. |
| |
679 add_test(function test_polling_implicit_acceptance() { |
| |
680 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling_implicit_acceptance"); |
| |
681 |
| |
682 // Redefine intervals with shorter, test-friendly values. |
| |
683 Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { |
| |
684 value: 250, |
| |
685 }); |
| |
686 |
| |
687 Object.defineProperty(policy, "IMPLICIT_ACCEPTANCE_INTERVAL_MSEC", { |
| |
688 value: 700, |
| |
689 }); |
| |
690 |
| |
691 let count = 0; |
| |
692 |
| |
693 // Track JS elapsed time, so we can decide if we've waited for enough ticks. |
| |
694 let start; |
| |
695 Object.defineProperty(policy, "checkStateAndTrigger", { |
| |
696 value: function CheckStateAndTriggerProxy() { |
| |
697 count++; |
| |
698 let now = Date.now(); |
| |
699 let delta = now - start; |
| |
700 print("checkStateAndTrigger count: " + count + ", now " + now + |
| |
701 ", delta " + delta); |
| |
702 |
| |
703 // Account for some slack. |
| |
704 DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); |
| |
705 |
| |
706 // What should happen on different invocations: |
| |
707 // |
| |
708 // 1) We are inside the prompt interval so user gets prompted. |
| |
709 // 2) still ~300ms away from implicit acceptance |
| |
710 // 3) still ~50ms away from implicit acceptance |
| |
711 // 4) Implicit acceptance recorded. Data submission requested. |
| |
712 // 5) Request still pending. No new submission requested. |
| |
713 // |
| |
714 // Note that, due to the inaccuracy of timers, 4 might not happen until 5 |
| |
715 // firings have occurred. Yay. So we watch times, not just counts. |
| |
716 |
| |
717 do_check_eq(listener.notifyUserCount, 1); |
| |
718 |
| |
719 if (count == 1) { |
| |
720 listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
721 } |
| |
722 |
| |
723 if (delta <= (policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + policy.POLL_INTERVAL_MSEC)) { |
| |
724 do_check_false(policy.dataSubmissionPolicyAccepted); |
| |
725 do_check_eq(listener.requestDataUploadCount, 0); |
| |
726 } else if (count > 3) { |
| |
727 do_check_true(policy.dataSubmissionPolicyAccepted); |
| |
728 do_check_eq(policy.dataSubmissionPolicyResponseType, |
| |
729 "accepted-implicit-time-elapsed"); |
| |
730 do_check_eq(listener.requestDataUploadCount, 1); |
| |
731 } |
| |
732 |
| |
733 if ((count > 4) && policy.dataSubmissionPolicyAccepted) { |
| |
734 do_check_eq(listener.requestDataUploadCount, 1); |
| |
735 policy.stopPolling(); |
| |
736 run_next_test(); |
| |
737 } |
| |
738 } |
| |
739 }); |
| |
740 |
| |
741 policy.firstRunDate = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000); |
| |
742 policy.nextDataSubmissionDate = new Date(Date.now()); |
| |
743 start = Date.now(); |
| |
744 policy.startPolling(); |
| |
745 }); |
| |
746 |
| |
747 add_task(function test_record_health_report_upload_enabled() { |
| |
748 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); |
| |
749 |
| |
750 // Preconditions. |
| |
751 do_check_false(policy.pendingDeleteRemoteData); |
| |
752 do_check_true(policy.healthReportUploadEnabled); |
| |
753 do_check_eq(listener.requestRemoteDeleteCount, 0); |
| |
754 |
| |
755 // User intent to disable should immediately result in a pending |
| |
756 // delete request. |
| |
757 policy.recordHealthReportUploadEnabled(false, "testing 1 2 3"); |
| |
758 do_check_false(policy.healthReportUploadEnabled); |
| |
759 do_check_true(policy.pendingDeleteRemoteData); |
| |
760 do_check_eq(listener.requestRemoteDeleteCount, 1); |
| |
761 |
| |
762 // Fulfilling it should make it go away. |
| |
763 yield listener.lastRemoteDeleteRequest.onNoDataAvailable(); |
| |
764 do_check_false(policy.pendingDeleteRemoteData); |
| |
765 |
| |
766 // User intent to enable should get us back to default state. |
| |
767 policy.recordHealthReportUploadEnabled(true, "testing 1 2 3"); |
| |
768 do_check_false(policy.pendingDeleteRemoteData); |
| |
769 do_check_true(policy.healthReportUploadEnabled); |
| |
770 }); |
| |
771 |
| |
772 add_test(function test_pref_change_initiates_deletion() { |
| |
773 let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); |
| |
774 |
| |
775 // Preconditions. |
| |
776 do_check_false(policy.pendingDeleteRemoteData); |
| |
777 do_check_true(policy.healthReportUploadEnabled); |
| |
778 do_check_eq(listener.requestRemoteDeleteCount, 0); |
| |
779 |
| |
780 // User intent to disable should indirectly result in a pending |
| |
781 // delete request, because the policy is watching for the pref |
| |
782 // to change. |
| |
783 Object.defineProperty(policy, "deleteRemoteData", { |
| |
784 value: function deleteRemoteDataProxy() { |
| |
785 do_check_false(policy.healthReportUploadEnabled); |
| |
786 do_check_false(policy.pendingDeleteRemoteData); // Just called. |
| |
787 |
| |
788 run_next_test(); |
| |
789 }, |
| |
790 }); |
| |
791 |
| |
792 hrPrefs.set("uploadEnabled", false); |
| |
793 }); |
| |
794 |
| |
795 add_task(function* test_policy_version() { |
| |
796 let policy, policyPrefs, hrPrefs, listener, now, firstRunTime; |
| |
797 function createPolicy(shouldBeAccepted = false, |
| |
798 currentPolicyVersion = 1, minimumPolicyVersion = 1, |
| |
799 branchMinimumVersionOverride) { |
| |
800 [policy, policyPrefs, hrPrefs, listener] = |
| |
801 getPolicy("policy_version_test", currentPolicyVersion, |
| |
802 minimumPolicyVersion, branchMinimumVersionOverride); |
| |
803 let firstRun = now === undefined; |
| |
804 if (firstRun) { |
| |
805 firstRunTime = policy.firstRunDate.getTime(); |
| |
806 do_check_true(firstRunTime > 0); |
| |
807 now = new Date(policy.firstRunDate.getTime() + |
| |
808 policy.SUBMISSION_NOTIFY_INTERVAL_MSEC); |
| |
809 } |
| |
810 else { |
| |
811 // The first-run time should not be reset even after policy-version |
| |
812 // upgrades. |
| |
813 do_check_eq(policy.firstRunDate.getTime(), firstRunTime); |
| |
814 } |
| |
815 defineNow(policy, now); |
| |
816 do_check_eq(policy.dataSubmissionPolicyAccepted, shouldBeAccepted); |
| |
817 } |
| |
818 |
| |
819 function* triggerPolicyCheckAndEnsureNotified(notified = true, accept = true) { |
| |
820 policy.checkStateAndTrigger(); |
| |
821 do_check_eq(listener.notifyUserCount, Number(notified)); |
| |
822 if (notified) { |
| |
823 yield listener.lastNotifyRequest.onUserNotifyComplete(); |
| |
824 if (accept) { |
| |
825 listener.lastNotifyRequest.onUserAccept("because,"); |
| |
826 do_check_true(policy.dataSubmissionPolicyAccepted); |
| |
827 do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), |
| |
828 policyPrefs.get("currentPolicyVersion")); |
| |
829 } |
| |
830 else { |
| |
831 do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); |
| |
832 } |
| |
833 } |
| |
834 } |
| |
835 |
| |
836 createPolicy(); |
| |
837 yield triggerPolicyCheckAndEnsureNotified(); |
| |
838 |
| |
839 // We shouldn't be notified again if the current version is still valid; |
| |
840 createPolicy(true); |
| |
841 yield triggerPolicyCheckAndEnsureNotified(false); |
| |
842 |
| |
843 // Just increasing the current version isn't enough. The minimum |
| |
844 // version must be changed. |
| |
845 let currentPolicyVersion = policyPrefs.get("currentPolicyVersion"); |
| |
846 let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion"); |
| |
847 createPolicy(true, ++currentPolicyVersion, minimumPolicyVersion); |
| |
848 yield triggerPolicyCheckAndEnsureNotified(false); |
| |
849 do_check_true(policy.dataSubmissionPolicyAccepted); |
| |
850 do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), |
| |
851 minimumPolicyVersion); |
| |
852 |
| |
853 // Increase the minimum policy version and check if we're notified. |
| |
854 createPolicy(false, currentPolicyVersion, ++minimumPolicyVersion); |
| |
855 do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); |
| |
856 yield triggerPolicyCheckAndEnsureNotified(); |
| |
857 |
| |
858 // Test increasing the minimum version just on the current channel. |
| |
859 createPolicy(true, currentPolicyVersion, minimumPolicyVersion); |
| |
860 yield triggerPolicyCheckAndEnsureNotified(false); |
| |
861 createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion, minimumPolicyVersion + 1); |
| |
862 yield triggerPolicyCheckAndEnsureNotified(true); |
| |
863 }); |