testgres 1.13.4__tar.gz → 1.13.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {testgres-1.13.4/testgres.egg-info → testgres-1.13.5}/PKG-INFO +2 -2
  2. {testgres-1.13.4 → testgres-1.13.5}/pyproject.toml +10 -2
  3. testgres-1.13.5/src/impl/internal_utils.py +20 -0
  4. testgres-1.13.5/src/impl/platforms/internal_platform_utils.py +62 -0
  5. testgres-1.13.5/src/impl/platforms/internal_platform_utils_factory.py +23 -0
  6. testgres-1.13.5/src/impl/platforms/linux/internal_platform_utils.py +120 -0
  7. testgres-1.13.5/src/impl/platforms/win32/internal_platform_utils.py +17 -0
  8. {testgres-1.13.4 → testgres-1.13.5}/src/node.py +4 -3
  9. {testgres-1.13.4 → testgres-1.13.5}/src/node_app.py +1 -1
  10. {testgres-1.13.4 → testgres-1.13.5}/src/utils.py +158 -76
  11. {testgres-1.13.4 → testgres-1.13.5/testgres.egg-info}/PKG-INFO +2 -2
  12. {testgres-1.13.4 → testgres-1.13.5}/testgres.egg-info/SOURCES.txt +5 -0
  13. {testgres-1.13.4 → testgres-1.13.5}/testgres.egg-info/requires.txt +1 -1
  14. {testgres-1.13.4 → testgres-1.13.5}/tests/test_os_ops_common.py +14 -0
  15. {testgres-1.13.4 → testgres-1.13.5}/tests/test_testgres_common.py +84 -4
  16. {testgres-1.13.4 → testgres-1.13.5}/LICENSE +0 -0
  17. {testgres-1.13.4 → testgres-1.13.5}/README.md +0 -0
  18. {testgres-1.13.4 → testgres-1.13.5}/setup.cfg +0 -0
  19. {testgres-1.13.4 → testgres-1.13.5}/src/__init__.py +0 -0
  20. {testgres-1.13.4 → testgres-1.13.5}/src/api.py +0 -0
  21. {testgres-1.13.4 → testgres-1.13.5}/src/backup.py +0 -0
  22. {testgres-1.13.4 → testgres-1.13.5}/src/cache.py +0 -0
  23. {testgres-1.13.4 → testgres-1.13.5}/src/config.py +0 -0
  24. {testgres-1.13.4 → testgres-1.13.5}/src/connection.py +0 -0
  25. {testgres-1.13.4 → testgres-1.13.5}/src/consts.py +0 -0
  26. {testgres-1.13.4 → testgres-1.13.5}/src/decorators.py +0 -0
  27. {testgres-1.13.4 → testgres-1.13.5}/src/defaults.py +0 -0
  28. {testgres-1.13.4 → testgres-1.13.5}/src/enums.py +0 -0
  29. {testgres-1.13.4 → testgres-1.13.5}/src/exceptions.py +0 -0
  30. {testgres-1.13.4 → testgres-1.13.5}/src/impl/port_manager__generic.py +0 -0
  31. {testgres-1.13.4 → testgres-1.13.5}/src/impl/port_manager__this_host.py +0 -0
  32. {testgres-1.13.4 → testgres-1.13.5}/src/logger.py +0 -0
  33. {testgres-1.13.4 → testgres-1.13.5}/src/port_manager.py +0 -0
  34. {testgres-1.13.4 → testgres-1.13.5}/src/pubsub.py +0 -0
  35. {testgres-1.13.4 → testgres-1.13.5}/src/raise_error.py +0 -0
  36. {testgres-1.13.4 → testgres-1.13.5}/src/standby.py +0 -0
  37. {testgres-1.13.4 → testgres-1.13.5}/testgres.egg-info/dependency_links.txt +0 -0
  38. {testgres-1.13.4 → testgres-1.13.5}/testgres.egg-info/top_level.txt +0 -0
  39. {testgres-1.13.4 → testgres-1.13.5}/tests/test_config.py +0 -0
  40. {testgres-1.13.4 → testgres-1.13.5}/tests/test_os_ops_local.py +0 -0
  41. {testgres-1.13.4 → testgres-1.13.5}/tests/test_os_ops_remote.py +0 -0
  42. {testgres-1.13.4 → testgres-1.13.5}/tests/test_raise_error.py +0 -0
  43. {testgres-1.13.4 → testgres-1.13.5}/tests/test_testgres_local.py +0 -0
  44. {testgres-1.13.4 → testgres-1.13.5}/tests/test_testgres_remote.py +0 -0
  45. {testgres-1.13.4 → testgres-1.13.5}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testgres
