All of lore.kernel.org
 help / color / mirror / Atom feed
* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
@ 2021-05-26 15:49 Petr Vorel
  2021-05-27  3:31 ` Li Wang
  2021-05-27 12:28 ` Cyril Hrubis
  0 siblings, 2 replies; 10+ messages in thread
From: Petr Vorel @ 2021-05-26 15:49 UTC (permalink / raw)
  To: ltp

Test Writing Guidelines wiki page is too long, thus split it
into 3 parts:

1) generic part (only first chapter, the same URL)
2) C test API (2.2 chapter, 4. Common problems)
3) shell test API

Unfortunately this breaks users' bookmarks.

Start numbering in headers from 1 on each page (links are broken
anyway).

NOTE: in order to have '...' formatting as code,
main header ====== was needed to add on the page.

Signed-off-by: Petr Vorel <pvorel@suse.cz>
---
See it:
https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
https://github.com/pevik/ltp/wiki/C-Test-API
https://github.com/pevik/ltp/wiki/Shell-Test-API

Kind regards,
Petr

 doc/c-test-api.txt              | 2241 +++++++++++++++++++++++
 doc/shell-test-api.txt          |  740 ++++++++
 doc/test-writing-guidelines.txt | 2993 +------------------------------
 3 files changed, 2993 insertions(+), 2981 deletions(-)
 create mode 100644 doc/c-test-api.txt
 create mode 100644 doc/shell-test-api.txt

