• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright 2016 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Helper script to update the test error expectations based on actual results.
7
8This is useful for regenerating test expectations after making changes to the
9error format.
10
11To use this run the affected tests, and then pass the input to this script
12(either via stdin, or as the first argument). For instance:
13
14  $ ./out/Release/net_unittests --gtest_filter="*VerifyCertificateChain*" | \
15     net/data/verify_certificate_chain_unittest/rebase-errors.py
16
17The script works by scanning the stdout looking for gtest failures having a
18particular format.  The C++ test side should have been instrumented to dump out
19the test file's path on mismatch.
20
21This script will then update the corresponding test/error file that contains the
22error expectation.
23"""
24
25import os
26import sys
27import re
28
29# Regular expression to find the failed errors in test stdout.
30#  * Group 1 of the match is file path (relative to //src) where the
31#    expected errors were read from.
32#  * Group 2 of the match is the actual error text
33failed_test_regex = re.compile(r"""
34Cert path errors don't match expectations \((.+?)\)
35
36EXPECTED:
37
38(?:.|\n)*?
39ACTUAL:
40
41((?:.|\n)*?)
42===> Use net/data/verify_certificate_chain_unittest/rebase-errors.py to rebaseline.
43""", re.MULTILINE)
44
45
46def read_file_to_string(path):
47  """Reads a file entirely to a string"""
48  with open(path, 'r') as f:
49    return f.read()
50
51
52def write_string_to_file(data, path):
53  """Writes a string to a file"""
54  print("Writing file %s ..." % (path))
55  with open(path, "w") as f:
56    f.write(data)
57
58
59def get_src_root():
60  """Returns the path to the enclosing //src directory. This assumes the
61  current script is inside the source tree."""
62  cur_dir = os.path.dirname(os.path.realpath(__file__))
63
64  while True:
65    parent_dir, dirname = os.path.split(cur_dir)
66    # Check if it looks like the src/ root.
67    if dirname == "src" and os.path.isdir(os.path.join(cur_dir, "net")):
68      return cur_dir
69    if not parent_dir or parent_dir == cur_dir:
70      break
71    cur_dir = parent_dir
72
73  print("Couldn't find src dir")
74  sys.exit(1)
75
76
77def get_abs_path(rel_path):
78  """Converts |rel_path| (relative to src) to a full path"""
79  return os.path.join(get_src_root(), rel_path)
80
81
82def fixup_errors_for_file(actual_errors, test_file_path):
83  """Updates the errors in |test_file_path| to match |actual_errors|"""
84  contents = read_file_to_string(test_file_path)
85
86  header = "\nexpected_errors:\n"
87  index = contents.find(header)
88  if index < 0:
89    print("Couldn't find expected_errors")
90    sys.exit(1)
91
92  # The rest of the file contains the errors (overwrite).
93  contents = contents[0:index] + header + actual_errors
94
95  write_string_to_file(contents, test_file_path)
96
97
98def main():
99  if len(sys.argv) > 2:
100    print('Usage: %s [path-to-unittest-stdout]' % (sys.argv[0]))
101    sys.exit(1)
102
103  # Read the input either from a file, or from stdin.
104  test_stdout = None
105  if len(sys.argv) == 2:
106    test_stdout = read_file_to_string(sys.argv[1])
107  else:
108    print('Reading input from stdin...')
109    test_stdout = sys.stdin.read()
110
111  for m in failed_test_regex.finditer(test_stdout):
112    src_relative_errors_path = m.group(1)
113    errors_path = get_abs_path(src_relative_errors_path)
114    actual_errors = m.group(2)
115
116    if errors_path.endswith(".test"):
117      fixup_errors_for_file(actual_errors, errors_path)
118    elif errors_path.endswith(".txt"):
119      write_string_to_file(actual_errors, errors_path)
120    else:
121      print('Unknown file extension')
122      sys.exit(1)
123
124
125
126if __name__ == "__main__":
127  main()
128