Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow topic and service to construct messages from description files #428

Merged
merged 3 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions src/Factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <google/protobuf/text_format.h>

#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iostream>
Expand Down Expand Up @@ -115,40 +116,54 @@
// Split all the directories containing .desc files.
std::vector<std::string> descDirs = split(_paths, ':');

for (const std::string &descDir : descDirs)
auto loadDescFile = [this](const std::string &descFile)
{
for (DirIter dirIter(descDir); dirIter != DirIter(); ++dirIter)
// Ignore files without the correct extensions.
if (descFile.rfind(".desc") == std::string::npos &&
descFile.rfind(".proto") == std::string::npos &&
descFile.rfind(".proto.bin") == std::string::npos)
return;

// Parse the .desc file.
std::ifstream ifs(descFile);
if (!ifs.is_open())
{
// Ignore files without the .desc extension.
if ((*dirIter).rfind(".desc") == std::string::npos)
continue;
std::cerr << "DynamicFactory(): Unable to open [" << descFile << "]"
<< std::endl;
return;
}

// Parse the .desc file.
std::ifstream ifs(*dirIter);
if (!ifs.is_open())
{
std::cerr << "DynamicFactory(): Unable to open [" << *dirIter << "]"
<< std::endl;
continue;
}
google::protobuf::FileDescriptorSet fileDescriptorSet;
if (!fileDescriptorSet.ParseFromIstream(&ifs))
{
std::cerr << "DynamicFactory(): Unable to parse descriptor set from ["
<< descFile << "]" << std::endl;
return;
}

google::protobuf::FileDescriptorSet fileDescriptorSet;
if (!fileDescriptorSet.ParseFromIstream(&ifs))
// Place the real descriptors in the descriptor pool.
for (const google::protobuf::FileDescriptorProto &fileDescriptorProto :
fileDescriptorSet.file())
{
if (!this->pool.BuildFile(fileDescriptorProto))
{
std::cerr << "DynamicFactory(): Unable to parse descriptor set from ["
<< *dirIter << "]" << std::endl;
continue;
std::cerr << "DynamicFactory(). Unable to place descriptors from ["
<< descFile << "] in the descriptor pool" << std::endl;

Check warning on line 151 in src/Factory.cc

View check run for this annotation

Codecov / codecov/patch

src/Factory.cc#L151

Added line #L151 was not covered by tests
}
}
};

// Place the real descriptors in the descriptor pool.
for (const google::protobuf::FileDescriptorProto &fileDescriptorProto :
fileDescriptorSet.file())
for (const std::string &descDir : descDirs)
{
if (!std::filesystem::is_directory(descDir))
{
loadDescFile(descDir);
}
else
{
for (DirIter dirIter(descDir); dirIter != DirIter(); ++dirIter)
{
if (!pool.BuildFile(fileDescriptorProto))
{
std::cerr << "DynamicFactory(). Unable to place descriptors from ["
<< *dirIter << "] in the descriptor pool" << std::endl;
}
loadDescFile(*dirIter);
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions test/desc/testing.invalid
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";

package testing;

message Bytes
{
bytes data = 1;
}
8 changes: 8 additions & 0 deletions test/desc/testing.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";

package testing;

message Bytes
{
bytes data = 1;
}
16 changes: 16 additions & 0 deletions test/desc/testing.proto.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

C
testing/bytes.prototesting"
Bytes
data ( Rdatabproto3
ˆ
testing/message.prototesting"

FooMessage
foo ( Rfoo"

BarMessage
foo ( Rfoo"

BazMessage
foo ( Rfoobproto3
8 changes: 8 additions & 0 deletions test/desc/testing/bytes.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
syntax = "proto3";

package testing;

message Bytes
{
bytes data = 1;
}
15 changes: 15 additions & 0 deletions test/desc/testing/message.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
syntax = "proto3";

package testing;

message FooMessage {
string data = 1;
}

message BarMessage {
FooMessage foo = 1;
}

message BazMessage {
BarMessage bar = 1;
}
5 changes: 5 additions & 0 deletions test/integration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ if(MSVC)
endif()

gz_build_tests(TYPE INTEGRATION SOURCES ${tests})

if (TARGET INTEGRATION_descriptors)
target_compile_definitions(INTEGRATION_descriptors
PRIVATE GZ_MSGS_TEST_PATH="${PROJECT_SOURCE_DIR}/test")
endif()
95 changes: 95 additions & 0 deletions test/integration/descriptors.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2024 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <gtest/gtest.h>

#include <filesystem>

#include "gz/msgs/Factory.hh"

static constexpr const char * kMsgsTestPath = GZ_MSGS_TEST_PATH;

TEST(FactoryTest, DynamicFactory)
{
EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage"));

// Test loading an invalid path
{
std::filesystem::path test_path(kMsgsTestPath);
std::string paths = (test_path / "desc" / "does_not_exist.desc").string();
gz::msgs::Factory::LoadDescriptors(paths);
}
EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage"));

// Test loading an incorrect extension
{
std::filesystem::path test_path(kMsgsTestPath);
std::string paths = (test_path / "desc" / "testing.invalid").string();
gz::msgs::Factory::LoadDescriptors(paths);
}
EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage"));

// Test loading a file with invalid content
{
std::filesystem::path test_path(kMsgsTestPath);
std::string paths = (test_path / "desc" / "testing.proto").string();
gz::msgs::Factory::LoadDescriptors(paths);
}
EXPECT_EQ(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage"));

// Load a valid descriptor file with one message type
{
std::filesystem::path test_path(kMsgsTestPath);
std::string paths = (test_path / "desc" / "stringmsg.desc").string();
gz::msgs::Factory::LoadDescriptors(paths);
}

EXPECT_NE(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_EQ(nullptr, gz::msgs::Factory::New("testing.BazMessage"));

// Load a directory
{
std::filesystem::path test_path(kMsgsTestPath);
std::string paths = (test_path / "desc").string();
gz::msgs::Factory::LoadDescriptors(paths);
}

EXPECT_NE(nullptr, gz::msgs::Factory::New("example.msgs.StringMsg"));
EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.Bytes"));
EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.FooMessage"));
EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.BarMessage"));
EXPECT_NE(nullptr, gz::msgs::Factory::New("testing.BazMessage"));
}
Loading