Skip to content

Commit

Permalink
Add Python tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tjol committed Sep 16, 2022
1 parent 2e0b9b8 commit 0142d5b
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 6 deletions.
8 changes: 6 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ project(ckdl C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_EXTENSIONS OFF)

set(KDL_COMPILE_OPTIONS)

if(MSVC)
string(REGEX REPLACE "/W3" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
add_compile_options(/wd4996 /wd5105)
list(APPEND KDL_COMPILE_OPTIONS /wd4996 /wd5105)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
else()
# try GCC-like options
Expand All @@ -25,7 +27,7 @@ else()
foreach(flag ${warning_flags})
check_c_compiler_flag("-${flag}" "COMPILER_FLAG_${flag}")
if(${COMPILER_FLAG_${flag}})
add_compile_options("-${flag}")
list(APPEND KDL_COMPILE_OPTIONS "-${flag}")
endif()
endforeach()
endif()
Expand Down Expand Up @@ -58,9 +60,11 @@ set(KDL_UTF8_C_SOURCES
)

add_library(kdl-utf8 STATIC ${KDL_UTF8_C_SOURCES})
target_compile_options(kdl-utf8 PRIVATE ${KDL_COMPILE_OPTIONS})
target_include_directories(kdl-utf8 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src)

add_library(kdl ${KDL_C_SOURCES})
target_compile_options(kdl PRIVATE ${KDL_COMPILE_OPTIONS})
target_link_libraries(kdl PRIVATE kdl-utf8 math)
target_include_directories(kdl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_compile_definitions(kdl PRIVATE -DBUILDING_KDL=1)
Expand Down
1 change: 1 addition & 0 deletions bindings/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ if(BUILD_KDLPP)
)

add_library(kdlpp STATIC ${KDLPP_CXX_SOURCES})
target_compile_options(kdlpp PRIVATE ${KDL_COMPILE_OPTIONS})
target_include_directories(kdlpp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(kdlpp PUBLIC kdl)
target_compile_features(kdlpp PUBLIC cxx_std_20)
Expand Down
125 changes: 122 additions & 3 deletions bindings/python/src/ckdl/_ckdl.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ cdef class Value:
def __str__(self):
return str(Document(Node("-", [self])))[2:].strip()

def __eq__(self, other):
return (isinstance(other, type(self))
and other.type_annotation == self.type_annotation
and other.value == self.value)

cdef _convert_kdl_value_no_type(const kdl_value* v):
if v.type == KDL_TYPE_NULL:
return None
Expand Down Expand Up @@ -173,6 +178,14 @@ cdef class Node:
def __str__(self):
return str(Document(self))

def __eq__(self, other):
return (isinstance(other, type(self))
and other.type_annotation == self.type_annotation
and other.name == self.name
and other.args == self.args
and other.properties == self.properties
and other.children == self.children)

cdef kdl_value _make_kdl_value(value, kdl_owned_string* tmp_str_t, kdl_owned_string* tmp_str_v):
cdef kdl_value result
result.type_annotation.data = NULL
Expand Down Expand Up @@ -302,14 +315,19 @@ cdef class Document:
def __str__(self):
return self.dump()

def dump(self):
def __eq__(self, other):
return isinstance(other, type(self)) and self.nodes == other.nodes

def dump(self, EmitterOptions opts=EmitterOptions()):
"""Convert to KDL"""
cdef kdl_emitter *emitter
cdef kdl_emitter_options opts
cdef kdl_emitter_options c_opts
cdef kdl_str buf
cdef str doc

emitter = kdl_create_buffering_emitter(&KDL_DEFAULT_EMITTER_OPTIONS)
c_opts = opts._to_c_struct()

emitter = kdl_create_buffering_emitter(&c_opts)

for node in self.nodes:
_emit_node(emitter, node)
Expand All @@ -321,6 +339,107 @@ cdef class Document:
kdl_destroy_emitter(emitter)
return doc

cpdef enum EscapeMode:
minimal = KDL_ESCAPE_MINIMAL
control = KDL_ESCAPE_CONTROL
newline = KDL_ESCAPE_NEWLINE
tab = KDL_ESCAPE_TAB
ascii_mode = KDL_ESCAPE_ASCII_MODE
default = KDL_ESCAPE_DEFAULT

cdef class FloatMode:
cdef public bint always_write_decimal_point
cdef public bint always_write_decimal_point_or_exponent
cdef public bint capital_e
cdef public bint exponent_plus
cdef public bint plus
cdef public int min_exponent

def __init__(
self, *,
always_write_decimal_point=None,
always_write_decimal_point_or_exponent=None,
capital_e=None,
exponent_plus=None,
plus=None,
min_exponent=None):
cdef kdl_float_printing_options* defaults = &KDL_DEFAULT_EMITTER_OPTIONS.float_mode
if always_write_decimal_point is not None:
self.always_write_decimal_point = always_write_decimal_point
else:
self.always_write_decimal_point = defaults.always_write_decimal_point
if always_write_decimal_point_or_exponent is not None:
self.always_write_decimal_point_or_exponent = always_write_decimal_point_or_exponent
else:
self.always_write_decimal_point_or_exponent = defaults.always_write_decimal_point_or_exponent
if capital_e is not None:
self.capital_e = capital_e
else:
self.capital_e = defaults.capital_e
if exponent_plus is not None:
self.exponent_plus = exponent_plus
else:
self.exponent_plus = defaults.exponent_plus
if plus is not None:
self.plus = plus
else:
self.plus = defaults.plus
if min_exponent is not None:
self.min_exponent = min_exponent
else:
self.min_exponent = defaults.min_exponent

cdef kdl_float_printing_options _to_c_struct(self):
cdef kdl_float_printing_options res
res.always_write_decimal_point = self.always_write_decimal_point
res.always_write_decimal_point_or_exponent = self.always_write_decimal_point_or_exponent
res.capital_e = self.capital_e
res.exponent_plus = self.exponent_plus
res.plus = self.plus
res.min_exponent = self.min_exponent
return res

cpdef enum IdentifierMode:
prefer_bare_identifiers = KDL_PREFER_BARE_IDENTIFIERS
quote_all_identifiers = KDL_QUOTE_ALL_IDENTIFIERS
ascii_identifiers = KDL_ASCII_IDENTIFIERS

cdef class EmitterOptions:
cdef public int indent
cdef public EscapeMode escape_mode
cdef public IdentifierMode identifier_mode
cdef public FloatMode float_mode

def __init__(
self, *,
indent=None,
escape_mode=None,
identifier_mode=None,
float_mode=None):
if indent is not None:
self.indent = indent
else:
self.indent = KDL_DEFAULT_EMITTER_OPTIONS.indent
if escape_mode is not None:
self.escape_mode = escape_mode
else:
self.escape_mode = <EscapeMode>KDL_DEFAULT_EMITTER_OPTIONS.escape_mode
if identifier_mode is not None:
self.identifier_mode = identifier_mode
else:
self.identifier_mode = <IdentifierMode>KDL_DEFAULT_EMITTER_OPTIONS.identifier_mode
if float_mode is not None:
self.float_mode = float_mode
else:
self.float_mode = FloatMode()

cdef kdl_emitter_options _to_c_struct(self):
cdef kdl_emitter_options res
res.indent = self.indent
res.escape_mode = <kdl_escape_mode>self.escape_mode
res.identifier_mode = <kdl_identifier_emission_mode>self.identifier_mode
res.float_mode = self.float_mode._to_c_struct()
return res

def parse(str kdl_text):
"""
Expand Down
167 changes: 167 additions & 0 deletions bindings/python/tests/ckdl_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env python3

import ckdl

import unittest


class CKDLTest(unittest.TestCase):
def _dedent_str(self, s):
lines = s.split("\n")
while lines[0] in ("", "\r"):
lines = lines[1:]
indents = []
for l in lines:
indent = 0
for c in l:
if c == " ":
indent += 1
else:
break
indents.append(indent)
indent = min(indents)
indent_str = " " * indent
dedented = [l[indent:] if l.startswith(indent_str) else l for l in lines]
return "\n".join(dedented)

def test_simple_parsing(self):
kdl = '(tp)node "arg1" 2 3; node2'
doc = ckdl.parse(kdl)
self.assertEqual(len(doc), 2)
self.assertEqual(doc[0].type_annotation, "tp")
self.assertEqual(doc[0].name, "node")
self.assertEqual(doc[0].args, ["arg1", 2, 3])
self.assertEqual(doc[0].children, [])
self.assertEqual(doc[0].properties, {})
self.assertIsNone(doc[1].type_annotation)
self.assertEqual(doc[1].name, "node2")
self.assertEqual(doc[1].args, [])
self.assertEqual(doc[1].children, [])
self.assertEqual(doc[1].properties, {})

def test_simple_emission(self):
doc = ckdl.Document(
ckdl.Node(
None,
"-",
"foo",
100,
None,
ckdl.Node("child1", a=ckdl.Value("i8", -1)),
ckdl.Node("child2", True),
)
)
expected = self._dedent_str(
"""
- "foo" 100 null {
child1 a=(i8)-1
child2 true
}
"""
)
self.assertEqual(str(doc), expected)
self.assertEqual(doc.dump(), expected)

def test_node_constructors(self):
doc = ckdl.Document(
ckdl.Node("n"),
ckdl.Node(None, "n"),
ckdl.Node(None, "n", "a"),
ckdl.Node("t", "n"),
ckdl.Node("n", None, True, False),
ckdl.Node("n", k="v"),
ckdl.Node("n", ckdl.Node("c1"), ckdl.Node("c2")),
ckdl.Node("n", 1, ckdl.Node("c1")),
ckdl.Node("t", "n", "a", k="v"),
ckdl.Node("n", [], []),
ckdl.Node("n", [1], [ckdl.Node("c1")]),
ckdl.Node(
"n", args=[None], properties={"#": True}, children=[ckdl.Node("c1")]
),
ckdl.Node("n", [None], children=[ckdl.Node("c1")]),
)
expected = self._dedent_str(
"""
n
n
n "a"
(t)n
n null true false
n k="v"
n {
c1
c2
}
n 1 {
c1
}
(t)n "a" k="v"
n
n 1 {
c1
}
n null #=true {
c1
}
n null {
c1
}
"""
)
self.assertEqual(doc.dump(), expected)

def test_emitter_opts(self):
doc = ckdl.Document(ckdl.Node("a", ckdl.Node(None, "🎉", "🎈", 0.002)))
expected_default = self._dedent_str(
"""
a {
🎉 "🎈" 0.002
}
"""
)
self.assertEqual(doc.dump(), expected_default)
opt1 = ckdl.EmitterOptions(
indent=1,
escape_mode=ckdl.EscapeMode.ascii_mode,
float_mode=ckdl.FloatMode(min_exponent=2),
)
expected_opt1 = self._dedent_str(
f"""
a {{
🎉 "\\u{{{ord('🎈'):x}}}" 2e-3
}}
"""
)
self.assertEqual(doc.dump(opt1), expected_opt1)
opt2 = ckdl.EmitterOptions(
indent=5,
identifier_mode=ckdl.IdentifierMode.quote_all_identifiers,
float_mode=ckdl.FloatMode(plus=True),
)
expected_opt2 = self._dedent_str(
"""
"a" {
"🎉" "🎈" +0.002
}
"""
)
self.assertEqual(doc.dump(opt2), expected_opt2)
opt3 = ckdl.EmitterOptions(
indent=0,
identifier_mode=ckdl.IdentifierMode.ascii_identifiers,
float_mode=ckdl.FloatMode(
always_write_decimal_point=True, min_exponent=2, capital_e=True
),
)
expected_opt3 = self._dedent_str(
"""
a {
"🎉" "🎈" 2.0E-3
}
"""
)
self.assertEqual(doc.dump(opt2), expected_opt2)


if __name__ == "__main__":
unittest.main()
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ requires = [
"setuptools>=42",
"scikit-build",
"cmake>=3.8",
"ninja; platform_system!='Windows'"
"ninja; platform_system!='Windows'",
"cython"
]
build-backend = "setuptools.build_meta"
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ if(NOT KDL_TEST_CASES_ROOT)
endif()

add_library(test_util STATIC test_util.c)
target_compile_options(test_util PUBLIC ${KDL_COMPILE_OPTIONS})
target_include_directories(test_util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

add_executable(utf8_test utf8_test.c)
Expand Down

0 comments on commit 0142d5b

Please sign in to comment.