Skip to content

Commit be79db9

Browse files
committed
adding functional tests for build_from_model functionality
1 parent 26b689e commit be79db9

File tree

4 files changed

+554
-2
lines changed

4 files changed

+554
-2
lines changed

nodescraper/plugins/inband/os/analyzer_args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ def build_from_model(cls, datamodel: OsDataModel) -> "OsAnalyzerArgs":
6161
Returns:
6262
OsAnalyzerArgs: instance of analyzer args class
6363
"""
64-
return cls(exp_os=datamodel.os_name)
64+
return cls(exp_os=datamodel.os_name, exact_match=True)

nodescraper/plugins/inband/rocm/analyzer_args.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,6 @@ def build_from_model(cls, datamodel: RocmDataModel) -> "RocmAnalyzerArgs":
6161
Returns:
6262
RocmAnalyzerArgs: instance of analyzer args class
6363
"""
64-
return cls(exp_rocm=datamodel.rocm_version)
64+
return cls(
65+
exp_rocm=datamodel.rocm_version, exp_rocm_latest=datamodel.rocm_latest_versioned_path
66+
)
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
###############################################################################
2+
#
3+
# MIT License
4+
#
5+
# Copyright (c) 2025 Advanced Micro Devices, Inc.
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
#
25+
###############################################################################
26+
"""
27+
Functional tests for reference config generation and usage workflow.
28+
29+
Tests the complete workflow:
30+
1. Generate reference config from system using --gen-reference-config
31+
2. Use the generated config with --plugin-configs
32+
"""
33+
import json
34+
from pathlib import Path
35+
36+
import pytest
37+
38+
from nodescraper.pluginregistry import PluginRegistry
39+
40+
41+
def find_reference_config(log_path):
42+
"""Find reference_config.json in timestamped log directory.
43+
44+
Args:
45+
log_path: Base log path where logs are stored
46+
47+
Returns:
48+
Path to reference_config.json or None if not found
49+
"""
50+
log_path = Path(log_path)
51+
if not log_path.exists():
52+
return None
53+
54+
log_dirs = list(log_path.glob("scraper_logs_*"))
55+
if not log_dirs:
56+
return None
57+
58+
most_recent = max(log_dirs, key=lambda p: p.stat().st_mtime)
59+
60+
reference_config = most_recent / "reference_config.json"
61+
if reference_config.exists():
62+
return reference_config
63+
64+
return None
65+
66+
67+
@pytest.fixture(scope="module")
68+
def all_plugin_names():
69+
"""Get list of all available plugin names."""
70+
registry = PluginRegistry()
71+
return sorted(registry.plugins.keys())
72+
73+
74+
def test_gen_reference_config_all_plugins(run_cli_command, tmp_path, all_plugin_names):
75+
"""Test generating reference config with all plugins via run-plugins subcommand.
76+
77+
Note: When running all plugins, some may fail but as long as at least one succeeds,
78+
the reference config should be generated.
79+
"""
80+
log_path = str(tmp_path / "logs_gen_ref_all")
81+
82+
result = run_cli_command(
83+
[
84+
"--log-path",
85+
log_path,
86+
"--gen-reference-config",
87+
"run-plugins",
88+
]
89+
+ all_plugin_names,
90+
check=False,
91+
)
92+
93+
assert result.returncode in [0, 1, 2, 120], (
94+
f"Unexpected return code: {result.returncode}\n"
95+
f"stdout: {result.stdout[:500]}\nstderr: {result.stderr[:500]}"
96+
)
97+
98+
reference_config_path = find_reference_config(log_path)
99+
100+
if reference_config_path is None:
101+
pytest.skip(
102+
"reference_config.json was not created - likely all plugins failed or timed out. "
103+
"This can happen in test environments."
104+
)
105+
106+
assert reference_config_path.exists()
107+
108+
with open(reference_config_path) as f:
109+
config = json.load(f)
110+
assert "plugins" in config
111+
assert isinstance(config["plugins"], dict)
112+
assert len(config["plugins"]) > 0
113+
114+
115+
def test_gen_reference_config_subset_plugins(run_cli_command, tmp_path):
116+
"""Test generating reference config with a subset of plugins."""
117+
log_path = str(tmp_path / "logs_gen_ref_subset")
118+
plugins = ["BiosPlugin", "OsPlugin", "KernelPlugin"]
119+
120+
result = run_cli_command(
121+
["--log-path", log_path, "--gen-reference-config", "run-plugins"] + plugins,
122+
check=False,
123+
)
124+
125+
assert result.returncode in [0, 1, 2]
126+
127+
reference_config_path = find_reference_config(log_path)
128+
assert reference_config_path is not None, "reference_config.json was not created"
129+
assert reference_config_path.exists()
130+
131+
with open(reference_config_path) as f:
132+
config = json.load(f)
133+
assert "plugins" in config
134+
135+
136+
def test_use_generated_reference_config(run_cli_command, tmp_path):
137+
"""Test using a generated reference config with --plugin-configs."""
138+
gen_log_path = str(tmp_path / "logs_gen")
139+
use_log_path = str(tmp_path / "logs_use")
140+
141+
plugins = ["BiosPlugin", "OsPlugin", "UptimePlugin"]
142+
143+
gen_result = run_cli_command(
144+
["--log-path", gen_log_path, "--gen-reference-config", "run-plugins"] + plugins,
145+
check=False,
146+
)
147+
148+
assert gen_result.returncode in [0, 1, 2]
149+
150+
reference_config_path = find_reference_config(gen_log_path)
151+
assert reference_config_path is not None, "reference_config.json was not created"
152+
assert reference_config_path.exists()
153+
154+
use_result = run_cli_command(
155+
["--log-path", use_log_path, "--plugin-configs", str(reference_config_path)],
156+
check=False,
157+
)
158+
159+
assert use_result.returncode in [0, 1, 2]
160+
output = use_result.stdout + use_result.stderr
161+
assert len(output) > 0
162+
163+
164+
def test_full_workflow_all_plugins(run_cli_command, tmp_path, all_plugin_names):
165+
"""
166+
Test complete workflow: generate reference config from all plugins,
167+
then use it with --plugin-configs.
168+
169+
Note: May skip if plugins fail to generate config in test environment.
170+
"""
171+
gen_log_path = str(tmp_path / "logs_gen_workflow")
172+
use_log_path = str(tmp_path / "logs_use_workflow")
173+
174+
gen_result = run_cli_command(
175+
[
176+
"--log-path",
177+
gen_log_path,
178+
"--gen-reference-config",
179+
"run-plugins",
180+
]
181+
+ all_plugin_names,
182+
check=False,
183+
)
184+
185+
assert gen_result.returncode in [0, 1, 2, 120], (
186+
f"Generation failed with return code {gen_result.returncode}\n"
187+
f"stdout: {gen_result.stdout[:500]}\n"
188+
f"stderr: {gen_result.stderr[:500]}"
189+
)
190+
191+
reference_config_path = find_reference_config(gen_log_path)
192+
193+
if reference_config_path is None:
194+
pytest.skip(
195+
"reference_config.json was not generated - plugins may have failed in test environment"
196+
)
197+
198+
assert reference_config_path.exists()
199+
200+
with open(reference_config_path) as f:
201+
config = json.load(f)
202+
assert "plugins" in config, "Config missing 'plugins' key"
203+
204+
for _plugin_name, plugin_config in config["plugins"].items():
205+
if "analysis_args" in plugin_config:
206+
assert isinstance(plugin_config["analysis_args"], dict)
207+
208+
use_result = run_cli_command(
209+
["--log-path", use_log_path, "--plugin-configs", str(reference_config_path)],
210+
check=False,
211+
)
212+
213+
assert use_result.returncode in [0, 1, 2], (
214+
f"Using config failed with return code {use_result.returncode}\n"
215+
f"stdout: {use_result.stdout}\n"
216+
f"stderr: {use_result.stderr}"
217+
)
218+
219+
output = use_result.stdout + use_result.stderr
220+
assert len(output) > 0, "No output generated when using reference config"
221+
222+
use_log_dirs = list(Path(tmp_path).glob("logs_use_workflow*"))
223+
assert len(use_log_dirs) > 0, "No log directory created when using config"
224+
225+
226+
def test_reference_config_with_analysis_args(run_cli_command, tmp_path):
227+
"""Test that generated reference config includes analysis_args where available."""
228+
log_path = str(tmp_path / "logs_analysis_args")
229+
230+
plugins_with_build_from_model = [
231+
"BiosPlugin",
232+
"CmdlinePlugin",
233+
"DeviceEnumerationPlugin",
234+
"DkmsPlugin",
235+
"KernelPlugin",
236+
"KernelModulePlugin",
237+
"OsPlugin",
238+
"PackagePlugin",
239+
"ProcessPlugin",
240+
"RocmPlugin",
241+
"SysctlPlugin",
242+
]
243+
244+
result = run_cli_command(
245+
["--log-path", log_path, "--gen-reference-config", "run-plugins"]
246+
+ plugins_with_build_from_model,
247+
check=False,
248+
)
249+
250+
assert result.returncode in [0, 1, 2, 120]
251+
252+
reference_config_path = find_reference_config(log_path)
253+
254+
if reference_config_path is None:
255+
pytest.skip(
256+
"reference_config.json was not created - plugins may have failed in test environment"
257+
)
258+
259+
assert reference_config_path.exists()
260+
261+
with open(reference_config_path) as f:
262+
config = json.load(f)
263+
plugins_with_args = [
264+
name for name, conf in config["plugins"].items() if "analysis_args" in conf
265+
]
266+
assert len(plugins_with_args) > 0, "No plugins have analysis_args in generated config"
267+
268+
269+
def test_reference_config_structure(run_cli_command, tmp_path):
270+
"""Test that generated reference config has correct structure."""
271+
log_path = str(tmp_path / "logs_structure")
272+
273+
result = run_cli_command(
274+
["--log-path", log_path, "--gen-reference-config", "run-plugins", "OsPlugin"],
275+
check=False,
276+
)
277+
278+
assert result.returncode in [0, 1, 2]
279+
280+
reference_config_path = find_reference_config(log_path)
281+
assert reference_config_path is not None, "reference_config.json was not created"
282+
assert reference_config_path.exists()
283+
284+
with open(reference_config_path) as f:
285+
config = json.load(f)
286+
287+
assert "plugins" in config
288+
assert isinstance(config["plugins"], dict)
289+
290+
if "OsPlugin" in config["plugins"]:
291+
os_config = config["plugins"]["OsPlugin"]
292+
if "analysis_args" in os_config:
293+
assert "exp_os" in os_config["analysis_args"]
294+
295+
296+
def test_gen_reference_config_without_run_plugins(run_cli_command, tmp_path):
297+
"""Test generating reference config without specifying plugins (uses default)."""
298+
log_path = str(tmp_path / "logs_default")
299+
300+
result = run_cli_command(
301+
["--log-path", log_path, "--gen-reference-config"],
302+
check=False,
303+
)
304+
305+
assert result.returncode in [0, 1, 2]
306+
307+
reference_config_path = find_reference_config(log_path)
308+
assert reference_config_path is not None, "reference_config.json was not created"
309+
assert reference_config_path.exists()
310+
311+
with open(reference_config_path) as f:
312+
config = json.load(f)
313+
assert "plugins" in config
314+
315+
316+
def test_reference_config_json_valid(run_cli_command, tmp_path):
317+
"""Test that generated reference config is valid JSON."""
318+
log_path = str(tmp_path / "logs_valid_json")
319+
320+
result = run_cli_command(
321+
[
322+
"--log-path",
323+
log_path,
324+
"--gen-reference-config",
325+
"run-plugins",
326+
"BiosPlugin",
327+
"OsPlugin",
328+
],
329+
check=False,
330+
)
331+
332+
assert result.returncode in [0, 1, 2]
333+
334+
reference_config_path = find_reference_config(log_path)
335+
assert reference_config_path is not None, "reference_config.json was not created"
336+
assert reference_config_path.exists()
337+
338+
with open(reference_config_path) as f:
339+
config = json.load(f)
340+
json_str = json.dumps(config, indent=2)
341+
assert len(json_str) > 0
342+
343+
reparsed = json.loads(json_str)
344+
assert reparsed == config

0 commit comments

Comments
 (0)