# -*- Mode: python; tab-width: 8; indent-tabs-mode: nil -*-
# vim: set ts=8 sts=4 et sw=4 tw=80:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
import os
import re
KEY_XRE =
"{xre}"
DEFAULT_DURATION = 100.0
class Allowlist:
# we need to find the root dir of the profile at runtime
PRE_PROFILE =
""
def __init__(
self,
test_name,
paths,
path_substitutions,
name_substitutions,
event_sources=
None,
init_with=
None,
):
self.test_name = test_name
self.listmap = init_with
if init_with
else {}
self.dependent_libs = (
self.load_dependent_libs()
if init_with
and KEY_XRE
in paths
else {}
)
self.paths = paths
self.path_substitutions = path_substitutions
self.name_substitutions = name_substitutions
self.expected_event_sources = event_sources
or []
def load(self, filename):
if not self.load_dependent_libs():
return False
try:
with open(filename,
"r")
as fHandle:
temp = json.load(fHandle)
for allowlist_name
in temp:
self.listmap[allowlist_name.lower()] = temp[allowlist_name]
except IOError
as e:
print(
"%s: %s" % (e.filename, e.strerror))
return False
return True
def sanitize_filename(self, filename):
filename = filename.lower()
filename.replace(
" (x86)",
"")
for path, subst
in self.path_substitutions.items():
parts = filename.split(path)
if len(parts) >= 2:
if self.PRE_PROFILE ==
"" and subst ==
"{profile}":
fname = self.sanitize_filename(parts[0])
self.listmap[fname] = {}
# Windows can have {appdata}\local\temp\longnamedfolder
# or {appdata}\local\temp\longna~1
self.listmap[fname] = {}
if not fname.endswith(
"~1"):
# parse the longname into longna~1
dirs = fname.split(
"\\")
dirs[-1] =
"%s~1" % (dirs[-1][:6])
# now we want to ensure that every parent dir is
# added since we seem to be accessing them sometimes
diter = 2
while diter < len(dirs):
self.listmap[
"\\".join(dirs[:diter])] = {}
diter = diter + 1
self.PRE_PROFILE = fname
filename =
"%s%s" % (subst, path.join(parts[1:]))
for old_name, new_name
in self.name_substitutions.items():
if isinstance(old_name, re.Pattern):
filename = re.sub(old_name, new_name, filename)
else:
parts = filename.split(old_name)
if len(parts) >= 2:
filename =
"%s%s" % (parts[0], new_name)
return filename.strip(
"/\\\\ \t")
def check(self, test, file_name_index, event_source_index=
None):
errors = {}
for row_key
in test.keys():
filename = self.sanitize_filename(row_key[file_name_index])
if filename
in self.listmap:
if (
"ignore" in self.listmap[filename]
and self.listmap[filename][
"ignore"]
):
continue
elif filename
in self.dependent_libs:
continue
elif (
event_source_index
is not None
and row_key[event_source_index]
in self.expected_event_sources
):
continue
else:
if filename
not in errors:
errors[filename] = []
errors[filename].append(test[row_key])
return errors
def checkDuration(self, test, file_name_index, file_duration_index):
errors = {}
for idx, (row_key, row_value)
in enumerate(test.items()):
if row_value[file_duration_index] > DEFAULT_DURATION:
filename = self.sanitize_filename(row_key[file_name_index])
if (
filename
in self.listmap
and "ignoreduration" in self.listmap[filename]
):
# we have defined in the json manifest max values
# (max found value * 2) and will ignore it
if (
row_value[file_duration_index]
<= self.listmap[filename][
"ignoreduration"]
):
continue
if filename
not in errors:
errors[filename] = []
errors[filename].append(
"Duration %s > %s" % (row_value[file_duration_index]),
DEFAULT_DURATION,
)
return errors
def filter(self, test, file_name_index):
for row_key
in test.keys():
filename = self.sanitize_filename(row_key[file_name_index])
if filename
in self.listmap:
if (
"ignore" in self.listmap[filename]
and self.listmap[filename][
"ignore"]
):
del test[row_key]
continue
elif filename
in self.dependent_libs:
del test[row_key]
continue
@staticmethod
def get_error_strings(errors):
error_strs = []
for filename, data
in errors.items():
for datum
in data:
error_strs.append(
"File '%s' was accessed and we were not"
" expecting it: %r" % (filename, datum)
)
return error_strs
def print_errors(self, error_strs):
for error_msg
in error_strs:
print(
"TEST-UNEXPECTED-FAIL | %s | %s" % (self.test_name, error_msg))
# Note that we don't store dependent libs in listmap. This makes
# save_baseline cleaner. Since a baseline allowlist should not include
# the dependent_libs, we would need to filter them out if everything was
# stored in the same dict.
def load_dependent_libs(self):
filename =
"%s%sdependentlibs.list" % (self.paths[KEY_XRE], os.path.sep)
try:
with open(filename,
"r")
as f:
libs = f.readlines()
self.dependent_libs = {
"%s%s%s" % (KEY_XRE, os.path.sep, lib.strip()): {
"ignore":
True}
for lib
in libs
}
return True
except IOError
as e:
print(
"%s: %s" % (e.filename, e.strerror))
return False