| Index: commit-queue/tests/reduce_test_data.py
 | 
| ===================================================================
 | 
| --- commit-queue/tests/reduce_test_data.py	(revision 249146)
 | 
| +++ commit-queue/tests/reduce_test_data.py	(working copy)
 | 
| @@ -1,223 +0,0 @@
 | 
| -#!/usr/bin/env python
 | 
| -# Copyright (c) 2012 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.
 | 
| -"""Reduce the amount of data in ../test/data/*.json"""
 | 
| -
 | 
| -import json
 | 
| -import logging
 | 
| -import optparse
 | 
| -import os
 | 
| -import re
 | 
| -import sys
 | 
| -
 | 
| -
 | 
| -class Filterer(object):
 | 
| -  def __init__(self):
 | 
| -    self._deleted_builds = {}
 | 
| -    self.max_cached_builds = 10
 | 
| -    self._allowed_builders = ('linux_clang', 'linux', 'linux_touch')
 | 
| -
 | 
| -  def reset(self):
 | 
| -    self._deleted_builds = {}
 | 
| -
 | 
| -  def reduce_data(self, data):
 | 
| -    """Reduces the amount of data sent from a server to simplify testing and the
 | 
| -    amount of test data stored.
 | 
| -    """
 | 
| -    self.reset()
 | 
| -    for line in data[:]:
 | 
| -      original_url, original_response = line
 | 
| -      # Unpack and filter the response.
 | 
| -      new_url, new_response = self.filter_response(
 | 
| -          original_url, json.loads(original_response))
 | 
| -      if not new_url or new_response is None:
 | 
| -        data.remove(line)
 | 
| -        continue
 | 
| -      # Repack the string.
 | 
| -      line[1] = json.dumps(new_response, separators=(',',':'))
 | 
| -      logging.info('%s length: %d -> %d' % (
 | 
| -          new_url, len(original_response), len(line[1])))
 | 
| -      if len(line[1]) > 20000:
 | 
| -        logging.debug(line[1])
 | 
| -    return data
 | 
| -
 | 
| -  def filter_response(self, url, response):
 | 
| -    """Trims a single request.
 | 
| -
 | 
| -    |response| must be decoded json. Decoded json will be returned.
 | 
| -    """
 | 
| -    # Builders
 | 
