Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1309442

Browse files
Total refactoring of os_ops::execute_command
Main - We check only an exit code to detect an error. - If someone utility returns a result through an exit code, a caller side should set ignore_errors=true and process this case itself. - If expect_error is true and no errors occurred, we raise an InvalidOperationException.
1 parent b0f90d9 commit 1309442

File tree

7 files changed

+134
-121
lines changed

7 files changed

+134
-121
lines changed

testgres/operations/local_ops.py

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,6 @@
2323
from distutils import rmtree
2424

2525
CMD_TIMEOUT_SEC = 60
26-
error_markers = [b'error', b'Permission denied', b'fatal']
27-
err_out_markers = [b'Failure']
28-
29-
30-
def has_errors(output=None, error=None):
31-
if output:
32-
if isinstance(output, str):
33-
output = output.encode(get_default_encoding())
34-
return any(marker in output for marker in err_out_markers)
35-
if error:
36-
if isinstance(error, str):
37-
error = error.encode(get_default_encoding())
38-
return any(marker in error for marker in error_markers)
39-
return False
4026

4127

4228
class LocalOperations(OsOperations):
@@ -134,19 +120,28 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
134120
process, output, error = self._run_command(cmd, shell, input, stdin, stdout, stderr, get_process, timeout, encoding)
135121
if get_process:
136122
return process
137-
if not ignore_errors and ((process.returncode != 0 or has_errors(output=output, error=error)) and not expect_error):
123+
124+
if expect_error:
125+
if process.returncode == 0:
126+
raise InvalidOperationException("We expected an execution error.")
127+
elif ignore_errors:
128+
pass
129+
elif process.returncode == 0:
130+
pass
131+
else:
132+
assert not expect_error
133+
assert not ignore_errors
134+
assert process.returncode != 0
138135
RaiseError.UtilityExitedWithNonZeroCode(
139136
cmd=cmd,
140137
exit_code=process.returncode,
141-
msg_arg=error or output,
142138
error=error,
143-
out=output
144-
)
139+
out=output)
145140

146141
if verbose:
147142
return process.returncode, output, error
148-
else:
149-
return output
143+
144+
return output
150145

151146
# Environment setup
152147
def environ(self, var_name):

testgres/operations/raise_error.py

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,27 @@
11
from ..exceptions import ExecUtilException
2-
from .helpers import Helpers
32

43

54
class RaiseError:
65
@staticmethod
7-
def UtilityExitedWithNonZeroCode(cmd, exit_code, msg_arg, error, out):
6+
def UtilityExitedWithNonZeroCode(cmd, exit_code, error, out):
87
assert type(exit_code) == int # noqa: E721
98

10-
msg_arg_s = __class__._TranslateDataIntoString(msg_arg).strip()
11-
assert type(msg_arg_s) == str # noqa: E721
12-
13-
if msg_arg_s == "":
14-
msg_arg_s = "#no_error_message"
15-
16-
message = "Utility exited with non-zero code. Error: `" + msg_arg_s + "`"
179
raise ExecUtilException(
18-
message=message,
10+
message="Utility exited with non-zero code.",
1911
command=cmd,
2012
exit_code=exit_code,
2113
out=out,
2214
error=error)
2315

2416
@staticmethod
25-
def _TranslateDataIntoString(data):
26-
if type(data) == bytes: # noqa: E721
27-
return __class__._TranslateDataIntoString__FromBinary(data)
28-
29-
return str(data)
30-
31-
@staticmethod
32-
def _TranslateDataIntoString__FromBinary(data):
33-
assert type(data) == bytes # noqa: E721
34-
35-
try:
36-
return data.decode(Helpers.GetDefaultEncoding())
37-
except UnicodeDecodeError:
38-
pass
39-
40-
return "#cannot_decode_text"
41-
42-
@staticmethod
43-
def _BinaryIsASCII(data):
44-
assert type(data) == bytes # noqa: E721
45-
46-
for b in data:
47-
if not (b >= 0 and b <= 127):
48-
return False
17+
def CommandExecutionError(cmd, exit_code, message, error, out):
18+
assert type(exit_code) == int # noqa: E721
19+
assert type(message) == str # noqa: E721
20+
assert message != ""
4921

