1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/datareporting/tests/xpcshell/test_policy.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,863 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +"use strict"; 1.8 + 1.9 +const {utils: Cu} = Components; 1.10 + 1.11 +Cu.import("resource://gre/modules/Preferences.jsm"); 1.12 +Cu.import("resource://gre/modules/services/datareporting/policy.jsm"); 1.13 +Cu.import("resource://testing-common/services/datareporting/mocks.jsm"); 1.14 +Cu.import("resource://gre/modules/UpdateChannel.jsm"); 1.15 + 1.16 +function getPolicy(name, 1.17 + aCurrentPolicyVersion = 1, 1.18 + aMinimumPolicyVersion = 1, 1.19 + aBranchMinimumVersionOverride) { 1.20 + let branch = "testing.datareporting." + name; 1.21 + 1.22 + // The version prefs should not be removed on reset, so set them in the 1.23 + // default branch. 1.24 + let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy." 1.25 + , defaultBranch: true }); 1.26 + defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion); 1.27 + defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion); 1.28 + let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false); 1.29 + if (aBranchMinimumVersionOverride !== undefined) 1.30 + defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride); 1.31 + else 1.32 + defaultPolicyPrefs.reset(branchOverridePrefName); 1.33 + 1.34 + let policyPrefs = new Preferences(branch + ".policy."); 1.35 + let healthReportPrefs = new Preferences(branch + ".healthreport."); 1.36 + 1.37 + let listener = new MockPolicyListener(); 1.38 + let policy = new DataReportingPolicy(policyPrefs, healthReportPrefs, listener); 1.39 + 1.40 + return [policy, policyPrefs, healthReportPrefs, listener]; 1.41 +} 1.42 + 1.43 +function defineNow(policy, now) { 1.44 + print("Adjusting fake system clock to " + now); 1.45 + Object.defineProperty(policy, "now", { 1.46 + value: function customNow() { 1.47 + return now; 1.48 + }, 1.49 + writable: true, 1.50 + }); 1.51 +} 1.52 + 1.53 +function run_test() { 1.54 + run_next_test(); 1.55 +} 1.56 + 1.57 +add_test(function test_constructor() { 1.58 + let policyPrefs = new Preferences("foo.bar.policy."); 1.59 + let hrPrefs = new Preferences("foo.bar.healthreport."); 1.60 + let listener = { 1.61 + onRequestDataUpload: function() {}, 1.62 + onRequestRemoteDelete: function() {}, 1.63 + onNotifyDataPolicy: function() {}, 1.64 + }; 1.65 + 1.66 + let policy = new DataReportingPolicy(policyPrefs, hrPrefs, listener); 1.67 + do_check_true(Date.now() - policy.firstRunDate.getTime() < 1000); 1.68 + 1.69 + let tomorrow = Date.now() + 24 * 60 * 60 * 1000; 1.70 + do_check_true(tomorrow - policy.nextDataSubmissionDate.getTime() < 1000); 1.71 + 1.72 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); 1.73 + 1.74 + run_next_test(); 1.75 +}); 1.76 + 1.77 +add_test(function test_prefs() { 1.78 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("prefs"); 1.79 + 1.80 + let now = new Date(); 1.81 + let nowT = now.getTime(); 1.82 + 1.83 + policy.firstRunDate = now; 1.84 + do_check_eq(policyPrefs.get("firstRunTime"), nowT); 1.85 + do_check_eq(policy.firstRunDate.getTime(), nowT); 1.86 + 1.87 + policy.dataSubmissionPolicyNotifiedDate= now; 1.88 + do_check_eq(policyPrefs.get("dataSubmissionPolicyNotifiedTime"), nowT); 1.89 + do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), nowT); 1.90 + 1.91 + policy.dataSubmissionPolicyResponseDate = now; 1.92 + do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseTime"), nowT); 1.93 + do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), nowT); 1.94 + 1.95 + policy.dataSubmissionPolicyResponseType = "type-1"; 1.96 + do_check_eq(policyPrefs.get("dataSubmissionPolicyResponseType"), "type-1"); 1.97 + do_check_eq(policy.dataSubmissionPolicyResponseType, "type-1"); 1.98 + 1.99 + policy.dataSubmissionEnabled = false; 1.100 + do_check_false(policyPrefs.get("dataSubmissionEnabled", true)); 1.101 + do_check_false(policy.dataSubmissionEnabled); 1.102 + 1.103 + policy.dataSubmissionPolicyAccepted = false; 1.104 + do_check_false(policyPrefs.get("dataSubmissionPolicyAccepted", true)); 1.105 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.106 + 1.107 + do_check_false(policy.dataSubmissionPolicyBypassAcceptance); 1.108 + policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); 1.109 + do_check_true(policy.dataSubmissionPolicyBypassAcceptance); 1.110 + 1.111 + policy.lastDataSubmissionRequestedDate = now; 1.112 + do_check_eq(hrPrefs.get("lastDataSubmissionRequestedTime"), nowT); 1.113 + do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), nowT); 1.114 + 1.115 + policy.lastDataSubmissionSuccessfulDate = now; 1.116 + do_check_eq(hrPrefs.get("lastDataSubmissionSuccessfulTime"), nowT); 1.117 + do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), nowT); 1.118 + 1.119 + policy.lastDataSubmissionFailureDate = now; 1.120 + do_check_eq(hrPrefs.get("lastDataSubmissionFailureTime"), nowT); 1.121 + do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), nowT); 1.122 + 1.123 + policy.nextDataSubmissionDate = now; 1.124 + do_check_eq(hrPrefs.get("nextDataSubmissionTime"), nowT); 1.125 + do_check_eq(policy.nextDataSubmissionDate.getTime(), nowT); 1.126 + 1.127 + policy.currentDaySubmissionFailureCount = 2; 1.128 + do_check_eq(hrPrefs.get("currentDaySubmissionFailureCount", 0), 2); 1.129 + do_check_eq(policy.currentDaySubmissionFailureCount, 2); 1.130 + 1.131 + policy.pendingDeleteRemoteData = true; 1.132 + do_check_true(hrPrefs.get("pendingDeleteRemoteData")); 1.133 + do_check_true(policy.pendingDeleteRemoteData); 1.134 + 1.135 + policy.healthReportUploadEnabled = false; 1.136 + do_check_false(hrPrefs.get("uploadEnabled")); 1.137 + do_check_false(policy.healthReportUploadEnabled); 1.138 + 1.139 + do_check_false(policy.healthReportUploadLocked); 1.140 + hrPrefs.lock("uploadEnabled"); 1.141 + do_check_true(policy.healthReportUploadLocked); 1.142 + hrPrefs.unlock("uploadEnabled"); 1.143 + do_check_false(policy.healthReportUploadLocked); 1.144 + 1.145 + run_next_test(); 1.146 +}); 1.147 + 1.148 +add_test(function test_notify_state_prefs() { 1.149 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notify_state_prefs"); 1.150 + 1.151 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); 1.152 + 1.153 + policy._dataSubmissionPolicyNotifiedDate = new Date(); 1.154 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); 1.155 + 1.156 + policy.dataSubmissionPolicyResponseDate = new Date(); 1.157 + policy._dataSubmissionPolicyNotifiedDate = null; 1.158 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); 1.159 + 1.160 + run_next_test(); 1.161 +}); 1.162 + 1.163 +add_task(function test_initial_submission_notification() { 1.164 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("initial_submission_notification"); 1.165 + 1.166 + do_check_eq(listener.notifyUserCount, 0); 1.167 + 1.168 + // Fresh instances should not do anything initially. 1.169 + policy.checkStateAndTrigger(); 1.170 + do_check_eq(listener.notifyUserCount, 0); 1.171 + 1.172 + // We still shouldn't notify up to the millisecond before the barrier. 1.173 + defineNow(policy, new Date(policy.firstRunDate.getTime() + 1.174 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC - 1)); 1.175 + policy.checkStateAndTrigger(); 1.176 + do_check_eq(listener.notifyUserCount, 0); 1.177 + do_check_null(policy._dataSubmissionPolicyNotifiedDate); 1.178 + do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); 1.179 + 1.180 + // We have crossed the threshold. We should see notification. 1.181 + defineNow(policy, new Date(policy.firstRunDate.getTime() + 1.182 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC)); 1.183 + policy.checkStateAndTrigger(); 1.184 + do_check_eq(listener.notifyUserCount, 1); 1.185 + yield listener.lastNotifyRequest.onUserNotifyComplete(); 1.186 + do_check_true(policy._dataSubmissionPolicyNotifiedDate instanceof Date); 1.187 + do_check_true(policy.dataSubmissionPolicyNotifiedDate.getTime() > 0); 1.188 + do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 1.189 + policy._dataSubmissionPolicyNotifiedDate.getTime()); 1.190 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); 1.191 +}); 1.192 + 1.193 +add_test(function test_bypass_acceptance() { 1.194 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("bypass_acceptance"); 1.195 + 1.196 + policyPrefs.set("dataSubmissionPolicyBypassAcceptance", true); 1.197 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.198 + do_check_true(policy.dataSubmissionPolicyBypassAcceptance); 1.199 + defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime())); 1.200 + policy.checkStateAndTrigger(); 1.201 + do_check_eq(listener.requestDataUploadCount, 1); 1.202 + 1.203 + run_next_test(); 1.204 +}); 1.205 + 1.206 +add_task(function test_notification_implicit_acceptance() { 1.207 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_implicit_acceptance"); 1.208 + 1.209 + let now = new Date(policy.nextDataSubmissionDate.getTime() - 1.210 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); 1.211 + defineNow(policy, now); 1.212 + policy.checkStateAndTrigger(); 1.213 + do_check_eq(listener.notifyUserCount, 1); 1.214 + yield listener.lastNotifyRequest.onUserNotifyComplete(); 1.215 + do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded"); 1.216 + 1.217 + do_check_true(5000 < policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC); 1.218 + defineNow(policy, new Date(now.getTime() + 5000)); 1.219 + policy.checkStateAndTrigger(); 1.220 + do_check_eq(listener.notifyUserCount, 1); 1.221 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); 1.222 + do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), 0); 1.223 + do_check_eq(policy.dataSubmissionPolicyResponseType, "none-recorded"); 1.224 + 1.225 + defineNow(policy, new Date(now.getTime() + policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + 1)); 1.226 + policy.checkStateAndTrigger(); 1.227 + do_check_eq(listener.notifyUserCount, 1); 1.228 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); 1.229 + do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), policy.now().getTime()); 1.230 + do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-implicit-time-elapsed"); 1.231 +}); 1.232 + 1.233 +add_task(function test_notification_rejected() { 1.234 + // User notification failed. We should not record it as being presented. 1.235 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_failed"); 1.236 + 1.237 + let now = new Date(policy.nextDataSubmissionDate.getTime() - 1.238 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); 1.239 + defineNow(policy, now); 1.240 + policy.checkStateAndTrigger(); 1.241 + do_check_eq(listener.notifyUserCount, 1); 1.242 + yield listener.lastNotifyRequest.onUserNotifyFailed(new Error("testing failed.")); 1.243 + do_check_null(policy._dataSubmissionPolicyNotifiedDate); 1.244 + do_check_eq(policy.dataSubmissionPolicyNotifiedDate.getTime(), 0); 1.245 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_UNNOTIFIED); 1.246 +}); 1.247 + 1.248 +add_task(function test_notification_accepted() { 1.249 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_accepted"); 1.250 + 1.251 + let now = new Date(policy.nextDataSubmissionDate.getTime() - 1.252 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); 1.253 + defineNow(policy, now); 1.254 + policy.checkStateAndTrigger(); 1.255 + yield listener.lastNotifyRequest.onUserNotifyComplete(); 1.256 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); 1.257 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.258 + listener.lastNotifyRequest.onUserNotifyComplete(); 1.259 + listener.lastNotifyRequest.onUserAccept("foo-bar"); 1.260 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); 1.261 + do_check_eq(policy.dataSubmissionPolicyResponseType, "accepted-foo-bar"); 1.262 + do_check_true(policy.dataSubmissionPolicyAccepted); 1.263 + do_check_eq(policy.dataSubmissionPolicyResponseDate.getTime(), now.getTime()); 1.264 +}); 1.265 + 1.266 +add_task(function test_notification_rejected() { 1.267 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("notification_rejected"); 1.268 + 1.269 + let now = new Date(policy.nextDataSubmissionDate.getTime() - 1.270 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC + 1); 1.271 + defineNow(policy, now); 1.272 + policy.checkStateAndTrigger(); 1.273 + yield listener.lastNotifyRequest.onUserNotifyComplete(); 1.274 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_WAIT); 1.275 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.276 + listener.lastNotifyRequest.onUserReject(); 1.277 + do_check_eq(policy.notifyState, policy.STATE_NOTIFY_COMPLETE); 1.278 + do_check_eq(policy.dataSubmissionPolicyResponseType, "rejected-no-reason"); 1.279 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.280 + 1.281 + // No requests for submission should occur if user has rejected. 1.282 + defineNow(policy, new Date(policy.nextDataSubmissionDate.getTime() + 10000)); 1.283 + policy.checkStateAndTrigger(); 1.284 + do_check_eq(listener.requestDataUploadCount, 0); 1.285 +}); 1.286 + 1.287 +add_test(function test_submission_kill_switch() { 1.288 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_kill_switch"); 1.289 + 1.290 + policy.firstRunDate = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000); 1.291 + policy.nextDataSubmissionDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.292 + policy.recordUserAcceptance("accept-old-ack"); 1.293 + do_check_true(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); 1.294 + policy.checkStateAndTrigger(); 1.295 + do_check_eq(listener.requestDataUploadCount, 1); 1.296 + 1.297 + defineNow(policy, 1.298 + new Date(Date.now() + policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC + 100)); 1.299 + policy.dataSubmissionEnabled = false; 1.300 + policy.checkStateAndTrigger(); 1.301 + do_check_eq(listener.requestDataUploadCount, 1); 1.302 + 1.303 + run_next_test(); 1.304 +}); 1.305 + 1.306 +add_test(function test_upload_kill_switch() { 1.307 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("upload_kill_switch"); 1.308 + 1.309 + defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); 1.310 + policy.recordUserAcceptance(); 1.311 + defineNow(policy, policy.nextDataSubmissionDate); 1.312 + 1.313 + // So that we don't trigger deletions, which cause uploads to be delayed. 1.314 + hrPrefs.ignore("uploadEnabled", policy.uploadEnabledObserver); 1.315 + 1.316 + policy.healthReportUploadEnabled = false; 1.317 + policy.checkStateAndTrigger(); 1.318 + do_check_eq(listener.requestDataUploadCount, 0); 1.319 + policy.healthReportUploadEnabled = true; 1.320 + policy.checkStateAndTrigger(); 1.321 + do_check_eq(listener.requestDataUploadCount, 1); 1.322 + 1.323 + run_next_test(); 1.324 +}); 1.325 + 1.326 +add_test(function test_data_submission_no_data() { 1.327 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_no_data"); 1.328 + 1.329 + policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.330 + policy.dataSubmissionPolicyAccepted = true; 1.331 + let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); 1.332 + defineNow(policy, now); 1.333 + do_check_eq(listener.requestDataUploadCount, 0); 1.334 + policy.checkStateAndTrigger(); 1.335 + do_check_eq(listener.requestDataUploadCount, 1); 1.336 + listener.lastDataRequest.onNoDataAvailable(); 1.337 + 1.338 + // The next trigger should try again. 1.339 + defineNow(policy, new Date(now.getTime() + 155 * 60 * 1000)); 1.340 + policy.checkStateAndTrigger(); 1.341 + do_check_eq(listener.requestDataUploadCount, 2); 1.342 + 1.343 + run_next_test(); 1.344 +}); 1.345 + 1.346 +add_task(function test_data_submission_submit_failure_hard() { 1.347 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_submit_failure_hard"); 1.348 + 1.349 + policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.350 + policy.dataSubmissionPolicyAccepted = true; 1.351 + let nextDataSubmissionDate = policy.nextDataSubmissionDate; 1.352 + let now = new Date(policy.nextDataSubmissionDate.getTime() + 1); 1.353 + defineNow(policy, now); 1.354 + 1.355 + policy.checkStateAndTrigger(); 1.356 + do_check_eq(listener.requestDataUploadCount, 1); 1.357 + yield listener.lastDataRequest.onSubmissionFailureHard(); 1.358 + do_check_eq(listener.lastDataRequest.state, 1.359 + listener.lastDataRequest.SUBMISSION_FAILURE_HARD); 1.360 + 1.361 + let expected = new Date(now.getTime() + 24 * 60 * 60 * 1000); 1.362 + do_check_eq(policy.nextDataSubmissionDate.getTime(), expected.getTime()); 1.363 + 1.364 + defineNow(policy, new Date(now.getTime() + 10)); 1.365 + policy.checkStateAndTrigger(); 1.366 + do_check_eq(listener.requestDataUploadCount, 1); 1.367 +}); 1.368 + 1.369 +add_task(function test_data_submission_submit_try_again() { 1.370 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("data_submission_failure_soft"); 1.371 + 1.372 + policy.recordUserAcceptance(); 1.373 + let nextDataSubmissionDate = policy.nextDataSubmissionDate; 1.374 + let now = new Date(policy.nextDataSubmissionDate.getTime()); 1.375 + defineNow(policy, now); 1.376 + policy.checkStateAndTrigger(); 1.377 + yield listener.lastDataRequest.onSubmissionFailureSoft(); 1.378 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.379 + nextDataSubmissionDate.getTime() + 15 * 60 * 1000); 1.380 +}); 1.381 + 1.382 +add_task(function test_submission_daily_scheduling() { 1.383 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_daily_scheduling"); 1.384 + 1.385 + policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.386 + policy.dataSubmissionPolicyAccepted = true; 1.387 + let nextDataSubmissionDate = policy.nextDataSubmissionDate; 1.388 + 1.389 + // Skip ahead to next submission date. We should get a submission request. 1.390 + let now = new Date(policy.nextDataSubmissionDate.getTime()); 1.391 + defineNow(policy, now); 1.392 + policy.checkStateAndTrigger(); 1.393 + do_check_eq(listener.requestDataUploadCount, 1); 1.394 + do_check_eq(policy.lastDataSubmissionRequestedDate.getTime(), now.getTime()); 1.395 + 1.396 + let finishedDate = new Date(now.getTime() + 250); 1.397 + defineNow(policy, new Date(finishedDate.getTime() + 50)); 1.398 + yield listener.lastDataRequest.onSubmissionSuccess(finishedDate); 1.399 + do_check_eq(policy.lastDataSubmissionSuccessfulDate.getTime(), finishedDate.getTime()); 1.400 + 1.401 + // Next scheduled submission should be exactly 1 day after the reported 1.402 + // submission success. 1.403 + 1.404 + let nextScheduled = new Date(finishedDate.getTime() + 24 * 60 * 60 * 1000); 1.405 + do_check_eq(policy.nextDataSubmissionDate.getTime(), nextScheduled.getTime()); 1.406 + 1.407 + // Fast forward some arbitrary time. We shouldn't do any work yet. 1.408 + defineNow(policy, new Date(now.getTime() + 40000)); 1.409 + policy.checkStateAndTrigger(); 1.410 + do_check_eq(listener.requestDataUploadCount, 1); 1.411 + 1.412 + defineNow(policy, nextScheduled); 1.413 + policy.checkStateAndTrigger(); 1.414 + do_check_eq(listener.requestDataUploadCount, 2); 1.415 + yield listener.lastDataRequest.onSubmissionSuccess(new Date(nextScheduled.getTime() + 200)); 1.416 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.417 + new Date(nextScheduled.getTime() + 24 * 60 * 60 * 1000 + 200).getTime()); 1.418 +}); 1.419 + 1.420 +add_test(function test_submission_far_future_scheduling() { 1.421 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_far_future_scheduling"); 1.422 + 1.423 + let now = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.424 + defineNow(policy, now); 1.425 + policy.recordUserAcceptance(); 1.426 + now = new Date(); 1.427 + defineNow(policy, now); 1.428 + 1.429 + let nextDate = policy._futureDate(3 * 24 * 60 * 60 * 1000 - 1); 1.430 + policy.nextDataSubmissionDate = nextDate; 1.431 + policy.checkStateAndTrigger(); 1.432 + do_check_eq(listener.requestDataUploadCount, 0); 1.433 + do_check_eq(policy.nextDataSubmissionDate.getTime(), nextDate.getTime()); 1.434 + 1.435 + policy.nextDataSubmissionDate = new Date(nextDate.getTime() + 1); 1.436 + policy.checkStateAndTrigger(); 1.437 + do_check_eq(listener.requestDataUploadCount, 0); 1.438 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.439 + policy._futureDate(24 * 60 * 60 * 1000).getTime()); 1.440 + 1.441 + run_next_test(); 1.442 +}); 1.443 + 1.444 +add_task(function test_submission_backoff() { 1.445 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_backoff"); 1.446 + 1.447 + do_check_eq(policy.FAILURE_BACKOFF_INTERVALS.length, 2); 1.448 + 1.449 + policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.450 + policy.dataSubmissionPolicyAccepted = true; 1.451 + 1.452 + let now = new Date(policy.nextDataSubmissionDate.getTime()); 1.453 + defineNow(policy, now); 1.454 + policy.checkStateAndTrigger(); 1.455 + do_check_eq(listener.requestDataUploadCount, 1); 1.456 + do_check_eq(policy.currentDaySubmissionFailureCount, 0); 1.457 + 1.458 + now = new Date(now.getTime() + 5000); 1.459 + defineNow(policy, now); 1.460 + 1.461 + // On first soft failure we should back off by scheduled interval. 1.462 + yield listener.lastDataRequest.onSubmissionFailureSoft(); 1.463 + do_check_eq(policy.currentDaySubmissionFailureCount, 1); 1.464 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.465 + new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[0]).getTime()); 1.466 + do_check_eq(policy.lastDataSubmissionFailureDate.getTime(), now.getTime()); 1.467 + 1.468 + // Should not request submission until scheduled. 1.469 + now = new Date(policy.nextDataSubmissionDate.getTime() - 1); 1.470 + defineNow(policy, now); 1.471 + policy.checkStateAndTrigger(); 1.472 + do_check_eq(listener.requestDataUploadCount, 1); 1.473 + 1.474 + // 2nd request for submission. 1.475 + now = new Date(policy.nextDataSubmissionDate.getTime()); 1.476 + defineNow(policy, now); 1.477 + policy.checkStateAndTrigger(); 1.478 + do_check_eq(listener.requestDataUploadCount, 2); 1.479 + 1.480 + now = new Date(now.getTime() + 5000); 1.481 + defineNow(policy, now); 1.482 + 1.483 + // On second failure we should back off by more. 1.484 + yield listener.lastDataRequest.onSubmissionFailureSoft(); 1.485 + do_check_eq(policy.currentDaySubmissionFailureCount, 2); 1.486 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.487 + new Date(now.getTime() + policy.FAILURE_BACKOFF_INTERVALS[1]).getTime()); 1.488 + 1.489 + now = new Date(policy.nextDataSubmissionDate.getTime()); 1.490 + defineNow(policy, now); 1.491 + policy.checkStateAndTrigger(); 1.492 + do_check_eq(listener.requestDataUploadCount, 3); 1.493 + 1.494 + now = new Date(now.getTime() + 5000); 1.495 + defineNow(policy, now); 1.496 + 1.497 + // On 3rd failure we should back off by a whole day. 1.498 + yield listener.lastDataRequest.onSubmissionFailureSoft(); 1.499 + do_check_eq(policy.currentDaySubmissionFailureCount, 0); 1.500 + do_check_eq(policy.nextDataSubmissionDate.getTime(), 1.501 + new Date(now.getTime() + 24 * 60 * 60 * 1000).getTime()); 1.502 +}); 1.503 + 1.504 +// Ensure that only one submission request can be active at a time. 1.505 +add_test(function test_submission_expiring() { 1.506 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("submission_expiring"); 1.507 + 1.508 + policy.dataSubmissionPolicyResponseDate = new Date(Date.now() - 24 * 60 * 60 * 1000); 1.509 + policy.dataSubmissionPolicyAccepted = true; 1.510 + let nextDataSubmission = policy.nextDataSubmissionDate; 1.511 + let now = new Date(policy.nextDataSubmissionDate.getTime()); 1.512 + defineNow(policy, now); 1.513 + policy.checkStateAndTrigger(); 1.514 + do_check_eq(listener.requestDataUploadCount, 1); 1.515 + defineNow(policy, new Date(now.getTime() + 500)); 1.516 + policy.checkStateAndTrigger(); 1.517 + do_check_eq(listener.requestDataUploadCount, 1); 1.518 + 1.519 + defineNow(policy, new Date(policy.now().getTime() + 1.520 + policy.SUBMISSION_REQUEST_EXPIRE_INTERVAL_MSEC)); 1.521 + 1.522 + policy.checkStateAndTrigger(); 1.523 + do_check_eq(listener.requestDataUploadCount, 2); 1.524 + 1.525 + run_next_test(); 1.526 +}); 1.527 + 1.528 +add_task(function test_delete_remote_data() { 1.529 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data"); 1.530 + 1.531 + do_check_false(policy.pendingDeleteRemoteData); 1.532 + let nextSubmissionDate = policy.nextDataSubmissionDate; 1.533 + 1.534 + let now = new Date(); 1.535 + defineNow(policy, now); 1.536 + 1.537 + policy.deleteRemoteData(); 1.538 + do_check_true(policy.pendingDeleteRemoteData); 1.539 + do_check_neq(nextSubmissionDate.getTime(), 1.540 + policy.nextDataSubmissionDate.getTime()); 1.541 + do_check_eq(now.getTime(), policy.nextDataSubmissionDate.getTime()); 1.542 + 1.543 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.544 + do_check_true(listener.lastRemoteDeleteRequest.isDelete); 1.545 + defineNow(policy, policy._futureDate(1000)); 1.546 + 1.547 + yield listener.lastRemoteDeleteRequest.onSubmissionSuccess(policy.now()); 1.548 + do_check_false(policy.pendingDeleteRemoteData); 1.549 +}); 1.550 + 1.551 +// Ensure that deletion requests take priority over regular data submission. 1.552 +add_test(function test_delete_remote_data_priority() { 1.553 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_priority"); 1.554 + 1.555 + let now = new Date(); 1.556 + defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); 1.557 + policy.recordUserAcceptance(); 1.558 + defineNow(policy, new Date(now.getTime() + 3 * 24 * 60 * 60 * 1000)); 1.559 + 1.560 + policy.checkStateAndTrigger(); 1.561 + do_check_eq(listener.requestDataUploadCount, 1); 1.562 + policy._inProgressSubmissionRequest = null; 1.563 + 1.564 + policy.deleteRemoteData(); 1.565 + policy.checkStateAndTrigger(); 1.566 + 1.567 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.568 + do_check_eq(listener.requestDataUploadCount, 1); 1.569 + 1.570 + run_next_test(); 1.571 +}); 1.572 + 1.573 +add_test(function test_delete_remote_data_backoff() { 1.574 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_backoff"); 1.575 + 1.576 + let now = new Date(); 1.577 + defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); 1.578 + policy.recordUserAcceptance(); 1.579 + defineNow(policy, now); 1.580 + policy.nextDataSubmissionDate = now; 1.581 + policy.deleteRemoteData(); 1.582 + 1.583 + policy.checkStateAndTrigger(); 1.584 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.585 + defineNow(policy, policy._futureDate(1000)); 1.586 + policy.checkStateAndTrigger(); 1.587 + do_check_eq(listener.requestDataUploadCount, 0); 1.588 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.589 + 1.590 + defineNow(policy, policy._futureDate(500)); 1.591 + listener.lastRemoteDeleteRequest.onSubmissionFailureSoft(); 1.592 + defineNow(policy, policy._futureDate(50)); 1.593 + 1.594 + policy.checkStateAndTrigger(); 1.595 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.596 + 1.597 + defineNow(policy, policy._futureDate(policy.FAILURE_BACKOFF_INTERVALS[0] - 50)); 1.598 + policy.checkStateAndTrigger(); 1.599 + do_check_eq(listener.requestRemoteDeleteCount, 2); 1.600 + 1.601 + run_next_test(); 1.602 +}); 1.603 + 1.604 +// If we request delete while an upload is in progress, delete should be 1.605 +// scheduled immediately after upload. 1.606 +add_task(function test_delete_remote_data_in_progress_upload() { 1.607 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("delete_remote_data_in_progress_upload"); 1.608 + 1.609 + let now = new Date(); 1.610 + defineNow(policy, policy._futureDate(-24 * 60 * 60 * 1000)); 1.611 + policy.recordUserAcceptance(); 1.612 + defineNow(policy, policy.nextDataSubmissionDate); 1.613 + 1.614 + policy.checkStateAndTrigger(); 1.615 + do_check_eq(listener.requestDataUploadCount, 1); 1.616 + defineNow(policy, policy._futureDate(50 * 1000)); 1.617 + 1.618 + // If we request a delete during a pending request, nothing should be done. 1.619 + policy.deleteRemoteData(); 1.620 + policy.checkStateAndTrigger(); 1.621 + do_check_eq(listener.requestDataUploadCount, 1); 1.622 + do_check_eq(listener.requestRemoteDeleteCount, 0); 1.623 + 1.624 + // Now wait a little bit and finish the request. 1.625 + defineNow(policy, policy._futureDate(10 * 1000)); 1.626 + yield listener.lastDataRequest.onSubmissionSuccess(policy._futureDate(1000)); 1.627 + defineNow(policy, policy._futureDate(5000)); 1.628 + 1.629 + policy.checkStateAndTrigger(); 1.630 + do_check_eq(listener.requestDataUploadCount, 1); 1.631 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.632 +}); 1.633 + 1.634 +add_test(function test_polling() { 1.635 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling"); 1.636 + let intended = 500; 1.637 + let acceptable = 250; // Because nsITimer doesn't guarantee times. 1.638 + 1.639 + // Ensure checkStateAndTrigger is called at a regular interval. 1.640 + let then = Date.now(); 1.641 + print("Starting run: " + then); 1.642 + Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { 1.643 + value: intended, 1.644 + }); 1.645 + let count = 0; 1.646 + 1.647 + Object.defineProperty(policy, "checkStateAndTrigger", { 1.648 + value: function fakeCheckStateAndTrigger() { 1.649 + let now = Date.now(); 1.650 + let after = now - then; 1.651 + count++; 1.652 + 1.653 + print("Polled at " + now + " after " + after + "ms, intended " + intended); 1.654 + do_check_true(after >= acceptable); 1.655 + DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); 1.656 + 1.657 + if (count >= 2) { 1.658 + policy.stopPolling(); 1.659 + 1.660 + do_check_eq(listener.notifyUserCount, 0); 1.661 + do_check_eq(listener.requestDataUploadCount, 0); 1.662 + 1.663 + run_next_test(); 1.664 + } 1.665 + 1.666 + // "Specified timer period will be at least the time between when 1.667 + // processing for last firing the callback completes and when the next 1.668 + // firing occurs." 1.669 + // 1.670 + // That means we should set 'then' at the *end* of our handler, not 1.671 + // earlier. 1.672 + then = Date.now(); 1.673 + } 1.674 + }); 1.675 + policy.startPolling(); 1.676 +}); 1.677 + 1.678 +// Ensure that implicit acceptance of policy is resolved through polling. 1.679 +// 1.680 +// This is probably covered by other tests. But, it's best to have explicit 1.681 +// coverage from a higher-level. 1.682 +add_test(function test_polling_implicit_acceptance() { 1.683 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("polling_implicit_acceptance"); 1.684 + 1.685 + // Redefine intervals with shorter, test-friendly values. 1.686 + Object.defineProperty(policy, "POLL_INTERVAL_MSEC", { 1.687 + value: 250, 1.688 + }); 1.689 + 1.690 + Object.defineProperty(policy, "IMPLICIT_ACCEPTANCE_INTERVAL_MSEC", { 1.691 + value: 700, 1.692 + }); 1.693 + 1.694 + let count = 0; 1.695 + 1.696 + // Track JS elapsed time, so we can decide if we've waited for enough ticks. 1.697 + let start; 1.698 + Object.defineProperty(policy, "checkStateAndTrigger", { 1.699 + value: function CheckStateAndTriggerProxy() { 1.700 + count++; 1.701 + let now = Date.now(); 1.702 + let delta = now - start; 1.703 + print("checkStateAndTrigger count: " + count + ", now " + now + 1.704 + ", delta " + delta); 1.705 + 1.706 + // Account for some slack. 1.707 + DataReportingPolicy.prototype.checkStateAndTrigger.call(policy); 1.708 + 1.709 + // What should happen on different invocations: 1.710 + // 1.711 + // 1) We are inside the prompt interval so user gets prompted. 1.712 + // 2) still ~300ms away from implicit acceptance 1.713 + // 3) still ~50ms away from implicit acceptance 1.714 + // 4) Implicit acceptance recorded. Data submission requested. 1.715 + // 5) Request still pending. No new submission requested. 1.716 + // 1.717 + // Note that, due to the inaccuracy of timers, 4 might not happen until 5 1.718 + // firings have occurred. Yay. So we watch times, not just counts. 1.719 + 1.720 + do_check_eq(listener.notifyUserCount, 1); 1.721 + 1.722 + if (count == 1) { 1.723 + listener.lastNotifyRequest.onUserNotifyComplete(); 1.724 + } 1.725 + 1.726 + if (delta <= (policy.IMPLICIT_ACCEPTANCE_INTERVAL_MSEC + policy.POLL_INTERVAL_MSEC)) { 1.727 + do_check_false(policy.dataSubmissionPolicyAccepted); 1.728 + do_check_eq(listener.requestDataUploadCount, 0); 1.729 + } else if (count > 3) { 1.730 + do_check_true(policy.dataSubmissionPolicyAccepted); 1.731 + do_check_eq(policy.dataSubmissionPolicyResponseType, 1.732 + "accepted-implicit-time-elapsed"); 1.733 + do_check_eq(listener.requestDataUploadCount, 1); 1.734 + } 1.735 + 1.736 + if ((count > 4) && policy.dataSubmissionPolicyAccepted) { 1.737 + do_check_eq(listener.requestDataUploadCount, 1); 1.738 + policy.stopPolling(); 1.739 + run_next_test(); 1.740 + } 1.741 + } 1.742 + }); 1.743 + 1.744 + policy.firstRunDate = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000); 1.745 + policy.nextDataSubmissionDate = new Date(Date.now()); 1.746 + start = Date.now(); 1.747 + policy.startPolling(); 1.748 +}); 1.749 + 1.750 +add_task(function test_record_health_report_upload_enabled() { 1.751 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); 1.752 + 1.753 + // Preconditions. 1.754 + do_check_false(policy.pendingDeleteRemoteData); 1.755 + do_check_true(policy.healthReportUploadEnabled); 1.756 + do_check_eq(listener.requestRemoteDeleteCount, 0); 1.757 + 1.758 + // User intent to disable should immediately result in a pending 1.759 + // delete request. 1.760 + policy.recordHealthReportUploadEnabled(false, "testing 1 2 3"); 1.761 + do_check_false(policy.healthReportUploadEnabled); 1.762 + do_check_true(policy.pendingDeleteRemoteData); 1.763 + do_check_eq(listener.requestRemoteDeleteCount, 1); 1.764 + 1.765 + // Fulfilling it should make it go away. 1.766 + yield listener.lastRemoteDeleteRequest.onNoDataAvailable(); 1.767 + do_check_false(policy.pendingDeleteRemoteData); 1.768 + 1.769 + // User intent to enable should get us back to default state. 1.770 + policy.recordHealthReportUploadEnabled(true, "testing 1 2 3"); 1.771 + do_check_false(policy.pendingDeleteRemoteData); 1.772 + do_check_true(policy.healthReportUploadEnabled); 1.773 +}); 1.774 + 1.775 +add_test(function test_pref_change_initiates_deletion() { 1.776 + let [policy, policyPrefs, hrPrefs, listener] = getPolicy("record_health_report_upload_enabled"); 1.777 + 1.778 + // Preconditions. 1.779 + do_check_false(policy.pendingDeleteRemoteData); 1.780 + do_check_true(policy.healthReportUploadEnabled); 1.781 + do_check_eq(listener.requestRemoteDeleteCount, 0); 1.782 + 1.783 + // User intent to disable should indirectly result in a pending 1.784 + // delete request, because the policy is watching for the pref 1.785 + // to change. 1.786 + Object.defineProperty(policy, "deleteRemoteData", { 1.787 + value: function deleteRemoteDataProxy() { 1.788 + do_check_false(policy.healthReportUploadEnabled); 1.789 + do_check_false(policy.pendingDeleteRemoteData); // Just called. 1.790 + 1.791 + run_next_test(); 1.792 + }, 1.793 + }); 1.794 + 1.795 + hrPrefs.set("uploadEnabled", false); 1.796 +}); 1.797 + 1.798 +add_task(function* test_policy_version() { 1.799 + let policy, policyPrefs, hrPrefs, listener, now, firstRunTime; 1.800 + function createPolicy(shouldBeAccepted = false, 1.801 + currentPolicyVersion = 1, minimumPolicyVersion = 1, 1.802 + branchMinimumVersionOverride) { 1.803 + [policy, policyPrefs, hrPrefs, listener] = 1.804 + getPolicy("policy_version_test", currentPolicyVersion, 1.805 + minimumPolicyVersion, branchMinimumVersionOverride); 1.806 + let firstRun = now === undefined; 1.807 + if (firstRun) { 1.808 + firstRunTime = policy.firstRunDate.getTime(); 1.809 + do_check_true(firstRunTime > 0); 1.810 + now = new Date(policy.firstRunDate.getTime() + 1.811 + policy.SUBMISSION_NOTIFY_INTERVAL_MSEC); 1.812 + } 1.813 + else { 1.814 + // The first-run time should not be reset even after policy-version 1.815 + // upgrades. 1.816 + do_check_eq(policy.firstRunDate.getTime(), firstRunTime); 1.817 + } 1.818 + defineNow(policy, now); 1.819 + do_check_eq(policy.dataSubmissionPolicyAccepted, shouldBeAccepted); 1.820 + } 1.821 + 1.822 + function* triggerPolicyCheckAndEnsureNotified(notified = true, accept = true) { 1.823 + policy.checkStateAndTrigger(); 1.824 + do_check_eq(listener.notifyUserCount, Number(notified)); 1.825 + if (notified) { 1.826 + yield listener.lastNotifyRequest.onUserNotifyComplete(); 1.827 + if (accept) { 1.828 + listener.lastNotifyRequest.onUserAccept("because,"); 1.829 + do_check_true(policy.dataSubmissionPolicyAccepted); 1.830 + do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), 1.831 + policyPrefs.get("currentPolicyVersion")); 1.832 + } 1.833 + else { 1.834 + do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); 1.835 + } 1.836 + } 1.837 + } 1.838 + 1.839 + createPolicy(); 1.840 + yield triggerPolicyCheckAndEnsureNotified(); 1.841 + 1.842 + // We shouldn't be notified again if the current version is still valid; 1.843 + createPolicy(true); 1.844 + yield triggerPolicyCheckAndEnsureNotified(false); 1.845 + 1.846 + // Just increasing the current version isn't enough. The minimum 1.847 + // version must be changed. 1.848 + let currentPolicyVersion = policyPrefs.get("currentPolicyVersion"); 1.849 + let minimumPolicyVersion = policyPrefs.get("minimumPolicyVersion"); 1.850 + createPolicy(true, ++currentPolicyVersion, minimumPolicyVersion); 1.851 + yield triggerPolicyCheckAndEnsureNotified(false); 1.852 + do_check_true(policy.dataSubmissionPolicyAccepted); 1.853 + do_check_eq(policyPrefs.get("dataSubmissionPolicyAcceptedVersion"), 1.854 + minimumPolicyVersion); 1.855 + 1.856 + // Increase the minimum policy version and check if we're notified. 1.857 + createPolicy(false, currentPolicyVersion, ++minimumPolicyVersion); 1.858 + do_check_false(policyPrefs.has("dataSubmissionPolicyAcceptedVersion")); 1.859 + yield triggerPolicyCheckAndEnsureNotified(); 1.860 + 1.861 + // Test increasing the minimum version just on the current channel. 1.862 + createPolicy(true, currentPolicyVersion, minimumPolicyVersion); 1.863 + yield triggerPolicyCheckAndEnsureNotified(false); 1.864 + createPolicy(false, ++currentPolicyVersion, minimumPolicyVersion, minimumPolicyVersion + 1); 1.865 + yield triggerPolicyCheckAndEnsureNotified(true); 1.866 +});