summaryrefslogtreecommitdiffstats
path: root/testing/variations/PRESUBMIT.py
blob: 003e611755c8ece41f1a635c30ed3ccb0875d13a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Presubmit script validating field trial configs.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details on the presubmit API built into depot_tools.
"""

import json
import sys


def PrettyPrint(contents):
  """Pretty prints a fieldtrial configuration.

  Args:
    contents: File contents as a string.

  Returns:
    Pretty printed file contents.
  """
  return json.dumps(json.loads(contents),
                    sort_keys=True, indent=4,
                    separators=(',', ': ')) + '\n'

def ValidateData(json_data, file_path, message_type):
  """Validates the format of a fieldtrial configuration.

  Args:
    json_data: Parsed JSON object representing the fieldtrial config.
    file_path: String representing the path to the JSON file.
    message_type: Type of message from |output_api| to return in the case of
        errors/warnings.

  Returns:
    A list of |message_type| messages. In the case of all tests passing with no
    warnings/errors, this will return [].
  """
  if not isinstance(json_data, dict):
    return [message_type(
        'Malformed config file %s: Expecting dict' % file_path)]
  for (study, groups) in json_data.iteritems():
    if not isinstance(study, unicode):
      return [message_type(
          'Malformed config file %s: Expecting keys to be string, got %s'
          % (file_path, type(study)))]
    if not isinstance(groups, list):
      return [message_type(
          'Malformed config file %s: Expecting list for study %s'
          % (file_path, study))]
    for group in groups:
      if not isinstance(group, dict):
        return [message_type(
            'Malformed config file %s: Expecting dict for group in '
            'Study[%s]' % (file_path, study))]
      if not 'group_name' in group or not isinstance(group['group_name'],
          unicode):
        return [message_type(
            'Malformed config file %s: Missing valid group_name for group'
            ' in Study[%s]' % (file_path, study))]
      if 'params' in group:
        params = group['params']
        if not isinstance(params, dict):
          return [message_type(
              'Malformed config file %s: Invalid params for Group[%s]'
              ' in Study[%s]' % (file_path, group['group_name'],
              study))]
        for (key, value) in params.iteritems():
          if not isinstance(key, unicode) or not isinstance(value,
              unicode):
            return [message_type(
                'Malformed config file %s: Invalid params for Group[%s]'
                ' in Study[%s]' % (file_path, group['group_name'],
                study))]
  return []

def CheckPretty(contents, file_path, message_type):
  """Validates the pretty printing of fieldtrial configuration.

  Args:
    contents: File contents as a string.
    file_path: String representing the path to the JSON file.
    message_type: Type of message from |output_api| to return in the case of
        errors/warnings.

  Returns:
    A list of |message_type| messages. In the case of all tests passing with no
    warnings/errors, this will return [].
  """
  pretty = PrettyPrint(contents)
  if contents != pretty:
    return [message_type(
        'Pretty printing error: Run '
        'python testing/variations/PRESUBMIT.py %s' % file_path)]
  return []

def CommonChecks(input_api, output_api):
  affected_files = input_api.AffectedFiles(
      include_deletes=False,
      file_filter=lambda x: x.LocalPath().endswith('.json'))
  for f in affected_files:
    contents = input_api.ReadFile(f)
    try:
      json_data = input_api.json.loads(contents)
      result = CheckPretty(contents, f.LocalPath(), output_api.PresubmitError)
      if len(result):
        return result
      result =  ValidateData(json_data, f.LocalPath(),
          output_api.PresubmitError)
      if len(result):
        return result
    except ValueError:
      return [output_api.PresubmitError(
          'Malformed JSON file: %s' % f.LocalPath())]
  return []

def CheckChangeOnUpload(input_api, output_api):
  return CommonChecks(input_api, output_api)

def CheckChangeOnCommit(input_api, output_api):
  return CommonChecks(input_api, output_api)


def main(argv):
  content = open(argv[1]).read()
  pretty = PrettyPrint(content)
  open(argv[1],'w').write(pretty)

if __name__ == "__main__":
  sys.exit(main(sys.argv))