Skip to content

Commit c1bfac6

Browse files
authored
More rcl_logging_spdlog tests (#40)
Signed-off-by: Scott K Logan <[email protected]>
1 parent b362128 commit c1bfac6

File tree

4 files changed

+259
-13
lines changed

4 files changed

+259
-13
lines changed

rcl_logging_spdlog/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ if(BUILD_TESTING)
4747
ament_lint_auto_find_test_dependencies()
4848

4949
find_package(ament_cmake_gtest REQUIRED)
50+
find_package(rcpputils REQUIRED)
5051
ament_add_gtest(test_logging_interface test/test_logging_interface.cpp)
5152
if(TARGET test_logging_interface)
5253
target_link_libraries(test_logging_interface ${PROJECT_NAME})
54+
ament_target_dependencies(test_logging_interface rcpputils)
5355
endif()
5456
endif()
5557

rcl_logging_spdlog/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
<test_depend>ament_lint_auto</test_depend>
2222
<test_depend>ament_lint_common</test_depend>
23+
<test_depend>rcpputils</test_depend>
2324

2425
<member_of_group>rcl_logging_packages</member_of_group>
2526

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2020 Open Source Robotics Foundation, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef FIXTURES_HPP_
16+
#define FIXTURES_HPP_
17+
18+
#include <rcpputils/filesystem_helper.hpp>
19+
#include <rcutils/allocator.h>
20+
#include <rcutils/error_handling.h>
21+
#include <rcutils/get_env.h>
22+
#include <rcutils/process.h>
23+
#include <rcutils/types/string_array.h>
24+
25+
#include <limits.h>
26+
#include <string>
27+
28+
#include "gtest/gtest.h"
29+
30+
#ifdef _WIN32
31+
#define popen _popen
32+
#define pclose _pclose
33+
#define DIR_CMD "dir /B"
34+
#else
35+
#define DIR_CMD "ls -d"
36+
#endif
37+
38+
namespace fs = rcpputils::fs;
39+
40+
class AllocatorTest : public ::testing::Test
41+
{
42+
public:
43+
AllocatorTest()
44+
: allocator(rcutils_get_default_allocator()),
45+
bad_allocator(get_bad_allocator()),
46+
invalid_allocator(rcutils_get_zero_initialized_allocator())
47+
{
48+
}
49+
50+
rcutils_allocator_t allocator;
51+
rcutils_allocator_t bad_allocator;
52+
rcutils_allocator_t invalid_allocator;
53+
54+
private:
55+
static rcutils_allocator_t get_bad_allocator()
56+
{
57+
rcutils_allocator_t bad_allocator = rcutils_get_default_allocator();
58+
bad_allocator.allocate = AllocatorTest::bad_malloc;
59+
bad_allocator.reallocate = AllocatorTest::bad_realloc;
60+
return bad_allocator;
61+
}
62+
63+
static void * bad_malloc(size_t, void *)
64+
{
65+
return nullptr;
66+
}
67+
68+
static void * bad_realloc(void *, size_t, void *)
69+
{
70+
return nullptr;
71+
}
72+
};
73+
74+
class LoggingTest : public AllocatorTest
75+
{
76+
public:
77+
LoggingTest()
78+
: AllocatorTest()
79+
{
80+
}
81+
82+
fs::path find_single_log()
83+
{
84+
fs::path log_dir = get_log_dir();
85+
std::stringstream dir_command;
86+
dir_command << DIR_CMD << " " << (log_dir / get_expected_log_prefix()).string() << "*";
87+
88+
FILE * fp = popen(dir_command.str().c_str(), "r");
89+
if (nullptr == fp) {
90+
throw std::runtime_error("Failed to glob for log files");
91+
}
92+
93+
#ifdef _WIN32
94+
char raw_line[MAX_PATH];
95+
#else
96+
char raw_line[PATH_MAX];
97+
#endif
98+
char * ret = fgets(raw_line, sizeof(raw_line), fp);
99+
pclose(fp);
100+
if (nullptr == ret) {
101+
throw std::runtime_error("No log files were found");
102+
}
103+
104+
std::string line(raw_line);
105+
fs::path line_path(line.substr(0, line.find_last_not_of(" \t\r\n") + 1));
106+
// This should be changed once ros2/rcpputils#68 is resolved
107+
return line_path.is_absolute() ? line_path : log_dir / line_path;
108+
}
109+
110+
private:
111+
std::string get_expected_log_prefix()
112+
{
113+
char * exe_name = rcutils_get_executable_name(allocator);
114+
if (nullptr == exe_name) {
115+
throw std::runtime_error("Failed to determine executable name");
116+
}
117+
std::stringstream prefix;
118+
prefix << exe_name << "_" << rcutils_get_pid() << "_";
119+
allocator.deallocate(exe_name, allocator.state);
120+
return prefix.str();
121+
}
122+
123+
fs::path get_log_dir()
124+
{
125+
return fs::path(rcutils_get_home_dir()) / ".ros" / "log";
126+
}
127+
};
128+
129+
#endif // FIXTURES_HPP_

rcl_logging_spdlog/test/test_logging_interface.cpp

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,79 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
#include <rcpputils/filesystem_helper.hpp>
16+
#include <rcpputils/get_env.hpp>
1517
#include <rcutils/allocator.h>
18+
#include <rcutils/env.h>
1619
#include <rcutils/error_handling.h>
1720
#include <rcutils/logging.h>
18-
#include "rcl_logging_spdlog/logging_interface.h"
21+
22+
#include <limits.h>
23+
#include <fstream>
24+
#include <string>
25+
26+
#include "fixtures.hpp"
1927
#include "gtest/gtest.h"
28+
#include "rcl_logging_spdlog/logging_interface.h"
2029

21-
static void * bad_malloc(size_t, void *)
30+
namespace fs = rcpputils::fs;
31+
32+
const int logger_levels[] =
2233
{
23-
return nullptr;
24-
}
34+
RCUTILS_LOG_SEVERITY_UNSET,
35+
RCUTILS_LOG_SEVERITY_DEBUG,
36+
RCUTILS_LOG_SEVERITY_INFO,
37+
RCUTILS_LOG_SEVERITY_WARN,
38+
RCUTILS_LOG_SEVERITY_ERROR,
39+
RCUTILS_LOG_SEVERITY_FATAL,
40+
};
41+
42+
// This is a helper class that resets an environment
43+
// variable when leaving scope
44+
class RestoreEnvVar
45+
{
46+
public:
47+
explicit RestoreEnvVar(const std::string & name)
48+
: name_(name),
49+
value_(rcpputils::get_env_var(name.c_str()))
50+
{
51+
}
52+
53+
~RestoreEnvVar()
54+
{
55+
if (!rcutils_set_env(name_.c_str(), value_.c_str())) {
56+
std::cerr << "Failed to restore value of environment variable: " << name_ << std::endl;
57+
}
58+
}
2559

26-
TEST(logging_interface, init_invalid)
60+
private:
61+
const std::string name_;
62+
const std::string value_;
63+
};
64+
65+
// TODO(cottsay): Remove when ros2/rcpputils#63 is resolved
66+
static fs::path current_path()
2767
{
28-
rcutils_allocator_t allocator = rcutils_get_default_allocator();
29-
rcutils_allocator_t bad_allocator = rcutils_get_default_allocator();
30-
rcutils_allocator_t invalid_allocator = rcutils_get_zero_initialized_allocator();
31-
bad_allocator.allocate = bad_malloc;
68+
#ifdef _WIN32
69+
#ifdef UNICODE
70+
#error "rcpputils::fs does not support Unicode paths"
71+
#endif
72+
char cwd[MAX_PATH];
73+
if (nullptr == _getcwd(cwd, MAX_PATH)) {
74+
#else
75+
char cwd[PATH_MAX];
76+
if (nullptr == getcwd(cwd, PATH_MAX)) {
77+
#endif
78+
std::error_code ec{errno, std::system_category()};
79+
errno = 0;
80+
throw std::system_error{ec, "cannot get current working directory"};
81+
}
3282

83+
return fs::path(cwd);
84+
}
85+
86+
TEST_F(LoggingTest, init_invalid)
87+
{
3388
// Config files are not supported by spdlog
3489
EXPECT_EQ(2, rcl_logging_external_initialize("anything", allocator));
3590
rcutils_reset_error();
@@ -39,12 +94,71 @@ TEST(logging_interface, init_invalid)
3994
rcutils_reset_error();
4095
}
4196

42-
TEST(logging_interface, full_cycle)
97+
TEST_F(LoggingTest, init_failure)
98+
{
99+
RestoreEnvVar home_var("HOME");
100+
RestoreEnvVar userprofile_var("USERPROFILE");
101+
102+
// No home directory to write log to
103+
ASSERT_EQ(true, rcutils_set_env("HOME", nullptr));
104+
ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr));
105+
EXPECT_EQ(2, rcl_logging_external_initialize(nullptr, allocator));
106+
rcutils_reset_error();
107+
108+
// Force failure to create directories
109+
fs::path fake_home = current_path() / "fake_home_dir";
110+
ASSERT_TRUE(fs::create_directories(fake_home));
111+
ASSERT_EQ(true, rcutils_set_env("HOME", fake_home.string().c_str()));
112+
113+
// ...fail to create .ros dir
114+
fs::path ros_dir = fake_home / ".ros";
115+
std::fstream(ros_dir.string(), std::ios_base::out).close();
116+
EXPECT_EQ(2, rcl_logging_external_initialize(nullptr, allocator));
117+
ASSERT_TRUE(fs::remove(ros_dir));
118+
119+
// ...fail to create .ros/log dir
120+
ASSERT_TRUE(fs::create_directories(ros_dir));
121+
fs::path ros_log_dir = ros_dir / "log";
122+
std::fstream(ros_log_dir.string(), std::ios_base::out).close();
123+
EXPECT_EQ(2, rcl_logging_external_initialize(nullptr, allocator));
124+
ASSERT_TRUE(fs::remove(ros_log_dir));
125+
ASSERT_TRUE(fs::remove(ros_dir));
126+
127+
ASSERT_TRUE(fs::remove(fake_home));
128+
}
129+
130+
TEST_F(LoggingTest, full_cycle)
43131
{
44-
rcutils_allocator_t allocator = rcutils_get_default_allocator();
132+
ASSERT_EQ(0, rcl_logging_external_initialize(nullptr, allocator));
45133

134+
// Make sure we can call initialize more than once
46135
ASSERT_EQ(0, rcl_logging_external_initialize(nullptr, allocator));
47-
EXPECT_EQ(0, rcl_logging_external_set_logger_level(nullptr, RCUTILS_LOG_SEVERITY_INFO));
48-
rcl_logging_external_log(RCUTILS_LOG_SEVERITY_INFO, nullptr, "Log Message");
136+
137+
std::stringstream expected_log;
138+
for (int level : logger_levels) {
139+
EXPECT_EQ(0, rcl_logging_external_set_logger_level(nullptr, level));
140+
141+
for (int severity : logger_levels) {
142+
std::stringstream ss;
143+
ss << "Message of severity " << severity << " at level " << level;
144+
rcl_logging_external_log(severity, nullptr, ss.str().c_str());
145+
146+
if (severity >= level) {
147+
expected_log << ss.str() << std::endl;
148+
} else if (severity == 0 && level == 10) {
149+
// This is a special case - not sure what the right behavior is
150+
expected_log << ss.str() << std::endl;
151+
}
152+
}
153+
}
154+
49155
EXPECT_EQ(0, rcl_logging_external_shutdown());
156+
157+
std::string log_file_path = find_single_log().string();
158+
std::ifstream log_file(log_file_path);
159+
std::stringstream actual_log;
160+
actual_log << log_file.rdbuf();
161+
EXPECT_EQ(
162+
expected_log.str(),
163+
actual_log.str()) << "Unexpected log contents in " << log_file_path;
50164
}

0 commit comments

Comments
 (0)