Tests: preserving unit.log when run without restart.

Introducing "unit.log.Log" class for "unit.log" file management.
Moving "findall()" function into TestApplicationProto.
Using "os.kill()" to send signals.
This commit is contained in:
Max Romanov
2021-04-08 19:11:11 +03:00
parent 30922c5741
commit 74b1b1fc17
9 changed files with 94 additions and 96 deletions

View File

@@ -22,6 +22,7 @@ from unit.check.regex import check_regex
from unit.check.tls import check_openssl from unit.check.tls import check_openssl
from unit.http import TestHTTP from unit.http import TestHTTP
from unit.option import option from unit.option import option
from unit.log import Log
from unit.utils import public_dir from unit.utils import public_dir
from unit.utils import waitforfiles from unit.utils import waitforfiles
@@ -71,7 +72,6 @@ def pytest_addoption(parser):
unit_instance = {} unit_instance = {}
unit_log_copy = "unit.log.copy"
_processes = [] _processes = []
_fds_check = { _fds_check = {
'main': {'fds': 0, 'skip': False}, 'main': {'fds': 0, 'skip': False},
@@ -165,12 +165,11 @@ def pytest_sessionstart(session):
option.available = {'modules': {}, 'features': {}} option.available = {'modules': {}, 'features': {}}
unit = unit_run() unit = unit_run()
option.temp_dir = unit['temp_dir']
# read unit.log # read unit.log
for i in range(50): for i in range(50):
with open(unit['temp_dir'] + '/unit.log', 'r') as f: with open(Log.get_path(), 'r') as f:
log = f.read() log = f.read()
m = re.search('controller started', log) m = re.search('controller started', log)
@@ -216,9 +215,6 @@ def pytest_sessionstart(session):
if option.restart: if option.restart:
shutil.rmtree(unit_instance['temp_dir']) shutil.rmtree(unit_instance['temp_dir'])
elif option.save_log:
open(unit_instance['temp_dir'] + '/' + unit_log_copy, 'w').close()
@pytest.hookimpl(tryfirst=True, hookwrapper=True) @pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call): def pytest_runtest_makereport(item, call):
@@ -269,7 +265,6 @@ def check_prerequisites(request):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def run(request): def run(request):
unit = unit_run() unit = unit_run()
option.temp_dir = unit['temp_dir']
option.skip_alerts = [ option.skip_alerts = [
r'read signalfd\(4\) failed', r'read signalfd\(4\) failed',
@@ -291,34 +286,25 @@ def run(request):
# prepare log # prepare log
with open( with Log.open(encoding='utf-8') as f:
unit_instance['log'], 'r', encoding='utf-8', errors='ignore'
) as f:
log = f.read() log = f.read()
Log.set_pos(f.tell())
if not option.restart and option.save_log:
with open(unit_instance['temp_dir'] + '/' + unit_log_copy, 'a') as f:
f.write(log)
# remove unit.log
if not option.save_log and option.restart: if not option.save_log and option.restart:
shutil.rmtree(unit['temp_dir']) shutil.rmtree(unit['temp_dir'])
Log.set_pos(0)
# clean temp_dir before the next test # clean temp_dir before the next test
if not option.restart: if not option.restart:
_clear_conf(unit['temp_dir'] + '/control.unit.sock', log) _clear_conf(unit['temp_dir'] + '/control.unit.sock', log)
open(unit['log'], 'w').close()
for item in os.listdir(unit['temp_dir']): for item in os.listdir(unit['temp_dir']):
if item not in [ if item not in [
'control.unit.sock', 'control.unit.sock',
'state', 'state',
'unit.pid', 'unit.pid',
'unit.log', 'unit.log',
unit_log_copy,
]: ]:
path = os.path.join(unit['temp_dir'], item) path = os.path.join(unit['temp_dir'], item)
@@ -439,10 +425,12 @@ def unit_run():
exit('Could not start unit') exit('Could not start unit')
unit_instance['temp_dir'] = temp_dir unit_instance['temp_dir'] = temp_dir
unit_instance['log'] = temp_dir + '/unit.log'
unit_instance['control_sock'] = temp_dir + '/control.unit.sock' unit_instance['control_sock'] = temp_dir + '/control.unit.sock'
unit_instance['unitd'] = unitd unit_instance['unitd'] = unitd
option.temp_dir = temp_dir
Log.temp_dir = temp_dir
with open(temp_dir + '/unit.pid', 'r') as f: with open(temp_dir + '/unit.pid', 'r') as f:
unit_instance['pid'] = f.read().rstrip() unit_instance['pid'] = f.read().rstrip()
@@ -489,12 +477,9 @@ def unit_stop():
return 'Could not terminate unit' return 'Could not terminate unit'
def _check_alerts(path=None, log=None): def _check_alerts(log=None):
if path is None:
path = unit_instance['log']
if log is None: if log is None:
with open(path, 'r', encoding='utf-8', errors='ignore') as f: with Log.open(encoding='utf-8') as f:
log = f.read() log = f.read()
found = False found = False
@@ -526,7 +511,7 @@ def _check_alerts(path=None, log=None):
def _print_log(data=None): def _print_log(data=None):
path = unit_instance['log'] path = Log.get_path()
print('Path to unit.log:\n' + path + '\n') print('Path to unit.log:\n' + path + '\n')
@@ -679,7 +664,7 @@ def unit_pid(request):
def pytest_sessionfinish(session): def pytest_sessionfinish(session):
if not option.restart and option.save_log: if not option.restart and option.save_log:
print('Path to unit.log:\n' + unit_instance['log'] + '\n') print('Path to unit.log:\n' + Log.get_path() + '\n')
option.restart = True option.restart = True

View File

@@ -249,7 +249,7 @@ Connection: close
assert self.search_in_log(r'/delete', 'access.log') is None, 'delete' assert self.search_in_log(r'/delete', 'access.log') is None, 'delete'
def test_access_log_change(self, temp_dir): def test_access_log_change(self):
self.load('empty') self.load('empty')
self.get() self.get()

View File

@@ -14,10 +14,6 @@ class TestASGIApplication(TestApplicationPython):
} }
load_module = 'asgi' load_module = 'asgi'
def findall(self, pattern):
with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read())
def test_asgi_application_variables(self): def test_asgi_application_variables(self):
self.load('variables') self.load('variables')

