{"id":18586221,"url":"https://github.com/savoirfairelinux/cukinia","last_synced_at":"2026-03-02T13:40:10.523Z","repository":{"id":19805910,"uuid":"87968238","full_name":"savoirfairelinux/cukinia","owner":"savoirfairelinux","description":"A simple on-target system test framework for Linux","archived":false,"fork":false,"pushed_at":"2024-04-12T14:55:11.000Z","size":352,"stargazers_count":34,"open_issues_count":6,"forks_count":19,"subscribers_count":18,"default_branch":"master","last_synced_at":"2024-04-14T06:01:21.970Z","etag":null,"topics":["embedded","firmware","linux","microtest","stdout","tdd","testing","validation"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/savoirfairelinux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2017-04-11T18:49:02.000Z","updated_at":"2024-04-15T13:36:44.652Z","dependencies_parsed_at":"2024-04-15T13:36:44.249Z","dependency_job_id":"18dea551-9461-494d-a714-ceedc3a77dd9","html_url":"https://github.com/savoirfairelinux/cukinia","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savoirfairelinux%2Fcukinia","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savoirfairelinux%2Fcukinia/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savoirfairelinux%2Fcukinia/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/savoirfairelinux%2Fcukinia/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/savoirfairelinux","download_url":"https://codeload.github.com/savoirfairelinux/cukinia/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225709,"owners_count":21068078,"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":["embedded","firmware","linux","microtest","stdout","tdd","testing","validation"],"created_at":"2024-11-07T00:37:27.456Z","updated_at":"2026-03-02T13:40:10.512Z","avatar_url":"https://github.com/savoirfairelinux.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"![cukinia logo](./doc/cukinia_logo.png?raw=true)\n\n\u003e Simple, on-target validation framework for embedded Linux systems\n\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Latest Release](https://img.shields.io/github/v/release/savoirfairelinux/cukinia)](https://github.com/savoirfairelinux/cukinia/releases)\n[![Cukinia Test Suite](https://github.com/savoirfairelinux/cukinia/actions/workflows/run-cukinia.yml/badge.svg)](https://github.com/savoirfairelinux/cukinia/actions/workflows/run-cukinia.yml)\n\n# Cukinia Test Framework\n\nCukinia is designed to help embedded Linux firmware developers run simple\nsystem-level validation tests on their systems.\n\nIt integrates well with embedded system generation frameworks such as Yocto and\nBuildroot, and can be run manually, or by your favourite CI framework.\n\n## Project objectives\n\nCukinia works if it offers the following value:\n\n* It is very simple to use\n* It requires no dependencies other than a POSIX shell (e.g. Busybox)\n* It integrates easily with CI/CD pipelines\n* It helps developers creating better systems\n\n## Usage\n\n```\nUSAGE:\n\tcukinia [options] [config file]\n\nOPTIONS:\n\t-h, --help\tdisplay this message\n\t-v, --version\tdisplay the version string\n\t-o \u003cfile\u003e\toutput results to \u003cfile\u003e\n\t-f \u003cformat\u003e\tset output format to \u003cformat\u003e, currently supported:\n       \t\t\tcsv junitxml\n\t--no-header\tdo not print headers in concerned output formats\n\t--trace         trace execution (for debugging)\n```\n\nBy default, Cukinia uses `/etc/cukinia.conf`.\n\n## Screenshot\n\nA sample screenshot when running Cukinia in interactive mode:\n\n![Screenshot](doc/screenshot.png)\n\n---\n\n# Cukinia Test Statements\n\nCukinia provides a set of simple statements for validating runtime aspects of a\nLinux system:\n\n## 👤 User \u0026 Group Management\n\nVerify the presence and configuration of system users and groups.\n\n- `cukinia_user \u003cname\u003e` → validate that a user exists\n- `cukinia_group \u003cname\u003e` → validate that a group exists\n- `cukinia_user_memberof \u003cuser\u003e \u003cgroup\u003e[ ...]` → validate that a user is member of one or more groups\n\n**Example:**\n```sh\ncukinia_user appuser\ncukinia_group dialout\ncukinia_user_memberof appuser dialout video\n```\n\n---\n\n## 🔧 Processes \u0026 Threads\n\n- `cukinia_kthread \u003cname\u003e` → validate that a kernel thread is running\n- `cukinia_process \u003cname\u003e [user]` → validate that a process is running (optional owner)\n- `cukinia_process_with_args \"\u003cargs\u003e\" [user]` → validate that a process with specific arguments is running (optional owner)\n\n**Examples**\n\n```sh\ncukinia_kthread kworker/0:1\ncukinia_process sshd\ncukinia_process_with_args \"gpsd --nodaemon\" appuser\n```\n\n---\n\n## 🐧 Kernel \u0026 System Configuration\n\n- `cukinia_cmdline \u003cparam\u003e[=\u003cvalue\u003e]` → validate that kernel cmdline contains a parameter (optional value)\n- `cukinia_kconf \u003csymbol\u003e \u003cy|m|n\u003e` → validate that a kernel config symbol has a given tristate value\n- `cukinia_kmod \u003cmodule\u003e` → validate that a kernel module is loaded\n- `cukinia_knoerror \u003cpriority\u003e` → validate that kernel boot has no important errors at or above the given log priority\n  - **Note:** This test requires `dmesg --level` support, which is not available in BusyBox. Install `dmesg` from `util-linux` to use it. Alternatively, use `cukinia_cmd` with custom grep patterns for more control over error detection.\n- `cukinia_kversion \u003cmaj.min\u003e` → validate kernel version (checks major.minor, e.g. 5.14)\n- `cukinia_sysctl \u003ckey\u003e \u003cvalue\u003e` → validate that a kernel sysctl parameter is set to value\n\n**Examples**\n\n```sh\ncukinia_cmdline console=ttyS0\ncukinia_kconf IPV6 y\ncukinia_kmod i2c_dev\ncukinia_kversion 6.6\ncukinia_sysctl net.ipv4.ip_forward 0\n```\n\n---\n\n## 🧩 Generic commands and test(1) expressions\n\nThe `cukinia_test` statement wraps the shell `test` command, allowing for\ngeneric tests, while `cukinia_cmd` allows for running arbitrary commands.\n\n- `cukinia_test \u003ctest(1) expression\u003e` → validate a generic `test` expression\n- `cukinia_cmd \u003ccommand...\u003e` → validate that an arbitrary command returns success\n\n**Examples**\n\n```sh\ncukinia_test -f /etc/os-release\n\nresult_string=$(some_command)\nas \"The remote sensor was detected\" \\\n    cukinia_test \"$result_string\" = \"Detected\"\n\nas \"The root user's password field is not empty\" \\\n    not cukinia_cmd \"grep -q ^root:: /etc/passwd\"\n```\n\n---\n\n## 💾 Filesystems \u0026 Paths\n\n- `cukinia_mount \u003cdevice\u003e \u003ctarget\u003e [fstype] [options]` → validate the presence of a mount\n- `cukinia_symlink \u003cpath\u003e \u003cexpected_target\u003e` → validate the target of a symlink\n\n**Examples**\n```sh\ncukinia_mount sysfs /sys\ncukinia_mount /dev/sda5 /mnt/maps ext4 ro\ncukinia_symlink /etc/alternatives/editor /usr/bin/vim\n```\n\n---\n\n## 🌐 Networking \u0026 Connectivity\n\n- `cukinia_dns_resolve \u003chostname\u003e` → validate that a hostname can be resolved\n- `cukinia_http_request \u003curl\u003e` → validate that an HTTP(S) request returns HTTP 200\n- `cukinia_listen4 \u003ctcp|udp\u003e \u003cport\u003e` → validate that a TCP/UDP v4 port is open locally\n- `cukinia_netif_has_ip \u003cifname\u003e [-4|-6] [flags]` → validate that an interface has IP configuration (examples below)\n- `cukinia_netif_is_up \u003cifname\u003e` → validate interface state is UP\n\n**Examples**\n\n```sh\ncukinia_dns_resolve example.org\ncukinia_http_request http://localhost:8080/health\ncukinia_listen4 tcp 22\ncukinia_netif_has_ip eth0 -4 dynamic\ncukinia_netif_has_ip eth0 -6 \"scope global\"\ncukinia_netif_is_up eth2\n```\n\n---\n\n## 🔌 Devices \u0026 Buses\n\n- `cukinia_gpio_libgpiod -i \u003cin_pins\u003e -l \u003cout_low\u003e -h \u003cout_high\u003e -g \u003cgpiochip\u003e` → validate GPIO via libgpiod\n- `cukinia_gpio_sysfs -i \u003cin_pins\u003e -l \u003cout_low\u003e -h \u003cout_high\u003e -g \u003cgpiochip\u003e` → validate GPIO via legacy sysfs\n- `cukinia_i2c \u003cbus\u003e [device_address] [driver_name]` → check I²C bus or (optional) device and (optionally) that it uses the indicated driver\n\n**Examples**\n\n```sh\ncukinia_gpio_libgpiod -i \"0 3 4\" -l \"10\" -h \"2 50\" -g gpiochip1\ncukinia_gpio_sysfs -i \"20 34\" -h \"3 99 55\"\n\nas \"Remote MCU is visible on I2C2 bus address 3c\" \\\n    cukinia_i2c 1 0x3c\n```\n\n---\n\n## 🟦 systemd units\n\n- `cukinia_systemd_unit \u003cunit\u003e` → validate that a systemd unit is active\n- `cukinia_systemd_failed` → fail if any systemd unit is in failed state\n\n**Examples**\n\n```sh\ncukinia_systemd_unit sshd.service\ncukinia_systemd_failed\n```\n\n---\n\n## 🐍 Python\n\n- `cukinia_python_pkg \u003cpackage\u003e` → validate that a Python package is installed\n\n**Examples**\n\n```sh\ncukinia_python_pkg requests\n```\n\n---\n\n## 🎛️ Modifiers (Prefixes)\n\nThese **prefix** any test statement and may be combined:\n\n- `as \"\u003cdescription\u003e\"` → change a test's textual description\n- `not` → invert the test result (appends `[!]` in the default description)\n- `test_id \"\u003cid\u003e\"` → set a test id for outputs (useful for mapping your system requirements)\n- `verbose` → preserve test stdout/stderr in the console output\n\n**Examples**\n\n```sh\nas \"Checking embedded webapp is up (with connect logs)\" \\\n  verbose \\\n  cukinia_http_request http://localhost:8080/sanitycheck\n\nnot cukinia_user baduser\n\ntest_id \"SWR_001\" \\\n    cukinia_systemd_unit sshd.service\n```\n\n---\n\n## 🔄 Conditions \u0026 Flow Control\n\nThese may also **prefix** any test statement, and can be combined:\n\n- `when \"\u003cshell_expr\u003e\"` … *test* → run test only if expression returns success\n- `unless \"\u003cshell_expr\u003e\"` … *test* → run test only if expression returns failure (reported as SKIP when not executed)\n- `on \u003cresult\u003e` → execute statements conditionally on test result (e.g., `on success`, `on failure`)\n  - `retry \u003ccount\u003e [after \u003cinterval\u003e]` → retry a test `count` times with optional interval (e.g., `2s`, `1m`, `3h`, `1d`)\n\n**Examples**\n\n```sh\non_eval_board() { grep -q EVK /sys/firmware/devicetree/base/model; }\non_arm64 { test \"$(uname -m)\" = \"aarch64\"; }\n\nwhen \"on_arm64 \u0026\u0026 !on_eval_board\" \\\n  as \"Custom LED controller was probed\"  \\\n    cukinia_test -d /sys/class/leds/customled\n\non failure retry 3 after 2s \\\n    cukinia_systemd_unit big-app.service\n```\n\n---\n\n## 🧰 Utility Statements\n\n- `cukinia_run_dir \u003cdir\u003e` → run all executables in given directory as individual tests\n- `cukinia_conf_include \u003cglob\u003e` → include additional config files, useful for splitting your tests into domain-specific files\n- `cukinia_log \"\u003cmessage\u003e\"` → log a message to stdout\n\n\n**Examples**\n\n```sh\ncukinia_conf_include /etc/cukinia/conf.d/*.conf\n\ncukinia_log \"Starting graphics tests, $cukinia_failures failures so far\"\ncukinia_run_dir /opt/gfx_tests/\n```\n\n---\n\n## 🪪 Logging Customization\n\nFor the console output:\n\n- `logging prefix \"string\"` → set prefix for following test results / cukinia_log outputs\n\nFor the JunitXML output:\n\n- `logging class \"string\"` → set JUnitXML class name for following tests\n- `logging suite \"string\"` → set JUnitXML test suite for following tests\n\n---\n\n## Useful Variables\n\n- `$cukinia_tests` → number of tests attempted\n- `$cukinia_failures` → number of tests that failed\n\n## Useful functions\n\nThose shell functions may be useful in your test suite:\n\n- `$(_colorize color_name string)\"` → colorize string argument for console display, useful with `cukinia_log` - Most colors have a '2' variant which is brighter\n- `$(_ver2int x.y.z)\"` → creates integer version of numeric version string, useful for comparing stuff with `cukinia_test`\n\n**Examples**\n\n```sh\ncukinia_log \"$(_colorize yellow2 \"Starting Graphics Test Suite\")\"\n\ncukinia_test $(_ver2int ${kernel_version}) -ge $(_ver2int 6.6.38)\n```\n\n## Environment variables\n\n- `$CUKINIA_ALWAYS_PASS` → if set, every test will succeed\n\n# Advanced Configuration\n\nA Cukinia config file is actually a POSIX shell script that is sourced by\ncukinia, so any shell logic can be used in a test file scenario.\n\nThis is useful for example to make certain groups of tests depend on preliminary\nchecks:\n\n```sh\nif cukinia_test -x /usr/bin/myapp; then\n\tcukinia_user myuser\n\tcukinia_process myapp myuser\n\tcukinia_http_request http://localhost:8080/testme\nelse\n\tcukinia_log \"$(_colorize red \"myapp not found :(\")\"\nfi\n\n```\n\n# Example cukinia.conf\n\n```sh\ncukinia_log \"----\u003e Base System Requirements \u003c----\"\n\ncukinia_user pipewire\ncukinia_group dialout\n\ntest_id SYS_033 \\\n    cukinia_process sshd\n\ntest_id SYS_049 \\\n    cukinia_kthread kaudit\n\n\ncukinia_log \"----\u003e Kernel Requirements \u003c----\"\n\ntest_id KRN_002 \\\n  as \"Linux kernel version is 6.12 for LTS support\" \\\n    cukinia_kversion 6.12\n\ntest_id KRN_044 \\\n    cukinia_kmod snd_usb_audio\n\n\ncukinia_log \"----\u003e Misc. tests \u003c----\"\n\ncukinia_test -f /etc/os-release\n\ntest_id SYS_037 \\\n  as \"/etc/os-release contains custom SYSVER= key\" \\\n    cukinia_cmd grep -q '^SYSVER=[0-9\\.]+$' /etc/os-release\n\n```\n\n# Development\n\nCukinia is validated using `bats`. The non-regression tests can be run with:\n\n```sh\ngit submodule update --init\nbats bats/cukinia.bats\n```\n\n# License\n\nCukinia is released under the Apache 2 license.\n\n`Copyright (C) 2017-2025 Savoir-faire Linux, Inc.`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsavoirfairelinux%2Fcukinia","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsavoirfairelinux%2Fcukinia","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsavoirfairelinux%2Fcukinia/lists"}