diff options
Diffstat (limited to 'tools/interface_generator')
27 files changed, 763 insertions, 0 deletions
diff --git a/tools/interface_generator/.bazelrc b/tools/interface_generator/.bazelrc new file mode 100644 index 0000000..71d2e60 --- /dev/null +++ b/tools/interface_generator/.bazelrc @@ -0,0 +1,2 @@ +build --cxxopt=-std=c++20 + diff --git a/tools/interface_generator/BUILD.bazel b/tools/interface_generator/BUILD.bazel new file mode 100644 index 0000000..8c4bde6 --- /dev/null +++ b/tools/interface_generator/BUILD.bazel @@ -0,0 +1,13 @@ +load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +package(default_visibility = ["//visibility:public"]) + +# This rule adds a convenient way to update the requirements file. +compile_pip_requirements( + name = "requirements", + src = "requirements.txt", + requirements_txt = "requirements_lock.txt", +) + +# make license available to test package +exports_files(["LICENSE.md"]) diff --git a/tools/interface_generator/LICENSE.md b/tools/interface_generator/LICENSE.md new file mode 100644 index 0000000..ddb4966 --- /dev/null +++ b/tools/interface_generator/LICENSE.md @@ -0,0 +1,16 @@ +Copyright (c) 2024 aqua@iserlohn-fortress.net + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA + OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + +SPDX License Identifier: [ISC](https://spdx.org/licenses/ISC.html) diff --git a/tools/interface_generator/MODULE.bazel b/tools/interface_generator/MODULE.bazel new file mode 100644 index 0000000..53b2ba4 --- /dev/null +++ b/tools/interface_generator/MODULE.bazel @@ -0,0 +1,20 @@ +############################################################################### +# Bazel now uses Bzlmod by default to manage external dependencies. +# Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel. +# +# For more details, please check https://github.com/bazelbuild/bazel/issues/18958 +############################################################################### + +module(name = "interface_generator", version = "0.1.0") + +# external dependencies +bazel_dep(name = "googletest", version = "1.15.2") +bazel_dep(name = "rules_python", version = "0.34.0") + +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + hub_name = "pip", + python_version = "3.11", + requirements_lock = "//:requirements_lock.txt", +) +use_repo(pip, "pip") diff --git a/tools/interface_generator/bin/BUILD.bazel b/tools/interface_generator/bin/BUILD.bazel new file mode 100644 index 0000000..04843b9 --- /dev/null +++ b/tools/interface_generator/bin/BUILD.bazel @@ -0,0 +1,45 @@ +load("@pip//:requirements.bzl", "requirement") +load("//private:defs.bzl", "py_pytest") + +""" interface declaration """ + +py_library( + name = "interface_declaration", + srcs = ["interface_declaration.py"], +) + +""" templates """ + +py_library( + name = "templates", + srcs = ["templates.py"], + data = glob(["templates/*"]), +) + +""" interface_generator """ + +py_binary( + name = "interface_generator", + srcs = [ + "interface_generator.py", + "templates.py", + ], + imports = ["."], + visibility = ["//visibility:public"], + deps = [ + ":interface_declaration", + ":templates", + requirement("mako"), + ], +) + +""" pytest """ + +py_pytest( + name = "pytest", + srcs = glob(["*.py"]), + data = glob(["templates/*"]), + deps = [ + requirement("mako"), + ], +) diff --git a/tools/interface_generator/bin/interface_declaration.py b/tools/interface_generator/bin/interface_declaration.py new file mode 100644 index 0000000..4560bbd --- /dev/null +++ b/tools/interface_generator/bin/interface_declaration.py @@ -0,0 +1,58 @@ +""" +interface_declaration.py +""" + +from dataclasses import dataclass, asdict +from pathlib import Path + + +@dataclass +class InterfaceDeclaration: + """interface declaration class""" + + name: str + license_hdr: str + system_includes: list[str] + types: list[dict] + functions: list[dict] + + def read_license(self, path: Path): + """read and starrify a license""" + if path is None: + self.license_hdr = "" + return + + with open(path, encoding="utf-8") as license_file: + self.license_hdr = "".join( + [ + f" * { line.rstrip().ljust(72)[:72] } * \n" + for line in license_file.readlines() + ] + ).rstrip() + + def __init__(self, name: str, license_path: Path): + self.name = name if name is not None else "kstdio" + self.read_license(license_path) + self.system_includes = ["stdarg.h"] + self.types = [ + { + "name": "File", + "members": [ + "int fd", + "int (*putc)(const struct kstdio_File*, const char)", + "int (*puts)(const struct kstdio_File*, const char*)", + ], + }, + ] + self.functions = [ + { + "name": "printf", + "return": "int", + "arguments": ["const char* format"], + "argument_names": ["format"], + }, + ] + + def into_dict(self) -> dict: + """create a dictionary for use in mako""" + return asdict(self) diff --git a/tools/interface_generator/bin/interface_declaration_unittest.py b/tools/interface_generator/bin/interface_declaration_unittest.py new file mode 100644 index 0000000..b2ad80c --- /dev/null +++ b/tools/interface_generator/bin/interface_declaration_unittest.py @@ -0,0 +1,19 @@ +""" +interface declaration unit tests +""" + +import unittest +from interface_declaration import InterfaceDeclaration + + +class InterfaceDeclarationUnittest(unittest.TestCase): + """interface_declaration unit tests""" + + def test_interfacedeclaration_class_asdict(self): + """test mock interface""" + interface = InterfaceDeclaration("kstdio", None) + interface_dict = interface.into_dict() + + self.assertEqual(interface.name, "kstdio") + self.assertEqual(len(interface.license_hdr), 0) + self.assertEqual(interface_dict["name"], "kstdio") diff --git a/tools/interface_generator/bin/interface_generator.py b/tools/interface_generator/bin/interface_generator.py new file mode 100755 index 0000000..eab93d7 --- /dev/null +++ b/tools/interface_generator/bin/interface_generator.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 + +""" +interface_generator.py +""" + +from argparse import ArgumentParser +from pathlib import Path +import sys +from mako.lookup import TemplateLookup +from interface_declaration import InterfaceDeclaration +from templates import get_templates, get_templates_dir + +PROG = { + "name": "interface_generator", + "version": "0.1", +} + + +def generate_file( + template: Path, templates: Path, output, interface: InterfaceDeclaration +): + """generate file using a tempalte and write it to the output location""" + lookup = TemplateLookup(directories=[".", templates.absolute()]) + mako_template = lookup.get_template(str(template.relative_to(templates))) + output_name = template.stem.replace("interface", interface.name) + print(f"{interface.name} via {template.name} => {output_name}") + + result = mako_template.render(**interface.into_dict(), PROG=PROG) + + if isinstance(output, Path): + # print(f"writing to {(output / output_name).absolute()}") + with open(output / output_name, "w", encoding="utf-8") as output_file: + print(result, file=output_file) + else: + print(result, file=output) + + +def main(): + """main function""" + parser = ArgumentParser( + prog="interface_generator", + description="Generate C header and mock files from an interface declaration", + ) + parser.add_argument( + "-i", + "--interface", + type=Path, + # required=True, + help="path to interface file", + ) + parser.add_argument( + "-t", + "--templates", + type=Path, + default=get_templates_dir(), + help="templates location", + ) + parser.add_argument( + "-l", + "--license", + type=Path, + required=True, + help="path to license file", + ) + parser.add_argument( + "-o", + "--output", + type=Path, + default=sys.stdout, + help="path to output, stdout by default", + ) + + args = parser.parse_args() + # print(args) + + interface = InterfaceDeclaration(args.interface, args.license) + # print(interface) + + for template in get_templates(args.templates): + # print(template) + generate_file(template, args.templates, args.output, interface) + + +if __name__ == "__main__": + main() diff --git a/tools/interface_generator/bin/templates.py b/tools/interface_generator/bin/templates.py new file mode 100644 index 0000000..9f7539e --- /dev/null +++ b/tools/interface_generator/bin/templates.py @@ -0,0 +1,19 @@ +"""template helper functions""" + +from pathlib import Path +import re + + +TEMPLATE_PATTERN = re.compile(r"^[^_]\S+\.mako$") + + +def get_templates_dir(name="templates") -> Path: + """get the templates directory""" + return Path(__file__).parent / name + + +def get_templates(path: Path) -> list[Path]: + """list templates in given path""" + result = list(path.glob("*.mako")) + result = [item for item in result if TEMPLATE_PATTERN.match(item.name)] + return result diff --git a/tools/interface_generator/bin/templates/__c_functions.mako b/tools/interface_generator/bin/templates/__c_functions.mako new file mode 100644 index 0000000..2e40119 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_functions.mako @@ -0,0 +1,4 @@ +/* Functions */ +% for fn in functions: +${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }); +% endfor diff --git a/tools/interface_generator/bin/templates/__c_system_include.mako b/tools/interface_generator/bin/templates/__c_system_include.mako new file mode 100644 index 0000000..d6a9d09 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_system_include.mako @@ -0,0 +1,4 @@ +/* System includes */ +% for path in system_includes: +#include <${path}> +% endfor diff --git a/tools/interface_generator/bin/templates/__c_types.mako b/tools/interface_generator/bin/templates/__c_types.mako new file mode 100644 index 0000000..ce6b6b5 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_types.mako @@ -0,0 +1,6 @@ +/* Types */ +% for type in types: +typedef struct ${name}_${type['name']} { +${ "\n".join([ " {};".format(member) for member in type['members'] ]) } +} ${name}_${type['name']}; +% endfor diff --git a/tools/interface_generator/bin/templates/__header.mako b/tools/interface_generator/bin/templates/__header.mako new file mode 100644 index 0000000..43ce1e7 --- /dev/null +++ b/tools/interface_generator/bin/templates/__header.mako @@ -0,0 +1,5 @@ +/* This file is generated by ${PROG['name']} v${PROG['version']} */ + +/****************************************************************************** +${ license_hdr } + ******************************************************************************/ diff --git a/tools/interface_generator/bin/templates/interface.h.mako b/tools/interface_generator/bin/templates/interface.h.mako new file mode 100644 index 0000000..47ea940 --- /dev/null +++ b/tools/interface_generator/bin/templates/interface.h.mako @@ -0,0 +1,8 @@ +<%include file="__header.mako" /> +#ifndef ${ name.upper() } +#define ${ name.upper() } + +<%include file="__c_system_include.mako" /> +<%include file="__c_types.mako" /> +<%include file="__c_functions.mako" /> +#endif /* ${ name.upper() } */ diff --git a/tools/interface_generator/bin/templates/interface_mock.cpp.mako b/tools/interface_generator/bin/templates/interface_mock.cpp.mako new file mode 100644 index 0000000..721f2c4 --- /dev/null +++ b/tools/interface_generator/bin/templates/interface_mock.cpp.mako @@ -0,0 +1,32 @@ +<%include file="__header.mako" /> + +#include <stdexcept> +#include "${name}_mock.hpp" + +static I${name}_mock *s_instance = nullptr; + +I${name}_mock::I${name}_mock() +{ + if(s_instance != nullptr) + { + throw std::runtime_error("Creating a second instance of mock object"); + } + s_instance = this; +} + +I${name}_mock::~I${name}_mock() +{ + // destructors shouldn't throw exceptions + s_instance = nullptr; +} + +% for fn in functions: +${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }) +{ + if(s_instance == nullptr) + { + throw std::runtime_error("No mock created to handle function ${name}_${fn['name']}"); + } + return s_instance->${fn['name']}(${ ", ".join(fn['argument_names']) }); +} +% endfor diff --git a/tools/interface_generator/bin/templates/interface_mock.hpp.mako b/tools/interface_generator/bin/templates/interface_mock.hpp.mako new file mode 100644 index 0000000..e33d50e --- /dev/null +++ b/tools/interface_generator/bin/templates/interface_mock.hpp.mako @@ -0,0 +1,26 @@ +<%include file="__header.mako" /> +#ifndef ${ name.upper() }_MOCK +#define ${ name.upper() }_MOCK + +extern "C" { +#include "${ name }.h" +} + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +class I${name}_mock +{ +public: + I${name}_mock(); + ~I${name}_mock(); + + /* Functions */ +% for fn in functions: + MOCK_METHOD(${fn['return']}, ${fn['name']}, (${ ", ".join(fn['arguments']) })); +% endfor +}; + +using ${name}_mock = ::testing::NiceMock<I${name}_mock>; + +#endif /* ${ name.upper() }_MOCK */ diff --git a/tools/interface_generator/bin/templates_unittest.py b/tools/interface_generator/bin/templates_unittest.py new file mode 100644 index 0000000..acb1685 --- /dev/null +++ b/tools/interface_generator/bin/templates_unittest.py @@ -0,0 +1,30 @@ +""" +templates unit tests +""" + +import unittest +from pathlib import Path +import templates + + +class TemplatesUnittest(unittest.TestCase): + """templates unit tests""" + + def test_get_templates_dir_is_valid(self): + """verify that default templates dir is valid""" + path = templates.get_templates_dir() + + self.assertTrue(isinstance(path, Path)) + self.assertTrue(path.exists()) + self.assertTrue(path.is_dir()) + + def test_get_templates(self): + """verify that templates are available by default""" + path = templates.get_templates_dir() + result = templates.get_templates(path) + + self.assertGreater(len(result), 0) + for template in result: + self.assertTrue(isinstance(template, Path)) + self.assertTrue(template.exists()) + self.assertTrue(template.is_file()) diff --git a/tools/interface_generator/defs.bzl b/tools/interface_generator/defs.bzl new file mode 100644 index 0000000..238588f --- /dev/null +++ b/tools/interface_generator/defs.bzl @@ -0,0 +1,53 @@ +def _generate_interface_impl(ctx): + out_hdrs = [ + ctx.actions.declare_file(ctx.attr.interface + ".h"), + ctx.actions.declare_file(ctx.attr.interface + "_mock.hpp"), + ] + out_srcs = [ + ctx.actions.declare_file(ctx.attr.interface + "_mock.cpp"), + ] + args = ["-l", ctx.file.license.path, "-o", out_hdrs[0].dirname] + + ctx.actions.run( + tools = ctx.attr._interface_generator_tool[DefaultInfo].data_runfiles.files, + inputs = [ctx.file.license], + outputs = out_srcs + out_hdrs, + executable = ctx.executable._interface_generator_tool, + arguments = args, + mnemonic = "GenerateInterface", + ) + + compilation_ctx = cc_common.create_compilation_context( + headers = depset(out_hdrs), + includes = depset([out_hdrs[0].dirname]), + ) + + return [ + DefaultInfo(files = depset(out_srcs)), + CcInfo(compilation_context = compilation_ctx), + ] + +generate_interface_rule = rule( + implementation = _generate_interface_impl, + attrs = { + "interface": attr.string(), + "license": attr.label(allow_single_file = True), + "_interface_generator_tool": attr.label( + executable = True, + cfg = "exec", + allow_files = True, + providers = [DefaultInfo], + default = Label("//bin:interface_generator"), + ), + "outs": attr.output_list(), + }, +) + +def generate_interface(name, interface, license, visibility = None): + generate_interface_rule( + name = name, + interface = interface, + outs = [name + "_mock.cpp"], + license = license, + visibility = visibility, + ) diff --git a/tools/interface_generator/private/BUILD.bazel b/tools/interface_generator/private/BUILD.bazel new file mode 100644 index 0000000..3012bcb --- /dev/null +++ b/tools/interface_generator/private/BUILD.bazel @@ -0,0 +1,9 @@ +""" targets internal to the tool and rule """ + +package(default_visibility = ["//:__pkg__"]) + +exports_files([ + "mypyrc", + "pylintrc", + "pytest_wrapper.py", +]) diff --git a/tools/interface_generator/private/defs.bzl b/tools/interface_generator/private/defs.bzl new file mode 100644 index 0000000..8e2ae8d --- /dev/null +++ b/tools/interface_generator/private/defs.bzl @@ -0,0 +1,29 @@ +load("@pip//:requirements.bzl", "requirement") + +def py_pytest(name, srcs, deps = [], data = [], **kwargs): + native.py_test( + name = name, + srcs = ["//private:pytest_wrapper.py"], + main = "//private:pytest_wrapper.py", + legacy_create_init = False, + imports = ["."], + args = [ + "--capture=no", + "--black", + "--pylint", + "--pylint-rcfile=$(location //private:pylintrc)", + "--mypy", + "--mypy-config-file=$(location //private:mypyrc)", + ] + ["$(location :%s)" % x for x in srcs], + deps = [ + requirement("pytest"), + requirement("pytest-black"), + requirement("pytest-pylint"), + requirement("pytest-mypy"), + ] + deps, + data = [ + "//private:mypyrc", + "//private:pylintrc", + ] + srcs + data, + **kwargs + ) diff --git a/tools/interface_generator/private/mypyrc b/tools/interface_generator/private/mypyrc new file mode 100644 index 0000000..d787271 --- /dev/null +++ b/tools/interface_generator/private/mypyrc @@ -0,0 +1,2 @@ +[mypy] +disable_error_code = import-untyped diff --git a/tools/interface_generator/private/pylintrc b/tools/interface_generator/private/pylintrc new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tools/interface_generator/private/pylintrc diff --git a/tools/interface_generator/private/pytest_wrapper.py b/tools/interface_generator/private/pytest_wrapper.py new file mode 100755 index 0000000..b4def3b --- /dev/null +++ b/tools/interface_generator/private/pytest_wrapper.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +import sys +import pytest + +if __name__ == "__main__": + sys.exit(pytest.main(sys.argv[1:])) diff --git a/tools/interface_generator/requirements.txt b/tools/interface_generator/requirements.txt new file mode 100644 index 0000000..c285e04 --- /dev/null +++ b/tools/interface_generator/requirements.txt @@ -0,0 +1,6 @@ +Mako==1.3.3 +MarkupSafe==2.1.5 +pytest==8.3.2 +pytest-black==0.3.12 +pytest-pylint==0.21.0 +pytest-mypy==0.10.3 diff --git a/tools/interface_generator/requirements_lock.txt b/tools/interface_generator/requirements_lock.txt new file mode 100644 index 0000000..62c9eb4 --- /dev/null +++ b/tools/interface_generator/requirements_lock.txt @@ -0,0 +1,220 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //:requirements.update +# +astroid==3.2.4 \ + --hash=sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a \ + --hash=sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25 + # via pylint +attrs==24.2.0 \ + --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ + --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 + # via pytest-mypy +black==24.8.0 \ + --hash=sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6 \ + --hash=sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e \ + --hash=sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f \ + --hash=sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018 \ + --hash=sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e \ + --hash=sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd \ + --hash=sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4 \ + --hash=sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed \ + --hash=sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2 \ + --hash=sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42 \ + --hash=sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af \ + --hash=sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb \ + --hash=sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368 \ + --hash=sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb \ + --hash=sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af \ + --hash=sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed \ + --hash=sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47 \ + --hash=sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2 \ + --hash=sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a \ + --hash=sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c \ + --hash=sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920 \ + --hash=sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1 + # via pytest-black +click==8.1.7 \ + --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ + --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de + # via black +dill==0.3.8 \ + --hash=sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca \ + --hash=sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7 + # via pylint +filelock==3.15.4 \ + --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ + --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 + # via pytest-mypy +iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 + # via pytest +isort==5.13.2 \ + --hash=sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109 \ + --hash=sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6 + # via pylint +mako==1.3.3 \ + --hash=sha256:5324b88089a8978bf76d1629774fcc2f1c07b82acdf00f4c5dd8ceadfffc4b40 \ + --hash=sha256:e16c01d9ab9c11f7290eef1cfefc093fb5a45ee4a3da09e2fec2e4d1bae54e73 + # via -r requirements.txt +markupsafe==2.1.5 \ + --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ + --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ + --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ + --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ + --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ + --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ + --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ + --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ + --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ + --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ + --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ + --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ + --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ + --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ + --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ + --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ + --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ + --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ + --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ + --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ + --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ + --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ + --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ + --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ + --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ + --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ + --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ + --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ + --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ + --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ + --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ + --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ + --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ + --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ + --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ + --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ + --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ + --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ + --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ + --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 + # via + # -r requirements.txt + # mako +mccabe==0.7.0 \ + --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ + --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e + # via pylint +mypy==1.11.1 \ + --hash=sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54 \ + --hash=sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a \ + --hash=sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72 \ + --hash=sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69 \ + --hash=sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b \ + --hash=sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe \ + --hash=sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4 \ + --hash=sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd \ + --hash=sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0 \ + --hash=sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525 \ + --hash=sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2 \ + --hash=sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c \ + --hash=sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5 \ + --hash=sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de \ + --hash=sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74 \ + --hash=sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c \ + --hash=sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e \ + --hash=sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58 \ + --hash=sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b \ + --hash=sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417 \ + --hash=sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411 \ + --hash=sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb \ + --hash=sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03 \ + --hash=sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca \ + --hash=sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8 \ + --hash=sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08 \ + --hash=sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809 + # via pytest-mypy +mypy-extensions==1.0.0 \ + --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \ + --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 + # via + # black + # mypy +packaging==24.1 \ + --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ + --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 + # via + # black + # pytest +pathspec==0.12.1 \ + --hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08 \ + --hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 + # via black +platformdirs==4.2.2 \ + --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ + --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 + # via + # black + # pylint +pluggy==1.5.0 \ + --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ + --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 + # via pytest +pylint==3.2.6 \ + --hash=sha256:03c8e3baa1d9fb995b12c1dbe00aa6c4bcef210c2a2634374aedeb22fb4a8f8f \ + --hash=sha256:a5d01678349454806cff6d886fb072294f56a58c4761278c97fb557d708e1eb3 + # via pytest-pylint +pytest==8.3.2 \ + --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ + --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce + # via + # -r requirements.txt + # pytest-black + # pytest-mypy + # pytest-pylint +pytest-black==0.3.12 \ + --hash=sha256:1d339b004f764d6cd0f06e690f6dd748df3d62e6fe1a692d6a5500ac2c5b75a5 + # via -r requirements.txt +pytest-mypy==0.10.3 \ + --hash=sha256:7638d0d3906848fc1810cb2f5cc7fceb4cc5c98524aafcac58f28620e3102053 \ + --hash=sha256:f8458f642323f13a2ca3e2e61509f7767966b527b4d8adccd5032c3e7b4fd3db + # via -r requirements.txt +pytest-pylint==0.21.0 \ + --hash=sha256:88764b8e1d5cfa18809248e0ccc2fc05035f08c35f0b0222ddcfea1c3c4e553e \ + --hash=sha256:f10d9eaa72b9fbe624ee4b55da0481f56482eee0a467afc1ee3ae8b1fefbd0b4 + # via -r requirements.txt +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via pytest-black +tomlkit==0.13.0 \ + --hash=sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72 \ + --hash=sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264 + # via pylint +typing-extensions==4.12.2 \ + --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ + --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 + # via mypy diff --git a/tools/interface_generator/test/BUILD.bazel b/tools/interface_generator/test/BUILD.bazel new file mode 100644 index 0000000..2166c4e --- /dev/null +++ b/tools/interface_generator/test/BUILD.bazel @@ -0,0 +1,24 @@ +load("//:defs.bzl", "generate_interface") + +generate_interface( + name = "kstdio", + interface = "kstdio", + license = "//:LICENSE.md", + visibility = ["//visibility:private"], +) + +cc_library( + name = "kstdio_mock", + srcs = ["kstdio_mock.cpp"], + deps = [ + ":kstdio", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "test_kstdio", + srcs = ["test_kstdio.cpp"], + deps = [":kstdio_mock"], +) diff --git a/tools/interface_generator/test/test_kstdio.cpp b/tools/interface_generator/test/test_kstdio.cpp new file mode 100644 index 0000000..95540a4 --- /dev/null +++ b/tools/interface_generator/test/test_kstdio.cpp @@ -0,0 +1,20 @@ +#include "kstdio_mock.hpp" + +using ::testing::StrEq; + +class kstdioFixture : public ::testing::Test { +protected: + void + SetUp() + { + } + + kstdio_mock m_kstdio_mock; +}; + +TEST_F(kstdioFixture, printfCallsMock) +{ + EXPECT_CALL(m_kstdio_mock, printf(StrEq("hello world"))); + + kstdio_printf("hello world"); +} |