50-
return True
22+
raise ExecUtilException(
23+
message=message,
24+
command=cmd,
25+
exit_code=exit_code,
26+
out=out,
27+
error=error)

testgres/operations/remote_ops.py

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -100,41 +100,39 @@ def exec_command(self, cmd, wait_exit=False, verbose=False, expect_error=False,
100100
return process
101101

102102
try:
103-
result, error = process.communicate(input=input_prepared, timeout=timeout)
103+
output, error = process.communicate(input=input_prepared, timeout=timeout)
104104
except subprocess.TimeoutExpired:
105105
process.kill()
106106
raise ExecUtilException("Command timed out after {} seconds.".format(timeout))
107107

108-
exit_status = process.returncode
109-
110-
assert type(result) == bytes # noqa: E721
108+
assert type(output) == bytes # noqa: E721
111109
assert type(error) == bytes # noqa: E721
112110

113-
if not error:
114-
error_found = False
115-
else:
116-
error_found = exit_status != 0 or any(
117-
marker in error for marker in [b'error', b'Permission denied', b'fatal', b'No such file or directory']
118-
)
119-
120-
assert type(error_found) == bool # noqa: E721
121-
122111
if encoding:
123-
result = result.decode(encoding)
112+
output = output.decode(encoding)
124113
error = error.decode(encoding)
125114

126-
if not ignore_errors and error_found and not expect_error:
115+
if expect_error:
116+
if process.returncode == 0:
117+
raise InvalidOperationException("We expected an execution error.")
118+
elif ignore_errors:
119+
pass
120+
elif process.returncode == 0:
121+
pass
122+
else:
123+
assert not expect_error
124+
assert not ignore_errors
125+
assert process.returncode != 0
127126
RaiseError.UtilityExitedWithNonZeroCode(
128127
cmd=cmd,
129-
exit_code=exit_status,
130-
msg_arg=error,
128+
exit_code=process.returncode,
131129
error=error,
132-
out=result)
130+
out=output)
133131

134132
if verbose:
135-
return exit_status, result, error
136-
else:
137-
return result
133+
return process.returncode, output, error
134+
135+
return output
138136

139137
# Environment setup
140138
def environ(self, var_name: str) -> str:
@@ -165,8 +163,30 @@ def find_executable(self, executable):
165163

166164
def is_executable(self, file):
167165
# Check if the file is executable
168-
is_exec = self.exec_command("test -x {} && echo OK".format(file))
169-
return is_exec == b"OK\n"
166+
command = ["test", "-x", file]
167+
168+
exit_status, output, error = self.exec_command(cmd=command, encoding=get_default_encoding(), ignore_errors=True, verbose=True)
169+
170+
assert type(output) == str # noqa: E721
171+
assert type(error) == str # noqa: E721
172+
173+
if exit_status == 0:
174+
return True
175+
176+
if exit_status == 1:
177+
return False
178+
179+
errMsg = "Test operation returns an unknown result code: {0}. File name is [{1}].".format(
180+
exit_status,
181+
file)
182+
183+
RaiseError.UtilityExitedWithNonZeroCode(
184+
cmd=command,
185+
exit_code=exit_status,
186+
msg_arg=errMsg,
187+
error=error,
188+
out=output
189+
)
170190

