diff --git a/projects/rocprofiler-compute/tests/test_profile_general.py b/projects/rocprofiler-compute/tests/test_profile_general.py index 69be33a897..7c46ac50de 100644 --- a/projects/rocprofiler-compute/tests/test_profile_general.py +++ b/projects/rocprofiler-compute/tests/test_profile_general.py @@ -1032,49 +1032,59 @@ def test_roofline_unsupported_datatype_error(binary_handler_profile_rocprof_comp @pytest.mark.roofline_2 -def test_roof_plot_modes(binary_handler_profile_rocprof_compute): +@pytest.mark.parametrize( + "options,expected_files,test_id", + [ + ( + ["--device", "0", "--roof-only", "--roofline-data-type", "FP32"], + ["empirRoof_gpu-0_FP32.pdf"], + "FP32_datatype", + ), + ( + ["--device", "0", "--roof-only", "--roofline-data-type", "FP16"], + ["empirRoof_gpu-0_FP16.pdf"], + "FP16_datatype", + ), + ( + ["--device", "0", "--roof-only", "--kernel", "KERNEL_NAME_PLACEHOLDER"], + ["EXPECTED_FILE_PLACEHOLDER"], + "kernel_filter", + ), + ], + ids=["FP32_datatype", "FP16_datatype", "kernel_filter"], +) +def test_roof_plot_modes( + binary_handler_profile_rocprof_compute, options, expected_files, test_id +): if soc in ("MI100"): - assert True + pytest.skip("Skipping roofline test for MI100") return + # Handle dynamic kernel name substitution for the kernel_filter test case + options = [ + config["kernel_name_1"] if opt == "KERNEL_NAME_PLACEHOLDER" else opt + for opt in options + ] # Test `--kernel` filtering outputs are present and labelled correctly filter_empirRoof = "empirRoof_gpu-0_" + config["kernel_name_1"] - - plot_configurations = [ - { - "options": ["--device", "0", "--roof-only", "--roofline-data-type", "FP32"], - "expected_files": ["empirRoof_gpu-0_FP32.pdf"], - }, - { - "options": ["--device", "0", "--roof-only", "--roofline-data-type", "FP16"], - "expected_files": ["empirRoof_gpu-0_FP16.pdf"], - }, - { - "options": [ - "--device", - "0", - "--roof-only", - "--kernel", - config["kernel_name_1"], - ], - "expected_files": [filter_empirRoof], - }, + expected_files = [ + filter_empirRoof if f == "EXPECTED_FILE_PLACEHOLDER" else f + for f in expected_files ] - for config_test in plot_configurations: - workload_dir = test_utils.get_output_dir() + workload_dir = test_utils.get_output_dir(param_id=test_id) - returncode = binary_handler_profile_rocprof_compute( - config, workload_dir, config_test["options"], check_success=False, roof=True - ) - assert returncode == 0 + returncode = binary_handler_profile_rocprof_compute( + config, workload_dir, options, check_success=False, roof=True + ) + assert returncode == 0 - for expected_file in config_test["expected_files"]: - expected_path = os.path.join(workload_dir, expected_file) - if os.path.exists(expected_path): - assert os.path.getsize(expected_path) > 0 + for expected_file in expected_files: + expected_path = os.path.join(workload_dir, expected_file) + if os.path.exists(expected_path): + assert os.path.getsize(expected_path) > 0 - test_utils.clean_output_dir(config["cleanup"], workload_dir) + test_utils.clean_output_dir(config["cleanup"], workload_dir) @pytest.mark.roofline_2 diff --git a/projects/rocprofiler-compute/tests/test_utils.py b/projects/rocprofiler-compute/tests/test_utils.py index 9b82490d25..7358fbbe3f 100644 --- a/projects/rocprofiler-compute/tests/test_utils.py +++ b/projects/rocprofiler-compute/tests/test_utils.py @@ -80,36 +80,65 @@ def check_file_pattern(pattern, file_path): return len(re.findall(pattern, content)) != 0 -def get_output_dir(suffix="_output", clean_existing=True): +def get_output_dir(suffix="_output", clean_existing=True, param_id=None): """ Provides a unique output directory based on the name of the calling test function - with a suffix applied. + with a suffix applied. For parametrized tests, pass param_id to ensure unique + directory names and avoid NFS conflicts. Args: suffix (str, optional): suffix to append to output_dir. Defaults to "_output". clean_existing (bool, optional): Whether to remove existing directory if exists. Defaults to True. + param_id (str, optional): Unique identifier for parametrized tests. + When provided, appended to the directory name to ensure uniqueness. + Defaults to None. """ - output_dir = inspect.stack()[1].function + suffix + func_name = inspect.stack()[1].function + + param_suffix = "" + if param_id: + param_suffix = "_" + re.sub(r"[^\w\-]", "_", str(param_id)) + + output_dir = func_name + param_suffix + suffix if clean_existing: if Path(output_dir).exists(): shutil.rmtree(output_dir) return output_dir -def setup_workload_dir(input_dir, suffix="_tmp", clean_existing=True): - """Provides a unique input workoad directory with contents of input_dir - based on the name of the calling test function. +def setup_workload_dir(input_dir, suffix="_tmp", clean_existing=True, param_id=None): + """Provides a unique input workload directory with contents of input_dir + based on the name of the calling test function. For parametrized tests, + pass param_id to ensure unique directory names and avoid NFS conflicts. Setup is a NOOP when tests run serially. + + Args: + input_dir (str): Source directory to copy from. + suffix (str, optional): suffix to append to output_dir. + Defaults to "_tmp". + clean_existing (bool, optional): Whether to remove existing directory if exists. + Defaults to True. + param_id (str, optional): Unique identifier for parametrized tests. + When provided, appended to the directory name to ensure uniqueness. + Defaults to None. """ if "PYTEST_XDIST_WORKER_COUNT" not in os.environ: return input_dir - output_dir = inspect.stack()[1].function + suffix + func_name = inspect.stack()[1].function + + # Include param_id in directory name if provided + param_suffix = "" + if param_id: + # Sanitize param_id: replace special chars that may not be valid in paths + param_suffix = "_" + re.sub(r"[^\w\-]", "_", str(param_id)) + + output_dir = func_name + param_suffix + suffix if clean_existing: if Path(output_dir).exists(): shutil.rmtree(output_dir)