From c7cc247baa2f9b6cd2499416644adf0bd13ff070 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 23 Mar 2020 02:12:44 +0000 Subject: Tests: terminate unitd process on exit(). --- test/unit/main.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index 69234dcc..a1182e5c 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -4,6 +4,7 @@ import sys import stat import time import fcntl +import atexit import shutil import signal import argparse @@ -188,10 +189,9 @@ class TestUnit(unittest.TestCase): self._print_log() def stop(self): - if self._started: - self._stop() - + self._stop() self.stop_processes() + atexit.unregister(self.stop) def _run(self): build_dir = self.pardir + '/build' @@ -224,11 +224,11 @@ class TestUnit(unittest.TestCase): stderr=log, ) + atexit.register(self.stop) + if not self.waitforfiles(self.testdir + '/control.unit.sock'): exit("Could not start unit") - self._started = True - self.skip_alerts = [ r'read signalfd\(4\) failed', r'last message send failed', @@ -238,6 +238,9 @@ class TestUnit(unittest.TestCase): self.skip_sanitizer = False def _stop(self): + if self._p.poll() is not None: + return + with self._p as p: p.send_signal(signal.SIGQUIT) @@ -248,10 +251,8 @@ class TestUnit(unittest.TestCase): "Child process terminated with code " + str(retcode) ) except: - self.fail("Could not terminate unit") p.kill() - - self._started = False + self.fail("Could not terminate unit") def _check_alerts(self, log): found = False @@ -295,11 +296,12 @@ class TestUnit(unittest.TestCase): return for process in self._processes: - process.terminate() - process.join(timeout=5) - if process.is_alive(): - self.fail('Fail to stop process') + process.terminate() + process.join(timeout=15) + + if process.is_alive(): + self.fail('Fail to stop process') def waitforfiles(self, *files): for i in range(50): -- cgit From 3fd4b4cfab9541437d07048f3973368e568fe98f Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 23 Mar 2020 02:13:46 +0000 Subject: Tests: rearranging functions in main.py. --- test/unit/main.py | 138 +++++++++++++++++++++++++++--------------------------- 1 file changed, 69 insertions(+), 69 deletions(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index a1182e5c..cdab486f 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -152,47 +152,6 @@ class TestUnit(unittest.TestCase): def setUp(self): self._run() - def tearDown(self): - self.stop() - - # detect errors and failures for current test - - def list2reason(exc_list): - if exc_list and exc_list[-1][0] is self: - return exc_list[-1][1] - - if hasattr(self, '_outcome'): - result = self.defaultTestResult() - self._feedErrorsToResult(result, self._outcome.errors) - else: - result = getattr( - self, '_outcomeForDoCleanups', self._resultForDoCleanups - ) - - success = not list2reason(result.errors) and not list2reason( - result.failures - ) - - # check unit.log for alerts - - unit_log = self.testdir + '/unit.log' - - with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f: - self._check_alerts(f.read()) - - # remove unit.log - - if not TestUnit.save_log and success: - shutil.rmtree(self.testdir) - - else: - self._print_log() - - def stop(self): - self._stop() - self.stop_processes() - atexit.unregister(self.stop) - def _run(self): build_dir = self.pardir + '/build' self.unitd = build_dir + '/unitd' @@ -237,6 +196,47 @@ class TestUnit(unittest.TestCase): ] self.skip_sanitizer = False + def tearDown(self): + self.stop() + + # detect errors and failures for current test + + def list2reason(exc_list): + if exc_list and exc_list[-1][0] is self: + return exc_list[-1][1] + + if hasattr(self, '_outcome'): + result = self.defaultTestResult() + self._feedErrorsToResult(result, self._outcome.errors) + else: + result = getattr( + self, '_outcomeForDoCleanups', self._resultForDoCleanups + ) + + success = not list2reason(result.errors) and not list2reason( + result.failures + ) + + # check unit.log for alerts + + unit_log = self.testdir + '/unit.log' + + with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f: + self._check_alerts(f.read()) + + # remove unit.log + + if not TestUnit.save_log and success: + shutil.rmtree(self.testdir) + + else: + self._print_log() + + def stop(self): + self._stop() + self.stop_processes() + atexit.unregister(self.stop) + def _stop(self): if self._p.poll() is not None: return @@ -254,34 +254,6 @@ class TestUnit(unittest.TestCase): p.kill() self.fail("Could not terminate unit") - def _check_alerts(self, log): - found = False - - alerts = re.findall('.+\[alert\].+', log) - - if alerts: - print('All alerts/sanitizer errors found in log:') - [print(alert) for alert in alerts] - found = True - - if self.skip_alerts: - for skip in self.skip_alerts: - alerts = [al for al in alerts if re.search(skip, al) is None] - - if alerts: - self._print_log(log) - self.assertFalse(alerts, 'alert(s)') - - if not self.skip_sanitizer: - sanitizer_errors = re.findall('.+Sanitizer.+', log) - - if sanitizer_errors: - self._print_log(log) - self.assertFalse(sanitizer_errors, 'sanitizer error(s)') - - if found: - print('skipped.') - def run_process(self, target, *args): if not hasattr(self, '_processes'): self._processes = [] @@ -331,6 +303,34 @@ class TestUnit(unittest.TestCase): for f in files: os.chmod(os.path.join(root, f), 0o777) + def _check_alerts(self, log): + found = False + + alerts = re.findall('.+\[alert\].+', log) + + if alerts: + print('All alerts/sanitizer errors found in log:') + [print(alert) for alert in alerts] + found = True + + if self.skip_alerts: + for skip in self.skip_alerts: + alerts = [al for al in alerts if re.search(skip, al) is None] + + if alerts: + self._print_log(log) + self.assertFalse(alerts, 'alert(s)') + + if not self.skip_sanitizer: + sanitizer_errors = re.findall('.+Sanitizer.+', log) + + if sanitizer_errors: + self._print_log(log) + self.assertFalse(sanitizer_errors, 'sanitizer error(s)') + + if found: + print('skipped.') + @staticmethod def _parse_args(): parser = argparse.ArgumentParser(add_help=False) -- cgit From b0161df42e9a20c69c912f67176c226ae9172b21 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Mon, 23 Mar 2020 19:09:29 +0000 Subject: Tests: wait for unit.pid file before running tests. Waiting for control.unit.sock was replaced by unit.pid due to current problem with race between connect() and listen() calls for control.unit.sock. This change should be reverted after fix. --- test/unit/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index cdab486f..3d95a5b1 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -185,7 +185,10 @@ class TestUnit(unittest.TestCase): atexit.register(self.stop) - if not self.waitforfiles(self.testdir + '/control.unit.sock'): + # Due to race between connect() and listen() after the socket binding + # tests waits for unit.pid file which is created after listen(). + + if not self.waitforfiles(self.testdir + '/unit.pid'): exit("Could not start unit") self.skip_alerts = [ -- cgit From 0935630cba069d6619e967404bb6c7c2a93fbe7e Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Mon, 30 Mar 2020 14:18:51 +0300 Subject: Fixing application process infinite loop. Main process exiting before app process init may have caused hanging. --- test/unit/main.py | 1 - 1 file changed, 1 deletion(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index 3d95a5b1..49c1eed3 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -193,7 +193,6 @@ class TestUnit(unittest.TestCase): self.skip_alerts = [ r'read signalfd\(4\) failed', - r'last message send failed', r'sendmsg.+failed', r'recvmsg.+failed', ] -- cgit From 2bb8b3d88a191d96c6693007ad79ae808f872941 Mon Sep 17 00:00:00 2001 From: Andrei Zeliankou Date: Fri, 3 Apr 2020 01:03:26 +0100 Subject: Tests: minor fixes. --- test/unit/main.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index 49c1eed3..060a03a5 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -199,7 +199,7 @@ class TestUnit(unittest.TestCase): self.skip_sanitizer = False def tearDown(self): - self.stop() + stop_errs = self.stop() # detect errors and failures for current test @@ -234,11 +234,19 @@ class TestUnit(unittest.TestCase): else: self._print_log() + self.assertListEqual(stop_errs, [None, None], 'stop errors') + def stop(self): - self._stop() - self.stop_processes() + errors = [] + + errors.append(self._stop()) + + errors.append(self.stop_processes()) + atexit.unregister(self.stop) + return errors + def _stop(self): if self._p.poll() is not None: return @@ -249,12 +257,10 @@ class TestUnit(unittest.TestCase): try: retcode = p.wait(15) if retcode: - self.fail( - "Child process terminated with code " + str(retcode) - ) + return 'Child process terminated with code ' + str(retcode) except: p.kill() - self.fail("Could not terminate unit") + return 'Could not terminate unit' def run_process(self, target, *args): if not hasattr(self, '_processes'): @@ -269,13 +275,17 @@ class TestUnit(unittest.TestCase): if not hasattr(self, '_processes'): return + fail = False for process in self._processes: if process.is_alive(): process.terminate() process.join(timeout=15) if process.is_alive(): - self.fail('Fail to stop process') + fail = True + + if fail: + return 'Fail to stop process' def waitforfiles(self, *files): for i in range(50): -- cgit From c7f5c1c6641838006088524c2122eae8f9c30431 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 8 Apr 2020 15:15:24 +0300 Subject: Controller: improved handling of unix domain control socket. One of the ways to detect Unit's startup and subsequent readiness to accept commands relies on waiting for the control socket file to be created. Earlier, it was unreliable due to a race condition between the client's connect() and the daemon's listen() calls after the socket's bind() call. Now, unix domain listening sockets are created with a nxt_listen_socket_create() call as follows: s = socket(); unlink("path/to/socket.tmp") bind(s, "path/to/socket.tmp"); listen(s); rename("path/to/socket.tmp", "path/to/socket"); This eliminates a time-lapse when the socket file is already created but nobody is listening on it yet, which therefore prevents the condition described above. Also, it allows reliably detecting whether the socket is being used or simply wasn't cleaned after the daemon stopped abruptly. A successful connection to the socket file means the daemon has been started; otherwise, the file can be overwritten. --- test/unit/main.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'test/unit/main.py') diff --git a/test/unit/main.py b/test/unit/main.py index 060a03a5..4507f71a 100644 --- a/test/unit/main.py +++ b/test/unit/main.py @@ -185,10 +185,7 @@ class TestUnit(unittest.TestCase): atexit.register(self.stop) - # Due to race between connect() and listen() after the socket binding - # tests waits for unit.pid file which is created after listen(). - - if not self.waitforfiles(self.testdir + '/unit.pid'): + if not self.waitforfiles(self.testdir + '/control.unit.sock'): exit("Could not start unit") self.skip_alerts = [ -- cgit