View File

@@ -1,8 +1,8 @@
import os import os
import re import re
import shutil import shutil
import signal
import time import time
from subprocess import call
import pytest import pytest
@@ -95,37 +95,29 @@ class TestPHPApplication(TestApplicationPHP):
assert resp['status'] == 200, 'query string empty status' assert resp['status'] == 200, 'query string empty status'
assert resp['headers']['Query-String'] == '', 'query string empty' assert resp['headers']['Query-String'] == '', 'query string empty'
def test_php_application_fastcgi_finish_request(self, temp_dir): def test_php_application_fastcgi_finish_request(self, unit_pid):
self.load('fastcgi_finish_request') self.load('fastcgi_finish_request')
assert self.get()['body'] == '0123' assert self.get()['body'] == '0123'
with open(temp_dir + '/unit.pid', 'r') as f: os.kill(unit_pid, signal.SIGUSR1);
pid = f.read().rstrip()
call(['kill', '-s', 'USR1', pid]) errs = self.findall(r'Error in fastcgi_finish_request')
with open(temp_dir + '/unit.log', 'r', errors='ignore') as f: assert len(errs) == 0, 'no error'
errs = re.findall(r'Error in fastcgi_finish_request', f.read())
assert len(errs) == 0, 'no error' def test_php_application_fastcgi_finish_request_2(self, unit_pid):
def test_php_application_fastcgi_finish_request_2(self, temp_dir):
self.load('fastcgi_finish_request') self.load('fastcgi_finish_request')
resp = self.get(url='/?skip') resp = self.get(url='/?skip')
assert resp['status'] == 200 assert resp['status'] == 200
assert resp['body'] == '' assert resp['body'] == ''
with open(temp_dir + '/unit.pid', 'r') as f: os.kill(unit_pid, signal.SIGUSR1);
pid = f.read().rstrip()
call(['kill', '-s', 'USR1', pid]) errs = self.findall(r'Error in fastcgi_finish_request')
with open(temp_dir + '/unit.log', 'r', errors='ignore') as f: assert len(errs) == 0, 'no error'
errs = re.findall(r'Error in fastcgi_finish_request', f.read())
assert len(errs) == 0, 'no error'
def test_php_application_query_string_absent(self): def test_php_application_query_string_absent(self):
self.load('query_string') self.load('query_string')
@@ -538,7 +530,7 @@ class TestPHPApplication(TestApplicationPHP):
r'012345', self.get()['body'] r'012345', self.get()['body']
), 'disable_classes before' ), 'disable_classes before'
def test_php_application_error_log(self, temp_dir): def test_php_application_error_log(self):
self.load('error_log') self.load('error_log')
assert self.get()['status'] == 200, 'status' assert self.get()['status'] == 200, 'status'
@@ -551,14 +543,13 @@ class TestPHPApplication(TestApplicationPHP):
assert self.wait_for_record(pattern) is not None, 'errors print' assert self.wait_for_record(pattern) is not None, 'errors print'
with open(temp_dir + '/unit.log', 'r', errors='ignore') as f: errs = self.findall(pattern)
errs = re.findall(pattern, f.read())
assert len(errs) == 2, 'error_log count' assert len(errs) == 2, 'error_log count'
date = errs[0].split('[')[0] date = errs[0].split('[')[0]
date2 = errs[1].split('[')[0] date2 = errs[1].split('[')[0]
assert date != date2, 'date diff' assert date != date2, 'date diff'
def test_php_application_script(self): def test_php_application_script(self):
assert 'success' in self.conf( assert 'success' in self.conf(

View File

@@ -7,16 +7,11 @@ import time
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option
class TestPythonApplication(TestApplicationPython): class TestPythonApplication(TestApplicationPython):
prerequisites = {'modules': {'python': 'all'}} prerequisites = {'modules': {'python': 'all'}}
def findall(self, pattern):
with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read())
def test_python_application_variables(self): def test_python_application_variables(self):
self.load('variables') self.load('variables')

View File

@@ -6,16 +6,11 @@ import subprocess
import pytest import pytest
from unit.applications.tls import TestApplicationTLS from unit.applications.tls import TestApplicationTLS
from unit.option import option
class TestTLS(TestApplicationTLS): class TestTLS(TestApplicationTLS):
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}} prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
def findall(self, pattern):
with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read())
def openssl_date_to_sec_epoch(self, date): def openssl_date_to_sec_epoch(self, date):
return self.date_to_sec_epoch(date, '%b %d %H:%M:%S %Y %Z') return self.date_to_sec_epoch(date, '%b %d %H:%M:%S %Y %Z')

