2022-04-21 18:11:49 +03:00
|
|
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
import enum
|
|
|
|
import functools
|
|
|
|
import operator
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
|
|
class TestStatus(enum.Enum):
|
|
|
|
PASSED = enum.auto()
|
|
|
|
FAILED = enum.auto()
|
|
|
|
SKIPPED = enum.auto()
|
|
|
|
ERRORED = enum.auto()
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def from_string(cls, value: str):
|
|
|
|
value = value.lower()
|
|
|
|
if value.startswith("pass"):
|
|
|
|
return cls.PASSED
|
|
|
|
if value.startswith(("ignore", "skip")):
|
|
|
|
return cls.SKIPPED
|
|
|
|
if value.startswith("fail"):
|
|
|
|
return cls.FAILED
|
|
|
|
raise ValueError(f"Unknown test status `{value}`")
|
|
|
|
|
|
|
|
|
|
|
|
class TestCaseSource:
|
|
|
|
def __init__(self, file, line=None):
|
|
|
|
self.file = file
|
|
|
|
self.line = line
|
|
|
|
|
|
|
|
|
|
|
|
class TestCase:
|
|
|
|
def __init__( # pylint: disable=too-many-arguments
|
2022-04-23 19:19:25 +03:00
|
|
|
self,
|
|
|
|
name,
|
|
|
|
status,
|
|
|
|
message=None,
|
|
|
|
stdout=None,
|
|
|
|
source=None,
|
|
|
|
duration=0,
|
|
|
|
exception=None,
|
2022-04-21 18:11:49 +03:00
|
|
|
):
|
|
|
|
assert isinstance(status, TestStatus)
|
2022-04-23 19:19:25 +03:00
|
|
|
if status == TestStatus.ERRORED:
|
|
|
|
assert isinstance(exception, Exception)
|
2022-04-21 18:11:49 +03:00
|
|
|
self.name = name.strip()
|
|
|
|
self.status = status
|
2022-04-23 19:19:25 +03:00
|
|
|
self.message = message
|
|
|
|
self.stdout = stdout
|
2022-04-21 18:11:49 +03:00
|
|
|
self.source = source
|
2022-04-23 19:19:25 +03:00
|
|
|
self.duration = duration
|
|
|
|
self.exception = exception
|
2022-04-21 18:11:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
class TestSuite:
|
|
|
|
def __init__(self, env_name, test_name):
|
|
|
|
self.env_name = env_name
|
|
|
|
self.test_name = test_name
|
2022-04-23 19:19:25 +03:00
|
|
|
self.timestamp = 0
|
2022-04-21 18:11:49 +03:00
|
|
|
self.duration = 0
|
|
|
|
self._cases = []
|
|
|
|
self._finished = False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cases(self):
|
|
|
|
return self._cases
|
|
|
|
|
|
|
|
@property
|
|
|
|
def status(self):
|
2022-04-23 19:19:25 +03:00
|
|
|
for s in (TestStatus.ERRORED, TestStatus.FAILED):
|
|
|
|
if self.get_status_nums(s):
|
|
|
|
return s
|
2022-04-21 18:11:49 +03:00
|
|
|
if self._cases and any(c.status == TestStatus.PASSED for c in self._cases):
|
|
|
|
return TestStatus.PASSED
|
|
|
|
return TestStatus.SKIPPED
|
|
|
|
|
2022-04-23 19:19:25 +03:00
|
|
|
def get_status_nums(self, status):
|
|
|
|
return len([True for c in self._cases if c.status == status])
|
|
|
|
|
2022-04-21 18:11:49 +03:00
|
|
|
def add_case(self, case: TestCase):
|
|
|
|
assert isinstance(case, TestCase)
|
|
|
|
self._cases.append(case)
|
|
|
|
|
|
|
|
def is_finished(self):
|
|
|
|
return self._finished
|
|
|
|
|
|
|
|
def on_start(self):
|
2022-04-23 19:19:25 +03:00
|
|
|
self.timestamp = time.time()
|
2022-04-21 18:11:49 +03:00
|
|
|
|
|
|
|
def on_finish(self):
|
|
|
|
if self.is_finished():
|
|
|
|
return
|
|
|
|
self._finished = True
|
2022-04-23 19:19:25 +03:00
|
|
|
self.duration = time.time() - self.timestamp
|
2022-04-21 18:11:49 +03:00
|
|
|
|
|
|
|
|
|
|
|
class TestSummary:
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
self._suites = []
|
|
|
|
|
|
|
|
@property
|
|
|
|
def suites(self):
|
|
|
|
return self._suites
|
|
|
|
|
|
|
|
def add_suite(self, suite):
|
|
|
|
assert isinstance(suite, TestSuite)
|
|
|
|
self._suites.append(suite)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def duration(self):
|
|
|
|
return functools.reduce(operator.add, [s.duration for s in self._suites])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def case_nums(self):
|
|
|
|
return functools.reduce(operator.add, [len(s.cases) for s in self._suites])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_errored(self):
|
|
|
|
return any(s.status == TestStatus.ERRORED for s in self._suites)
|
|
|
|
|
|
|
|
def get_status_nums(self, status):
|
|
|
|
return functools.reduce(
|
|
|
|
operator.add, [s.get_status_nums(status) for s in self._suites]
|
|
|
|
)
|