blob: 9b9b1268db9570bc1861ae9e89b4f92871aa9382 [file] [log] [blame]
#!/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.
"""Verify perf_expectations.json can be loaded using simplejson.
perf_expectations.json is a JSON-formatted file. This script verifies
that simplejson can load it correctly. It should catch most common
formatting problems.
"""
import subprocess
import sys
import os
import unittest
import re
simplejson = None
def OnTestsLoad():
old_path = sys.path
script_path = os.path.dirname(sys.argv[0])
load_path = None
global simplejson
# This test script should be stored in src/tools/perf_expectations/. That
# directory will most commonly live in 2 locations:
#
# - a regular Chromium checkout, in which case src/third_party
# is where to look for simplejson
#
# - a buildbot checkout, in which case .../pylibs is where
# to look for simplejson
#
# Locate and install the correct path based on what we can find.
#
for path in ('../../../third_party', '../../../../../pylibs'):
path = os.path.join(script_path, path)
if os.path.exists(path) and os.path.isdir(path):
load_path = os.path.abspath(path)
break
if load_path is None:
msg = "%s expects to live within a Chromium checkout" % sys.argv[0]
raise Exception, "Error locating simplejson load path (%s)" % msg
# Try importing simplejson once. If this succeeds, we found it and will
# load it again later properly. Fail if we cannot load it.
sys.path.append(load_path)
try:
import simplejson as Simplejson
simplejson = Simplejson
except ImportError, e:
msg = "%s expects to live within a Chromium checkout" % sys.argv[0]
raise Exception, "Error trying to import simplejson from %s (%s)" % \
(load_path, msg)
finally:
sys.path = old_path
return True
def LoadJsonFile(filename):
f = open(filename, 'r')
try:
data = simplejson.load(f)
except ValueError, e:
f.seek(0)
print "Error reading %s:\n%s" % (filename,
f.read()[:50]+'...')
raise e
f.close()
return data
OnTestsLoad()
CONFIG_JSON = os.path.join(os.path.dirname(sys.argv[0]),
'../chromium_perf_expectations.cfg')
MAKE_EXPECTATIONS = os.path.join(os.path.dirname(sys.argv[0]),
'../make_expectations.py')
PERF_EXPECTATIONS = os.path.join(os.path.dirname(sys.argv[0]),
'../perf_expectations.json')
class PerfExpectationsUnittest(unittest.TestCase):
def testPerfExpectations(self):
# Test data is dictionary.
perf_data = LoadJsonFile(PERF_EXPECTATIONS)
if not isinstance(perf_data, dict):
raise Exception('perf expectations is not a dict')
# Test the 'load' key.
if not 'load' in perf_data:
raise Exception("perf expectations is missing a load key")
if not isinstance(perf_data['load'], bool):
raise Exception("perf expectations load key has non-bool value")
# Test all key values are dictionaries.
bad_keys = []
for key in perf_data:
if key == 'load':
continue
if not isinstance(perf_data[key], dict):
bad_keys.append(key)
if len(bad_keys) > 0:
msg = "perf expectations keys have non-dict values"
raise Exception("%s: %s" % (msg, bad_keys))
# Test all key values have delta and var keys.
for key in perf_data:
if key == 'load':
continue
# First check if regress/improve is in the key's data.
if 'regress' in perf_data[key]:
if 'improve' not in perf_data[key]:
bad_keys.append(key)
if (not isinstance(perf_data[key]['regress'], int) and
not isinstance(perf_data[key]['regress'], float)):
bad_keys.append(key)
if (not isinstance(perf_data[key]['improve'], int) and
not isinstance(perf_data[key]['improve'], float)):
bad_keys.append(key)
else:
# Otherwise check if delta/var is in the key's data.
if 'delta' not in perf_data[key] or 'var' not in perf_data[key]:
bad_keys.append(key)
if (not isinstance(perf_data[key]['delta'], int) and
not isinstance(perf_data[key]['delta'], float)):
bad_keys.append(key)
if (not isinstance(perf_data[key]['var'], int) and
not isinstance(perf_data[key]['var'], float)):
bad_keys.append(key)
if len(bad_keys) > 0:
msg = "perf expectations key values missing or invalid delta/var"
raise Exception("%s: %s" % (msg, bad_keys))
# Test all keys have the correct format.
for key in perf_data:
if key == 'load':
continue
# tools/buildbot/scripts/master/log_parser.py should have a matching
# regular expression.
if not re.match(r"^([\w\.-]+)/([\w\.-]+)/([\w\.-]+)/([\w\.-]+)$", key):
bad_keys.append(key)
if len(bad_keys) > 0:
msg = "perf expectations keys in bad format, expected a/b/c/d"
raise Exception("%s: %s" % (msg, bad_keys))
def testNoUpdatesNeeded(self):
p = subprocess.Popen([MAKE_EXPECTATIONS, '-s'], stdout=subprocess.PIPE)
p.wait();
self.assertEqual(p.returncode, 0,
msg='Update expectations first by running ./make_expectations.py')
def testConfigFile(self):
# Test that the config file can be parsed as JSON.
config = LoadJsonFile(CONFIG_JSON)
# Require the following keys.
if 'base_url' not in config:
raise Exception('base_url not specified in config file')
if 'perf_file' not in config:
raise Exception('perf_file not specified in config file')
if __name__ == '__main__':
unittest.main()