diff --git a/doc/c-test-api.txt b/doc/c-test-api.txt
new file mode 100644
index 000000000..8caf9ec67
--- /dev/null
+++ b/doc/c-test-api.txt
@@ -0,0 +1,2241 @@
+LTP C Test API
+==============
+
+1 Writing a test in C
+---------------------
+
+1.1 Basic test structure
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Let's start with an example, following code is a simple test for a 'getenv()'.
+
+[source,c]
+-------------------------------------------------------------------------------
+/*
+ * This is test for basic functionality of getenv().
+ *
+ *  - create an env variable and verify that getenv() can get get it
+ *  - call getenv() with nonexisting variable name, check that it returns NULL
+ */
+
+#include "tst_test.h"
+
+#define ENV1 "LTP_TEST_ENV"
+#define ENV2 "LTP_TEST_THIS_DOES_NOT_EXIST"
+#define ENV_VAL "val"
+
+static void setup(void)
+{
+	if (setenv(ENV1, ENV_VAL, 1))
+		tst_brk(TBROK | TERRNO, "setenv() failed");
+}
+
+static void test(void)
+{
+	char *ret;
+
+	ret = getenv(ENV1);
+
+	if (!ret) {
+		tst_res(TFAIL, "getenv(" ENV1 ") = NULL");
+		goto next;
+	}
+
+	if (!strcmp(ret, ENV_VAL)) {
+		tst_res(TPASS, "getenv(" ENV1 ") = '"ENV_VAL "'");
+	} else {
+		tst_res(TFAIL, "getenv(" ENV1 ") = '%s', expected '"
+		               ENV_VAL "'", ret);
+	}
+
+next:
+	ret = getenv(ENV2);
+
+	if (ret)
+		tst_res(TFAIL, "getenv(" ENV2 ") = '%s'", ret);
+	else
+		tst_res(TPASS, "getenv(" ENV2 ") = NULL");
+}
+
+static struct tst_test test = {
+	.test_all = test,
+	.setup = setup,
+};
+-------------------------------------------------------------------------------
+
+Each test includes the 'tst_test.h' header and must define the 'struct
+tst_test test' structure.
+
+The overall test initialization is done in the 'setup()' function.
+
+The overall cleanup is done in a 'cleanup()' function. Here 'cleanup()' is
+omitted as the test does not have anything to clean up. If cleanup is set in
+the test structure it's called on test exit just before the test library
+cleanup. That especially means that cleanup can be called at any point in a
+test execution. For example even when a test setup step has failed, therefore
+the 'cleanup()' function must be able to cope with unfinished initialization,
+and so on.
+
+The test itself is done in the 'test()' function. The test function must work
+fine if called in a loop.
+
+There are two types of a test function pointers in the test structure. The
+first one is a '.test_all' pointer that is used when test is implemented as a
+single function. Then there is a '.test' function along with the number of
+tests '.tcnt' that allows for more detailed result reporting. If the '.test'
+pointer is set the function is called '.tcnt' times with an integer parameter
+in range of [0, '.tcnt' - 1].
+
+IMPORTANT: Only one of '.test' and '.test_all' can be set at a time.
+
+Each test has a default timeout set to 300s. The default timeout can be
+overridden by setting '.timeout' in the test structure or by calling
+'tst_set_timeout()' in the test 'setup()'. There are a few testcases whose run
+time may vary arbitrarily, for these timeout can be disabled by setting it to
+-1.
+
+Test can find out how much time (in seconds) is remaining to timeout,
+by calling 'tst_timeout_remaining()'.
+
+LAPI headers
+++++++++++++
+
+Use our LAPI headers ('include "lapi/foo.h"') to keep compatibility with old
+distributions. LAPI header should always include original header. Older linux
+headers were problematic, therefore we preferred to use libc headers. There are
+still some bugs when combining certain glibc headers with linux headers, see
+https://sourceware.org/glibc/wiki/Synchronizing_Headers.
+
+A word about the cleanup() callback
++++++++++++++++++++++++++++++++++++
+
+There are a few rules that needs to be followed in order to write correct
+cleanup() callback.
+
+1. Free only resources that were initialized. Keep in mind that callback can
+   be executed at any point in the test run.
+
+2. Make sure to free resources in the reverse order they were
+   initialized. (Some of the steps may not depend on others and everything
+   will work if there were swapped but let's keep it in order.)
+
+The first rule may seem complicated at first however, on the contrary, it's
+quite easy. All you have to do is to keep track of what was already
+initialized. For example file descriptors needs to be closed only if they were
+assigned a valid file descriptor. For most of the things you need to create
+extra flag that is set right after successful initialization though. Consider,
+for example, test setup below.
+
+We also prefer cleaning up resources that would otherwise be released on the
+program exit. There are two main reasons for this decision. Resources such as
+file descriptors and mmaped memory could block umounting a block device in
+cases where the test library has mounted a filesystem for the test temporary
+directory. Not freeing allocated memory would upset static analysis and tools
+such as valgrind and produce false-positives when checking for leaks in the
+libc and other low level libraries.
+
+[source,c]
+-------------------------------------------------------------------------------
+static int fd0, fd1, mount_flag;
+
+#define MNTPOINT "mntpoint"
+#define FILE1 "mntpoint/file1"
+#define FILE2 "mntpoint/file2"
+
+static void setup(void)
+{
+	SAFE_MKDIR(MNTPOINT, 0777);
+	SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL);
+	SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, 0);
+	mount_flag = 1;
+
+	fd0 = SAFE_OPEN(cleanup, FILE1, O_CREAT | O_RDWR, 0666);
+	fd1 = SAFE_OPEN(cleanup, FILE2, O_CREAT | O_RDWR, 0666);
+}
+-------------------------------------------------------------------------------
+
+In this case the 'cleanup()' function may be invoked when any of the 'SAFE_*'
+macros has failed and therefore must be able to work with unfinished
+initialization as well. Since global variables are initialized to zero we can
+just check that fd > 0 before we attempt to close it. The mount function
+requires extra flag to be set after device was successfully mounted.
+
+[source,c]
+-------------------------------------------------------------------------------
+static void cleanup(void)
+{
+	if (fd1 > 0)
+		SAFE_CLOSE(fd1);
+
+	if (fd0 > 0)
+		SAFE_CLOSE(fd0);
+
+	if (mount_flag && tst_umouont(MNTPOINT))
+		tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT);
+}
+-------------------------------------------------------------------------------
+
+IMPORTANT: 'SAFE_MACROS()' used in cleanup *do not* exit the test. Failure
+           only produces a warning and the 'cleanup()' carries on. This is
+	   intentional as we want to execute as much 'cleanup()' as possible.
+
+WARNING: Calling tst_brk() in test 'cleanup()' does not exit the test as well
+         and 'TBROK' is converted to 'TWARN'.
+
+NOTE: Creation and removal of the test temporary directory is handled in
+      the test library and the directory is removed recursively. Therefore
+      we do not have to remove files and directories in the test cleanup.
+
+1.2 Basic test interface
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+void tst_res(int ttype, char *arg_fmt, ...);
+-------------------------------------------------------------------------------
+
+Printf-like function to report test result, it's mostly used with ttype:
+
+|==============================
+| 'TPASS' | Test has passed.
+| 'TFAIL' | Test has failed.
+| 'TINFO' | General message.
+| 'TWARN' | Something went wrong but we decided to continue. Mostly used in cleanup functions.
+|==============================
+
+The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print
+'errno', 'TST_ERR' respectively.
+
+[source,c]
+-------------------------------------------------------------------------------
+void tst_brk(int ttype, char *arg_fmt, ...);
+-------------------------------------------------------------------------------
+
+Printf-like function to report error and exit the test, it can be used with ttype:
+
+|============================================================
+| 'TBROK' | Something has failed in test preparation phase.
+| 'TCONF' | Test is not appropriate for current configuration
+            (syscall not implemented, unsupported arch, ...)
+|============================================================
+
+The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print
+'errno', 'TST_ERR' respectively.
+
+There are also 'TST_EXP_*()' macros that can simplify syscall unit tests to a
+single line, use them whenever possible. 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.
+
+[source,c]
+-------------------------------------------------------------------------------
+static void test(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.
+
+[source,c]
+-------------------------------------------------------------------------------
+static void test(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.
+
+[source,c]
+-------------------------------------------------------------------------------
+static void test(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.
+
+[source,c]
+-------------------------------------------------------------------------------
+const char *tst_strsig(int sig);
+-------------------------------------------------------------------------------
+
+Return the given signal number's corresponding string.
+
+[source,c]
+-------------------------------------------------------------------------------
+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.
+
+[source,c]
+-------------------------------------------------------------------------------
+const char *tst_strstatus(int status);
+-------------------------------------------------------------------------------
+
+Returns string describing the status as returned by 'wait()'.
+
+WARNING: This function is not thread safe.
+
+[source,c]
+-------------------------------------------------------------------------------
+void tst_set_timeout(unsigned int timeout);
+-------------------------------------------------------------------------------
+
+Allows for setting timeout 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 -1.
+
+[source,c]
+-------------------------------------------------------------------------------
+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.
+
+1.3 Test temporary directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If '.needs_tmpdir' is set to '1' in the 'struct tst_test' unique test
+temporary is created and it's set as the test working directory. Tests *MUST
+NOT* create temporary files outside that directory. The flag is not needed to
+be set when use these flags: '.all_filesystems', '.format_device', '.mntpoint',
+'.mount_device' '.needs_checkpoints', '.needs_device', '.resource_file'
+(these flags imply creating temporary directory).
+
+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").
+
+1.4 Safe macros
+~~~~~~~~~~~~~~~
+
+Safe macros aim to simplify error checking in test preparation. Instead of
+calling system API functions, checking for their return value and aborting the
+test if the operation has failed, you just use corresponding safe macro.
+
+Use them whenever it's possible.
+
+Instead of writing:
+
+[source,c]
+-------------------------------------------------------------------------------
+	fd = open("/dev/null", O_RDONLY);
+	if (fd < 0)
+		tst_brk(TBROK | TERRNO, "opening /dev/null failed");
+-------------------------------------------------------------------------------
+
+You write just:
+
+[source,c]
+-------------------------------------------------------------------------------
+	fd = SAFE_OPEN("/dev/null", O_RDONLY);
+-------------------------------------------------------------------------------
+
+IMPORTANT: The SAFE_CLOSE() function also sets the passed file descriptor to -1
+           after it's successfully closed.
+
+They can also simplify reading and writing of sysfs files, you can, for
+example, do:
+
+[source,c]
+-------------------------------------------------------------------------------
+	SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%lu", &pid_max);
+-------------------------------------------------------------------------------
+
+See 'include/tst_safe_macros.h', 'include/tst_safe_stdio.h' and
+'include/tst_safe_file_ops.h' and 'include/tst_safe_net.h' for a complete list.
+
+1.5 Test specific command line options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+struct tst_option {
+        char *optstr;
+        char **arg;
+        char *help;
+};
+-------------------------------------------------------------------------------
+
+Test specific command line parameters can be passed with the 'NULL' terminated
+array of 'struct tst_option'. The 'optstr' is the command line option i.e. "o"
+or "o:" if option has a parameter. Only short options are supported. The 'arg'
+is where 'optarg' is stored upon match. If option has no parameter it's set to
+non-'NULL' value if option was present. The 'help' is a short help string.
+
+NOTE: The test parameters must not collide with common test parameters defined
+      in the library the currently used ones are +-i+, +-I+, +-C+, and +-h+.
+
+[source,c]
+-------------------------------------------------------------------------------
+int tst_parse_int(const char *str, int *val, int min, int max);
+int tst_parse_float(const char *str, float *val, float min, float max);
+-------------------------------------------------------------------------------
+
+Helpers for parsing the strings returned in the 'struct tst_option'.
+
+Both return zero on success and 'errno', mostly 'EINVAL' or 'ERANGE', on
+failure.
+
+Both functions are no-op if 'str' is 'NULL'.
+
+The valid range for result includes both 'min' and 'max'.
+
+.Example Usage
+[source,c]
+-------------------------------------------------------------------------------
+#include <limits.h>
+#include "tst_test.h"
+
+static char *str_threads;
+static int threads = 10;
+
+static struct tst_option options[] = {
+	{"t:", &str_threads, "Number of threads (default 10)"},
+	...
+	{NULL, NULL, NULL}
+};
+
+static void setup(void)
+{
+	if (tst_parse_int(str_threads, &threads, 1, INT_MAX))
+		tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
+
+	...
+}
+
+static void test_threads(void)
+{
+	...
+
+	for (i = 0; i < threads; i++) {
+		...
+	}
+
+	...
+}
+
+static struct tst_test test = {
+	...
+	.options = options,
+	...
+};
+-------------------------------------------------------------------------------
+
+
+1.6 Runtime kernel version detection
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Testcases for newly added kernel functionality require kernel newer than a
+certain version to run. All you need to skip a test on older kernels is to
+set the '.min_kver' string in the 'struct tst_test' to a minimal required
+kernel version, e.g. '.min_kver = "2.6.30"'.
+
+For more complicated operations such as skipping a test for a certain range
+of kernel versions, following functions could be used:
+
+[source,c]
+-------------------------------------------------------------------------------
+int tst_kvercmp(int r1, int r2, int r3);
+
+struct tst_kern_exv {
+        char *dist_name;
+        char *extra_ver;
+};
+
+int tst_kvercmp2(int r1, int r2, int r3, struct tst_kern_exv *vers);
+-------------------------------------------------------------------------------
+
+These two functions are intended for runtime kernel version detection. They
+parse the output from 'uname()' and compare it to the passed values.
+
+The return value is similar to the 'strcmp()' function, i.e. zero means equal,
+negative value means that the kernel is older than than the expected value and
+positive means that it's newer.
+
+The second function 'tst_kvercmp2()' allows for specifying per-vendor table of
+kernel versions as vendors typically backport fixes to their kernels and the
+test may be relevant even if the kernel version does not suggests so. See
+'testcases/kernel/syscalls/inotify/inotify04.c' for example usage.
+
+WARNING: The shell 'tst_kvercmp' maps the result into unsigned integer - the
+         process exit value.
+
+1.7 Fork()-ing
+~~~~~~~~~~~~~~
+
+Be wary that if the test forks and there were messages printed by the
+'tst_*()' interfaces, the data may still be in libc/kernel buffers and these
+*ARE NOT* flushed automatically.
+
+This happens when 'stdout' gets redirected to a file. In this case, the
+'stdout' is not line buffered, but block buffered. Hence after a fork content
+of the buffers will be printed by the parent and each of the children.
+
+To avoid that you should use 'SAFE_FORK()', 'SAFE_CLONE()' or 'tst_clone()'.
+
+IMPORTANT: You have to set the '.forks_child' flag in the test structure
+           if your testcase forks or calls 'SAFE_CLONE()'.
+
+1.8 Doing the test in the child process
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Results reported by 'tst_res()' are propagated to the parent test process via
+block of shared memory.
+
+Calling 'tst_brk()' causes child process to exit with non-zero exit value.
+Which means that it's safe to use 'SAFE_*()' macros in the child processes as
+well.
+
+Children that outlive the 'test()' function execution are waited for in the
+test library. Unclean child exit (killed by signal, non-zero exit value, etc.)
+will cause the main test process to exit with 'tst_brk()', which especially
+means that 'TBROK' propagated from a child process will cause the whole test
+to exit with 'TBROK'.
+
+If a test needs a child that segfaults or does anything else that cause it to
+exit uncleanly all you need to do is to wait for such children from the
+'test()' function so that it's reaped before the main test exits the 'test()'
+function.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+void tst_reap_children(void);
+-------------------------------------------------------------------------------
+
+The 'tst_reap_children()' function makes the process wait for all of its
+children and exits with 'tst_brk(TBROK, ...)' if any of them returned
+a non zero exit code.
+
+When using 'SAFE_CLONE' or 'tst_clone', this may not work depending on
+the parameters passed to clone. The following call to 'SAFE_CLONE' is
+identical to 'fork()', so will work as expected.
+
+[source,c]
+--------------------------------------------------------------------------------
+const struct tst_clone_args args = {
+	.exit_signal = SIGCHLD,
+};
+
+SAFE_CLONE(&args);
+--------------------------------------------------------------------------------
+
+If 'exit_signal' is set to something else, then this will break
+'tst_reap_children'. It's not expected that all parameters to clone will
+work with the LTP library unless specific action is taken by the test code.
+
+.Using 'tst_res()' from binaries started by 'exec()'
+[source,c]
+-------------------------------------------------------------------------------
+/* test.c */
+#define _GNU_SOURCE
+#include <unistd.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	char *const argv[] = {"test_exec_child", NULL};
+	char path[4096];
+
+	if (tst_get_path("test_exec_child", path, sizeof(path)))
+		tst_brk(TCONF, "Couldn't find test_exec_child in $PATH");
+
+	execve(path, argv, environ);
+
+	tst_res(TFAIL | TERRNO, "EXEC!");
+}
+
+static struct tst_test test = {
+	.test_all = do_test,
+	.child_needs_reinit = 1,
+};
+
+/* test_exec_child.c */
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+
+int main(void)
+{
+	tst_reinit();
+	tst_res(TPASS, "Child passed!");
+	return 0;
+}
+-------------------------------------------------------------------------------
+
+The 'tst_res()' function can be also used from binaries started by 'exec()',
+the parent test process has to set the '.child_needs_reinit' flag so that the
+library prepares for it and has to make sure the 'LTP_IPC_PATH' environment
+variable is passed down, then the very fist thing the program has to call in
+'main()' is 'tst_reinit()' that sets up the IPC.
+
+1.9 Fork() and Parent-child synchronization
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As LTP tests are written for Linux, most of the tests involve fork()-ing and
+parent-child process synchronization. LTP includes a checkpoint library that
+provides wait/wake futex based functions.
+
+In order to use checkpoints the '.needs_checkpoints' flag in the 'struct
+tst_test' must be set to '1', this causes the test library to initialize
+checkpoints before the 'test()' function is called.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+TST_CHECKPOINT_WAIT(id)
+
+TST_CHECKPOINT_WAIT2(id, msec_timeout)
+
+TST_CHECKPOINT_WAKE(id)
+
+TST_CHECKPOINT_WAKE2(id, nr_wake)
+
+TST_CHECKPOINT_WAKE_AND_WAIT(id)
+-------------------------------------------------------------------------------
+
+The checkpoint interface provides pair of wake and wait functions. The 'id' is
+unsigned integer which specifies checkpoint to wake/wait for. As a matter of
+fact it's an index to an array stored in a shared memory, so it starts on
+'0' and there should be enough room for at least of hundred of them.
+
+The 'TST_CHECKPOINT_WAIT()' and 'TST_CHECKPOINT_WAIT2()' suspends process
+execution until it's woken up or until timeout is reached.
+
+The 'TST_CHECKPOINT_WAKE()' wakes one process waiting on the checkpoint.
+If no process is waiting the function retries until it success or until
+timeout is reached.
+
+If timeout has been reached process exits with appropriate error message (uses
+'tst_brk()').
+
+The 'TST_CHECKPOINT_WAKE2()' does the same as 'TST_CHECKPOINT_WAKE()' but can
+be used to wake precisely 'nr_wake' processes.
+
+The 'TST_CHECKPOINT_WAKE_AND_WAIT()' is a shorthand for doing wake and then
+immediately waiting on the same checkpoint.
+
+Child processes created via 'SAFE_FORK()' are ready to use the checkpoint
+synchronization functions, as they inherited the mapped page automatically.
+
+Child processes started via 'exec()', or any other processes not forked from
+the test process must initialize the checkpoint by calling 'tst_reinit()'.
+
+For the details of the interface, look into the 'include/tst_checkpoint.h'.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+/*
+ * Waits for process state change.
+ *
+ * The state is one of the following:
+ *
+ * R - process is running
+ * S - process is sleeping
+ * D - process sleeping uninterruptibly
+ * Z - zombie process
+ * T - process is traced
+ */
+TST_PROCESS_STATE_WAIT(pid, state, msec_timeout)
+-------------------------------------------------------------------------------
+
+The 'TST_PROCESS_STATE_WAIT()' waits until process 'pid' is in requested
+'state' or timeout is reached. The call polls +/proc/pid/stat+ to get this
+information. A timeout of 0 will wait infinitely.
+
+On timeout -1 is returned and errno set to ETIMEDOUT.
+
+It's mostly used with state 'S' which means that process is sleeping in kernel
+for example in 'pause()' or any other blocking syscall.
+
+1.10 Signals and signal handlers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you need to use signal handlers, keep the code short and simple. Don't
+forget that the signal handler is called asynchronously and can interrupt the
+code execution at any place.
+
+This means that problems arise when global state is changed both from the test
+code and signal handler, which will occasionally lead to:
+
+* Data corruption (data gets into inconsistent state), this may happen, for
+  example, for any operations on 'FILE' objects.
+
+* Deadlock, this happens, for example, if you call 'malloc(2)', 'free(2)',
+  etc. from both the test code and the signal handler at the same time since
+  'malloc' has global lock for it's internal data structures. (Be wary that
+  'malloc(2)' is used by the libc functions internally too.)
+
+* Any other unreproducible and unexpected behavior.
+
+Quite common mistake is to call 'exit(3)' from a signal handler. Note that this
+function is not signal-async-safe as it flushes buffers, etc. If you need to
+exit a test immediately from a signal handler use '_exit(2)' instead.
+
+TIP: See 'man 7 signal' for the list of signal-async-safe functions.
+
+If a signal handler sets a variable, its declaration must be 'volatile',
+otherwise compiler may misoptimize the code. This is because the variable may
+not be changed in the compiler code flow analysis. There is 'sig_atomic_t'
+type defined in C99 but this one *DOES NOT* imply 'volatile' (it's just a
+'typedef' to 'int'). So the correct type for a flag that is changed from a
+signal handler is either 'volatile int' or 'volatile sig_atomic_t'.
+
+If a crash (e.g. triggered by signal SIGSEGV) is expected in testing, you
+can avoid creation of core files by calling tst_no_corefile() function.
+This takes effect for process (and its children) which invoked it, unless
+they subsequently modify RLIMIT_CORE.
+
+Note that LTP library will reap any processes that test didn't reap itself,
+and report any non-zero exit code as failure.
+
+1.11 Kernel Modules
+~~~~~~~~~~~~~~~~~~~
+
+There are certain cases where the test needs a kernel part and userspace part,
+happily, LTP can build a kernel module and then insert it to the kernel on test
+start for you. See 'testcases/kernel/device-drivers/block' for details.
+
+1.12 Useful macros
+~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+ARRAY_SIZE(arr)
+-------------------------------------------------------------------------------
+
+Returns the size of statically defined array, i.e.
+'(sizeof(arr) / sizeof(*arr))'
+
+[source,c]
+-------------------------------------------------------------------------------
+LTP_ALIGN(x, a)
+-------------------------------------------------------------------------------
+
+Aligns the x to be next multiple of a. The a must be power of 2.
+
+1.13 Filesystem type detection and skiplist
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests are known to fail on certain filesystems (you cannot swap on TMPFS,
+there are unimplemented 'fcntl()' etc.).
+
+If your test needs to be skipped on certain filesystems use the
+'.skip_filesystems' field in the tst_test structure as follows:
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static struct tst_test test = {
+	...
+        .skip_filesystems = (const char *const []) {
+                "tmpfs",
+                "ramfs",
+                "nfs",
+                NULL
+        },
+};
+-------------------------------------------------------------------------------
+
+When the '.all_filesystem' 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.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void run(void)
+{
+	...
+
+	switch ((type = tst_fs_type("."))) {
+	case TST_NFS_MAGIC:
+	case TST_TMPFS_MAGIC:
+	case TST_RAMFS_MAGIC:
+		tst_brk(TCONF, "Subtest not supported on %s",
+		        tst_fs_type_name(type));
+		return;
+	break;
+	}
+
+	...
+}
+-------------------------------------------------------------------------------
+
+1.14 Thread-safety in the LTP library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is safe to use library 'tst_res()' function in multi-threaded tests.
+
+Only the main thread must return from the 'test()' function to the test
+library and that must be done only after all threads that may call any library
+function has been terminated. That especially means that threads that may call
+'tst_brk()' must terminate before the execution of the 'test()' function
+returns to the library. This is usually done by the main thread joining all
+worker threads at the end of the 'test()' function. Note that the main thread
+will never get to the library code in a case that 'tst_brk()' was called from
+one of the threads since it will sleep at least in 'pthread_join()' on the
+thread that called the 'tst_brk()' till 'exit()' is called by 'tst_brk()'.
+
+The test-supplied cleanup function runs *concurrently* to the rest of the
+threads in a case that cleanup was entered from 'tst_brk()'. Subsequent
+threads entering 'tst_brk()' must be suspended or terminated at the start of
+the user supplied cleanup function. It may be necessary to stop or exit
+the rest of the threads before the test cleans up as well. For example threads
+that create new files should be stopped before temporary directory is be
+removed.
+
+Following code example shows thread safe cleanup function example using atomic
+increment as a guard. The library calls its cleanup after the execution returns
+from the user supplied cleanup and expects that only one thread returns from
+the user supplied cleanup to the test library.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void cleanup(void)
+{
+	static int flag;
+
+	if (tst_atomic_inc(&flag) != 1)
+		pthread_exit(NULL);
+
+	/* if needed stop the rest of the threads here */
+
+	...
+
+	/* then do cleanup work */
+
+	...
+
+	/* only one thread returns to the library */
+}
+-------------------------------------------------------------------------------
+
+
+1.15 Testing with a block device
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests needs a block device (inotify tests, syscall 'EROFS' failures,
+etc.). LTP library contains a code to prepare a testing device.
+
+If '.needs_device' flag in the 'struct tst_test' is set the 'tst_device'
+structure is initialized with a path to a test device and default filesystem
+to be used.
+
+You can also request minimal device size in megabytes by setting
+'.dev_min_size' the device is guaranteed to have at least the requested size
+then.
+
+If '.format_device' flag is set the device is formatted with a filesystem as
+well. You can use '.dev_fs_type' to override the default filesystem type if
+needed and pass additional options to mkfs via '.dev_fs_opts' and
+'.dev_extra_opts' pointers. Note that '.format_device' implies '.needs_device'
+there is no need to set both.
+
+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.
+
+If '.needs_rofs' is set, read-only filesystem is mounted at '.mntpoint' this
+one is supposed to be used for 'EROFS' tests.
+
+If '.all_filesystems' is set the test function is executed for all supported
+filesystems. Supported filesystems are detected based on existence of the
+'mkfs.$fs' helper and on kernel support to mount it. For each supported
+filesystem the 'tst_device.fs_type' is set to the currently tested fs type, if
+'.format_device' is set the device is formatted as well, if '.mount_device' is
+set it's mounted at '.mntpoint'. Also the test timeout is reset for each
+execution of the test function. This flag is expected to be used for filesystem
+related syscalls that are at least partly implemented in the filesystem
+specific code e.g. fallocate().
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+struct tst_device {
+	const char *dev;
+	const char *fs_type;
+};
+
+extern struct tst_device *tst_device;
+
+int tst_umount(const char *path);
+-------------------------------------------------------------------------------
+
+In case that 'LTP_DEV' is passed to the test in an environment, the library
+checks that the file exists and that it's a block device, if
+'.device_min_size' is set the device size is checked as well. If 'LTP_DEV'
+wasn't set or if size requirements were not met a temporary file is created
+and attached to a free loop device.
+
+If there is no usable device and loop device couldn't be initialized the test
+exits with 'TCONF'.
+
+The 'tst_umount()' function works exactly as 'umount(2)' but retries several
+times on 'EBUSY'. This is because various desktop daemons (gvfsd-trash is known
+for that) may be stupid enough to probe all newly mounted filesystem which
+results in 'umount(2)' failing with 'EBUSY'.
+
+IMPORTANT: All testcases should use 'tst_umount()' instead of 'umount(2)' to
+           umount filesystems.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_find_free_loopdev(const char *path, size_t path_len);
+-------------------------------------------------------------------------------
+
+This function finds a free loopdev and returns the free loopdev minor (-1 for no
+free loopdev). If path is non-NULL, it will be filled with free loopdev path.
+If you want to use a customized loop device, we can call tst_find_free_loopdev
+(NULL, 0) in tests to get a free minor number and then mknod.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+unsigned long tst_dev_bytes_written(const char *dev);
+-------------------------------------------------------------------------------
+
+This function reads test block device stat file (/sys/block/<device>/stat) and
+returns the bytes written since the last invocation of this function. To avoid
+FS deferred IO metadata/cache interference, we suggest doing "syncfs" before the
+tst_dev_bytes_written first invocation. And an inline function named tst_dev_sync
+is created for that intention.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+voud tst_find_backing_dev(const char *path, char *dev);
+-------------------------------------------------------------------------------
+
+This function finds the block dev that this path belongs to, it uses stat function
+to get the major/minor number of the path. Then scan them in "/proc/self/mountinfo"
+and list 2th column value after ' - ' string as its block dev if match succeeds.
+
+1.16 Formatting a device with a filesystem
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void setup(void)
+{
+	...
+	SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL);
+	...
+}
+-------------------------------------------------------------------------------
+
+This function takes a path to a device, filesystem type and an array of extra
+options passed to mkfs.
+
+The fs options 'fs_opts' should either be 'NULL' if there are none, or a
+'NULL' terminated array of strings such as:
++const char *const opts[] = {"-b", "1024", NULL}+.
+
+The extra options 'extra_opts' should either be 'NULL' if there are none, or a
+'NULL' terminated array of strings such as +{"102400", NULL}+; 'extra_opts'
+will be passed after device name. e.g: +mkfs -t ext4 -b 1024 /dev/sda1 102400+
+in this case.
+
+Note that perfer to store the options which can be passed before or after device
+name by 'fs_opts' array.
+
+1.17 Verifying a filesystem's free space
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests have size requirements for the filesystem's free space. If these
+requirements are not satisfied, the tests should be skipped.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_fs_has_free(const char *path, unsigned int size, unsigned int mult);
+-------------------------------------------------------------------------------
+
+The 'tst_fs_has_free()' function returns 1 if there is enough space and 0 if
+there is not.
+
+The 'path' is the pathname of any directory/file within a filesystem.
+
+The 'mult' is a multiplier, one of 'TST_BYTES', 'TST_KB', 'TST_MB' or 'TST_GB'.
+
+The required free space is calculated by 'size * mult', e.g.
+'tst_fs_has_free("/tmp/testfile", 64, TST_MB)' will return 1 if the
+filesystem, which '"/tmp/testfile"' is in, has 64MB free space at least, and 0
+if not.
+
+1.18 Files, directories and fs limits
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests need to know the maximum count of links to a regular file or
+directory, such as 'rename(2)' or 'linkat(2)' to test 'EMLINK' error.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_fs_fill_hardlinks(const char *dir);
+-------------------------------------------------------------------------------
+
+Try to get maximum count of hard links to a regular file inside the 'dir'.
+
+NOTE: This number depends on the filesystem 'dir' is on.
+
+This function uses 'link(2)' to create hard links to a single file until it
+gets 'EMLINK' or creates 65535 links. If the limit is hit, the maximum number of
+hardlinks is returned and the 'dir' is filled with hardlinks in format
+"testfile%i", where i belongs to [0, limit) interval. If no limit is hit or if
+'link(2)' failed with 'ENOSPC' or 'EDQUOT', zero is returned and previously
+created files are removed.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_fs_fill_subdirs(const char *dir);
+-------------------------------------------------------------------------------
+
+Try to get maximum number of subdirectories in directory.
+
+NOTE: This number depends on the filesystem 'dir' is on. For current kernel,
+subdir limit is not available for all filesystems (available for ext2, ext3,
+minix, sysv and more). If the test runs on some other filesystems, like ramfs,
+tmpfs, it will not even try to reach the limit and return 0.
+
+This function uses 'mkdir(2)' to create directories in 'dir' until it gets
+'EMLINK' or creates 65535 directories. If the limit is hit, the maximum number
+of subdirectories is returned and the 'dir' is filled with subdirectories in
+format "testdir%i", where i belongs to [0, limit - 2) interval (because each
+newly created dir has two links already - the '.' and the link from parent
+dir). If no limit is hit or if 'mkdir(2)' failed with 'ENOSPC' or 'EDQUOT',
+zero is returned and previously created directories are removed.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_dir_is_empty(const char *dir, int verbose);
+-------------------------------------------------------------------------------
+
+Returns non-zero if directory is empty and zero otherwise.
+
+Directory is considered empty if it contains only '.' and '..'.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+void tst_purge_dir(const char *path);
+-------------------------------------------------------------------------------
+
+Deletes the contents of given directory but keeps the directory itself. Useful
+for cleaning up the temporary directory and mount points between test cases or
+test iterations. Terminates the program with 'TBROK' on error.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_fill_fd(int fd, char pattern, size_t bs, size_t bcount);
+-------------------------------------------------------------------------------
+
+Fill a file with specified pattern using file descriptor.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_prealloc_size_fd(int fd, size_t bs, size_t bcount);
+-------------------------------------------------------------------------------
+
+Preallocate the specified amount of space using 'fallocate()'. Falls back to
+'tst_fill_fd()' if 'fallocate()' fails.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_fill_file(const char *path, char pattern, size_t bs, size_t bcount);
+-------------------------------------------------------------------------------
+
+Creates/overwrites a file with specified pattern using file path.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_prealloc_file(const char *path, size_t bs, size_t bcount);
+-------------------------------------------------------------------------------
+
+Create/overwrite a file and preallocate the specified amount of space for it.
+The allocated space will not be initialized to any particular content.
+
+1.19 Getting an unused PID number
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests require a 'PID', which is not used by the OS (does not belong to
+any process within it). For example, kill(2) should set errno to 'ESRCH' if
+it's passed such 'PID'.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+pid_t tst_get_unused_pid(void);
+-------------------------------------------------------------------------------
+
+Return a 'PID' value not used by the OS or any process within it.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_get_free_pids(void);
+-------------------------------------------------------------------------------
+
+Returns number of unused pids in the system. Note that this number may be
+different once the call returns and should be used only for rough estimates.
+
+1.20 Running executables
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+int tst_cmd(const char *const argv[],
+	        const char *stdout_path,
+	        const char *stderr_path,
+	        enum tst_cmd_flags flags);
+-------------------------------------------------------------------------------
+
+'tst_cmd()' is a wrapper for 'vfork() + execvp()' which provides a way
+to execute an external program.
+
+'argv[]' is a 'NULL' terminated array of strings starting with the program name
+which is followed by optional arguments.
+
+'TST_CMD_PASS_RETVAL' enum 'tst_cmd_flags' makes 'tst_cmd()'
+return the program exit code to the caller, otherwise 'tst_cmd()' exit the
+tests on failure. 'TST_CMD_TCONF_ON_MISSING' check for program in '$PATH' and exit
+with 'TCONF' if not found.
+
+In case that 'execvp()' has failed and the enum 'TST_CMD_PASS_RETVAL' flag was set, the
+return value is '255' if 'execvp()' failed with 'ENOENT' and '254' otherwise.
+
+'stdout_path' and 'stderr_path' determine where to redirect the program
+stdout and stderr I/O streams.
+
+The 'SAFE_CMD()' macro can be used automatic handling non-zero exits (exits
+with 'TBROK') and 'ENOENT' (exits with 'TCONF').
+
+.Example
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+const char *const cmd[] = { "ls", "-l", NULL };
+
+...
+	/* Store output of 'ls -l' into log.txt */
+	tst_cmd(cmd, "log.txt", NULL, 0);
+...
+-------------------------------------------------------------------------------
+
+1.21 Measuring elapsed time and helper functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_timer.h"
+
+void tst_timer_check(clockid_t clk_id);
+
+void tst_timer_start(clockid_t clk_id);
+
+void tst_timer_stop(void);
+
+struct timespec tst_timer_elapsed(void);
+
+long long tst_timer_elapsed_ms(void);
+
+long long tst_timer_elapsed_us(void);
+
+int tst_timer_expired_ms(long long ms);
+-------------------------------------------------------------------------------
+
+The 'tst_timer_check()' function checks if specified 'clk_id' is suppored and
+exits the test with 'TCONF' otherwise. It's expected to be used in test
+'setup()' before any resources that needs to be cleaned up are initialized,
+hence it does not include a cleanup function parameter.
+
+The 'tst_timer_start()' marks start time and stores the 'clk_id' for further
+use.
+
+The 'tst_timer_stop()' marks the stop time using the same 'clk_id' as last
+call to 'tst_timer_start()'.
+
+The 'tst_timer_elapsed*()' returns time difference between the timer start and
+last timer stop in several formats and units.
+
+The 'tst_timer_expired_ms()' function checks if the timer started by
+'tst_timer_start()' has been running longer than ms milliseconds. The function
+returns non-zero if timer has expired and zero otherwise.
+
+IMPORTANT: The timer functions use 'clock_gettime()' internally which needs to
+           be linked with '-lrt' on older glibc. Please do not forget to add
+	   'LDLIBS+=-lrt' in Makefile.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+#include "tst_timer.h"
+
+static void setup(void)
+{
+	...
+	tst_timer_check(CLOCK_MONOTONIC);
+	...
+}
+
+static void run(void)
+{
+	...
+	tst_timer_start(CLOCK_MONOTONIC);
+	...
+	while (!tst_timer_expired_ms(5000)) {
+		...
+	}
+	...
+}
+
+struct tst_test test = {
+	...
+	.setup = setup,
+	.test_all = run,
+	...
+};
+-------------------------------------------------------------------------------
+
+Expiration timer example usage.
+
+[source,c]
+-------------------------------------------------------------------------------
+long long tst_timespec_to_us(struct timespec t);
+long long tst_timespec_to_ms(struct timespec t);
+
+struct timeval tst_us_to_timeval(long long us);
+struct timeval tst_ms_to_timeval(long long ms);
+
+int tst_timespec_lt(struct timespec t1, struct timespec t2);
+
+struct timespec tst_timespec_add_us(struct timespec t, long long us);
+
+struct timespec tst_timespec_diff(struct timespec t1, struct timespec t2);
+long long tst_timespec_diff_us(struct timespec t1, struct timespec t2);
+long long tst_timespec_diff_ms(struct timespec t1, struct timespec t2);
+
+struct timespec tst_timespec_abs_diff(struct timespec t1, struct timespec t2);
+long long tst_timespec_abs_diff_us(struct timespec t1, struct timespec t2);
+long long tst_timespec_abs_diff_ms(struct timespec t1, struct timespec t2);
+-------------------------------------------------------------------------------
+
+The first four functions are simple inline conversion functions.
+
+The 'tst_timespec_lt()' function returns non-zero if 't1' is earlier than
+'t2'.
+
+The 'tst_timespec_add_us()' function adds 'us' microseconds to the timespec
+'t'. The 'us' is expected to be positive.
+
+The 'tst_timespec_diff*()' functions returns difference between two times, the
+'t1' is expected to be later than 't2'.
+
+The 'tst_timespec_abs_diff*()' functions returns absolute value of difference
+between two times.
+
+NOTE: All conversions to ms and us rounds the value.
+
+1.22 Datafiles
+~~~~~~~~~~~~~~
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static const char *const res_files[] = {
+	"foo",
+	"bar",
+	NULL
+};
+
+static struct tst_test test = {
+	...
+	.resource_files = res_files,
+	...
+}
+-------------------------------------------------------------------------------
+
+If the test needs additional files to be copied to the test temporary
+directory all you need to do is to list their filenames in the
+'NULL' terminated array '.resource_files' in the tst_test structure.
+
+When resource files is set test temporary directory is created automatically,
+there is need to set '.needs_tmpdir' as well.
+
+The test library looks for datafiles first, these are either stored in a
+directory called +datafiles+ in the +$PWD+ at the start of the test or in
++$LTPROOT/testcases/data/${test_binary_name}+. If the file is not found the
+library looks into +$LTPROOT/testcases/bin/+ and to +$PWD+ at the start of the
+test. This ensures that the testcases can copy the file(s) effortlessly both
+when test is started from the directory it was compiled in as well as when LTP
+was installed.
+
+The file(s) are copied to the newly created test temporary directory which is
+set as the test working directory when the 'test()' functions is executed.
+
+1.23 Code path tracing
+~~~~~~~~~~~~~~~~~~~~~~
+
+'tst_res' is a macro, so on when you define a function in one file:
+
+[source,c]
+-------------------------------------------------------------------------------
+int do_action(int arg)
+{
+	...
+
+	if (ok) {
+		tst_res(TPASS, "check passed");
+		return 0;
+	} else {
+		tst_res(TFAIL, "check failed");
+		return -1;
+	}
+}
+-------------------------------------------------------------------------------
+
+and call it from another file, the file and line reported by 'tst_res' in this
+function will be from the former file.
+
+'TST_TRACE' can make the analysis of such situations easier. It's a macro which
+inserts a call to 'tst_res(TINFO, ...)' in case its argument evaluates to
+non-zero. In this call to 'tst_res(TINFO, ...)' the file and line will be
+expanded using the actual location of 'TST_TRACE'.
+
+For example, if this another file contains:
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+if (TST_TRACE(do_action(arg))) {
+	...
+}
+-------------------------------------------------------------------------------
+
+the generated output may look similar to:
+
+-------------------------------------------------------------------------------
+common.h:9: FAIL: check failed
+test.c:8: INFO: do_action(arg) failed
+-------------------------------------------------------------------------------
+
+1.24 Tainted kernels
+~~~~~~~~~~~~~~~~~~~~
+
+If you need to detect whether a testcase triggers a kernel warning, bug or
+oops, the following can be used to detect TAINT_W or TAINT_D:
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static struct tst_test test = {
+	...
+	.taint_check = TST_TAINT_W | TST_TAINT_D,
+	...
+};
+
+void run(void)
+{
+	...
+	if (tst_taint_check() != 0)
+		tst_res(TFAIL, "kernel has issues");
+	else
+		tst_res(TPASS, "kernel seems to be fine");
+}
+-------------------------------------------------------------------------------
+
+To initialize taint checks, you have to set the taint flags you want to test
+for in the 'taint_check' attribute of the tst_test struct. LTP library will
+then automatically call 'tst_taint_init()' during test setup. The function
+will generate a 'TCONF' if the requested flags are not fully supported on the
+running kernel, and 'TBROK' if the kernel is already tainted before executing
+the test.
+
+LTP library will then automatically check kernel taint at the end of testing.
+If '.all_filesystems' is set in struct tst_test, taint check will be performed
+after each file system and taint will abort testing early with 'TFAIL'. You
+can optionally also call 'tst_taint_check()' during 'run()', which returns 0
+or the tainted flags set in '/proc/sys/kernel/tainted' as specified earlier.
+
+Depending on your kernel version, not all tainted-flags will be supported.
+
+For reference to tainted kernels, see kernel documentation:
+Documentation/admin-guide/tainted-kernels.rst or
+https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
+
+1.25 Checksums
+~~~~~~~~~~~~~~
+
+CRC32c checksum generation is supported by LTP. In order to use it, the
+test should include 'tst_checksum.h' header, then can call 'tst_crc32c()'.
+
+1.26 Checking kernel for the driver support
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+Since it relies on modprobe command, the check will be skipped if the command
+itself is not available on the system.
+
+1.27 Saving & restoring /proc|sys values
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LTP library 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 strings where
+each string represents a file, whose value is saved at the beginning
+and restored at the end of the test. Only first line of a specified
+file is saved and restored.
+
+Pathnames can be optionally prefixed to specify how strictly (during
+'store') are handled errors:
+
+* (no prefix) - test ends with 'TCONF', if file doesn't exist
+* '?'         - test prints info message and continues,
+                if file doesn't exist or open/read fails
+* '!'         - test ends with 'TBROK', if file doesn't exist
+
+'restore' is always strict and will TWARN if it encounters any error.
+
+[source,c]
+-------------------------------------------------------------------------------
+static const char *save_restore[] = {
+	"/proc/sys/kernel/core_pattern",
+	NULL,
+};
+
+static void setup(void)
+{
+	FILE_PRINTF("/proc/sys/kernel/core_pattern", "/mypath");
+}
+
+static struct tst_test test = {
+	...
+	.setup = setup,
+	.save_restore = save_restore,
+};
+-------------------------------------------------------------------------------
+
+1.28 Parsing kernel .config
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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).
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static const char *kconfigs[] = {
+	"CONFIG_X86_INTEL_UMIP | CONFIG_X86_UMIP",
+	NULL
+};
+
+static struct tst_test test = {
+	...
+	.needs_kconfigs = kconfigs,
+	...
+};
+-------------------------------------------------------------------------------
+
+1.29 Changing the Wall Clock Time during test execution
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are some tests that, for different reasons, might need to change the
+system-wide clock time. Whenever this happens, it is imperative that the clock
+is restored, at the end of test's execution, taking in consideration the amount
+of time elapsed during that test.
+
+In order for that to happen, struct tst_test has a variable called
+"restore_wallclock" that should be set to "1" so LTP knows it should: (1)
+initialize a monotonic clock during test setup phase and (2) use that monotonic
+clock to fix the system-wide clock time at the test cleanup phase.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void setup(void)
+{
+	...
+}
+
+static void run(void)
+{
+	...
+}
+
+struct tst_test test = {
+	...
+	.setup = setup,
+	.test_all = run,
+	.restore_wallclock = 1,
+	...
+};
+-------------------------------------------------------------------------------
+
+1.30 Testing similar syscalls in one test
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+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,
+	...
+};
+-------------------------------------------------------------------------------
+
+1.31 Guarded buffers
+~~~~~~~~~~~~~~~~~~~~
+
+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 patch 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.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static struct foo *foo_ptr;
+static struct iovec *iov;
+static void *buf_ptr;
+static char *id;
+...
+
+static void run(void)
+{
+	...
+
+	foo_ptr->bar = 1;
+	foo_ptr->buf = buf_ptr;
+
+	...
+}
+
+static void setup(void)
+{
+	...
+
+	id = tst_strdup(string);
+
+	...
+}
+
+static struct tst_test test = {
+	...
+	.bufs = (struct tst_buffers []) {
+		{&foo_ptr, .size = sizeof(*foo_ptr)},
+		{&buf_ptr, .size = BUF_SIZE},
+		{&iov, .iov_sizes = (int[]){128, 32, -1},
+		{}
+	}
+};
+-------------------------------------------------------------------------------
+
+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 structure.
+
+So far the tst_test structure 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.
+
+1.32 Adding and removing capabilities
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+[source,c]
+--------------------------------------------------------------------------------
+#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),
+		{}
+	},
+};
+--------------------------------------------------------------------------------
+
+Look at the test struct at the bottom. We have filled in the 'caps' field with
+a 'NULL' terminated array containing two 'tst_cap' structs. 'TST_CAP_REQ'
+actions are executed before setup and 'TST_CAP_DROP' are executed after
+setup. This means it is possible to both request and drop a capability.
+
+[source,c]
+--------------------------------------------------------------------------------
+static struct tst_test test = {
+	.test_all = run,
+	.caps = (struct tst_cap []) {
+		TST_CAP(TST_CAP_REQ, CAP_NET_RAW),
+		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
+		{}
+	},
+};
+--------------------------------------------------------------------------------
+
+Here we request 'CAP_NET_RAW', but drop 'CAP_SYS_ADMIN'. If the capability is
+in the permitted set, but not the effective set, the library will try to
+permit it. If it is not in the permitted set, then it will fail with 'TCONF'.
+
+This API does not require 'libcap' to be installed. However it has limited
+features relative to 'libcap'. It only tries to add or remove capabilities
+from the effective set. This means that tests which need to spawn child
+processes may have difficulties ensuring the correct capabilities are
+available to the children (see the capabilities (7) manual pages).
+
+However a lot of problems can be solved by using 'tst_cap_action(struct
+tst_cap  *cap)' directly which can be called at any time. This also helps if
+you wish to drop a capability at the begining of setup.
+
+1.33 Reproducing race-conditions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a bug is caused by two tasks in the kernel racing and you wish to create a
+regression test (or bug-fix validation test) then the 'tst_fuzzy_sync.h'
+library should be used.
+
+It allows you to specify, in your code, two race windows. One window in each
+thread's loop (triggering a race usually requires many iterations). These
+windows show fuzzy-sync where the race can happen. They don't need to be
+exact, hence the 'fuzzy' part. If the race condition is not immediately
+triggered then the library will begin experimenting with different timings.
+
+[source,c]
+--------------------------------------------------------------------------------
+#include "tst_fuzzy_sync.h"
+
+static struct tst_fzsync_pair fzsync_pair;
+
+static void setup(void)
+{
+        tst_fzsync_pair_init(&fzsync_pair);
+}
+
+static void cleanup(void)
+{
+	tst_fzsync_pair_cleanup(&fzsync_pair);
+}
+
+static void *thread_b(void *arg)
+{
+	while (tst_fzsync_run_b(&fzsync_pair)) {
+
+		tst_fzsync_start_race_b(&fzsync_pair);
+
+                /* This is the race window for thread B */
+
+                tst_fzsync_end_race_b(&fzsync_pair);
+	}
+
+	return arg;
+}
+
+static void thread_a(void)
+{
+	tst_fzsync_pair_reset(&fzsync_pair, thread_b);
+
+        while (tst_fzsync_run_a(&fzsync_pair)) {
+
+		tst_fzsync_start_race_a(&fzsync_pair);
+
+		/* This is the race window for thread A */
+
+                tst_fzsync_end_race_a(&fzsync_pair);
+	}
+}
+
+static struct tst_test test = {
+	.test_all = thread_a,
+	.setup = setup,
+	.cleanup = cleanup,
+};
+--------------------------------------------------------------------------------
+
+Above is a minimal template for a test using fuzzy-sync. In a simple case, you
+just need to put the bits you want to race inbetween 'start_race' and
+'end_race'. Meanwhile, any setup you need to do per-iteration goes outside the
+windows.
+
+Fuzzy sync synchronises 'run_a' and 'run_b', which act as barriers, so that
+neither thread can progress until the other has caught up with it. There is
+also the 'pair_wait' function which can be used to add barriers in other
+locations. Of course 'start/end_race_a/b' are also a barriers.
+
+The library decides how long the test should run for based on the timeout
+specified by the user plus some other heuristics.
+
+For full documentation see the comments in 'include/tst_fuzzy_sync.h'.
+
+1.34 Reserving hugepages
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Many of the LTP tests need to use hugepage in their testing, this allows the
+test can reserve hugepages from system only via '.request_hugepages = xx'.
+
+If set non-zero number of 'request_hugepages', test will try to reserve the
+expected number of hugepage for testing in setup phase. If system does not
+have enough hpage for using, it will try the best to reserve 80% available
+number of hpages. With success test stores the reserved hugepage number in
+'tst_hugepages'. For the system without hugetlb supporting, variable
+'tst_hugepages' will be set to 0.
+
+Also, we do cleanup and restore work for the hpages resetting automatically.
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void run(void)
+{
+	...
+
+	if (tst_hugepages == test.request_hugepages)
+		TEST(do_hpage_test);
+	else
+		...
+	...
+}
+
+struct tst_test test = {
+	.test_all = run,
+	.request_hugepages = 2,
+	...
+};
+-------------------------------------------------------------------------------
+
+or,
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+
+static void run(void)
+{
+	...
+}
+
+static void setup(void)
+{
+? ? ? ? if (tst_hugepages != test.requested_hugepages)
+? ? ? ? ? ? ? ? tst_brk(TCONF, "...");
+}
+
+struct tst_test test = {
+	.test_all = run,
+	.request_hugepages = 2,
+	...
+};
+-------------------------------------------------------------------------------
+
+1.35 Checking for required commands
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Required commands can be checked with '.needs_cmds', which points to a 'NULL'
+terminated array of strings such as:
+
+[source,c]
+-------------------------------------------------------------------------------
+.needs_cmds = (const char *const []) {
+	"useradd",
+	"userdel",
+	NULL
+},
+-------------------------------------------------------------------------------
+
+1.36 Assert sys or proc file value
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Using TST_ASSERT_INT/STR(path, val) to assert that integer value or string stored in
+the prefix field of file pointed by path equals to the value passed to this function.
+
+Also having a similar api pair TST_ASSERT_FILE_INT/STR(path, prefix, val) to assert
+the field value of file.
+
+1.36 Using Control Group
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some LTP tests need specific Control Group configurations. tst_cgroup.h provides
+APIs to discover and use CGroups. There are many differences between CGroups API
+V1 and V2. We encapsulate the details of configuring CGroups in high-level
+functions which follow the V2 kernel API. Allowing one to use CGroups without
+caring too much about the current system's configuration.
+
+Also, the LTP library will automatically mount/umount and configure the CGroup
+hierarchies if that is required (e.g. if you run the tests from init with no
+system manager).
+
+[source,c]
+-------------------------------------------------------------------------------
+#include "tst_test.h"
+#include "tst_cgroup.h"
+
+static const struct tst_cgroup_group *cg;
+
+static void run(void)
+{
+	...
+	// do test under cgroup
+	...
+}
+
+static void setup(void)
+{
+	tst_cgroup_require("memory", NULL);
+	cg = tst_cgroup_get_test_group();
+	SAFE_CGROUP_PRINTF(cg, "cgroup.procs", "%d", getpid());
+	SAFE_CGROUP_PRINTF(cg, "memory.max", "%lu", MEMSIZE);
+	if (SAFE_CGROUP_HAS(cg, "memory.swap.max"))
+		SAFE_CGROUP_PRINTF(cg, "memory.swap.max", "%zu", memsw);
+}
+
+static void cleanup(void)
+{
+	tst_cgroup_cleanup();
+}
+
+struct tst_test test = {
+	.setup = setup,
+	.test_all = run,
+	.cleanup = cleanup,
+	...
+};
+-------------------------------------------------------------------------------
+
+Above, we first ensure the memory controller is available on the
+test's CGroup with 'tst_cgroup_require'. We then get a structure,
+'cg', which represents the test's CGroup. Note that
+'tst_cgroup_get_test_group' should not be called many times, as it is
+allocated in a guarded buffer (See section 2.2.31). Therefor it is
+best to call it once in 'setup' and not 'run' because 'run' may be
+repeated with the '-i' option.
+
+We then write the current processes PID into 'cgroup.procs', which
+moves the current process into the test's CGroup. After which we set
+the maximum memory size by writing to 'memory.max'. If the memory
+controller is mounted on CGroups V1 then the library will actually
+write to 'memory.limit_in_bytes'. As a general rule, if a file exists
+on both CGroup versions, then we use the V2 naming.
+
+Some controller features, such as 'memory.swap', can be
+disabled. Therefor we need to check if they exist before accessing
+them. This can be done with 'SAFE_CGROUP_HAS' which can be called on
+any control file or feature.
+
+Most tests only require setting a few limits similar to the above. In
+such cases the differences between V1 and V2 are hidden. Setup and
+cleanup is also mostly hidden. However things can get much worse.
+
+[source,c]
+-------------------------------------------------------------------------------
+static const struct tst_cgroup_group *cg;
+static const struct tst_cgroup_group *cg_drain;
+static struct tst_cgroup_group *cg_child;
+
+static void run(void)
+{
+	char buf[BUFSIZ];
+	size_t mem = 0;
+
+	cg_child = tst_cgroup_group_mk(cg, "child");
+	SAFE_CGROUP_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
+
+	if (TST_CGROUP_VER(cg, "memory") != TST_CGROUP_V1)
+		SAFE_CGROUP_PRINT(cg, "cgroup.subtree_control", "+memory");
+	if (TST_CGROUP_VER(cg, "cpuset") != TST_CGROUP_V1)
+		SAFE_CGROUP_PRINT(cg, "cgroup.subtree_control", "+cpuset");
+
+	if (!SAFE_FORK()) {
+		SAFE_CGROUP_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
+
+		if (SAFE_CGROUP_HAS(cg_child, "memory.swap"))
+			SAFE_CGROUP_SCANF(cg_child, "memory.swap.current", "%zu", &mem);
+		SAFE_CGROUP_READ(cg_child, "cpuset.mems", buf, sizeof(buf));
+
+		// Do something with cpuset.mems and memory.current values
+		...
+
+		exit(0);
+	}
+
+	tst_reap_children();
+	SAFE_CGROUP_PRINTF(cg_drain, "cgroup.procs", "%d", getpid());
+	cg_child = tst_cgroup_group_rm(cg_child);
+}
+
+static void setup(void)
+{
+	tst_cgroup_require("memory", NULL);
+	tst_cgroup_require("cpuset", NULL);
+
+	cg = tst_cgroup_get_test_group();
+	cg_drain = tst_cgroup_get_drain_group();
+}
+
+static void cleanup(void)
+{
+	if (cg_child) {
+		SAFE_CGROUP_PRINTF(cg_drain, "cgroup.procs", "%d", getpid());
+		cg_child = tst_cgroup_group_rm(cg_child);
+	}
+
+	tst_cgroup_cleanup();
+}
+
+struct tst_test test = {
+	.setup = setup,
+	.test_all = run,
+	.cleanup = cleanup,
+	...
+};
+-------------------------------------------------------------------------------
+
+Starting with setup; we can see here that we also fetch the 'drain'
+CGroup. This is a shared group (between parallel tests) which may
+contain processes from other tests. It should have default settings and
+these should not be changed by the test. It can be used to remove
+processes from other CGroups incase the hierarchy root is not
+accessible.
+
+In 'run', we first create a child CGroup with 'tst_cgroup_mk'. As we
+create this CGroup in 'run' we should also remove it at the end of
+run. We also need to check if it exists and remove it in cleanup as
+well. Because there are 'SAFE_' functions which may jump to cleanup.
+
+We then move the main test process into the child CGroup. This is
+important as it means that before we destroy the child CGroup we have
+to move the main test process elsewhere. For that we use the 'drain'
+group.
+
+Next we enable the memory and cpuset controller configuration on the
+test CGroup's descendants (i.e. 'cg_child'). This allows each child to
+have its own settings. The file 'cgroup.subtree_control' does not
+exist on V1. Because it is possible to have both V1 and V2 active at
+the same time. We can not simply check if 'subtree_control' exists
+before writing to it. We have to check if a particular controller is
+on V2 before trying to add it to 'subtree_control'. Trying to add a V1
+controller will result in 'ENOENT'.
+
+We then fork a child process and add this to the child CGroup. Within
+the child process we try to read 'memory.swap.current'. It is possible
+that the memory controller was compiled without swap support, so it is
+necessary to check if 'memory.swap' is enabled. That is unless the
+test will never reach the point where 'memory.swap.*' are used without
+swap support.
+
+The parent process waits for the child process to be reaped before
+destroying the child CGroup. So there is no need to transfer the child
+to drain. However the parent process must be moved otherwise we will
+get 'EBUSY' when trying to remove the child CGroup.
+
+Another example of an edge case is the following.
+
+[source,c]
+-------------------------------------------------------------------------------
+	if (TST_CGROUP_VER(cg, "memory") == TST_CGROUP_V1)
+		SAFE_CGROUP_PRINTF(cg, "memory.swap.max", "%lu", ~0UL);
+	else
+		SAFE_CGROUP_PRINT(cg, "memory.swap.max", "max");
+-------------------------------------------------------------------------------
+
+CGroups V2 introduced a feature where 'memory[.swap].max' could be set to
+"max". This does not appear to work on V1 'limit_in_bytes' however. For most
+tests, simply using a large number is sufficient and there is no need to use
+"max". Importantly though, one should be careful to read both the V1 and V2
+kernel docs. The LTP library can not handle all edge cases. It does the minimal
+amount of work to make testing on both V1 and V2 feasible.
+
+1.37 Require minimum numbers of CPU for a testcase
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some tests require more than specific number of CPU. It can be defined with
+`.min_cpus = N`.
+
+1.38 Test tags
+~~~~~~~~~~~~~~
+
+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 and exported
+into documentation.
+
+CVE, mainline and stable kernel git commits in a regression test for a kernel bug:
+[source,c]
+-------------------------------------------------------------------------------
+struct tst_test test = {
+	...
+	.tags = (const struct tst_tag[]) {
+		{"linux-git", "9392a27d88b9"},
+		{"linux-git", "ff002b30181d"},
+		{"linux-stable-git", "c4a23c852e80"},
+		{"CVE", "2020-29373"},
+		{}
+	}
+};
+-------------------------------------------------------------------------------
+
+Glibc git commit in a regression test for a glibc bug:
+[source,c]
+-------------------------------------------------------------------------------
+struct tst_test test = {
+	...
+	.tags = (const struct tst_tag[]) {
+		{"glibc-git", "574500a108be"},
+		{}
+	}
+};
+-------------------------------------------------------------------------------
+
+2. Common problems
+------------------
+
+This chapter describes common problems/misuses and less obvious design patters
+(quirks) in UNIX interfaces. Read it carefully :)
+
+2.1 umask()
+~~~~~~~~~~~
+
+I've been hit by this one several times already... When you create files
+with 'open()' or 'creat()' etc, the mode specified as the last parameter *is
+not* the mode the file is created with. The mode depends on current 'umask()'
+settings which may clear some of the bits. If your test depends on specific
+file permissions you need either to change umask to 0 or 'chmod()' the file
+afterwards or use 'SAFE_TOUCH()' that does the 'chmod()' for you.
+
+2.2 access()
+~~~~~~~~~~~~
+
+If 'access(some_file, W_OK)' is executed by root, it will return success even
+if the file doesn't have write permission bits set (the same holds for R_OK
+too). For sysfs files you can use 'open()' as a workaround to check file
+read/write permissions. It might not work for other filesystems, for these you
+have to use 'stat()', 'lstat()' or 'fstat()'.
+
+2.3 umount() EBUSY
+~~~~~~~~~~~~~~~~~~
+
+Various desktop daemons (gvfsd-trash is known for that) may be stupid enough
+to probe all newly mounted filesystem which results in 'umount(2)' failing
+with 'EBUSY'; use 'tst_umount()' described in 1.19 that retries in this case
+instead of plain 'umount(2)'.
+
+2.4 FILE buffers and fork()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Be vary that if a process calls 'fork(2)' the child process inherits open
+descriptors as well as copy of the parent memory so especially if there are
+any open 'FILE' buffers with a data in them they may be written both by the
+parent and children resulting in corrupted/duplicated data in the resulting
+files.
+
+Also open 'FILE' streams are flushed and closed at 'exit(3)' so if your
+program works with 'FILE' streams, does 'fork(2)', and the child may end up
+calling 'exit(3)' you will likely end up with corrupted files.
+
+The solution to this problem is either simply call 'fflush(NULL)' that flushes
+all open output 'FILE' streams just before doing 'fork(2)'. You may also use
+'_exit(2)' in child processes which does not flush 'FILE' buffers and also
+skips 'atexit(3)' callbacks.
diff --git a/doc/shell-test-api.txt b/doc/shell-test-api.txt
new file mode 100644
index 000000000..21fefc8e0
--- /dev/null
+++ b/doc/shell-test-api.txt
@@ -0,0 +1,740 @@
+LTP Shell Test API
+==================
+
+1 Writing a testcase in shell
+-----------------------------
+
+LTP supports testcases to be written in a portable shell too.
+
+There is a shell library modeled closely to the C interface at
+'testcases/lib/tst_test.sh'.
+
+WARNING: All identifiers starting with 'TST_' or 'tst_' are reserved for the
+         test library.
+
+1.1 Basic test interface
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# This is a basic test for true shell builtin
+
+TST_TESTFUNC=do_test
+. tst_test.sh
+
+do_test()
+{
+	true
+	ret=$?
+
+	if [ $ret -eq 0 ]; then
+		tst_res TPASS "true returned 0"
+	else
+		tst_res TFAIL "true returned $ret"
+	fi
+}
+
+tst_run
+-------------------------------------------------------------------------------
+
+TIP: To execute this test the 'tst_test.sh' library must be in '$PATH'. If you
+     are executing the test from a git checkout you can run it as
+     'PATH="$PATH:../../lib" ./foo01.sh'
+
+The shell library expects test setup, cleanup and the test function executing
+the test in the '$TST_SETUP', '$TST_CLEANUP' and '$TST_TESTFUNC' variables.
+
+Both '$TST_SETUP' and '$TST_CLEANUP' are optional.
+
+The '$TST_TESTFUNC' may be called several times if more than one test
+iteration was requested by passing right command line options to the test.
+
+The '$TST_CLEANUP' may be called even in the middle of the setup and must be
+able to clean up correctly even in this situation. The easiest solution for
+this is to keep track of what was initialized and act accordingly in the
+cleanup.
+
+WARNING: Similar to the C library, calling 'tst_brk' in the $TST_CLEANUP does
+         not exit the test and 'TBROK' is converted to 'TWARN'.
+
+Notice also the 'tst_run' shell API function called@the end of the test that
+actually starts the test.
+
+WARNING: cleanup function is called only after 'tst_run' has been started.
+Calling 'tst_brk' in shell libraries, e.g. 'tst_test.sh' or 'tst_net.sh' does
+not trigger calling it.
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Example test with tests in separate functions
+
+TST_TESTFUNC=test
+TST_CNT=2
+. tst_test.sh
+
+test1()
+{
+	tst_res TPASS "Test $1 passed"
+}
+
+test2()
+{
+	tst_res TPASS "Test $1 passed"
+}
+
+tst_run
+# output:
+# foo 1 TPASS: Test 1 passed
+# foo 2 TPASS: Test 2 passed
+-------------------------------------------------------------------------------
+
+If '$TST_CNT' is set, the test library looks if there are functions named
+'$\{TST_TESTFUNC\}1', ..., '$\{TST_TESTFUNC\}$\{TST_CNT\}' and if these are
+found they are executed one by one. The test number is passed to it in the '$1'.
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Example test with tests in a single function
+
+TST_TESTFUNC=do_test
+TST_CNT=2
+. tst_test.sh
+
+do_test()
+{
+	case $1 in
+	1) tst_res TPASS "Test $1 passed";;
+	2) tst_res TPASS "Test $1 passed";;
+	esac
+}
+
+tst_run
+# output:
+# foo 1 TPASS: Test 1 passed
+# foo 2 TPASS: Test 2 passed
+-------------------------------------------------------------------------------
+
+Otherwise, if '$TST_CNT' is set but there is no '$\{TST_TESTFUNC\}1', etc.,
+the '$TST_TESTFUNC' is executed '$TST_CNT' times and the test number is passed
+to it in the '$1'.
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Example test with tests in a single function, using $TST_TEST_DATA and
+# $TST_TEST_DATA_IFS
+
+TST_TESTFUNC=do_test
+TST_TEST_DATA="foo:bar:d dd"
+TST_TEST_DATA_IFS=":"
+. tst_test.sh
+
+do_test()
+{
+	tst_res TPASS "Test $1 passed with data '$2'"
+}
+
+tst_run
+# output:
+# foo 1 TPASS: Test 1 passed with data 'foo'
+# foo 2 TPASS: Test 1 passed with data 'bar'
+# foo 3 TPASS: Test 1 passed with data 'd dd'
+-------------------------------------------------------------------------------
+
+It's possible to pass data for function with '$TST_TEST_DATA'. Optional
+'$TST_TEST_DATA_IFS' is used for splitting, default value is space.
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Example test with tests in a single function, using $TST_TEST_DATA and $TST_CNT
+
+TST_TESTFUNC=do_test
+TST_CNT=2
+TST_TEST_DATA="foo bar"
+. tst_test.sh
+
+do_test()
+{
+	case $1 in
+	1) tst_res TPASS "Test $1 passed with data '$2'";;
+	2) tst_res TPASS "Test $1 passed with data '$2'";;
+	esac
+}
+
+tst_run
+# output:
+# foo 1 TPASS: Test 1 passed with data 'foo'
+# foo 2 TPASS: Test 2 passed with data 'foo'
+# foo 3 TPASS: Test 1 passed with data 'bar'
+# foo 4 TPASS: Test 2 passed with data 'bar'
+-------------------------------------------------------------------------------
+
+'$TST_TEST_DATA' can be used with '$TST_CNT'. If '$TST_TEST_DATA_IFS' not specified,
+space as default value is used. Of course, it's possible to use separate functions.
+
+1.2 Library environment variables and functions for shell
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Similarily to the C library various checks and preparations can be requested
+simply by setting right '$TST_NEEDS_FOO'.
+
+[options="header"]
+|=============================================================================
+| Variable name      | Action done
+| 'TST_NEEDS_ROOT'   | Exit the test with 'TCONF' unless executed under root.
+|                    | Alternatively the 'tst_require_root' command can be used.
+| 'TST_NEEDS_TMPDIR' | Create test temporary directory and cd into it.
+| 'TST_NEEDS_DEVICE' | Prepare test temporary device, the path to testing
+                       device is stored in '$TST_DEVICE' variable.
+                       The option implies 'TST_NEEDS_TMPDIR'.
+| 'TST_NEEDS_CMDS'   | String with command names that has to be present for
+                       the test (see below).
+| 'TST_NEEDS_MODULE' | Test module name needed for the test (see below).
+| 'TST_NEEDS_DRIVERS'| Checks kernel drivers support for the test.
+| 'TST_TIMEOUT'      | Maximum timeout set for the test in sec. Must be int >= 1,
+                       or -1 (special value to disable timeout), default is 300.
+                       Variable is meant be set in tests, not by user.
+                       It's an equivalent of `tst_test.timeout` in C, can be set
+                       via 'tst_set_timeout(timeout)' after test has started.
+|=============================================================================
+
+[options="header"]
+|=============================================================================
+| Function name              | Action done
+| 'tst_set_timeout(timeout)' | Maximum timeout set for the test in sec.
+                               See 'TST_TIMEOUT' variable.
+|=============================================================================
+
+NOTE: Network tests (see testcases/network/README.md) use additional variables
+and functions in 'tst_net.sh'.
+
+Checking for presence of commands
++++++++++++++++++++++++++++++++++
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+
+...
+
+TST_NEEDS_CMDS="modinfo modprobe"
+. tst_test.sh
+
+...
+
+-------------------------------------------------------------------------------
+
+Setting '$TST_NEEDS_CMDS' to a string listing required commands will check for
+existence each of them and exits the test with 'TCONF' on first missing.
+
+Alternatively the 'tst_require_cmds()' function can be used to do the same on
+runtime, since sometimes we need to the check at runtime too.
+
+'tst_check_cmds()' can be used for requirements just for a particular test
+as it doesn't exit (it issues 'tst_res TCONF'). Expected usage is:
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+
+TST_TESTFUNC=do_test
+. tst_test.sh
+
+do_test()
+{
+	tst_check_cmds cmd || return
+	cmd --foo
+	...
+}
+
+tst_run
+-------------------------------------------------------------------------------
+
+Locating kernel modules
++++++++++++++++++++++++
+
+The LTP build system can build kernel modules as well, setting
+'$TST_NEEDS_MODULE' to module name will cause the library to look for the
+module in a few possible paths.
+
+If module was found the path to it will be stored into '$TST_MODPATH'
+variable, if module wasn't found the test will exit with 'TCONF'.
+
+Alternatively the 'tst_require_module()' function can be used to do the same
+at runtime.
+
+1.3 Optional command line parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Optional test command line parameters
+
+TST_OPTS="af:"
+TST_USAGE=usage
+TST_PARSE_ARGS=parse_args
+TST_TESTFUNC=do_test
+
+. tst_test.sh
+
+ALTERNATIVE=0
+MODE="foo"
+
+usage()
+{
+	cat << EOF
+usage: $0 [-a] [-f <foo|bar>]
+
+OPTIONS
+-a     Enable support for alternative foo
+-f     Specify foo or bar mode
+EOF
+}
+
+parse_args()
+{
+	case $1 in
+	a) ALTERNATIVE=1;;
+	f) MODE="$2";;
+	esac
+}
+
+do_test()
+{
+	...
+}
+
+tst_run
+-------------------------------------------------------------------------------
+
+The 'getopts' string for optional parameters is passed in the '$TST_OPTS'
+variable. There are a few default parameters that cannot be used by a test,
+these can be listed with passing help '-h' option to any test.
+
+The function that prints the usage is passed in '$TST_USAGE', the help for
+the options implemented in the library is appended when usage is printed.
+
+Lastly the function '$PARSE_ARGS' is called with the option name in the '$1'
+and, if option has argument, its value in the '$2'.
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Optional test positional parameters
+
+TST_POS_ARGS=3
+TST_USAGE=usage
+TST_TESTFUNC=do_test
+
+. tst_test.sh
+
+usage()
+{
+	cat << EOF
+usage: $0 [min] [max] [size]
+
+EOF
+}
+
+min="$1"
+max="$2"
+size="$3"
+
+do_test()
+{
+	...
+}
+
+tst_run
+-------------------------------------------------------------------------------
+
+You can also request a number of positional parameters by setting the
+'$TST_POS_ARGS' variable. If you do, these will be available as they were
+passed directly to the script in '$1', '$2', ..., '$n'.
+
+1.4 Useful library functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Retrieving configuration variables
+++++++++++++++++++++++++++++++++++
+
+You may need to retrieve configuration values such as PAGESIZE, there is
+'getconf' but as some system may not have it, you are advised to use
+'tst_getconf' instead. Note that it implements subset of 'getconf'
+system variables used by the testcases only.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# retrieve PAGESIZE
+pagesize=`tst_getconf PAGESIZE`
+-------------------------------------------------------------------------------
+
+Sleeping for subsecond intervals
+++++++++++++++++++++++++++++++++
+
+Albeit there is a sleep command available basically everywhere not all
+implementations can support sleeping for less than one second. And most of the
+time sleeping for a second is too much. Therefore LTP includes 'tst_sleep'
+that can sleep for defined amount of seconds, milliseconds or microseconds.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# sleep for 100 milliseconds
+tst_sleep 100ms
+-------------------------------------------------------------------------------
+
+Retry a function call multiple times
+++++++++++++++++++++++++++++++++++++
+
+Sometimes an LTP test needs to retry a function call multiple times because
+the system is not ready to process it successfully on the first try. The LTP
+library has useful tools to handle the call retry automatically.
+'TST_RETRY_FUNC()' will keep retrying for up to 1 second. If you want a custom
+time limit use 'TST_RETRY_FN_EXP_BACKOFF()'. Both methods return the value
+returned by the last 'FUNC' call.
+
+The delay between retries starts@1 microsecond and doubles after each call.
+The retry loop ends when the function call succeeds or when the next delay
+exceeds the specified time (1 second for 'TST_RETRY_FUNC()'). The maximum
+delay is multiplied by TST_TIMEOUT_MUL. The total cumulative delay may be up
+to twice as long as the adjusted maximum delay.
+
+The C version of 'TST_RETRY_FUNC()' is a macro which takes two arguments:
+
+* 'FUNC' is the complete function call with arguments which should be retried
+  multiple times.
+* 'SUCCESS_CHECK' is a macro or function which will validate 'FUNC' return
+  value. 'FUNC' call was successful if 'SUCCESS_CHECK(ret)' evaluates to
+  non-zero.
+
+Both retry methods clear 'errno' before every 'FUNC' call so your
+'SUCCESS_CHECK' can look for specific error codes as well. The LTP library
+also includes predefined 'SUCCESS_CHECK' macros for the most common call
+conventions:
+
+* 'TST_RETVAL_EQ0()' - The call was successful if 'FUNC' returned 0 or NULL
+* 'TST_RETVAL_NOTNULL()' - The call was successful if 'FUNC' returned any
+  value other than 0 or NULL.
+* 'TST_RETVAL_GE0()' - The call was successful if 'FUNC' returned value >= 0.
+
+[source,c]
+-------------------------------------------------------------------------------
+/* Keep trying for 1 second */
+TST_RETRY_FUNC(FUNC, SUCCESS_CHECK)
+
+/* Keep trying for up to 2*N seconds */
+TST_RETRY_FN_EXP_BACKOFF(FUNC, SUCCESS_CHECK, N)
+-------------------------------------------------------------------------------
+
+The shell version of 'TST_RETRY_FUNC()' is simpler and takes slightly
+different arguments:
+
+* 'FUNC' is a string containing the complete function or program call with
+  arguments.
+* 'EXPECTED_RET' is a single expected return value. 'FUNC' call was successful
+  if the return value is equal to EXPECTED_RET.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# Keep trying for 1 second
+TST_RETRY_FUNC "FUNC arg1 arg2 ..." "EXPECTED_RET"
+
+# Keep trying for up to 2*N seconds
+TST_RETRY_FN_EXP_BACKOFF "FUNC arg1 arg2 ..." "EXPECTED_RET" "N"
+-------------------------------------------------------------------------------
+
+Checking for integers
++++++++++++++++++++++
+
+[source,sh]
+-------------------------------------------------------------------------------
+# returns zero if passed an integer parameter, non-zero otherwise
+tst_is_int "$FOO"
+-------------------------------------------------------------------------------
+
+Checking for integers and floating point numbers
+++++++++++++++++++++++++++++++++++++++++++++++++
+
+[source,sh]
+-------------------------------------------------------------------------------
+# returns zero if passed an integer or floating point number parameter,
+# non-zero otherwise
+tst_is_num "$FOO"
+-------------------------------------------------------------------------------
+
+Obtaining random numbers
+++++++++++++++++++++++++
+
+There is no '$RANDOM' in portable shell, use 'tst_random' instead.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# get random integer between 0 and 1000 (including 0 and 1000)
+tst_random 0 1000
+-------------------------------------------------------------------------------
+
+Formatting device with a filesystem
++++++++++++++++++++++++++++++++++++
+
+The 'tst_mkfs' helper will format device with the filesystem.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# format test device with ext2
+tst_mkfs ext2 $TST_DEVICE
+# default params are $TST_FS_TYPE $TST_DEVICE
+tst_mkfs
+# optional parameters
+tst_mkfs ext4 /dev/device -T largefile
+-------------------------------------------------------------------------------
+
+Mounting and unmounting filesystems
++++++++++++++++++++++++++++++++++++
+
+The 'tst_mount' and 'tst_umount' helpers are a safe way to mount/umount
+a filesystem.
+
+The 'tst_mount' mounts '$TST_DEVICE' of '$TST_FS_TYPE' (optional) to
+'$TST_MNTPOINT' (defaults to mntpoint), optionally using the
+'$TST_MNT_PARAMS'. The '$TST_MNTPOINT' directory is created if it didn't
+exist prior to the function call.
+
+If the path passed (optional, defaults to '$TST_DEVICE') to the 'tst_umount' is
+not mounted (present in '/proc/mounts') it's noop.
+Otherwise it retries to umount the filesystem a few times on a failure, which
+is a workaround since there are a daemons dumb enough to probe all newly
+mounted filesystems, which prevents them from umounting shortly after they
+were mounted.
+
+ROD and ROD_SILENT
+++++++++++++++++++
+
+These functions supply the 'SAFE_MACROS' used in C although they work and are
+named differently.
+
+[source,sh]
+-------------------------------------------------------------------------------
+ROD_SILENT command arg1 arg2 ...
+
+# is shorthand for:
+
+command arg1 arg2 ... > /dev/null 2>&1
+if [ $? -ne 0 ]; then
+        tst_brk TBROK "..."
+fi
+
+
+ROD command arg1 arg2 ...
+
+# is shorthand for:
+
+ROD arg1 arg2 ...
+if [ $? -ne 0 ]; then
+        tst_brk TBROK "..."
+fi
+-------------------------------------------------------------------------------
+
+WARNING: Keep in mind that output redirection (to a file) happens in the
+         caller rather than in the ROD function and cannot be checked for
+         write errors by the ROD function.
+
+As a matter of a fact doing +ROD echo a > /proc/cpuinfo+ would work just fine
+since the 'ROD' function will only get the +echo a+ part that will run just
+fine.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# Redirect output to a file with ROD
+ROD echo foo \> bar
+-------------------------------------------------------------------------------
+
+Note the '>' is escaped with '\', this causes that the '>' and filename are
+passed to the 'ROD' function as parameters and the 'ROD' function contains
+code to split '$@' on '>' and redirects the output to the file.
+
+EXPECT_PASS{,_BRK} and EXPECT_FAIL{,_BRK}
++++++++++++++++++++++++++++++++++++++++++
+
+[source,sh]
+-------------------------------------------------------------------------------
+EXPECT_PASS command arg1 arg2 ... [ \> file ]
+EXPECT_FAIL command arg1 arg2 ... [ \> file ]
+-------------------------------------------------------------------------------
+
+'EXPECT_PASS' calls 'tst_res TPASS' if the command exited with 0 exit code,
+and 'tst_res TFAIL' otherwise. 'EXPECT_FAIL' does vice versa.
+
+Output redirection rules are the same as for the 'ROD' function. In addition
+to that, 'EXPECT_FAIL' always redirects the command's stderr to '/dev/null'.
+
+There are also 'EXPECT_PASS_BRK' and 'EXPECT_FAIL_BRK', which works the same way
+except breaking a test when unexpected action happen.
+
+It's possible to detect whether expected value happened:
+[source,sh]
+-------------------------------------------------------------------------------
+if ! EXPECT_PASS command arg1 2\> /dev/null; then
+	continue
+fi
+-------------------------------------------------------------------------------
+
+tst_kvcmp
++++++++++
+
+This command compares the currently running kernel version given conditions
+with syntax similar to the shell test command.
+
+[source,sh]
+-------------------------------------------------------------------------------
+# Exit the test if kernel version is older or equal to 2.6.8
+if tst_kvcmp -le 2.6.8; then
+	tst_brk TCONF "Kernel newer than 2.6.8 is needed"
+fi
+
+# Exit the test if kernel is newer than 3.8 and older than 4.0.1
+if tst_kvcmp -gt 3.8 -a -lt 4.0.1; then
+	tst_brk TCONF "Kernel must be older than 3.8 or newer than 4.0.1"
+fi
+-------------------------------------------------------------------------------
+
+[options="header"]
+|=======================================================================
+| expression | description
+| -eq kver   | Returns true if kernel version is equal
+| -ne kver   | Returns true if kernel version is not equal
+| -gt kver   | Returns true if kernel version is greater
+| -ge kver   | Returns true if kernel version is greater or equal
+| -lt kver   | Returns true if kernel version is lesser
+| -le kver   | Returns true if kernel version is lesser or equal
+| -a         | Does logical and between two expressions
+| -o         | Does logical or between two expressions
+|=======================================================================
+
+The format for kernel version has to either be with one dot e.g. '2.6' or with
+two dots e.g. '4.8.1'.
+
+.tst_fs_has_free
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+
+...
+
+# whether current directory has 100MB free space at least.
+if ! tst_fs_has_free . 100MB; then
+	tst_brkm TCONF "Not enough free space"
+fi
+
+...
+-------------------------------------------------------------------------------
+
+The 'tst_fs_has_free' shell interface returns 0 if the specified free space is
+satisfied, 1 if not, and 2 on error.
+
+The second argument supports suffixes kB, MB and GB, the default unit is Byte.
+
+.tst_retry
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+
+...
+
+# Retry ping command three times
+tst_retry "ping -c 1 127.0.0.1"
+
+if [ $? -ne 0 ]; then
+	tst_resm TFAIL "Failed to ping 127.0.0.1"
+else
+	tst_resm TPASS "Successfully pinged 127.0.0.1"
+fi
+
+...
+-------------------------------------------------------------------------------
+
+The 'tst_retry' function allows you to retry a command after waiting small
+amount of time until it succeeds or until given amount of retries has been
+reached (default is three attempts).
+
+1.5 Restarting daemons
+~~~~~~~~~~~~~~~~~~~~~~
+
+Restarting system daemons is a complicated task for two reasons.
+
+* There are different init systems
+  (SysV init, systemd, etc...)
+
+* Daemon names are not unified between distributions
+  (apache vs httpd, cron vs crond, various syslog variations)
+
+To solve these problems LTP has 'testcases/lib/daemonlib.sh' library that
+provides functions to start/stop/query daemons as well as variables that store
+correct daemon name.
+
+.Supported operations
+|==============================================================================
+| start_daemon()   | Starts daemon, name is passed as first parameter.
+| stop_daemon()    | Stops daemon, name is passed as first parameter.
+| restart_daemon() | Restarts daemon, name is passed as first parameter.
+| status_daemon()  | Detect daemon status (exit code: 0: running, 1: not running).
+|==============================================================================
+
+.Variables with detected names
+|==============================================================================
+| CROND_DAEMON | Cron daemon name (cron, crond).
+| SYSLOG_DAEMON | Syslog daemon name (syslog, syslog-ng, rsyslog).
+|==============================================================================
+
+.Cron daemon restart example
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Cron daemon restart example
+
+TCID=cron01
+TST_COUNT=1
+. test.sh
+. daemonlib.sh
+
+...
+
+restart_daemon $CROND_DAEMON
+
+...
+
+tst_exit
+-------------------------------------------------------------------------------
+
+1.6 Access to the checkpoint interface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The shell library provides an implementation of the checkpoint interface
+compatible with the C version. All 'TST_CHECKPOINT_*' functions are available.
+
+In order to initialize checkpoints '$TST_NEEDS_CHECKPOINTS' must be set to '1'
+before the inclusion of 'test.sh':
+
+[source,sh]
+-------------------------------------------------------------------------------
+#!/bin/sh
+
+TST_NEEDS_CHECKPOINTS=1
+. test.sh
+-------------------------------------------------------------------------------
+
+Since both the implementations are compatible, it's also possible to start
+a child binary process from a shell test and synchronize with it. This process
+must have checkpoints initialized by calling 'tst_reinit()'.
diff --git a/doc/test-writing-guidelines.txt b/doc/test-writing-guidelines.txt
index c268b8804..0accac704 100644
--- a/doc/test-writing-guidelines.txt
+++ b/doc/test-writing-guidelines.txt
@@ -5,6 +5,10 @@ This document describes LTP guidelines and LTP test interface and is intended
 for anybody who want to write or modify a LTP testcase. It's not a definitive
 guide and it's not, by any means, a substitute for common sense.
 
