@@ -60,6 +60,7 @@ def __init__(self, test_class: unittest.TestCase,
6060self .archive_compress = init_params .archive_compress
6161self .test_class .output = None
6262self .execution_time = None
63+ self .valgrind_sup_path = init_params .valgrind_sup_path
6364
6465def form_daemon_process (self ,cmdline ,env ):
6566def stream_output (stream :subprocess .PIPE )-> None :
@@ -88,6 +89,7 @@ def stream_output(stream: subprocess.PIPE) -> None:
8889
8990return self .process .pid
9091
92+ # ---- Start run function ---- #
9193def run (self ,command ,gdb = False ,old_binary = False ,return_id = True ,env = None ,
9294skip_log_directory = False ,expect_error = False ,use_backup_dir = True ,daemonize = False ):
9395"""
@@ -98,53 +100,93 @@ def run(self, command, gdb=False, old_binary=False, return_id=True, env=None,
98100 gdb: when True it returns GDBObj(), when tuple('suspend', port) it runs probackup
99101 in suspended gdb mode with attachable gdb port, for local debugging
100102 """
103+ command = self ._add_backup_dir_to_cmd (command ,use_backup_dir )
104+ # Old bin or regular one
105+ binary_path = self ._get_binary_path (old_binary )
106+
107+ if not env :
108+ env = self .test_env
109+ # Add additional options if needed
110+ command ,strcommand = self ._add_options (command ,skip_log_directory )
111+
112+ self .test_class .cmd = f"{ binary_path } { strcommand } "
113+ if self .verbose :
114+ print (self .test_class .cmd )
115+
116+ cmdline = self ._form_cmdline (binary_path ,command )
117+
118+ if gdb is True :
119+ # general test flow for using GDBObj
120+ return GDBobj (cmdline ,self .test_class )
121+
122+ return self ._execute_command (cmdline ,env ,command ,gdb ,expect_error ,return_id ,daemonize )
123+
124+ def _add_backup_dir_to_cmd (self ,command :list ,use_backup_dir :TestBackupDir ):
101125if isinstance (use_backup_dir ,TestBackupDir ):
102- command = [command [0 ],* use_backup_dir .pb_args ,* command [1 :]]
126+ return [command [0 ],* use_backup_dir .pb_args ,* command [1 :]]
103127elif use_backup_dir :
104- command = [command [0 ],* self .backup_dir .pb_args ,* command [1 :]]
128+ return [command [0 ],* self .backup_dir .pb_args ,* command [1 :]]
105129else :
106- command = [command [0 ],* self .backup_dir .pb_args [2 :],* command [1 :]]
107-
108- if not self .probackup_old_path and old_binary :
109- logging .error ('PGPROBACKUPBIN_OLD is not set' )
110- exit (1 )
130+ return [command [0 ],* self .backup_dir .pb_args [2 :],* command [1 :]]
111131
132+ def _get_binary_path (self ,old_binary ):
112133if old_binary :
113- binary_path = self .probackup_old_path
114- else :
115- binary_path = self .probackup_path
116-
117- if not env :
118- env = self .test_env
134+ if not self .probackup_old_path :
135+ logging .error ('PGPROBACKUPBIN_OLD is not set' )
136+ exit (1 )
137+ return self .probackup_old_path
138+ return self .probackup_path
119139
140+ def _add_options (self ,command :list ,skip_log_directory :bool ):
120141strcommand = ' ' .join (str (p )for p in command )
142+
121143if '--log-level-file' in strcommand and \
122144'--log-directory' not in strcommand and \
123145not skip_log_directory :
124146command += ['--log-directory=' + self .pb_log_path ]
125147strcommand += ' ' + command [- 1 ]
126148
127149if 'pglz' in strcommand and \
128- ' -j' not in strcommand and '--thread' not in strcommand :
150+ ' -j' not in strcommand and \
151+ '--thread' not in strcommand :
129152command += ['-j' ,'1' ]
130153strcommand += ' -j 1'
131154
132- self .test_class .cmd = binary_path + ' ' + strcommand
133- if self .verbose :
134- print (self .test_class .cmd )
155+ return command ,strcommand
135156
157+ def _form_cmdline (self ,binary_path ,command ):
136158cmdline = [binary_path ,* command ]
137- if gdb is True :
138- # general test flow for using GDBObj
139- return GDBobj (cmdline ,self .test_class )
140159
160+ if self .valgrind_sup_path and command [0 ]!= "--version" :
161+ os .makedirs (self .pb_log_path ,exist_ok = True )
162+ if self .valgrind_sup_path and not os .path .isfile (self .valgrind_sup_path ):
163+ raise FileNotFoundError (f"PG_PROBACKUP_VALGRIND_SUP should contain path to valgrind suppression file, "
164+ f"but found:{ self .valgrind_sup_path } " )
165+ valgrind_cmd = [
166+ "valgrind" ,
167+ "--gen-suppressions=all" ,
168+ "--leak-check=full" ,
169+ "--show-reachable=yes" ,
170+ "--error-limit=no" ,
171+ "--show-leak-kinds=all" ,
172+ "--errors-for-leak-kinds=all" ,
173+ "--error-exitcode=0" ,
174+ f"--log-file={ os .path .join (self .pb_log_path ,f'valgrind-{ command [0 ]} -%p.log' )} " ,
175+ f"--suppressions={ self .valgrind_sup_path } " ,
176+ "--"
177+ ]
178+ cmdline = valgrind_cmd + cmdline
179+
180+ return cmdline
181+
182+ def _execute_command (self ,cmdline ,env ,command ,gdb ,expect_error ,return_id ,daemonize ):
141183try :
142- if type (gdb )is tuple and gdb [0 ]== 'suspend' :
143- # special test flow for manually debug probackup
184+ if isinstance (gdb ,tuple )and gdb [0 ]== 'suspend' :
144185gdb_port = gdb [1 ]
145186cmdline = ['gdbserver' ]+ ['localhost:' + str (gdb_port )]+ cmdline
146187logging .warning ("pg_probackup gdb suspended, waiting gdb connection on localhost:{0}" .format (gdb_port ))
147188
189+ # Execute command
148190start_time = time .time ()
149191if daemonize :
150192return self .form_daemon_process (cmdline ,env )
@@ -174,6 +216,7 @@ def run(self, command, gdb=False, old_binary=False, return_id=True, env=None,
174216return self .test_class .output
175217else :
176218raise ProbackupException (self .test_class .output ,self .test_class .cmd )
219+ # ---- End run function ---- #
177220
178221def get_backup_id (self ):
179222if init_params .major_version > 2 :