Skip to content

Commit 74ef6c0

Browse files
Bi0T1Nmjcarroll
andcommitted
Port embedSdf script from Ruby to Python3 and provide unittests (#884)
Converts the script that creates the .cc file for mapping of SDF filename and SDF content from Ruby to Python. Additionally a few unittests are added for the function that uses the mapping and which wasn't tested before. Signed-off-by: Bi0T1N <[email protected]> Co-authored-by: Bi0T1N <[email protected]> Co-authored-by: Michael Carroll <[email protected]>
1 parent f1d13b4 commit 74ef6c0

File tree

4 files changed

+212
-46
lines changed

4 files changed

+212
-46
lines changed

sdf/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ add_subdirectory(1.10)
1212
add_custom_target(schema)
1313
add_dependencies(schema schema1_10)
1414

15+
if (NOT Python3_Interpreter_FOUND)
16+
gz_build_error("Python is required to generate the C++ file with the SDF content")
17+
endif()
18+
1519
# Generate the EmbeddedSdf.cc file, which contains all the supported SDF
1620
# descriptions in a map of strings. The parser.cc file uses EmbeddedSdf.hh.
1721
execute_process(
18-
COMMAND ${RUBY} ${CMAKE_SOURCE_DIR}/sdf/embedSdf.rb
22+
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/sdf/embedSdf.py
1923
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/sdf"
2024
OUTPUT_FILE "${PROJECT_BINARY_DIR}/src/EmbeddedSdf.cc"
2125
)

sdf/embedSdf.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env python3
2+
import inspect
3+
from pathlib import Path, PurePosixPath
4+
5+
""""Script for generating a C++ file that contains the content from all SDF files"""
6+
7+
# The list of supported SDF specification versions. This will let us drop
8+
# versions without removing the directories.
9+
SUPPORTED_SDF_VERSIONS = ['1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3', '1.2']
10+
11+
# The list of supported SDF conversions. This list includes versions that
12+
# a user can convert an existing SDF version to.
13+
SUPPORTED_SDF_CONVERSIONS = ['1.10', '1.9', '1.8', '1.7', '1.6', '1.5', '1.4', '1.3']
14+
15+
# whitespace indentation for C++ code
16+
INDENTATION = ' '
17+
# newline character
18+
NEWLINE = '\n'
19+
20+
def get_copyright_notice() -> str:
21+
"""
22+
Provides the copyrigt notice for the C++ file
23+
24+
:returns: copyright notice
25+
"""
26+
res = inspect.cleandoc("""
27+
/*
28+
* Copyright 2022 Open Source Robotics Foundation
29+
*
30+
* Licensed under the Apache License, Version 2.0 (the "License");
31+
* you may not use this file except in compliance with the License.
32+
* You may obtain a copy of the License at
33+
*
34+
* http://www.apache.org/licenses/LICENSE-2.0
35+
*
36+
* Unless required by applicable law or agreed to in writing, software
37+
* distributed under the License is distributed on an "AS IS" BASIS,
38+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39+
* See the License for the specific language governing permissions and
40+
* limitations under the License.
41+
*
42+
*/
43+
""")
44+
return res + 2*NEWLINE
45+
46+
47+
def get_file_header_prolog() -> str:
48+
"""
49+
Provides the include statement, namespace and variable declaration of the C++ file
50+
51+
:returns: prolog of the C++ file
52+
"""
53+
res = inspect.cleandoc("""
54+
#include "EmbeddedSdf.hh"
55+
56+
namespace sdf
57+
{
58+
inline namespace SDF_VERSION_NAMESPACE
59+
{
60+
/////////////////////////////////////////////////
61+
const std::map<std::string, std::string> &GetEmbeddedSdf()
62+
{
63+
static const std::map<std::string, std::string> result {
64+
""")
65+
return res + NEWLINE
66+
67+
68+
def embed_sdf_content(arg_path: str, arg_file_content: str) -> str:
69+
"""
70+
Generates a string pair with the folder and filename as well as the content of the file
71+
72+
:param arg_path: Foldername and filename of the SDF
73+
:param arg_file_content: Content of the provided file
74+
:returns: raw string literal mapping pair for the std::map
75+
"""
76+
res = []
77+
res.append('// NOLINT')
78+
res.append('{')
79+
res.append(f'"{arg_path}",')
80+
res.append('R"__sdf_literal__(')
81+
res.append(f'{arg_file_content}')
82+
res.append(')__sdf_literal__"')
83+
res.append('},')
84+
res.append('')
85+
return NEWLINE.join(res)
86+
87+
88+
def get_map_content(arg_pathlist: Path) -> str:
89+
"""
90+
Generates a string pair with the folder and filename as well as the content
91+
of the file in ascending order
92+
93+
:param arg_pathlist: Foldername and all filenames inside it
94+
:returns: mapping pairs for the std::map
95+
"""
96+
map_str = ''
97+
files = []
98+
for path in arg_pathlist:
99+
# dir separator is hardcoded to '/' in C++ mapping
100+
posix_path = PurePosixPath(path)
101+
files.append(str(posix_path))
102+
# get ascending order
103+
files.sort()
104+
for file in files:
105+
with Path(file).open() as f:
106+
content = f.read()
107+
map_str += embed_sdf_content(file, content)
108+
return map_str
109+
110+
111+
def get_file_header_epilog() -> str:
112+
"""
113+
Provides the return statement and the closing brackets of the C++ file
114+
115+
:returns: epilog of the C++ file
116+
"""
117+
res = inspect.cleandoc("""
118+
};
119+
120+
return result;
121+
}
122+
123+
}
124+
} // namespace sdf
125+
126+
""")
127+
return NEWLINE + res
128+
129+
130+
if __name__ == "__main__":
131+
copyright = get_copyright_notice()
132+
prolog = get_file_header_prolog()
133+
134+
map_str = ""
135+
for sdf_version in SUPPORTED_SDF_VERSIONS:
136+
pathlist = Path(sdf_version).glob('*.sdf')
137+
map_str += get_map_content(pathlist)
138+
139+
for sdf_conversion in SUPPORTED_SDF_CONVERSIONS:
140+
pathlist = Path(sdf_conversion).glob('*.convert')
141+
map_str += get_map_content(pathlist)
142+
143+
# remove the last comma
144+
map_str = map_str[:-2]
145+
146+
epilog = get_file_header_epilog()
147+
148+
output = copyright + prolog + map_str + epilog
149+
150+
# output to stdin so that CMake can read it and create the appropriate file
151+
print(output)

sdf/embedSdf.rb

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/SDF_TEST.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <gz/utils/Environment.hh>
2222
#include <gz/utils/SuppressWarning.hh>
2323

24+
#include "test_config.hh"
25+
#include "test_utils.hh"
26+
2427
#include "sdf/sdf.hh"
2528

2629
class SDFUpdateFixture
@@ -559,6 +562,59 @@ TEST(SDF, Version)
559562
EXPECT_STREQ(SDF_VERSION, sdf::SDF::Version().c_str());
560563
}
561564

