Create Allure report for perf tests (#2326)

This commit is contained in:
Alexander Bayandin
2022-08-31 16:15:26 +01:00
committed by GitHub
parent 5745dbdd33
commit d7c9cfe7bb
8 changed files with 195 additions and 189 deletions
+21 -42
View File
@@ -6,12 +6,10 @@ import enum
import filecmp
import json
import os
import pathlib
import re
import shutil
import socket
import subprocess
import tarfile
import tempfile
import textwrap
import time
@@ -22,7 +20,6 @@ from enum import Flag, auto
from pathlib import Path
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, TypeVar, Union, cast
import allure # type: ignore
import asyncpg
import backoff # type: ignore
import boto3
@@ -38,7 +35,14 @@ from psycopg2.extensions import connection as PgConnection
from psycopg2.extensions import make_dsn, parse_dsn
from typing_extensions import Literal
from .utils import etcd_path, get_self_dir, lsn_from_hex, lsn_to_hex, subprocess_capture
from .utils import (
allure_attach_from_dir,
etcd_path,
get_self_dir,
lsn_from_hex,
lsn_to_hex,
subprocess_capture,
)
"""
This file contains pytest fixtures. A fixture is a test resource that can be
@@ -99,7 +103,7 @@ def pytest_configure(config):
top_output_dir = env_test_output
else:
top_output_dir = os.path.join(base_dir, DEFAULT_OUTPUT_DIR)
pathlib.Path(top_output_dir).mkdir(exist_ok=True)
Path(top_output_dir).mkdir(exist_ok=True)
# Find the postgres installation.
global pg_distrib_dir
@@ -234,11 +238,12 @@ def default_broker(request: Any, port_distributor: PortDistributor):
client_port = port_distributor.get_port()
# multiple pytest sessions could get launched in parallel, get them different datadirs
etcd_datadir = os.path.join(get_test_output_dir(request), f"etcd_datadir_{client_port}")
pathlib.Path(etcd_datadir).mkdir(exist_ok=True, parents=True)
Path(etcd_datadir).mkdir(exist_ok=True, parents=True)
broker = Etcd(datadir=etcd_datadir, port=client_port, peer_port=port_distributor.get_port())
yield broker
broker.stop()
allure_attach_from_dir(Path(etcd_datadir))
@pytest.fixture(scope="session")
@@ -1882,7 +1887,7 @@ class Postgres(PgProtocol):
self.env.neon_cli.pg_create(
branch_name, node_name=self.node_name, tenant_id=self.tenant_id, lsn=lsn, port=self.port
)
path = pathlib.Path("pgdatadirs") / "tenants" / self.tenant_id.hex / self.node_name
path = Path("pgdatadirs") / "tenants" / self.tenant_id.hex / self.node_name
self.pgdata_dir = os.path.join(self.env.repo_dir, path)
if config_lines is None:
@@ -1913,7 +1918,7 @@ class Postgres(PgProtocol):
def pg_data_dir_path(self) -> str:
"""Path to data directory"""
assert self.node_name
path = pathlib.Path("pgdatadirs") / "tenants" / self.tenant_id.hex / self.node_name
path = Path("pgdatadirs") / "tenants" / self.tenant_id.hex / self.node_name
return os.path.join(self.env.repo_dir, path)
def pg_xact_dir_path(self) -> str:
@@ -2289,7 +2294,7 @@ class Etcd:
log.debug(f"etcd is already running on port {self.port}")
return
pathlib.Path(self.datadir).mkdir(exist_ok=True)
Path(self.datadir).mkdir(exist_ok=True)
if not self.binary_path.is_file():
raise RuntimeError(f"etcd broker binary '{self.binary_path}' is not a file")
@@ -2329,26 +2334,16 @@ class Etcd:
self.handle.wait()
def get_test_output_dir(request: Any) -> pathlib.Path:
def get_test_output_dir(request: Any) -> Path:
"""Compute the working directory for an individual test."""
test_name = request.node.name
test_dir = pathlib.Path(top_output_dir) / test_name.replace("/", "-")
test_dir = Path(top_output_dir) / test_name.replace("/", "-")
log.info(f"get_test_output_dir is {test_dir}")
# make mypy happy
assert isinstance(test_dir, pathlib.Path)
assert isinstance(test_dir, Path)
return test_dir
ATTACHMENT_SUFFIXES = frozenset(
(
".log",
".stderr",
".stdout",
".diffs",
)
)
# This is autouse, so the test output directory always gets created, even
# if a test doesn't put anything there. It also solves a problem with the
# neon_simple_env fixture: if TEST_SHARED_FIXTURES is not set, it
@@ -2359,7 +2354,7 @@ ATTACHMENT_SUFFIXES = frozenset(
# this fixture ensures that the directory exists. That works because
# 'autouse' fixtures are run before other fixtures.
@pytest.fixture(scope="function", autouse=True)
def test_output_dir(request: Any) -> Iterator[pathlib.Path]:
def test_output_dir(request: Any) -> Iterator[Path]:
"""Create the working directory for an individual test."""
# one directory per test
@@ -2370,23 +2365,7 @@ def test_output_dir(request: Any) -> Iterator[pathlib.Path]:
yield test_dir
for attachment in test_dir.glob("**/*"):
if attachment.suffix in ATTACHMENT_SUFFIXES:
source = str(attachment)
name = str(attachment.relative_to(test_dir))
attachment_type = "text/plain"
extension = attachment.suffix.removeprefix(".")
# compress files larger than 1Mb, they're hardly readable in a browser
if attachment.stat().st_size > 1024 * 1024:
source = f"{attachment}.tar.gz"
with tarfile.open(source, "w:gz") as tar:
tar.add(attachment, arcname=attachment.name)
name = f"{name}.tar.gz"
attachment_type = "application/gzip"
extension = "tar.gz"
allure.attach.file(source, name, attachment_type, extension)
allure_attach_from_dir(test_dir)
SKIP_DIRS = frozenset(
@@ -2439,7 +2418,7 @@ def should_skip_file(filename: str) -> bool:
#
# Test helpers
#
def list_files_to_compare(pgdata_dir: pathlib.Path):
def list_files_to_compare(pgdata_dir: Path):
pgdata_files = []
for root, _file, filenames in os.walk(pgdata_dir):
for filename in filenames:
@@ -2492,7 +2471,7 @@ def check_restored_datadir_content(test_output_dir: Path, env: NeonEnv, pg: Post
# list files we're going to compare
assert pg.pgdata_dir
pgdata_files = list_files_to_compare(pathlib.Path(pg.pgdata_dir))
pgdata_files = list_files_to_compare(Path(pg.pgdata_dir))
restored_files = list_files_to_compare(restored_dir_path)
# check that file sets are equal
+37 -2
View File
@@ -1,11 +1,13 @@
import contextlib
import os
import pathlib
import re
import shutil
import subprocess
import tarfile
from pathlib import Path
from typing import Any, List, Tuple
import allure # type: ignore
from fixtures.log_helper import log
from psycopg2.extensions import cursor
@@ -116,7 +118,7 @@ def get_dir_size(path: str) -> int:
return totalbytes
def get_timeline_dir_size(path: pathlib.Path) -> int:
def get_timeline_dir_size(path: Path) -> int:
"""Get the timeline directory's total size, which only counts the layer files' size."""
sz = 0
for dir_entry in path.iterdir():
@@ -161,3 +163,36 @@ def get_scale_for_db(size_mb: int) -> int:
"""
return round(0.06689 * size_mb - 0.5)
ATTACHMENT_NAME_REGEX = re.compile(
r".+\.log|.+\.stderr|.+\.stdout|.+\.filediff|.+\.metrics|flamegraph\.svg|regression\.diffs"
)
def allure_attach_from_dir(dir: Path):
"""Attach all non-empty files from `dir` that matches `ATTACHMENT_NAME_REGEX` to Allure report"""
for attachment in Path(dir).glob("**/*"):
if ATTACHMENT_NAME_REGEX.fullmatch(attachment.name) and attachment.stat().st_size > 0:
source = str(attachment)
name = str(attachment.relative_to(dir))
# compress files larger than 1Mb, they're hardly readable in a browser
if attachment.stat().st_size > 1024 * 1024:
source = f"{attachment}.tar.gz"
with tarfile.open(source, "w:gz") as tar:
tar.add(attachment, arcname=attachment.name)
name = f"{name}.tar.gz"
if source.endswith(".tar.gz"):
attachment_type = "application/gzip"
extension = "tar.gz"
elif source.endswith(".svg"):
attachment_type = "image/svg+xml"
extension = "svg"
else:
attachment_type = "text/plain"
extension = attachment.suffix.removeprefix(".")
allure.attach.file(source, name, attachment_type, extension)