View File

@@ -1,14 +1,15 @@
import os import os
from subprocess import call import signal
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.log import Log
from unit.utils import waitforfiles from unit.utils import waitforfiles
class TestUSR1(TestApplicationPython): class TestUSR1(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}} prerequisites = {'modules': {'python': 'any'}}
def test_usr1_access_log(self, temp_dir): def test_usr1_access_log(self, temp_dir, unit_pid):
self.load('empty') self.load('empty')
log = 'access.log' log = 'access.log'
@@ -31,10 +32,7 @@ class TestUSR1(TestApplicationPython):
), 'rename new' ), 'rename new'
assert not os.path.isfile(log_path), 'rename old' assert not os.path.isfile(log_path), 'rename old'
with open(temp_dir + '/unit.pid', 'r') as f: os.kill(unit_pid, signal.SIGUSR1)
pid = f.read().rstrip()
call(['kill', '-s', 'USR1', pid])
assert waitforfiles(log_path), 'reopen' assert waitforfiles(log_path), 'reopen'
@@ -46,7 +44,7 @@ class TestUSR1(TestApplicationPython):
), 'reopen 2' ), 'reopen 2'
assert self.search_in_log(r'/usr1', log_new) is None, 'rename new 2' assert self.search_in_log(r'/usr1', log_new) is None, 'rename new 2'
def test_usr1_unit_log(self, temp_dir): def test_usr1_unit_log(self, temp_dir, unit_pid):
self.load('log_body') self.load('log_body')
log_new = 'new.log' log_new = 'new.log'
@@ -55,28 +53,37 @@ class TestUSR1(TestApplicationPython):
os.rename(log_path, log_path_new) os.rename(log_path, log_path_new)
body = 'body_for_a_log_new' Log.swap(log_new)
assert self.post(body=body)['status'] == 200
assert self.wait_for_record(body, log_new) is not None, 'rename new' try:
assert not os.path.isfile(log_path), 'rename old' body = 'body_for_a_log_new\n'
assert self.post(body=body)['status'] == 200
with open(temp_dir + '/unit.pid', 'r') as f: assert (
pid = f.read().rstrip() self.wait_for_record(body, log_new) is not None
), 'rename new'
assert not os.path.isfile(log_path), 'rename old'
call(['kill', '-s', 'USR1', pid]) os.kill(unit_pid, signal.SIGUSR1)
assert waitforfiles(log_path), 'reopen' assert waitforfiles(log_path), 'reopen'
body = 'body_for_a_log_unit' body = 'body_for_a_log_unit\n'
assert self.post(body=body)['status'] == 200 assert self.post(body=body)['status'] == 200
assert self.wait_for_record(body) is not None, 'rename new' assert self.wait_for_record(body) is not None, 'rename new'
assert self.search_in_log(body, log_new) is None, 'rename new 2' assert self.search_in_log(body, log_new) is None, 'rename new 2'
# merge two log files into unit.log to check alerts finally:
# merge two log files into unit.log to check alerts
with open(log_path, 'w') as unit_log, open( with open(log_path, 'r', errors='ignore') as unit_log:
log_path_new, 'r' log = unit_log.read()
) as unit_log_new:
unit_log.write(unit_log_new.read()) with open(log_path, 'w') as unit_log, open(
log_path_new, 'r', errors='ignore'
) as unit_log_new:
unit_log.write(unit_log_new.read())
unit_log.write(log)
Log.swap(log_new)

