2023-01-11 15:21:14 +01:00
|
|
|
# Copyright 2020-2023 Ternaris.
|
2021-05-02 14:43:48 +02:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
"""Message definition parser tests."""
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
2021-08-01 18:03:18 +02:00
|
|
|
from rosbags.typesys import (
|
|
|
|
|
TypesysError,
|
|
|
|
|
generate_msgdef,
|
|
|
|
|
get_types_from_idl,
|
|
|
|
|
get_types_from_msg,
|
|
|
|
|
register_types,
|
|
|
|
|
)
|
2021-05-02 14:43:48 +02:00
|
|
|
from rosbags.typesys.base import Nodetype
|
|
|
|
|
from rosbags.typesys.types import FIELDDEFS
|
|
|
|
|
|
|
|
|
|
MSG = """
|
|
|
|
|
# comment
|
|
|
|
|
|
2021-11-25 14:26:48 +01:00
|
|
|
bool b=true
|
2021-05-02 14:43:48 +02:00
|
|
|
int32 global=42
|
2021-11-25 14:26:48 +01:00
|
|
|
float32 f=1.33
|
2021-10-03 06:28:02 +02:00
|
|
|
string str= foo bar\t
|
2021-05-02 14:43:48 +02:00
|
|
|
|
|
|
|
|
std_msgs/Header header
|
|
|
|
|
std_msgs/msg/Bool bool
|
|
|
|
|
test_msgs/Bar sibling
|
|
|
|
|
float64 base
|
|
|
|
|
float64[] seq1
|
|
|
|
|
float64[] seq2
|
|
|
|
|
float64[4] array
|
|
|
|
|
"""
|
|
|
|
|
|
2021-11-09 10:40:25 +01:00
|
|
|
MSG_BOUNDS = """
|
|
|
|
|
int32[] unbounded_integer_array
|
|
|
|
|
int32[5] five_integers_array
|
|
|
|
|
int32[<=5] up_to_five_integers_array
|
|
|
|
|
|
|
|
|
|
string string_of_unbounded_size
|
|
|
|
|
string<=10 up_to_ten_characters_string
|
|
|
|
|
|
|
|
|
|
string[<=5] up_to_five_unbounded_strings
|
|
|
|
|
string<=10[] unbounded_array_of_string_up_to_ten_characters_each
|
|
|
|
|
string<=10[<=5] up_to_five_strings_up_to_ten_characters_each
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
MSG_DEFAULTS = """
|
|
|
|
|
bool b false
|
|
|
|
|
uint8 i 42
|
|
|
|
|
uint8 o 0377
|
|
|
|
|
uint8 h 0xff
|
|
|
|
|
float32 y -314.15e-2
|
|
|
|
|
string name1 "John"
|
|
|
|
|
string name2 'Ringo'
|
|
|
|
|
int32[] samples [-200, -100, 0, 100, 200]
|
|
|
|
|
"""
|
|
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
MULTI_MSG = """
|
|
|
|
|
std_msgs/Header header
|
|
|
|
|
byte b
|
|
|
|
|
char c
|
|
|
|
|
Other[] o
|
|
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
|
MSG: std_msgs/Header
|
|
|
|
|
time time
|
|
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
|
MSG: test_msgs/Other
|
|
|
|
|
uint64[3] Header
|
2021-08-01 17:38:18 +02:00
|
|
|
uint32 static = 42
|
2021-05-02 14:43:48 +02:00
|
|
|
"""
|
|
|
|
|
|
2021-10-04 16:46:22 +02:00
|
|
|
CSTRING_CONFUSION_MSG = """
|
|
|
|
|
std_msgs/Header header
|
|
|
|
|
string s
|
|
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
|
MSG: std_msgs/Header
|
|
|
|
|
time time
|
|
|
|
|
"""
|
|
|
|
|
|
2021-07-08 09:18:54 +02:00
|
|
|
RELSIBLING_MSG = """
|
|
|
|
|
Header header
|
|
|
|
|
Other other
|
|
|
|
|
"""
|
|
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
IDL_LANG = """
|
|
|
|
|
// assign different literals and expressions
|
|
|
|
|
|
|
|
|
|
#ifndef FOO
|
|
|
|
|
#define FOO
|
|
|
|
|
|
|
|
|
|
#include <global>
|
|
|
|
|
#include "local"
|
|
|
|
|
|
|
|
|
|
const bool g_bool = TRUE;
|
|
|
|
|
const int8 g_int1 = 7;
|
|
|
|
|
const int8 g_int2 = 07;
|
|
|
|
|
const int8 g_int3 = 0x7;
|
|
|
|
|
const float64 g_float1 = 1.1;
|
|
|
|
|
const float64 g_float2 = 1e10;
|
|
|
|
|
const char g_char = 'c';
|
|
|
|
|
const string g_string1 = "";
|
|
|
|
|
const string<128> g_string2 = "str" "ing";
|
|
|
|
|
|
|
|
|
|
module Foo {
|
|
|
|
|
const int64 g_expr1 = ~1;
|
|
|
|
|
const int64 g_expr2 = 2 * 4;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
IDL = """
|
|
|
|
|
// comment in file
|
|
|
|
|
module test_msgs {
|
|
|
|
|
// comment in module
|
|
|
|
|
typedef std_msgs::msg::Bool Bool;
|
|
|
|
|
|
2022-07-28 19:14:39 +02:00
|
|
|
/**/ /***/ /* block comment */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* block comment
|
|
|
|
|
*/
|
|
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
module msg {
|
|
|
|
|
// comment in submodule
|
|
|
|
|
typedef Bool Balias;
|
|
|
|
|
typedef test_msgs::msg::Bar Bar;
|
|
|
|
|
typedef double d4[4];
|
|
|
|
|
|
2021-08-01 17:38:18 +02:00
|
|
|
module Foo_Constants {
|
|
|
|
|
const int32 FOO = 32;
|
|
|
|
|
const int64 BAR = 64;
|
|
|
|
|
};
|
|
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
@comment(type="text", text="ignore")
|
|
|
|
|
struct Foo {
|
2022-07-28 19:14:39 +02:00
|
|
|
// comment in struct
|
2021-05-02 14:43:48 +02:00
|
|
|
std_msgs::msg::Header header;
|
|
|
|
|
Balias bool;
|
|
|
|
|
Bar sibling;
|
2022-07-28 19:14:39 +02:00
|
|
|
double/* comment in member declaration */x;
|
2021-05-02 14:43:48 +02:00
|
|
|
sequence<double> seq1;
|
|
|
|
|
sequence<double, 4> seq2;
|
|
|
|
|
d4 array;
|
|
|
|
|
};
|
|
|
|
|
};
|
2022-04-11 10:46:12 +02:00
|
|
|
|
|
|
|
|
struct Bar {
|
|
|
|
|
int i;
|
|
|
|
|
};
|
2021-05-02 14:43:48 +02:00
|
|
|
};
|
|
|
|
|
"""
|
|
|
|
|
|
2022-09-23 12:15:08 +02:00
|
|
|
IDL_STRINGARRAY = """
|
|
|
|
|
module test_msgs {
|
|
|
|
|
module msg {
|
|
|
|
|
typedef string string__3[3];
|
|
|
|
|
struct Strings {
|
|
|
|
|
string__3 values;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
"""
|
|
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_empty_msg() -> None:
|
2021-11-08 15:11:39 +01:00
|
|
|
"""Test msg parser with empty message."""
|
|
|
|
|
ret = get_types_from_msg('', 'std_msgs/msg/Empty')
|
|
|
|
|
assert ret == {'std_msgs/msg/Empty': ([], [])}
|
|
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_bounds_msg() -> None:
|
2021-11-09 10:40:25 +01:00
|
|
|
"""Test msg parser."""
|
|
|
|
|
ret = get_types_from_msg(MSG_BOUNDS, 'test_msgs/msg/Foo')
|
|
|
|
|
assert ret == {
|
|
|
|
|
'test_msgs/msg/Foo': (
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
('unbounded_integer_array', (4, ((1, 'int32'), None))),
|
|
|
|
|
('five_integers_array', (3, ((1, 'int32'), 5))),
|
|
|
|
|
('up_to_five_integers_array', (4, ((1, 'int32'), None))),
|
|
|
|
|
('string_of_unbounded_size', (1, 'string')),
|
|
|
|
|
('up_to_ten_characters_string', (1, 'string')),
|
|
|
|
|
('up_to_five_unbounded_strings', (4, ((1, 'string'), None))),
|
|
|
|
|
('unbounded_array_of_string_up_to_ten_characters_each', (4, ((1, 'string'), None))),
|
|
|
|
|
('up_to_five_strings_up_to_ten_characters_each', (4, ((1, 'string'), None))),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_defaults_msg() -> None:
|
2021-11-09 10:40:25 +01:00
|
|
|
"""Test msg parser."""
|
|
|
|
|
ret = get_types_from_msg(MSG_DEFAULTS, 'test_msgs/msg/Foo')
|
|
|
|
|
assert ret == {
|
|
|
|
|
'test_msgs/msg/Foo': (
|
|
|
|
|
[],
|
|
|
|
|
[
|
|
|
|
|
('b', (1, 'bool')),
|
|
|
|
|
('i', (1, 'uint8')),
|
|
|
|
|
('o', (1, 'uint8')),
|
|
|
|
|
('h', (1, 'uint8')),
|
|
|
|
|
('y', (1, 'float32')),
|
|
|
|
|
('name1', (1, 'string')),
|
|
|
|
|
('name2', (1, 'string')),
|
|
|
|
|
('samples', (4, ((1, 'int32'), None))),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_msg() -> None:
|
2021-05-02 14:43:48 +02:00
|
|
|
"""Test msg parser."""
|
|
|
|
|
with pytest.raises(TypesysError, match='Could not parse'):
|
2021-11-08 15:11:39 +01:00
|
|
|
get_types_from_msg('invalid', 'test_msgs/msg/Foo')
|
2021-05-02 14:43:48 +02:00
|
|
|
ret = get_types_from_msg(MSG, 'test_msgs/msg/Foo')
|
|
|
|
|
assert 'test_msgs/msg/Foo' in ret
|
2021-08-01 17:38:18 +02:00
|
|
|
consts, fields = ret['test_msgs/msg/Foo']
|
2021-11-25 14:26:48 +01:00
|
|
|
assert consts == [
|
|
|
|
|
('b', 'bool', True),
|
|
|
|
|
('global', 'int32', 42),
|
|
|
|
|
('f', 'float32', 1.33),
|
|
|
|
|
('str', 'string', 'foo bar'),
|
|
|
|
|
]
|
2021-08-01 17:38:18 +02:00
|
|
|
assert fields[0][0] == 'header'
|
|
|
|
|
assert fields[0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert fields[1][0] == 'bool'
|
|
|
|
|
assert fields[1][1][1] == 'std_msgs/msg/Bool'
|
|
|
|
|
assert fields[2][0] == 'sibling'
|
|
|
|
|
assert fields[2][1][1] == 'test_msgs/msg/Bar'
|
|
|
|
|
assert fields[3][1][0] == Nodetype.BASE
|
|
|
|
|
assert fields[4][1][0] == Nodetype.SEQUENCE
|
|
|
|
|
assert fields[5][1][0] == Nodetype.SEQUENCE
|
|
|
|
|
assert fields[6][1][0] == Nodetype.ARRAY
|
2021-05-02 14:43:48 +02:00
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_multi_msg() -> None:
|
2021-05-02 14:43:48 +02:00
|
|
|
"""Test multi msg parser."""
|
|
|
|
|
ret = get_types_from_msg(MULTI_MSG, 'test_msgs/msg/Foo')
|
|
|
|
|
assert len(ret) == 3
|
|
|
|
|
assert 'test_msgs/msg/Foo' in ret
|
|
|
|
|
assert 'std_msgs/msg/Header' in ret
|
|
|
|
|
assert 'test_msgs/msg/Other' in ret
|
2021-08-01 17:38:18 +02:00
|
|
|
fields = ret['test_msgs/msg/Foo'][1]
|
|
|
|
|
assert fields[0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert fields[1][1][1] == 'uint8'
|
|
|
|
|
assert fields[2][1][1] == 'uint8'
|
|
|
|
|
consts = ret['test_msgs/msg/Other'][0]
|
|
|
|
|
assert consts == [('static', 'uint32', 42)]
|
2021-05-02 14:43:48 +02:00
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_cstring_confusion() -> None:
|
2021-10-04 16:46:22 +02:00
|
|
|
"""Test if msg separator is confused with const string."""
|
|
|
|
|
ret = get_types_from_msg(CSTRING_CONFUSION_MSG, 'test_msgs/msg/Foo')
|
|
|
|
|
assert len(ret) == 2
|
|
|
|
|
assert 'test_msgs/msg/Foo' in ret
|
|
|
|
|
assert 'std_msgs/msg/Header' in ret
|
|
|
|
|
consts, fields = ret['test_msgs/msg/Foo']
|
|
|
|
|
assert consts == []
|
|
|
|
|
assert fields[0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert fields[1][1][1] == 'string'
|
|
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_relative_siblings_msg() -> None:
|
2021-07-08 09:18:54 +02:00
|
|
|
"""Test relative siblings with msg parser."""
|
|
|
|
|
ret = get_types_from_msg(RELSIBLING_MSG, 'test_msgs/msg/Foo')
|
2021-08-01 17:38:18 +02:00
|
|
|
assert ret['test_msgs/msg/Foo'][1][0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert ret['test_msgs/msg/Foo'][1][1][1][1] == 'test_msgs/msg/Other'
|
2021-07-08 09:18:54 +02:00
|
|
|
|
|
|
|
|
ret = get_types_from_msg(RELSIBLING_MSG, 'rel_msgs/msg/Foo')
|
2021-08-01 17:38:18 +02:00
|
|
|
assert ret['rel_msgs/msg/Foo'][1][0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert ret['rel_msgs/msg/Foo'][1][1][1][1] == 'rel_msgs/msg/Other'
|
2021-07-08 09:18:54 +02:00
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_parse_idl() -> None:
|
2021-05-02 14:43:48 +02:00
|
|
|
"""Test idl parser."""
|
|
|
|
|
ret = get_types_from_idl(IDL_LANG)
|
|
|
|
|
assert ret == {}
|
|
|
|
|
|
|
|
|
|
ret = get_types_from_idl(IDL)
|
|
|
|
|
assert 'test_msgs/msg/Foo' in ret
|
2021-08-01 17:38:18 +02:00
|
|
|
consts, fields = ret['test_msgs/msg/Foo']
|
|
|
|
|
assert consts == [('FOO', 'int32', 32), ('BAR', 'int64', 64)]
|
|
|
|
|
assert fields[0][0] == 'header'
|
|
|
|
|
assert fields[0][1][1] == 'std_msgs/msg/Header'
|
|
|
|
|
assert fields[1][0] == 'bool'
|
|
|
|
|
assert fields[1][1][1] == 'std_msgs/msg/Bool'
|
|
|
|
|
assert fields[2][0] == 'sibling'
|
|
|
|
|
assert fields[2][1][1] == 'test_msgs/msg/Bar'
|
|
|
|
|
assert fields[3][1][0] == Nodetype.BASE
|
|
|
|
|
assert fields[4][1][0] == Nodetype.SEQUENCE
|
|
|
|
|
assert fields[5][1][0] == Nodetype.SEQUENCE
|
|
|
|
|
assert fields[6][1][0] == Nodetype.ARRAY
|
2021-05-02 14:43:48 +02:00
|
|
|
|
2022-04-11 10:46:12 +02:00
|
|
|
assert 'test_msgs/Bar' in ret
|
|
|
|
|
consts, fields = ret['test_msgs/Bar']
|
|
|
|
|
assert consts == []
|
|
|
|
|
assert len(fields) == 1
|
|
|
|
|
assert fields[0][0] == 'i'
|
|
|
|
|
assert fields[0][1][1] == 'int'
|
|
|
|
|
|
2022-09-23 12:15:08 +02:00
|
|
|
ret = get_types_from_idl(IDL_STRINGARRAY)
|
|
|
|
|
consts, fields = ret['test_msgs/msg/Strings']
|
|
|
|
|
assert consts == []
|
|
|
|
|
assert len(fields) == 1
|
|
|
|
|
assert fields[0][0] == 'values'
|
2023-01-13 12:16:05 +01:00
|
|
|
assert fields[0][1] == (Nodetype.ARRAY, ((Nodetype.BASE, 'string'), 3))
|
2022-09-23 12:15:08 +02:00
|
|
|
|
2021-05-02 14:43:48 +02:00
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_register_types() -> None:
|
2021-05-02 14:43:48 +02:00
|
|
|
"""Test type registeration."""
|
|
|
|
|
assert 'foo' not in FIELDDEFS
|
|
|
|
|
register_types({})
|
2021-11-25 14:26:17 +01:00
|
|
|
register_types({'foo': [[], [('b', (1, 'bool'))]]}) # type: ignore
|
2021-05-02 14:43:48 +02:00
|
|
|
assert 'foo' in FIELDDEFS
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
register_types({'std_msgs/msg/Header': [[], []]}) # type: ignore
|
2021-08-01 17:38:18 +02:00
|
|
|
assert len(FIELDDEFS['std_msgs/msg/Header'][1]) == 2
|
2021-05-02 14:43:48 +02:00
|
|
|
|
|
|
|
|
with pytest.raises(TypesysError, match='different definition'):
|
2021-11-25 14:26:17 +01:00
|
|
|
register_types({'foo': [[], [('x', (1, 'bool'))]]}) # type: ignore
|
2021-08-01 18:03:18 +02:00
|
|
|
|
|
|
|
|
|
2021-11-25 14:26:17 +01:00
|
|
|
def test_generate_msgdef() -> None:
|
2021-08-01 18:03:18 +02:00
|
|
|
"""Test message definition generator."""
|
|
|
|
|
res = generate_msgdef('std_msgs/msg/Header')
|
|
|
|
|
assert res == ('uint32 seq\ntime stamp\nstring frame_id\n', '2176decaecbce78abc3b96ef049fabed')
|
|
|
|
|
|
|
|
|
|
res = generate_msgdef('geometry_msgs/msg/PointStamped')
|
|
|
|
|
assert res[0].split(f'{"=" * 80}\n') == [
|
|
|
|
|
'std_msgs/Header header\ngeometry_msgs/Point point\n',
|
|
|
|
|
'MSG: std_msgs/Header\nuint32 seq\ntime stamp\nstring frame_id\n',
|
|
|
|
|
'MSG: geometry_msgs/Point\nfloat64 x\nfloat64 y\nfloat64 z\n',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
res = generate_msgdef('geometry_msgs/msg/Twist')
|
|
|
|
|
assert res[0].split(f'{"=" * 80}\n') == [
|
|
|
|
|
'geometry_msgs/Vector3 linear\ngeometry_msgs/Vector3 angular\n',
|
|
|
|
|
'MSG: geometry_msgs/Vector3\nfloat64 x\nfloat64 y\nfloat64 z\n',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
res = generate_msgdef('shape_msgs/msg/Mesh')
|
|
|
|
|
assert res[0].split(f'{"=" * 80}\n') == [
|
|
|
|
|
'shape_msgs/MeshTriangle[] triangles\ngeometry_msgs/Point[] vertices\n',
|
|
|
|
|
'MSG: shape_msgs/MeshTriangle\nuint32[3] vertex_indices\n',
|
|
|
|
|
'MSG: geometry_msgs/Point\nfloat64 x\nfloat64 y\nfloat64 z\n',
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
res = generate_msgdef('shape_msgs/msg/Plane')
|
|
|
|
|
assert res[0] == 'float64[4] coef\n'
|
|
|
|
|
|
|
|
|
|
res = generate_msgdef('sensor_msgs/msg/MultiEchoLaserScan')
|
|
|
|
|
assert len(res[0].split('=' * 80)) == 3
|
|
|
|
|
|
|
|
|
|
register_types(get_types_from_msg('time[3] times\nuint8 foo=42', 'foo_msgs/Timelist'))
|
|
|
|
|
res = generate_msgdef('foo_msgs/msg/Timelist')
|
|
|
|
|
assert res[0] == 'uint8 foo=42\ntime[3] times\n'
|
|
|
|
|
|
|
|
|
|
with pytest.raises(TypesysError, match='is unknown'):
|
|
|
|
|
generate_msgdef('foo_msgs/msg/Badname')
|