3
- Version: 1.13.4
3
+ Version: 1.13.5
4
4
  Summary: Testing utility for PostgreSQL and its extensions
5
5
  Author-email: Postgres Professional <testgres@postgrespro.ru>
6
6
  License: PostgreSQL
@@ -27,7 +27,7 @@ Requires-Dist: port-for>=0.4
27
27
  Requires-Dist: six>=1.9.0
28
28
  Requires-Dist: psutil
29
29
  Requires-Dist: packaging
30
- Requires-Dist: testgres.os_ops<3.0.0,>=2.0.2
30
+ Requires-Dist: testgres.os_ops<3.0.0,>=2.1.0
31
31
  Dynamic: license-file
32
32
 
33
33
  [![CI Status](https://img.shields.io/github/actions/workflow/status/postgrespro/testgres/.github/workflows/package-verification.yml?label=CI)](https://github.com/postgrespro/testgres/actions/workflows/package-verification.yml)
@@ -9,9 +9,17 @@ build-backend = "setuptools.build_meta"
9
9
  extend-ignore = ["E501"]
10
10
  exclude = [".git", "__pycache__", "env", "venv"]
11
11
 
12
+ # Pytest settings
13
+ [tool.pytest.ini_options]
14
+
15
+ testpaths = ["tests"]
16
+ log_file_level = "NOTSET"
17
+ log_file_format = "%(levelname)8s [%(asctime)s] %(message)s"
18
+ log_file_date_format = "%Y-%m-%d %H:%M:%S"
19
+
12
20
  [project]
13
21
  name = "testgres"
14
- version = "1.13.4"
22
+ version = "1.13.5"
15
23
 
16
24
  description = "Testing utility for PostgreSQL and its extensions"
17
25
  readme = "README.md"
@@ -54,7 +62,7 @@ dependencies = [
54
62
  "six>=1.9.0",
55
63
  "psutil",
56
64
  "packaging",
57
- "testgres.os_ops>=2.0.2,<3.0.0",
65
+ "testgres.os_ops>=2.1.0,<3.0.0",
58
66
  ]
59
67
 
60
68
  [project.urls]
@@ -0,0 +1,20 @@
1
+ import logging
2
+
3
+
4
+ def send_log(level: int, msg: str) -> None:
5
+ assert type(level) == int # noqa: E721
6
+ assert type(msg) == str # noqa: E721
7
+
8
+ return logging.log(level, "[testgres] " + msg)
9
+
10
+
11
+ def send_log_info(msg: str) -> None:
12
+ assert type(msg) == str # noqa: E721
13
+
14
+ return send_log(logging.INFO, msg)
15
+
16
+
17
+ def send_log_debug(msg: str) -> None:
18
+ assert type(msg) == str # noqa: E721
19
+
20
+ return send_log(logging.DEBUG, msg)
@@ -0,0 +1,62 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ import typing
5
+
6
+ from testgres.operations.os_ops import OsOperations
7
+
8
+
9
+ class InternalPlatformUtils:
10
+ class FindPostmasterResultCode(enum.Enum):
11
+ ok = 0
12
+ not_found = 1,
13
+ not_implemented = 2
14
+ many_processes = 3
15
+ has_problems = 4
16
+
17
+ class FindPostmasterResult:
18
+ code: InternalPlatformUtils.FindPostmasterResultCode
19
+ pid: typing.Optional[int]
20
+
21
+ def __init__(
22
+ self,
23
+ code: InternalPlatformUtils.FindPostmasterResultCode,
24
+ pid: typing.Optional[int]
25
+ ):
26
+ assert type(code) == InternalPlatformUtils.FindPostmasterResultCode # noqa: E721
27
+ assert pid is None or type(pid) == int # noqa: E721
28
+ self.code = code
29
+ self.pid = pid
30
+ return
31
+
32
+ @staticmethod
33
+ def create_ok(pid: int) -> InternalPlatformUtils.FindPostmasterResult:
34
+ assert type(pid) == int # noqa: E721
35
+ return __class__(InternalPlatformUtils.FindPostmasterResultCode.ok, pid)
36
+
37
+ @staticmethod
38
+ def create_not_found() -> InternalPlatformUtils.FindPostmasterResult:
39
+ return __class__(InternalPlatformUtils.FindPostmasterResultCode.not_found, None)
40
+
41
+ @staticmethod
42
+ def create_not_implemented() -> InternalPlatformUtils.FindPostmasterResult:
43
+ return __class__(InternalPlatformUtils.FindPostmasterResultCode.not_implemented, None)
44
+
45
+ @staticmethod
46
+ def create_many_processes() -> InternalPlatformUtils.FindPostmasterResult:
47
+ return __class__(InternalPlatformUtils.FindPostmasterResultCode.many_processes, None)
48
+
49
+ @staticmethod
50
+ def create_has_problems() -> InternalPlatformUtils.FindPostmasterResult:
51
+ return __class__(InternalPlatformUtils.FindPostmasterResultCode.has_problems, None)
52
+
53
+ def FindPostmaster(
54
+ self,
55
+ os_ops: OsOperations,
56
+ bin_dir: str,
57
+ data_dir: str
58
+ ) -> FindPostmasterResult:
59
+ assert isinstance(os_ops, OsOperations)
60
+ assert type(bin_dir) == str # noqa: E721
61
+ assert type(data_dir) == str # noqa: E721
62
+ raise NotImplementedError("InternalPlatformUtils::FindPostmaster is not implemented.")
@@ -0,0 +1,23 @@
1
+ from .internal_platform_utils import InternalPlatformUtils
2
+
3
+ from testgres.operations.os_ops import OsOperations
4
+
5
+
6
+ def create_internal_platform_utils(
7
+ os_ops: OsOperations
8
+ ) -> InternalPlatformUtils:
9
+ assert isinstance(os_ops, OsOperations)
10
+
11
+ platform_name = os_ops.get_platform()
12
+ assert type(platform_name) == str # noqa: E721
13
+
14
+ if platform_name == "linux":
15
+ from .linux import internal_platform_utils as x
16
+ return x.InternalPlatformUtils()
17
+
18
+ if platform_name == "win32":
19
+ from .win32 import internal_platform_utils as x
20
+ return x.InternalPlatformUtils()
21
+
22
+ # not implemented
23
+ return InternalPlatformUtils()
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ from .. import internal_platform_utils as base
4
+ from ... import internal_utils
5
+
6
+ from testgres.operations.os_ops import OsOperations
7
+ from testgres.operations.exceptions import ExecUtilException
8
+
9
+ import re
10
+ import shlex
11
+
12
+
13
+ class InternalPlatformUtils(base.InternalPlatformUtils):
14
+ C_BASH_EXE = "/bin/bash"
15
+
16
+ sm_exec_env = {
17
+ "LANG": "en_US.UTF-8",
18
+ "LC_ALL": "en_US.UTF-8",
19
+ }
20
+
21
+ # --------------------------------------------------------------------
22
+ def FindPostmaster(
23
+ self,
24
+ os_ops: OsOperations,
25
+ bin_dir: str,
26
+ data_dir: str
27
+ ) -> InternalPlatformUtils.FindPostmasterResult:
28
+ assert isinstance(os_ops, OsOperations)
29
+ assert type(bin_dir) == str # noqa: E721
30
+ assert type(data_dir) == str # noqa: E721
31
+ assert type(__class__.C_BASH_EXE) == str # noqa: E721
32
+ assert type(__class__.sm_exec_env) == dict # noqa: E721
33
+ assert len(__class__.C_BASH_EXE) > 0
34
+ assert len(bin_dir) > 0
35
+ assert len(data_dir) > 0
36
+
37
+ pg_path_e = re.escape(os_ops.build_path(bin_dir, "postgres"))
38
+ data_dir_e = re.escape(data_dir)
39
+
40
+ assert type(pg_path_e) == str # noqa: E721
41
+ assert type(data_dir_e) == str # noqa: E721
42
+
43
+ regexp = r"^\s*[0-9]+\s+" + pg_path_e + r"(\s+.*)?\s+\-[D]\s+" + data_dir_e + r"(\s+.*)?"
44
+
45
+ cmd = [
46
+ __class__.C_BASH_EXE,
47
+ "-c",
48
+ "ps -ewwo \"pid=,args=\" | grep -E " + shlex.quote(regexp),
49
+ ]
50
+
51
+ exit_status, output_b, error_b = os_ops.exec_command(
52
+ cmd=cmd,
53
+ ignore_errors=True,
54
+ verbose=True,
55
+ exec_env=__class__.sm_exec_env,
56
+ )
57
+
58
+ assert type(output_b) == bytes # noqa: E721
59
+ assert type(error_b) == bytes # noqa: E721
60
+
61
+ output = output_b.decode("utf-8")
62
+ error = error_b.decode("utf-8")
63
+
64
+ assert type(output) == str # noqa: E721
65
+ assert type(error) == str # noqa: E721
66
+
67
+ if exit_status == 1:
68
+ return __class__.FindPostmasterResult.create_not_found()
69
+
70
+ if exit_status != 0:
71
+ errMsg = f"test command returned an unexpected exit code: {exit_status}"
72
+ raise ExecUtilException(
73
+ message=errMsg,
74
+ command=cmd,
75
+ exit_code=exit_status,
76
+ out=output,
77
+ error=error,
78
+ )
79
+
80
+ lines = output.splitlines()
81
+ assert type(lines) == list # noqa: E721
82
+
83
+ if len(lines) == 0:
84
+ return __class__.FindPostmasterResult.create_not_found()
85
+
86
+ if len(lines) > 1:
87
+ msgs = []
88
+ msgs.append("Many processes like a postmaster are found: {}.".format(len(lines)))
89
+
90
+ for i in range(len(lines)):
91
+ assert type(lines[i]) == str # noqa: E721
92
+ lines.append("[{}] '{}'".format(i, lines[i]))
93
+ continue
94
+
95
+ internal_utils.send_log_debug("\n".join(lines))
96
+ return __class__.FindPostmasterResult.create_many_processes()
97
+
98
+ def is_space_or_tab(ch) -> bool:
99
+ assert type(ch) == str # noqa: E721
100
+ return ch == " " or ch == "\t"
101
+
102
+ line = lines[0]
103
+ start = 0
104
+ while start < len(line) and is_space_or_tab(line[start]):
105
+ start += 1
106
+
107
+ pos = start
108
+ while pos < len(line) and line[pos].isnumeric():
109
+ pos += 1
110
+
111
+ if pos == start:
112
+ return __class__.FindPostmasterResult.create_has_problems()
113
+
114
+ if pos != len(line) and not line[pos].isspace():
115
+ return __class__.FindPostmasterResult.create_has_problems()
116
+
117
+ pid = int(line[start:pos])
118
+ assert type(pid) == int # noqa: E721
119
+
120
+ return __class__.FindPostmasterResult.create_ok(pid)
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from .. import internal_platform_utils as base
4
+ from testgres.operations.os_ops import OsOperations
5
+
6
+
7
+ class InternalPlatformUtils(base.InternalPlatformUtils):
8
+ def FindPostmaster(
9
+ self,
10
+ os_ops: OsOperations,
11
+ bin_dir: str,
12
+ data_dir: str
13
+ ) -> InternalPlatformUtils.FindPostmasterResult:
14
+ assert isinstance(os_ops, OsOperations)
15
+ assert type(bin_dir) == str # noqa: E721
16
+ assert type(data_dir) == str # noqa: E721
17
+ return __class__.FindPostmasterResult.create_not_implemented()
@@ -122,7 +122,7 @@ class ProcessProxy(object):
122
122
  ptype: instance of ProcessType
123
123
  """
124
124
 
125
- _process: any
125
+ _process: typing.Any
126
126
  _ptype: ProcessType
127
127
 
128
128
  def __init__(self, process, ptype: typing.Optional[ProcessType] = None):
@@ -147,7 +147,7 @@ class ProcessProxy(object):
147
147
  repr(self.process))
148
148
 
149
149
  @property
150
- def process(self) -> any:
150
+ def process(self) -> typing.Any:
151
151
  assert self._process is not None
152
152
  return self._process
153
153
 
@@ -174,7 +174,7 @@ class PostgresNode(object):
174
174
  name=None,
175
175
  base_dir=None,
176
176
  port: typing.Optional[int] = None,
177
- conn_params: ConnectionParams = None,
177
+ conn_params: typing.Optional[ConnectionParams] = None,
178
178
  bin_dir=None,
179
179
  prefix=None,
180
180
  os_ops: typing.Optional[OsOperations] = None,
@@ -2168,6 +2168,7 @@ class PostgresNode(object):
2168
2168
  bin_path = get_bin_path2(self.os_ops, filename)
2169
2169
  return bin_path
2170
2170
 
2171
+ @staticmethod
2171
2172
  def _escape_config_value(value):
2172
2173
  assert type(value) == str # noqa: E721
2173
2174
 
@@ -92,7 +92,7 @@ class NodeApp:
92
92
  self._os_ops.rmdirs(real_base_dir, ignore_errors=True)
93
93
  self._os_ops.makedirs(real_base_dir)
94
94
 
95
- port_manager: PortManager = None
95
+ port_manager: typing.Optional[PortManager] = None
96
96
 
97
97
  if port is None:
98
98
  port_manager = self._port_manager
@@ -4,8 +4,8 @@ from __future__ import division
4
4
  from __future__ import print_function
5
5
 
6
6
  import os
7
-
8
7
  import sys
8
+ import time
9
9
 
10
10
  from contextlib import contextmanager
11
11
  from packaging.version import Version, InvalidVersion
@@ -28,6 +28,9 @@ from testgres.operations.helpers import Helpers as OsHelpers
28
28
 
29
29
  from .impl.port_manager__generic import PortManager__Generic
30
30
 
31
+ from .impl.platforms import internal_platform_utils_factory
32
+ from .impl import internal_utils
33
+
31
34
  # rows returned by PG_CONFIG
32
35
  _pg_config_data = {}
33
36
 
@@ -351,6 +354,10 @@ def get_pg_node_state(
351
354
  assert type(data_dir) == str # noqa: E721
352
355
  assert utils_log_file is None or type(utils_log_file) == str # noqa: E721
353
356
 
357
+ C_MAX_ATTEMPTS = 3
358
+ C_SLEEP_TIME1 = 1
359
+ C_SLEEP_TIME_MULT = 2
360
+
354
361
  _params = [
355
362
  os_ops.build_path(bin_dir, "pg_ctl"),
356
363
  "-D",
@@ -358,102 +365,177 @@ def get_pg_node_state(
358
365
  "status",
359
366
  ]
360
367
 
361
- status_code, out, error = execute_utility2(
362
- os_ops,
363
- _params,
364
- utils_log_file,
365
- verbose=True,
366
- ignore_errors=True,
367
- )
368
+ attempt = 0
369
+ sleep_time = C_SLEEP_TIME1
368
370
 
369
- assert type(status_code) == int # noqa: E721
370
- assert type(out) == str # noqa: E721
371
- assert type(error) == str # noqa: E721
371
+ platform_utils: typing.Optional[internal_platform_utils_factory.InternalPlatformUtils] = None
372
372
 
373
- # -----------------
374
- if status_code == PG_CTL__STATUS__NODE_IS_STOPPED:
375
- return PostgresNodeState(NodeStatus.Stopped, None)
373
+ while True:
374
+ assert type(attempt) == int # noqa: E721
375
+ assert attempt >= 0
376
+ assert attempt < C_MAX_ATTEMPTS
377
+
378
+ attempt += 1
379
+
380
+ if attempt > 1:
381
+ internal_utils.send_log_debug("Sleep {} second(s) before an attempt #{}".format(
382
+ sleep_time,
383
+ attempt
384
+ ))
385
+ time.sleep(sleep_time)
386
+ sleep_time = sleep_time * C_SLEEP_TIME_MULT
387
+
388
+ status_code, out, error = execute_utility2(
389
+ os_ops,
390
+ _params,
391
+ utils_log_file,
392
+ verbose=True,
393
+ ignore_errors=True,
394
+ )
376
395
 
377
- # -----------------
378
- if status_code == PG_CTL__STATUS__BAD_DATADIR:
379
- return PostgresNodeState(NodeStatus.Uninitialized, None)
396
+ assert type(status_code) == int # noqa: E721
397
+ assert type(out) == str # noqa: E721
398
+ assert type(error) == str # noqa: E721
380
399
 
381
- # -----------------
382
- if status_code != PG_CTL__STATUS__OK:
383
- errMsg = "Getting of a node status [data_dir is {0}] failed.".format(
384
- data_dir
385
- )
400
+ # -----------------
401
+ if status_code == PG_CTL__STATUS__NODE_IS_STOPPED:
402
+ return PostgresNodeState(NodeStatus.Stopped, None)
386
403
 
387
- raise ExecUtilException(
388
- message=errMsg,
389
- command=_params,
390
- exit_code=status_code,
391
- out=out,
392
- error=error,
393
- )
404
+ # -----------------
405
+ if status_code == PG_CTL__STATUS__BAD_DATADIR:
406
+ return PostgresNodeState(NodeStatus.Uninitialized, None)
394
407
 
395
- if out == "":
396
- RaiseError.pg_ctl_returns_an_empty_string(
397
- _params
398
- )
408
+ # -----------------
409
+ if status_code == PG_CTL__STATUS__OK:
410
+ if out == "":
411
+ RaiseError.pg_ctl_returns_an_empty_string(
412
+ _params
413
+ )
399
414
 
400
- C_PID_PREFIX = "(PID: "
415
+ C_PID_PREFIX = "(PID: "
401
416
 
402
- i = out.find(C_PID_PREFIX)
417
+ i = out.find(C_PID_PREFIX)
403
418
 
404
- if i == -1:
405
- RaiseError.pg_ctl_returns_an_unexpected_string(
406
- out,
407
- _params
408
- )
419
+ if i == -1:
420
+ RaiseError.pg_ctl_returns_an_unexpected_string(
421
+ out,
422
+ _params
423
+ )
409
424
 
410
- assert i > 0
411
- assert i < len(out)
412
- assert len(C_PID_PREFIX) <= len(out)
413
- assert i <= len(out) - len(C_PID_PREFIX)
425
+ assert i > 0
426
+ assert i < len(out)
427
+ assert len(C_PID_PREFIX) <= len(out)
428
+ assert i <= len(out) - len(C_PID_PREFIX)
414
429
 
415
- i += len(C_PID_PREFIX)
416
- start_pid_s = i
430
+ i += len(C_PID_PREFIX)
431
+ start_pid_s = i
417
432
 
418
- while True:
419
- if i == len(out):
420
- RaiseError.pg_ctl_returns_an_unexpected_string(
421
- out,
422
- _params
423
- )
433
+ while True:
434
+ if i == len(out):
435
+ RaiseError.pg_ctl_returns_an_unexpected_string(
436
+ out,
437
+ _params
438
+ )
424
439
 
425
- ch = out[i]
440
+ ch = out[i]
426
441
 
427
- if ch == ")":
428
- break
442
+ if ch == ")":
443
+ break
429
444
 
430
- if ch.isdigit():
431
- i += 1
432
- continue
445
+ if ch.isdigit():
446
+ i += 1
447
+ continue
433
448
 
434
- RaiseError.pg_ctl_returns_an_unexpected_string(
435
- out,
436
- _params
437
- )
438
- assert False
449
+ RaiseError.pg_ctl_returns_an_unexpected_string(
450
+ out,
451
+ _params
452
+ )
453
+ assert False
454
+
455
+ if i == start_pid_s:
456
+ RaiseError.pg_ctl_returns_an_unexpected_string(
457
+ out,
458
+ _params
459
+ )
460
+
461
+ # TODO: Let's verify a length of pid string.
462
+
463
+ pid = int(out[start_pid_s:i])
439
464
 
440
- if i == start_pid_s:
441
- RaiseError.pg_ctl_returns_an_unexpected_string(
442
- out,
443
- _params
465
+ if pid == 0:
466
+ RaiseError.pg_ctl_returns_a_zero_pid(
467
+ out,
468
+ _params
469
+ )
470
+
471
+ assert pid != 0
472
+
473
+ # -----------------
474
+ return PostgresNodeState(NodeStatus.Running, pid)
475
+
476
+ assert status_code != PG_CTL__STATUS__OK
477
+
478
+ errMsg = "Getting of a node status [data_dir is {0}] failed.".format(
479
+ data_dir
444
480
  )
445
481
 
446
- # TODO: Let's verify a length of pid string.
482
+ e1 = ExecUtilException(
483
+ message=errMsg,
484
+ command=_params,
485
+ exit_code=status_code,
486
+ out=out,
487
+ error=error,
488
+ )
447
489
 
448
- pid = int(out[start_pid_s:i])
490
+ pid_file = os_ops.build_path(data_dir, "postmaster.pid")
449
491
 
450
- if pid == 0:
451
- RaiseError.pg_ctl_returns_a_zero_pid(
452
- out,
453
- _params
492
+ postmaster_pid_is_empty = "pg_ctl: the PID file \"{}\" is empty\n".format(
493
+ pid_file,
454
494
  )
455
495
 
456
- assert pid != 0
496
+ if error == postmaster_pid_is_empty:
497
+ internal_utils.send_log_debug(
498
+ "PID file [{}] is empty. A check is being carried out to ensure that the postmaster is alive [bindir: {}] ...".format(
499
+ pid_file,
500
+ bin_dir,
501
+ ))
502
+
503
+ if platform_utils is None:
504
+ platform_utils = internal_platform_utils_factory.create_internal_platform_utils(os_ops)
505
+ assert isinstance(platform_utils, internal_platform_utils_factory.InternalPlatformUtils)
506
+
507
+ assert isinstance(platform_utils, internal_platform_utils_factory.InternalPlatformUtils)
508
+
509
+ try:
510
+ find_postmaster_r = platform_utils.FindPostmaster(
511
+ os_ops,
512
+ bin_dir,
513
+ data_dir,
514
+ )
515
+ except Exception as e2:
516
+ e2.__cause__ = e1
517
+ raise e2
518
+
519
+ assert type(find_postmaster_r) == internal_platform_utils_factory.InternalPlatformUtils.FindPostmasterResult # noqa: E721
520
+
521
+ if find_postmaster_r.code == internal_platform_utils_factory.InternalPlatformUtils.FindPostmasterResultCode.ok:
522
+ # Postmaster is alive. Let's wait a few seconds and check its status again.
523
+ internal_utils.send_log_debug(
524
+ "Postmaster is found and has PID {}.".format(
525
+ find_postmaster_r.pid
526
+ ))
527
+
528
+ if attempt < C_MAX_ATTEMPTS:
529
+ continue
457
530
 
458
- # -----------------
459
- return PostgresNodeState(NodeStatus.Running, pid)
531
+ errMsg = "Getting of a node status [data_dir is {0}] failed.".format(
532
+ data_dir
533
+ )
534
+
535
+ raise ExecUtilException(
536
+ message=errMsg,
537
+ command=_params,
538
+ exit_code=status_code,
539
+ out=out,
540
+ error=error,
541
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testgres
3
- Version: 1.13.4
3
+ Version: 1.13.5
4
4
  Summary: Testing utility for PostgreSQL and its extensions
5
5
  Author-email: Postgres Professional <testgres@postgrespro.ru>
6
6
  License: PostgreSQL
@@ -27,7 +27,7 @@ Requires-Dist: port-for>=0.4
27
27
  Requires-Dist: six>=1.9.0
28
28
  Requires-Dist: psutil
29
29
  Requires-Dist: packaging
30
- Requires-Dist: testgres.os_ops<3.0.0,>=2.0.2
30
+ Requires-Dist: testgres.os_ops<3.0.0,>=2.1.0
31
31
  Dynamic: license-file
32
32
 
33
33
  [![CI Status](https://img.shields.io/github/actions/workflow/status/postgrespro/testgres/.github/workflows/package-verification.yml?label=CI)](https://github.com/postgrespro/testgres/actions/workflows/package-verification.yml)
@@ -20,8 +20,13 @@ src/pubsub.py
20
20
  src/raise_error.py
21
21
  src/standby.py
22
22
  src/utils.py
23
+ src/impl/internal_utils.py
23
24
  src/impl/port_manager__generic.py
24
25
  src/impl/port_manager__this_host.py
26
+ src/impl/platforms/internal_platform_utils.py
27
+ src/impl/platforms/internal_platform_utils_factory.py
28
+ src/impl/platforms/linux/internal_platform_utils.py
29
+ src/impl/platforms/win32/internal_platform_utils.py
25
30
  testgres.egg-info/PKG-INFO
26
31
  testgres.egg-info/SOURCES.txt
27
32
  testgres.egg-info/dependency_links.txt
@@ -3,4 +3,4 @@ port-for>=0.4
3
3
  six>=1.9.0
4
4
  psutil
5
5
  packaging
6
- testgres.os_ops<3.0.0,>=2.0.2
6
+ testgres.os_ops<3.0.0,>=2.1.0
@@ -42,6 +42,20 @@ class TestOsOpsCommon:
42
42
  assert isinstance(request.param, OsOperations)
43
43
  return request.param
44
44
 
45
+ def test_get_platform(self, os_ops: OsOperations):
46
+ assert isinstance(os_ops, OsOperations)
47
+ p = os_ops.get_platform()
48
+ assert p is not None
49
+ assert type(p) == str # noqa: E721
50
+ assert p == sys.platform
51
+
52
+ def test_get_platform__is_known(self, os_ops: OsOperations):
53
+ assert isinstance(os_ops, OsOperations)
54
+ p = os_ops.get_platform()
55
+ assert p is not None
56
+ assert type(p) == str # noqa: E721
57
+ assert p in {"win32", "linux"}
58
+
45
59
  def test_create_clone(self, os_ops: OsOperations):
46
60
  assert isinstance(os_ops, OsOperations)
47
61
  clone = os_ops.create_clone()
@@ -416,6 +416,84 @@ class TestTestgresCommon:
416
416
  assert (node.pid == 0)
417
417
  assert (node.status() == NodeStatus.Uninitialized)
418
418
 
419
+ def test_status__empty_postmaster_pid(self, node_svc: PostgresNodeService):
420
+ assert isinstance(node_svc, PostgresNodeService)
421
+
422
+ assert (NodeStatus.Running)
423
+ assert not (NodeStatus.Stopped)
424
+ assert not (NodeStatus.Uninitialized)
425
+
426
+ # check statuses after each operation
427
+ with __class__.helper__get_node(node_svc) as node:
428
+ assert (node.pid == 0)
429
+ assert (node.status() == NodeStatus.Uninitialized)
430
+
431
+ node.init()
432
+
433
+ postmaster_pid_file = node.os_ops.build_path(node.data_dir, "postmaster.pid")
434
+
435
+ node.os_ops.write(
436
+ postmaster_pid_file,
437
+ ""
438
+ )
439
+
440
+ with pytest.raises(expected_exception=ExecUtilException) as x:
441
+ node.status()
442
+
443
+ expected_msg = "pg_ctl: the PID file \"{}\" is empty\n".format(
444
+ postmaster_pid_file
445
+ )
446
+
447
+ assert expected_msg == x.value.error
448
+ return
449
+
450
+ def test_status__force_clean_postmaster_pid(self, node_svc: PostgresNodeService):
451
+ assert isinstance(node_svc, PostgresNodeService)
452
+
453
+ assert (NodeStatus.Running)
454
+ assert not (NodeStatus.Stopped)
455
+ assert not (NodeStatus.Uninitialized)
456
+
457
+ # check statuses after each operation
458
+ with __class__.helper__get_node(node_svc) as node:
459
+ assert (node.pid == 0)
460
+ assert (node.status() == NodeStatus.Uninitialized)
461
+
462
+ node.init()
463
+ node.start()
464
+
465
+ assert node.status() == NodeStatus.Running
466
+ logging.info("Postmaster PID is {}.".format(node.pid))
467
+
468
+ postmaster_pid_file = node.os_ops.build_path(node.data_dir, "postmaster.pid")
469
+
470
+ logging.info("Clean postmaster pid file [{}].".format(
471
+ postmaster_pid_file
472
+ ))
473
+
474
+ node.os_ops.write(
475
+ postmaster_pid_file,
476
+ "",
477
+ truncate=True,
478
+ )
479
+
480
+ x = node.os_ops.read(
481
+ postmaster_pid_file,
482
+ encoding="utf-8",
483
+ binary=False
484
+ )
485
+ assert x == ""
486
+
487
+ with pytest.raises(expected_exception=ExecUtilException) as x:
488
+ node.status()
489
+
490
+ expected_msg = "pg_ctl: the PID file \"{}\" is empty\n".format(
491
+ postmaster_pid_file
492
+ )
493
+
494
+ assert expected_msg == x.value.error
495
+ return
496
+
419
497
  def test_kill__is_not_initialized(
420
498
  self,
421
499
  node_svc: PostgresNodeService
@@ -981,14 +1059,16 @@ class TestTestgresCommon:
981
1059
  assert lines is not None
982
1060
  assert type(lines) == list # noqa: E721
983
1061
 
984
- def LOCAL__test_lines():
1062
+ def LOCAL__test_lines(lines: typing.Iterable[str]) -> bool:
1063
+ assert isinstance(lines, typing.Iterable)
985
1064
  for s in lines:
986
- if any(C_NODE_NAME in s for s in lines):
1065
+ assert type(s) == str # noqa: E721
1066
+ if C_NODE_NAME in s:
987
1067
  logging.info("OK. We found the node_name in a line \"{0}\"".format(s))
988
1068
  return True
989
- return False
1069
+ return False
990
1070
 
991
- if LOCAL__test_lines():
1071
+ if LOCAL__test_lines(lines):
992
1072
  break
993
1073
 
994
1074
  logging.info("Master node log file does not have an expected information.")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes