11#!/usr/bin/env python3
2+ """
3+ Test runner script for a PPC project.
24
5+ This script provides functionality to run tests in different modes:
6+ - threads: for multithreading tests
7+ - processes: for multiprocessing tests
8+ - processes_coverage: for multiprocessing tests with coverage collection
9+ - performance: for performance testing
10+ """
11+
12+ import argparse
313import os
414import shlex
515import subprocess
818
919
1020def init_cmd_args ():
11- import argparse
21+ """Initialize and parse command line arguments."""
1222 parser = argparse .ArgumentParser ()
1323 parser .add_argument (
1424 "--running-type" ,
1525 required = True ,
16- choices = ["threads" , "processes" , "performance" ],
17- help = "Specify the execution mode. Choose 'threads' for multithreading or 'processes' for multiprocessing."
26+ choices = ["threads" , "processes" , "processes_coverage" , "performance" ],
27+ help = "Specify the execution mode. Choose 'threads' for multithreading, "
28+ "'processes' for multiprocessing, 'processes_coverage' for multiprocessing "
29+ "with coverage, or 'performance' for performance testing."
1830 )
1931 parser .add_argument (
2032 "--additional-mpi-args" ,
@@ -34,6 +46,8 @@ def init_cmd_args():
3446
3547
3648class PPCRunner :
49+ """Runner class for PPC test execution in different modes."""
50+
3751 def __init__ (self ):
3852 self .__ppc_num_threads = None
3953 self .__ppc_num_proc = None
@@ -54,6 +68,7 @@ def __get_project_path():
5468 return script_dir .parent
5569
5670 def setup_env (self , ppc_env ):
71+ """Setup environment variables and working directory."""
5772 self .__ppc_env = ppc_env
5873
5974 self .__ppc_num_threads = self .__ppc_env .get ("PPC_NUM_THREADS" )
@@ -71,9 +86,9 @@ def setup_env(self, ppc_env):
7186 self .work_dir = Path (self .__get_project_path ()) / "build" / "bin"
7287
7388 def __run_exec (self , command ):
74- result = subprocess .run (command , shell = False , env = self .__ppc_env )
89+ result = subprocess .run (command , shell = False , env = self .__ppc_env , check = False )
7590 if result .returncode != 0 :
76- raise Exception ( f"Subprocess return { result .returncode } ." )
91+ raise subprocess . CalledProcessError ( result .returncode , command )
7792
7893 @staticmethod
7994 def __get_gtest_settings (repeats_count , type_task ):
@@ -87,6 +102,7 @@ def __get_gtest_settings(repeats_count, type_task):
87102 return command
88103
89104 def run_threads (self ):
105+ """Run tests in threading mode."""
90106 if platform .system () == "Linux" and not self .__ppc_env .get ("PPC_ASAN_RUN" ):
91107 for task_type in ["seq" , "stl" ]:
92108 self .__run_exec (
@@ -97,10 +113,12 @@ def run_threads(self):
97113
98114 for task_type in ["omp" , "seq" , "stl" , "tbb" ]:
99115 self .__run_exec (
100- [str (self .work_dir / 'ppc_func_tests' )] + self .__get_gtest_settings (3 , '_' + task_type + '_' )
116+ [str (self .work_dir / 'ppc_func_tests' )] +
117+ self .__get_gtest_settings (3 , '_' + task_type + '_' )
101118 )
102119
103120 def run_core (self ):
121+ """Run core functionality tests."""
104122 if platform .system () == "Linux" and not self .__ppc_env .get ("PPC_ASAN_RUN" ):
105123 self .__run_exec (
106124 shlex .split (self .valgrind_cmd )
@@ -114,6 +132,7 @@ def run_core(self):
114132 )
115133
116134 def run_processes (self , additional_mpi_args ):
135+ """Run tests in multiprocessing mode."""
117136 ppc_num_proc = self .__ppc_env .get ("PPC_NUM_PROC" )
118137 if ppc_num_proc is None :
119138 raise EnvironmentError ("Required environment variable 'PPC_NUM_PROC' is not set." )
@@ -127,7 +146,63 @@ def run_processes(self, additional_mpi_args):
127146 + self .__get_gtest_settings (10 , '_' + task_type )
128147 )
129148
149+ def run_processes_coverage (self , additional_mpi_args ):
150+ """Run tests in multiprocessing mode with a coverage collection."""
151+ ppc_num_proc = self .__ppc_env .get ("PPC_NUM_PROC" )
152+ if ppc_num_proc is None :
153+ raise EnvironmentError ("Required environment variable 'PPC_NUM_PROC' is not set." )
154+
155+ mpi_running = [self .mpi_exec ] + shlex .split (additional_mpi_args ) + ["-np" , ppc_num_proc ]
156+
157+ # Set up coverage environment for MPI processes
158+ if not self .__ppc_env .get ("PPC_ASAN_RUN" ):
159+ # Enable coverage data collection for each MPI process
160+ self .__ppc_env ["GCOV_PREFIX_STRIP" ] = "0"
161+ # Use MPI rank to create unique coverage directories for each process
162+ gcov_base_dir = Path (self .__get_project_path ()) / "build" / "gcov_data"
163+ gcov_base_dir .mkdir (parents = True , exist_ok = True )
164+
165+ # Set GCOV_PREFIX to include MPI rank - this creates separate directories
166+ # for each MPI process at runtime
167+ self .__ppc_env ["GCOV_PREFIX" ] = str (
168+ gcov_base_dir / "rank_${PMI_RANK:-${OMPI_COMM_WORLD_RANK:-${SLURM_PROCID:-0}}}"
169+ )
170+
171+ # Create a wrapper script to set a unique prefix per process
172+ wrapper_script = Path (self .__get_project_path ()) / "build" / "mpi_coverage_wrapper.sh"
173+ wrapper_content = f"""#!/bin/bash
174+ # Get MPI rank from environment variables
175+ if [ -n "$PMIX_RANK" ]; then
176+ RANK=$PMIX_RANK
177+ elif [ -n "$PMI_RANK" ]; then
178+ RANK=$PMI_RANK
179+ elif [ -n "$OMPI_COMM_WORLD_RANK" ]; then
180+ RANK=$OMPI_COMM_WORLD_RANK
181+ elif [ -n "$SLURM_PROCID" ]; then
182+ RANK=$SLURM_PROCID
183+ else
184+ RANK=0
185+ fi
186+
187+ export GCOV_PREFIX="{ gcov_base_dir } /rank_$RANK"
188+ mkdir -p "$GCOV_PREFIX"
189+ exec "$@"
190+ """
191+ wrapper_script .write_text (wrapper_content )
192+ wrapper_script .chmod (0o755 )
193+
194+ # Run tests with a coverage wrapper
195+ for task_type in ["all" , "mpi" ]:
196+ test_command = (
197+ mpi_running
198+ + [str (wrapper_script )]
199+ + [str (self .work_dir / 'ppc_func_tests' )]
200+ + self .__get_gtest_settings (10 , '_' + task_type )
201+ )
202+ self .__run_exec (test_command )
203+
130204 def run_performance (self ):
205+ """Run performance tests."""
131206 if not self .__ppc_env .get ("PPC_ASAN_RUN" ):
132207 mpi_running = [self .mpi_exec , "-np" , self .__ppc_num_proc ]
133208 for task_type in ["all" , "mpi" ]:
@@ -139,25 +214,29 @@ def run_performance(self):
139214
140215 for task_type in ["omp" , "seq" , "stl" , "tbb" ]:
141216 self .__run_exec (
142- [str (self .work_dir / 'ppc_perf_tests' )] + self .__get_gtest_settings (1 , '_' + task_type )
217+ [str (self .work_dir / 'ppc_perf_tests' )] +
218+ self .__get_gtest_settings (1 , '_' + task_type )
143219 )
144220
145221
146- def _execute (args_dict , env ):
222+ def _execute (args_dict_ , env ):
223+ """Execute tests based on the provided arguments."""
147224 runner = PPCRunner ()
148225 runner .setup_env (env )
149226
150- if args_dict ["running_type" ] in ["threads" , "processes" ]:
227+ if args_dict_ ["running_type" ] in ["threads" , "processes" , "processes_coverage " ]:
151228 runner .run_core ()
152229
153- if args_dict ["running_type" ] == "threads" :
230+ if args_dict_ ["running_type" ] == "threads" :
154231 runner .run_threads ()
155- elif args_dict ["running_type" ] == "processes" :
156- runner .run_processes (args_dict ["additional_mpi_args" ])
157- elif args_dict ["running_type" ] == "performance" :
232+ elif args_dict_ ["running_type" ] == "processes" :
233+ runner .run_processes (args_dict_ ["additional_mpi_args" ])
234+ elif args_dict_ ["running_type" ] == "processes_coverage" :
235+ runner .run_processes_coverage (args_dict_ ["additional_mpi_args" ])
236+ elif args_dict_ ["running_type" ] == "performance" :
158237 runner .run_performance ()
159238 else :
160- raise Exception ( " running-type is wrong! " )
239+ raise ValueError ( f"Invalid running-type: { args_dict_ [ 'running_type' ] } " )
161240
162241
163242if __name__ == "__main__" :
@@ -171,7 +250,7 @@ def _execute(args_dict, env):
171250 if args_dict ["running_type" ] == "threads" :
172251 env_copy ["PPC_NUM_THREADS" ] = str (count )
173252 env_copy .setdefault ("PPC_NUM_PROC" , "1" )
174- elif args_dict ["running_type" ] == "processes" :
253+ elif args_dict ["running_type" ] in [ "processes" , "processes_coverage" ] :
175254 env_copy ["PPC_NUM_PROC" ] = str (count )
176255 env_copy .setdefault ("PPC_NUM_THREADS" , "1" )
177256
0 commit comments