# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/

from __future__ import absolute_import

import time
import os
from marionette_harness import MarionetteTestCase

LABELS_SCRIPT_PRELOADER_REQUESTS = {
  "Hit": "0",
  "HitChild": "1",
  "Miss": "2",
}


class TestScriptPreloader(MarionetteTestCase):
    def test_preloader_requests_histogram(self):
        start_time = time.time()
        self.invalidate_caches()
        self.marionette.restart(clean=False, in_app=True)
        histogram = self.get_histogram("SCRIPT_PRELOADER_REQUESTS")
        misses = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Miss"], 0)
        hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Hit"], 0)
        child_hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["HitChild"], 0)
        self.assertMuchGreaterThan(misses, hits)
        self.assertMuchGreaterThan(misses, child_hits)

        profile = self.marionette.profile_path
        self.wait_for_file_change(start_time,
                                  "{}/startupCache/scriptCache.bin".format(profile))
        self.wait_for_file_change(start_time,
                                  "{}/startupCache/scriptCache-child.bin".format(profile))
        self.marionette.restart(clean=False, in_app=True)
        histogram = self.get_histogram("SCRIPT_PRELOADER_REQUESTS")
        misses = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Miss"], 0)
        hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["Hit"], 0)
        child_hits = histogram.get(LABELS_SCRIPT_PRELOADER_REQUESTS["HitChild"], 0)
        # This is just heuristic. We certainly want to be made aware if this ratio
        # changes and we didn't intend it to.
        self.assertSimilar(misses, hits)
        self.assertNotEqual(child_hits, 0)

    def assertMuchGreaterThan(self, lhs, rhs):
        self.assertTrue(lhs > 4 * rhs)

    def assertSimilar(self, lhs, rhs):
        self.assertTrue(lhs < 2 * rhs)
        self.assertTrue(lhs > rhs / 2)

    def wait_for_file_change(self, start_time, path):
        expires = time.time() + 20
        while True:
            try:
                if os.stat(path).st_mtime > start_time:
                    return
                if time.time() > expires:
                    raise Exception("Never observed file change for {}".format(path))
                time.sleep(1)
            except OSError:
                pass

    def wait_for_observer_notification(self, name):
        with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
            return self.marionette.execute_script("""
                let [resolve] = arguments;
                Services.obs.addObserver(() => resolve(), "{}");
            """.format(name))

    def get_histogram(self, name):
        with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
            return self.marionette.execute_script("""
                let snapshot = Services.telemetry.getSnapshotForHistograms("main", true);
                return snapshot.parent.{}.values;
            """.format(name))

    def invalidate_caches(self):
        with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
            return self.marionette.execute_script(
                "Services.obs.notifyObservers(null, 'startupcache-invalidate');"
            )
