{"id":13793333,"url":"https://github.com/S-S-X/mineunit","last_synced_at":"2025-05-12T20:30:56.096Z","repository":{"id":38017425,"uuid":"314741580","full_name":"S-S-X/mineunit","owner":"S-S-X","description":"Luanti / Minetest integration and unit testing framework.","archived":false,"fork":false,"pushed_at":"2025-05-10T15:59:50.000Z","size":312,"stargazers_count":10,"open_issues_count":36,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-10T16:37:41.997Z","etag":null,"topics":["integration-testing","luanti","minetest","modding-tools","regression-testing","unit-test"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/mineunit/mineunit","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/S-S-X.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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-11-21T05:36:57.000Z","updated_at":"2025-05-08T15:35:09.000Z","dependencies_parsed_at":"2025-05-10T16:31:10.014Z","dependency_job_id":"1d3d6395-2ccf-41fb-956c-c8012624c3a0","html_url":"https://github.com/S-S-X/mineunit","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/S-S-X%2Fmineunit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/S-S-X%2Fmineunit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/S-S-X%2Fmineunit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/S-S-X%2Fmineunit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/S-S-X","download_url":"https://codeload.github.com/S-S-X/mineunit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253816674,"owners_count":21968864,"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":["integration-testing","luanti","minetest","modding-tools","regression-testing","unit-test"],"created_at":"2024-08-03T23:00:18.973Z","updated_at":"2025-05-12T20:30:56.077Z","avatar_url":"https://github.com/S-S-X.png","language":"Lua","readme":"# mineunit\n\nMinetest and Luanti core / engine libraries for regression tests.\n\n![](https://byob.yarr.is/S-S-X/mineunit/coverage)\n\nProbably will not currently work with Windows so unless you want to help fixing things use Linux or similar OS.\n\nGithub integration is available to automatically execute tests when new code is pushed: https://github.com/marketplace/actions/mineunit-runner\n\n### How to use mineunit\n\nRecommended way is docker, it keeps both Mineunit and mod code isolated.\nDocker images are also currenlty best way to get latest features.\nSee https://hub.docker.com/r/mineunit/mineunit for more information.\n\nYou can also install Mineunit from luarocks and create spec directory for tests:\n\n```bash\n$ luarocks install --server=https://luarocks.org/dev --local mineunit\n$ cd ~/.minetest/mods/my_minetest_mod\n$ mkdir spec\n```\n\n* Add tests by creating test files inside `spec` directory.\n* File names should match pattern `*_spec.lua`, for example `mymod_spec.lua`.\n* See examples below for possible spec file contents.\n\n### Install demo spec, alternative to above `mkdir spec`\n\nYou can install demo `spec` directory containing simple tests and showing some things you can do.\u003cbr\u003e\nTo install demo spec cd to your mod directory, there must be `init.lua` file and there cannot be existing `spec` directory.\n\n* Run command: `$ mineunit --demo`\n\n### Define world for tests\n\nWorld can be replaced by calling `world.layout` with table containing nodes, this will reset previously created world layout.\nYou can also add more nodes without resetting previously added world layout by calling `world.add_layout` instead of `world.layout`.\n```lua\nworld.layout({\n\t{{x=0,y=0,z=0}, \"default:cobble\"},\n\t{{x=0,y=1,z=0}, \"default:cobble\"},\n\t{{x=0,y=2,z=0}, \"default:cobble\"},\n\t{{x=0,y=3,z=0}, \"default:cobble\"},\n})\n```\nIndividual nodes can be added and removed with `world.set_node`:\n```lua\nworld.set_node({x=0,y=0,z=0}, {name=\"default:stone\", param2=0})\n```\nto remove node from world just set node to `nil`:\n```lua\nworld.set_node({x=0,y=0,z=0}, nil)\n```\nto remove everything from world:\n```lua\nworld.clear()\n```\n\nEngine functions are also available if you like, for example `core.set_node(pos, node)` and `core.remove_node(pos)`.\n\n### Using Minetest classes and methods\n\nAPI is not complete yet but issues are getting fixed and more functinoality have been added, create issue if you find problems\n* To set node metadata, simply call `minetest.get_meta(pos):set_string(\"hello\", \"world\")` just like you would do in your mod.\n* To create ItemStack, simply call `ItemStack(\"default:cobble 99\")` just like you would do in your mod.\n* Any other things similar way, just like you'd do it in Minetest mods.\n\n### Example mymod/spec/mymod_spec.lua file\n\nFollowing comes with a lot of useless stuff just to show how to use some mineunit functionality\n\n```lua\n-- Load and configure mineunit\nrequire(\"mineunit\")\n\n-- Load some mineunit modules\nmineunit(\"core\")\nmineunit(\"player\")\nmineunit(\"protection\")\nmineunit(\"default/functions\")\n\n-- Load some fixtures from spec/fixtures/nodes.lua for tests\n-- Skip this if your tests do not need fixtures\nfixture(\"nodes\")\n\n-- Load some mymod source files, you wanted to test these right?\n-- This will execute init.lua in current directory\nsourcefile(\"init\")\n\n-- Maybe we need actual world for test?\n-- If world is larger or reused by multiple test specs it might be good\n-- idea to put this into spec/fixtures/world.lua and load using fixture(\"world\")\nworld.layout({\n\t{{x=0,y=1,z=0}, \"mymod:special_dirt\"},\n\t{{x=0,y=0,z=0}, \"mymod:special_dirt\"},\n\t{{x=1,y=0,z=0}, \"mymod:special_dirt\"},\n\t{{x=0,y=0,z=1}, \"mymod:special_dirt\"},\n\t{{x=1,y=0,z=1}, \"mymod:special_dirt\"},\n})\n\n-- Protect some nodes, this will affect outcome of minetest.is_protected calls\nmineunit:protect({x=0,y=1,z=0}, \"Sam\")\n\n-- Create few players\nlocal player1 = Player(\"Sam\", {interact=1})\nlocal player2 = Player(\"SX\", {interact=1})\n\n-- Define tests for busted\ndescribe(\"My test world\", function()\n\n\tit(\"contains special_dirt\", function()\n\t\tlocal node = minetest.get_node({x=0,y=0,z=0})\n\t\tassert.not_nil(node)\n\t\tassert.equals(\"mymod:special_dirt\", node.name)\n\tend)\n\n\tit(\"allows Sam to dig dirt at y 1\", function()\n\t\tassert.equals(false, minetest.is_protected({x=0,y=1,z=0}, player1:get_player_name()))\n\tend)\n\n\tit(\"protects dirt at y 1 from SX\", function()\n\t\tassert.equals(true, minetest.is_protected({x=0,y=1,z=0}, player2:get_player_name()))\n\tend)\n\nend)\n```\n\n### Useful Mineunit API functions\n\nMineunit itself comes with some additional functionality to allow controlled test execution:\n\n#### Generic utility functions\n\n| Function                                | Description\n| --------------------------------------- | -------------------------------------------------------------------------------\n| `mineunit:config(key)`                  | Read Mineunit configuration values.\n| `mineunit:config_set(key, value)`       | Temporarily change Mineunit configuration values.\n| `mineunit:has_module(name)`             | Tell if Mineunit module has been loaded.\n| `mineunit:set_modpath(name, path)`      | Set modpath for named mod, `core.get_modpath(name)` will then report this path.\n| `mineunit:set_current_modname(name)`    | Temporarily switch current mod name to another.\n| `mineunit:restore_current_modname()`    | Restore original modname after changing it using `mineunit:set_current_modname(name)`.\n| `mineunit:protect(pos, name_or_player)` | Add position to protection list to simlate protection without loading protection mods.\n| `mineunit:get_entities()`               | Get entities added with `core.add_entity(pos, name, staticdata)`.\n| `mineunit:get_players()`                | Get all registered players, when auth module is lodaded also returns offline players.\n\nEspecially `mineunit:set_modpath(name, path)`, `mineunit:set_current_modname(name)` and\n`mineunit:restore_current_modname()` will come handy in case you need to load multiple mods for tests.\nTechnic Plus for example uses following to load `technic_worldgen` along with main mod for testing:\n\n```lua\nmineunit:set_modpath(\"technic_worldgen\", \"../technic_worldgen\")\nmineunit:set_current_modname(\"technic_worldgen\")\nsourcefile(\"../technic_worldgen/init\")\nmineunit:restore_current_modname()\n```\n\nOr for example while registering tools/nodes/etc. for test and fixtures following snippet might make things simpler:\n\n```lua\nlocal function do_register_things_for_mod(modname, callback)\n\tmineunit:set_modpath(modname, \"spec/fixtures\")\n\tmineunit:set_current_modname(modname)\n\tcallback()\n\tmineunit:restore_current_modname()\nend\n```\n\n#### Engine event simulation\n\n| Function                                                              | Description\n| --------------------------------------------------------------------- | ----------------------------------------------\n| `mineunit:execute_entitystep(dtime, filter)`                          | Execute engine entitystep.\n| `mineunit:execute_globalstep(dtime)`                                  | Execute engine globalstep: will trigger registered globalsteps, nodetimers, core.after and similar callbacks.\n| `mineunit:execute_modchannel_message(channel, sender, message)`       | Modchannel message handlers.\n| `mineunit:execute_modchannel_signal(channel, signal)`                 | Modchannel message handlers.\n| `mineunit:execute_on_chat_message(sender, message)`                   | Simulate `Player` sending chat message.\n| `mineunit:execute_on_joinplayer(player, options)`                     | Simulate `Player` joining the game. Use `options` table for details like `address` and `lastlogin`.\n| `mineunit:execute_on_leaveplayer(player, timeout)`                    | Simulate `Player` leaving the game.\n| `mineunit:execute_on_player_receive_fields(player, formname, fields)` | Simulate `Player` sending form fields.\n| `mineunit:execute_shutdown()`                                         | Simulate server shutdown event.\n| `mineunit:mods_loaded()`                                              | Execute functions registered with `core.register_on_mods_loaded(func)`.\n\nWith many mods it is good to run through `mineunit:mods_loaded()` and `mineunit:execute_globalstep(dtime)` either during\ntest set loading or soon after to make sure that all initial registrations, globalstep and timer hacks get settled.\n\nFor tests that depend oon players it can be useful to register `before_each` and `after_each` with calls to\n`mineunit:execute_on_joinplayer(player, options)` and `mineunit:execute_on_leaveplayer(player, timeout)`.\n\n#### Debug, info, warning and error formatting / output\n\n| Function                         | Description\n| -------------------------------- | -----------------------------------------------------------------------------------\n| `print(...)`                     | Same as `mineunit:print(...)`. Use `io` module if you want to get around this.\n| `printf(...)`                    | Sorry, this function does not exist.\n| `mineunit:debug(...)`            | Prints to console if `verbose` option is higher than 3.\n| `mineunit:info(...)`             | Prints to console if `verbose` option is higher than 2.\n| `mineunit:warning(...)`          | Prints to console if `verbose` option is higher than 1.\n| `mineunit:error(...)`            | Prints to console if `verbose` option is higher than 0.\n| `mineunit:print(...)`            | Prints to console if `print` option is enabled.\n| `mineunit:debugf(fmtstr, ...)`   | Like `debug` but with format string. Based on custom `string.format`, details below.\n| `mineunit:infof(fmtstr, ...)`    | Like `info` but with format string. Based on custom `string.format`, details below.\n| `mineunit:warningf(fmtstr, ...)` | Like `warning` but with format string. Based on custom `string.format`, details below.\n| `mineunit:errorf(fmtstr, ...)`   | Like `error` but with format string. Based on custom `string.format`, details below.\n| `mineunit:printf(fmtstr, ...)`   | Like `print` but with format string. Based on custom `string.format`, details below.\n\nFormat strings for above `*f` functions accept default Lua format strings with few exceptions.\nString formatter `%s` can accept any argument type and will do special formatting for some common\ndata such as coordinates, pointed_thing and such. This holds for both Lua 5.1 and LuaJIT.\nAdditional `%t` formatter simply uses `dump` for everything. Besides that, same as Lua `string.format`.\n\n#### Mostly internal / questionable / possibly unstable utility functions\n\n| Function                                           | Description\n| -------------------------------------------------- | -----------------------------------------------------------------\n| `mineunit:destroy_nodetimer(pos)`                  | Use core/engine counterpart instead.\n| `mineunit:get_current_modname()`                   | Use core/engine counterpart instead.\n| `mineunit:get_worldpath()`                         | Use core/engine counterpart instead.\n| `mineunit:get_modpath(name)`                       | Use core/engine counterpart instead.\n| `mineunit:register_on_mods_loaded(func)`           | Use core/engine counterpart instead.\n| `mineunit:apply_default_settings(settings)`        | Probably bad idea, possibly unstable.\n| `mineunit:get_InvRef_data(thing)`                  | Direct access to invetory data `{ lists = {...}, sizes = {...}, empty = {...} }`.\n| `mineunit:clear_InvRef(thing)`                     | Unstable, for now internal testing.\n| `mineunit.deep_merge(data, target, defaults)`      | Internal use, might be removed in future.\n| `mineunit:DEPRECATED(msg)`                         | Internal use, might be removed in future.\n| `mineunit.export_object(obj, def)`                 | Internal use, possibly unstable.\n| `mineunit:prepend_flush()`                         | Internal use, likely removed in future.\n| `mineunit:prepend_print(s)`                        | Internal use, might be removed in future.\n| `mineunit.registered_craft_recipe(output, method)` | Internal use, likely removed in future.\n| `mineunit:set_timeofday(d)`                        | Set current time of day.\n\nMineunit modules will add some functionality like some simple player actions simulation and such.\n\n### Mineunit modules\n\nUnfortunately these lists are incomplete and in reality there's actually many more modules available.\nAnyway, these might help a bit. And taking a look at other mods utilizing mineunit could give some ideas too.\n\n#### core module\n\n`mineunit(\"core\")` will load multiple modules and setups basic environment for simple tests, modules that will be loaded automatically with `core` module:\n\n| Module name         | Description\n| ------------------- | -------------------\n| world               | Provides `world` namespace to allow node manipulation in test world.\n| settings            | Provides `Settings` class. `core` module will also load engine configuration file from fixtures directory if present.\n| metadata            | Provides metadata and inventory manipulation for tests.\n| itemstack           | Provides `ItemStack` class.\n| game/constants      | Engine library.\n| game/item           | Engine library.\n| game/misc           | Engine library.\n| game/register       | Engine library.\n| game/privileges     | Engine library.\n| common/misc_helpers | Engine library.\n| common/vector       | Engine library.\n| common/serialize    | Engine library.\n| common/fs           | Engine library.\n\nIt is recommended to always load `core` module instead of selecting individual automatically loaded modules.\n\n#### Additional modules\n\n| Module name         | Description\n| ------------------- | -------------------\n| http                | Provides functionality for testing mods that request HTTP API using `core.request_http_api()`.\n| nodetimer           | Provides nodetimer functionality.\n| player              | Provides `Player` class, privilege functions and formspec functions. Loads `metadata` as dependency.\n| protection          | Provides simple node protection API to simulate `core.is_protected(pos)` behavior.\n| server              | Provides functionality for globalstep, player, modchannel and chat. Loads `nodetimer`, `common/chatcommands` and `game/chat` as dependencies.\n| voxelmanip          | Provides `VoxelManip` class.\n| auth                | Provides authentication API.\n| entity              | Provides SAO entity API.\n| common/chatcommands | Engine library.\n| game/chat           | Engine library.\n| assert              | Provides custom assertions, see assertions section or `--help-assert` command line argument.\n\n### Command line arguments\n\n```\nMineunit v0.14.0 (Lua 5.1)\nUsage:\n\tmineunit [-c|--coverage] [-v|--verbose] [-q|--quiet] [-x|--exclude \u003cpattern\u003e]\n\t\t[--engine-version \u003cversion\u003e] [--fetch-core \u003cversion\u003e] [--core-root \u003cpath\u003e]\n\nOptions:\n\t-c, --coverage  Execute luacov test coverage analysis.\n\t-r, --report    Build report after successful coverage analysis.\n\t                Currently cannot be combined with --coverage\n\t-p|--pattern \u003cpattern\u003e\n\t                Only run test files matching the Lua pattern (default: _spec).\n\t                Can be repeated for multiple patterns.\n\t-F|--filter \u003cpattern\u003e\n\t                Only run test names matching the Lua pattern.\n\t                Can be repeated for multiple patterns.\n\t-x|--exclude \u003cpattern\u003e\n\t                Exclude source file path patterns from test coverage analysis.\n\t                Can be repeated for multiple patterns.\n\n\t--demo          Install demo tests to current directory.\n\t                Good way to learn Mineunit or initialize tests for project.\n\n\t--core-root \u003cpath\u003e\n\t                Root directory for core libraries, defaults to install path.\n\t--engine-version \u003ctag\u003e\n\t                Use core engine libraries from git tag version.\n\t--fetch-core \u003ctag\u003e\n\t                Download core engine libraries for tag.\n\t                This is simple wrapper around `git clone`.\n\n\t-v|--verbose    Be more verbose by printing more useless crap to console.\n\t                Can be repeated up to six times for even more annoying output.\n\t-q|--quiet      Be quiet, most of time keeps your console fairly clean.\n\t                Always disables regular Lua print which can make output\n\t                somewhat less annoying when combined with --verbose output.\n\t-V|--version    Display Lua and Mineunit version information.\n\t-h|--help       Display this cheat sheet.\n\t--help-assert   Display another cheat sheet, reference for special assertions.\n\nResources:\n\tLuarocks package: https://luarocks.org/modules/S-S-X/mineunit\n\tIssue tracker: https://github.com/S-S-X/mineunit/issues\n\tGitHub integration: https://github.com/marketplace/actions/mineunit-runner\n\tDocker images: https://hub.docker.com/r/mineunit/mineunit\n\nConfiguration files (in order):\n\t/etc/mineunit/mineunit.conf\n\t$HOME/.mineunit.conf\n\t$HOME/.mineunit/mineunit.conf\n\t./spec/mineunit.conf\n\nLicense:\n\tMIT Expat with LGPL libraries, see LICENSE file for details.\n```\n\nConfiguration files are checked and merged in order and last configuration entry will take effect.\nFor example core_root in project configuration will override core_root in user configuration.\n\nCommand line arguments will override all configuration file entries except for luacov excludes which will be merged.\nTable values (other than luacov excludes) are only supported in project configuration file.\n\n### Using `--coverage` and `--report` cli args\n\nUsing `--coverage` and `--report` together wont work, with `--report` cli argument mineunit\nwont run any tests but just instructs luacov to format coverage data producing human readable\ntest coverage report called `luacov.report.out`. File is placed in current working directory.\nWith LuaJIT, reports generated by luacov will be broken.\nRun `mineunit -V` to check used Lua version and make sure it is Lua 5.1 instead of LuaJIT.\n\nSo basically to get code coverage report you have to run `mineunit` twice, example follows:\n\n```\n$ mineunit --coverage\n$ mineunit --report\n```\n\nFirst command executes tests, collects test coverage information and produces coverage data file.\nSecond command does not execute tests but reads coverage data file and formats it with source\ncode to produce human readable test coverage report file called `luacov.report.out`. Use any\ntext editor to read this file.\n\n### Known issues\n\nCode coverage hits and misses are very likely miscalculated if using LuaJIT, let me know in\ncase this gets fixed in luacov. Some preprocessing and filtering could make this a little\nbit better but for now not my highest priority.\n\nMineunit with LuaJIT can be unpredictable.\nWhile slower, Lua 5.1 will bring better predictability and stability.\n\nGenerally Lua 5.1 is recommended.\n\n### Known projects using mineunit\n\nSee following projects for more examples on how to use mineunit and what you can do with it\n\n#### Technic Plus: large test sets for networks, tools, machines, nodes, custom placement and more.\n* Technic tests https://github.com/mt-mods/technic/tree/master/technic/spec\n* CNC tests https://github.com/mt-mods/technic/tree/master/technic_cnc/spec\n* Technic chests https://github.com/mt-mods/technic/tree/master/technic_chests/spec\n* GitHub workflow https://github.com/mt-mods/technic/blob/master/.github/workflows/mineunit.yml\n\n#### Metatool: complex multi mod test setup. Mineunit development began here.\n* Metatool API tests https://github.com/S-S-X/metatool/tree/master/metatool/spec\n* Containertool tests https://github.com/S-S-X/metatool/tree/master/containertool/spec\n* Sharetool tests https://github.com/S-S-X/metatool/tree/master/sharetool/spec\n* Tubetool tests https://github.com/S-S-X/metatool/tree/master/tubetool/spec\n* GitHub workflow https://github.com/S-S-X/metatool/blob/master/.github/workflows/mineunit.yml\n\n#### Other mods\n* Advtrains (few simple tests for wagon registration) https://git.bananach.space/advtrains.git/tree/advtrains/spec\n* Beerchat (chat commands and message delivery) https://github.com/minetest-beerchat/beerchat/tree/master/spec\n* Digistuff (very simple, only load mod) https://github.com/mt-mods/digistuff/tree/master/spec\n* Geoip (HTTP API) https://github.com/mt-mods/geoip/tree/master/spec\n* Machine_parts https://github.com/mt-mods/machine_parts/tree/master/spec\n* Mesecons (a bit different way to run mineunit) https://github.com/minetest-mods/mesecons/tree/master/mesecons/spec\n* QoS (basic unit testing and chat commands) https://github.com/S-S-X/qos/tree/master/spec\n* spectator_mode (player API and chat) https://github.com/minetest-mods/spectator_mode/tree/master/spec\n","funding_links":[],"categories":["Modding"],"sub_categories":["Testing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FS-S-X%2Fmineunit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FS-S-X%2Fmineunit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FS-S-X%2Fmineunit/lists"}