171191
def set_env(self, var_name: str, var_val: str):
172192
"""
@@ -251,15 +271,21 @@ def mkdtemp(self, prefix=None):
251271
else:
252272
command = ["mktemp", "-d"]
253273

254-
exit_status, result, error = self.exec_command(command, verbose=True, encoding=get_default_encoding(), ignore_errors=True)
274+
exec_exitcode, exec_output, exec_error = self.exec_command(command, verbose=True, encoding=get_default_encoding(), ignore_errors=True)
255275

256-
assert type(result) == str # noqa: E721
257-
assert type(error) == str # noqa: E721
276+
assert type(exec_exitcode) == int # noqa: E721
277+
assert type(exec_output) == str # noqa: E721
278+
assert type(exec_error) == str # noqa: E721
258279

259-
if exit_status != 0:
260-
raise ExecUtilException("Could not create temporary directory. Error code: {0}. Error message: {1}".format(exit_status, error))
280+
if exec_exitcode != 0:
281+
RaiseError.CommandExecutionError(
282+
cmd=command,
283+
exit_code=exec_exitcode,
284+
message="Could not create temporary directory.",
285+
error=exec_error,
286+
out=exec_output)
261287

262-
temp_dir = result.strip()
288+
temp_dir = exec_output.strip()
263289
return temp_dir
264290

265291
def mkstemp(self, prefix=None):
@@ -273,15 +299,21 @@ def mkstemp(self, prefix=None):
273299
else:
274300
command = ["mktemp"]
275301

276-
exit_status, result, error = self.exec_command(command, verbose=True, encoding=get_default_encoding(), ignore_errors=True)
302+
exec_exitcode, exec_output, exec_error = self.exec_command(command, verbose=True, encoding=get_default_encoding(), ignore_errors=True)
277303

278-
assert type(result) == str # noqa: E721
279-
assert type(error) == str # noqa: E721
304+
assert type(exec_exitcode) == int # noqa: E721
305+
assert type(exec_output) == str # noqa: E721
306+
assert type(exec_error) == str # noqa: E721
280307

281-
if exit_status != 0:
282-
raise ExecUtilException("Could not create temporary file. Error code: {0}. Error message: {1}".format(exit_status, error))
308+
if exec_exitcode != 0:
309+
RaiseError.CommandExecutionError(
310+
cmd=command,
311+
exit_code=exec_exitcode,
312+
message="Could not create temporary file.",
313+
error=exec_error,
314+
out=exec_output)
283315

284-
temp_file = result.strip()
316+
temp_file = exec_output.strip()
285317
return temp_file
286318

287319
def copytree(self, src, dst):

testgres/utils.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .config import testgres_config as tconf
1919
from .operations.os_ops import OsOperations
2020
from .operations.remote_ops import RemoteOperations
21+
from .operations.helpers import Helpers as OsHelpers
2122

2223
# rows returned by PG_CONFIG
2324
_pg_config_data = {}
@@ -79,13 +80,13 @@ def execute_utility2(os_ops: OsOperations, args, logfile=None, verbose=False, ig
7980
assert type(verbose) == bool # noqa: E721
8081
assert type(ignore_errors) == bool # noqa: E721
8182

82-
exit_status, out, error = os_ops.exec_command(args, verbose=True, ignore_errors=ignore_errors)
83-
# decode result
83+
exit_status, out, error = os_ops.exec_command(
84+
args,
85+
verbose=True,
86+
ignore_errors=ignore_errors,
87+
encoding=OsHelpers.GetDefaultEncoding())
88+
8489
out = '' if not out else out
85-
if isinstance(out, bytes):
86-
out = out.decode('utf-8')
87-
if isinstance(error, bytes):
88-
error = error.decode('utf-8')
8990

9091
# write new log entry if possible
9192
if logfile:

tests/test_local.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,11 @@ def test_exec_command_failure(self):
4040
try:
4141
self.operations.exec_command(cmd, wait_exit=True, shell=True)
4242
except ExecUtilException as e:
43-
error = e.message
43+
assert e.message == "Utility exited with non-zero code."
44+
assert type(e.error) == bytes # noqa: E721
45+
assert e.error.strip() == b"/bin/sh: 1: nonexistent_command: not found"
4446
break
4547
raise Exception("We wait an exception!")
46-
assert error == "Utility exited with non-zero code. Error: `/bin/sh: 1: nonexistent_command: not found`"
4748

4849
def test_exec_command_failure__expect_error(self):
4950
"""

