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 0Each 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 testFollowing 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");
#endifTest 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_securebootis set, on enabled SecureBoot - if
.skip_in_lockdownis set, on lockdown - if
.skip_in_compatis 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 withTCONFif 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 withTBROKif the file does not existTST_SR_TCONF_MISSING: End test withTCONFif the file does not existTST_SR_SKIP_MISSING: Continue without saving the file if it does not existTST_SR_TBROK_RO: End test withTBROKif the file is read-onlyTST_SR_TCONF_RO: End test withTCONFif 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_ROTST_SR_TBROK: Equivalent toTST_SR_TBROK_MISSING | TST_SR_TBROK_ROTST_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_NONEpage - 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_LIBACLHAVE_LIBCRYPTOHAVE_LIBMNL
HAVE_LIBCAP/HAVE_SYS_CAPABILITY_HHAVE_LIBAIO/HAVE_LIBAIO_H
- remove old API
- more documentation on tst_test features
- more documentation on cross-compiling
- CI configuration