Skip to content

Commit 0de4ec0

Browse files
committed
tests: PGPRO-1230 ptrack race condition tests
1 parent f97fab6 commit 0de4ec0

File tree

3 files changed

+378
-41
lines changed

3 files changed

+378
-41
lines changed

tests/Readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Check archive compression:
1414
export ARCHIVE_COMPRESSION=ON
1515
1616
Usage:
17-
pip install testgres=0.4.0
17+
pip install testgres==0.4.0
1818
export PG_CONFIG=/path/to/pg_config
1919
python -m unittest [-v] tests[.specific_module][.class.test]
2020
```

tests/helpers/ptrack_helpers.py

Lines changed: 128 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
import hashlib
99
import re
1010
import pwd
11+
import select
12+
import psycopg2
13+
from time import sleep
1114

1215

1316
idx_ptrack = {
@@ -946,6 +949,34 @@ def compare_pgdata(self, original_pgdata, restored_pgdata):
946949
fail = True
947950
self.assertFalse(fail, error_message)
948951

952+
def get_asyc_connect(self, database=None, host=None, port=5432):
953+
if not database:
954+
database = 'postgres'
955+
if not host:
956+
host = '127.0.0.1'
957+
958+
return psycopg2.connect(
959+
database="postgres",
960+
host='127.0.0.1',
961+
port=port,
962+
async=True
963+
)
964+
965+
def wait(self, connection):
966+
while True:
967+
state = connection.poll()
968+
if state == psycopg2.extensions.POLL_OK:
969+
break
970+
elif state == psycopg2.extensions.POLL_WRITE:
971+
select.select([], [connection.fileno()], [])
972+
elif state == psycopg2.extensions.POLL_READ:
973+
select.select([connection.fileno()], [], [])
974+
else:
975+
raise psycopg2.OperationalError("poll() returned %s" % state)
976+
977+
def gdb_attach(self, pid):
978+
return GDBobj([str(pid)], self.verbose, attach=True)
979+
949980

950981
class GdbException(Exception):
951982
def __init__(self, message=False):
@@ -956,7 +987,7 @@ def __str__(self):
956987

957988

958989
class GDBobj(ProbackupTest):
959-
def __init__(self, cmd, verbose):
990+
def __init__(self, cmd, verbose, attach=False):
960991
self.verbose = verbose
961992

962993
# Check gdb presense
@@ -972,84 +1003,159 @@ def __init__(self, cmd, verbose):
9721003
'gdb',
9731004
'--interpreter',
9741005
'mi2',
975-
'--args'
976-
] + cmd
1006+
]
1007+
1008+
if attach:
1009+
self.cmd = self.base_cmd + ['--pid'] + cmd
1010+
else:
1011+
self.cmd = self.base_cmd + ['--args'] + cmd
9771012

9781013
# Get version
9791014
gdb_version_number = re.search(
9801015
b"^GNU gdb [^\d]*(\d+)\.(\d)",
9811016
gdb_version)
9821017
self.major_version = int(gdb_version_number.group(1))
9831018
self.minor_version = int(gdb_version_number.group(2))
1019+
9841020
if self.verbose:
985-
print([' '.join(map(str, self.base_cmd))])
1021+
print([' '.join(map(str, self.cmd))])
1022+
1023+
print(self.cmd)
9861024

9871025
self.proc = subprocess.Popen(
988-
self.base_cmd,
1026+
self.cmd,
9891027
stdin=subprocess.PIPE,
9901028
stdout=subprocess.PIPE,
9911029
stderr=subprocess.STDOUT,
992-
bufsize=0, universal_newlines=True
1030+
bufsize=0,
1031+
universal_newlines=True
9931032
)
9941033
self.gdb_pid = self.proc.pid
9951034

9961035
# discard data from pipe,
9971036
# is there a way to do it a less derpy way?
9981037
while True:
9991038
line = self.proc.stdout.readline()
1039+
1040+
if 'No such process' in line:
1041+
raise GdbException(line)
1042+
10001043
if not line.startswith('(gdb)'):
10011044
pass
10021045
else:
10031046
break
10041047

10051048
def set_breakpoint(self, location):
10061049
result = self._execute('break ' + location)
1007-
success = False
10081050
for line in result:
1051+
print(line)
10091052
if line.startswith('~"Breakpoint'):
1010-
success = True
1011-
break
1012-
if line.startswith('^error') or line.startswith('(gdb)'):
1053+
return
1054+
1055+
elif line.startswith('^error') or line.startswith('(gdb)'):
10131056
break
10141057

1015-
if line.startswith('&"break'):
1058+
elif line.startswith('&"break'):
10161059
pass
10171060

1018-
if line.startswith('&"Function'):
1061+
elif line.startswith('&"Function'):
10191062
raise GdbException(line)
10201063

1021-
if line.startswith('&"No line'):
1064+
elif line.startswith('&"No line'):
10221065
raise GdbException(line)
1023-
return success
10241066

1025-
def run(self):
1026-
result = self._execute('run')
1067+
elif line.startswith('~"Make breakpoint pending on future shared'):
1068+
raise GdbException(line)
1069+
1070+
raise GdbException(
1071+
'Failed to set breakpoint.\n Output:\n {0}'.format(result)
1072+
)
1073+
1074+
def run_until_break(self):
1075+
result = self._execute('run', False)
10271076
for line in result:
10281077
if line.startswith('*stopped,reason="breakpoint-hit"'):
1029-
return 'breakpoint-hit'
1030-
if line.startswith('*stopped,reason="exited-normally"'):
1031-
return 'exit correct'
1078+
return
1079+
raise GdbException(
1080+
'Failed to run until breakpoint.\n'
1081+
)
10321082

1033-
def continue_execution(self, sync=True):
1083+
def continue_execution_until_running(self):
10341084
result = self._execute('continue')
1085+
1086+
running = False
10351087
for line in result:
1088+
if line.startswith('*running'):
1089+
running = True
1090+
break
1091+
if line.startswith('*stopped,reason="breakpoint-hit"'):
1092+
running = False
1093+
continue
1094+
if line.startswith('*stopped,reason="exited-normally"'):
1095+
running = False
1096+
continue
1097+
return running
1098+
1099+
def continue_execution_until_exit(self):
1100+
result = self._execute('continue', False)
1101+
1102+
for line in result:
1103+
if line.startswith('*running'):
1104+
continue
1105+
if line.startswith('*stopped,reason="breakpoint-hit"'):
1106+
continue
1107+
if line.startswith('*stopped,reason="exited-normally"'):
1108+
return
1109+
raise GdbException(
1110+
'Failed to continue execution until exit.\n'
1111+
)
1112+
1113+
def continue_execution_until_break(self, ignore_count=0):
1114+
if ignore_count > 0:
1115+
result = self._execute(
1116+
'continue ' + str(ignore_count),
1117+
False
1118+
)
1119+
else:
1120+
result = self._execute('continue', False)
1121+
1122+
running = False
1123+
for line in result:
1124+
if line.startswith('*running'):
1125+
running = True
10361126
if line.startswith('*stopped,reason="breakpoint-hit"'):
10371127
return 'breakpoint-hit'
10381128
if line.startswith('*stopped,reason="exited-normally"'):
1039-
return 'exit correct'
1129+
return 'exited-normally'
1130+
if running:
1131+
return 'running'
1132+
1133+
def stopped_in_breakpoint(self):
1134+
output = []
1135+
while True:
1136+
line = self.proc.stdout.readline()
1137+
output += [line]
1138+
if self.verbose:
1139+
print(line)
1140+
if line.startswith('*stopped,reason="breakpoint-hit"'):
1141+
return True
1142+
return False
10401143

10411144
# use for breakpoint, run, continue
1042-
def _execute(self, cmd):
1145+
def _execute(self, cmd, running=True):
10431146
output = []
10441147
self.proc.stdin.flush()
10451148
self.proc.stdin.write(cmd + '\n')
10461149
self.proc.stdin.flush()
10471150

10481151
while True:
1152+
sleep(1)
10491153
line = self.proc.stdout.readline()
10501154
output += [line]
10511155
if self.verbose:
10521156
print(line)
10531157
if line == '^done\n' or line.startswith('*stopped'):
10541158
break
1159+
if running and line.startswith('*running'):
1160+
break
10551161
return output

0 commit comments

Comments
 (0)