|
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 }); |