# Lint as: python2, python3
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This is a client side WebGL many planets deep test."""

import numpy
import os
import time

from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib.cros import chrome
from autotest_lib.client.cros.graphics import graphics_utils
from autotest_lib.client.cros.power import power_rapl


class graphics_WebGLManyPlanetsDeep(graphics_utils.GraphicsTest):
    """WebGL many planets deep graphics test."""
    version = 1
    frame_data = {}
    perf_keyval = {}
    test_duration_secs = 30

    def setup(self):
        self.job.setup_dep(['webgl_mpd'])
        self.job.setup_dep(['graphics'])

    def initialize(self):
        super(graphics_WebGLManyPlanetsDeep, self).initialize()

    def cleanup(self):
        super(graphics_WebGLManyPlanetsDeep, self).cleanup()

    def run_many_planets_deep_test(self, browser, test_url):
        """Runs the many planets deep test from the given url.

        @param browser: The Browser object to run the test with.
        @param test_url: The URL to the many planets deep test site.
        """
        if not utils.wait_for_idle_cpu(60.0, 0.1):
            if not utils.wait_for_idle_cpu(20.0, 0.2):
                raise error.TestFail('Failed: Could not get idle CPU.')

        tab = browser.tabs.New()
        tab.Navigate(test_url)
        tab.Activate()
        tab.WaitForDocumentReadyStateToBeComplete()

        # Wait 3 seconds for the page to stabilize.
        time.sleep(3)

        # Reset our own FPS counter and start recording FPS and rendering time.
        end_time = time.time() + self.test_duration_secs
        tab.ExecuteJavaScript('g_crosFpsCounter.reset();')
        while time.time() < end_time:
            frame_data = tab.EvaluateJavaScript(
                'g_crosFpsCounter.getFrameData();')
            for datum in frame_data:
                if not datum or datum['seq'] in self.frame_data:
                    continue
                self.frame_data[datum['seq']] = {
                    'start_time': datum['startTime'],
                    'frame_elapsed_time': datum['frameElapsedTime'],
                    'js_elapsed_time': datum['jsElapsedTime']
                }
            time.sleep(1)

        # Intel only: Record the power consumption for the next few seconds.
        self.rapl_rate = power_rapl.get_rapl_measurement(
            'rapl_many_planets_deep')
        tab.Close()

    def calculate_perf_values(self):
        """Calculates all the perf values from the collected data."""
        arr = numpy.array([[v['frame_elapsed_time'], v['js_elapsed_time']]
                           for v in list(self.frame_data.values())])
        std = arr.std(axis=0)
        mean = arr.mean(axis=0)
        avg_fps = 1000.0 / mean[0]
        self.perf_keyval.update({
            'average_fps': avg_fps,
            'per_frame_dt_ms_std': std[0],
            'per_frame_dt_ms_mean': mean[0],
            'js_render_time_ms_std': std[1],
            'js_render_time_ms_mean': mean[1]
        })

        # Remove entries that we don't care about.
        rapl_rate = {key: self.rapl_rate[key]
                     for key in list(self.rapl_rate.keys()) if key.endswith('pwr')}
        # Report to chromeperf/ dashboard.
        for key, values in list(rapl_rate.items()):
            self.output_perf_value(
                description=key,
                value=values,
                units='W',
                higher_is_better=False,
                graph='rapl_power_consumption'
            )
        self.output_perf_value(
            description='average_fps',
            value=avg_fps,
            units='fps',
            higher_is_better=True)

        with open('frame_data', 'w') as f:
            line_format = '%10s %20s %20s %20s\n'
            f.write(line_format % ('seq', 'start_time', 'frame_render_time_ms',
                                   'js_render_time_ms'))
            for k in sorted(self.frame_data.keys()):
                d = self.frame_data[k]
                f.write(line_format % (k, d['start_time'],
                                       d['frame_elapsed_time'],
                                       d['js_elapsed_time']))

    @graphics_utils.GraphicsTest.failure_report_decorator('graphics_WebGLManyPlanetsDeep')
    def run_once(self, test_duration_secs=30, fullscreen=True):
        """Finds a brower with telemetry, and run the test.

        @param test_duration_secs: The test duration in seconds to run the test
                for.
        @param fullscreen: Whether to run the test in fullscreen.
        """
        self.test_duration_secs = test_duration_secs

        ext_paths = []
        if fullscreen:
            ext_paths.append(
                os.path.join(self.autodir, 'deps', 'graphics',
                             'graphics_test_extension'))

        with chrome.Chrome(logged_in=False,
                           extension_paths=ext_paths,
                           init_network_controller=True) as cr:
            websrc_dir = os.path.join(self.autodir, 'deps', 'webgl_mpd', 'src')
            if not cr.browser.platform.SetHTTPServerDirectories(websrc_dir):
                raise error.TestFail('Failed: Unable to start HTTP server')
            test_url = cr.browser.platform.http_server.UrlOf(
                os.path.join(websrc_dir, 'ManyPlanetsDeep.html'))
            self.run_many_planets_deep_test(cr.browser, test_url)

        self.calculate_perf_values()
        self.write_perf_keyval(self.perf_keyval)
