1c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# Copyright 2013 The Chromium Authors. All rights reserved.
2c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# Use of this source code is governed by a BSD-style license that can be
3c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch# found in the LICENSE file.
4c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
5c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch"""Request handler to serve the main_view page."""
6c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
7c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport jinja2
8c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport json
9c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport os
10c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport re
11c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport sys
12c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport webapp2
13c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
14c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport ispy_api
15c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfrom common import constants
16c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochfrom common import ispy_utils
17c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
18c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport gs_bucket
19c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochimport views
20c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
21c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen MurdochJINJA = jinja2.Environment(
22c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    loader=jinja2.FileSystemLoader(os.path.dirname(views.__file__)),
23c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    extensions=['jinja2.ext.autoescape'])
24c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
25c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
26c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdochclass MainViewHandler(webapp2.RequestHandler):
27c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  """Request handler to serve the main_view page."""
28c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
29c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def get(self):
30c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """Handles a get request to the main_view page.
31c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
32c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    If the test_run parameter is specified, then a page displaying all of
33c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    the failed runs in the test_run will be shown. Otherwise a view listing
34c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    all of the test_runs available for viewing will be displayed.
35c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    test_run = self.request.get('test_run')
37c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    bucket = gs_bucket.GoogleCloudStorageBucket(constants.BUCKET)
38c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    ispy = ispy_utils.ISpyUtils(bucket)
39c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    # Load the view.
40c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if test_run:
41c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      self._GetForTestRun(test_run, ispy)
42c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return
43c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self._GetAllTestRuns(ispy)
44c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
45c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def _GetAllTestRuns(self, ispy):
46c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """Renders a list view of all of the test_runs available in GS.
47c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
48c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    Args:
49c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ispy: An instance of ispy_api.ISpyApi.
50c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """
51c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    template = JINJA.get_template('list_view.html')
52c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    data = {}
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    max_keys = 1000
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    marker = 'failures/%s' % self.request.get('marker')
55116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    test_runs = list([path.split('/')[1] for path in
56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch       ispy.GetAllPaths('failures/', max_keys=max_keys,
57116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                        marker=marker, delimiter='/')])
58c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    base_url = '/?test_run=%s'
59116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    next_url = '/?marker=%s' % test_runs[-1]
60116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    data['next_url'] = next_url
61c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    data['links'] = [(test_run, base_url % test_run) for test_run in test_runs]
62c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self.response.write(template.render(data))
63c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
64c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def _GetForTestRun(self, test_run, ispy):
65c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """Renders a sorted list of failure-rows for a given test_run.
66c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
67c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    This method will produce a list of failure-rows that are sorted
68c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    in descending order by number of different pixels.
69c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
70c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    Args:
71c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      test_run: The name of the test_run to render failure rows from.
72c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ispy: An instance of ispy_api.ISpyApi.
73c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """
74c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    paths = set([path for path in ispy.GetAllPaths('failures/' + test_run)
75c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                 if path.endswith('actual.png')])
76c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    can_rebaseline = ispy_api.ISpyApi(
77c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        ispy.cloud_bucket).CanRebaselineToTestRun(test_run)
78c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    rows = [self._CreateRow(test_run, path, ispy) for path in paths]
79c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
80c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    # Function that sorts by the different_pixels field in the failure-info.
81c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    def _Sorter(a, b):
82c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return cmp(b['percent_different'],
83c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                 a['percent_different'])
84c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    template = JINJA.get_template('main_view.html')
85c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    self.response.write(
86c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        template.render({'comparisons': sorted(rows, _Sorter),
87c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         'test_run': test_run,
88c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                         'can_rebaseline': can_rebaseline}))
89c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
90c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  def _CreateRow(self, test_run, path, ispy):
91c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """Creates one failure-row.
92c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
93c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    This method builds a dictionary with the data necessary to display a
94c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    failure in the main_view html template.
95c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
96c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    Args:
97c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      test_run: The name of the test_run the failure is in.
98c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      path: A path to the failure's actual.png file.
99c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      ispy: An instance of ispy_api.ISpyApi.
100c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch
101c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    Returns:
102c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      A dictionary with fields necessary to render a failure-row
103c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        in the main_view html template.
104c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    """
105c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res = {}
106c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['expectation'] = path.lstrip('/').split('/')[2]
107c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['test_run'] = test_run
108c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['info'] = json.loads(ispy.cloud_bucket.DownloadFile(
109c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        ispy_utils.GetFailurePath(res['test_run'], res['expectation'],
110c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch                                  'info.txt')))
111c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    expected = ispy_utils.GetExpectationPath(
112c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        res['expectation'], 'expected.png')
113c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    diff = ispy_utils.GetFailurePath(test_run, res['expectation'], 'diff.png')
114c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['percent_different'] = res['info']['fraction_different'] * 100
115c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['expected_path'] = expected
116c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['diff_path'] = diff
117c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['actual_path'] = path
118c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['expected'] = ispy.cloud_bucket.GetImageURL(expected)
119c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['diff'] = ispy.cloud_bucket.GetImageURL(diff)
120c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    res['actual'] = ispy.cloud_bucket.GetImageURL(path)
121c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    return res
122