tests/test_remote.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ def test_exec_command_failure(self):
3838
try:
3939
self.operations.exec_command(cmd, verbose=True, wait_exit=True)
4040
except ExecUtilException as e:
41-
error = e.message
41+
assert e.message == "Utility exited with non-zero code."
42+
assert type(e.error) == bytes # noqa: E721
43+
assert e.error.strip() == b"bash: line 1: nonexistent_command: command not found"
4244
break
4345
raise Exception("We wait an exception!")
44-
assert error == 'Utility exited with non-zero code. Error: `bash: line 1: nonexistent_command: command not found`'
4546

4647
def test_exec_command_failure__expect_error(self):
4748
"""
@@ -108,10 +109,11 @@ def test_makedirs_and_rmdirs_failure(self):
108109
try:
109110
self.operations.rmdirs(path, verbose=True)
110111
except ExecUtilException as e:
111-
error = e.message
112+
assert e.message == "Utility exited with non-zero code."
113+
assert type(e.error) == bytes # noqa: E721
114+
assert e.error.strip() == b"rm: cannot remove '/root/test_dir': Permission denied"
112115
break
113116
raise Exception("We wait an exception!")
114-
assert error == "Utility exited with non-zero code. Error: `rm: cannot remove '/root/test_dir': Permission denied`"
115117

116118
def test_listdir(self):
117119
"""

tests/test_simple_remote.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -178,27 +178,32 @@ def test_init__unk_LANG_and_LC_CTYPE(self):
178178
assert os.environ.get("LC_CTYPE") == unkData[1]
179179
assert not ("LC_COLLATE" in os.environ.keys())
180180

181-
while True:
181+
assert os.getenv('LANG') == unkData[0]
182+
assert os.getenv('LANGUAGE') is None
183+
assert os.getenv('LC_CTYPE') == unkData[1]
184+
assert os.getenv('LC_COLLATE') is None
185+
186+
exc: ExecUtilException = None
187+
with __class__.helper__get_node() as node:
182188
try:
183-
with __class__.helper__get_node():
184-
pass
185-
except ExecUtilException as e:
186-
#
187-
# Example of an error message:
188-
#
189-
# warning: setlocale: LC_CTYPE: cannot change locale (UNKNOWN_CTYPE): No such file or directory
190-
# postgres (PostgreSQL) 14.12
191-
#
192-
errMsg = str(e)
193-
194-
logging.info("Error message is: {0}".format(errMsg))
195-
196-
assert "LC_CTYPE" in errMsg
197-
assert unkData[1] in errMsg
198-
assert "warning: setlocale: LC_CTYPE: cannot change locale (" + unkData[1] + "): No such file or directory" in errMsg
199-
assert ("postgres" in errMsg) or ("PostgreSQL" in errMsg)
200-
break
189+
node.init() # IT RAISES!
190+
except InitNodeException as e:
191+
exc = e.__cause__
192+
assert exc is not None
193+
assert isinstance(exc, ExecUtilException)
194+
195+
if exc is None:
201196
raise Exception("We expected an error!")
197+
198+
assert isinstance(exc, ExecUtilException)
199+
200+
errMsg = str(exc)
201+
logging.info("Error message is {0}: {1}".format(type(exc).__name__, errMsg))
202+
203+
assert "warning: setlocale: LC_CTYPE: cannot change locale (" + unkData[1] + ")" in errMsg
204+
assert "initdb: error: invalid locale settings; check LANG and LC_* environment variables" in errMsg
205+
continue
206+
202207
finally:
203208
__class__.helper__restore_envvar("LANG", prev_LANG)
204209
__class__.helper__restore_envvar("LANGUAGE", prev_LANGUAGE)

0 commit comments

Comments
 (0)