+NOTE: See also
+      https://github.com/linux-test-project/ltp/wiki/C-Test-API[C Test API],
+      https://github.com/linux-test-project/ltp/wiki/Shell-Test-API[Shell Test API].
+
 1. General Rules
 ----------------
 
@@ -154,7 +158,9 @@ separate patch.
 ~~~~~~~~~~~
 
 Code contributed to LTP should be licensed under GPLv2+ (GNU GPL version 2 or
-any later version). Use `SPDX-License-Identifier: GPL-2.0-or-later`.
+any later version).
+
+Use `SPDX-License-Identifier: GPL-2.0-or-later`
 
 2. Writing a testcase
 ---------------------
@@ -232,2985 +238,11 @@ binaries is added to the '$PATH' and you can execute it just by its name.
 TIP: If you need to execute such test from the LTP tree, you can add path to
      current directory to '$PATH' manually with: 'PATH="$PATH:$PWD" ./foo01'.
 
-2.2 Writing a test in C
-~~~~~~~~~~~~~~~~~~~~~~~
-
-2.2.1 Basic test structure
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Let's start with an example, following code is a simple test for a 'getenv()'.
-
-[source,c]
--------------------------------------------------------------------------------
-/*
- * This is test for basic functionality of getenv().
- *
- *  - create an env variable and verify that getenv() can get get it
- *  - call getenv() with nonexisting variable name, check that it returns NULL
- */
-
-#include "tst_test.h"
-
-#define ENV1 "LTP_TEST_ENV"
-#define ENV2 "LTP_TEST_THIS_DOES_NOT_EXIST"
-#define ENV_VAL "val"
-
-static void setup(void)
-{
-	if (setenv(ENV1, ENV_VAL, 1))
-		tst_brk(TBROK | TERRNO, "setenv() failed");
-}
-
-static void test(void)
-{
-	char *ret;
-
-	ret = getenv(ENV1);
-
-	if (!ret) {
-		tst_res(TFAIL, "getenv(" ENV1 ") = NULL");
-		goto next;
-	}
-
-	if (!strcmp(ret, ENV_VAL)) {
-		tst_res(TPASS, "getenv(" ENV1 ") = '"ENV_VAL "'");
-	} else {
-		tst_res(TFAIL, "getenv(" ENV1 ") = '%s', expected '"
-		               ENV_VAL "'", ret);
-	}
-
-next:
-	ret = getenv(ENV2);
-
-	if (ret)
-		tst_res(TFAIL, "getenv(" ENV2 ") = '%s'", ret);
-	else
-		tst_res(TPASS, "getenv(" ENV2 ") = NULL");
-}
-
-static struct tst_test test = {
-	.test_all = test,
-	.setup = setup,
-};
--------------------------------------------------------------------------------
-
-Each test includes the 'tst_test.h' header and must define the 'struct
-tst_test test' structure.
-
-The overall test initialization is done in the 'setup()' function.
-
-The overall cleanup is done in a 'cleanup()' function. Here 'cleanup()' is
-omitted as the test does not have anything to clean up. If cleanup is set in
-the test structure it's called on test exit just before the test library
-cleanup. That especially means that cleanup can be called at any point in a
-test execution. For example even when a test setup step has failed, therefore
-the 'cleanup()' function must be able to cope with unfinished initialization,
-and so on.
-
-The test itself is done in the 'test()' function. The test function must work
-fine if called in a loop.
-
-There are two types of a test function pointers in the test structure. The
-first one is a '.test_all' pointer that is used when test is implemented as a
-single function. Then there is a '.test' function along with the number of
-tests '.tcnt' that allows for more detailed result reporting. If the '.test'
-pointer is set the function is called '.tcnt' times with an integer parameter
-in range of [0, '.tcnt' - 1].
-
-IMPORTANT: Only one of '.test' and '.test_all' can be set at a time.
-
-Each test has a default timeout set to 300s. The default timeout can be
-overridden by setting '.timeout' in the test structure or by calling
-'tst_set_timeout()' in the test 'setup()'. There are a few testcases whose run
-time may vary arbitrarily, for these timeout can be disabled by setting it to
--1.
-
-Test can find out how much time (in seconds) is remaining to timeout,
-by calling 'tst_timeout_remaining()'.
-
-LAPI headers
-++++++++++++
-
-Use our LAPI headers ('include "lapi/foo.h"') to keep compatibility with old
-distributions. LAPI header should always include original header. Older linux
-headers were problematic, therefore we preferred to use libc headers. There are
-still some bugs when combining certain glibc headers with linux headers, see
-https://sourceware.org/glibc/wiki/Synchronizing_Headers.
-
-A word about the cleanup() callback
-+++++++++++++++++++++++++++++++++++
-
-There are a few rules that needs to be followed in order to write correct
-cleanup() callback.
-
-1. Free only resources that were initialized. Keep in mind that callback can
-   be executed at any point in the test run.
-
-2. Make sure to free resources in the reverse order they were
-   initialized. (Some of the steps may not depend on others and everything
-   will work if there were swapped but let's keep it in order.)
-
-The first rule may seem complicated at first however, on the contrary, it's
-quite easy. All you have to do is to keep track of what was already
-initialized. For example file descriptors needs to be closed only if they were
-assigned a valid file descriptor. For most of the things you need to create
-extra flag that is set right after successful initialization though. Consider,
-for example, test setup below.
-
-We also prefer cleaning up resources that would otherwise be released on the
-program exit. There are two main reasons for this decision. Resources such as
-file descriptors and mmaped memory could block umounting a block device in
-cases where the test library has mounted a filesystem for the test temporary
-directory. Not freeing allocated memory would upset static analysis and tools
-such as valgrind and produce false-positives when checking for leaks in the
-libc and other low level libraries.
-
-[source,c]
--------------------------------------------------------------------------------
-static int fd0, fd1, mount_flag;
-
-#define MNTPOINT "mntpoint"
-#define FILE1 "mntpoint/file1"
-#define FILE2 "mntpoint/file2"
-
-static void setup(void)
-{
-	SAFE_MKDIR(MNTPOINT, 0777);
-	SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL);
-	SAFE_MOUNT(tst_device->dev, MNTPOINT, tst_device->fs_type, 0, 0);
-	mount_flag = 1;
-
-	fd0 = SAFE_OPEN(cleanup, FILE1, O_CREAT | O_RDWR, 0666);
-	fd1 = SAFE_OPEN(cleanup, FILE2, O_CREAT | O_RDWR, 0666);
-}
--------------------------------------------------------------------------------
-
-In this case the 'cleanup()' function may be invoked when any of the 'SAFE_*'
-macros has failed and therefore must be able to work with unfinished
-initialization as well. Since global variables are initialized to zero we can
-just check that fd > 0 before we attempt to close it. The mount function
-requires extra flag to be set after device was successfully mounted.
-
-[source,c]
--------------------------------------------------------------------------------
-static void cleanup(void)
-{
-	if (fd1 > 0)
-		SAFE_CLOSE(fd1);
-
-	if (fd0 > 0)
-		SAFE_CLOSE(fd0);
-
-	if (mount_flag && tst_umouont(MNTPOINT))
-		tst_res(TWARN | TERRNO, "umount(%s)", MNTPOINT);
-}
--------------------------------------------------------------------------------
-
-IMPORTANT: 'SAFE_MACROS()' used in cleanup *do not* exit the test. Failure
-           only produces a warning and the 'cleanup()' carries on. This is
-	   intentional as we want to execute as much 'cleanup()' as possible.
-
-WARNING: Calling tst_brk() in test 'cleanup()' does not exit the test as well
-         and 'TBROK' is converted to 'TWARN'.
-
-NOTE: Creation and removal of the test temporary directory is handled in
-      the test library and the directory is removed recursively. Therefore
-      we do not have to remove files and directories in the test cleanup.
-
-2.2.2 Basic test interface
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-void tst_res(int ttype, char *arg_fmt, ...);
--------------------------------------------------------------------------------
-
-Printf-like function to report test result, it's mostly used with ttype:
-
-|==============================
-| 'TPASS' | Test has passed.
-| 'TFAIL' | Test has failed.
-| 'TINFO' | General message.
-| 'TWARN' | Something went wrong but we decided to continue. Mostly used in cleanup functions.
-|==============================
-
-The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print
-'errno', 'TST_ERR' respectively.
-
-[source,c]
--------------------------------------------------------------------------------
-void tst_brk(int ttype, char *arg_fmt, ...);
--------------------------------------------------------------------------------
-
-Printf-like function to report error and exit the test, it can be used with ttype:
-
-|============================================================
-| 'TBROK' | Something has failed in test preparation phase.
-| 'TCONF' | Test is not appropriate for current configuration
-            (syscall not implemented, unsupported arch, ...)
-|============================================================
-
-The 'ttype' can be combined bitwise with 'TERRNO' or 'TTERRNO' to print
-'errno', 'TST_ERR' respectively.
-
-There are also 'TST_EXP_*()' macros that can simplify syscall unit tests to a
-single line, use them whenever possible. 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.
-
-[source,c]
--------------------------------------------------------------------------------
-static void test(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.
-
-[source,c]
--------------------------------------------------------------------------------
-static void test(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.
-
-[source,c]
--------------------------------------------------------------------------------
-static void test(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.
-
-[source,c]
--------------------------------------------------------------------------------
-const char *tst_strsig(int sig);
--------------------------------------------------------------------------------
-
-Return the given signal number's corresponding string.
-
-[source,c]
--------------------------------------------------------------------------------
-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.
-
-[source,c]
--------------------------------------------------------------------------------
-const char *tst_strstatus(int status);
--------------------------------------------------------------------------------
-
-Returns string describing the status as returned by 'wait()'.
-
-WARNING: This function is not thread safe.
-
-[source,c]
--------------------------------------------------------------------------------
-void tst_set_timeout(unsigned int timeout);
--------------------------------------------------------------------------------
-
-Allows for setting timeout 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 -1.
-
-[source,c]
--------------------------------------------------------------------------------
-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 3.4 FILE buffers and fork() for explanation
-why is this needed.
-
-2.2.3 Test temporary directory
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If '.needs_tmpdir' is set to '1' in the 'struct tst_test' unique test
-temporary is created and it's set as the test working directory. Tests *MUST
-NOT* create temporary files outside that directory. The flag is not needed to
-be set when use these flags: '.all_filesystems', '.format_device', '.mntpoint',
-'.mount_device' '.needs_checkpoints', '.needs_device', '.resource_file'
-(these flags imply creating temporary directory).
-
-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").
-
-2.2.4 Safe macros
-^^^^^^^^^^^^^^^^^
-
-Safe macros aim to simplify error checking in test preparation. Instead of
-calling system API functions, checking for their return value and aborting the
-test if the operation has failed, you just use corresponding safe macro.
-
-Use them whenever it's possible.
-
-Instead of writing:
-
-[source,c]
--------------------------------------------------------------------------------
-	fd = open("/dev/null", O_RDONLY);
-	if (fd < 0)
-		tst_brk(TBROK | TERRNO, "opening /dev/null failed");
--------------------------------------------------------------------------------
-
-You write just:
-
-[source,c]
--------------------------------------------------------------------------------
-	fd = SAFE_OPEN("/dev/null", O_RDONLY);
--------------------------------------------------------------------------------
-
-IMPORTANT: The SAFE_CLOSE() function also sets the passed file descriptor to -1
-           after it's successfully closed.
-
-They can also simplify reading and writing of sysfs files, you can, for
-example, do:
-
-[source,c]
--------------------------------------------------------------------------------
-	SAFE_FILE_SCANF("/proc/sys/kernel/pid_max", "%lu", &pid_max);
--------------------------------------------------------------------------------
-
-See 'include/tst_safe_macros.h', 'include/tst_safe_stdio.h' and
-'include/tst_safe_file_ops.h' and 'include/tst_safe_net.h' for a complete list.
-
-2.2.5 Test specific command line options
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-struct tst_option {
-        char *optstr;
-        char **arg;
-        char *help;
-};
--------------------------------------------------------------------------------
-
-Test specific command line parameters can be passed with the 'NULL' terminated
-array of 'struct tst_option'. The 'optstr' is the command line option i.e. "o"
-or "o:" if option has a parameter. Only short options are supported. The 'arg'
-is where 'optarg' is stored upon match. If option has no parameter it's set to
-non-'NULL' value if option was present. The 'help' is a short help string.
-
-NOTE: The test parameters must not collide with common test parameters defined
-      in the library the currently used ones are +-i+, +-I+, +-C+, and +-h+.
-
-[source,c]
--------------------------------------------------------------------------------
-int tst_parse_int(const char *str, int *val, int min, int max);
-int tst_parse_float(const char *str, float *val, float min, float max);
--------------------------------------------------------------------------------
-
-Helpers for parsing the strings returned in the 'struct tst_option'.
-
-Both return zero on success and 'errno', mostly 'EINVAL' or 'ERANGE', on
-failure.
-
-Both functions are no-op if 'str' is 'NULL'.
-
-The valid range for result includes both 'min' and 'max'.
-
-.Example Usage
-[source,c]
--------------------------------------------------------------------------------
-#include <limits.h>
-#include "tst_test.h"
-
-static char *str_threads;
-static int threads = 10;
-
-static struct tst_option options[] = {
-	{"t:", &str_threads, "Number of threads (default 10)"},
-	...
-	{NULL, NULL, NULL}
-};
-
-static void setup(void)
-{
-	if (tst_parse_int(str_threads, &threads, 1, INT_MAX))
-		tst_brk(TBROK, "Invalid number of threads '%s'", str_threads);
-
-	...
-}
-
-static void test_threads(void)
-{
-	...
-
-	for (i = 0; i < threads; i++) {
-		...
-	}
-
-	...
-}
-
-static struct tst_test test = {
-	...
-	.options = options,
-	...
-};
--------------------------------------------------------------------------------
-
-
-2.2.6 Runtime kernel version detection
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Testcases for newly added kernel functionality require kernel newer than a
-certain version to run. All you need to skip a test on older kernels is to
-set the '.min_kver' string in the 'struct tst_test' to a minimal required
-kernel version, e.g. '.min_kver = "2.6.30"'.
-
-For more complicated operations such as skipping a test for a certain range
-of kernel versions, following functions could be used:
-
-[source,c]
--------------------------------------------------------------------------------
-int tst_kvercmp(int r1, int r2, int r3);
-
-struct tst_kern_exv {
-        char *dist_name;
-        char *extra_ver;
-};
-
-int tst_kvercmp2(int r1, int r2, int r3, struct tst_kern_exv *vers);
--------------------------------------------------------------------------------
-
-These two functions are intended for runtime kernel version detection. They
-parse the output from 'uname()' and compare it to the passed values.
-
-The return value is similar to the 'strcmp()' function, i.e. zero means equal,
-negative value means that the kernel is older than than the expected value and
-positive means that it's newer.
-
-The second function 'tst_kvercmp2()' allows for specifying per-vendor table of
-kernel versions as vendors typically backport fixes to their kernels and the
-test may be relevant even if the kernel version does not suggests so. See
-'testcases/kernel/syscalls/inotify/inotify04.c' for example usage.
-
-WARNING: The shell 'tst_kvercmp' maps the result into unsigned integer - the
-         process exit value.
-
-2.2.7 Fork()-ing
-^^^^^^^^^^^^^^^^
-
-Be wary that if the test forks and there were messages printed by the
-'tst_*()' interfaces, the data may still be in libc/kernel buffers and these
-*ARE NOT* flushed automatically.
-
-This happens when 'stdout' gets redirected to a file. In this case, the
-'stdout' is not line buffered, but block buffered. Hence after a fork content
-of the buffers will be printed by the parent and each of the children.
-
-To avoid that you should use 'SAFE_FORK()', 'SAFE_CLONE()' or 'tst_clone()'.
-
-IMPORTANT: You have to set the '.forks_child' flag in the test structure
-           if your testcase forks or calls 'SAFE_CLONE()'.
-
-2.2.8 Doing the test in the child process
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Results reported by 'tst_res()' are propagated to the parent test process via
-block of shared memory.
-
-Calling 'tst_brk()' causes child process to exit with non-zero exit value.
-Which means that it's safe to use 'SAFE_*()' macros in the child processes as
-well.
-
-Children that outlive the 'test()' function execution are waited for in the
-test library. Unclean child exit (killed by signal, non-zero exit value, etc.)
-will cause the main test process to exit with 'tst_brk()', which especially
-means that 'TBROK' propagated from a child process will cause the whole test
-to exit with 'TBROK'.
-
-If a test needs a child that segfaults or does anything else that cause it to
-exit uncleanly all you need to do is to wait for such children from the
-'test()' function so that it's reaped before the main test exits the 'test()'
-function.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-void tst_reap_children(void);
--------------------------------------------------------------------------------
-
-The 'tst_reap_children()' function makes the process wait for all of its
-children and exits with 'tst_brk(TBROK, ...)' if any of them returned
-a non zero exit code.
-
-When using 'SAFE_CLONE' or 'tst_clone', this may not work depending on
-the parameters passed to clone. The following call to 'SAFE_CLONE' is
-identical to 'fork()', so will work as expected.
-
-[source,c]
---------------------------------------------------------------------------------
-const struct tst_clone_args args = {
-	.exit_signal = SIGCHLD,
-};
-
-SAFE_CLONE(&args);
---------------------------------------------------------------------------------
-
-If 'exit_signal' is set to something else, then this will break
-'tst_reap_children'. It's not expected that all parameters to clone will
-work with the LTP library unless specific action is taken by the test code.
-
-.Using 'tst_res()' from binaries started by 'exec()'
-[source,c]
--------------------------------------------------------------------------------
-/* test.c */
-#define _GNU_SOURCE
-#include <unistd.h>
-#include "tst_test.h"
-
-static void do_test(void)
-{
-	char *const argv[] = {"test_exec_child", NULL};
-	char path[4096];
-
-	if (tst_get_path("test_exec_child", path, sizeof(path)))
-		tst_brk(TCONF, "Couldn't find test_exec_child in $PATH");
-
-	execve(path, argv, environ);
-
-	tst_res(TFAIL | TERRNO, "EXEC!");
-}
-
-static struct tst_test test = {
-	.test_all = do_test,
-	.child_needs_reinit = 1,
-};
-
-/* test_exec_child.c */
-#define TST_NO_DEFAULT_MAIN
-#include "tst_test.h"
-
-int main(void)
-{
-	tst_reinit();
-	tst_res(TPASS, "Child passed!");
-	return 0;
-}
--------------------------------------------------------------------------------
-
-The 'tst_res()' function can be also used from binaries started by 'exec()',
-the parent test process has to set the '.child_needs_reinit' flag so that the
-library prepares for it and has to make sure the 'LTP_IPC_PATH' environment
-variable is passed down, then the very fist thing the program has to call in
-'main()' is 'tst_reinit()' that sets up the IPC.
-
-2.2.9 Fork() and Parent-child synchronization
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As LTP tests are written for Linux, most of the tests involve fork()-ing and
-parent-child process synchronization. LTP includes a checkpoint library that
-provides wait/wake futex based functions.
-
-In order to use checkpoints the '.needs_checkpoints' flag in the 'struct
-tst_test' must be set to '1', this causes the test library to initialize
-checkpoints before the 'test()' function is called.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-TST_CHECKPOINT_WAIT(id)
-
-TST_CHECKPOINT_WAIT2(id, msec_timeout)
-
-TST_CHECKPOINT_WAKE(id)
-
-TST_CHECKPOINT_WAKE2(id, nr_wake)
-
-TST_CHECKPOINT_WAKE_AND_WAIT(id)
--------------------------------------------------------------------------------
-
-The checkpoint interface provides pair of wake and wait functions. The 'id' is
-unsigned integer which specifies checkpoint to wake/wait for. As a matter of
-fact it's an index to an array stored in a shared memory, so it starts on
-'0' and there should be enough room for at least of hundred of them.
-
-The 'TST_CHECKPOINT_WAIT()' and 'TST_CHECKPOINT_WAIT2()' suspends process
-execution until it's woken up or until timeout is reached.
-
-The 'TST_CHECKPOINT_WAKE()' wakes one process waiting on the checkpoint.
-If no process is waiting the function retries until it success or until
-timeout is reached.
-
-If timeout has been reached process exits with appropriate error message (uses
-'tst_brk()').
-
-The 'TST_CHECKPOINT_WAKE2()' does the same as 'TST_CHECKPOINT_WAKE()' but can
-be used to wake precisely 'nr_wake' processes.
-
-The 'TST_CHECKPOINT_WAKE_AND_WAIT()' is a shorthand for doing wake and then
-immediately waiting on the same checkpoint.
-
-Child processes created via 'SAFE_FORK()' are ready to use the checkpoint
-synchronization functions, as they inherited the mapped page automatically.
-
-Child processes started via 'exec()', or any other processes not forked from
-the test process must initialize the checkpoint by calling 'tst_reinit()'.
-
-For the details of the interface, look into the 'include/tst_checkpoint.h'.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-/*
- * Waits for process state change.
- *
- * The state is one of the following:
- *
- * R - process is running
- * S - process is sleeping
- * D - process sleeping uninterruptibly
- * Z - zombie process
- * T - process is traced
- */
-TST_PROCESS_STATE_WAIT(pid, state, msec_timeout)
--------------------------------------------------------------------------------
-
-The 'TST_PROCESS_STATE_WAIT()' waits until process 'pid' is in requested
-'state' or timeout is reached. The call polls +/proc/pid/stat+ to get this
-information. A timeout of 0 will wait infinitely.
-
-On timeout -1 is returned and errno set to ETIMEDOUT.
-
-It's mostly used with state 'S' which means that process is sleeping in kernel
-for example in 'pause()' or any other blocking syscall.
-
-2.2.10 Signals and signal handlers
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If you need to use signal handlers, keep the code short and simple. Don't
-forget that the signal handler is called asynchronously and can interrupt the
-code execution at any place.
-
-This means that problems arise when global state is changed both from the test
-code and signal handler, which will occasionally lead to:
-
-* Data corruption (data gets into inconsistent state), this may happen, for
-  example, for any operations on 'FILE' objects.
-
-* Deadlock, this happens, for example, if you call 'malloc(2)', 'free(2)',
-  etc. from both the test code and the signal handler at the same time since
-  'malloc' has global lock for it's internal data structures. (Be wary that
-  'malloc(2)' is used by the libc functions internally too.)
-
-* Any other unreproducible and unexpected behavior.
-
-Quite common mistake is to call 'exit(3)' from a signal handler. Note that this
-function is not signal-async-safe as it flushes buffers, etc. If you need to
-exit a test immediately from a signal handler use '_exit(2)' instead.
-
-TIP: See 'man 7 signal' for the list of signal-async-safe functions.
-
-If a signal handler sets a variable, its declaration must be 'volatile',
-otherwise compiler may misoptimize the code. This is because the variable may
-not be changed in the compiler code flow analysis. There is 'sig_atomic_t'
-type defined in C99 but this one *DOES NOT* imply 'volatile' (it's just a
-'typedef' to 'int'). So the correct type for a flag that is changed from a
-signal handler is either 'volatile int' or 'volatile sig_atomic_t'.
-
-If a crash (e.g. triggered by signal SIGSEGV) is expected in testing, you
-can avoid creation of core files by calling tst_no_corefile() function.
-This takes effect for process (and its children) which invoked it, unless
-they subsequently modify RLIMIT_CORE.
-
-Note that LTP library will reap any processes that test didn't reap itself,
-and report any non-zero exit code as failure.
-
-2.2.11 Kernel Modules
-^^^^^^^^^^^^^^^^^^^^^
-
-There are certain cases where the test needs a kernel part and userspace part,
-happily, LTP can build a kernel module and then insert it to the kernel on test
-start for you. See 'testcases/kernel/device-drivers/block' for details.
-
-2.2.12 Useful macros
-^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-ARRAY_SIZE(arr)
--------------------------------------------------------------------------------
-
-Returns the size of statically defined array, i.e.
-'(sizeof(arr) / sizeof(*arr))'
-
-[source,c]
--------------------------------------------------------------------------------
-LTP_ALIGN(x, a)
--------------------------------------------------------------------------------
-
-Aligns the x to be next multiple of a. The a must be power of 2.
-
-2.2.13 Filesystem type detection and skiplist
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests are known to fail on certain filesystems (you cannot swap on TMPFS,
-there are unimplemented 'fcntl()' etc.).
-
-If your test needs to be skipped on certain filesystems use the
-'.skip_filesystems' field in the tst_test structure as follows:
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static struct tst_test test = {
-	...
-        .skip_filesystems = (const char *const []) {
-                "tmpfs",
-                "ramfs",
-                "nfs",
-                NULL
-        },
-};
--------------------------------------------------------------------------------
-
-When the '.all_filesystem' 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.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void run(void)
-{
-	...
-
-	switch ((type = tst_fs_type("."))) {
-	case TST_NFS_MAGIC:
-	case TST_TMPFS_MAGIC:
-	case TST_RAMFS_MAGIC:
-		tst_brk(TCONF, "Subtest not supported on %s",
-		        tst_fs_type_name(type));
-		return;
-	break;
-	}
-
-	...
-}
--------------------------------------------------------------------------------
-
-2.2.14 Thread-safety in the LTP library
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-It is safe to use library 'tst_res()' function in multi-threaded tests.
-
-Only the main thread must return from the 'test()' function to the test
-library and that must be done only after all threads that may call any library
-function has been terminated. That especially means that threads that may call
-'tst_brk()' must terminate before the execution of the 'test()' function
-returns to the library. This is usually done by the main thread joining all
-worker threads at the end of the 'test()' function. Note that the main thread
-will never get to the library code in a case that 'tst_brk()' was called from
-one of the threads since it will sleep at least in 'pthread_join()' on the
-thread that called the 'tst_brk()' till 'exit()' is called by 'tst_brk()'.
-
-The test-supplied cleanup function runs *concurrently* to the rest of the
-threads in a case that cleanup was entered from 'tst_brk()'. Subsequent
-threads entering 'tst_brk()' must be suspended or terminated at the start of
-the user supplied cleanup function. It may be necessary to stop or exit
-the rest of the threads before the test cleans up as well. For example threads
-that create new files should be stopped before temporary directory is be
-removed.
-
-Following code example shows thread safe cleanup function example using atomic
-increment as a guard. The library calls its cleanup after the execution returns
-from the user supplied cleanup and expects that only one thread returns from
-the user supplied cleanup to the test library.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void cleanup(void)
-{
-	static int flag;
-
-	if (tst_atomic_inc(&flag) != 1)
-		pthread_exit(NULL);
-
-	/* if needed stop the rest of the threads here */
-
-	...
-
-	/* then do cleanup work */
-
-	...
-
-	/* only one thread returns to the library */
-}
--------------------------------------------------------------------------------
-
-
-2.2.15 Testing with a block device
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests needs a block device (inotify tests, syscall 'EROFS' failures,
-etc.). LTP library contains a code to prepare a testing device.
-
-If '.needs_device' flag in the 'struct tst_test' is set the 'tst_device'
-structure is initialized with a path to a test device and default filesystem
-to be used.
-
-You can also request minimal device size in megabytes by setting
-'.dev_min_size' the device is guaranteed to have at least the requested size
-then.
-
-If '.format_device' flag is set the device is formatted with a filesystem as
-well. You can use '.dev_fs_type' to override the default filesystem type if
-needed and pass additional options to mkfs via '.dev_fs_opts' and
-'.dev_extra_opts' pointers. Note that '.format_device' implies '.needs_device'
-there is no need to set both.
-
-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.
-
-If '.needs_rofs' is set, read-only filesystem is mounted at '.mntpoint' this
-one is supposed to be used for 'EROFS' tests.
-
-If '.all_filesystems' is set the test function is executed for all supported
-filesystems. Supported filesystems are detected based on existence of the
-'mkfs.$fs' helper and on kernel support to mount it. For each supported
-filesystem the 'tst_device.fs_type' is set to the currently tested fs type, if
-'.format_device' is set the device is formatted as well, if '.mount_device' is
-set it's mounted at '.mntpoint'. Also the test timeout is reset for each
-execution of the test function. This flag is expected to be used for filesystem
-related syscalls that are at least partly implemented in the filesystem
-specific code e.g. fallocate().
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-struct tst_device {
-	const char *dev;
-	const char *fs_type;
-};
-
-extern struct tst_device *tst_device;
+3. Test Contribution Checklist
+------------------------------
 
-int tst_umount(const char *path);
--------------------------------------------------------------------------------
-
-In case that 'LTP_DEV' is passed to the test in an environment, the library
-checks that the file exists and that it's a block device, if
-'.device_min_size' is set the device size is checked as well. If 'LTP_DEV'
-wasn't set or if size requirements were not met a temporary file is created
-and attached to a free loop device.
-
-If there is no usable device and loop device couldn't be initialized the test
-exits with 'TCONF'.
-
-The 'tst_umount()' function works exactly as 'umount(2)' but retries several
-times on 'EBUSY'. This is because various desktop daemons (gvfsd-trash is known
-for that) may be stupid enough to probe all newly mounted filesystem which
-results in 'umount(2)' failing with 'EBUSY'.
-
-IMPORTANT: All testcases should use 'tst_umount()' instead of 'umount(2)' to
-           umount filesystems.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_find_free_loopdev(const char *path, size_t path_len);
--------------------------------------------------------------------------------
-
-This function finds a free loopdev and returns the free loopdev minor (-1 for no
-free loopdev). If path is non-NULL, it will be filled with free loopdev path.
-If you want to use a customized loop device, we can call tst_find_free_loopdev
-(NULL, 0) in tests to get a free minor number and then mknod.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-unsigned long tst_dev_bytes_written(const char *dev);
--------------------------------------------------------------------------------
-
-This function reads test block device stat file (/sys/block/<device>/stat) and
-returns the bytes written since the last invocation of this function. To avoid
-FS deferred IO metadata/cache interference, we suggest doing "syncfs" before the
-tst_dev_bytes_written first invocation. And an inline function named tst_dev_sync
-is created for that intention.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-voud tst_find_backing_dev(const char *path, char *dev);
--------------------------------------------------------------------------------
-
-This function finds the block dev that this path belongs to, it uses stat function
-to get the major/minor number of the path. Then scan them in "/proc/self/mountinfo"
-and list 2th column value after ' - ' string as its block dev if match succeeds.
-
-2.2.16 Formatting a device with a filesystem
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void setup(void)
-{
-	...
-	SAFE_MKFS(tst_device->dev, tst_device->fs_type, NULL, NULL);
-	...
-}
--------------------------------------------------------------------------------
-
-This function takes a path to a device, filesystem type and an array of extra
-options passed to mkfs.
-
-The fs options 'fs_opts' should either be 'NULL' if there are none, or a
-'NULL' terminated array of strings such as:
-+const char *const opts[] = {"-b", "1024", NULL}+.
-
-The extra options 'extra_opts' should either be 'NULL' if there are none, or a
-'NULL' terminated array of strings such as +{"102400", NULL}+; 'extra_opts'
-will be passed after device name. e.g: +mkfs -t ext4 -b 1024 /dev/sda1 102400+
-in this case.
-
-Note that perfer to store the options which can be passed before or after device
-name by 'fs_opts' array.
-
-2.2.17 Verifying a filesystem's free space
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests have size requirements for the filesystem's free space. If these
-requirements are not satisfied, the tests should be skipped.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_fs_has_free(const char *path, unsigned int size, unsigned int mult);
--------------------------------------------------------------------------------
-
-The 'tst_fs_has_free()' function returns 1 if there is enough space and 0 if
-there is not.
-
-The 'path' is the pathname of any directory/file within a filesystem.
-
-The 'mult' is a multiplier, one of 'TST_BYTES', 'TST_KB', 'TST_MB' or 'TST_GB'.
-
-The required free space is calculated by 'size * mult', e.g.
-'tst_fs_has_free("/tmp/testfile", 64, TST_MB)' will return 1 if the
-filesystem, which '"/tmp/testfile"' is in, has 64MB free space at least, and 0
-if not.
-
-2.2.18 Files, directories and fs limits
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests need to know the maximum count of links to a regular file or
-directory, such as 'rename(2)' or 'linkat(2)' to test 'EMLINK' error.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_fs_fill_hardlinks(const char *dir);
--------------------------------------------------------------------------------
-
-Try to get maximum count of hard links to a regular file inside the 'dir'.
-
-NOTE: This number depends on the filesystem 'dir' is on.
-
-This function uses 'link(2)' to create hard links to a single file until it
-gets 'EMLINK' or creates 65535 links. If the limit is hit, the maximum number of
-hardlinks is returned and the 'dir' is filled with hardlinks in format
-"testfile%i", where i belongs to [0, limit) interval. If no limit is hit or if
-'link(2)' failed with 'ENOSPC' or 'EDQUOT', zero is returned and previously
-created files are removed.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_fs_fill_subdirs(const char *dir);
--------------------------------------------------------------------------------
-
-Try to get maximum number of subdirectories in directory.
-
-NOTE: This number depends on the filesystem 'dir' is on. For current kernel,
-subdir limit is not available for all filesystems (available for ext2, ext3,
-minix, sysv and more). If the test runs on some other filesystems, like ramfs,
-tmpfs, it will not even try to reach the limit and return 0.
-
-This function uses 'mkdir(2)' to create directories in 'dir' until it gets
-'EMLINK' or creates 65535 directories. If the limit is hit, the maximum number
-of subdirectories is returned and the 'dir' is filled with subdirectories in
-format "testdir%i", where i belongs to [0, limit - 2) interval (because each
-newly created dir has two links already - the '.' and the link from parent
-dir). If no limit is hit or if 'mkdir(2)' failed with 'ENOSPC' or 'EDQUOT',
-zero is returned and previously created directories are removed.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_dir_is_empty(const char *dir, int verbose);
--------------------------------------------------------------------------------
-
-Returns non-zero if directory is empty and zero otherwise.
-
-Directory is considered empty if it contains only '.' and '..'.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-void tst_purge_dir(const char *path);
--------------------------------------------------------------------------------
-
-Deletes the contents of given directory but keeps the directory itself. Useful
-for cleaning up the temporary directory and mount points between test cases or
-test iterations. Terminates the program with 'TBROK' on error.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_fill_fd(int fd, char pattern, size_t bs, size_t bcount);
--------------------------------------------------------------------------------
-
-Fill a file with specified pattern using file descriptor.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_prealloc_size_fd(int fd, size_t bs, size_t bcount);
--------------------------------------------------------------------------------
-
-Preallocate the specified amount of space using 'fallocate()'. Falls back to
-'tst_fill_fd()' if 'fallocate()' fails.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_fill_file(const char *path, char pattern, size_t bs, size_t bcount);
--------------------------------------------------------------------------------
-
-Creates/overwrites a file with specified pattern using file path.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_prealloc_file(const char *path, size_t bs, size_t bcount);
--------------------------------------------------------------------------------
-
-Create/overwrite a file and preallocate the specified amount of space for it.
-The allocated space will not be initialized to any particular content.
-
-2.2.19 Getting an unused PID number
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests require a 'PID', which is not used by the OS (does not belong to
-any process within it). For example, kill(2) should set errno to 'ESRCH' if
-it's passed such 'PID'.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-pid_t tst_get_unused_pid(void);
--------------------------------------------------------------------------------
-
-Return a 'PID' value not used by the OS or any process within it.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_get_free_pids(void);
--------------------------------------------------------------------------------
-
-Returns number of unused pids in the system. Note that this number may be
-different once the call returns and should be used only for rough estimates.
-
-2.2.20 Running executables
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-int tst_cmd(const char *const argv[],
-	        const char *stdout_path,
-	        const char *stderr_path,
-	        enum tst_cmd_flags flags);
--------------------------------------------------------------------------------
-
-'tst_cmd()' is a wrapper for 'vfork() + execvp()' which provides a way
-to execute an external program.
-
-'argv[]' is a 'NULL' terminated array of strings starting with the program name
-which is followed by optional arguments.
-
-'TST_CMD_PASS_RETVAL' enum 'tst_cmd_flags' makes 'tst_cmd()'
-return the program exit code to the caller, otherwise 'tst_cmd()' exit the
-tests on failure. 'TST_CMD_TCONF_ON_MISSING' check for program in '$PATH' and exit
-with 'TCONF' if not found.
-
-In case that 'execvp()' has failed and the enum 'TST_CMD_PASS_RETVAL' flag was set, the
-return value is '255' if 'execvp()' failed with 'ENOENT' and '254' otherwise.
-
-'stdout_path' and 'stderr_path' determine where to redirect the program
-stdout and stderr I/O streams.
-
-The 'SAFE_CMD()' macro can be used automatic handling non-zero exits (exits
-with 'TBROK') and 'ENOENT' (exits with 'TCONF').
-
-.Example
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-const char *const cmd[] = { "ls", "-l", NULL };
-
-...
-	/* Store output of 'ls -l' into log.txt */
-	tst_cmd(cmd, "log.txt", NULL, 0);
-...
--------------------------------------------------------------------------------
-
-2.2.21 Measuring elapsed time and helper functions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_timer.h"
-
-void tst_timer_check(clockid_t clk_id);
-
-void tst_timer_start(clockid_t clk_id);
-
-void tst_timer_stop(void);
-
-struct timespec tst_timer_elapsed(void);
-
-long long tst_timer_elapsed_ms(void);
-
-long long tst_timer_elapsed_us(void);
-
-int tst_timer_expired_ms(long long ms);
--------------------------------------------------------------------------------
-
-The 'tst_timer_check()' function checks if specified 'clk_id' is suppored and
-exits the test with 'TCONF' otherwise. It's expected to be used in test
-'setup()' before any resources that needs to be cleaned up are initialized,
-hence it does not include a cleanup function parameter.
-
-The 'tst_timer_start()' marks start time and stores the 'clk_id' for further
-use.
-
-The 'tst_timer_stop()' marks the stop time using the same 'clk_id' as last
-call to 'tst_timer_start()'.
-
-The 'tst_timer_elapsed*()' returns time difference between the timer start and
-last timer stop in several formats and units.
-
-The 'tst_timer_expired_ms()' function checks if the timer started by
-'tst_timer_start()' has been running longer than ms milliseconds. The function
-returns non-zero if timer has expired and zero otherwise.
-
-IMPORTANT: The timer functions use 'clock_gettime()' internally which needs to
-           be linked with '-lrt' on older glibc. Please do not forget to add
-	   'LDLIBS+=-lrt' in Makefile.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-#include "tst_timer.h"
-
-static void setup(void)
-{
-	...
-	tst_timer_check(CLOCK_MONOTONIC);
-	...
-}
-
-static void run(void)
-{
-	...
-	tst_timer_start(CLOCK_MONOTONIC);
-	...
-	while (!tst_timer_expired_ms(5000)) {
-		...
-	}
-	...
-}
-
-struct tst_test test = {
-	...
-	.setup = setup,
-	.test_all = run,
-	...
-};
--------------------------------------------------------------------------------
-
-Expiration timer example usage.
-
-[source,c]
--------------------------------------------------------------------------------
-long long tst_timespec_to_us(struct timespec t);
-long long tst_timespec_to_ms(struct timespec t);
-
-struct timeval tst_us_to_timeval(long long us);
-struct timeval tst_ms_to_timeval(long long ms);
-
-int tst_timespec_lt(struct timespec t1, struct timespec t2);
-
-struct timespec tst_timespec_add_us(struct timespec t, long long us);
-
-struct timespec tst_timespec_diff(struct timespec t1, struct timespec t2);
-long long tst_timespec_diff_us(struct timespec t1, struct timespec t2);
-long long tst_timespec_diff_ms(struct timespec t1, struct timespec t2);
-
-struct timespec tst_timespec_abs_diff(struct timespec t1, struct timespec t2);
-long long tst_timespec_abs_diff_us(struct timespec t1, struct timespec t2);
-long long tst_timespec_abs_diff_ms(struct timespec t1, struct timespec t2);
--------------------------------------------------------------------------------
-
-The first four functions are simple inline conversion functions.
-
-The 'tst_timespec_lt()' function returns non-zero if 't1' is earlier than
-'t2'.
-
-The 'tst_timespec_add_us()' function adds 'us' microseconds to the timespec
-'t'. The 'us' is expected to be positive.
-
-The 'tst_timespec_diff*()' functions returns difference between two times, the
-'t1' is expected to be later than 't2'.
-
-The 'tst_timespec_abs_diff*()' functions returns absolute value of difference
-between two times.
-
-NOTE: All conversions to ms and us rounds the value.
-
-2.2.22 Datafiles
-^^^^^^^^^^^^^^^^
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static const char *const res_files[] = {
-	"foo",
-	"bar",
-	NULL
-};
-
-static struct tst_test test = {
-	...
-	.resource_files = res_files,
-	...
-}
--------------------------------------------------------------------------------
-
-If the test needs additional files to be copied to the test temporary
-directory all you need to do is to list their filenames in the
-'NULL' terminated array '.resource_files' in the tst_test structure.
-
-When resource files is set test temporary directory is created automatically,
-there is need to set '.needs_tmpdir' as well.
-
-The test library looks for datafiles first, these are either stored in a
-directory called +datafiles+ in the +$PWD+ at the start of the test or in
-+$LTPROOT/testcases/data/${test_binary_name}+. If the file is not found the
-library looks into +$LTPROOT/testcases/bin/+ and to +$PWD+ at the start of the
-test. This ensures that the testcases can copy the file(s) effortlessly both
-when test is started from the directory it was compiled in as well as when LTP
-was installed.
-
-The file(s) are copied to the newly created test temporary directory which is
-set as the test working directory when the 'test()' functions is executed.
-
-2.2.23 Code path tracing
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-'tst_res' is a macro, so on when you define a function in one file:
-
-[source,c]
--------------------------------------------------------------------------------
-int do_action(int arg)
-{
-	...
-
-	if (ok) {
-		tst_res(TPASS, "check passed");
-		return 0;
-	} else {
-		tst_res(TFAIL, "check failed");
-		return -1;
-	}
-}
--------------------------------------------------------------------------------
-
-and call it from another file, the file and line reported by 'tst_res' in this
-function will be from the former file.
-
-'TST_TRACE' can make the analysis of such situations easier. It's a macro which
-inserts a call to 'tst_res(TINFO, ...)' in case its argument evaluates to
-non-zero. In this call to 'tst_res(TINFO, ...)' the file and line will be
-expanded using the actual location of 'TST_TRACE'.
-
-For example, if this another file contains:
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-if (TST_TRACE(do_action(arg))) {
-	...
-}
--------------------------------------------------------------------------------
-
-the generated output may look similar to:
-
--------------------------------------------------------------------------------
-common.h:9: FAIL: check failed
-test.c:8: INFO: do_action(arg) failed
--------------------------------------------------------------------------------
-
-2.2.24 Tainted kernels
-^^^^^^^^^^^^^^^^^^^^^^
-
-If you need to detect whether a testcase triggers a kernel warning, bug or
-oops, the following can be used to detect TAINT_W or TAINT_D:
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static struct tst_test test = {
-	...
-	.taint_check = TST_TAINT_W | TST_TAINT_D,
-	...
-};
-
-void run(void)
-{
-	...
-	if (tst_taint_check() != 0)
-		tst_res(TFAIL, "kernel has issues");
-	else
-		tst_res(TPASS, "kernel seems to be fine");
-}
--------------------------------------------------------------------------------
-
-To initialize taint checks, you have to set the taint flags you want to test
-for in the 'taint_check' attribute of the tst_test struct. LTP library will
-then automatically call 'tst_taint_init()' during test setup. The function
-will generate a 'TCONF' if the requested flags are not fully supported on the
-running kernel, and 'TBROK' if the kernel is already tainted before executing
-the test.
-
-LTP library will then automatically check kernel taint at the end of testing.
-If '.all_filesystems' is set in struct tst_test, taint check will be performed
-after each file system and taint will abort testing early with 'TFAIL'. You
-can optionally also call 'tst_taint_check()' during 'run()', which returns 0
-or the tainted flags set in '/proc/sys/kernel/tainted' as specified earlier.
-
-Depending on your kernel version, not all tainted-flags will be supported.
-
-For reference to tainted kernels, see kernel documentation:
-Documentation/admin-guide/tainted-kernels.rst or
-https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
-
-2.2.25 Checksums
-^^^^^^^^^^^^^^^^
-
-CRC32c checksum generation is supported by LTP. In order to use it, the
-test should include 'tst_checksum.h' header, then can call 'tst_crc32c()'.
-
-2.2.26 Checking kernel for the driver support
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-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.
-
-Since it relies on modprobe command, the check will be skipped if the command
-itself is not available on the system.
-
-2.2.27 Saving & restoring /proc|sys values
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-LTP library 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 strings where
-each string represents a file, whose value is saved at the beginning
-and restored at the end of the test. Only first line of a specified
-file is saved and restored.
-
-Pathnames can be optionally prefixed to specify how strictly (during
-'store') are handled errors:
-
-* (no prefix) - test ends with 'TCONF', if file doesn't exist
-* '?'         - test prints info message and continues,
-                if file doesn't exist or open/read fails
-* '!'         - test ends with 'TBROK', if file doesn't exist
-
-'restore' is always strict and will TWARN if it encounters any error.
-
-[source,c]
--------------------------------------------------------------------------------
-static const char *save_restore[] = {
-	"/proc/sys/kernel/core_pattern",
-	NULL,
-};
-
-static void setup(void)
-{
-	FILE_PRINTF("/proc/sys/kernel/core_pattern", "/mypath");
-}
-
-static struct tst_test test = {
-	...
-	.setup = setup,
-	.save_restore = save_restore,
-};
--------------------------------------------------------------------------------
-
-2.2.28 Parsing kernel .config
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-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).
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static const char *kconfigs[] = {
-	"CONFIG_X86_INTEL_UMIP | CONFIG_X86_UMIP",
-	NULL
-};
-
-static struct tst_test test = {
-	...
-	.needs_kconfigs = kconfigs,
-	...
-};
--------------------------------------------------------------------------------
-
-2.2.29 Changing the Wall Clock Time during test execution
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-There are some tests that, for different reasons, might need to change the
-system-wide clock time. Whenever this happens, it is imperative that the clock
-is restored, at the end of test's execution, taking in consideration the amount
-of time elapsed during that test.
-
-In order for that to happen, struct tst_test has a variable called
-"restore_wallclock" that should be set to "1" so LTP knows it should: (1)
-initialize a monotonic clock during test setup phase and (2) use that monotonic
-clock to fix the system-wide clock time at the test cleanup phase.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void setup(void)
-{
-	...
-}
-
-static void run(void)
-{
-	...
-}
-
-struct tst_test test = {
-	...
-	.setup = setup,
-	.test_all = run,
-	.restore_wallclock = 1,
-	...
-};
--------------------------------------------------------------------------------
-
-2.2.30 Testing similar syscalls in one test
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-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.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-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,
-	...
-};
--------------------------------------------------------------------------------
-
-2.2.31 Guarded buffers
-^^^^^^^^^^^^^^^^^^^^^^
-
-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 patch 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.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static struct foo *foo_ptr;
-static struct iovec *iov;
-static void *buf_ptr;
-static char *id;
-...
-
-static void run(void)
-{
-	...
-
-	foo_ptr->bar = 1;
-	foo_ptr->buf = buf_ptr;
-
-	...
-}
-
-static void setup(void)
-{
-	...
-
-	id = tst_strdup(string);
-
-	...
-}
-
-static struct tst_test test = {
-	...
-	.bufs = (struct tst_buffers []) {
-		{&foo_ptr, .size = sizeof(*foo_ptr)},
-		{&buf_ptr, .size = BUF_SIZE},
-		{&iov, .iov_sizes = (int[]){128, 32, -1},
-		{}
-	}
-};
--------------------------------------------------------------------------------
-
-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 structure.
-
-So far the tst_test structure 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.
-
-2.2.32 Adding and removing capabilities
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-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.
-
-[source,c]
---------------------------------------------------------------------------------
-#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),
-		{}
-	},
-};
---------------------------------------------------------------------------------
-
-Look at the test struct at the bottom. We have filled in the 'caps' field with
-a 'NULL' terminated array containing two 'tst_cap' structs. 'TST_CAP_REQ'
-actions are executed before setup and 'TST_CAP_DROP' are executed after
-setup. This means it is possible to both request and drop a capability.
-
-[source,c]
---------------------------------------------------------------------------------
-static struct tst_test test = {
-	.test_all = run,
-	.caps = (struct tst_cap []) {
-		TST_CAP(TST_CAP_REQ, CAP_NET_RAW),
-		TST_CAP(TST_CAP_DROP, CAP_SYS_ADMIN),
-		{}
-	},
-};
---------------------------------------------------------------------------------
-
-Here we request 'CAP_NET_RAW', but drop 'CAP_SYS_ADMIN'. If the capability is
-in the permitted set, but not the effective set, the library will try to
-permit it. If it is not in the permitted set, then it will fail with 'TCONF'.
-
-This API does not require 'libcap' to be installed. However it has limited
-features relative to 'libcap'. It only tries to add or remove capabilities
-from the effective set. This means that tests which need to spawn child
-processes may have difficulties ensuring the correct capabilities are
-available to the children (see the capabilities (7) manual pages).
-
-However a lot of problems can be solved by using 'tst_cap_action(struct
-tst_cap  *cap)' directly which can be called at any time. This also helps if
-you wish to drop a capability at the begining of setup.
-
-2.2.33 Reproducing race-conditions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-If a bug is caused by two tasks in the kernel racing and you wish to create a
-regression test (or bug-fix validation test) then the 'tst_fuzzy_sync.h'
-library should be used.
-
-It allows you to specify, in your code, two race windows. One window in each
-thread's loop (triggering a race usually requires many iterations). These
-windows show fuzzy-sync where the race can happen. They don't need to be
-exact, hence the 'fuzzy' part. If the race condition is not immediately
-triggered then the library will begin experimenting with different timings.
-
-[source,c]
---------------------------------------------------------------------------------
-#include "tst_fuzzy_sync.h"
-
-static struct tst_fzsync_pair fzsync_pair;
-
-static void setup(void)
-{
-        tst_fzsync_pair_init(&fzsync_pair);
-}
-
-static void cleanup(void)
-{
-	tst_fzsync_pair_cleanup(&fzsync_pair);
-}
-
-static void *thread_b(void *arg)
-{
-	while (tst_fzsync_run_b(&fzsync_pair)) {
-
-		tst_fzsync_start_race_b(&fzsync_pair);
-
-                /* This is the race window for thread B */
-
-                tst_fzsync_end_race_b(&fzsync_pair);
-	}
-
-	return arg;
-}
-
-static void thread_a(void)
-{
-	tst_fzsync_pair_reset(&fzsync_pair, thread_b);
-
-        while (tst_fzsync_run_a(&fzsync_pair)) {
-
-		tst_fzsync_start_race_a(&fzsync_pair);
-
-		/* This is the race window for thread A */
-
-                tst_fzsync_end_race_a(&fzsync_pair);
-	}
-}
-
-static struct tst_test test = {
-	.test_all = thread_a,
-	.setup = setup,
-	.cleanup = cleanup,
-};
---------------------------------------------------------------------------------
-
-Above is a minimal template for a test using fuzzy-sync. In a simple case, you
-just need to put the bits you want to race inbetween 'start_race' and
-'end_race'. Meanwhile, any setup you need to do per-iteration goes outside the
-windows.
-
-Fuzzy sync synchronises 'run_a' and 'run_b', which act as barriers, so that
-neither thread can progress until the other has caught up with it. There is
-also the 'pair_wait' function which can be used to add barriers in other
-locations. Of course 'start/end_race_a/b' are also a barriers.
-
-The library decides how long the test should run for based on the timeout
-specified by the user plus some other heuristics.
-
-For full documentation see the comments in 'include/tst_fuzzy_sync.h'.
-
-2.2.34 Reserving hugepages
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Many of the LTP tests need to use hugepage in their testing, this allows the
-test can reserve hugepages from system only via '.request_hugepages = xx'.
-
-If set non-zero number of 'request_hugepages', test will try to reserve the
-expected number of hugepage for testing in setup phase. If system does not
-have enough hpage for using, it will try the best to reserve 80% available
-number of hpages. With success test stores the reserved hugepage number in
-'tst_hugepages'. For the system without hugetlb supporting, variable
-'tst_hugepages' will be set to 0.
-
-Also, we do cleanup and restore work for the hpages resetting automatically.
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void run(void)
-{
-	...
-
-	if (tst_hugepages == test.request_hugepages)
-		TEST(do_hpage_test);
-	else
-		...
-	...
-}
-
-struct tst_test test = {
-	.test_all = run,
-	.request_hugepages = 2,
-	...
-};
--------------------------------------------------------------------------------
-
-or,
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-
-static void run(void)
-{
-	...
-}
-
-static void setup(void)
-{
-? ? ? ? if (tst_hugepages != test.requested_hugepages)
-? ? ? ? ? ? ? ? tst_brk(TCONF, "...");
-}
-
-struct tst_test test = {
-	.test_all = run,
-	.request_hugepages = 2,
-	...
-};
--------------------------------------------------------------------------------
-
-2.2.35 Checking for required commands
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Required commands can be checked with '.needs_cmds', which points to a 'NULL'
-terminated array of strings such as:
-
-[source,c]
--------------------------------------------------------------------------------
-.needs_cmds = (const char *const []) {
-	"useradd",
-	"userdel",
-	NULL
-},
--------------------------------------------------------------------------------
-
-2.2.36 Assert sys or proc file value
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Using TST_ASSERT_INT/STR(path, val) to assert that integer value or string stored in
-the prefix field of file pointed by path equals to the value passed to this function.
-
-Also having a similar api pair TST_ASSERT_FILE_INT/STR(path, prefix, val) to assert
-the field value of file.
-
-2.2.36 Using Control Group
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some LTP tests need specific Control Group configurations. tst_cgroup.h provides
-APIs to discover and use CGroups. There are many differences between CGroups API
-V1 and V2. We encapsulate the details of configuring CGroups in high-level
-functions which follow the V2 kernel API. Allowing one to use CGroups without
-caring too much about the current system's configuration.
-
-Also, the LTP library will automatically mount/umount and configure the CGroup
-hierarchies if that is required (e.g. if you run the tests from init with no
-system manager).
-
-[source,c]
--------------------------------------------------------------------------------
-#include "tst_test.h"
-#include "tst_cgroup.h"
-
-static const struct tst_cgroup_group *cg;
-
-static void run(void)
-{
-	...
-	// do test under cgroup
-	...
-}
-
-static void setup(void)
-{
-	tst_cgroup_require("memory", NULL);
-	cg = tst_cgroup_get_test_group();
-	SAFE_CGROUP_PRINTF(cg, "cgroup.procs", "%d", getpid());
-	SAFE_CGROUP_PRINTF(cg, "memory.max", "%lu", MEMSIZE);
-	if (SAFE_CGROUP_HAS(cg, "memory.swap.max"))
-		SAFE_CGROUP_PRINTF(cg, "memory.swap.max", "%zu", memsw);
-}
-
-static void cleanup(void)
-{
-	tst_cgroup_cleanup();
-}
-
-struct tst_test test = {
-	.setup = setup,
-	.test_all = run,
-	.cleanup = cleanup,
-	...
-};
--------------------------------------------------------------------------------
-
-Above, we first ensure the memory controller is available on the
-test's CGroup with 'tst_cgroup_require'. We then get a structure,
-'cg', which represents the test's CGroup. Note that
-'tst_cgroup_get_test_group' should not be called many times, as it is
-allocated in a guarded buffer (See section 2.2.31). Therefor it is
-best to call it once in 'setup' and not 'run' because 'run' may be
-repeated with the '-i' option.
-
-We then write the current processes PID into 'cgroup.procs', which
-moves the current process into the test's CGroup. After which we set
-the maximum memory size by writing to 'memory.max'. If the memory
-controller is mounted on CGroups V1 then the library will actually
-write to 'memory.limit_in_bytes'. As a general rule, if a file exists
-on both CGroup versions, then we use the V2 naming.
-
-Some controller features, such as 'memory.swap', can be
-disabled. Therefor we need to check if they exist before accessing
-them. This can be done with 'SAFE_CGROUP_HAS' which can be called on
-any control file or feature.
-
-Most tests only require setting a few limits similar to the above. In
-such cases the differences between V1 and V2 are hidden. Setup and
-cleanup is also mostly hidden. However things can get much worse.
-
-[source,c]
--------------------------------------------------------------------------------
-static const struct tst_cgroup_group *cg;
-static const struct tst_cgroup_group *cg_drain;
-static struct tst_cgroup_group *cg_child;
-
-static void run(void)
-{
-	char buf[BUFSIZ];
-	size_t mem = 0;
-
-	cg_child = tst_cgroup_group_mk(cg, "child");
-	SAFE_CGROUP_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
-
-	if (TST_CGROUP_VER(cg, "memory") != TST_CGROUP_V1)
-		SAFE_CGROUP_PRINT(cg, "cgroup.subtree_control", "+memory");
-	if (TST_CGROUP_VER(cg, "cpuset") != TST_CGROUP_V1)
-		SAFE_CGROUP_PRINT(cg, "cgroup.subtree_control", "+cpuset");
-
-	if (!SAFE_FORK()) {
-		SAFE_CGROUP_PRINTF(cg_child, "cgroup.procs", "%d", getpid());
-
-		if (SAFE_CGROUP_HAS(cg_child, "memory.swap"))
-			SAFE_CGROUP_SCANF(cg_child, "memory.swap.current", "%zu", &mem);
-		SAFE_CGROUP_READ(cg_child, "cpuset.mems", buf, sizeof(buf));
-
-		// Do something with cpuset.mems and memory.current values
-		...
-
-		exit(0);
-	}
-
-	tst_reap_children();
-	SAFE_CGROUP_PRINTF(cg_drain, "cgroup.procs", "%d", getpid());
-	cg_child = tst_cgroup_group_rm(cg_child);
-}
-
-static void setup(void)
-{
-	tst_cgroup_require("memory", NULL);
-	tst_cgroup_require("cpuset", NULL);
-
-	cg = tst_cgroup_get_test_group();
-	cg_drain = tst_cgroup_get_drain_group();
-}
-
-static void cleanup(void)
-{
-	if (cg_child) {
-		SAFE_CGROUP_PRINTF(cg_drain, "cgroup.procs", "%d", getpid());
-		cg_child = tst_cgroup_group_rm(cg_child);
-	}
-
-	tst_cgroup_cleanup();
-}
-
-struct tst_test test = {
-	.setup = setup,
-	.test_all = run,
-	.cleanup = cleanup,
-	...
-};
--------------------------------------------------------------------------------
-
-Starting with setup; we can see here that we also fetch the 'drain'
-CGroup. This is a shared group (between parallel tests) which may
-contain processes from other tests. It should have default settings and
-these should not be changed by the test. It can be used to remove
-processes from other CGroups incase the hierarchy root is not
-accessible.
-
-In 'run', we first create a child CGroup with 'tst_cgroup_mk'. As we
-create this CGroup in 'run' we should also remove it at the end of
-run. We also need to check if it exists and remove it in cleanup as
-well. Because there are 'SAFE_' functions which may jump to cleanup.
-
-We then move the main test process into the child CGroup. This is
-important as it means that before we destroy the child CGroup we have
-to move the main test process elsewhere. For that we use the 'drain'
-group.
-
-Next we enable the memory and cpuset controller configuration on the
-test CGroup's descendants (i.e. 'cg_child'). This allows each child to
-have its own settings. The file 'cgroup.subtree_control' does not
-exist on V1. Because it is possible to have both V1 and V2 active at
-the same time. We can not simply check if 'subtree_control' exists
-before writing to it. We have to check if a particular controller is
-on V2 before trying to add it to 'subtree_control'. Trying to add a V1
-controller will result in 'ENOENT'.
-
-We then fork a child process and add this to the child CGroup. Within
-the child process we try to read 'memory.swap.current'. It is possible
-that the memory controller was compiled without swap support, so it is
-necessary to check if 'memory.swap' is enabled. That is unless the
-test will never reach the point where 'memory.swap.*' are used without
-swap support.
-
-The parent process waits for the child process to be reaped before
-destroying the child CGroup. So there is no need to transfer the child
-to drain. However the parent process must be moved otherwise we will
-get 'EBUSY' when trying to remove the child CGroup.
-
-Another example of an edge case is the following.
-
-[source,c]
--------------------------------------------------------------------------------
-	if (TST_CGROUP_VER(cg, "memory") == TST_CGROUP_V1)
-		SAFE_CGROUP_PRINTF(cg, "memory.swap.max", "%lu", ~0UL);
-	else
-		SAFE_CGROUP_PRINT(cg, "memory.swap.max", "max");
--------------------------------------------------------------------------------
-
-CGroups V2 introduced a feature where 'memory[.swap].max' could be set to
-"max". This does not appear to work on V1 'limit_in_bytes' however. For most
-tests, simply using a large number is sufficient and there is no need to use
-"max". Importantly though, one should be careful to read both the V1 and V2
-kernel docs. The LTP library can not handle all edge cases. It does the minimal
-amount of work to make testing on both V1 and V2 feasible.
-
-2.2.37 Require minimum numbers of CPU for a testcase
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Some tests require more than specific number of CPU. It can be defined with
-`.min_cpus = N`.
-
-2.2.38 Test tags
-^^^^^^^^^^^^^^^^
-
-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 and exported
-into documentation.
-
-CVE, mainline and stable kernel git commits in a regression test for a kernel bug:
-[source,c]
--------------------------------------------------------------------------------
-struct tst_test test = {
-	...
-	.tags = (const struct tst_tag[]) {
-		{"linux-git", "9392a27d88b9"},
-		{"linux-git", "ff002b30181d"},
-		{"linux-stable-git", "c4a23c852e80"},
-		{"CVE", "2020-29373"},
-		{}
-	}
-};
--------------------------------------------------------------------------------
-
-Glibc git commit in a regression test for a glibc bug:
-[source,c]
--------------------------------------------------------------------------------
-struct tst_test test = {
-	...
-	.tags = (const struct tst_tag[]) {
-		{"glibc-git", "574500a108be"},
-		{}
-	}
-};
--------------------------------------------------------------------------------
-
-2.3 Writing a testcase in shell
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-LTP supports testcases to be written in a portable shell too.
-
-There is a shell library modeled closely to the C interface at
-'testcases/lib/tst_test.sh'.
-
-WARNING: All identifiers starting with TST_ or tst_ are reserved for the
-         test library.
-
-2.3.1 Basic test interface
-^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# This is a basic test for true shell builtin
-
-TST_TESTFUNC=do_test
-. tst_test.sh
-
-do_test()
-{
-	true
-	ret=$?
-
-	if [ $ret -eq 0 ]; then
-		tst_res TPASS "true returned 0"
-	else
-		tst_res TFAIL "true returned $ret"
-	fi
-}
-
-tst_run
--------------------------------------------------------------------------------
-
-TIP: To execute this test the 'tst_test.sh' library must be in '$PATH'. If you
-     are executing the test from a git checkout you can run it as
-     'PATH="$PATH:../../lib" ./foo01.sh'
-
-The shell library expects test setup, cleanup and the test function executing
-the test in the '$TST_SETUP', '$TST_CLEANUP' and '$TST_TESTFUNC' variables.
-
-Both '$TST_SETUP' and '$TST_CLEANUP' are optional.
-
-The '$TST_TESTFUNC' may be called several times if more than one test
-iteration was requested by passing right command line options to the test.
-
-The '$TST_CLEANUP' may be called even in the middle of the setup and must be
-able to clean up correctly even in this situation. The easiest solution for
-this is to keep track of what was initialized and act accordingly in the
-cleanup.
-
-WARNING: Similar to the C library, calling 'tst_brk' in the $TST_CLEANUP does
-         not exit the test and 'TBROK' is converted to 'TWARN'.
-
-Notice also the 'tst_run' shell API function called@the end of the test that
-actually starts the test.
-
-WARNING: cleanup function is called only after 'tst_run' has been started.
-Calling 'tst_brk' in shell libraries, e.g. 'tst_test.sh' or 'tst_net.sh' does
-not trigger calling it.
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Example test with tests in separate functions
-
-TST_TESTFUNC=test
-TST_CNT=2
-. tst_test.sh
-
-test1()
-{
-	tst_res TPASS "Test $1 passed"
-}
-
-test2()
-{
-	tst_res TPASS "Test $1 passed"
-}
-
-tst_run
-# output:
-# foo 1 TPASS: Test 1 passed
-# foo 2 TPASS: Test 2 passed
--------------------------------------------------------------------------------
-
-If '$TST_CNT' is set, the test library looks if there are functions named
-'$\{TST_TESTFUNC\}1', ..., '$\{TST_TESTFUNC\}$\{TST_CNT\}' and if these are
-found they are executed one by one. The test number is passed to it in the '$1'.
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Example test with tests in a single function
-
-TST_TESTFUNC=do_test
-TST_CNT=2
-. tst_test.sh
-
-do_test()
-{
-	case $1 in
-	1) tst_res TPASS "Test $1 passed";;
-	2) tst_res TPASS "Test $1 passed";;
-	esac
-}
-
-tst_run
-# output:
-# foo 1 TPASS: Test 1 passed
-# foo 2 TPASS: Test 2 passed
--------------------------------------------------------------------------------
-
-Otherwise, if '$TST_CNT' is set but there is no '$\{TST_TESTFUNC\}1', etc.,
-the '$TST_TESTFUNC' is executed '$TST_CNT' times and the test number is passed
-to it in the '$1'.
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Example test with tests in a single function, using $TST_TEST_DATA and
-# $TST_TEST_DATA_IFS
-
-TST_TESTFUNC=do_test
-TST_TEST_DATA="foo:bar:d dd"
-TST_TEST_DATA_IFS=":"
-. tst_test.sh
-
-do_test()
-{
-	tst_res TPASS "Test $1 passed with data '$2'"
-}
-
-tst_run
-# output:
-# foo 1 TPASS: Test 1 passed with data 'foo'
-# foo 2 TPASS: Test 1 passed with data 'bar'
-# foo 3 TPASS: Test 1 passed with data 'd dd'
--------------------------------------------------------------------------------
-
-It's possible to pass data for function with '$TST_TEST_DATA'. Optional
-'$TST_TEST_DATA_IFS' is used for splitting, default value is space.
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Example test with tests in a single function, using $TST_TEST_DATA and $TST_CNT
-
-TST_TESTFUNC=do_test
-TST_CNT=2
-TST_TEST_DATA="foo bar"
-. tst_test.sh
-
-do_test()
-{
-	case $1 in
-	1) tst_res TPASS "Test $1 passed with data '$2'";;
-	2) tst_res TPASS "Test $1 passed with data '$2'";;
-	esac
-}
-
-tst_run
-# output:
-# foo 1 TPASS: Test 1 passed with data 'foo'
-# foo 2 TPASS: Test 2 passed with data 'foo'
-# foo 3 TPASS: Test 1 passed with data 'bar'
-# foo 4 TPASS: Test 2 passed with data 'bar'
--------------------------------------------------------------------------------
-
-'$TST_TEST_DATA' can be used with '$TST_CNT'. If '$TST_TEST_DATA_IFS' not specified,
-space as default value is used. Of course, it's possible to use separate functions.
-
-2.3.2 Library environment variables and functions for shell
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Similarily to the C library various checks and preparations can be requested
-simply by setting right '$TST_NEEDS_FOO'.
-
-[options="header"]
-|=============================================================================
-| Variable name      | Action done
-| 'TST_NEEDS_ROOT'   | Exit the test with 'TCONF' unless executed under root.
-|                    | Alternatively the 'tst_require_root' command can be used.
-| 'TST_NEEDS_TMPDIR' | Create test temporary directory and cd into it.
-| 'TST_NEEDS_DEVICE' | Prepare test temporary device, the path to testing
-                       device is stored in '$TST_DEVICE' variable.
-                       The option implies 'TST_NEEDS_TMPDIR'.
-| 'TST_NEEDS_CMDS'   | String with command names that has to be present for
-                       the test (see below).
-| 'TST_NEEDS_MODULE' | Test module name needed for the test (see below).
-| 'TST_NEEDS_DRIVERS'| Checks kernel drivers support for the test.
-| 'TST_TIMEOUT'      | Maximum timeout set for the test in sec. Must be int >= 1,
-                       or -1 (special value to disable timeout), default is 300.
-                       Variable is meant be set in tests, not by user.
-                       It's an equivalent of `tst_test.timeout` in C, can be set
-                       via 'tst_set_timeout(timeout)' after test has started.
-|=============================================================================
-
-[options="header"]
-|=============================================================================
-| Function name              | Action done
-| 'tst_set_timeout(timeout)' | Maximum timeout set for the test in sec.
-                               See 'TST_TIMEOUT' variable.
-|=============================================================================
-
-NOTE: Network tests (see testcases/network/README.md) use additional variables
-and functions in 'tst_net.sh'.
-
-Checking for presence of commands
-+++++++++++++++++++++++++++++++++
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-
-...
-
-TST_NEEDS_CMDS="modinfo modprobe"
-. tst_test.sh
-
-...
-
--------------------------------------------------------------------------------
-
-Setting '$TST_NEEDS_CMDS' to a string listing required commands will check for
-existence each of them and exits the test with 'TCONF' on first missing.
-
-Alternatively the 'tst_require_cmds()' function can be used to do the same on
-runtime, since sometimes we need to the check at runtime too.
-
-'tst_check_cmds()' can be used for requirements just for a particular test
-as it doesn't exit (it issues 'tst_res TCONF'). Expected usage is:
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-
-TST_TESTFUNC=do_test
-. tst_test.sh
-
-do_test()
-{
-	tst_check_cmds cmd || return
-	cmd --foo
-	...
-}
-
-tst_run
--------------------------------------------------------------------------------
-
-Locating kernel modules
-+++++++++++++++++++++++
-
-The LTP build system can build kernel modules as well, setting
-'$TST_NEEDS_MODULE' to module name will cause the library to look for the
-module in a few possible paths.
-
-If module was found the path to it will be stored into '$TST_MODPATH'
-variable, if module wasn't found the test will exit with 'TCONF'.
-
-Alternatively the 'tst_require_module()' function can be used to do the same
-at runtime.
-
-2.3.3 Optional command line parameters
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Optional test command line parameters
-
-TST_OPTS="af:"
-TST_USAGE=usage
-TST_PARSE_ARGS=parse_args
-TST_TESTFUNC=do_test
-
-. tst_test.sh
-
-ALTERNATIVE=0
-MODE="foo"
-
-usage()
-{
-	cat << EOF
-usage: $0 [-a] [-f <foo|bar>]
-
-OPTIONS
--a     Enable support for alternative foo
--f     Specify foo or bar mode
-EOF
-}
-
-parse_args()
-{
-	case $1 in
-	a) ALTERNATIVE=1;;
-	f) MODE="$2";;
-	esac
-}
-
-do_test()
-{
-	...
-}
-
-tst_run
--------------------------------------------------------------------------------
-
-The 'getopts' string for optional parameters is passed in the '$TST_OPTS'
-variable. There are a few default parameters that cannot be used by a test,
-these can be listed with passing help '-h' option to any test.
-
-The function that prints the usage is passed in '$TST_USAGE', the help for
-the options implemented in the library is appended when usage is printed.
-
-Lastly the function '$PARSE_ARGS' is called with the option name in the '$1'
-and, if option has argument, its value in the '$2'.
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Optional test positional parameters
-
-TST_POS_ARGS=3
-TST_USAGE=usage
-TST_TESTFUNC=do_test
-
-. tst_test.sh
-
-usage()
-{
-	cat << EOF
-usage: $0 [min] [max] [size]
-
-EOF
-}
-
-min="$1"
-max="$2"
-size="$3"
-
-do_test()
-{
-	...
-}
-
-tst_run
--------------------------------------------------------------------------------
-
-You can also request a number of positional parameters by setting the
-'$TST_POS_ARGS' variable. If you do, these will be available as they were
-passed directly to the script in '$1', '$2', ..., '$n'.
-
-2.3.4 Useful library functions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Retrieving configuration variables
-++++++++++++++++++++++++++++++++++
-
-You may need to retrieve configuration values such as PAGESIZE, there is
-'getconf' but as some system may not have it, you are advised to use
-'tst_getconf' instead. Note that it implements subset of 'getconf'
-system variables used by the testcases only.
-
-[source,sh]
--------------------------------------------------------------------------------
-# retrieve PAGESIZE
-pagesize=`tst_getconf PAGESIZE`
--------------------------------------------------------------------------------
-
-Sleeping for subsecond intervals
-++++++++++++++++++++++++++++++++
-
-Albeit there is a sleep command available basically everywhere not all
-implementations can support sleeping for less than one second. And most of the
-time sleeping for a second is too much. Therefore LTP includes 'tst_sleep'
-that can sleep for defined amount of seconds, milliseconds or microseconds.
-
-[source,sh]
--------------------------------------------------------------------------------
-# sleep for 100 milliseconds
-tst_sleep 100ms
--------------------------------------------------------------------------------
-
-Retry a function call multiple times
-++++++++++++++++++++++++++++++++++++
-
-Sometimes an LTP test needs to retry a function call multiple times because
-the system is not ready to process it successfully on the first try. The LTP
-library has useful tools to handle the call retry automatically.
-'TST_RETRY_FUNC()' will keep retrying for up to 1 second. If you want a custom
-time limit use 'TST_RETRY_FN_EXP_BACKOFF()'. Both methods return the value
-returned by the last 'FUNC' call.
-
-The delay between retries starts@1 microsecond and doubles after each call.
-The retry loop ends when the function call succeeds or when the next delay
-exceeds the specified time (1 second for 'TST_RETRY_FUNC()'). The maximum
-delay is multiplied by TST_TIMEOUT_MUL. The total cumulative delay may be up
-to twice as long as the adjusted maximum delay.
-
-The C version of 'TST_RETRY_FUNC()' is a macro which takes two arguments:
-
-* 'FUNC' is the complete function call with arguments which should be retried
-  multiple times.
-* 'SUCCESS_CHECK' is a macro or function which will validate 'FUNC' return
-  value. 'FUNC' call was successful if 'SUCCESS_CHECK(ret)' evaluates to
-  non-zero.
-
-Both retry methods clear 'errno' before every 'FUNC' call so your
-'SUCCESS_CHECK' can look for specific error codes as well. The LTP library
-also includes predefined 'SUCCESS_CHECK' macros for the most common call
-conventions:
-
-* 'TST_RETVAL_EQ0()' - The call was successful if 'FUNC' returned 0 or NULL
-* 'TST_RETVAL_NOTNULL()' - The call was successful if 'FUNC' returned any
-  value other than 0 or NULL.
-* 'TST_RETVAL_GE0()' - The call was successful if 'FUNC' returned value >= 0.
-
-[source,c]
--------------------------------------------------------------------------------
-/* Keep trying for 1 second */
-TST_RETRY_FUNC(FUNC, SUCCESS_CHECK)
-
-/* Keep trying for up to 2*N seconds */
-TST_RETRY_FN_EXP_BACKOFF(FUNC, SUCCESS_CHECK, N)
--------------------------------------------------------------------------------
-
-The shell version of 'TST_RETRY_FUNC()' is simpler and takes slightly
-different arguments:
-
-* 'FUNC' is a string containing the complete function or program call with
-  arguments.
-* 'EXPECTED_RET' is a single expected return value. 'FUNC' call was successful
-  if the return value is equal to EXPECTED_RET.
-
-[source,sh]
--------------------------------------------------------------------------------
-# Keep trying for 1 second
-TST_RETRY_FUNC "FUNC arg1 arg2 ..." "EXPECTED_RET"
-
-# Keep trying for up to 2*N seconds
-TST_RETRY_FN_EXP_BACKOFF "FUNC arg1 arg2 ..." "EXPECTED_RET" "N"
--------------------------------------------------------------------------------
-
-Checking for integers
-+++++++++++++++++++++
-
-[source,sh]
--------------------------------------------------------------------------------
-# returns zero if passed an integer parameter, non-zero otherwise
-tst_is_int "$FOO"
--------------------------------------------------------------------------------
-
-Checking for integers and floating point numbers
-++++++++++++++++++++++++++++++++++++++++++++++++
-
-[source,sh]
--------------------------------------------------------------------------------
-# returns zero if passed an integer or floating point number parameter,
-# non-zero otherwise
-tst_is_num "$FOO"
--------------------------------------------------------------------------------
-
-Obtaining random numbers
-++++++++++++++++++++++++
-
-There is no '$RANDOM' in portable shell, use 'tst_random' instead.
-
-[source,sh]
--------------------------------------------------------------------------------
-# get random integer between 0 and 1000 (including 0 and 1000)
-tst_random 0 1000
--------------------------------------------------------------------------------
-
-Formatting device with a filesystem
-+++++++++++++++++++++++++++++++++++
-
-The 'tst_mkfs' helper will format device with the filesystem.
-
-[source,sh]
--------------------------------------------------------------------------------
-# format test device with ext2
-tst_mkfs ext2 $TST_DEVICE
-# default params are $TST_FS_TYPE $TST_DEVICE
-tst_mkfs
-# optional parameters
-tst_mkfs ext4 /dev/device -T largefile
--------------------------------------------------------------------------------
-
-Mounting and unmounting filesystems
-+++++++++++++++++++++++++++++++++++
-
-The 'tst_mount' and 'tst_umount' helpers are a safe way to mount/umount
-a filesystem.
-
-The 'tst_mount' mounts '$TST_DEVICE' of '$TST_FS_TYPE' (optional) to
-'$TST_MNTPOINT' (defaults to mntpoint), optionally using the
-'$TST_MNT_PARAMS'. The '$TST_MNTPOINT' directory is created if it didn't
-exist prior to the function call.
-
-If the path passed (optional, defaults to '$TST_DEVICE') to the 'tst_umount' is
-not mounted (present in '/proc/mounts') it's noop.
-Otherwise it retries to umount the filesystem a few times on a failure, which
-is a workaround since there are a daemons dumb enough to probe all newly
-mounted filesystems, which prevents them from umounting shortly after they
-were mounted.
-
-ROD and ROD_SILENT
-++++++++++++++++++
-
-These functions supply the 'SAFE_MACROS' used in C although they work and are
-named differently.
-
-[source,sh]
--------------------------------------------------------------------------------
-ROD_SILENT command arg1 arg2 ...
-
-# is shorthand for:
-
-command arg1 arg2 ... > /dev/null 2>&1
-if [ $? -ne 0 ]; then
-        tst_brk TBROK "..."
-fi
-
-
-ROD command arg1 arg2 ...
-
-# is shorthand for:
-
-ROD arg1 arg2 ...
-if [ $? -ne 0 ]; then
-        tst_brk TBROK "..."
-fi
--------------------------------------------------------------------------------
-
-WARNING: Keep in mind that output redirection (to a file) happens in the
-         caller rather than in the ROD function and cannot be checked for
-         write errors by the ROD function.
-
-As a matter of a fact doing +ROD echo a > /proc/cpuinfo+ would work just fine
-since the 'ROD' function will only get the +echo a+ part that will run just
-fine.
-
-[source,sh]
--------------------------------------------------------------------------------
-# Redirect output to a file with ROD
-ROD echo foo \> bar
--------------------------------------------------------------------------------
-
-Note the '>' is escaped with '\', this causes that the '>' and filename are
-passed to the 'ROD' function as parameters and the 'ROD' function contains
-code to split '$@' on '>' and redirects the output to the file.
-
-EXPECT_PASS{,_BRK} and EXPECT_FAIL{,_BRK}
-+++++++++++++++++++++++++++++++++++++++++
-
-[source,sh]
--------------------------------------------------------------------------------
-EXPECT_PASS command arg1 arg2 ... [ \> file ]
-EXPECT_FAIL command arg1 arg2 ... [ \> file ]
--------------------------------------------------------------------------------
-
-'EXPECT_PASS' calls 'tst_res TPASS' if the command exited with 0 exit code,
-and 'tst_res TFAIL' otherwise. 'EXPECT_FAIL' does vice versa.
-
-Output redirection rules are the same as for the 'ROD' function. In addition
-to that, 'EXPECT_FAIL' always redirects the command's stderr to '/dev/null'.
-
-There are also 'EXPECT_PASS_BRK' and 'EXPECT_FAIL_BRK', which works the same way
-except breaking a test when unexpected action happen.
-
-It's possible to detect whether expected value happened:
-[source,sh]
--------------------------------------------------------------------------------
-if ! EXPECT_PASS command arg1 2\> /dev/null; then
-	continue
-fi
--------------------------------------------------------------------------------
-
-tst_kvcmp
-+++++++++
-
-This command compares the currently running kernel version given conditions
-with syntax similar to the shell test command.
-
-[source,sh]
--------------------------------------------------------------------------------
-# Exit the test if kernel version is older or equal to 2.6.8
-if tst_kvcmp -le 2.6.8; then
-	tst_brk TCONF "Kernel newer than 2.6.8 is needed"
-fi
-
-# Exit the test if kernel is newer than 3.8 and older than 4.0.1
-if tst_kvcmp -gt 3.8 -a -lt 4.0.1; then
-	tst_brk TCONF "Kernel must be older than 3.8 or newer than 4.0.1"
-fi
--------------------------------------------------------------------------------
-
-[options="header"]
-|=======================================================================
-| expression | description
-| -eq kver   | Returns true if kernel version is equal
-| -ne kver   | Returns true if kernel version is not equal
-| -gt kver   | Returns true if kernel version is greater
-| -ge kver   | Returns true if kernel version is greater or equal
-| -lt kver   | Returns true if kernel version is lesser
-| -le kver   | Returns true if kernel version is lesser or equal
-| -a         | Does logical and between two expressions
-| -o         | Does logical or between two expressions
-|=======================================================================
-
-The format for kernel version has to either be with one dot e.g. '2.6' or with
-two dots e.g. '4.8.1'.
-
-.tst_fs_has_free
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-
-...
-
-# whether current directory has 100MB free space at least.
-if ! tst_fs_has_free . 100MB; then
-	tst_brkm TCONF "Not enough free space"
-fi
-
-...
--------------------------------------------------------------------------------
-
-The 'tst_fs_has_free' shell interface returns 0 if the specified free space is
-satisfied, 1 if not, and 2 on error.
-
-The second argument supports suffixes kB, MB and GB, the default unit is Byte.
-
-.tst_retry
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-
-...
-
-# Retry ping command three times
-tst_retry "ping -c 1 127.0.0.1"
-
-if [ $? -ne 0 ]; then
-	tst_resm TFAIL "Failed to ping 127.0.0.1"
-else
-	tst_resm TPASS "Successfully pinged 127.0.0.1"
-fi
-
-...
--------------------------------------------------------------------------------
-
-The 'tst_retry' function allows you to retry a command after waiting small
-amount of time until it succeeds or until given amount of retries has been
-reached (default is three attempts).
-
-2.3.5 Restarting daemons
-^^^^^^^^^^^^^^^^^^^^^^^^
-
-Restarting system daemons is a complicated task for two reasons.
-
-* There are different init systems
-  (SysV init, systemd, etc...)
-
-* Daemon names are not unified between distributions
-  (apache vs httpd, cron vs crond, various syslog variations)
-
-To solve these problems LTP has 'testcases/lib/daemonlib.sh' library that
-provides functions to start/stop/query daemons as well as variables that store
-correct daemon name.
-
-.Supported operations
-|==============================================================================
-| start_daemon()   | Starts daemon, name is passed as first parameter.
-| stop_daemon()    | Stops daemon, name is passed as first parameter.
-| restart_daemon() | Restarts daemon, name is passed as first parameter.
-| status_daemon()  | Detect daemon status (exit code: 0: running, 1: not running).
-|==============================================================================
-
-.Variables with detected names
-|==============================================================================
-| CROND_DAEMON | Cron daemon name (cron, crond).
-| SYSLOG_DAEMON | Syslog daemon name (syslog, syslog-ng, rsyslog).
-|==============================================================================
-
-.Cron daemon restart example
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0-or-later
-# Cron daemon restart example
-
-TCID=cron01
-TST_COUNT=1
-. test.sh
-. daemonlib.sh
-
-...
-
-restart_daemon $CROND_DAEMON
-
-...
-
-tst_exit
--------------------------------------------------------------------------------
-
-2.3.6 Access to the checkpoint interface
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The shell library provides an implementation of the checkpoint interface
-compatible with the C version. All 'TST_CHECKPOINT_*' functions are available.
-
-In order to initialize checkpoints '$TST_NEEDS_CHECKPOINTS' must be set to '1'
-before the inclusion of 'test.sh':
-
-[source,sh]
--------------------------------------------------------------------------------
-#!/bin/sh
-
-TST_NEEDS_CHECKPOINTS=1
-. test.sh
--------------------------------------------------------------------------------
-
-Since both the implementations are compatible, it's also possible to start
-a child binary process from a shell test and synchronize with it. This process
-must have checkpoints initialized by calling 'tst_reinit()'.
-
-3. Common problems
-------------------
-
-This chapter describes common problems/misuses and less obvious design patters
-(quirks) in UNIX interfaces. Read it carefully :)
-
-3.1 umask()
-~~~~~~~~~~~
-
-I've been hit by this one several times already... When you create files
-with 'open()' or 'creat()' etc, the mode specified as the last parameter *is
-not* the mode the file is created with. The mode depends on current 'umask()'
-settings which may clear some of the bits. If your test depends on specific
-file permissions you need either to change umask to 0 or 'chmod()' the file
-afterwards or use 'SAFE_TOUCH()' that does the 'chmod()' for you.
-
-3.2 access()
-~~~~~~~~~~~
-
-If 'access(some_file, W_OK)' is executed by root, it will return success even
-if the file doesn't have write permission bits set (the same holds for R_OK
-too). For sysfs files you can use 'open()' as a workaround to check file
-read/write permissions. It might not work for other filesystems, for these you
-have to use 'stat()', 'lstat()' or 'fstat()'.
-
-3.3 umount() EBUSY
-~~~~~~~~~~~~~~~~~~
-
-Various desktop daemons (gvfsd-trash is known for that) may be stupid enough
-to probe all newly mounted filesystem which results in 'umount(2)' failing
-with 'EBUSY'; use 'tst_umount()' described in 2.2.19 that retries in this case
-instead of plain 'umount(2)'.
-
-3.4 FILE buffers and fork()
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Be vary that if a process calls 'fork(2)' the child process inherits open
-descriptors as well as copy of the parent memory so especially if there are
-any open 'FILE' buffers with a data in them they may be written both by the
-parent and children resulting in corrupted/duplicated data in the resulting
-files.
-
-Also open 'FILE' streams are flushed and closed at 'exit(3)' so if your
-program works with 'FILE' streams, does 'fork(2)', and the child may end up
-calling 'exit(3)' you will likely end up with corrupted files.
-
-The solution to this problem is either simply call 'fflush(NULL)' that flushes
-all open output 'FILE' streams just before doing 'fork(2)'. You may also use
-'_exit(2)' in child processes which does not flush 'FILE' buffers and also
-skips 'atexit(3)' callbacks.
-
-4. Test Contribution Checklist
-------------------------------
+NOTE: See also
+      https://github.com/linux-test-project/ltp/wiki/Maintainer-Patch-Review-Checklist[Maintainer Patch Review Checklist].
 
 1. Test compiles and runs fine (check with `-i 10` too)
 2. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/scripts/checkpatch.pl[checkpatch.pl]
