Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/streamline testplan report - part 1 #1163

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/test_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ jobs:
with:
python-version: 3.11
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: "16.x"
node-version: 20
- name: Restore pip cache
uses: ./.github/actions/pip-cache
- name: Set up PNPM
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
with:
version: 8.10.4
version: 9
- name: Setup
run: |
pip install -r requirements-build.txt -U
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:
- name: Set up Kafka for tests
if: ${{ matrix.os == 'ubuntu-22.04' }}
run: |
wget https://archive.apache.org/dist/kafka/3.5.2/kafka_2.13-3.5.2.tgz -O kafka.tgz
wget https://dlcdn.apache.org/kafka/3.9.0/kafka_2.13-3.9.0.tgz -O kafka.tgz
sudo mkdir /opt/kafka
sudo chown -R $USER:$USER /opt/kafka
tar zxf kafka.tgz -C /opt/kafka --strip-components 1
Expand Down
9 changes: 9 additions & 0 deletions doc/newsfragments/2900_changed.streamline_json_report.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Change Testplan exported JSON report structure to reduce report size.

* Remove unused report entry fields.
* ``fix_spec_path`` and ``host``.
* ``status_override`` and ``status_reason`` in case they are empty.
* ``line_no``, ``code_context`` and ``file_path`` if ``--code`` is not enabled.
* ``env_status``, ``part`` and ``strict_order`` depending on report category.
* Remove unused assertion entry fields ``category`` and ``flag`` if they are ``DEFAULT``.
* Merge assertion entry field ``utc_time`` and ``machine_time`` into unix-format ``timestamp``, and store timezone info in parent Test-level report under key ``timezone``.
6 changes: 5 additions & 1 deletion dodo.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ def task_test():

def task_build():
return {
"actions": ["python -m build -w"],
"actions": [
CmdAction(
"python -m build -w", env=updated_env({"DEV_BUILD": "0"})
)
],
"task_dep": ["build_ui"],
"doc": "Build a wheel package",
}
Expand Down
15 changes: 14 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"pytest-mock",
"psutil",
"schema",
"tzlocal",
"lxml",
"reportlab",
"marshmallow",
Expand Down Expand Up @@ -109,7 +110,6 @@
]

[tool.releaseherald]

news_fragments_directory = 'doc/newsfragments'
unreleased = true
news_file = 'doc/news_template.rst'
Expand All @@ -120,3 +120,16 @@
[tool.releaseherald.filename_metadata_extractor]
type="re"
pattern='''^(((?P<id>\d+)_?)?((?P<type>changed|new|deprecated|removed))?\.)?.*$'''

[tool.pytest.ini_options]
filterwarnings = [
"ignore::pytest.PytestWarning",
"ignore:.*flask_restx.*:DeprecationWarning",
# jsonschema warning from flask_restx
"ignore:.*jsonschema.*:DeprecationWarning",
"ignore:.*load_module.*:DeprecationWarning",
"ignore:.*LogMatcher.*:UserWarning",
# under most cases, included files are not hit
"ignore:No data was collected:coverage.exceptions.CoverageWarning",
]
norecursedirs = "tests/helpers"
12 changes: 0 additions & 12 deletions pytest.ini

This file was deleted.

6 changes: 3 additions & 3 deletions testplan/common/exporters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from testplan.common.config import Config, Configurable
from testplan.common.utils import strings
from testplan.common.utils.comparison import is_regex
from testplan.common.utils.timing import utcnow
from testplan.common.utils.timing import now
from testplan.report import TestReport


Expand All @@ -18,7 +18,7 @@ class ExporterResult:
result: Dict = None
traceback: str = None
uid: str = strings.uuid4()
start_time: datetime = utcnow()
start_time: datetime = now()
end_time: datetime = None

