Skip to content

Commit

Permalink
Merge pull request #12 from vkottler/dev/struct-tests
Browse files Browse the repository at this point in the history
Adding initial structure for struct tests
  • Loading branch information
vkottler authored Sep 24, 2023
2 parents be57cb0 + f0d971f commit 99e71b1
Show file tree
Hide file tree
Showing 27 changed files with 469 additions and 95 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
ignore = E203,W503
exclude = tests/data
6 changes: 3 additions & 3 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
python-version: ${{matrix.python-version}}
cache: pip

- run: pip${{matrix.python-version}} install vmklib>=1.8.0
- run: pip${{matrix.python-version}} install vmklib

# Begin project-specific setup.
- uses: seanmiddleditch/gha-setup-ninja@master
Expand All @@ -62,7 +62,7 @@ jobs:
env:
PY_TEST_EXTRA_ARGS: --cov-report=xml

- uses: codecov/codecov-action@main
- uses: codecov/codecov-action@v3

- run: mk pypi-upload-ci
env:
Expand All @@ -75,7 +75,7 @@ jobs:
- run: |
mk python-release owner=vkottler \
repo=ifgen version=1.2.0
repo=ifgen version=2.0.0
if: |
matrix.python-version == '3.11'
&& matrix.system == 'ubuntu-latest'
Expand Down
1 change: 1 addition & 0 deletions .isort.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[settings]
known_first_party=ifgen,vmklib
skip=tests/data
3 changes: 3 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
[DESIGN]
max-args=7

[MESSAGES CONTROL]
disable=duplicate-code
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
=====================================
generator=datazen
version=3.1.3
hash=5e7b7fed8b0401fcfa361931270f999b
hash=6281f97e0bcb4e95cd6f93142f045f34
=====================================
-->

# ifgen ([1.2.0](https://pypi.org/project/ifgen/))
# ifgen ([2.0.0](https://pypi.org/project/ifgen/))