@@ -3219,8 +251,7 @@ skips 'atexit(3)' callbacks.
 4. Test binaries are added into corresponding '.gitignore' files
 5. Patches apply over the latest git
 
-
-4.1 About .gitignore files
+3.1 About .gitignore files
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 There are numerous '.gitignore' files in the LTP tree. Usually there is a
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-26 15:49 [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines Petr Vorel
@ 2021-05-27  3:31 ` Li Wang
  2021-05-27  3:49   ` Li Wang
  2021-05-27 12:28 ` Cyril Hrubis
  1 sibling, 1 reply; 10+ messages in thread
From: Li Wang @ 2021-05-27  3:31 UTC (permalink / raw)
  To: ltp

Hi Petr,

On Wed, May 26, 2021 at 11:50 PM Petr Vorel <pvorel@suse.cz> wrote:
>
> Test Writing Guidelines wiki page is too long, thus split it
> into 3 parts:
>
> 1) generic part (only first chapter, the same URL)
> 2) C test API (2.2 chapter, 4. Common problems)
> 3) shell test API
>
> Unfortunately this breaks users' bookmarks.
>
> Start numbering in headers from 1 on each page (links are broken
> anyway).
>
> NOTE: in order to have '...' formatting as code,
> main header ====== was needed to add on the page.
>
> Signed-off-by: Petr Vorel <pvorel@suse.cz>
> ---
> See it:
> https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
> https://github.com/pevik/ltp/wiki/C-Test-API
> https://github.com/pevik/ltp/wiki/Shell-Test-API

+1
I think this is a good proposal, the split doc looks more friendly to read.

-- 
Regards,
Li Wang


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  3:31 ` Li Wang
@ 2021-05-27  3:49   ` Li Wang
  2021-05-27  4:00     ` Xie Ziyao
  2021-05-27  4:19     ` Petr Vorel
  0 siblings, 2 replies; 10+ messages in thread
From: Li Wang @ 2021-05-27  3:49 UTC (permalink / raw)
  To: ltp

> > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
> > https://github.com/pevik/ltp/wiki/C-Test-API

Seems we have a similar guideline "c-test-tutorial-simple.txt",
maybe better to combine them together?

> > https://github.com/pevik/ltp/wiki/Shell-Test-API

Forgot to mention, with the increased number of docs, do you think it
necessary to create an index for including all documents in a sort?

-- 
Regards,
Li Wang


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  3:49   ` Li Wang
@ 2021-05-27  4:00     ` Xie Ziyao
  2021-05-27  4:19     ` Petr Vorel
  1 sibling, 0 replies; 10+ messages in thread
From: Xie Ziyao @ 2021-05-27  4:00 UTC (permalink / raw)
  To: ltp

>>> https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
>>> https://github.com/pevik/ltp/wiki/C-Test-API
> 
> Seems we have a similar guideline "c-test-tutorial-simple.txt",
> maybe better to combine them together?
> 
>>> https://github.com/pevik/ltp/wiki/Shell-Test-API
> 
> Forgot to mention, with the increased number of docs, do you think it
> necessary to create an index for including all documents in a sort?
+1

I think this is a good proposal.

Kind regards,
Ziyao

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  3:49   ` Li Wang
  2021-05-27  4:00     ` Xie Ziyao
@ 2021-05-27  4:19     ` Petr Vorel
  2021-05-27  4:51       ` Li Wang
  1 sibling, 1 reply; 10+ messages in thread
From: Petr Vorel @ 2021-05-27  4:19 UTC (permalink / raw)
  To: ltp

Hi Li, all,

> > > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
> > > https://github.com/pevik/ltp/wiki/C-Test-API

> Seems we have a similar guideline "c-test-tutorial-simple.txt",
> maybe better to combine them together?
I'd prefer to keep them separate. Because otherwise page gets long again.
But I'd consider to unify names ("LTP C Test API" vs. "C Test Case Tutorial"
- API vs. Case).

