1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""This is a client side WebGL many planets deep test."""
5
6import numpy
7import os
8import time
9
10from autotest_lib.client.bin import test
11from autotest_lib.client.common_lib import error
12from autotest_lib.client.bin import utils
13from autotest_lib.client.common_lib.cros import chrome
14from autotest_lib.client.cros.graphics import graphics_utils
15from autotest_lib.client.cros.power import power_rapl
16
17
18class graphics_WebGLManyPlanetsDeep(graphics_utils.GraphicsTest):
19    """WebGL many planets deep graphics test."""
20    version = 1
21    frame_data = {}
22    perf_keyval = {}
23    test_duration_secs = 30
24
25    def setup(self):
26        self.job.setup_dep(['webgl_mpd'])
27        self.job.setup_dep(['graphics'])
28
29    def initialize(self):
30        super(graphics_WebGLManyPlanetsDeep, self).initialize()
31
32    def cleanup(self):
33        super(graphics_WebGLManyPlanetsDeep, self).cleanup()
34
35    def run_many_planets_deep_test(self, browser, test_url):
36        """Runs the many planets deep test from the given url.
37
38        @param browser: The Browser object to run the test with.
39        @param test_url: The URL to the many planets deep test site.
40        """
41        if not utils.wait_for_idle_cpu(60.0, 0.1):
42            if not utils.wait_for_idle_cpu(20.0, 0.2):
43                raise error.TestFail('Failed: Could not get idle CPU.')
44
45        tab = browser.tabs.New()
46        tab.Navigate(test_url)
47        tab.Activate()
48        tab.WaitForDocumentReadyStateToBeComplete()
49
50        # Wait 3 seconds for the page to stabilize.
51        time.sleep(3)
52
53        # Reset our own FPS counter and start recording FPS and rendering time.
54        end_time = time.time() + self.test_duration_secs
55        tab.ExecuteJavaScript('g_crosFpsCounter.reset();')
56        while time.time() < end_time:
57            frame_data = tab.EvaluateJavaScript(
58                'g_crosFpsCounter.getFrameData();')
59            for datum in frame_data:
60                if not datum or datum['seq'] in self.frame_data:
61                    continue
62                self.frame_data[datum['seq']] = {
63                    'start_time': datum['startTime'],
64                    'frame_elapsed_time': datum['frameElapsedTime'],
65                    'js_elapsed_time': datum['jsElapsedTime']
66                }
67            time.sleep(1)
68
69        # Intel only: Record the power consumption for the next few seconds.
70        self.rapl_rate = power_rapl.get_rapl_measurement(
71            'rapl_many_planets_deep')
72        tab.Close()
73
74    def calculate_perf_values(self):
75        """Calculates all the perf values from the collected data."""
76        arr = numpy.array([[v['frame_elapsed_time'], v['js_elapsed_time']]
77                           for v in self.frame_data.itervalues()])
78        std = arr.std(axis=0)
79        mean = arr.mean(axis=0)
80        avg_fps = 1000.0 / mean[0]
81        self.perf_keyval.update({
82            'average_fps': avg_fps,
83            'per_frame_dt_ms_std': std[0],
84            'per_frame_dt_ms_mean': mean[0],
85            'js_render_time_ms_std': std[1],
86            'js_render_time_ms_mean': mean[1]
87        })
88
89        # Remove entries that we don't care about.
90        rapl_rate = {key: self.rapl_rate[key]
91                     for key in self.rapl_rate.keys() if key.endswith('pwr')}
92        # Report to chromeperf/ dashboard.
93        for key, values in rapl_rate.iteritems():
94            self.output_perf_value(
95                description=key,
96                value=values,
97                units='W',
98                higher_is_better=False,
99                graph='rapl_power_consumption'
100            )
101        self.output_perf_value(
102            description='average_fps',
103            value=avg_fps,
104            units='fps',
105            higher_is_better=True)
106
107        with open('frame_data', 'w') as f:
108            line_format = '%10s %20s %20s %20s\n'
109            f.write(line_format % ('seq', 'start_time', 'frame_render_time_ms',
110                                   'js_render_time_ms'))
111            for k in sorted(self.frame_data.keys()):
112                d = self.frame_data[k]
113                f.write(line_format % (k, d['start_time'],
114                                       d['frame_elapsed_time'],
115                                       d['js_elapsed_time']))
116
117    @graphics_utils.GraphicsTest.failure_report_decorator('graphics_WebGLManyPlanetsDeep')
118    def run_once(self, test_duration_secs=30, fullscreen=True):
119        """Finds a brower with telemetry, and run the test.
120
121        @param test_duration_secs: The test duration in seconds to run the test
122                for.
123        @param fullscreen: Whether to run the test in fullscreen.
124        """
125        self.test_duration_secs = test_duration_secs
126
127        ext_paths = []
128        if fullscreen:
129            ext_paths.append(
130                os.path.join(self.autodir, 'deps', 'graphics',
131                             'graphics_test_extension'))
132
133        with chrome.Chrome(logged_in=False,
134                           extension_paths=ext_paths,
135                           init_network_controller=True) as cr:
136            websrc_dir = os.path.join(self.autodir, 'deps', 'webgl_mpd', 'src')
137            if not cr.browser.platform.SetHTTPServerDirectories(websrc_dir):
138                raise error.TestFail('Failed: Unable to start HTTP server')
139            test_url = cr.browser.platform.http_server.UrlOf(
140                os.path.join(websrc_dir, 'ManyPlanetsDeep.html'))
141            self.run_many_planets_deep_test(cr.browser, test_url)
142
143        self.calculate_perf_values()
144        self.write_perf_keyval(self.perf_keyval)
145