[![python](https://img.shields.io/pypi/pyversions/ifgen.svg)](https://pypi.org/project/ifgen/)
![Build Status](https://github.com/vkottler/ifgen/workflows/Python%20Package/badge.svg)
Expand Down Expand Up @@ -46,14 +46,18 @@ This package is tested on the following platforms:
```
$ ./venv3.11/bin/ig -h
usage: ig [-h] [--version] [-v] [-C DIR] {gen,noop} ...
usage: ig [-h] [--version] [-v] [-q] [--curses] [--no-uvloop] [-C DIR]
{gen,noop} ...
An interface generator for distributed computing.
options:
-h, --help show this help message and exit
--version show program's version number and exit
-v, --verbose set to increase logging verbosity
-q, --quiet set to reduce output
--curses whether or not to use curses.wrapper when starting
--no-uvloop whether or not to disable uvloop as event loop driver
-C DIR, --dir DIR execute from a specific directory
commands:
Expand Down
4 changes: 2 additions & 2 deletions ifgen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.3
# hash=dcfab6b503a06109b92ab9ee84f0bec4
# hash=a5fbdd144be22177fbedbc33d8b58b3d
# =====================================

"""
Expand All @@ -10,4 +10,4 @@

DESCRIPTION = "An interface generator for distributed computing."
PKG_NAME = "ifgen"
VERSION = "1.2.0"
VERSION = "2.0.0"
39 changes: 38 additions & 1 deletion ifgen/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,48 @@ def create_common_test(task: GenerateTask) -> None:
def create_common(task: GenerateTask) -> None:
"""Create a unit test for the enum string-conversion methods."""

with task.boilerplate(includes=["<bit>"]) as writer:
streams = task.stream_implementation

includes = [
"<cstdint>",
"<bit>",
"<span>" if not streams else "<spanstream>",
]

# probably get rid of everything besides the spanstream
if streams:
includes.extend(["<streambuf>", "<istream>", "<ostream>"])

with task.boilerplate(includes=includes) as writer:
writer.c_comment("Enforce that this isn't a mixed-endian system.")
writer.write(
"static_assert(std::endian::native == std::endian::big or"
)
writer.write(
" std::endian::native == std::endian::little);"
)

with writer.padding():
writer.c_comment("Create useful aliases for bytes.")
writer.write("template <std::size_t Extent = std::dynamic_extent>")
writer.write("using byte_span = std::span<std::byte, Extent>;")
writer.write(
(
"template <std::size_t size> using byte_array = "
"std::array<std::byte, size>;"
)
)

if streams:
writer.c_comment("Abstract byte-stream interfaces.")
writer.write("using byte_istream = std::basic_istream<std::byte>;")
writer.write("using byte_ostream = std::basic_ostream<std::byte>;")

writer.empty()
writer.c_comment(
"Concrete byte-stream interfaces (based on span)."
)
writer.write("using byte_spanbuf = std::basic_spanbuf<std::byte>;")
writer.write(
"using byte_spanstream = std::basic_spanstream<std::byte>;"
)
4 changes: 4 additions & 0 deletions ifgen/data/schemas/Config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,7 @@ properties:

ifgen:
type: object

stream_implementation:
type: boolean
default: true
21 changes: 7 additions & 14 deletions ifgen/entry.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# =====================================
# generator=datazen
# version=3.1.3
# hash=6018c3513f5723ef6420ac8718d5f8ad
# hash=089f57617fd119bfbae72f24dfa671c7
# =====================================

"""
Expand All @@ -10,14 +10,14 @@

# built-in
import argparse
import logging
from logging import getLogger
import os
from pathlib import Path
import sys
from typing import List

# third-party
from vcorelib.logging import log_time as _log_time
from vcorelib.logging import init_logging, log_time, logging_args

# internal
from ifgen import DESCRIPTION, VERSION
Expand All @@ -41,12 +41,7 @@ def main(argv: List[str] = None) -> int:
action="version",
version=f"%(prog)s {VERSION}",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="set to increase logging verbosity",
)
logging_args(parser)
parser.add_argument(
"-C",
"--dir",
Expand All @@ -66,17 +61,15 @@ def main(argv: List[str] = None) -> int:
args.dir = args.dir.resolve()

# initialize logging
log_level = logging.DEBUG if args.verbose else logging.INFO
logging.basicConfig(
level=log_level,
format="%(name)-36s - %(levelname)-6s - %(message)s",
init_logging(
args, default_format="%(name)-36s - %(levelname)-6s - %(message)s"
)

# change to the specified directory
os.chdir(args.dir)

# run the application
with _log_time(logging.getLogger(__name__), "Command"):
with log_time(getLogger(__name__), "Command"):
result = entry(args)
except SystemExit as exc:
result = 1
Expand Down
18 changes: 13 additions & 5 deletions ifgen/generation/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
Iterator,
NamedTuple,
Optional,
Union,
)

# third-party
Expand Down Expand Up @@ -59,6 +60,13 @@ def cpp_namespace(
data = f"{self.name}::{data}"
return data if not prefix else prefix + data

@property
def stream_implementation(self) -> bool:
"""
Determine if this instances should include a stream implementations.
"""
return self.env.config.data["stream_implementation"] # type: ignore

@property
def source_path(self) -> Path:
"""Get a source file for this task."""
Expand All @@ -79,11 +87,11 @@ def protocol(self) -> Protocol:

return self.env.get_protocol(self.name)

def namespace(self) -> str:
def namespace(self, *names: str) -> str:
"""Get this task's namespace."""

nspace = self.env.types.root_namespace
with nspace.pushed(*self.instance.get("namespace", [])):
with nspace.pushed(*self.instance.get("namespace", []), *names):
result = nspace.namespace(track=False)

assert result, f"No namespace for '{self.name}'!"
Expand Down Expand Up @@ -198,7 +206,7 @@ def boilerplate(
includes: Iterable[str] = None,
is_test: bool = False,
use_namespace: bool = True,
description: str = None,
description: Union[bool, None, str] = None,
json: bool = False,
) -> Iterator[IndentedFileWriter]:
"""
Expand Down Expand Up @@ -232,9 +240,9 @@ def boilerplate(
description = self.instance["description"]

# Write struct definition.
if description is not None:
if description:
with writer.javadoc():
writer.write(description)
writer.write(description) # type: ignore

yield writer

Expand Down
94 changes: 72 additions & 22 deletions ifgen/generation/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
A module implementing unit-testing related generation utilities.
"""

from contextlib import contextmanager

# built-in
from contextlib import ExitStack, contextmanager
from os import linesep
from typing import Iterator, List

Expand All @@ -15,9 +14,59 @@
from ifgen.generation.interface import GenerateTask


def unit_test_method_name(name: str, task: GenerateTask) -> str:
"""Get the name of a unit test."""
return f"test_{task.name}_{name}"


@contextmanager
def unit_test_method(
name: str, task: GenerateTask, writer: IndentedFileWriter
) -> Iterator[None]:
"""Generate unit-test method boilerplate."""

unit_test_method_name(name, task)
writer.write(
f"void {unit_test_method_name(name, task)}(std::endian endianness)"
)
with writer.scope():
nspace = task.env.types.root_namespace

project_wide = nspace.namespace(track=False)
writer.write(f"using namespace {project_wide};")

with nspace.pushed(*task.instance.get("namespace", [])):
curr = nspace.namespace(track=False)
if curr != project_wide:
writer.write(f"using namespace {curr};")

writer.empty()
yield


@contextmanager
def unit_test_main(
task: GenerateTask, writer: IndentedFileWriter, description: bool = True
) -> Iterator[None]:
"""A method for generating main-function boilerplate for unit tests."""

if description:
with writer.javadoc():
writer.write(f"A unit test for {task.generator} {task.name}.")
writer.empty()
writer.write("\\return 0 on success.")

writer.write("int main(void)")
with writer.scope():
writer.write(f"using namespace {task.namespace()};")
writer.empty()
yield
writer.write("return 0;")


@contextmanager
def unit_test_boilerplate(
task: GenerateTask, includes: List[str] = None
task: GenerateTask, includes: List[str] = None, main: bool = True
) -> Iterator[IndentedFileWriter]:
"""Handle standard unit-test boilerplate."""

Expand All @@ -28,22 +77,23 @@ def unit_test_boilerplate(

linesep.join([f"A unit test for {task.generator} {task.name}."])

with task.boilerplate(
includes=["<cassert>", "<cstring>", "<iostream>", f'"{include}"']
+ includes,
is_test=True,
use_namespace=False,
description=linesep.join(
[
f"A unit test for {task.generator} {task.name}.",
"",
"\\return 0 on success.",
]
),
) as writer:
writer.write("int main(void)")
with writer.scope():
writer.write(f"using namespace {task.namespace()};")
writer.empty()
yield writer
writer.write("return 0;")
with ExitStack() as stack:
writer = stack.enter_context(
task.boilerplate(
includes=[
"<cassert>",
"<cstring>",
"<iostream>",
f'"{include}"',
]
+ includes,
is_test=True,
use_namespace=False,
description=False,
)
)

if main:
stack.enter_context(unit_test_main(task, writer))

yield writer
Loading

0 comments on commit 99e71b1

Please sign in to comment.