Kind regards,
Petr

> > > https://github.com/pevik/ltp/wiki/Shell-Test-API

> Forgot to mention, with the increased number of docs, do you think it
> necessary to create an index for including all documents in a sort?
There is list of pages on the right. But as it's sorted alphabetically,
it's not enough. Maybe we should add this list to README.md and to HOME wiki
page.

Well, I have to say I hate github Wiki features. There is no table of content,
for page, we cannot change list of pages, ...

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  4:19     ` Petr Vorel
@ 2021-05-27  4:51       ` Li Wang
  2021-05-27  6:02         ` Petr Vorel
  0 siblings, 1 reply; 10+ messages in thread
From: Li Wang @ 2021-05-27  4:51 UTC (permalink / raw)
  To: ltp

On Thu, May 27, 2021 at 12:19 PM Petr Vorel <pvorel@suse.cz> wrote:
>
> Hi Li, all,
>
> > > > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
> > > > https://github.com/pevik/ltp/wiki/C-Test-API
>
> > Seems we have a similar guideline "c-test-tutorial-simple.txt",
> > maybe better to combine them together?
> I'd prefer to keep them separate. Because otherwise page gets long again.
> But I'd consider to unify names ("LTP C Test API" vs. "C Test Case Tutorial"
> - API vs. Case).

No problem, I'm OK with unifying names.

And yes, we can rename with the same prefix start on purposely to make
them keep nearby in alphabetical sorting.

> > > > https://github.com/pevik/ltp/wiki/Shell-Test-API
>
> > Forgot to mention, with the increased number of docs, do you think it
> > necessary to create an index for including all documents in a sort?
> There is list of pages on the right. But as it's sorted alphabetically,
> it's not enough. Maybe we should add this list to README.md and to HOME wiki
> page.

To HOME wiki sounds good, we can do a simple triage in there.

Thanks a lot for the documentation work!

-- 
Regards,
Li Wang


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  4:51       ` Li Wang
@ 2021-05-27  6:02         ` Petr Vorel
  2021-05-27  6:56           ` Richard Palethorpe
  0 siblings, 1 reply; 10+ messages in thread
