1336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org#!/usr/bin/env python
2336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org
3336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org
4336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org# Copyright (c) 2014 The Chromium Authors. All rights reserved.
5336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org# Use of this source code is governed by a BSD-style license that can be
6336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org# found in the LICENSE file.
7336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org
85ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
95ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org"""greenify.py: standalone script to correct flaky bench expectations.
10336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org
11336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org    Requires Rietveld credentials on the running machine.
12336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org
13336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org    Usage:
14336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org      Copy script to a separate dir outside Skia repo. The script will create a
15336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org          skia dir on the first run to host the repo, and will create/delete
16336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org          temp dirs as needed.
17336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org      ./greenify.py --url <the stdio url from failed CheckForRegressions step>
185ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org"""
195ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
205ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport argparse
215ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport filecmp
225ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport os
235ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport re
245ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport shutil
255ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport subprocess
265ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport time
275ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgimport urllib2
285ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
295ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
305ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org# Regular expression for matching exception data.
315ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgEXCEPTION_RE = ('Bench (\S+) out of range \[(\d+.\d+), (\d+.\d+)\] \((\d+.\d+) '
325ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org                'vs (\d+.\d+), ')
335ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgEXCEPTION_RE_COMPILED = re.compile(EXCEPTION_RE)
345ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
355ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
365ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef clean_dir(d):
375ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if os.path.exists(d):
385ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    shutil.rmtree(d)
395ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.makedirs(d)
405ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
415ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef checkout_or_update_skia(repo_dir):
425ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  status = True
435ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  old_cwd = os.getcwd()
445ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.chdir(repo_dir)
455ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  print 'CHECK SKIA REPO...'
465ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if subprocess.call(['git', 'pull'],
475ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org                     stderr=subprocess.PIPE):
485ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Checking out Skia from git, please be patient...'
495ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    os.chdir(old_cwd)
505ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    clean_dir(repo_dir)
515ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    os.chdir(repo_dir)
525ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    if subprocess.call(['git', 'clone', '-q', '--depth=50', '--single-branch',
535ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org                        'https://skia.googlesource.com/skia.git', '.']):
545ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      status = False
555ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  subprocess.call(['git', 'checkout', 'master'])
565ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  subprocess.call(['git', 'pull'])
575ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.chdir(old_cwd)
585ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  return status
595ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
605ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef git_commit_expectations(repo_dir, exp_dir, bot, build, commit):
615ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  commit_msg = """Greenify bench bot %s at build %s
625ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
6377767358a3e26693282f9958df6c64df68b65676commit-bot@chromium.orgTBR=bsalomon@google.com
645ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
6577767358a3e26693282f9958df6c64df68b65676commit-bot@chromium.orgBypassing trybots:
6677767358a3e26693282f9958df6c64df68b65676commit-bot@chromium.orgNOTRY=true""" % (bot, build)
675ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  old_cwd = os.getcwd()
685ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.chdir(repo_dir)
695ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  upload = ['git', 'cl', 'upload', '-f', '--bypass-hooks',
705ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org            '--bypass-watchlists', '-m', commit_msg]
715ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if commit:
725ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    upload.append('--use-commit-queue')
735ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  branch = exp_dir[exp_dir.rfind('/') + 1:]
745ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  filename = 'bench_expectations_%s.txt' % bot
755ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  cmds = ([['git', 'checkout', 'master'],
765ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'pull'],
775ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'checkout', '-b', branch, '-t', 'origin/master'],
785ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['cp', '%s/%s' % (exp_dir, filename), 'expectations/bench'],
795ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'add', 'expectations/bench/' + filename],
805ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'commit', '-m', commit_msg],
815ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           upload,
825ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'checkout', 'master'],
835ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org           ['git', 'branch', '-D', branch],
845ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org          ])
855ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  status = True
865ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  for cmd in cmds:
875ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Running ' + ' '.join(cmd)
885ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    if subprocess.call(cmd):
895ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      print 'FAILED. Please check if skia git repo is present.'
905ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      subprocess.call(['git', 'checkout', 'master'])
915ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      status = False
925ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      break
935ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.chdir(old_cwd)
945ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  return status
955ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
965ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef delete_dirs(li):
975ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  for d in li:
985ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Deleting directory %s' % d
995ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    shutil.rmtree(d)
1005ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1015ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef widen_bench_ranges(url, bot, repo_dir, exp_dir):
1025ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  fname = 'bench_expectations_%s.txt' % bot
1035ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  src = os.path.join(repo_dir, 'expectations', 'bench', fname)
1045ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not os.path.isfile(src):
1055ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'This bot has no expectations! %s' % bot
1065ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return False
1075ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  row_dic = {}
1085ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  for l in urllib2.urlopen(url).read().split('\n'):
1095ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    data = EXCEPTION_RE_COMPILED.search(l)
1105ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    if data:
1115ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      row = data.group(1)
1125ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      lb = float(data.group(2))
1135ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      ub = float(data.group(3))
1145ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      actual = float(data.group(4))
1155ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      exp = float(data.group(5))
1165ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      avg = (actual + exp) / 2
1175ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      shift = avg - exp
1185ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      lb = lb + shift
1195ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      ub = ub + shift
1205ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      # In case outlier really fluctuates a lot
1215ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      if actual < lb:
1225ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org        lb = actual - abs(shift) * 0.1 + 0.5
1235ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      elif actual > ub:
1245ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org        ub = actual + abs(shift) * 0.1 + 0.5
1255ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      row_dic[row] = '%.2f,%.2f,%.2f' % (avg, lb, ub)
1265ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not row_dic:
1275ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'NO out-of-range benches found at %s' % url
1285ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return False
1295ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1305ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  changed = 0
1315ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  li = []
1325ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  for l in open(src).readlines():
1335ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    parts = l.strip().split(',')
1345ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    if parts[0].startswith('#') or len(parts) != 5:
1355ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      li.append(l.strip())
1365ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      continue
1375ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    if ','.join(parts[:2]) in row_dic:
1385ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      li.append(','.join(parts[:2]) + ',' + row_dic[','.join(parts[:2])])
1395ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      changed += 1
1405ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    else:
1415ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      li.append(l.strip())
1425ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not changed:
1435ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Not in source file:\n' + '\n'.join(row_dic.keys())
1445ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return False
1455ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1465ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  dst = os.path.join(exp_dir, fname)
1475ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  with open(dst, 'w+') as f:
1485ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    f.write('\n'.join(li))
1495ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  return True
1505ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1515ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1525ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgdef main():
1535ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  d = os.path.dirname(os.path.abspath(__file__))
1545ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  os.chdir(d)
1555ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not subprocess.call(['git', 'rev-parse'], stderr=subprocess.PIPE):
1565ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Please copy script to a separate dir outside git repos to use.'
1575ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return
1585ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  ts_str = '%s' % time.time()
1595ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1605ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  parser = argparse.ArgumentParser()
1615ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  parser.add_argument('--url',
1625ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org                      help='Broken bench build CheckForRegressions page url.')
1635ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  parser.add_argument('--commit', action='store_true',
1645ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org                      help='Whether to commit changes automatically.')
1655ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  args = parser.parse_args()
1665ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  repo_dir = os.path.join(d, 'skia')
1675ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not os.path.exists(repo_dir):
1685ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    os.makedirs(repo_dir)
1695ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not checkout_or_update_skia(repo_dir):
1705ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'ERROR setting up Skia repo at %s' % repo_dir
1715ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return 1
1725ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1735ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  file_in_repo = os.path.join(d, 'skia/experimental/benchtools/greenify.py')
1745ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not filecmp.cmp(__file__, file_in_repo):
1755ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    shutil.copy(file_in_repo, __file__)
1765ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'Updated this script from repo; please run again.'
1775ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    return
1785ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
1795ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not args.url:
1805ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    raise Exception('Please provide a url with broken CheckForRegressions.')
1815ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  path = args.url.split('/')
1825ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if len(path) != 11 or not path[6].isdigit():
1835ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    raise Exception('Unexpected url format: %s' % args.url)
1845ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  bot = path[4]
1855ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  build = path[6]
1865ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  commit = False
1875ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if args.commit:
1885ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    commit = True
1895ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
190336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org  exp_dir = os.path.join(d, 'exp' + ts_str)
191336df8fd3494af14fa00c759578f9845fc0ec4b3commit-bot@chromium.org  clean_dir(exp_dir)
1925ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  if not widen_bench_ranges(args.url, bot, repo_dir, exp_dir):
1935ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'NO bench exceptions found! %s' % args.url
1945ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  elif not git_commit_expectations(
1955ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org      repo_dir, exp_dir, bot, build, commit):
1965ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'ERROR uploading expectations using git.'
1975ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  elif not commit:
1985ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'CL created. Please take a look at the link above.'
1995ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  else:
2005ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org    print 'New bench baselines should be in CQ now.'
2015ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  delete_dirs([exp_dir])
2025ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
2035ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org
2045ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.orgif __name__ == "__main__":
2055ddea761a97d7ab41eecff630b602297ce7299d4commit-bot@chromium.org  main()
206