565+
/////////////////////////////////////////////////
566+
TEST(SDF, EmbeddedSpec)
567+
{
568+
std::string result;
569+
570+
result = sdf::SDF::EmbeddedSpec("actor.sdf", false);
571+
EXPECT_NE(result.find("<!-- Actor -->"), std::string::npos);
572+
EXPECT_NE(result.find("<element name=\"actor\" required=\"*\">"),
573+
std::string::npos);
574+
result = sdf::SDF::EmbeddedSpec("actor.sdf", true);
575+
EXPECT_NE(result.find("<!-- Actor -->"), std::string::npos);
576+
EXPECT_NE(result.find("<element name=\"actor\" required=\"*\">"),
577+
std::string::npos);
578+
579+
result = sdf::SDF::EmbeddedSpec("root.sdf", false);
580+
EXPECT_NE(result.find("SDFormat base element"), std::string::npos);
581+
EXPECT_NE(result.find("name=\"version\" type=\"string\""), std::string::npos);
582+
result = sdf::SDF::EmbeddedSpec("root.sdf", true);
583+
EXPECT_NE(result.find("SDFormat base element"), std::string::npos);
584+
EXPECT_NE(result.find("name=\"version\" type=\"string\""), std::string::npos);
585+
}
586+
587+
TEST(SDF, EmbeddedSpecNonExistent)
588+
{
589+
std::string result;
590+
591+
// Capture sdferr output
592+
std::stringstream stderr_buffer;
593+
sdf::testing::RedirectConsoleStream redir(
594+
sdf::Console::Instance()->GetMsgStream(), &stderr_buffer);
595+
#ifdef _WIN32
596+
sdf::Console::Instance()->SetQuiet(false);
597+
sdf::testing::ScopeExit revertSetQuiet(
598+
[]
599+
{
600+
sdf::Console::Instance()->SetQuiet(true);
601+
});
602+
#endif
603+
604+
result = sdf::SDF::EmbeddedSpec("unavailable.sdf", false);
605+
EXPECT_NE(stderr_buffer.str().find("Unable to find SDF filename"),
606+
std::string::npos);
607+
EXPECT_NE(stderr_buffer.str().find("with version"), std::string::npos);
608+
EXPECT_TRUE(result.empty());
609+
610+
// clear the contents of the buffer
611+
stderr_buffer.str("");
612+
613+
result = sdf::SDF::EmbeddedSpec("unavailable.sdf", true);
614+
EXPECT_TRUE(stderr_buffer.str().empty());
615+
EXPECT_TRUE(result.empty());
616+
}
617+
562618
/////////////////////////////////////////////////
563619
TEST(SDF, FilePath)
564620
{

0 commit comments

Comments
 (0)