From: Petr Vorel @ 2021-05-27  6:02 UTC (permalink / raw)
  To: ltp

> On Thu, May 27, 2021 at 12:19 PM Petr Vorel <pvorel@suse.cz> wrote:

> > Hi Li, all,

> > > > > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
> > > > > https://github.com/pevik/ltp/wiki/C-Test-API

> > > Seems we have a similar guideline "c-test-tutorial-simple.txt",
> > > maybe better to combine them together?
> > I'd prefer to keep them separate. Because otherwise page gets long again.
> > But I'd consider to unify names ("LTP C Test API" vs. "C Test Case Tutorial"
> > - API vs. Case).

> No problem, I'm OK with unifying names.

> And yes, we can rename with the same prefix start on purposely to make
> them keep nearby in alphabetical sorting.

I'm not sure myself about renaming. But adding list of files generated by us in
HOME [1] and Developers corner [2] should help.

Also I'd add some NOTE: See also links (eg. to C Test API at the top in C Test
Case Tutorial and vice versa, in Shell Test API to C Test API and vice versa).

> > > > > https://github.com/pevik/ltp/wiki/Shell-Test-API

> > > Forgot to mention, with the increased number of docs, do you think it
> > > necessary to create an index for including all documents in a sort?
> > There is list of pages on the right. But as it's sorted alphabetically,
> > it's not enough. Maybe we should add this list to README.md and to HOME wiki
> > page.

