|
7 | 7 | import shutil |
8 | 8 | from ipaddress import IPv4Interface |
9 | 9 | from pathlib import Path |
| 10 | +from typing import Dict |
10 | 11 |
|
11 | 12 | from mfd_host import Host |
12 | 13 | from mfd_connect.exceptions import ( |
|
16 | 17 | import pytest |
17 | 18 |
|
18 | 19 | from common.nicctl import Nicctl |
| 20 | + |
19 | 21 | from Engine.const import ( |
20 | 22 | ALLOWED_FFMPEG_VERSIONS, |
21 | 23 | DEFAULT_FFMPEG_PATH, |
|
32 | 34 | MCM_BUILD_PATH, |
33 | 35 | MTL_BUILD_PATH, |
34 | 36 | OPENH264_VERSION_TAG, |
| 37 | + TESTCMD_LVL |
| 38 | +) |
| 39 | +from Engine.csv_report import ( |
| 40 | + csv_add_test, |
| 41 | + csv_write_report, |
| 42 | + update_compliance_result, |
| 43 | + |
35 | 44 | ) |
36 | 45 | from Engine.mcm_apps import MediaProxy, MeshAgent, get_mcm_path, get_mtl_path |
37 | 46 | from datetime import datetime |
| 47 | +from mfd_common_libs.custom_logger import add_logging_level |
| 48 | +from mfd_common_libs.log_levels import TEST_FAIL, TEST_INFO, TEST_PASS |
| 49 | +from pytest_mfd_logging.amber_log_formatter import AmberLogFormatter |
| 50 | + |
38 | 51 |
|
39 | 52 | logger = logging.getLogger(__name__) |
| 53 | +phase_report_key = pytest.StashKey[Dict[str, pytest.CollectReport]]() |
| 54 | + |
| 55 | + |
| 56 | +@pytest.hookimpl(wrapper=True, tryfirst=True) |
| 57 | +def pytest_runtest_makereport(item, call): |
| 58 | + # execute all other hooks to obtain the report object |
| 59 | + rep = yield |
| 60 | + |
| 61 | + # store test results for each phase of a call, which can |
| 62 | + # be "setup", "call", "teardown" |
| 63 | + item.stash.setdefault(phase_report_key, {})[rep.when] = rep |
| 64 | + |
| 65 | + return rep |
40 | 66 |
|
41 | 67 |
|
42 | 68 | @pytest.fixture(scope="function") |
@@ -770,3 +796,89 @@ def log_interface_driver_info(hosts: dict[str, Host]) -> None: |
770 | 796 | logger.info( |
771 | 797 | f"Interface {interface.name} on host {host.name} uses driver: {driver_info.driver_name} ({driver_info.driver_version})" |
772 | 798 | ) |
| 799 | + |
| 800 | + |
| 801 | +@pytest.fixture(scope="session", autouse=True) |
| 802 | +def log_session(): |
| 803 | + add_logging_level("TESTCMD", TESTCMD_LVL) |
| 804 | + |
| 805 | + today = datetime.today() |
| 806 | + folder = today.strftime("%Y-%m-%dT%H:%M:%S") |
| 807 | + path = os.path.join(LOG_FOLDER, folder) |
| 808 | + path_symlink = os.path.join(LOG_FOLDER, "latest") |
| 809 | + try: |
| 810 | + os.remove(path_symlink) |
| 811 | + except FileNotFoundError: |
| 812 | + pass |
| 813 | + os.makedirs(path, exist_ok=True) |
| 814 | + os.symlink(folder, path_symlink) |
| 815 | + yield |
| 816 | + shutil.copy("pytest.log", f"{LOG_FOLDER}/latest/pytest.log") |
| 817 | + csv_write_report(f"{LOG_FOLDER}/latest/report.csv") |
| 818 | + |
| 819 | +@pytest.fixture(scope="function", autouse=True) |
| 820 | +def log_case(request, caplog: pytest.LogCaptureFixture): |
| 821 | + case_id = request.node.nodeid |
| 822 | + case_folder = os.path.dirname(case_id) |
| 823 | + os.makedirs(os.path.join(LOG_FOLDER, "latest", case_folder), exist_ok=True) |
| 824 | + logfile = os.path.join(LOG_FOLDER, "latest", f"{case_id}.log") |
| 825 | + fh = logging.FileHandler(logfile) |
| 826 | + formatter = request.session.config.pluginmanager.get_plugin( |
| 827 | + "logging-plugin" |
| 828 | + ).formatter |
| 829 | + format = AmberLogFormatter(formatter) |
| 830 | + fh.setFormatter(format) |
| 831 | + fh.setLevel(logging.DEBUG) |
| 832 | + logger = logging.getLogger() |
| 833 | + logger.addHandler(fh) |
| 834 | + yield |
| 835 | + report = request.node.stash[phase_report_key] |
| 836 | + if report["setup"].failed: |
| 837 | + logging.log(level=TEST_FAIL, msg=f"Setup failed for {case_id}") |
| 838 | + os.chmod(logfile, 0o4755) |
| 839 | + result = "Fail" |
| 840 | + elif ("call" not in report) or report["call"].failed: |
| 841 | + logging.log(level=TEST_FAIL, msg=f"Test failed for {case_id}") |
| 842 | + os.chmod(logfile, 0o4755) |
| 843 | + result = "Fail" |
| 844 | + elif report["call"].passed: |
| 845 | + logging.log(level=TEST_PASS, msg=f"Test passed for {case_id}") |
| 846 | + os.chmod(logfile, 0o755) |
| 847 | + result = "Pass" |
| 848 | + else: |
| 849 | + logging.log(level=TEST_INFO, msg=f"Test skipped for {case_id}") |
| 850 | + result = "Skip" |
| 851 | + |
| 852 | + logger.removeHandler(fh) |
| 853 | + |
| 854 | + commands = [] |
| 855 | + for record in caplog.get_records("call"): |
| 856 | + if record.levelno == TESTCMD_LVL: |
| 857 | + commands.append(record.message) |
| 858 | + |
| 859 | + csv_add_test( |
| 860 | + test_case=case_id, |
| 861 | + commands="\n".join(commands), |
| 862 | + result=result, |
| 863 | + issue="n/a", |
| 864 | + result_note="n/a", |
| 865 | + ) |
| 866 | + |
| 867 | + |
| 868 | +@pytest.fixture(scope="session") |
| 869 | +def compliance_report(request, log_session, test_config): |
| 870 | + """ |
| 871 | + This function is used for compliance check and report. |
| 872 | + """ |
| 873 | + # TODO: Implement compliance check logic. When tcpdump pcap is enabled, at the end of the test session all pcaps |
| 874 | + # shall be send into EBU list. |
| 875 | + # Pcaps shall be stored in the ramdisk, and then moved to the compliance |
| 876 | + # folder or send into EBU list after each test finished and remove it from the ramdisk. |
| 877 | + # Compliance report generation logic goes here after yield. Or in another class / function but triggered here. |
| 878 | + # AFAIK names of pcaps contains test name so it can be matched with result of each test like in code below. |
| 879 | + yield |
| 880 | + if test_config.get("compliance", False): |
| 881 | + logging.info("Compliance mode enabled, updating compliance results") |
| 882 | + for item in request.session.items: |
| 883 | + test_case = item.nodeid |
| 884 | + update_compliance_result(test_case, "Fail") |
0 commit comments