View File

@@ -4,6 +4,7 @@ import time
from unit.control import TestControl from unit.control import TestControl
from unit.option import option from unit.option import option
from unit.log import Log
class TestApplicationProto(TestControl): class TestApplicationProto(TestControl):
@@ -15,18 +16,23 @@ class TestApplicationProto(TestControl):
def date_to_sec_epoch(self, date, template='%a, %d %b %Y %H:%M:%S %Z'): def date_to_sec_epoch(self, date, template='%a, %d %b %Y %H:%M:%S %Z'):
return time.mktime(time.strptime(date, template)) return time.mktime(time.strptime(date, template))
def findall(self, pattern, name='unit.log'):
with Log.open(name) as f:
return re.findall(pattern, f.read())
def search_in_log(self, pattern, name='unit.log'): def search_in_log(self, pattern, name='unit.log'):
with open(option.temp_dir + '/' + name, 'r', errors='ignore') as f: with Log.open(name) as f:
return re.search(pattern, f.read()) return re.search(pattern, f.read())
def wait_for_record(self, pattern, name='unit.log', wait=150): def wait_for_record(self, pattern, name='unit.log', wait=150):
for i in range(wait): with Log.open(name) as f:
found = self.search_in_log(pattern, name) for i in range(wait):
found = re.search(pattern, f.read())
if found is not None: if found is not None:
break break
time.sleep(0.1) time.sleep(0.1)
return found return found

23
test/unit/log.py Normal file
View File

@@ -0,0 +1,23 @@
UNIT_LOG = 'unit.log'
class Log:
temp_dir = None
pos = {}
def open(name=UNIT_LOG, encoding=None):
f = open(Log.get_path(name), 'r', encoding=encoding, errors='ignore')
f.seek(Log.pos.get(name, 0))
return f
def set_pos(pos, name=UNIT_LOG):
Log.pos[name] = pos
def swap(name):
pos = Log.pos.get(UNIT_LOG, 0)
Log.pos[UNIT_LOG] = Log.pos.get(name, 0)
Log.pos[name] = pos
def get_path(name=UNIT_LOG):
return Log.temp_dir + '/' + name