> To HOME wiki sounds good, we can do a simple triage in there.

> Thanks a lot for the documentation work!
Thanks for your review!

Kind regards,
Petr

[1] https://github.com/linux-test-project/ltp/wiki
[2] https://github.com/linux-test-project/ltp#developers-corner

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27  6:02         ` Petr Vorel
@ 2021-05-27  6:56           ` Richard Palethorpe
  0 siblings, 0 replies; 10+ messages in thread
From: Richard Palethorpe @ 2021-05-27  6:56 UTC (permalink / raw)
  To: ltp

Hello,

Petr Vorel <pvorel@suse.cz> writes:

>> On Thu, May 27, 2021 at 12:19 PM Petr Vorel <pvorel@suse.cz> wrote:
>
>> > Hi Li, all,
>
>> > > > > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines
>> > > > > https://github.com/pevik/ltp/wiki/C-Test-API
>
>> > > Seems we have a similar guideline "c-test-tutorial-simple.txt",
>> > > maybe better to combine them together?

The tutorial is a step-by-step guide which also includes general info,
like how to use Git. The test writing guidelines are a reference
specifically for LTP. So they are very different IMO.

This reminds me though, that the tutorial probably needs updating.

>> > I'd prefer to keep them separate. Because otherwise page gets long again.
>> > But I'd consider to unify names ("LTP C Test API" vs. "C Test Case Tutorial"
>> > - API vs. Case).
>
>> No problem, I'm OK with unifying names.
>
>> And yes, we can rename with the same prefix start on purposely to make
>> them keep nearby in alphabetical sorting.

