{"id":18794839,"url":"https://github.com/acerv/ltp-core","last_synced_at":"2026-04-28T23:34:12.118Z","repository":{"id":206414826,"uuid":"716554572","full_name":"acerv/ltp-core","owner":"acerv","description":"Linux Test Project (core)","archived":false,"fork":false,"pushed_at":"2024-03-07T15:53:36.000Z","size":64566,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-29T16:36:55.986Z","etag":null,"topics":["c","linux","linux-kernel","linux-testing","ltp","ltp-core","testing","testing-framework"],"latest_commit_sha":null,"homepage":"https://ltp-core.readthedocs.io/en/latest/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acerv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-09T11:35:01.000Z","updated_at":"2023-11-27T10:44:11.000Z","dependencies_parsed_at":"2023-11-24T16:26:37.501Z","dependency_job_id":"a2048b36-bcfb-41bf-8238-0e3d98f9d877","html_url":"https://github.com/acerv/ltp-core","commit_stats":null,"previous_names":["acerv/ltp-core"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acerv%2Fltp-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acerv%2Fltp-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acerv%2Fltp-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acerv%2Fltp-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acerv","download_url":"https://codeload.github.com/acerv/ltp-core/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239719154,"owners_count":19685898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","linux","linux-kernel","linux-testing","ltp","ltp-core","testing","testing-framework"],"created_at":"2024-11-07T21:31:16.288Z","updated_at":"2025-12-29T22:30:14.756Z","avatar_url":"https://github.com/acerv.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Linux Test Project (core)\n\nLTP is a lightweight and versatile testing framework based on [LTP library] and\ndesigned specifically for C applications on the Linux platform. With an emphasis\non simplicity and ease of use, LTP provides developers with a powerful toolset\nfor creating and executing test cases, both for Kernel and regular C\napplications.\n\nHere you can see a short example:\n\n```c\n#include \u003ctst_test.h\u003e\n\nvoid setup(void) {\n\t// your setup code goes here\n\n\ttst_res(TINFO, \"example setup\");\n}\n\nvoid cleanup(void) {\n\t// your cleanup code goes here\n\n\ttst_res(TINFO, \"example cleanup\");\n}\n\nvoid run(void) {\n\t// your test code goes here\n\n\ttst_res(TPASS, \"Doing hardly anything is easy\");\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.setup = setup,\n\t.cleanup = cleanup,\n};\n```\n\nNow we just need to compile and run:\n\n```sh\ngcc -lltp example.c -o example\n\n./example\n../lib/tst_test.c:1690: TINFO: LTP version: 20230929-148-g121b0e2ce\n../lib/tst_test.c:1576: TINFO: Timeout per run is 0h 00m 30s\nexample.c:6: TINFO: example setup\nexample.c:18: TPASS: example test passed\nexample.c:12: TINFO: example cleanup\n\nSummary:\npassed   1\nfailed   0\nbroken   0\nskipped  0\nwarnings 0\n```\n\nEach test has some default options which are linked into the test. They can be\nseen with `./example -h` command.\n\n# How to build/install/test the framework\n\nLTP framework is using [Meson](https://mesonbuild.com/) as the main build\nsystem. Following commands show how to use it in order to build/install/test\nthe LTP framework:\n\n```sh\n# prepare build directory\nmeson setup builddir \u0026\u0026 cd builddir\n\n# compile library\nmeson compile\n\n# compile shared library\nmeson configure -Ddefault_library=shared\nmeson compile\n\n# install library\nmeson install\n\n# running library tests\nmeson configure -Dbuild_tests=true\nmeson test\n```\n\n# Features\n\nFollowing paragraph shows available features in the LTP core framework.\n\n## Show messages\n\nInside the framework we have many ways to send messages without stopping the\ntest. All of them can be sent using `tst_res` function with the following flags:\n\n- `TINFO`: information message\n- `TWARN`: warning message\n- `TPASS`: PASS message\n- `TFAIL`: FAIL message\n\nWhen `TERRNO` is used in combination with one of the flags, framework will\nappend message with `errno` at the end.\n\nAll tests have to send **at least** a `TPASS` or `TFAIL` message before the end\nof test execution. An error will be shown as remainder otherwise.\n\n```c\nstatic void run(void)\n{\n\ttst_res(TINFO, \"This is an information message\");\n\ttst_res(TWARN, \"This is a warning message\");\n\ttst_res(TPASS, \"This is a pass message\");\n\ttst_res(TFAIL, \"This is a fail message\");\n\n\t// TERRNO can be used by all flags above\n\ttst_res(TINFO | TERRNO, \"This is a message with errno at the end\");\n}\n```\n\n## Raise a breaking error\n\nAny time we need to stop the test because something broke during the execution,\nwe can use `tst_brk` function to send a broken error via `TBROK` flag.\nThe test will immediately stop.\n\nWhen `TERRNO` is used in combination with `TBROK`, the framework will print\n`errno` that caused the failure.\n\n```c\nstatic void run(void)\n{\n\t// if something is broken raise an error and stop the test\n\n\ttst_brk(TBROK, \"Something is broken. Stop the test.\");\n\ttst_brk(TBROK | TERRNO, \"Something is broken. Print errno\");\n}\n```\n\n## Raise a configuration error\n\nEach time we want to end the test because it's not satisfying our needs, we can\nuse `tst_brk` function to send a configuration error via `TCONF` flag.\nTest will immediately stop.\n\n```c\nstatic void setup(void)\n{\n\t// my requirements are not satisfied, so stop the test with message\n\n\ttst_brk(TCONF, \"Configuration problem. Stop the test.\");\n}\n\nstatic struct tst_test test = {\n\t.setup = setup,\n\t.test_all = run,\n};\n```\n\n## Test results using TST_* macros\n\nThe framework provides a few macros that can be used to automatically check if\na specific function has passed or failed. These macros take a function call as\nthe first parameter and a printf-like format string and parameters as well.\nThese test macros then expand to a code that runs the call, checks the return\nvalue and `errno` and reports the test result.\n\n```c\nstatic void run(void)\n{\n\t...\n\tTST_EXP_PASS(stat(fname, \u0026statbuf), \"stat(%s, ...)\", fname);\n\n\tif (!TST_PASS)\n\t\treturn;\n\t...\n}\n```\n\nThe `TST_EXP_PASS()` can be used for calls that return -1 on failure and 0 on\nsuccess. It will check for the return value and reports failure if the return\nvalue is not equal to 0. The call also sets the `TST_PASS` variable to 1 if\nthe call succeeeded.\n\nAs seen above, this and similar macros take optional variadic arguments. These\nbegin with a format string and then appropriate values to be formatted.\n\n```c\nstatic void run(void)\n{\n\t...\n\tTST_EXP_FD(open(fname, O_RDONLY), \"open(%s, O_RDONLY)\", fname);\n\n\tSAFE_CLOSE(TST_RET);\n\t...\n}\n```\n\nThe `TST_EXP_FD()` is the same as `TST_EXP_PASS()` the only difference is that\nthe return value is expected to be a file descriptor so the call passes if\npositive integer is returned.\n\n```c\nstatic void run(void)\n{\n\t...\n\tTST_EXP_FAIL(stat(fname, \u0026statbuf), ENOENT, \"stat(%s, ...)\", fname);\n\t...\n}\n```\n\nThe `TST_EXP_FAIL()` is similar to `TST_EXP_PASS()` but it fails the test if\nthe call haven't failed with -1 and `errno` wasn't set to the expected one\npassed as the second argument.\n\n```c\nstatic void run(void)\n{\n\t...\n\tTST_EXP_FAIL2(msgget(key, flags), EINVAL, \"msgget(%i, %i)\", key, flags);\n\t...\n}\n```\n\nThe `TST_EXP_FAIL2()` is the same as `TST_EXP_FAIL()` except the return value is\nexpected to be non-negative integer if call passes. \n\n`TST_EXP_FAIL_SILENT()` and `TST_EXP_FAIL2_SILENT()` variants are less verbose\nand do not print `TPASS` messages when SCALL fails as expected.\n\n```c\nTEST(socket(AF_INET, SOCK_RAW, 1));\nif (TST_RET \u003e -1) {\n\ttst_res(TFAIL, \"Created raw socket\");\n\tSAFE_CLOSE(TST_RET);\n} else if (TST_ERR != EPERM) {\n\ttst_res(TFAIL | TTERRNO,\n\t\t\"Failed to create socket for wrong reason\");\n} else {\n\ttst_res(TPASS | TTERRNO, \"Didn't create raw socket\");\n}\n```\n\nThe `TST_*` macro sets `TST_RET` to its argument's return value and `TST_ERR` to\n`errno`+ The `TTERNO` flag can be used to print the error number's symbolic\nvalue.\n\nNo library function or macro, except those in `tst_test_macros.h`, will\nwrite to these variables. So their values will not be changed unexpectedly.\n\n```c\nTST_EXP_POSITIVE(wait(\u0026status));\n\nif (!TST_PASS)\n\treturn;\n```\n\nIf the return value of `wait` is positive or zero, this macro will print a pass\nresult and set `TST_PASS` appropriately. If the return value is negative, then\nit will print fail.  There are many similar macros to those shown here, please\nsee `tst_test_macros.h`.\n\n```c\nTST_EXP_EQ_LI(val1, val2);\nTST_EXP_EQ_UI(val1, val2);\nTST_EXP_EQ_SZ(val1, val2);\nTST_EXP_EQ_SSZ(val1, val2);\n\n/* Use as */\nTST_EXP_EQ_LI(sig_caught, SIGCHLD);\n```\n\nSet of macros for different integer type comparsions. These macros print the\nvariable names as well as values in both pass and fail scenarios.\n\n## Utilities functions\n\nSome of the following utilities can be used to obtain information during tests\nexecution.\n\n```c\nconst char *tst_strsig(int sig);\n```\n\nReturn the given signal number's corresponding string.\n\n```c\nconst char *tst_strerrno(int err);\n```\n\nReturn the given errno number's corresponding string. Using this function to\ntranslate `errno` values to strings is preferred. You should not use the\n`strerror()` function in the testcases.\n\n```c\nconst char *tst_strstatus(int status);\n```\n\nReturns string describing the status as returned by `wait()`. This function is\nnot thread safe.\n\n```c\nvoid tst_set_max_runtime(int max_runtime);\n```\n\nAllows for setting max_runtime per test iteration dynamically in the test 'setup()',\nthe timeout is specified in seconds. There are a few testcases whose runtime\ncan vary arbitrarily, these can disable timeouts by setting it to\nTST_UNLIMITED_RUNTIME.\n\n```c\nvoid tst_flush(void);\n```\n\nFlush output streams, handling errors appropriately.\n\nThis function is rarely needed when you have to flush the output streams\nbefore calling `fork()` or `clone()`. Note that the `SAFE_FORK()` and \n`SAFE_CLONE()` calls this function automatically. See 2.4 FILE buffers and\n`fork()` for explanation why is this needed.\n\n## Using LTP API without defining a test\n\nSometimes we need to define a simple binary, without linking to the `tst_test`\nstruct definition, but using the framework functionalities, such as macros,\nbroken errors, etc. This is common in such cases where we need to call an\nexternal binary that has to perform specific operations and we don't want to\nwaste time redefining features implemented inside the framework. To do so,\n`TST_NO_DEFAULT_MAIN` can be used.\n\n```c\n#define TST_NO_DEFAULT_MAIN\n#include \"tst_test.h\"\n\nint main(void)\n{\n\ttst_res(TPASS, \"Child passed!\");\n\treturn 0;\n}\n```\n\n## Skip test using preprocessor\n\nIn some cases we need to skip the entire test if system does not satisfy\nour needs. It can be the case of a test that requires to be run on x86. In this\ncase, `TST_TEST_TCONF` macro can be used.\n\n```c\n#include \u003ctst_test.h\u003e\n\n#if defined(__i386__) || defined(__x86_64__)\n\nstatic void run(void)\n{\n\t// my test code\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n};\n\n#else\nTST_TEST_TCONF(\"Test supported only on x86\");\n#endif\n```\n\n## Customize test options\n\nTest options can be customized assigning `.options` argument to the `tst_test`\nstruct definition.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.options = (struct tst_option[]) {\n\t\t{\"v\", \u0026verbose, \"Verbose output\"},\n\t\t{\"s\", \u0026size, \"Size of the file to generate\"},\n\t\t{},\n\t},\n};\n```\n\n## Run test inside a temporary folder\n\nWhen we need to create files or directories, we can use a temporary folder so\nwe won't generate data in the current directory.\nThis can be achieved setting `.needs_tmpdir` flag.\nThe framework will take care to create a temporary directory before test, to\nmove inside it and to delete it once test is completed.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_tmpdir = 1,\n};\n```\n\n## Root requirement\n\nSome operations inside test might require root. To ensure that we are running\nthe test as root user, we can set `.needs_root`.\nIf test will be executed as normal user, the framework will send a configuration\nerror and stop the test.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_root = 1,\n};\n```\n\n## Maximum time\n\nIf we want that our test never exceed a specific execution time, we can set\n`.max_runtime` flag in seconds. At the end of this time, test will be killed\nby the framework raising a `TBROK` error.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t// 1 hour max execution\n\t.max_runtime = 3600,\n};\n```\n\n## Forked children\n\nSome tests require to spawn one or multiple children using `SAFE_FORK()` macro.\nThe framework automatically handles spawned children inside the test, ensuring\nthat all of them will be completed before the end of the test. This feature can\nbe activated setting `.forks_child` flag.\nWhen `SAFE_FORK()` is used, the framework will remind that the flag has to be\nset.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.forks_child = 1,\n};\n```\n\n## Checkpoint handling\n\nWhen using `TST_CHECKPOINT_*` macros, `.needs_checkpoints` flag has to be set.\nIn this way, futex will be released just before ending of the test.\nWhen `TST_CHECKPOINT_` macros are used, the framework will remind that the\nflag has to be set.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_checkpoints = 1,\n};\n```\n\n## Using a device\n\nIf `.needs_device` flag is set, the `tst_device` structure is initialized with\npath the test device and default filesystem to be used.\n\n```c\nstatic void run(void)\n{\n\ttst_res(TINFO, \"My device is %s\", tst_device-\u003edev);\n\n\t// my test code using device\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_device = 1,\n};\n```\n\n## Create and mount a device\n\nIf `.mount_device` is set, the device is mounted at `.mntpoint` which is used\nto pass a directory name that will be created and used as mount destination.\nYou can pass additional flags and data to the mount command via `.mnt_flags`\nand `.mnt_data` pointers. Note that `.mount_device` implies `.needs_device`\nand `.format_device` so there is no need to set the later two. To pass\nadditional options to `mkfs.$fs` utility, `.dev_fs_type` and `dev_extra_opts`\ncan be used.\nDevice filesystem can be defined by `.dev_fs_type`.\nIf `.dev_min_size` is set, framework will check that device has that minimum\nsize in megabytes.\n\n```c\nstatic void run(void)\n{\n\ttst_res(TINFO,\n\t\t\"My device %s is formatted with %s filesystem\",\n\t\ttst_device-\u003edev,\n\t\ttst_device-\u003efs_type);\n\n\t// my test code using tst_device\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_device = 1,\n\t.mount_device = 1,\n\t.format_device = 1,\n\t.dev_min_size = 2, // reserve 2MB device\n\t.dev_fs_type = \"xfs\",\n\t.mntpoint = \"mntpoint\",\n\t.mnt_data = \"usrquota\",\n\t.mnt_flags = MS_STRICTATIME,\n};\n```\n\n**IMPORTANT**: Close all file descriptors (that point to files in test temporary\ndirectory, even the unlinked ones) either in the 'test()' function or in the\ntest 'cleanup()' otherwise the test may break temporary directory removal on NFS\n(look for \"NFS silly rename\").\n\n### Overlay filesystem\n\nIf `.needs_overlay` is set, mount point will use an overlay fs.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_device = 1,\n\t.mount_device = 1,\n\t.format_device = 1,\n\t.mntpoint = \"mntpoint\",\n\t.needs_overlay = 1,\n};\n```\n\n### Read-only filesystem\n\nIf `.needs_rofs` is set, read-only filesystem is mounted at `.mntpoint`. This\none is supposed to be used for `EROFS` tests.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_device = 1,\n\t.mount_device = 1,\n\t.format_device = 1,\n\t.mntpoint = \"mntpoint\",\n\t.needs_rofs = 1,\n};\n```\n\n### Mount hugepages\n\nIf `.needs_hugelbfs` is set, the hugetlbfs will be mounted at `.mntpoint`.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_device = 1,\n\t.mount_device = 1,\n\t.format_device = 1,\n\t.mntpoint = \"mntpoint\",\n\t.needs_hugelbfs = 1,\n};\n```\n\n## Execute for all filesystems\n\nWhen the `.all_filesystems` flag is set the `.skip_filesystems` list is passed\nto the function that detects supported filesystems any listed filesystem is\nnot included in the resulting list of supported filesystems.\n\nIf test needs to adjust expectations based on filesystem type it's also\npossible to detect filesystem type at the runtime. This is preferably used\nwhen only subset of the test is not applicable for a given filesystem.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.all_filesystems = 1,\n\t.skip_filesystems = (const char *const []) {\n\t\t\t\"exfat\",\n\t\t\t\"tmpfs\",\n\t\t\t\"ramfs\",\n\t\t\t\"nfs\",\n\t\t\t\"vfat\",\n\t\t\tNULL\n\t},\n};\n```\n\n**NOTE** that ext2, ext3 or ext4 in `.skip_filesystems` on tests which does\n**not** use `.all_filesystems` needs to be defined as 'ext2/ext3/ext4'. The\nreason is that it is hard to detect used filesystem due to overlapping the\nfunctionality.\n\nOTOH tests which use `.skip_filesystems` **with** `.all_filesystems` can skip\nonly filesystems which are actually used in `.all_filesystems`: ext2, ext3,\next4, xfs, btrfs, vfat, exfat, ntfs, tmpfs (defined in `fs_type_whitelist[]`).\nIt does not make sense to list other filesystems.\n\n## Minimum supported kernel\n\nTo avoid using kernels which are too old for the test, we can use `.min_kver`\nattribute in the `tst_test` struct definition. If minimum kernel is not\nsatisfied, a configuration error will be shown.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.min_kver = \"4.11\",\n};\n```\n\n## Supported architectures\n\nTest's supported architectures can be defined using the `.supported_archs` flag\ninside `tst_test` struct definition. If current architecture is not supported,\na configuration error will be shown.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.supported_archs = (const char *const []) {\n\t\t\"x86_64\",\n\t\t\"x86\",\n\t\tNULL\n\t}\n}\n```\n\n## Skip tests based on system state\n\nTest can be skipped on various conditions:\n\n- if `.skip_in_secureboot` is set, on enabled SecureBoot\n- if `.skip_in_lockdown` is set, on lockdown\n- if `.skip_in_compat` is set, in 32-bit compat mode\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.skip_in_secureboot = 1,\n\t.skip_in_lockdown = 1,\n\t.skip_in_compat = 1,\n}\n```\n\n## Require minimum numbers of CPU\n\nSome tests require more than specific number of CPU. It can be defined with\n`.min_cpus`.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.min_cpus = 2,\n}\n```\n\n## Require minimum memory or swap size\n\nSome tests require a minimum amount of memory or swap.\nTo make sure that test will run only on systems with more than minimal required\namount of RAM set `.min_mem_avail`. Similarily for tests that require certain\namount of free Swap use `.min_swap_avail`. Both flags are expressed in MB.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t// check that minimum memory is 20 MB\n\t.min_mem_avail = 20,\n\t// check that minimum swap is 2 MB\n\t.min_swap_avail = 2,\n}\n```\n\n## Reserving hugepages\n\nMany of the LTP tests need to use hugepage in their testing, this allows the\ntest can reserve hugepages from system via `.hugepages = {xx, TST_REQUEST}`.\n\nWe achieved two policies for reserving hugepages:\n\n- `TST_REQUEST`: It will try the best to reserve available huge pages and return\n  the number of available hugepages in `tst_hugepages`, which may be 0 if\n  hugepages are not supported at all.\n\n- `TST_NEEDS`: This is an enforced requirement, LTP should strictly do hpages\n  applying and guarantee the **HugePages_Free** no less than pages which makes\n  that test can use these specified numbers correctly. Otherwise, test exits\n  with `TCONF` if the attempt to reserve hugepages fails or reserves less than\n  requested.\n\nWith success test stores the reserved hugepage number in `tst_hugepages`. For\nsystem without hugetlb supporting, variable `tst_hugepages` will be set to 0.\nIf the hugepage number needs to be set to 0 on supported hugetlb system, please\nuse `.hugepages = {TST_NO_HUGEPAGES}`.\n\nAlso, we do cleanup and restore work for the hpages resetting automatically.\n\n```c\nstatic void run(void)\n{\n\t...\n\n\tif (tst_hugepages == test.hugepages.number)\n\t\tTEST(do_hpage_test);\n\telse\n\t\t...\n\t...\n}\n\nstruct tst_test test = {\n\t.test_all = run,\n\t.hugepages = {2, TST_REQUEST},\n};\n```\n\n## Tainted kernels\n\nIn some cases we need to detect whether a testcase triggers a kernel warning,\nbug or oops. The `.taint_check` flag can be used to detect them.\n\nList of supported taint flags:\n\n- `TST_TAINT_G`: a module with non-GPL license loaded\n- `TST_TAINT_F`: a module was force-loaded\n- `TST_TAINT_S`: SMP with Non-SMP kernel\n- `TST_TAINT_R`: module force unloaded\n- `TST_TAINT_M`: machine check error occurred\n- `TST_TAINT_B`: page-release function found bad page\n- `TST_TAINT_U`: user requested taint flag\n- `TST_TAINT_D`: kernel died recently - OOPS or BUG\n- `TST_TAINT_A`: ACPI table has been overwritten\n- `TST_TAINT_W`: a warning has been issued by kernel\n- `TST_TAINT_C`: driver from drivers/staging was loaded\n- `TST_TAINT_I`: working around BIOS/Firmware bug\n- `TST_TAINT_O`: out of tree module loaded\n- `TST_TAINT_E`: unsigned module was loaded\n- `TST_TAINT_L`: A soft lock-up has previously occurred\n- `TST_TAINT_K`: kernel has been live-patched\n- `TST_TAINT_X`: auxiliary taint, for distro's use\n- `TST_TAINT_T`: kernel was built with the struct randomization plugin\n\n```c\nvoid run(void)\n{\n\t...\n\tif (tst_taint_check() != 0)\n\t\ttst_res(TFAIL, \"kernel has issues\");\n\telse\n\t\ttst_res(TPASS, \"kernel seems to be fine\");\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.tint_check = TST_TAINT_W | TST_TAINT_D,\n}\n```\n\n## Testing similar syscalls in one test\n\nIn some cases kernel has several very similar syscalls that do either the same\nor very similar job. This is most noticeable on i386 where we commonly have\ntwo or three syscall versions. That is because i386 was first platform that\nLinux was developed on and because of that most mistakes in API happened there\nas well. However this is not limited to i386 at all, it's quite common that\nversion two syscall has added missing flags parameters or so.\n\nIn such cases it does not make much sense to copy\u0026paste the test code over and\nover, rather than that the test library provides support for test variants.\nThe idea behind test variants is simple, we run the test several times each\ntime with different syscall variant.\n\nThe implementation consist of `test_variants` integer that, if set, denotes\nnumber of test variants. The test is then forked and executed `test_variants`\ntimes each time with different value in global `tst_variant` variable.\n\n```c\nstatic int do_foo(void)\n{\n\tswitch (tst_variant) {\n\tcase 0:\n\t\treturn foo();\n\tcase 1:\n\t\treturn syscall(__NR_foo);\n\t}\n\n\treturn -1;\n}\n\nstatic void run(void)\n{\n\tTEST(do_foo);\n}\n\nstatic void setup(void)\n{\n\tswitch (tst_variant) {\n\tcase 0:\n\t\ttst_res(TINFO, \"Testing foo variant 1\");\n\tbreak;\n\tcase 1:\n\t\ttst_res(TINFO, \"Testing foo variant 2\");\n\tbreak;\n\t}\n}\n\nstruct tst_test test = {\n\t.setup = setup,\n\t.test_all = run,\n\t.test_variants = 2,\n};\n```\n\n## Checking kernel for the driver support\n\nSome tests may need specific kernel drivers, either compiled in, or built\nas a module. If `.needs_drivers` points to a `NULL` terminated array of kernel\nmodule names these are all checked and the test exits with `TCONF` on the\nfirst missing driver.\n\nThe detection is based on reading `modules.dep` and `modules.builtin` files\ngenerated by kmod. The check is skipped on Android.\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_drivers = (const char *const []) {\n\t\t\"zram\",\n\t\tNULL\n\t},\n};\n```\n\n## Saving \u0026 restoring /proc|sys values\n\nFramework can be instructed to save and restore value of specified (/proc|sys)\nfiles. This is achieved by initialized tst_test struct field 'save_restore'.\nIt is a NULL-terminated array of struct `tst_path_val` where each\n`tst_path_val.path` represents a file, whose value is saved at the beginning and\nrestored at the end of the test.\n\nIf non-NULL string is passed in `tst_path_val.val`, it is written to the\nrespective file at the beginning of the test. Only the first line of a specified\nfile is saved and restored.\n\nBy default, the test will end with `TCONF` if the file is read-only or does not\nexist. If the optional write of new value fails, the test will end with `TBROK`.\nThis behavior can be changed using `tst_path_val.flags`:\n\n- `TST_SR_TBROK_MISSING`: End test with `TBROK` if the file does not exist\n- `TST_SR_TCONF_MISSING`: End test with `TCONF` if the file does not exist\n- `TST_SR_SKIP_MISSING`: Continue without saving the file if it does not exist\n- `TST_SR_TBROK_RO`: End test with `TBROK` if the file is read-only\n- `TST_SR_TCONF_RO`: End test with `TCONF` if the file is read-only\n- `TST_SR_SKIP_RO`: Continue without saving the file if it is read-only\n- `TST_SR_IGNORE_ERR`: Ignore errors when writing new value into the file\n\nCommon flag combinations also have shortcuts:\n\n- `TST_SR_TCONF`: Equivalent to `TST_SR_TCONF_MISSING | TST_SR_TCONF_RO`\n- `TST_SR_TBROK`: Equivalent to `TST_SR_TBROK_MISSING | TST_SR_TBROK_RO`\n- `TST_SR_SKIP`: Equivalent to `TST_SR_SKIP_MISSING | TST_SR_SKIP_RO`\n\n`restore` is always strict and will `TWARN` if it encounters any error.\n\n```c\nstatic struct tst_test test = {\n\t...\n\t.setup = setup,\n\t.save_restore = (const struct tst_path_val[]) {\n\t\t{\"/proc/sys/kernel/core_pattern\", NULL, TST_SR_TCONF},\n\t\t{\"/proc/sys/user/max_user_namespaces\", NULL, TST_SR_SKIP},\n\t\t{\"/sys/kernel/mm/ksm/run\", \"1\", TST_SR_TBROK},\n\t\t{}\n\t},\n};\n```\n\n## Checking for kernel configuration\n\nGenerally testcases should attempt to autodetect as much kernel features as\npossible based on the currently running kernel. We do have `tst_check_driver()`\nto check if functionality that could be compiled as kernel module is present\non the system, disabled syscalls can be detected by checking for `ENOSYS`\nerrno etc.\n\nHowever, in rare cases core kernel features couldn't be detected based on the\nkernel userspace API and we have to resort to parse the kernel `.config`.\n\nFor this cases the test should set the NULL-terminated `.needs_kconfigs`\narray of boolean expressions with constraints on the **kconfig** variables. The\nboolean expression consits of variables, two binary operations `\u0026` and `|`,\nnegation `!` and correct sequence of parentesis `()`. Variables are expected\nto be in a form of `CONFIG_FOO[=bar]`.\n\nThe test will continue to run if all expressions are evaluated to `True`.\nMissing variable is mapped to `False` as well as variable with different than\nspecified value, e.g. `CONFIG_FOO=bar` will evaluate to `False` if the value\nis anything else but `bar`. If config variable is specified as plain\n`CONFIG_FOO`, it's evaluated to true it's set to any value (typically =y or =m).\n\n```c\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.needs_kconfigs = (const char *[]) {\n\t\t\"CONFIG_VETH\",\n\t\t\"CONFIG_USER_NS=y\",\n\t\t\"CONFIG_NET_NS=y\",\n\t\t\"CONFIG_NET_SCH_HTB\",\n\t\t\"CONFIG_NET_CLS_TCINDEX\",\n\t\tNULL\n\t},\n};\n```\n\n## Guarded buffers\n\nThe test library supports guarded buffers, which are buffers allocated so\nthat:\n\n- The end of the buffer is followed by a `PROT_NONE` page\n- The remainder of the page before the buffer is filled with random canary data\n\nWhich means that the any access after the buffer will yield a `Segmentation\nfault` or `EFAULT` depending on if the access happened in userspace or the kernel\nrespectively. The canary before the buffer will also catch any write access\noutside of the buffer.\n\nThe purpose of the feature is to catch off-by-one bugs which happens when\nbuffers and structures are passed to syscalls. New tests should allocate\nguarded buffers for all data passed to the tested syscall which are passed by\na pointer.\n\n```c\nstatic struct timex *buf;\n\nstatic void run(void)\n{\n\t// normally use buf as it is\n}\n\nstatic struct tst_test test = {\n\t.test_all = run,\n\t.bufs = (struct tst_buffers []) {\n\t\t{\u0026buf, .size = sizeof(*buf)},\n\t\t{},\n\t},\n};\n```\n\nGuarded buffers can be allocated on runtime in a test `setup()` by a\n`tst_alloc()` or by `tst_strdup()` as well as by filling up the `.bufs` array in\nthe `tst_test` struct.\n\nSo far the `tst_test` struct supports allocating either a plain buffer by\nsetting up the size or `struct iovec`, which is allocated recursively including\nthe individual buffers as described by an `-1` terminated array of buffer\nsizes.\n\n## Adding and removing capabilities\n\nSome tests may require the presence or absence of particular\ncapabilities. Using the API provided by `tst_capability.h` the test author can\ntry to ensure that some capabilities are either present or absent during the\ntest.\n\nFor example; below we try to create a raw socket, which requires\n`CAP_NET_ADMIN`. During setup we should be able to do it, then during run it\nshould be impossible. The LTP capability library will check before setup that\nwe have this capability, then after setup it will drop it.\n\n```c\n#include \"tst_test.h\"\n#include \"tst_capability.h\"\n#include \"tst_safe_net.h\"\n#include \"lapi/socket.h\"\n\nstatic void run(void)\n{\n\tTEST(socket(AF_INET, SOCK_RAW, 1));\n\tif (TST_RET \u003e -1) {\n\t\ttst_res(TFAIL, \"Created raw socket\");\n\t} else if (TST_ERR != EPERM) {\n\t\ttst_res(TFAIL | TTERRNO,\n\t\t\t\"Failed to create socket for wrong reason\");\n\t} else {\n\t\ttst_res(TPASS | TTERRNO, \"Didn't create raw socket\");\n\t}\n}\n\nstatic void setup(void)\n{\n\tTEST(socket(AF_INET, SOCK_RAW, 1));\n\tif (TST_RET \u003c 0)\n\t\ttst_brk(TCONF | TTERRNO, \"We don't have CAP_NET_RAW to begin with\");\n\n\tSAFE_CLOSE(TST_RET);\n}\n\nstatic struct tst_test test = {\n\t.setup = setup,\n\t.test_all = run,\n\t.caps = (struct tst_cap []) {\n\t\tTST_CAP(TST_CAP_REQ, CAP_NET_RAW),\n\t\tTST_CAP(TST_CAP_DROP, CAP_NET_RAW),\n\t\t{}\n\t},\n};\n```\n\n## Checking for required commands\n\nRequired commands can be checked with `.needs_cmds`, which points to a `NULL`\nterminated array of strings such as:\n\n```c\n.needs_cmds = (const char *const []) {\n\t\"useradd\",\n\t\"userdel\",\n\tNULL\n},\n```\n\nAlso can check required command version whether is satisfied by using\n`needs_cmds` such as:\n\n```c\n.needs_cmds = (const char *const []) {\n\t\"mkfs.ext4 \u003e= 1.43.0\",\n\tNULL\n},\n```\n\n## Test tags\n\nTest tags are name-value pairs that can hold any test metadata.\n\nWe have additional support for CVE entries, git commit in mainline kernel,\nstable kernel or glibc git repository.  If a test is a regression test it\nshould include these tags.  They are printed when test fails.\n\nCVE, mainline and stable kernel git commits in a regression test for a\nkernel bug:\n\n```c\nstruct tst_test test = {\n\t.test_all = run,\n\t.tags = (const struct tst_tag[]) {\n\t\t{\"linux-git\", \"9392a27d88b9\"},\n\t\t{\"linux-git\", \"ff002b30181d\"},\n\t\t{\"known-fail\", \"ustat() is known to fail with EINVAL on Btrfs\"},\n\t\t{\"linux-stable-git\", \"c4a23c852e80\"},\n\t\t{\"CVE\", \"2020-29373\"},\n\t\t{}\n\t}\n};\n```\n\nGlibc and musl git commits in a regression test for `glibc` and `musl` bugs:\n\n```c\nstruct tst_test test = {\n\t.test_all = run,\n\t.tags = (const struct tst_tag[]) {\n\t\t{\"glibc-git\", \"574500a108be\"},\n\t\t{\"musl-git\", \"fa4a8abd06a4\"},\n\t\t{}\n\t}\n};\n```\n\n# From LTP to LTP core\n\nThe following paragraph will show what's changed between LTP and LTP core.\n\n## Deprecated libraries\n\n- libipc\n- libmsgctl\n- libnewipc\n- sigwait\n- libswap\n- vdso_helpers\n\n## Autotools to Meson conversion\n\nWhen moving to meson build system, a few modifications have been made, some\nvariables have been dropped and some autotools function have been ignored.\nAnd this is something you should know before compiling (for example) testcases\ncreated with the previous LTP library.\n\n### Unused LTP_CHECK_*\n\n- `LTP_CHECK_ACL_SUPPORT`: used by tests\n- `LTP_CHECK_CC_WARN_OLDSTYLE`: not needed\n- `LTP_CHECK_CRYPTO`: used by tests\n- `LTP_CHECK_KERNEL_DEVEL`: compile kernel, we don't need it\n- `LTP_CHECK_LIBMNL`: used by tests\n- `LTP_CHECK_LINUX_PTRACE`: only used by ptrace tests\n- `LTP_CHECK_NOMMU_LINUX`: no idea how to use it\n- `LTP_CHECK_SELINUX`: used by tests\n- `LTP_CHECK_SYSCALL_FCNTL`: used by tests\n- `LTP_CHECK_SYSCALL_NUMA`: used by tests\n- `LTP_CHECK_SYSCALL_SIGNALFD`: used by tests\n- `LTP_DETECT_HOST_CPU`: meson has cross-compilation mechanism\n\n### Undefined flags\n\n- `HAVE_LIBACL`\n- `HAVE_LIBCRYPTO`\n- `HAVE_LIBMNL`\n\n### Flags to merge\n\n- `HAVE_LIBCAP` / `HAVE_SYS_CAPABILITY_H`\n- `HAVE_LIBAIO` / `HAVE_LIBAIO_H`\n\n## TODO\n\n- remove old API\n- more documentation on tst_test features\n- more documentation on cross-compiling\n- CI configuration\n\n[LTP library]: https://github.com/linux-test-project/ltp\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facerv%2Fltp-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facerv%2Fltp-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facerv%2Fltp-core/lists"}