# Lint as: python2, python3
# Copyright 2018 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.

import logging, time

from autotest_lib.client.bin import utils
from autotest_lib.client.common_lib.cros import chrome
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import service_stopper
from autotest_lib.client.cros.graphics import graphics_utils
from autotest_lib.client.cros.power import power_rapl
from autotest_lib.client.cros.power import power_status
from autotest_lib.client.cros.power import power_utils

TEST_NAME_AND_FLAGS = [
    ['hw_overlays_hw_decode', ['']],
    ['no_overlays_hw_decode', ['--enable-hardware-overlays=']],
    ['hw_overlays_sw_decode', ['--disable-accelerated-video-decode']],
    [
        'no_overlays_sw_decode',
        ['--disable-accelerated-video-decode', '--enable-hardware-overlays=']
    ]
]
# Amount of time to wait for the URL to load and the video to start playing.
PREAMBLE_DURATION_SECONDS = 8
# Amount of time to let the video play while measuring power consumption.
MEASUREMENT_DURATION_SECONDS = 12

# Time in seconds to wait for cpu idle until giveup.
IDLE_CPU_WAIT_TIMEOUT_SECONDS = 60.0
# Maximum percent of cpu usage considered as idle.
IDLE_CPU_LOAD_PERCENTAGE = 2.5

GRAPH_NAME = 'power_consumption'


class graphics_VideoRenderingPower(graphics_utils.GraphicsTest):
    """This test renders on screen for a short while a video from a given

    (controlled) URL while measuring the power consumption of the different SoC
    domains.
    """
    version = 1
    _backlight = None
    _service_stopper = None
    _power_status = None

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

        self._backlight = power_utils.Backlight()
        self._backlight.set_default()

        self._service_stopper = service_stopper.ServiceStopper(
            service_stopper.ServiceStopper.POWER_DRAW_SERVICES)
        self._service_stopper.stop_services()

        self._power_status = power_status.get_status()

    def cleanup(self):
        if self._backlight:
            self._backlight.restore()
        if self._service_stopper:
            self._service_stopper.restore_services()
        super(graphics_VideoRenderingPower, self).cleanup()

    @graphics_utils.GraphicsTest.failure_report_decorator(
        'graphics_VideoRenderingPower')
    def run_once(self, video_url, video_short_name):
        """Runs the graphics_VideoRenderingPower test.

        @param video_url: URL with autoplay video inside. It's assumed that
                 there's just one <video> in the HTML, and that it fits in the
                 viewport.
        @param video_short_name: short string describing the video; itt will be
                 presented as part of the dashboard entry name.
        """

        # TODO(mcasas): Extend this test to non-Intel platforms.
        if not power_utils.has_rapl_support():
            logging.warning('This board has no RAPL power measurement support, '
                            'skipping test.')
            return

        rapl = []
        if power_utils.has_battery():
            # Sometimes, the DUT is supposed to have a battery but we may not
            # detect one. This is a symptom of a bad battery (b/145144707).
            if self._power_status.battery_path is None:
                raise error.TestFail('No battery found in this DUT (this is a '
                                     'symptom of a bad battery).')
            rapl.append(
                power_status.SystemPower(self._power_status.battery_path))
        else:
            logging.warning('This board has no battery.')
        rapl += power_rapl.create_rapl()

        for test_name_and_flags in TEST_NAME_AND_FLAGS:
            logging.info('Test case: %s', test_name_and_flags[0])
            # Launch Chrome with the appropriate flag combination.
            with chrome.Chrome(
                    extra_browser_args=test_name_and_flags[1],
                    init_network_controller=True) as cr:

                if not utils.wait_for_idle_cpu(IDLE_CPU_WAIT_TIMEOUT_SECONDS,
                                               IDLE_CPU_LOAD_PERCENTAGE):
                    raise error.TestFail('Failed: Could not get idle CPU.')
                if not utils.wait_for_cool_machine():
                    raise error.TestFail('Failed: Could not get cold machine.')

                tab = cr.browser.tabs[0]
                tab.Navigate(video_url)
                tab.WaitForDocumentReadyStateToBeComplete()
                tab.EvaluateJavaScript(
                    'document.'
                    'getElementsByTagName(\'video\')[0].scrollIntoView(true)')

                # Disabling hardware overlays is difficult because the flag is
                # already in the browser. Instead, scroll a bit down to make the
                # video bleed out of the viewport.
                if '--enable-hardware-overlays=' in test_name_and_flags[1]:
                    tab.EvaluateJavaScript('window.scrollBy(0, 1)')

                power_logger = power_status.PowerLogger(rapl)
                power_logger.start()
                time.sleep(PREAMBLE_DURATION_SECONDS)

                start_time = time.time()
                time.sleep(MEASUREMENT_DURATION_SECONDS)
                power_logger.checkpoint('result', start_time)

                measurements = power_logger.calc()
                logging.debug(measurements)

                for category in sorted(measurements):
                    if category.endswith('_pwr_avg'):
                        description = '%s_%s_%s' % (
                            video_short_name, test_name_and_flags[0], category)
                        self.output_perf_value(
                            description=description,
                            value=measurements[category],
                            units='W',
                            higher_is_better=False,
                            graph=GRAPH_NAME)

                    if category.endswith('_pwr_avg'):
                        # write_perf_keyval() wants units (W) first in lowercase.
                        description = '%s_%s_%s' % (
                            video_short_name, test_name_and_flags[0], category)
                        self.write_perf_keyval({
                            'w_' + description: measurements[category]
                        })
