Skip to content

Commit 2d8a6d2

Browse files
hakehuangcfriedt
authored andcommitted
tests: display: support headless mode
for test machine without display, need support open-cv-headless mode. 1. support headless mode 2. add parser for environment path Signed-off-by: Hake Huang <[email protected]>
1 parent 1d6da40 commit 2d8a6d2

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

scripts/pylib/display-twister-harness/camera_shield/main.py

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ def resolve_env_vars(yaml_dict):
2424
"""Process yaml with Template strings for safer environment variable resolution."""
2525
if isinstance(yaml_dict, dict):
2626
return {k: resolve_env_vars(v) for k, v in yaml_dict.items()}
27-
elif isinstance(yaml_dict, list):
27+
if isinstance(yaml_dict, list):
2828
return [resolve_env_vars(i) for i in yaml_dict]
29-
elif isinstance(yaml_dict, str):
29+
if isinstance(yaml_dict, str):
3030
# Create a template and substitute environment variables
3131
template = Template(yaml_dict)
3232
return template.safe_substitute(os.environ)
33-
else:
34-
return yaml_dict
33+
34+
return yaml_dict
3535

3636
self.active_plugins = {} # Initialize empty plugin dictionary
3737
with open(config_path, encoding="utf-8-sig") as f:
@@ -93,15 +93,48 @@ def run(self):
9393
continue
9494

9595
# Maintain OpenCV event loop
96-
if cv2.waitKey(1) == 27: # ESC key
97-
break
96+
try:
97+
if cv2.waitKey(1) == 27: # ESC key
98+
break
99+
except Exception as e:
100+
error_msg = str(e).lower()
101+
if any(
102+
phrase in error_msg
103+
for phrase in [
104+
"not implemented",
105+
"rebuild the library",
106+
"gtk",
107+
"cocoa support",
108+
]
109+
):
110+
# Expected error in headless environment - continue
111+
pass
112+
else:
113+
print(f"Error during waitKey: {e}")
98114

99115
results = {}
100116
for name, plugin in self.active_plugins.items():
101117
results[name] = plugin.process_frame(frame)
102118

103119
self.handle_results(results, frame)
104-
self.camera.show_frame(frame)
120+
try:
121+
self.camera.show_frame(frame)
122+
except Exception as e:
123+
error_msg = str(e).lower()
124+
if any(
125+
phrase in error_msg
126+
for phrase in [
127+
"not implemented",
128+
"rebuild the library",
129+
"gtk",
130+
"cocoa support",
131+
]
132+
):
133+
# Expected error in headless environment - continue
134+
pass
135+
else:
136+
print(f"Error during show_frame: {e}")
137+
105138
frame_delay = 1 / self.case_config["fps"]
106139
if time.time() - start_time > self.case_config["run_time"]:
107140
break

scripts/pylib/display-twister-harness/camera_shield/uvc_core/camera_controller.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def show_analysis(self, analysis, frame=None):
168168
print(f" Color Balance - B Channel: {analysis['color_balance']['b_channel']:.2f}")
169169
print(f" Sharpness: {analysis['sharpness']:.2f}")
170170

171-
# 在帧上显示分析结果
171+
# show report
172172
if frame is not None:
173173
text_y = 30
174174
cv2.putText(
@@ -269,4 +269,13 @@ def print_settings(self):
269269

270270
def release(self):
271271
self.cap.release()
272-
cv2.destroyAllWindows()
272+
try:
273+
# compatible with openCV-headless mode
274+
cv2.destroyAllWindows()
275+
except Exception as e:
276+
# Handle cv2.error and other potential exceptions
277+
if "not implemented" in str(e) or "Rebuild the library" in str(e):
278+
# This is expected in headless/no-GUI environments
279+
pass
280+
else:
281+
print(f"Warning: Could not destroy windows: {e}")

scripts/pylib/twister/twisterlib/harness.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import xml.etree.ElementTree as ET
1616
from collections import OrderedDict
1717
from enum import Enum
18+
from string import Template
1819

1920
import junitparser.junitparser as junit
2021
import yaml
@@ -647,7 +648,9 @@ def generate_command(self):
647648

648649
def _get_display_config_file(self, harness_config):
649650
if test_config_file := harness_config.get('display_capture_config'):
650-
test_config_path = os.path.join(self.source_dir, test_config_file)
651+
_template = Template(test_config_file)
652+
_config_file = _template.safe_substitute(os.environ)
653+
test_config_path = os.path.join(self.source_dir, _config_file)
651654
logger.info(f'test_config_path = {test_config_path}')
652655
if os.path.exists(test_config_path):
653656
return test_config_path

0 commit comments

Comments
 (0)