Bug 1438953 Increase the epsilon on timer reduction tests r?froydnj draft
authorTom Ritter <tom@mozilla.com>
Fri, 16 Feb 2018 12:31:55 -0600
changeset 757476 e1bfad6a134ae934f99b0c57bb18c504f842b841
parent 756125 928f0f09172fae67f6ec00e7a63969f7b28bd12e
child 758827 3e89f12fe14691944f64c8c5922ff212cb6d9516
push id99765
push userbmo:tom@mozilla.com
push dateTue, 20 Feb 2018 18:38:34 +0000
reviewersfroydnj
bugs1438953, 1435296
milestone60.0a1
Bug 1438953 Increase the epsilon on timer reduction tests r?froydnj In Javascript, we re-clamp timers to ensure they stay the same. Because of double imprecision sometimes they don't stay the same, and are clamped downwards. If that happens we detect it, and if we were originally off by an epsilon from a clamped value, we accept the value in the name of double imprecision. However, the epsilons were originally chosen somewhat arbitrarily. They worked for small numbers, where imprecision from doubles were very small. But large doubles have much less precise fractional parts. So the epsilons were too small for large numbers where the imprecision was larger. In Bug 1435296 we stopped reducing the precision of CSS Animations by default, and changed the test to throw an error if they WERE rounded. After we increased the epsilon in this patch, we began to see false positives - we were getting values that were not rounded, but happened to be within the epsilon window and thus appeared rounded. We change the check so instead of seeing if an Animation is not rounded, and erroring if it is, to accepting any value. MozReview-Commit-ID: HnYYo4cuv96
browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
browser/components/resistfingerprinting/test/browser/file_workerPerformance.js
browser/components/resistfingerprinting/test/mochitest/file_animation_api.html
browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
--- a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
@@ -32,20 +32,25 @@ const PERFORMANCE_TIMINGS = [
 
 let isRounded = (x, expectedPrecision) => {
   let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision);
   // First we do the perfectly normal check that should work just fine
   if (rounded === x || x === 0)
     return true;
 
   // When we're diving by non-whole numbers, we may not get perfect
-  // multiplication/division because of floating points
-  if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+  // multiplication/division because of floating points.
+  // When dealing with ms since epoch, a double's precision is on the order
+  // of 1/5 of a microsecond, so we use a value a little higher than that as
+  // our epsilon.
+  // To be clear, this error is introduced in our re-calculation of 'rounded'
+  // above in JavaScript.
+  if (Math.abs(rounded - x + expectedPrecision) < .0005) {
     return true;
-  } else if (Math.abs(rounded - x) < .0000001) {
+  } else if (Math.abs(rounded - x) < .0005) {
     return true;
   }
 
   // Then we handle the case where you're sub-millisecond and the timer is not
   // We check that the timer is not sub-millisecond by assuming it is not if it
   // returns an even number of milliseconds
   if (expectedPrecision < 1 && Math.round(x) == x) {
     if (Math.round(rounded) == x) {
--- a/browser/components/resistfingerprinting/test/browser/file_workerPerformance.js
+++ b/browser/components/resistfingerprinting/test/browser/file_workerPerformance.js
@@ -12,20 +12,25 @@ function finish() {
 
 let isRounded = (x, expectedPrecision) => {
   let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision);
   // First we do the perfectly normal check that should work just fine
   if (rounded === x || x === 0)
     return true;
 
   // When we're diving by non-whole numbers, we may not get perfect
-  // multiplication/division because of floating points
-  if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+  // multiplication/division because of floating points.
+  // When dealing with ms since epoch, a double's precision is on the order
+  // of 1/5 of a microsecond, so we use a value a little higher than that as
+  // our epsilon.
+  // To be clear, this error is introduced in our re-calculation of 'rounded'
+  // above in JavaScript.
+  if (Math.abs(rounded - x + expectedPrecision) < .0005) {
     return true;
-  } else if (Math.abs(rounded - x) < .0000001) {
+  } else if (Math.abs(rounded - x) < .0005) {
     return true;
   }
 
   // Then we handle the case where you're sub-millisecond and the timer is not
   // We check that the timer is not sub-millisecond by assuming it is not if it
   // returns an even number of milliseconds
   if (expectedPrecision < 1 && Math.round(x) == x) {
     if (Math.round(rounded) == x) {
--- a/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html
+++ b/browser/components/resistfingerprinting/test/mochitest/file_animation_api.html
@@ -31,20 +31,25 @@
     let expectedPrecision = opener.expectedPrecision / 1000;
     let isRounded = (x) => {
       let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision);
       // First we do the perfectly normal check that should work just fine
       if (rounded === x || x === 0)
         return true;
 
       // When we're diving by non-whole numbers, we may not get perfect
-      // multiplication/division because of floating points
-      if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
+      // multiplication/division because of floating points.
+      // When dealing with ms since epoch, a double's precision is on the order
+      // of 1/5 of a microsecond, so we use a value a little higher than that as
+      // our epsilon.
+      // To be clear, this error is introduced in our re-calculation of 'rounded'
+      // above in JavaScript.
+      if (Math.abs(rounded - x + expectedPrecision) < .0005) {
         return true;
-      } else if (Math.abs(rounded - x) < .0000001) {
+      } else if (Math.abs(rounded - x) < .0005) {
         return true;
       }
 
       // Then we handle the case where you're sub-millisecond and the timer is not
       // We check that the timer is not sub-millisecond by assuming it is not if it
       // returns an even number of milliseconds
       if (expectedPrecision < 1 && Math.round(x) == x) {
         if (Math.round(rounded) == x) {
@@ -64,32 +69,32 @@
     const animation = testDiv.animate({ opacity: [0, 1] }, 100000);
     animation.play();
 
     waitForCondition(
       () => animation.currentTime > 100,
         () => {
 
           // We have disabled Time Precision Reduction for CSS Animations, so we expect those tests to fail.
-          // If we are testing that preference, turn failures into successes and successes into failures
-          var maybeInvert = function(value) {
+          // If we are testing that preference, we accept either rounded or not rounded values as A-OK.
+          var maybeAcceptEverything = function(value) {
             if (opener.prefName.includes("privacy.reduceTimerPrecision") &&
                 !opener.prefName.includes("privacy.resistFingerprinting"))
-              return !value;
+              return true;
             return value;
           };
 
-          opener.ok(maybeInvert(isRounded(animation.startTime)),
+          opener.ok(maybeAcceptEverything(isRounded(animation.startTime)),
              "pref: " + opener.prefName + " - animation.startTime with precision " + expectedPrecision + " is not rounded: " + animation.startTime);
-          opener.ok(maybeInvert(isRounded(animation.currentTime)),
+          opener.ok(maybeAcceptEverything(isRounded(animation.currentTime)),
              "pref: " + opener.prefName + " - animation.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.currentTime);
-          opener.ok(maybeInvert(isRounded(animation.timeline.currentTime)),
+          opener.ok(maybeAcceptEverything(isRounded(animation.timeline.currentTime)),
              "pref: " + opener.prefName + " - animation.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + animation.timeline.currentTime);
           if (document.timeline) {
-            opener.ok(maybeInvert(isRounded(document.timeline.currentTime)),
+            opener.ok(maybeAcceptEverything(isRounded(document.timeline.currentTime)),
                "pref: " + opener.prefName + " - document.timeline.currentTime with precision " + expectedPrecision + " is not rounded: " + document.timeline.currentTime);
           }
           opener.done();
           window.close();
         },
         "animation failed to start");
   }
 </script>
--- a/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -45,23 +45,28 @@ https://trac.torproject.org/projects/tor
   ]);
 
   let isRounded = (x, expectedPrecision) => {
     let rounded = (Math.floor(x / expectedPrecision) * expectedPrecision);
     // First we do the perfectly normal check that should work just fine
     if (rounded === x || x === 0)
       return true;
 
-    // When we're diving by non-whole numbers, we may not get perfect
-    // multiplication/division because of floating points
-    if (Math.abs(rounded - x + expectedPrecision) < .0000001) {
-      return true;
-    } else if (Math.abs(rounded - x) < .0000001) {
-      return true;
-    }
+  // When we're diving by non-whole numbers, we may not get perfect
+  // multiplication/division because of floating points.
+  // When dealing with ms since epoch, a double's precision is on the order
+  // of 1/5 of a microsecond, so we use a value a little higher than that as
+  // our epsilon.
+  // To be clear, this error is introduced in our re-calculation of 'rounded'
+  // above in JavaScript.
+  if (Math.abs(rounded - x + expectedPrecision) < .0005) {
+    return true;
+  } else if (Math.abs(rounded - x) < .0005) {
+    return true;
+  }
 
     // Then we handle the case where you're sub-millisecond and the timer is not
     // We check that the timer is not sub-millisecond by assuming it is not if it
     // returns an even number of milliseconds
     if (expectedPrecision < 1 && Math.round(x) == x) {
       if (Math.round(rounded) == x) {
         return true;
       }