Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: tools/lint/test/python.toml
import contextlib
import os
import pathlib
import shutil
import tempfile
from unittest import mock
import mozunit
import pytest
LINTER = "perfdocs"
class PerfDocsLoggerMock:
LOGGER = None
PATHS = []
FAILED = True
"""
This is a sample mozperftest test that we use for testing
the verification process.
"""
SAMPLE_TEST = """
"use strict";
async function setUp(context) {
context.log.info("setUp example!");
}
async function test(context, commands) {
context.log.info("Test with setUp/tearDown example!");
}
async function tearDown(context) {
context.log.info("tearDown example!");
}
module.noexport = {};
module.exports = {
setUp,
tearDown,
test,
owner: "Performance Testing Team",
name: "Example",
description: "The description of the example test.",
longDescription: `
This is a longer description of the test perhaps including information
about how it should be run locally or links to relevant information.
`
};
"""
SAMPLE_CONFIG = """
name: mozperftest
manifest: None
static-only: False
suites:
suite:
description: "Performance tests from the 'suite' folder."
tests:
Example: ""
"""
DYNAMIC_SAMPLE_CONFIG = """
name: {}
manifest: None
static-only: False
suites:
suite:
description: "Performance tests from the 'suite' folder."
tests:
Example: "Performance test Example from suite."
another_suite:
description: "Performance tests from the 'another_suite' folder."
tests:
Example: "Performance test Example from another_suite."
"""
SAMPLE_METRICS_CONFIG = """
name: raptor
manifest: "None"
metrics:
'test':
aliases: [t1, t2]
description: a description
matcher: f.*|S.*
static-only: False
suites:
suite:
description: "Performance tests from the 'suite' folder."
tests:
Example: "Performance test Example from another_suite."
another_suite:
description: "Performance tests from the 'another_suite' folder."
tests:
Example: "Performance test Example from another_suite."
"""
DYNAMIC_METRICS_CONFIG = """
name: raptor
manifest: "None"{}
static-only: False
suites:
suite:
description: "Performance tests from the 'suite' folder."
tests:
Example: "Performance test Example from another_suite."
another_suite:
description: "Performance tests from the 'another_suite' folder."
tests:
Example: "Performance test Example from another_suite."
"""
SAMPLE_INI = """
[Example]
test_url = Example_url
alert_on = fcp
"""
SAMPLE_METRICS_INI = """
[Example]
test_url = Example_url
alert_on = fcp,SpeedIndex
"""
@contextlib.contextmanager
def temp_file(name="temp", tempdir=None, content=None):
if tempdir is None:
tempdir = tempfile.mkdtemp()
path = pathlib.Path(tempdir, name)
if content is not None:
with path.open("w", newline="\n") as f:
f.write(content)
try:
yield path
finally:
try:
shutil.rmtree(str(tempdir))
except FileNotFoundError:
pass
@contextlib.contextmanager
def temp_dir():
tempdir = pathlib.Path(tempfile.mkdtemp())
try:
yield tempdir
finally:
try:
shutil.rmtree(str(tempdir))
except FileNotFoundError:
pass
def setup_sample_logger(logger, structured_logger, top_dir):
from perfdocs.logger import PerfDocLogger
PerfDocLogger.LOGGER = structured_logger
PerfDocLogger.PATHS = ["perfdocs"]
PerfDocLogger.TOP_DIR = top_dir
import perfdocs.gatherer as gt
import perfdocs.generator as gn
import perfdocs.verifier as vf
gt.logger = logger
vf.logger = logger
gn.logger = logger
@mock.patch("taskgraph.util.taskcluster.get_artifact")
@mock.patch("tryselect.tasks.generate_tasks")
@mock.patch("perfdocs.generator.Generator")
@mock.patch("perfdocs.verifier.Verifier")
@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock)
def test_perfdocs_start_and_fail(
verifier,
generator,
get_artifact_mock,
gen_tasks_mock,
structured_logger,
config,
paths,
):
from perfdocs.perfdocs import run_perfdocs
with temp_file("bad", content="foo") as temp:
run_perfdocs(
config, logger=structured_logger, paths=[str(temp)], generate=False
)
assert PerfDocsLoggerMock.LOGGER == structured_logger
assert PerfDocsLoggerMock.PATHS == [temp]
assert PerfDocsLoggerMock.FAILED
assert verifier.call_count == 1
assert mock.call().validate_tree() in verifier.mock_calls
assert generator.call_count == 0
@mock.patch("taskgraph.util.taskcluster.get_artifact")
@mock.patch("tryselect.tasks.generate_tasks")
@mock.patch("perfdocs.generator.Generator")
@mock.patch("perfdocs.verifier.Verifier")
@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock)
def test_perfdocs_start_and_pass(verifier, generator, structured_logger, config, paths):
from perfdocs.perfdocs import run_perfdocs
PerfDocsLoggerMock.FAILED = False
with temp_file("bad", content="foo") as temp:
run_perfdocs(
config, logger=structured_logger, paths=[str(temp)], generate=False
)
assert PerfDocsLoggerMock.LOGGER == structured_logger
assert PerfDocsLoggerMock.PATHS == [temp]
assert not PerfDocsLoggerMock.FAILED
assert verifier.call_count == 1
assert mock.call().validate_tree() in verifier.mock_calls
assert generator.call_count == 1
assert mock.call().generate_perfdocs() in generator.mock_calls
@mock.patch("perfdocs.logger.PerfDocLogger", new=PerfDocsLoggerMock)
def test_perfdocs_bad_paths(structured_logger, config, paths):
from perfdocs.perfdocs import run_perfdocs
with pytest.raises(Exception):
run_perfdocs(config, logger=structured_logger, paths=["bad"], generate=False)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_gatherer_fetch_perfdocs_tree(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.gatherer import Gatherer
gatherer = Gatherer(top_dir)
assert not gatherer._perfdocs_tree
gatherer.fetch_perfdocs_tree()
expected = "Found 1 perfdocs directories"
args, _ = logger.log.call_args
assert expected in args[0]
assert logger.log.call_count == 1
assert gatherer._perfdocs_tree
expected = ["path", "yml", "rst", "static"]
for i, key in enumerate(gatherer._perfdocs_tree[0].keys()):
assert key == expected[i]
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_gatherer_get_test_list(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.gatherer import Gatherer
gatherer = Gatherer(top_dir)
gatherer.fetch_perfdocs_tree()
framework = gatherer.get_test_list(gatherer._perfdocs_tree[0])
expected = ["name", "test_list", "yml_content", "yml_path"]
for i, key in enumerate(sorted(framework.keys())):
assert key == expected[i]
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verification(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
verifier = Verifier(top_dir)
verifier.validate_tree()
# Make sure that we had no warnings
assert logger.warning.call_count == 0
assert logger.log.call_count == 1
assert len(logger.mock_calls) == 1
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_validate_yaml_pass(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
yaml_path = perfdocs_sample["config"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
valid = Verifier(top_dir).validate_yaml(pathlib.Path(yaml_path))
assert valid
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_invalid_yaml(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
yaml_path = perfdocs_sample["config"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
verifier = Verifier("top_dir")
with open(yaml_path, "r", newline="\n") as f:
lines = f.readlines()
print(lines)
with open(yaml_path, "w", newline="\n") as f:
f.write("\n".join(lines[2:]))
valid = verifier.validate_yaml(yaml_path)
expected = ("YAML ValidationError: 'name' is a required property\n", yaml_path)
args, _ = logger.warning.call_args
assert logger.warning.call_count == 1
assert expected[0] in args[0]
assert not valid
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_validate_rst_pass(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
rst_path = perfdocs_sample["index"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
valid = Verifier(top_dir).validate_rst_content(
pathlib.Path(rst_path), expected_str="{documentation}"
)
assert valid
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_invalid_rst(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
rst_path = perfdocs_sample["index"]
setup_sample_logger(logger, structured_logger, top_dir)
# Replace the target string to invalid Keyword for test
with open(rst_path, "r") as file:
filedata = file.read()
filedata = filedata.replace("documentation", "Invalid Keyword")
with open(rst_path, "w", newline="\n") as file:
file.write(filedata)
from perfdocs.verifier import Verifier
verifier = Verifier("top_dir")
valid = verifier.validate_rst_content(rst_path, expected_str="{documentation}")
expected = (
"Cannot find a '{documentation}' entry in the given index file",
rst_path,
)
args, _ = logger.warning.call_args
assert logger.warning.call_count == 1
assert args == expected
assert not valid
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_validate_descriptions_pass(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
verifier = Verifier(top_dir)
verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0])
assert logger.warning.call_count == 0
assert logger.log.call_count == 1
assert len(logger.mock_calls) == 1
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_not_existing_suite_in_test_list(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
manifest_path = perfdocs_sample["manifest"]["path"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
verifier = Verifier(top_dir)
os.remove(manifest_path)
verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0])
expected = (
"Could not find an existing suite for suite - bad suite name?",
perfdocs_sample["config"],
)
args, _ = logger.warning.call_args
assert logger.warning.call_count == 1
assert args == expected
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_not_existing_tests_in_suites(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "r") as file:
filedata = file.read()
filedata = filedata.replace("Example", "DifferentName")
with open(perfdocs_sample["config"], "w", newline="\n") as file:
file.write(filedata)
from perfdocs.verifier import Verifier
verifier = Verifier(top_dir)
verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0])
expected = [
"Could not find an existing test for DifferentName - bad test name?",
"Could not find a test description for Example",
]
assert logger.warning.call_count == 2
for i, call in enumerate(logger.warning.call_args_list):
args, _ = call
assert args[0] == expected[i]
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_missing_contents_in_suite(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "r") as file:
filedata = file.read()
filedata = filedata.replace("suite:", "InvalidSuite:")
with open(perfdocs_sample["config"], "w", newline="\n") as file:
file.write(filedata)
from perfdocs.verifier import Verifier
verifier = Verifier(top_dir)
verifier._check_framework_descriptions(verifier._gatherer.perfdocs_tree[0])
expected = (
"Could not find an existing suite for InvalidSuite - bad suite name?",
"Missing suite description for suite",
)
assert logger.warning.call_count == 2
for i, call in enumerate(logger.warning.call_args_list):
args, _ = call
assert args[0] == expected[i]
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_invalid_dir(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
verifier = Verifier("invalid_path")
with pytest.raises(Exception) as exceinfo:
verifier.validate_tree()
assert str(exceinfo.value) == "No valid perfdocs directories found"
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_file_invalidation(
logger, structured_logger, perfdocs_sample
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.verifier import Verifier
with mock.patch("perfdocs.verifier.Verifier.validate_yaml", return_value=False):
verifier = Verifier(top_dir)
with pytest.raises(Exception):
verifier.validate_tree()
# Check if "File validation error" log is called
# and Called with a log inside perfdocs_tree().
assert logger.log.call_count == 2
assert len(logger.mock_calls) == 2
@pytest.mark.parametrize(
"manifest, metric_definitions, expected",
[
[
SAMPLE_INI,
"""
metrics:
"FirstPaint":
aliases:
- fcp
description: "Example" """,
1,
],
[
SAMPLE_METRICS_INI,
"""
metrics:
FirstPaint:
aliases:
- fcp
description: Example
SpeedIndex:
aliases:
- speedindex
- si
description: Example
""",
2,
],
],
)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_nonexistent_documented_metrics(
logger, structured_logger, perfdocs_sample, manifest, metric_definitions, expected
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_METRICS_CONFIG.format(metric_definitions, ""))
with open(perfdocs_sample["manifest"]["path"], "w", newline="\n") as f:
f.write(manifest)
sample_gatherer_result = {
"suite": {"Example": {}},
"another_suite": {"Example": {}},
}
from perfdocs.verifier import Verifier
with mock.patch("perfdocs.framework_gatherers.RaptorGatherer.get_test_list") as m:
m.return_value = sample_gatherer_result
verifier = Verifier(top_dir)
verifier.validate_tree()
assert len(logger.warning.call_args_list) == expected
for args, _ in logger.warning.call_args_list:
assert "Cannot find documented metric" in args[0]
assert "being used" in args[0]
@pytest.mark.parametrize(
"manifest, metric_definitions",
[
[
SAMPLE_INI,
"""
metrics:
"FirstPaint":
aliases:
- fcp
description: "Example" """,
],
[
SAMPLE_METRICS_INI,
"""
metrics:
SpeedIndex:
aliases:
- speedindex
- si
description: Example
""",
],
],
)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_undocumented_metrics(
logger, structured_logger, perfdocs_sample, manifest, metric_definitions
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_METRICS_CONFIG.format(metric_definitions, ""))
with open(perfdocs_sample["manifest"]["path"], "w", newline="\n") as f:
f.write(manifest)
sample_gatherer_result = {
"suite": {"Example": {"metrics": ["fcp", "SpeedIndex"]}},
"another_suite": {"Example": {}},
}
from perfdocs.verifier import Verifier
with mock.patch("perfdocs.framework_gatherers.RaptorGatherer.get_test_list") as m:
m.return_value = sample_gatherer_result
verifier = Verifier(top_dir)
verifier.validate_tree()
assert len(logger.warning.call_args_list) == 1
for args, _ in logger.warning.call_args_list:
assert "Missing description for the metric" in args[0]
@pytest.mark.parametrize(
"manifest, metric_definitions, expected",
[
[
SAMPLE_INI,
"""
metrics:
"FirstPaint":
aliases:
- fcp
- SpeedIndex
- SpeedIndex2
description: "Example"
"FirstPaint2":
aliases:
- fcp
- SpeedIndex
- SpeedIndex2
description: "Example" """,
3,
],
[
SAMPLE_METRICS_INI,
"""
metrics:
FirstPaint:
aliases:
- fcp
- SpeedIndex3
- SpeedIndex
description: Example
SpeedIndex:
aliases:
- speedindex
- si
description: Example
SpeedIndex3:
aliases:
- speedindex
- si
- fcp
description: Example
""",
5,
],
],
)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_duplicate_metrics(
logger, structured_logger, perfdocs_sample, manifest, metric_definitions, expected
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_METRICS_CONFIG.format(metric_definitions))
with open(perfdocs_sample["manifest"]["path"], "w", newline="\n") as f:
f.write(manifest)
sample_gatherer_result = {
"suite": {"Example": {"metrics": ["fcp", "SpeedIndex"]}},
"another_suite": {"Example": {}},
}
from perfdocs.verifier import Verifier
with mock.patch("perfdocs.framework_gatherers.RaptorGatherer.get_test_list") as m:
m.return_value = sample_gatherer_result
verifier = Verifier(top_dir)
verifier.validate_tree()
assert len(logger.warning.call_args_list) == expected
for args, _ in logger.warning.call_args_list:
assert "Duplicate definitions found for " in args[0]
@pytest.mark.parametrize(
"manifest, metric_definitions",
[
[
SAMPLE_INI,
"""
metrics:
"FirstPaint":
aliases:
- fcp
- SpeedIndex
description: "Example" """,
],
[
SAMPLE_METRICS_INI,
"""
metrics:
FirstPaint:
aliases:
- fcp
description: Example
SpeedIndex:
aliases:
- speedindex
- si
description: Example
""",
],
],
)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_verifier_valid_metrics(
logger, structured_logger, perfdocs_sample, manifest, metric_definitions
):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_METRICS_CONFIG.format(metric_definitions, ""))
with open(perfdocs_sample["manifest"]["path"], "w", newline="\n") as f:
f.write(manifest)
sample_gatherer_result = {
"suite": {"Example": {"metrics": ["fcp", "SpeedIndex"]}},
"another_suite": {"Example": {}},
}
from perfdocs.verifier import Verifier
with mock.patch("perfdocs.framework_gatherers.RaptorGatherer.get_test_list") as m:
m.return_value = sample_gatherer_result
verifier = Verifier(top_dir)
verifier.validate_tree()
assert len(logger.warning.call_args_list) == 0
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_framework_gatherers(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
# Check to make sure that every single framework
# gatherer that has been implemented produces a test list
# in every suite that contains a test with an associated
# manifest.
from perfdocs.gatherer import frameworks
for framework, gatherer in frameworks.items():
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_SAMPLE_CONFIG.format(framework))
fg = gatherer(perfdocs_sample["config"], top_dir)
if getattr(fg, "get_test_list", None) is None:
# Skip framework gatherers that have not
# implemented a method to build a test list.
continue
# Setup some framework-specific things here if needed
if framework == "raptor":
fg._manifest_path = perfdocs_sample["manifest"]["path"]
fg._get_subtests_from_ini = mock.Mock()
fg._get_subtests_from_ini.return_value = {
"Example": perfdocs_sample["manifest"],
}
if framework == "talos":
fg._get_ci_tasks = mock.Mock()
for suite, suitetests in fg.get_test_list().items():
assert suite == "Talos Tests"
assert suitetests
continue
if framework == "awsy":
for suite, suitetests in fg.get_test_list().items():
assert suite == "Awsy tests"
assert suitetests
continue
for suite, suitetests in fg.get_test_list().items():
assert suite == "suite"
for test, manifest in suitetests.items():
assert test == "Example"
assert (
pathlib.Path(manifest["path"])
== perfdocs_sample["manifest"]["path"]
)
@mock.patch("perfdocs.logger.PerfDocLogger")
def test_perfdocs_framework_gatherers_urls(logger, structured_logger, perfdocs_sample):
top_dir = perfdocs_sample["top_dir"]
setup_sample_logger(logger, structured_logger, top_dir)
from perfdocs.gatherer import frameworks
from perfdocs.generator import Generator
from perfdocs.utils import read_yaml
from perfdocs.verifier import Verifier
# This test is only for raptor
gatherer = frameworks["raptor"]
with open(perfdocs_sample["config"], "w", newline="\n") as f:
f.write(DYNAMIC_SAMPLE_CONFIG.format("raptor"))
fg = gatherer(perfdocs_sample["config_2"], top_dir)
fg.get_suite_list = mock.Mock()
fg.get_suite_list.return_value = {
"suite": [perfdocs_sample["example1_manifest"]],
"another_suite": [perfdocs_sample["example2_manifest"]],
}
v = Verifier(top_dir)
gn = Generator(v, generate=True, workspace=top_dir)
# Check to make sure that if a test is present under multiple
# suties the urls are generated correctly for the test under
# every suite
for suite, suitetests in fg.get_test_list().items():
url = fg._descriptions.get(suite)
assert url is not None
assert url[0]["name"] == "Example"
assert url[0]["test_url"] == "Example_url"
perfdocs_tree = gn._perfdocs_tree[0]
yaml_content = read_yaml(
pathlib.Path(
os.path.join(os.path.join(perfdocs_tree["path"], perfdocs_tree["yml"]))
)
)
suites = yaml_content["suites"]
# Check that the sections for each suite are generated correctly
for suite_name, suite_details in suites.items():
gn._verifier._gatherer = mock.Mock(framework_gatherers={"raptor": gatherer})
section = gn._verifier._gatherer.framework_gatherers[
"raptor"
].build_suite_section(fg, suite_name, suites.get(suite_name)["description"])
assert suite_name.capitalize() == section[0]
assert suite_name in section[2]
tests = suites.get(suite_name).get("tests", {})
for test_name in tests.keys():
desc = gn._verifier._gatherer.framework_gatherers[
"raptor"
].build_test_description(
fg, test_name, tests[test_name], suite_name, {"fcp": {}}
)
assert f"**test url**: `<{url[0]['test_url']}>`__" in desc[0]
assert f"**expected**: {url[0]['expected']}" in desc[0]
assert test_name in desc[0]
def test_perfdocs_logger_failure(config, paths):
from perfdocs.logger import PerfDocLogger
PerfDocLogger.LOGGER = None
with pytest.raises(Exception):
PerfDocLogger()
PerfDocLogger.PATHS = []
with pytest.raises(Exception):
PerfDocLogger()
if __name__ == "__main__":
mozunit.main()