Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Test Suite

on: [push, pull_request]

jobs:
test:
name: Build and test
runs-on: ubuntu-latest

steps:
- name: Set up Git repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install dependencies
run: |
sudo apt-get -q update
sudo apt-get -q -y install --no-install-recommends \
build-essential automake autoconf libtool pkg-config \
icu-devtools libicu-dev libxml2-dev uuid-dev libsnmp-dev \
libfuse-dev attr
sudo install -m 755 tests/docker/icu-config /usr/local/bin/icu-config

- name: Build
run: |
./autogen.sh
./configure --enable-icu-6x
make -j"$(nproc)"

- name: Run tests
run: make check VERBOSE=1 || { cat tests/test-suite.log; exit 1; }
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ GTAGS
TAGS
tags
*~
# Files generated by make check
tests/t/*.log
tests/t/*.trs
tests/test-suite.log
build.log
configure.log
tests/helpers/fsops_helper
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ ACLOCAL_AMFLAGS = -I m4

nobase_pkginclude_HEADERS = config.h

SUBDIRS = messages src conf init.d man
SUBDIRS = messages src conf init.d man tests

install-data-local: ltfs.pc
if [ ! -d $(DESTDIR)$(libdir)/pkgconfig ]; then \
Expand Down
6 changes: 6 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,11 @@ dnl
dnl Output files
dnl
AC_CONFIG_HEADERS([config.h])
dnl Compute the integration test list at configure time so branches can
dnl add tests/t/*.sh scripts without editing the build files.
TESTS_LIST=`cd "$srcdir/tests" 2>/dev/null && ls t/*.sh 2>/dev/null | sort | tr '\n' ' '`
AC_SUBST([TESTS_LIST])

AC_CONFIG_FILES([
Makefile
messages/Makefile
Expand All @@ -619,6 +624,7 @@ AC_CONFIG_FILES([
src/iosched/Makefile
src/kmi/Makefile
src/utils/Makefile
tests/Makefile
ltfs.pc:ltfs.pc.in
])

Expand Down
5 changes: 4 additions & 1 deletion src/libltfs/arch/ltfs_arch_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ extern "C" {

#define arch_sscanf sscanf

#define arch_open( descriptor_ptr, filename_ptr, open_flg, share_flg, unused) do{ *descriptor_ptr = open(filename_ptr, open_flg, share_flg); }while(0)
/* Share flags are a Windows concept; on POSIX the permission argument
* is the mode passed to open(2). Passing the share flag as the mode
* created write-only (0200) files, unreadable for non-root users. */
#define arch_open( descriptor_ptr, filename_ptr, open_flg, share_flg, perm) do{ *descriptor_ptr = open(filename_ptr, open_flg, perm); }while(0)

#define arch_fopen(file, mode, file_ptr) do {file_ptr = fopen(file, mode);}while(0)

Expand Down
31 changes: 31 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#
# Integration tests for LTFS using the file tape backend.
#
# Run with: make check
# The tests need a Linux host with /dev/fuse; elsewhere they are skipped.
# On macOS use tests/run-in-docker.sh to run them in a Linux container.
#

TEST_EXTENSIONS = .sh
SH_LOG_COMPILER = $(SHELL)

AM_TESTS_ENVIRONMENT = \
top_builddir='$(abs_top_builddir)' top_srcdir='$(abs_top_srcdir)'; \
export top_builddir top_srcdir;

check_PROGRAMS = helpers/fsops_helper
helpers_fsops_helper_SOURCES = helpers/fsops_helper.c

# The test list is computed at configure time (TESTS_LIST in configure.ac)
# so feature branches can ship additional t/*.sh scripts without editing
# the build files; ctest registers them the same way. GNU make functions
# cannot be used here: automake runs with -Wall -Werror, which rejects
# them as non-POSIX.
TESTS = $(TESTS_LIST)

EXTRA_DIST = \
lib/harness.sh \
$(TESTS) \
docker/Dockerfile \
docker/icu-config \
run-in-docker.sh
16 changes: 16 additions & 0 deletions tests/docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get -q update && apt-get -q -y install --no-install-recommends \
build-essential automake autoconf libtool pkg-config \
icu-devtools libicu-dev libxml2-dev uuid-dev libsnmp-dev \
libfuse-dev fuse3 libfuse3-dev \
attr file procps util-linux tar \
&& rm -rf /var/lib/apt/lists/*

# configure.ac probes icu-config, which modern ICU no longer ships.
COPY icu-config /usr/local/bin/icu-config
RUN chmod +x /usr/local/bin/icu-config

WORKDIR /ltfs
12 changes: 12 additions & 0 deletions tests/docker/icu-config
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
# Minimal icu-config replacement for distributions that no longer ship it.
case "$1" in
--cppflags)
pkg-config --cflags icu-uc ;;
--ldflags)
pkg-config --libs icu-uc icu-io ;;
--version)
pkg-config --modversion icu-uc ;;
*)
echo '' ;;
esac
64 changes: 64 additions & 0 deletions tests/helpers/fsops_helper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Test helper exercising syscalls that shell utilities do not reach
* directly: renameat2() flags and ftruncate() on an open descriptor.
*
* Usage:
* fsops_helper noreplace <old> <new> renameat2 with RENAME_NOREPLACE
* fsops_helper exchange <a> <b> renameat2 with RENAME_EXCHANGE
* fsops_helper ftruncate <file> <len> ftruncate an open fd, print new size
*
* Exit codes: 0 = success, 2 = syscall failed (errno printed), 3 = usage.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
if (argc != 4) {
fprintf(stderr, "usage: %s noreplace|exchange|ftruncate <arg> <arg>\n",
argv[0]);
return 3;
}

if (!strcmp(argv[1], "noreplace") || !strcmp(argv[1], "exchange")) {
#ifdef __linux__
unsigned int flags = !strcmp(argv[1], "noreplace") ?
RENAME_NOREPLACE : RENAME_EXCHANGE;

if (renameat2(AT_FDCWD, argv[2], AT_FDCWD, argv[3], flags) < 0) {
printf("%s\n", strerror(errno));
return 2;
}
return 0;
#else
/* The integration tests only run on Linux; keep the helper
* compiling on the other platforms. */
fprintf(stderr, "rename flags are not supported on this platform\n");
return 3;
#endif
}

if (!strcmp(argv[1], "ftruncate")) {
struct stat st;
off_t len = strtoll(argv[3], NULL, 10);
int fd = open(argv[2], O_RDWR);

if (fd < 0 || ftruncate(fd, len) < 0 || fstat(fd, &st) < 0) {
printf("%s\n", strerror(errno));
return 2;
}
printf("%lld\n", (long long)st.st_size);
close(fd);
return 0;
}

fprintf(stderr, "unknown command: %s\n", argv[1]);
return 3;
}
Loading
Loading