@property
Expand Down Expand Up @@ -154,7 +154,7 @@ def run_exporter(
except Exception:
exp_result.traceback = traceback.format_exc()
finally:
exp_result.end_time = utcnow()
exp_result.end_time = now()
if not exp_result.success:
exporter.logger.error(exp_result.traceback)
if result:
Expand Down
18 changes: 17 additions & 1 deletion testplan/common/report/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,23 @@ class ReportCategories:
# use for before/after_start/stop, setup, teardown, etc
SYNTHESIZED = "synthesized"

@classmethod
def is_test_level(cls, cat):
return cat in (
cls.MULTITEST,
cls.TASK_RERUN,
cls.GTEST,
cls.CPPUNIT,
cls.BOOST_TEST,
cls.HOBBESTEST,
cls.PYTEST,
cls.PYUNIT,
cls.UNITTEST,
cls.QUNIT,
cls.JUNIT,
cls.ERROR,
)


class Report:
"""
Expand Down Expand Up @@ -510,7 +527,6 @@ def __init__(self, name, **kwargs):
super(BaseReportGroup, self).__init__(name=name, **kwargs)

self._index: Dict = {}
self.host: Optional[str] = None
self.children = []

self.build_index()
Expand Down
4 changes: 2 additions & 2 deletions testplan/common/report/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def emit(self, record):
if hasattr(record, "report_obj_id"):
report = REPORT_MAP.get(record.report_obj_id)
if report is not None:
created = datetime.datetime.utcfromtimestamp(
created = datetime.datetime.fromtimestamp(
record.created
).replace(tzinfo=timezone.utc)
).astimezone()
report.logs.append(
{
"message": self.format(record),
Expand Down
34 changes: 30 additions & 4 deletions testplan/common/report/schemas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""
Base schemas for report serialization.
"""
from marshmallow import Schema, fields, post_load
import datetime

from marshmallow import Schema, fields, post_dump, post_load, pre_load
from marshmallow.utils import EXCLUDE

from testplan.common.report.base import (
Expand All @@ -22,8 +24,24 @@
class IntervalSchema(Schema):
"""Schema for ``timer.Interval``"""

start = custom_fields.UTCDateTime()
end = custom_fields.UTCDateTime(allow_none=True)
start = fields.DateTime("timestamp")
end = fields.DateTime("timestamp", allow_none=True)

@pre_load
def accept_old_isoformat(self, data, **kwargs):
try:
if data.get("start", None) and isinstance(data["start"], str):
data["start"] = datetime.datetime.fromisoformat(
data["start"]
).timestamp()
if data.get("end", None) and isinstance(data["end"], str):
data["end"] = datetime.datetime.fromisoformat(
data["end"]
).timestamp()
return data
except ValueError as e:
# no need to defer
raise ValueError("Invalid value when loading Interval.") from e

@post_load
def make_interval(self, data, **kwargs):
Expand Down Expand Up @@ -68,7 +86,7 @@ class ReportLogSchema(Schema):
message = fields.String()
levelname = fields.String()
levelno = fields.Integer()
created = custom_fields.UTCDateTime()
created = fields.DateTime("timestamp")
funcName = fields.String()
lineno = fields.Integer()
uid = fields.UUID()
Expand Down Expand Up @@ -126,6 +144,14 @@ def make_report(self, data, **kwargs):
rep.timer = timer
return rep

@post_dump
def strip_none(self, data, **kwargs):
if data["status_override"] is None:
del data["status_override"]
if data["status_reason"] is None:
del data["status_reason"]
return data


class BaseReportGroupSchema(ReportSchema):
"""Schema for ``base.BaseReportGroup``."""
Expand Down
21 changes: 12 additions & 9 deletions testplan/common/utils/timing.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ def retry_until_timeout(

def utcnow() -> datetime.datetime:
"""Timezone aware UTC now."""
return datetime.datetime.utcnow().replace(tzinfo=timezone.utc)
return datetime.datetime.now(tz=timezone.utc)


def now() -> datetime.datetime:
"""Timezone aware local time."""
return datetime.datetime.now().astimezone()


_Interval = collections.namedtuple("_Interval", "start end")
Expand Down Expand Up @@ -299,17 +304,15 @@ def __init__(self, timer, key):
self.start_ts = None

def __enter__(self):
self.start_ts = utcnow()
self.start_ts = now()

def __exit__(self, exc_type, exc_value, _):
if self.key in self.timer:
self.timer[self.key].append(
Interval(start=self.start_ts, end=utcnow())
Interval(start=self.start_ts, end=now())
)
else:
self.timer[self.key] = [
Interval(start=self.start_ts, end=utcnow())
]
self.timer[self.key] = [Interval(start=self.start_ts, end=now())]


class Timer(dict):
Expand All @@ -333,9 +336,9 @@ def record(self, key):
def start(self, key):
"""Record the start timestamp for the given key."""
if key in self:
self[key].append(Interval(utcnow(), None))
self[key].append(Interval(now(), None))
else:
self[key] = [Interval(utcnow(), None)]
self[key] = [Interval(now(), None)]

def end(self, key):
"""
Expand All @@ -344,7 +347,7 @@ def end(self, key):
if key not in self or self.last(key).end is not None:
raise KeyError(f"`start` missing for {key}, cannot record end.")

self[key][-1] = Interval(self[key][-1].start, utcnow())
self[key][-1] = Interval(self[key][-1].start, now())

def merge(self, timer):
for key in timer:
Expand Down
5 changes: 4 additions & 1 deletion testplan/exporters/testing/xml/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,10 @@ def export(
xml_dir = pathlib.Path(self.cfg.xml_dir).resolve()

if xml_dir.exists():
shutil.rmtree(xml_dir)
if xml_dir.is_dir():
shutil.rmtree(xml_dir)
else:
xml_dir.unlink()

xml_dir.mkdir(parents=True, exist_ok=True)

Expand Down
4 changes: 3 additions & 1 deletion testplan/importers/cppunit.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,9 @@ def cppunit_to_junit(report: str, name: str) -> str:
if testsuite.get("timestamp") is None:
testsuite.set(
"timestamp",
datetime.datetime.utcnow().isoformat().split(".")[0],
datetime.datetime.now(tz=datetime.timezone.utc)
.isoformat()
.split(".")[0],
)
if testsuite.get("hostname") is None:
testsuite.set("hostname", socket.gethostname())
Expand Down
Loading
Loading