{"id":18564534,"url":"https://github.com/guardsquare/mocxx","last_synced_at":"2025-04-10T04:32:00.781Z","repository":{"id":39666905,"uuid":"305667675","full_name":"Guardsquare/mocxx","owner":"Guardsquare","description":"A versatile C++ function mocking framework.","archived":false,"fork":false,"pushed_at":"2022-10-17T11:17:03.000Z","size":45318,"stargazers_count":126,"open_issues_count":4,"forks_count":13,"subscribers_count":13,"default_branch":"main","last_synced_at":"2024-07-30T19:12:39.694Z","etag":null,"topics":["cpp","frida","instrumentation","mocking","testing"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Guardsquare.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}},"created_at":"2020-10-20T10:11:50.000Z","updated_at":"2024-07-11T11:07:53.000Z","dependencies_parsed_at":"2023-01-19T17:15:21.765Z","dependency_job_id":null,"html_url":"https://github.com/Guardsquare/mocxx","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Guardsquare%2Fmocxx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Guardsquare%2Fmocxx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Guardsquare%2Fmocxx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Guardsquare%2Fmocxx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Guardsquare","download_url":"https://codeload.github.com/Guardsquare/mocxx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223424337,"owners_count":17142744,"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":["cpp","frida","instrumentation","mocking","testing"],"created_at":"2024-11-06T22:15:35.158Z","updated_at":"2024-11-06T22:15:35.222Z","avatar_url":"https://github.com/Guardsquare.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- TODO: logo --\u003e\n\n# Mocxx\n\n[![license](https://img.shields.io/badge/License-GPL%203.0-blue.svg)](https://choosealicense.com/licenses/gpl-3.0/)\n![version](https://img.shields.io/badge/version-0.1.0-g)\n[![tests-macos-clang](https://github.com/Guardsquare/mocxx/workflows/MacOS%2FClang/badge.svg)](https://github.com/Guardsquare/mocxx/actions?query=workflow%3AMacOS%2FClang)\n[![tests-linux-clang](https://github.com/Guardsquare/mocxx/workflows/Linux%2FClang/badge.svg)](https://github.com/Guardsquare/mocxx/actions?query=workflow%3ALinux%2FClang)\n[![tests-linux-gcc](https://github.com/Guardsquare/mocxx/workflows/Linux%2FGCC/badge.svg)](https://github.com/Guardsquare/mocxx/actions?query=workflow%3ALinux%2FGCC)\n\nA versatile [C++](https://www.stroustrup.com/C++.html) function mocking\nframework. It replaces a target function with the provided implementation, and\nintegrates well with existing testing and mocking frameworks.\n\n[Article](https://tech.guardsquare.com/posts/mocxx-the-mocking-tool/) about how it works.\n\n## Features\n\nA few things that might catch your eye:\n\n- No macros\n- Virtually no source code modification\n- State-of-the-art code injection (thanks to [Frida](https://github.com/frida))\n- System functions mocking, for example **open**\n- Type safety\n- RAII friendly\n\n## Examples\n\nMocking [std::filesystem](https://en.cppreference.com/w/cpp/filesystem):\n```cpp\nMocxx mocxx;\n\n// Lambda is typed and allows to resolve function overload set\nmocxx.ReplaceOnce([\u0026](const std::filesystem::path\u0026 p) { return true; },\n                  std::filesystem::exists);\n\nstd::string file = \"/this/file/now/exists\";\n\n// Returns true\nstd::filesystem::exists(file);\n\n// Returns false, because of ReplaceOnce\nstd::filesystem::exists(file);\n```\n\nThis example could be a good start for implementing an in-process fake file\nsystem. We haven't tried yet. Dare to try?\n\nMocxx can also replace functions by name:\n\n```cpp\nmocxx.Replace([]() { return 0.0; }, \"atof\");\n\n// Always true\natof(\"1.0\") == 0.0;\n```\n\nReplacing member functions is also straightforward:\n\n```cpp\nstruct Name\n{\n  using SizeType = std::string::SizeType;\n\n  SizeType Size() const { return name.size(); }\n  SizeType Size() { return name.size(); }\n\n  std::string name;\n};\n\n// First argument to such lambdas is the type target (Name in this case) this\n// member function belongs. Type constness decides which overload to use.\nmocxx.ReplaceMember([](Name* foo) -\u003e Name::SizeType { return 13; }, \u0026Name::Size);\n```\n\nIn many cases you might want to simply replace the result:\n\n```cpp\nmocxx.Result(0, getpid);\n```\n\nMocxx of course works with more complex types than just integers:\n\n```cpp\nstd::optional\u003cstd:vector\u003cstd::string\u003e\u003e\nRepeat(const std::string\u0026 string, std::int32_t times)\n{\n  if (times == 0) {\n    return std::nullopt;\n  }\n\n  std::vector\u003cstd::string\u003e result;\n  while (times --\u003e 0) {\n    result.push_back(string);\n  }\n\n  return result;\n}\n\nmocxx.Result(std::nullopt, Repeat);\n\nmocxx.Result(std::vector\u003cstd::string\u003e(), Repeat);\n```\n\nKeep in mind that if you pass a named value, a local variable, function\narguments etc. you are passing it as a reference. If Mocxx leaves the context\nof this value you might end up with a runtime exception in the best case, and\nin the worst you might just see garbage being returned by the replacement.\n\n```cpp\nMocxx CreateFilesystemExistsMocks(bool result) {\n  Mocxx mocxx;\n  mocxx.Result(result, \n               (bool(*)(const std::filesystem::path\u0026))std::filesystem::exists);\n  mocxx.Result(result,\n               (bool(*)(const std::filesystem::path\u0026, std::error_code\u0026))std::filesystem::exists);\n  return mocxx;\n}\n\nauto mocxx = CreateFilesystemExistsMocks(true);\n\nif (std::filesystem::exists(\"/dev/null\")) {\n  // Whether control enters this block is undefined\n}\n```\n\nIn case you don't need the parameters passed to the replaced function, but\nstill want to generate a new value every call, you can pass a generator, which\nis a lambda with no arguments returning a result convertible to the result of\nthe replaced function:\n\n```cpp\nstd::random_device rd;\nstd::uniform_int_distribution\u003cint\u003e dist(0, 9);\n\nint notSoRandom = 0;\n\nmocxx.ReplaceGenerator([\u0026]() mutable { return (notSoRandom %= 9); },\n                       \u0026std::uniform_int_distribution::operator());\n```\n\n## Limitations\n\nAs all good things, this one does have some drawbacks. One major issue you\nmight encounter while using Mocxx is that your functions are not being\nreplaced, this is especially true for **system functions**. This section will\ngo over most common problems.\n\n### Optimisations\n\nC++ is a powerful language and it is usually packaged within a powerful\ncompiler that tries to optimise every bit of your code. The function you are\ntrying to replace might be **inlined**, or simply removed, because it is no\nlonger required for the program. This is especially evident in the very first\nexample this document provides. In order to successfully replace\n**std::filesystem** API you want to wrap its header inclusion with the following\npragma (clang only).\n\n```cpp\n#pragma clang optimize off\n#include \u003cfilesystem\u003e\n#pragma clang optimize on\n```\n### Overloading sets\n\nThe problem with overloading sets in C++ is that they are not first-class\ncitizens, you cannot bind an overloading set to a name, or pass it through a\nfunction call. Overloading sets are always resolved at call site. At the moment\nthere is no straightforward solution to this problem, and you have to provide\ntarget function type for lambdaless API.\n\n### Templates\n\nThe major issue with template functions is the fact that they are not actual\nfunctions. Mocxx is a runtime tool, it can only replace a function that has an\naddress. What this means is that you first need to instantiate the template\nfunction, and then pass its address to the tool.\n\n### Virtual methods\n\nThe nature of virtual methods (aka dynamic dispatch, or late method binding) in\nC++ makes it impossible to home in on the target member function using language\nmeans, which is what Mocxx requires. You can of course pass in the virtual\nmember function pointer, but it contains no information about the actual\nfunction. The support for virtual member functions can be added to Mocxx, but\nnot in a portable way. A possible solution is explained in the [article](TODO).\n\n### Forbidden Mocks\n\nThis tool makes use of some STL types, such as **std::variant**,\n**std::string**, etc. To save your sanity, suppress the urge to mock commonly\nused generic API.\n\n### Different Compilers\n\nReplacement by name works well for free functions, but not that well for member\nfunctions, in a sense that you would have to provide properly mangled name for\nyour specific compiler. This can be improved of course.\n\n## Future Work\n\nAfter writing this tool and using it for some time, we have identified the\nfollowing, as the potential extensions and improvements:\n\n- Ability to invoke the original function\n- Sanity checks, for instance, recursive invocations\n- Automatically mock the entire overload set\n- Virtual functions replacement\n- Template functions. All or some instantiations\n- Full type/namespace API mocking \n- Concurrent mocks\n\nYou are welcome to contribute.\n\n## Requirements\n\nThe base requirements to use Mocxx are quite humble:\n- A C++17-standard-compliant compiler\n- [Frida Gum](https://github.com/frida/frida-gum) (included for MacOS and Linux)\n- Catch2 for testing (included)\n- CMake for building\n\n## Testing\n\n```sh\ngit clone git@github.com:Guardsquare/mocxx.git; cd mocxx\nmkdir build; cd build\ncmake ../ -GNinja -DCMAKE_BUILD_TYPE=Debug\nninja\ntest/test_mocxx\n```\n\n## Installation\n\n### Cmake\n\nCopy `cmake/Mocxx.cmake` into your project `cmake` directory and add the\nfollowing in your test `CMakeLists.txt`:\n```cmake\n// Add ./cmake module folder if not already\nlist(APPEND CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/cmake\")\n\ninclude(Mocxx)\n\ntarget_link_libraries(\u003ctest_target\u003e PUBLIC Mocxx)\n```\n\n### Other\n\nTo install without cmake you need to add `Mocxx` includes and `Frida Gum`\nincludes and static library located in `vendor/frida/`. Make sure you are\ncompiling your tests with these flags:\n\n```\n-O0 -g -fno-lto -fno-inline-functions -fno-inline\n```\n\nAnd link with these:\n\n```\n-lresolv -lpthread -ldl\n```\n\n## Contributing\nPull requests are welcome. For major changes, please open an issue first to\ndiscuss what you would like to change.\n\nPlease make sure to update tests as appropriate.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguardsquare%2Fmocxx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguardsquare%2Fmocxx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguardsquare%2Fmocxx/lists"}