| -    match = re.match('.+/json/builders/(\w+)(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      return url, self._filter_builder(match.group(1), response)
 | 
| -
 | 
| -    match = re.match('.+/json/builders(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      for builder in response.keys():
 | 
| -        value = self._filter_builder(builder, response[builder])
 | 
| -        if value is None:
 | 
| -          del response[builder]
 | 
| -        else:
 | 
| -          response[builder] = value
 | 
| -      return url, response
 | 
| -
 | 
| -    # Pending
 | 
| -    match = re.match('.+/json/builders/(\w+)/pendingBuilds(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      assert match.group(1) in self._allowed_builders
 | 
| -      return url, self._filter_pending(response)
 | 
| -
 | 
| -    # Builds
 | 
| -    match = re.match('.+/json/builders/(\w+)/builds/_all(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      builder = match.group(1)
 | 
| -      assert builder in self._allowed_builders, url
 | 
| -      keys = response.keys()
 | 
| -      keys = [int(k) for k in keys if int(k) not in self._deleted(builder)]
 | 
| -      keys = sorted(keys)[:self.max_cached_builds]
 | 
| -      response = dict((k, v) for k, v in response.iteritems() if int(k) in keys)
 | 
| -      return url, response
 | 
| -
 | 
| -    match = re.match('.+/json/builders/(\w+)/builds/\?(.+?)(|\&filter=1)$', url)
 | 
| -    if match:
 | 
| -      assert match.group(1) in self._allowed_builders
 | 
| -      # Ignore the query, reconstruct it from what it kept.
 | 
| -      for build in response.keys():
 | 
| -        value = self._filter_build(response[build])
 | 
| -        if value is None:
 | 
| -          del response[build]
 | 
| -        else:
 | 
| -          response[build] = value
 | 
| -      if not response:
 | 
| -        return None, None
 | 
| -      url = '%s?%s' % (
 | 
| -          url.split('?', 1)[0],
 | 
| -          '&'.join('select=%s' % b for b in sorted(response)))
 | 
| -      return url, response
 | 
| -
 | 
| -    match = re.match('.+/json/builders/(\w+)/builds/(\d+)(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      return url, self._filter_build(response)
 | 
| -
 | 
| -    # Slaves
 | 
| -    match = re.match('.+/json/slaves/([^/]+?)(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      return url, self._filter_slave(match.group(1), response)
 | 
| -
 | 
| -    match = re.match('.+/json/slaves(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      for slave in response.keys():
 | 
| -        value = self._filter_slave(slave, response[slave])
 | 
| -        if value is None:
 | 
| -          del response[slave]
 | 
| -        else:
 | 
| -          response[slave] = value
 | 
| -      return url, response
 | 
| -
 | 
| -    # Project
 | 
| -    match = re.match('.+/json/project(|\?filter=1)$', url)
 | 
| -    if match:
 | 
| -      return url, response
 | 
| -
 | 
| -    assert False, url
 | 
| -
 | 
| -  @staticmethod
 | 
| -  def _filter_pending(pending):
 | 
| -    """Trim pendingBuilds."""
 | 
| -    return pending[:2]
 | 
| -
 | 
| -  def _filter_builder(self, builder, response):
 | 
| -    """Trims a builder.
 | 
| -
 | 
| -    Reduces the number of cached builds.
 | 
| -    """
 | 
| -    # TODO(maruel): Reduce the number of slaves.
 | 
| -    if builder not in self._allowed_builders:
 | 
| -      return None
 | 
| -    builds_kept = response['cachedBuilds'][-self.max_cached_builds:]
 | 
| -    builds_discarded = response['cachedBuilds'][:-self.max_cached_builds]
 | 
| -    assert len(builds_kept) <= self.max_cached_builds
 | 
| -    assert (
 | 
| -        len(builds_kept) + len(builds_discarded) ==
 | 
| -          len(response['cachedBuilds']))
 | 
| -    if builds_discarded:
 | 
| -      assert min(builds_kept) > max(builds_discarded)
 | 
| -    response['cachedBuilds'] = builds_kept
 | 
| -    self._deleted(builder).union(int(b) for b in builds_discarded)
 | 
| -    if response.get('currentBuilds'):
 | 
| -      response['currentBuilds'] = [
 | 
| -          build for build in response['currentBuilds']
 | 
| -          if int(build) not in self._deleted(builder)
 | 
| -      ]
 | 
| -    if response.get('pendingBuilds', 0) > 2:
 | 
| -      response['pendingBuilds'] = 2
 | 
| -    return response
 | 
| -
 | 
| -  def _filter_build(self, response):
 | 
| -    """Trims a build."""
 | 
| -    if response['builderName'] not in self._allowed_builders:
 | 
| -      return None
 | 
| -    if int(response['number']) in self._deleted(response['builderName']):
 | 
| -      return None
 | 
| -    # TODO(maruel): Fix StatusJson to not push that much logs data.
 | 
| -    if 'logs' in response:
 | 
| -      del response['logs']
 | 
| -    if response.get('currentStep') and response['currentStep'].get('logs'):
 | 
| -      del response['currentStep']['logs']
 | 
| -    for step in response['steps']:
 | 
| -      if 'logs' in step:
 | 
| -        del step['logs']
 | 
| -    return response
 | 
| -
 | 
| -  def _filter_slave(self, _slave, response):
 | 
| -    """Trims a slave."""
 | 
| -    if response.get('builders'):
 | 
| -      for builder in response['builders'].keys():
 | 
| -        if not builder in self._allowed_builders:
 | 
| -          del response['builders'][builder]
 | 
| -      if not response['builders']:
 | 
| -        return None
 | 
| -    if response.get('builderName'):
 | 
| -      if not response['builderName'] in self._allowed_builders:
 | 
| -        return None
 | 
| -    if response.get('runningBuilds'):
 | 
| -      for i, build in enumerate(response['runningBuilds'][:]):
 | 
| -        value = self._filter_build(build)
 | 
| -        if value is None:
 | 
| -          response['runningBuilds'].remove(build)
 | 
| -        else:
 | 
| -          response['runningBuilds'][i] = value
 | 
| -    return response
 | 
| -
 | 
| -  def _deleted(self, builder):
 | 
| -    return self._deleted_builds.get(builder, set())
 | 
| -
 | 
| -
 | 
| -def main():
 | 
| -  parser = optparse.OptionParser(
 | 
| -      description=sys.modules['__main__'].__doc__)
 | 
| -  parser.add_option('-v', '--verbose', action='count', default=0)
 | 
| -  parser.add_option('-d', '--dry-run', action='store_true')
 | 
| -  options, args = parser.parse_args()
 | 
| -  if args:
 | 
| -    parser.error('Unsupported args: %s' % args)
 | 
| -  logging.basicConfig(
 | 
| -      level=[logging.WARNING, logging.INFO, logging.DEBUG][
 | 
| -        min(2, options.verbose)])
 | 
| -
 | 
| -  datadir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
 | 
| -  for filename in os.listdir(datadir):
 | 
| -    if not filename.endswith('.json') or filename.endswith('_expected.json'):
 | 
| -      continue
 | 
| -    filepath = os.path.join(datadir, filename)
 | 
| -    print 'Processing %s'  % filename
 | 
| -    data = json.load(open(filepath))
 | 
| -    data = Filterer().reduce_data(data)
 | 
| -    if not options.dry_run:
 | 
| -      json.dump(data, open(filepath, 'w'), separators=(',',':'))
 | 
| -  return 0
 | 
| -
 | 
| -
 | 
| -if __name__ == '__main__':
 | 
| -  sys.exit(main())
 | 
| 
 |