+1

>
> I'm not sure myself about renaming. But adding list of files generated by us in
> HOME [1] and Developers corner [2] should help.
>
> Also I'd add some NOTE: See also links (eg. to C Test API at the top in C Test
> Case Tutorial and vice versa, in Shell Test API to C Test API and vice
> versa).

+1

>
>> > > > > https://github.com/pevik/ltp/wiki/Shell-Test-API
>
>> > > Forgot to mention, with the increased number of docs, do you think it
>> > > necessary to create an index for including all documents in a sort?
>> > There is list of pages on the right. But as it's sorted alphabetically,
>> > it's not enough. Maybe we should add this list to README.md and to HOME wiki
>> > page.
>
>> To HOME wiki sounds good, we can do a simple triage in there.
>
>> Thanks a lot for the documentation work!
> Thanks for your review!
>
> Kind regards,
> Petr
>
> [1] https://github.com/linux-test-project/ltp/wiki
> [2] https://github.com/linux-test-project/ltp#developers-corner


-- 
Thank you,
Richard.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-26 15:49 [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines Petr Vorel
  2021-05-27  3:31 ` Li Wang
@ 2021-05-27 12:28 ` Cyril Hrubis
  2021-05-27 14:44   ` Petr Vorel
  1 sibling, 1 reply; 10+ messages in thread
From: Cyril Hrubis @ 2021-05-27 12:28 UTC (permalink / raw)
  To: ltp

Hi!
> Test Writing Guidelines wiki page is too long, thus split it
> into 3 parts:
> 
> 1) generic part (only first chapter, the same URL)
> 2) C test API (2.2 chapter, 4. Common problems)
> 3) shell test API
> 
> Unfortunately this breaks users' bookmarks.
> 
> Start numbering in headers from 1 on each page (links are broken
> anyway).
> 
> NOTE: in order to have '...' formatting as code,
> main header ====== was needed to add on the page.

The split looks good to me, acked.

> Signed-off-by: Petr Vorel <pvorel@suse.cz>
> ---
> See it:
> https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines

This page should be updated, at least the paragraph about commenting code
should now explain top level comment format.

The backward compatibility should be improved as well, since we have
kernel config parser now as well, etc.

I guess that I should sit down and write a patch for that one once the
split is applied.

-- 
Cyril Hrubis
chrubis@suse.cz

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines
  2021-05-27 12:28 ` Cyril Hrubis
@ 2021-05-27 14:44   ` Petr Vorel
  0 siblings, 0 replies; 10+ messages in thread
From: Petr Vorel @ 2021-05-27 14:44 UTC (permalink / raw)
  To: ltp

> Hi!
> > Test Writing Guidelines wiki page is too long, thus split it
> > into 3 parts:

> > 1) generic part (only first chapter, the same URL)
> > 2) C test API (2.2 chapter, 4. Common problems)
> > 3) shell test API

> > Unfortunately this breaks users' bookmarks.

> > Start numbering in headers from 1 on each page (links are broken
> > anyway).

> > NOTE: in order to have '...' formatting as code,
> > main header ====== was needed to add on the page.

> The split looks good to me, acked.
Thank to you all, merged.
Unfortunately I left two things, thus two more small fixes were needed.

> > Signed-off-by: Petr Vorel <pvorel@suse.cz>
> > ---
> > See it:
> > https://github.com/pevik/ltp/wiki/Test-Writing-Guidelines

> This page should be updated, at least the paragraph about commenting code
> should now explain top level comment format.

> The backward compatibility should be improved as well, since we have
> kernel config parser now as well, etc.

> I guess that I should sit down and write a patch for that one once the
> split is applied.

Agree to all.

Kind regards,
Petr

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-05-27 14:44 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-26 15:49 [LTP] [RFC PATCH 1/1] doc: Split test-writing-guidelines Petr Vorel
2021-05-27  3:31 ` Li Wang
2021-05-27  3:49   ` Li Wang
2021-05-27  4:00     ` Xie Ziyao
2021-05-27  4:19     ` Petr Vorel
2021-05-27  4:51       ` Li Wang
2021-05-27  6:02         ` Petr Vorel
2021-05-27  6:56           ` Richard Palethorpe
2021-05-27 12:28 ` Cyril Hrubis
2021-05-27 14:44   ` Petr Vorel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.