Skip to content

Commit a271ceb

Browse files
sofyakurilovajboddeyOlgaMardvilkohitnik
authored
Merge release v2.3.2 into main (#1435)
* Show loader for stopping status (#1299) * Fix settings update (#1300) Co-authored-by: J Boddey <boddey@google.com> * Highlight Required if possible and Required with Non-Compliant result (#1305) * 409766621: (feat) add clear search button (#1307) * Calculate tests count for first page (#1306) * Calculate tests count for first page * refactor first page calculation --------- Co-authored-by: Aliaksandr Nikitsin <aliaksandrn@google.com> * 418729718: (fix) change error message view for the same port selected (#1308) * Add details feild to the test case result * pylint * fix "details" key error * 416241072: (feat) provide further context in test results (#1309) * 413638736: (fix) change design for discard popup (#1310) * 410565507: (fix) changes to close form when creating via actions button (#1311) * 410583302: (fix) changes for prevent RA disappeared after copy (#1315) * 421374390: (fix) change save risk profile popup design (#1316) * As create date does not contain time, the validator change to compare profile (#1317) * Change menu for outdated device (#1318) * 410583302: (fix) changes for prevent profile disappeared after copy (#1319) * Align the details by center (#1320) * 421375877: (fix) remove actions menu for copy profile (#1322) * Change order of inputs (#1324) * Adds new status for easier profile management (#1325) * Change condition as value can be null (#1327) * Clear copy is profile status is not Copy (#1328) * refactor _connection_ipaddr_ip_change * refactor _connection_dhcp_address * refactor _connection_mac_address * refactor _connection_mac_oui * fix oui not found * refactor of _connection_target_ping * refactor _connection_ipaddr_dhcp_failover * refactor _connection_dhcp_disconnect * refactor _connection_ipv6_slaac * refactor _connection_ipv6_ping * refactor setup_single_dhcp_server * refactor _change_subnet * pylint * added time to risk profile creation date * device created_at, modified_at field * FE: Refactor RA date format - add time (#1331) * Adds device sorting * Fix tests * change device creation approach * add details to report json * split details to list * refactor split test results to pages approach * Adds styles for details * qualification test details * fix report variables * Do not insert copy when the same copy is already exist (#1339) * Fix css (#1341) * Hide scroll for items list (#1342) * fix text aligment and add title * place details under the description field * Service module timeout fix (#1333) * discover open posrt before full scan * tsp ports discover refactor * refactor service module tests no-complient * scan only http port in TLS module * description line break * Do not highlight the error row (#1352) * Get updated tip element after timeout to prevent focus previous (undefined) tip (#1354) * Fix tooltips (#1355) * fix ip.dst keyerror * error messages * fix pyshark error * fix not_valid_before depracation warning * fix tests deprecation warning * pylint * fix inconsistent quotas * refactor https_detect * set timeout to 5 seconds * pylint * Fix npm audit issues (#1366) * docs: fix typo DEGUG → DEBUG in log level example * update python dependancies * update grcpio * update risk profiles * convert details to the list of strings * unit tests * pylint * change dns module styles * unit tests * detecting https using requests module * pylint * Change the method to count test results (#1373) * Highlight row when test result non compliant and required (#1374) * Change regular expression to satisfy the requirements (#1376) * Show "Welcome modal" when the version is updated (#1377) * change details display method * Github. Update documentation (#1382) Signed-off-by: Sofia Kurilova <kurilova@google.com> * create a virtual test device * vagrant and unsible base configs * local CA tutorial * Do not include In Progress tests in test count * Merge release v2.2.2 into main (#1378) (#1393) * Show loader for stopping status (#1299) * Fix settings update (#1300) * Highlight Required if possible and Required with Non-Compliant result (#1305) * 409766621: (feat) add clear search button (#1307) * Calculate tests count for first page (#1306) * Calculate tests count for first page * refactor first page calculation --------- * 418729718: (fix) change error message view for the same port selected (#1308) * Add details feild to the test case result * pylint * fix "details" key error * 416241072: (feat) provide further context in test results (#1309) * 413638736: (fix) change design for discard popup (#1310) * 410565507: (fix) changes to close form when creating via actions button (#1311) * 410583302: (fix) changes for prevent RA disappeared after copy (#1315) * 421374390: (fix) change save risk profile popup design (#1316) * As create date does not contain time, the validator change to compare profile (#1317) * Change menu for outdated device (#1318) * 410583302: (fix) changes for prevent profile disappeared after copy (#1319) * Align the details by center (#1320) * 421375877: (fix) remove actions menu for copy profile (#1322) * Change order of inputs (#1324) * Adds new status for easier profile management (#1325) * Change condition as value can be null (#1327) * Clear copy is profile status is not Copy (#1328) * refactor _connection_ipaddr_ip_change * refactor _connection_dhcp_address * refactor _connection_mac_address * refactor _connection_mac_oui * fix oui not found * refactor of _connection_target_ping * refactor _connection_ipaddr_dhcp_failover * refactor _connection_dhcp_disconnect * refactor _connection_ipv6_slaac * refactor _connection_ipv6_ping * refactor setup_single_dhcp_server * refactor _change_subnet * pylint * added time to risk profile creation date * device created_at, modified_at field * FE: Refactor RA date format - add time (#1331) * Adds device sorting * Fix tests * change device creation approach * add details to report json * split details to list * refactor split test results to pages approach * Adds styles for details * qualification test details * fix report variables * Do not insert copy when the same copy is already exist (#1339) * Fix css (#1341) * Hide scroll for items list (#1342) * fix text aligment and add title * place details under the description field * Service module timeout fix (#1333) * discover open posrt before full scan * tsp ports discover refactor * refactor service module tests no-complient * scan only http port in TLS module * description line break * Do not highlight the error row (#1352) * Get updated tip element after timeout to prevent focus previous (undefined) tip (#1354) * Fix tooltips (#1355) * fix ip.dst keyerror * error messages * fix pyshark error * fix not_valid_before depracation warning * fix tests deprecation warning * pylint * fix inconsistent quotas * refactor https_detect * set timeout to 5 seconds * pylint * Fix npm audit issues (#1366) * docs: fix typo DEGUG → DEBUG in log level example * update python dependancies * update grcpio * update risk profiles * convert details to the list of strings * unit tests * pylint * change dns module styles * unit tests * detecting https using requests module * pylint * Change the method to count test results (#1373) * Highlight row when test result non compliant and required (#1374) * Change regular expression to satisfy the requirements (#1376) * Show "Welcome modal" when the version is updated (#1377) * change details display method * Bump version --------- Co-authored-by: J Boddey <boddey@google.com> Co-authored-by: Olga Mardvilko <mardvilka@google.com> Co-authored-by: Aliaksandr Nikitsin <aliaksandrn@google.com> * ignore vagrant files * protocol compliant * change device mac address * connection module * ntp module comliant * changes in readme * dns module * tls outbound complient * tls compliant server * firewall * intermidiate certificate * pylint * add ansible * modbus non-compliant * fix modbus error * ftp ssh non-compiant * smtp imamp pop * http 80 port * snmp server * vnc * tftp * tftp ntp * bacnet * dhcp server * readme improve * dhcp server non-compliant * non-comliant mac manufacturer * non-compliant connection module * ntp non-compliant * tls non-compliant * feature not detected config * refactor compliant config * remove firewall * Highlight non-compliant test result; update version (the current release) (#1394) * Adds navigation bypass from main content * Deselect options when None is selected * Lint fix * Improve report to fix the rows on the page (#1396) * Improve report to fix the rows on the page * Fix some lint issues * Adds tests --------- Co-authored-by: Aleksander <aliaksandrn@google.com> * refactor ping methods * Fix the dependencies in Github (#1400) * Update dependencies * Update node in github action * Improve ui github actions * Prevent changing previously saved profile options after load (#1402) * add ntp pools whitelist to module config * dnspython * add dns servers to config * ntp whitelist implementation * change compliant vm device * remove comments * change tnp dhcp logic * Adds tooltip to add root or intermediate certificate (#1404) * Highlight all tests with the status "Non-compliant" (#1405) * AI suggested fix for proper rendering (#1406) * determining the type of certificates * intermidiate certificates tools * certificate validation refactior to support intermidiate certificates * remove certificates chain support * add check chain * instructions for certificate chain * [TR] update angular version (#1409) * Perform migration to the latest angular version (dependencies and code) * Fix tests * Fix budget * Update dependencies * Update dist path; remove alpine (#1413) * fix docker warning * fix get host user * Adds recommendations for tests (#1415) * ping refactor * start timer only when testrun is in progress * pylint * Fix styles (#1422) * [TR] The testing page is not updated properly after the test attempt has been started (#1421) * Change condition for proper rendering * 5 attempts to ping * pylint * move ping test uper in a test modules order * attempt to connect to the device web server with uploaded certificates * pylint * unite tests * Test Module Report - TLS Module (#1419) (#1423) * Test Module Report - TLS Module (#1419) * Generate html report for tls module --------- Co-authored-by: Aliaksandr Nikitsin <aliaksandrn@google.com> * Adds html report * Fix pylint * Adds html report * Adds html report * change module reports extension to html * rename module report file * rename report * remane report * pylint * fix dns module report --------- Co-authored-by: Aliaksandr Nikitsin <aliaksandrn@google.com> * Dump version --------- Signed-off-by: Sofia Kurilova <kurilova@google.com> Co-authored-by: J Boddey <boddey@google.com> Co-authored-by: Olga Mardvilko <mardvilka@google.com> Co-authored-by: Aliaksandr Nikitsin <aliaksandrn@google.com>
1 parent a226482 commit a271ceb

File tree

168 files changed

+13453
-14467
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+13453
-14467
lines changed

.github/workflows/testing.yml

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,21 @@ jobs:
138138
runs-on: ubuntu-latest
139139
steps:
140140
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
141+
141142
- name: Install Node
142143
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
143144
with:
144-
node-version: 18.19.0
145+
node-version: '20'
146+
cache: 'npm'
147+
cache-dependency-path: './modules/ui/package-lock.json'
148+
145149
- name: Install Chromium Browser
146150
run: sudo apt install chromium-browser
151+
147152
- name: Install dependencies
148-
run: npm install && npm ci
153+
run: npm ci
149154
working-directory: ./modules/ui
155+
150156
- name: Run tests
151157
run: |
152158
export CHROME_BIN=/usr/bin/chromium-browser
@@ -161,16 +167,22 @@ jobs:
161167
runs-on: ubuntu-latest
162168
steps:
163169
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
170+
164171
- name: Install Node
165172
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
166173
with:
167-
node-version: 18.19.0
174+
node-version: '20'
175+
cache: 'npm'
176+
cache-dependency-path: './modules/ui/package-lock.json'
177+
168178
- name: Install dependencies
169-
run: npm install && npm ci
179+
run: npm ci
170180
working-directory: ./modules/ui
181+
171182
- name: Run ESLint
172183
run: npm run lint
173184
working-directory: ./modules/ui
185+
174186
- name: Run format check
175187
run: npm run format
176188
working-directory: ./modules/ui

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,14 @@ testing/unit/services/output/
2727
make/DEBIAN/postinst
2828

2929
testrun.log
30+
31+
# Ignore vagrant files
32+
test_vm/.vagrant/
33+
34+
# Ingnore TLS test certificates
35+
test_vm/certs/
36+
test_vm/*.cnf
37+
test_vm/*.csr
38+
test_vm/*.pem
39+
test_vm/*.key
40+
test_vm/*.crt

docs/get_started.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,23 @@ Testrun installs under the `/usr/local/testrun` directory. Testing data is avail
6767
## Start Testrun
6868

6969
Follow these steps to start Testrun:
70-
1. Attach the network interfaces.
71-
- Connect one USB Ethernet adapter to the internet source (e.g., router or switch) using an Ethernet cable.
72-
- Connect the other USB Ethernet adapter directly to the IoT device you want to test using an Ethernet cable.
7370

74-
Notes:
75-
76-
- Disable both adapters in the host system (IPv4, IPv6, and general) by opening **Settings**, then **Network**.
77-
- Keep the DUT powered off until prompted. Otherwise, Testrun will not be able to fully capture the device behavior during startup - resulting in inaccurate test results.
71+
1. Connect the ethernet cables.
72+
73+
This is where you physically plug in the ethernet cables to your hardware: PC, IoT device, Internet connection. You must use two separate network ports on your PC (either two built-in ports, two USB-ethernet adapters, or one of each).
74+
75+
* **Internet Connection**.
76+
* If using a USB adapter: Plug the USB end of the adapter into your PC. Then, connect an ethernet cable to the adapter's ethernet port and plug the other end of the cable into your internet source (like a router or network switch)
77+
* If using a built-in port: Connect an ethernet cable from your PC's built-in port directly to your internet source.
78+
* **IoT Device Connection**. Using a second ethernet cable, plug the other network port (the remaining built-in port or the other USB adapter) directly into the IoT device you intend to test.
79+
- If using a USB Adapter: Plug the USB end of the second adapter into your PC. Then, connect an ethernet cable to the adapter's ethernet port and plug the other end of the cable directly into the IoT device you intend to test.
80+
- If using a built-in port: Connect an ethernet cable from the remaining built-in port directly into the IoT device you intend to test.
81+
82+
Notes:
83+
- **Disable Host Networking**: You must disable both network adapters in the host system. Open Settings, then Network, and turn off IPv4, IPv6, and general network functions for both interfaces.
84+
- **Keep Device Powered Off**: Keep the DUT (Device Under Test) physically connected to your PC using the ethernet cable but powered off until you are prompted to turn it on later in the setup process. If the device starts up early, Testrun won't be able to fully capture its behavior during startup, which will result in inaccurate test results.
7885

79-
1. Start Testrun with the command `sudo testrun`
86+
2. Start Testrun with the command `sudo testrun`
8087
- To run Testrun in network-only mode (without running any tests), use the `--net-only` option.
8188
- To run Testrun with just one interface (connected to the device), use the `--single-intf` option.
8289

framework/python/src/common/util.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Provides basic utilities for Testrun."""
1616
import getpass
1717
import os
18+
import pwd
1819
import subprocess
1920
import shlex
2021
import typing as t
@@ -55,52 +56,54 @@ def interface_exists(interface):
5556
"""Checks whether an interface is available"""
5657
return interface in netifaces.interfaces()
5758

59+
5860
def prettify(mac_string):
5961
"""Formats a MAC address with colons"""
6062
return ':'.join([f'{ord(b):02x}' for b in mac_string])
6163

62-
def get_host_user():
63-
"""Returns the username of the host user"""
64-
user = get_os_user()
6564

66-
# If primary method failed, try secondary
67-
if user is None:
68-
user = get_user()
65+
def get_sudo_user() -> t.Optional[str]:
66+
"""Returns the username of the sudo user if running under sudo, else None."""
67+
return os.environ.get('SUDO_USER')
6968

70-
return user
7169

72-
def get_os_user():
73-
"""Attempts to get the username using os library"""
70+
def get_pwd_user() -> t.Optional[str]:
71+
"""Returns the username of the current user using pwd module"""
7472
user = None
75-
try:
76-
user = os.getlogin()
77-
except OSError:
78-
# Handle the OSError exception
79-
LOGGER.error('An OS error occurred whilst calling os.getlogin()')
80-
except Exception: # pylint: disable=W0703
81-
# Catch any other unexpected exceptions
82-
LOGGER.error('An unknown exception occurred whilst calling os.getlogin()')
73+
user = pwd.getpwuid(os.getuid()).pw_name
8374
return user
8475

85-
def get_user():
86-
"""Attempts to get the host user using the getpass library"""
76+
77+
def get_host_user()-> t.Optional[str]:
78+
"""Returns the username of the host user"""
79+
user_functions = [get_sudo_user, os.getlogin, getpass.getuser, get_pwd_user]
8780
user = None
88-
try:
89-
user = getpass.getuser()
90-
except (KeyError, ImportError, ModuleNotFoundError, OSError) as e:
81+
error_messages = []
82+
for func in user_functions:
83+
try:
84+
user = func()
85+
if user is not None:
86+
break
87+
except (KeyError, ImportError, ModuleNotFoundError, OSError) as e:
9188
# Handle specific exceptions individually
92-
if isinstance(e, KeyError):
93-
LOGGER.error('USER environment variable not set or unavailable.')
94-
elif isinstance(e, ImportError):
95-
LOGGER.error('Unable to import the getpass module.')
96-
elif isinstance(e, ModuleNotFoundError):
97-
LOGGER.error('The getpass module was not found.')
98-
elif isinstance(e, OSError):
99-
LOGGER.error('An OS error occurred while retrieving the username.')
100-
else:
101-
LOGGER.error('An exception occurred:', e)
89+
if isinstance(e, KeyError):
90+
error_message = 'USER environment variable not set or unavailable.'
91+
error_messages.append(error_message)
92+
elif isinstance(e, ImportError):
93+
error_messages.append('Unable to import the getpass module.')
94+
elif isinstance(e, ModuleNotFoundError):
95+
error_messages.append('The getpass module was not found.')
96+
elif isinstance(e, OSError):
97+
error_message = 'OS error occurred while retrieving the username.'
98+
error_messages.append(error_message)
99+
else:
100+
error_messages.append('An exception occurred: ' + str(e))
101+
if user is None:
102+
LOGGER.error(next(error_messages))
103+
102104
return user
103105

106+
104107
def set_file_owner(path, owner):
105108
"""Change the owner of a file"""
106109
run_command(f'chown -R {owner} {path}')

framework/python/src/core/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ def start(self):
175175
self.reset()
176176
self._status = TestrunStatus.STARTING
177177
self.pause_message = False
178+
179+
def start_timer(self):
178180
self._started = datetime.datetime.now()
179181

180182
def get_started(self):

framework/python/src/core/testrun.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,10 @@ def _device_stable(self, mac_addr):
491491

492492
LOGGER.info(f'Device with mac address {mac_addr} is ready for testing.')
493493
self._set_status(TestrunStatus.IN_PROGRESS)
494+
495+
# Start testrun timer
496+
self.get_session().start_timer()
497+
494498
self._test_orc.run_test_modules()
495499

496500
self._stop_network()

framework/python/src/net_orc/ip_control.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,11 @@ def configure_container_interface(self,
264264
return False
265265
return True
266266

267-
def ping_via_gateway(self, host):
267+
def ping_via_gateway(self, host: str) -> bool:
268268
"""Ping the host trough the gateway container"""
269269
command = f'timeout 3 docker exec tr-ct-gateway ping -W 1 -c 1 {host}'
270270
output = util.run_command(command, supress_error=True)
271-
if '0% packet loss' in output[0]:
271+
if re.search(r'\s0% packet loss', output[0]):
272272
return True
273273
return False
274274

framework/python/src/net_orc/network_orchestrator.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import ipaddress
1717
import json
1818
import os
19+
import re
1920
from scapy.all import sniff, wrpcap, BOOTP, AsyncSniffer
2021
from scapy.error import Scapy_Exception
2122
import shutil
@@ -356,8 +357,10 @@ def _ping(self, net_module):
356357
host = net_module.net_config.ipv4_address
357358
namespace = 'tr-ctns-' + net_module.dir_name
358359
cmd = 'ip netns exec ' + namespace + ' ping -c 1 ' + str(host)
359-
success = util.run_command(cmd, output=False)
360-
return success
360+
out, _ = util.run_command(cmd, supress_error=True)
361+
if re.search(r'\s0% packet loss', out):
362+
return True
363+
return False
361364

362365
def _ci_pre_network_create(self):
363366
""" Stores network properties to restore network after

framework/python/src/net_orc/network_validator.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Holds logic for validation of network services prior to runtime."""
1616
import json
1717
import os
18+
import pwd
1819
import shutil
1920
import time
2021
import docker
@@ -179,44 +180,50 @@ def _start_network_device(self, device):
179180

180181
LOGGER.info('Validation device ' + device.name + ' has finished')
181182

182-
def _get_host_user(self):
183-
user = self._get_os_user()
184-
185-
# If primary method failed, try secondary
186-
if user is None:
187-
user = self._get_user()
183+
def _get_sudo_user(self):
184+
"""Returns the username of the sudo user."""
185+
return os.environ.get('SUDO_USER')
188186

189-
LOGGER.debug(f'Network validator host user: {user}')
190-
return user
191-
192-
def _get_os_user(self):
187+
def _get_pwd_user(self):
188+
"""Returns the username of the current user using pwd module"""
193189
user = None
194-
try:
195-
user = os.getlogin()
196-
except OSError:
197-
# Handle the OSError exception
198-
LOGGER.error('An OS error occurred while retrieving the login name.')
199-
except Exception as error: # pylint: disable=W0703
200-
# Catch any other unexpected exceptions
201-
LOGGER.error('An exception occurred:', error)
190+
user = pwd.getpwuid(os.getuid()).pw_name
202191
return user
203192

204-
def _get_user(self):
193+
def _get_host_user(self):
194+
"""Returns the username of the host user"""
195+
user_functions = [
196+
self._get_sudo_user,
197+
os.getlogin,
198+
getpass.getuser,
199+
self._get_pwd_user
200+
]
205201
user = None
206-
try:
207-
user = getpass.getuser()
208-
except (KeyError, ImportError, ModuleNotFoundError, OSError) as e:
202+
error_messages = []
203+
for func in user_functions:
204+
try:
205+
user = func()
206+
if user is not None:
207+
break
208+
except (KeyError, ImportError, ModuleNotFoundError, OSError) as e:
209209
# Handle specific exceptions individually
210-
if isinstance(e, KeyError):
211-
LOGGER.error('USER environment variable not set or unavailable.')
212-
elif isinstance(e, ImportError):
213-
LOGGER.error('Unable to import the getpass module.')
214-
elif isinstance(e, ModuleNotFoundError):
215-
LOGGER.error('The getpass module was not found.')
216-
elif isinstance(e, OSError):
217-
LOGGER.error('An OS error occurred while retrieving the username.')
218-
else:
219-
LOGGER.error('An exception occurred:', e)
210+
if isinstance(e, KeyError):
211+
error_message = 'USER environment variable not set or unavailable.'
212+
error_messages.append(error_message)
213+
elif isinstance(e, ImportError):
214+
error_messages.append('Unable to import the getpass module.')
215+
elif isinstance(e, ModuleNotFoundError):
216+
error_messages.append('The getpass module was not found.')
217+
elif isinstance(e, OSError):
218+
error_message = 'OS error occurred while retrieving the username.'
219+
error_messages.append(error_message)
220+
else:
221+
error_messages.append('An exception occurred: ' + str(e))
222+
if user is None:
223+
LOGGER.error(next(error_messages))
224+
225+
LOGGER.debug(f'Network validator host user: {user}')
226+
220227
return user
221228

222229
def _get_device_status(self, module):

framework/python/src/test_orc/test_orchestrator.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import copy
1616
import os
1717
import json
18+
import pathlib
1819
import re
1920
import time
2021
import shutil
@@ -234,6 +235,8 @@ def _write_reports(self, test_report):
234235

235236
util.run_command(f"chown -R {self._host_user} {out_dir}")
236237

238+
self._cleanup_modules_html_reports(out_dir)
239+
237240
def _generate_report(self):
238241

239242
device = self.get_session().get_target_device()
@@ -271,6 +274,27 @@ def _generate_report(self):
271274

272275
return report
273276

277+
def _cleanup_modules_html_reports(self, out_dir):
278+
"""Cleans up any HTML reports generated by test modules to save space."""
279+
280+
for module in self._test_modules:
281+
module_template_path = os.path.join(
282+
os.path.join(out_dir, module.name),
283+
f"{module.name}_report.j2.html")
284+
module_report_path = os.path.join(
285+
os.path.join(out_dir, module.name),
286+
f"{module.name}_report.jinja2")
287+
try:
288+
if os.path.exists(module_template_path):
289+
os.remove(module_template_path)
290+
LOGGER.debug(f"Removed module template: {module_template_path}")
291+
if os.path.exists(module_report_path):
292+
p = pathlib.Path(module_report_path)
293+
p.rename(p.with_suffix(".html"))
294+
except Exception as e:
295+
LOGGER.error(f"Error {module_template_path}: {e}")
296+
297+
274298
def _cleanup_old_test_results(self, device):
275299

276300
if device.max_device_reports is not None:

0 commit comments

Comments
 (0)