• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (c) 2012 The Chromium 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
5"""A module for the history of the test expectation file."""
6
7from datetime import datetime
8from datetime import timedelta
9
10import os
11import re
12import sys
13import tempfile
14import time
15import pysvn
16
17TEST_EXPECTATIONS_ROOT = 'http://src.chromium.org/blink/trunk/'
18# A map from earliest revision to path.
19# TODO(imasaki): support multiple test expectation files.
20TEST_EXPECTATIONS_LOCATIONS = {
21    148348: 'LayoutTests/TestExpectations',
22    119317: 'LayoutTests/platform/chromium/TestExpectations',
23    0: 'LayoutTests/platform/chromium/test_expectations.txt'}
24TEST_EXPECTATIONS_DEFAULT_PATH = (
25    TEST_EXPECTATIONS_ROOT + TEST_EXPECTATIONS_LOCATIONS[148348])
26
27class TestExpectationsHistory(object):
28  """A class to represent history of the test expectation file.
29
30  The history is obtained by calling PySVN.log()/diff() APIs.
31
32  TODO(imasaki): Add more functionalities here like getting some statistics
33      about the test expectation file.
34  """
35
36  @staticmethod
37  def GetTestExpectationsPathForRevision(revision):
38    for i in sorted(TEST_EXPECTATIONS_LOCATIONS.keys(), reverse=True):
39      if revision >= i:
40        return TEST_EXPECTATIONS_ROOT + TEST_EXPECTATIONS_LOCATIONS[i]
41
42  @staticmethod
43  def GetDiffBetweenTimes(start, end, testname_list,
44                          te_location=TEST_EXPECTATIONS_DEFAULT_PATH):
45    """Get difference between time period for the specified test names.
46
47    Given the time period, this method first gets the revision number. Then,
48    it gets the diff for each revision. Finally, it keeps the diff relating to
49    the test names and returns them along with other information about
50    revision.
51
52    Args:
53      start: A timestamp specifying start of the time period to be
54          looked at.
55      end: A timestamp object specifying end of the time period to be
56          looked at.
57      testname_list: A list of strings representing test names of interest.
58      te_location: A location of the test expectation file.
59
60    Returns:
61      A list of tuples (old_rev, new_rev, author, date, message, lines). The
62          |lines| contains the diff of the tests of interest.
63    """
64    temp_directory = tempfile.mkdtemp()
65    test_expectations_path = os.path.join(temp_directory, 'TestExpectations')
66    # Get directory name which is necesary to call PySVN.checkout().
67    te_location_dir = te_location[0:te_location.rindex('/')]
68    client = pysvn.Client()
69    client.checkout(te_location_dir, temp_directory, recurse=False)
70    # PySVN.log() (http://pysvn.tigris.org/docs/pysvn_prog_ref.html
71    # #pysvn_client_log) returns the log messages (including revision
72    # number in chronological order).
73    logs = client.log(test_expectations_path,
74                      revision_start=pysvn.Revision(
75                          pysvn.opt_revision_kind.date, start),
76                      revision_end=pysvn.Revision(
77                          pysvn.opt_revision_kind.date, end))
78    result_list = []
79    gobackdays = 1
80    while gobackdays < sys.maxint:
81      goback_start = time.mktime(
82          (datetime.fromtimestamp(start) - (
83              timedelta(days=gobackdays))).timetuple())
84      logs_before_time_period = (
85          client.log(test_expectations_path,
86                     revision_start=pysvn.Revision(
87                         pysvn.opt_revision_kind.date, goback_start),
88                     revision_end=pysvn.Revision(
89                         pysvn.opt_revision_kind.date, start)))
90      if logs_before_time_period:
91        # Prepend at the beginning of logs.
92        logs.insert(0, logs_before_time_period[len(logs_before_time_period)-1])
93        break
94      gobackdays *= 2
95
96    for i in xrange(len(logs) - 1):
97      old_rev = logs[i].revision.number
98      new_rev = logs[i + 1].revision.number
99      # Parsing the actual diff.
100
101      new_path = TestExpectationsHistory.GetTestExpectationsPathForRevision(
102          new_rev);
103      old_path = TestExpectationsHistory.GetTestExpectationsPathForRevision(
104          old_rev);
105
106      text = client.diff(temp_directory,
107                         url_or_path=old_path,
108                         revision1=pysvn.Revision(
109                             pysvn.opt_revision_kind.number, old_rev),
110                         url_or_path2=new_path,
111                         revision2=pysvn.Revision(
112                             pysvn.opt_revision_kind.number, new_rev))
113      lines = text.split('\n')
114      target_lines = []
115      for line in lines:
116        for testname in testname_list:
117          matches = re.findall(testname, line)
118          if matches:
119            if line[0] == '+' or line[0] == '-':
120              target_lines.append(line)
121      if target_lines:
122        # Needs to convert to normal date string for presentation.
123        result_list.append((
124            old_rev, new_rev, logs[i + 1].author,
125            datetime.fromtimestamp(
126                logs[i + 1].date).strftime('%Y-%m-%d %H:%M:%S'),
127            logs[i + 1].message, target_lines))
128    return result_list
129