LTP is a lightweight and versatile testing framework based on LTP library and designed specifically for C applications on the Linux platform. With an emphasis on simplicity and ease of use, LTP provides developers with a powerful toolset for creating and executing test cases, both for Kernel and regular C applications.
Here you can see a short example:
#include <tst_test.h>
void setup(void) {
// your setup code goes here
tst_res(TINFO, "example setup");
}
void cleanup(void) {
// your cleanup code goes here
tst_res(TINFO, "example cleanup");
}
void run(void) {
// your test code goes here
tst_res(TPASS, "Doing hardly anything is easy");
}
static struct tst_test test = {
.test_all = run,
.setup = setup,
.cleanup = cleanup,
};
Now we just need to compile and run:
gcc -lltp example.c -o example
./example
../lib/tst_test.c:1690: TINFO: LTP version: 20230929-148-g121b0e2ce
../lib/tst_test.c:1576: TINFO: Timeout per run is 0h 00m 30s
example.c:6: TINFO: example setup
example.c:18: TPASS: example test passed
example.c:12: TINFO: example cleanup
Summary:
passed 1
failed 0
broken 0
skipped 0
warnings 0
Each test has some default options which are linked into the test. They can be
seen with ./example -h
command.
LTP framework is using Meson as the main build system. Following commands show how to use it in order to build/install/test the LTP framework:
# prepare build directory
meson setup builddir && cd builddir
# compile library
meson compile
# compile shared library
meson configure -Ddefault_library=shared
meson compile
# install library
meson install
# running library tests
meson configure -Dbuild_tests=true
meson test
Following paragraph shows available features in the LTP core framework.
Inside the framework we have many ways to send messages without stopping the
test. All of them can be sent using tst_res
function with the following flags:
TINFO
: information messageTWARN
: warning messageTPASS
: PASS messageTFAIL
: FAIL message
When TERRNO
is used in combination with one of the flags, framework will
append message with errno
at the end.
All tests have to send at least a TPASS
or TFAIL
message before the end
of test execution. An error will be shown as remainder otherwise.
static void run(void)
{
tst_res(TINFO, "This is an information message");
tst_res(TWARN, "This is a warning message");
tst_res(TPASS, "This is a pass message");
tst_res(TFAIL, "This is a fail message");
// TERRNO can be used by all flags above
tst_res(TINFO | TERRNO, "This is a message with errno at the end");
}
Any time we need to stop the test because something broke during the execution,
we can use tst_brk
function to send a broken error via TBROK
flag.
The test will immediately stop.
When TERRNO
is used in combination with TBROK
, the framework will print
errno
that caused the failure.
static void run(void)
{
// if something is broken raise an error and stop the test
tst_brk(TBROK, "Something is broken. Stop the test.");
tst_brk(TBROK | TERRNO, "Something is broken. Print errno");
}
Each time we want to end the test because it's not satisfying our needs, we can
use tst_brk
function to send a configuration error via TCONF
flag.
Test will immediately stop.
static void setup(void)
{
// my requirements are not satisfied, so stop the test with message
tst_brk(TCONF, "Configuration problem. Stop the test.");
}
static struct tst_test test = {
.setup = setup,
.test_all = run,
};
The framework provides a few macros that can be used to automatically check if
a specific function has passed or failed. These macros take a function call as
the first parameter and a printf-like format string and parameters as well.
These test macros then expand to a code that runs the call, checks the return
value and errno
and reports the test result.
static void run(void)
{
...
TST_EXP_PASS(stat(fname, &statbuf), "stat(%s, ...)", fname);
if (!TST_PASS)
return;
...
}
The TST_EXP_PASS()
can be used for calls that return -1 on failure and 0 on
success. It will check for the return value and reports failure if the return
value is not equal to 0. The call also sets the TST_PASS
variable to 1 if
the call succeeeded.
As seen above, this and similar macros take optional variadic arguments. These begin with a format string and then appropriate values to be formatted.
static void run(void)
{
...
TST_EXP_FD(open(fname, O_RDONLY), "open(%s, O_RDONLY)", fname);
SAFE_CLOSE(TST_RET);
...
}
The TST_EXP_FD()
is the same as TST_EXP_PASS()
the only difference is that
the return value is expected to be a file descriptor so the call passes if
positive integer is returned.
static void run(void)
{
...
TST_EXP_FAIL(stat(fname, &statbuf), ENOENT, "stat(%s, ...)", fname);
...
}
The TST_EXP_FAIL()
is similar to TST_EXP_PASS()
but it fails the test if
the call haven't failed with -1 and errno
wasn't set to the expected one
passed as the second argument.
static void run(void)
{
...
TST_EXP_FAIL2(msgget(key, flags), EINVAL, "msgget(%i, %i)", key, flags);
...
}
The TST_EXP_FAIL2()
is the same as TST_EXP_FAIL()
except the return value is
expected to be non-negative integer if call passes.
TST_EXP_FAIL_SILENT()
and TST_EXP_FAIL2_SILENT()
variants are less verbose
and do not print TPASS
messages when SCALL fails as expected.
TEST(socket(AF_INET, SOCK_RAW, 1));
if (TST_RET > -1) {
tst_res(TFAIL, "Created raw socket");
SAFE_CLOSE(TST_RET);
} else if (TST_ERR != EPERM) {
tst_res(TFAIL | TTERRNO,
"Failed to create socket for wrong reason");
} else {
tst_res(TPASS | TTERRNO, "Didn't create raw socket");
}
The TST_*
macro sets TST_RET
to its argument's return value and TST_ERR
to
errno
+ The TTERNO
flag can be used to print the error number's symbolic
value.
No library function or macro, except those in tst_test_macros.h
, will
write to these variables. So their values will not be changed unexpectedly.
TST_EXP_POSITIVE(wait(&status));
if (!TST_PASS)
return;
If the return value of wait
is positive or zero, this macro will print a pass
result and set TST_PASS
appropriately. If the return value is negative, then
it will print fail. There are many similar macros to those shown here, please
see tst_test_macros.h
.
TST_EXP_EQ_LI(val1, val2);
TST_EXP_EQ_UI(val1, val2);
TST_EXP_EQ_SZ(val1, val2);
TST_EXP_EQ_SSZ(val1, val2);
/* Use as */
TST_EXP_EQ_LI(sig_caught, SIGCHLD);
Set of macros for different integer type comparsions. These macros print the variable names as well as values in both pass and fail scenarios.
Some of the following utilities can be used to obtain information during tests execution.
const char *tst_strsig(int sig);
Return the given signal number's corresponding string.
const char *tst_strerrno(int err);
Return the given errno number's corresponding string. Using this function to
translate errno
values to strings is preferred. You should not use the
strerror()
function in the testcases.
const char *tst_strstatus(int status);
Returns string describing the status as returned by wait()
. This function is
not thread safe.
void tst_set_max_runtime(int max_runtime);
Allows for setting max_runtime per test iteration dynamically in the test 'setup()', the timeout is specified in seconds. There are a few testcases whose runtime can vary arbitrarily, these can disable timeouts by setting it to TST_UNLIMITED_RUNTIME.
void tst_flush(void);
Flush output streams, handling errors appropriately.
This function is rarely needed when you have to flush the output streams
before calling fork()
or clone()
. Note that the SAFE_FORK()
and
SAFE_CLONE()
calls this function automatically. See 2.4 FILE buffers and
fork()
for explanation why is this needed.
Sometimes we need to define a simple binary, without linking to the tst_test
struct definition, but using the framework functionalities, such as macros,
broken errors, etc. This is common in such cases where we need to call an
external binary that has to perform specific operations and we don't want to
waste time redefining features implemented inside the framework. To do so,
TST_NO_DEFAULT_MAIN
can be used.
#define TST_NO_DEFAULT_MAIN
#include "tst_test.h"
int main(void)
{
tst_res(TPASS, "Child passed!");
return 0;
}
In some cases we need to skip the entire test if system does not satisfy
our needs. It can be the case of a test that requires to be run on x86. In this
case, TST_TEST_TCONF
macro can be used.
#include <tst_test.h>
#if defined(__i386__) || defined(__x86_64__)
static void run(void)
{
// my test code
}
static struct tst_test test = {
.test_all = run,
};
#else
TST_TEST_TCONF("Test supported only on x86");
#endif
Test options can be customized assigning .options
argument to the tst_test
struct definition.
static struct tst_test test = {
.test_all = run,
.options = (struct tst_option[]) {
{"v", &verbose, "Verbose output"},
{"s", &size, "Size of the file to generate"},
{},
},
};
When we need to create files or directories, we can use a temporary folder so
we won't generate data in the current directory.
This can be achieved setting .needs_tmpdir
flag.
The framework will take care to create a temporary directory before test, to
move inside it and to delete it once test is completed.
static struct tst_test test = {
.test_all = run,
.needs_tmpdir = 1,
};
Some operations inside test might require root. To ensure that we are running
the test as root user, we can set .needs_root
.
If test will be executed as normal user, the framework will send a configuration
error and stop the test.
static struct tst_test test = {
.test_all = run,
.needs_root = 1,
};
If we want that our test never exceed a specific execution time, we can set
.max_runtime
flag in seconds. At the end of this time, test will be killed
by the framework raising a TBROK
error.
static struct tst_test test = {
.test_all = run,
// 1 hour max execution
.max_runtime = 3600,
};
Some tests require to spawn one or multiple children using SAFE_FORK()
macro.
The framework automatically handles spawned children inside the test, ensuring
that all of them will be completed before the end of the test. This feature can
be activated setting .forks_child
flag.
When SAFE_FORK()
is used, the framework will remind that the flag has to be
set.
static struct tst_test test = {
.test_all = run,
.forks_child = 1,
};
When using TST_CHECKPOINT_*
macros, .needs_checkpoints
flag has to be set.
In this way, futex will be released just before ending of the test.
When TST_CHECKPOINT_
macros are used, the framework will remind that the
flag has to be set.
static struct tst_test test = {
.test_all = run,
.needs_checkpoints = 1,
};
If .needs_device
flag is set, the tst_device
structure is initialized with
path the test device and default filesystem to be used.
static void run(void)
{
tst_res(TINFO, "My device is %s", tst_device->dev);
// my test code using device
}
static struct tst_test test = {
.test_all = run,
.needs_device = 1,
};
If .mount_device
is set, the device is mounted at .mntpoint
which is used
to pass a directory name that will be created and used as mount destination.
You can pass additional flags and data to the mount command via .mnt_flags
and .mnt_data
pointers. Note that .mount_device
implies .needs_device
and .format_device
so there is no need to set the later two. To pass
additional options to mkfs.$fs
utility, .dev_fs_type
and dev_extra_opts
can be used.
Device filesystem can be defined by .dev_fs_type
.
If .dev_min_size
is set, framework will check that device has that minimum
size in megabytes.
static void run(void)
{
tst_res(TINFO,
"My device %s is formatted with %s filesystem",
tst_device->dev,
tst_device->fs_type);
// my test code using tst_device
}
static struct tst_test test = {
.test_all = run,
.needs_device = 1,
.mount_device = 1,
.format_device = 1,
.dev_min_size = 2, // reserve 2MB device
.dev_fs_type = "xfs",
.mntpoint = "mntpoint",
.mnt_data = "usrquota",
.mnt_flags = MS_STRICTATIME,
};
IMPORTANT: Close all file descriptors (that point to files in test temporary directory, even the unlinked ones) either in the 'test()' function or in the test 'cleanup()' otherwise the test may break temporary directory removal on NFS (look for "NFS silly rename").
If .needs_overlay
is set, mount point will use an overlay fs.
static struct tst_test test = {
.test_all = run,
.needs_device = 1,
.mount_device = 1,
.format_device = 1,
.mntpoint = "mntpoint",
.needs_overlay = 1,
};
If .needs_rofs
is set, read-only filesystem is mounted at .mntpoint
. This
one is supposed to be used for EROFS
tests.
static struct tst_test test = {
.test_all = run,
.needs_device = 1,
.mount_device = 1,
.format_device = 1,
.mntpoint = "mntpoint",
.needs_rofs = 1,
};
If .needs_hugelbfs
is set, the hugetlbfs will be mounted at .mntpoint
.
static struct tst_test test = {
.test_all = run,
.needs_device = 1,
.mount_device = 1,
.format_device = 1,
.mntpoint = "mntpoint",
.needs_hugelbfs = 1,
};
When the .all_filesystems
flag is set the .skip_filesystems
list is passed
to the function that detects supported filesystems any listed filesystem is
not included in the resulting list of supported filesystems.
If test needs to adjust expectations based on filesystem type it's also possible to detect filesystem type at the runtime. This is preferably used when only subset of the test is not applicable for a given filesystem.
static struct tst_test test = {
.test_all = run,
.all_filesystems = 1,
.skip_filesystems = (const char *const []) {
"exfat",
"tmpfs",
"ramfs",
"nfs",
"vfat",
NULL
},
};
NOTE that ext2, ext3 or ext4 in .skip_filesystems
on tests which does
not use .all_filesystems
needs to be defined as 'ext2/ext3/ext4'. The
reason is that it is hard to detect used filesystem due to overlapping the
functionality.
OTOH tests which use .skip_filesystems
with .all_filesystems
can skip
only filesystems which are actually used in .all_filesystems
: ext2, ext3,
ext4, xfs, btrfs, vfat, exfat, ntfs, tmpfs (defined in fs_type_whitelist[]
).
It does not make sense to list other filesystems.
To avoid using kernels which are too old for the test, we can use .min_kver
attribute in the tst_test
struct definition. If minimum kernel is not
satisfied, a configuration error will be shown.
static struct tst_test test = {
.test_all = run,
.min_kver = "4.11",
};
Test's supported architectures can be defined using the .supported_archs
flag
inside tst_test
struct definition. If current architecture is not supported,
a configuration error will be shown.
static struct tst_test test = {
.test_all = run,
.supported_archs = (const char *const []) {
"x86_64",
"x86",
NULL
}
}
Test can be skipped on various conditions:
- if
.skip_in_secureboot
is set, on enabled SecureBoot - if
.skip_in_lockdown
is set, on lockdown - if
.skip_in_compat
is set, in 32-bit compat mode
static struct tst_test test = {
.test_all = run,
.skip_in_secureboot = 1,
.skip_in_lockdown = 1,
.skip_in_compat = 1,
}
Some tests require more than specific number of CPU. It can be defined with
.min_cpus
.
static struct tst_test test = {
.test_all = run,
.min_cpus = 2,
}
Some tests require a minimum amount of memory or swap.
To make sure that test will run only on systems with more than minimal required
amount of RAM set .min_mem_avail
. Similarily for tests that require certain
amount of free Swap use .min_swap_avail
. Both flags are expressed in MB.
static struct tst_test test = {
.test_all = run,
// check that minimum memory is 20 MB
.min_mem_avail = 20,
// check that minimum swap is 2 MB
.min_swap_avail = 2,
}
Many of the LTP tests need to use hugepage in their testing, this allows the
test can reserve hugepages from system via .hugepages = {xx, TST_REQUEST}
.
We achieved two policies for reserving hugepages:
-
TST_REQUEST
: It will try the best to reserve available huge pages and return the number of available hugepages intst_hugepages
, which may be 0 if hugepages are not supported at all. -
TST_NEEDS
: This is an enforced requirement, LTP should strictly do hpages applying and guarantee the HugePages_Free no less than pages which makes that test can use these specified numbers correctly. Otherwise, test exits withTCONF
if the attempt to reserve hugepages fails or reserves less than requested.
With success test stores the reserved hugepage number in tst_hugepages
. For
system without hugetlb supporting, variable tst_hugepages
will be set to 0.
If the hugepage number needs to be set to 0 on supported hugetlb system, please
use .hugepages = {TST_NO_HUGEPAGES}
.
Also, we do cleanup and restore work for the hpages resetting automatically.
static void run(void)
{
...
if (tst_hugepages == test.hugepages.number)
TEST(do_hpage_test);
else
...
...
}
struct tst_test test = {
.test_all = run,
.hugepages = {2, TST_REQUEST},
};
In some cases we need to detect whether a testcase triggers a kernel warning,
bug or oops. The .taint_check
flag can be used to detect them.
List of supported taint flags:
TST_TAINT_G
: a module with non-GPL license loadedTST_TAINT_F
: a module was force-loadedTST_TAINT_S
: SMP with Non-SMP kernelTST_TAINT_R
: module force unloadedTST_TAINT_M
: machine check error occurredTST_TAINT_B
: page-release function found bad pageTST_TAINT_U
: user requested taint flagTST_TAINT_D
: kernel died recently - OOPS or BUGTST_TAINT_A
: ACPI table has been overwrittenTST_TAINT_W
: a warning has been issued by kernelTST_TAINT_C
: driver from drivers/staging was loadedTST_TAINT_I
: working around BIOS/Firmware bugTST_TAINT_O
: out of tree module loadedTST_TAINT_E
: unsigned module was loadedTST_TAINT_L
: A soft lock-up has previously occurredTST_TAINT_K
: kernel has been live-patchedTST_TAINT_X
: auxiliary taint, for distro's useTST_TAINT_T
: kernel was built with the struct randomization plugin
void run(void)
{
...
if (tst_taint_check() != 0)
tst_res(TFAIL, "kernel has issues");
else
tst_res(TPASS, "kernel seems to be fine");
}
static struct tst_test test = {
.test_all = run,
.tint_check = TST_TAINT_W | TST_TAINT_D,
}
In some cases kernel has several very similar syscalls that do either the same or very similar job. This is most noticeable on i386 where we commonly have two or three syscall versions. That is because i386 was first platform that Linux was developed on and because of that most mistakes in API happened there as well. However this is not limited to i386 at all, it's quite common that version two syscall has added missing flags parameters or so.
In such cases it does not make much sense to copy&paste the test code over and over, rather than that the test library provides support for test variants. The idea behind test variants is simple, we run the test several times each time with different syscall variant.
The implementation consist of test_variants
integer that, if set, denotes
number of test variants. The test is then forked and executed test_variants
times each time with different value in global tst_variant
variable.
static int do_foo(void)
{
switch (tst_variant) {
case 0:
return foo();
case 1:
return syscall(__NR_foo);
}
return -1;
}
static void run(void)
{
TEST(do_foo);
}
static void setup(void)
{
switch (tst_variant) {
case 0:
tst_res(TINFO, "Testing foo variant 1");
break;
case 1:
tst_res(TINFO, "Testing foo variant 2");
break;
}
}
struct tst_test test = {
.setup = setup,
.test_all = run,
.test_variants = 2,
};
Some tests may need specific kernel drivers, either compiled in, or built
as a module. If .needs_drivers
points to a NULL
terminated array of kernel
module names these are all checked and the test exits with TCONF
on the
first missing driver.
The detection is based on reading modules.dep
and modules.builtin
files
generated by kmod. The check is skipped on Android.
static struct tst_test test = {
.test_all = run,
.needs_drivers = (const char *const []) {
"zram",
NULL
},
};
Framework can be instructed to save and restore value of specified (/proc|sys)
files. This is achieved by initialized tst_test struct field 'save_restore'.
It is a NULL-terminated array of struct tst_path_val
where each
tst_path_val.path
represents a file, whose value is saved at the beginning and
restored at the end of the test.
If non-NULL string is passed in tst_path_val.val
, it is written to the
respective file at the beginning of the test. Only the first line of a specified
file is saved and restored.
By default, the test will end with TCONF
if the file is read-only or does not
exist. If the optional write of new value fails, the test will end with TBROK
.
This behavior can be changed using tst_path_val.flags
:
TST_SR_TBROK_MISSING
: End test withTBROK
if the file does not existTST_SR_TCONF_MISSING
: End test withTCONF
if the file does not existTST_SR_SKIP_MISSING
: Continue without saving the file if it does not existTST_SR_TBROK_RO
: End test withTBROK
if the file is read-onlyTST_SR_TCONF_RO
: End test withTCONF
if the file is read-onlyTST_SR_SKIP_RO
: Continue without saving the file if it is read-onlyTST_SR_IGNORE_ERR
: Ignore errors when writing new value into the file
Common flag combinations also have shortcuts:
TST_SR_TCONF
: Equivalent toTST_SR_TCONF_MISSING | TST_SR_TCONF_RO
TST_SR_TBROK
: Equivalent toTST_SR_TBROK_MISSING | TST_SR_TBROK_RO
TST_SR_SKIP
: Equivalent toTST_SR_SKIP_MISSING | TST_SR_SKIP_RO
restore
is always strict and will TWARN
if it encounters any error.
static struct tst_test test = {
...
.setup = setup,
.save_restore = (const struct tst_path_val[]) {
{"/proc/sys/kernel/core_pattern", NULL, TST_SR_TCONF},
{"/proc/sys/user/max_user_namespaces", NULL, TST_SR_SKIP},
{"/sys/kernel/mm/ksm/run", "1", TST_SR_TBROK},
{}
},
};
Generally testcases should attempt to autodetect as much kernel features as
possible based on the currently running kernel. We do have tst_check_driver()
to check if functionality that could be compiled as kernel module is present
on the system, disabled syscalls can be detected by checking for ENOSYS
errno etc.
However, in rare cases core kernel features couldn't be detected based on the
kernel userspace API and we have to resort to parse the kernel .config
.
For this cases the test should set the NULL-terminated .needs_kconfigs
array of boolean expressions with constraints on the kconfig variables. The
boolean expression consits of variables, two binary operations &
and |
,
negation !
and correct sequence of parentesis ()
. Variables are expected
to be in a form of CONFIG_FOO[=bar]
.
The test will continue to run if all expressions are evaluated to True
.
Missing variable is mapped to False
as well as variable with different than
specified value, e.g. CONFIG_FOO=bar
will evaluate to False
if the value
is anything else but bar
. If config variable is specified as plain
CONFIG_FOO
, it's evaluated to true it's set to any value (typically =y or =m).
static struct tst_test test = {
.test_all = run,
.needs_kconfigs = (const char *[]) {
"CONFIG_VETH",
"CONFIG_USER_NS=y",
"CONFIG_NET_NS=y",
"CONFIG_NET_SCH_HTB",
"CONFIG_NET_CLS_TCINDEX",
NULL
},
};
The test library supports guarded buffers, which are buffers allocated so that:
- The end of the buffer is followed by a
PROT_NONE
page - The remainder of the page before the buffer is filled with random canary data
Which means that the any access after the buffer will yield a Segmentation fault
or EFAULT
depending on if the access happened in userspace or the kernel
respectively. The canary before the buffer will also catch any write access
outside of the buffer.
The purpose of the feature is to catch off-by-one bugs which happens when buffers and structures are passed to syscalls. New tests should allocate guarded buffers for all data passed to the tested syscall which are passed by a pointer.
static struct timex *buf;
static void run(void)
{
// normally use buf as it is
}
static struct tst_test test = {
.test_all = run,
.bufs = (struct tst_buffers []) {
{&buf, .size = sizeof(*buf)},
{},
},
};
Guarded buffers can be allocated on runtime in a test setup()
by a
tst_alloc()
or by tst_strdup()
as well as by filling up the .bufs
array in
the tst_test
struct.
So far the tst_test
struct supports allocating either a plain buffer by
setting up the size or struct iovec
, which is allocated recursively including
the individual buffers as described by an -1
terminated array of buffer
sizes.
Some tests may require the presence or absence of particular
capabilities. Using the API provided by tst_capability.h
the test author can
try to ensure that some capabilities are either present or absent during the
test.
For example; below we try to create a raw socket, which requires
CAP_NET_ADMIN
. During setup we should be able to do it, then during run it
should be impossible. The LTP capability library will check before setup that
we have this capability, then after setup it will drop it.
#include "tst_test.h"
#include "tst_capability.h"
#include "tst_safe_net.h"
#include "lapi/socket.h"
static void run(void)
{
TEST(socket(AF_INET, SOCK_RAW, 1));
if (TST_RET > -1) {
tst_res(TFAIL, "Created raw socket");
} else if (TST_ERR != EPERM) {
tst_res(TFAIL | TTERRNO,
"Failed to create socket for wrong reason");
} else {
tst_res(TPASS | TTERRNO, "Didn't create raw socket");
}
}
static void setup(void)
{
TEST(socket(AF_INET, SOCK_RAW, 1));
if (TST_RET < 0)
tst_brk(TCONF | TTERRNO, "We don't have CAP_NET_RAW to begin with");
SAFE_CLOSE(TST_RET);
}
static struct tst_test test = {
.setup = setup,
.test_all = run,
.caps = (struct tst_cap []) {
TST_CAP(TST_CAP_REQ, CAP_NET_RAW),
TST_CAP(TST_CAP_DROP, CAP_NET_RAW),
{}
},
};
Required commands can be checked with .needs_cmds
, which points to a NULL
terminated array of strings such as:
.needs_cmds = (const char *const []) {
"useradd",
"userdel",
NULL
},
Also can check required command version whether is satisfied by using
needs_cmds
such as:
.needs_cmds = (const char *const []) {
"mkfs.ext4 >= 1.43.0",
NULL
},
Test tags are name-value pairs that can hold any test metadata.
We have additional support for CVE entries, git commit in mainline kernel, stable kernel or glibc git repository. If a test is a regression test it should include these tags. They are printed when test fails.
CVE, mainline and stable kernel git commits in a regression test for a kernel bug:
struct tst_test test = {
.test_all = run,
.tags = (const struct tst_tag[]) {
{"linux-git", "9392a27d88b9"},
{"linux-git", "ff002b30181d"},
{"known-fail", "ustat() is known to fail with EINVAL on Btrfs"},
{"linux-stable-git", "c4a23c852e80"},
{"CVE", "2020-29373"},
{}
}
};
Glibc and musl git commits in a regression test for glibc
and musl
bugs:
struct tst_test test = {
.test_all = run,
.tags = (const struct tst_tag[]) {
{"glibc-git", "574500a108be"},
{"musl-git", "fa4a8abd06a4"},
{}
}
};
The following paragraph will show what's changed between LTP and LTP core.
- libipc
- libmsgctl
- libnewipc
- sigwait
- libswap
- vdso_helpers
When moving to meson build system, a few modifications have been made, some variables have been dropped and some autotools function have been ignored. And this is something you should know before compiling (for example) testcases created with the previous LTP library.
LTP_CHECK_ACL_SUPPORT
: used by testsLTP_CHECK_CC_WARN_OLDSTYLE
: not neededLTP_CHECK_CRYPTO
: used by testsLTP_CHECK_KERNEL_DEVEL
: compile kernel, we don't need itLTP_CHECK_LIBMNL
: used by testsLTP_CHECK_LINUX_PTRACE
: only used by ptrace testsLTP_CHECK_NOMMU_LINUX
: no idea how to use itLTP_CHECK_SELINUX
: used by testsLTP_CHECK_SYSCALL_FCNTL
: used by testsLTP_CHECK_SYSCALL_NUMA
: used by testsLTP_CHECK_SYSCALL_SIGNALFD
: used by testsLTP_DETECT_HOST_CPU
: meson has cross-compilation mechanism
HAVE_LIBACL
HAVE_LIBCRYPTO
HAVE_LIBMNL
HAVE_LIBCAP
/HAVE_SYS_CAPABILITY_H
HAVE_LIBAIO
/HAVE_LIBAIO_H
- remove old API
- more documentation on tst_test features
- more documentation on cross-compiling
- CI configuration