Files
Codesys-Library-Atom-Modbus/tests/test_boot_link.py
T

93 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""Linux host integration tests for the real dladdr/symlinkat helper; needs cc."""
import ctypes
import os
from pathlib import Path
import subprocess
import sys
import tempfile
HEADER = Path(__file__).resolve().parents[1] / 'component-source/component'
CASES = ['missing', 'correct', 'file', 'wrong_link', 'dangling', 'same_directory', 'unwritable', 'missing_source']
def child(case, base):
base = Path(base)
module = base / 'PlcLogic/libCmpAtomModbus.so'
lib = ctypes.CDLL(str(module), use_errno=True)
lib.warning_count.restype = ctypes.c_int
lib.info_count.restype = ctypes.c_int
os.chdir(base)
link = base / 'libCmpAtomModbus.so'
if case == 'correct':
link.symlink_to('PlcLogic/libCmpAtomModbus.so')
elif case == 'file':
link.write_text('do not replace me')
elif case in ('wrong_link', 'dangling'):
if case == 'wrong_link':
(base / 'other').write_text('other file')
link.symlink_to('other')
elif case == 'same_directory':
os.chdir(module.parent)
elif case == 'missing_source':
module.unlink()
elif case == 'unwritable':
# sysfs cannot accept arbitrary regular symlinks, even as root.
os.chdir('/sys')
link = Path('/sys/libCmpAtomModbus.so')
assert not os.path.lexists(link)
ctypes.set_errno(123)
lib.run_check()
assert ctypes.get_errno() == 123, 'caller errno changed'
warnings = lib.warning_count()
infos = lib.info_count()
lib.run_check()
assert (warnings, infos) == (lib.warning_count(), lib.info_count()), 'retried or logged twice'
if case == 'missing':
assert link.is_symlink() and link.resolve() == module
assert (warnings, infos) == (0, 1)
elif case == 'correct':
assert os.readlink(link) == 'PlcLogic/libCmpAtomModbus.so'
assert (warnings, infos) == (0, 0)
elif case == 'same_directory':
assert module.is_file() and not module.is_symlink()
assert (warnings, infos) == (0, 0)
else:
assert (warnings, infos) == (1, 0)
if case == 'file':
assert link.read_text() == 'do not replace me'
elif case in ('wrong_link', 'dangling'):
assert os.readlink(link) == 'other'
else:
assert not os.path.lexists(link)
print('PASS', case)
if len(sys.argv) == 3:
child(sys.argv[1], sys.argv[2])
else:
with tempfile.TemporaryDirectory(prefix='atmb-link-test-') as temp:
root = Path(temp)
root.chmod(0o755)
harness = root / 'harness.c'
harness.write_text('''#define _GNU_SOURCE
#define syslog test_syslog
#include "atmb_boot_link.h"
static int warnings, infos;
void test_syslog(int priority, const char *format, ...) {
(void)format;
if (priority == LOG_WARNING) ++warnings;
if (priority == LOG_INFO) ++infos;
}
void run_check(void) { atmb_ensure_boot_link(); }
int warning_count(void) { return warnings; }
int info_count(void) { return infos; }
''')
output = root / 'test.so'
subprocess.run([os.environ.get('CC', 'cc'), '-std=c99', '-Wall', '-Wextra', '-Werror',
'-fPIC', '-shared', '-I', str(HEADER), str(harness), '-ldl', '-o', str(output)], check=True)
for case in CASES:
base = root / case
(base / 'PlcLogic').mkdir(parents=True)
(base / 'PlcLogic/libCmpAtomModbus.so').write_bytes(output.read_bytes())
subprocess.run([sys.executable, __file__, case, str(base)], check=True)
base.chmod(0o755)