133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright 2014 The Chromium Authors. All rights reserved.
233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Use of this source code is governed by a BSD-style license that can be
333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# found in the LICENSE file.
433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport os
533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport StringIO
633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport unittest
733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport mock
933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry import story
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.internal.results import csv_pivot_table_output_formatter
1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.internal.results import page_test_results
1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry import page as page_module
1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.value import improvement_direction
1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.value import scalar
1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckfrom telemetry.value import trace
17a0e5c0de428e9dea6d07dd57c5594fb1f1c17c20Chris Craikfrom tracing.trace_data import trace_data
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef _MakeStorySet():
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  story_set = story.StorySet(base_dir=os.path.dirname(__file__))
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  story_set.AddStory(
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      page_module.Page('http://www.foo.com/', story_set, story_set.base_dir))
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  story_set.AddStory(
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      page_module.Page('http://www.bar.com/', story_set, story_set.base_dir))
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  return story_set
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass CsvPivotTableOutputFormatterTest(unittest.TestCase):
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # The line separator used by CSV formatter.
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  _LINE_SEPARATOR = '\r\n'
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def setUp(self):
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._output = StringIO.StringIO()
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._story_set = _MakeStorySet()
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._results = page_test_results.PageTestResults()
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._formatter = None
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.MakeFormatter()
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def MakeFormatter(self, trace_tag=''):
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._formatter = (
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        csv_pivot_table_output_formatter.CsvPivotTableOutputFormatter(
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            self._output, trace_tag))
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def SimulateBenchmarkRun(self, list_of_page_and_values):
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Simulate one run of a benchmark, using the supplied values.
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    Args:
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      list_of_pages_and_values: a list of tuple (page, list of values)
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    for page, values in list_of_page_and_values:
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._results.WillRunPage(page)
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      for v in values:
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        v.page = page
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        self._results.AddValue(v)
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self._results.DidRunPage(page)
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def Format(self):
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self._formatter.Format(self._results)
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return self._output.getvalue()
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def testSimple(self):
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Test a simple benchmark with only one value:
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.SimulateBenchmarkRun([
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (self._story_set[0], [scalar.ScalarValue(
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            None, 'foo', 'seconds', 3,
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            improvement_direction=improvement_direction.DOWN)])])
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    expected = self._LINE_SEPARATOR.join([
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        'story_set,page,name,value,units,run_index',
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        'story_set,http://www.foo.com/,foo,3,seconds,0',
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        ''])
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertEqual(expected, self.Format())
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  @mock.patch('py_utils.cloud_storage.Insert')
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def testMultiplePagesAndValues(self, cs_insert_mock):
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    cs_insert_mock.return_value = 'https://cloud_storage_url/foo'
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    trace_value = trace.TraceValue(
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        None, trace_data.CreateTraceDataFromRawData('{"traceEvents": []}'))
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    trace_value.UploadToCloud(bucket='foo')
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.SimulateBenchmarkRun([
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (self._story_set[0], [
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck              None, 'foo', 'seconds', 4,
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck              improvement_direction=improvement_direction.DOWN)]),
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (self._story_set[1], [
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                None, 'foo', 'seconds', 3.4,
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                improvement_direction=improvement_direction.DOWN),
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            trace_value,
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                None, 'bar', 'km', 10,
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                improvement_direction=improvement_direction.DOWN),
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                None, 'baz', 'count', 5,
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                improvement_direction=improvement_direction.DOWN)])])
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Parse CSV output into list of lists.
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    csv_string = self.Format()
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    lines = csv_string.split(self._LINE_SEPARATOR)
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    values = [s.split(',') for s in lines[1:-1]]
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertEquals(len(values), 5)  # We expect 5 value in total.
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertEquals(len(set((v[1] for v in values))), 2)  # 2 pages.
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertEquals(len(set((v[2] for v in values))), 4)  # 4 value names.
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertEquals(values[2],
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        ['story_set', 'http://www.bar.com/', 'trace',
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck         'https://cloud_storage_url/foo', '', '1'])
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def testTraceTag(self):
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.MakeFormatter(trace_tag='date,option')
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.SimulateBenchmarkRun([
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        (self._story_set[0], [
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                None, 'foo', 'seconds', 3,
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                improvement_direction=improvement_direction.DOWN),
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            scalar.ScalarValue(
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                None, 'bar', 'tons', 5,
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                improvement_direction=improvement_direction.DOWN)])])
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    output = self.Format().split(self._LINE_SEPARATOR)
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    self.assertTrue(output[0].endswith(',trace_tag_0,trace_tag_1'))
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    for line in output[1:-1]:
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      self.assertTrue(line.endswith(',date,option'))
126