364 lines
15 KiB
C++
364 lines
15 KiB
C++
![]() |
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||
|
//
|
||
|
// 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 "osrf_testing_tools_cpp/scope_exit.hpp"
|
||
|
#include "rcl_yaml_param_parser/parser.h"
|
||
|
#include "rcutils/error_handling.h"
|
||
|
#include "rcutils/filesystem.h"
|
||
|
#include "./time_bomb_allocator_testing_utils.h"
|
||
|
|
||
|
TEST(RclYamlParamParser, node_init_fini) {
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
EXPECT_NE(params_st, nullptr);
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
|
||
|
allocator = get_time_bomb_allocator();
|
||
|
// Bad alloc of params_st
|
||
|
set_time_bomb_allocator_calloc_count(allocator, 0);
|
||
|
// This cleans up after itself if it fails so no need to call fini()
|
||
|
EXPECT_EQ(rcl_yaml_node_struct_init(allocator), nullptr);
|
||
|
|
||
|
// Bad alloc of params_st->node_names
|
||
|
set_time_bomb_allocator_calloc_count(allocator, 1);
|
||
|
EXPECT_EQ(rcl_yaml_node_struct_init(allocator), nullptr);
|
||
|
|
||
|
// Bad alloc of params_st->params
|
||
|
set_time_bomb_allocator_calloc_count(allocator, 2);
|
||
|
EXPECT_EQ(rcl_yaml_node_struct_init(allocator), nullptr);
|
||
|
|
||
|
// Check this doesn't die.
|
||
|
rcl_yaml_node_struct_fini(nullptr);
|
||
|
}
|
||
|
|
||
|
TEST(RclYamlParamParser, node_copy) {
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
EXPECT_NE(params_st, nullptr);
|
||
|
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(nullptr));
|
||
|
|
||
|
const char node_name[] = "node name";
|
||
|
const char param_name[] = "param name";
|
||
|
const char yaml_value[] = "true";
|
||
|
EXPECT_TRUE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
rcl_params_t * copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(copy, nullptr);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
|
||
|
params_st->allocator = get_time_bomb_allocator();
|
||
|
|
||
|
// init of out_params_st fails
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, 0);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, 1);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
constexpr int expected_num_calloc_calls = 5;
|
||
|
for (int i = 0; i < expected_num_calloc_calls; ++i) {
|
||
|
// Check various locations for allocation failures
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, i);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
}
|
||
|
// Check that the expected number of calloc calls occur
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, expected_num_calloc_calls);
|
||
|
copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(nullptr, copy);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
|
||
|
// Reset calloc countdown
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, -1);
|
||
|
|
||
|
constexpr int expected_num_malloc_calls = 3;
|
||
|
for (int i = 0; i < expected_num_malloc_calls; ++i) {
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, i);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
}
|
||
|
|
||
|
// Check that the expected number of malloc calls occur
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, expected_num_malloc_calls);
|
||
|
copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(nullptr, copy);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
|
||
|
constexpr int num_malloc_calls_until_copy_param = 2;
|
||
|
|
||
|
// Check integer value
|
||
|
int64_t temp_int = 42;
|
||
|
if (params_st->params->parameter_values[0].bool_value != nullptr) {
|
||
|
// Since this bool was allocated above in rcl_parse_yaml_value, it needs to be freed.
|
||
|
params_st->allocator.deallocate(
|
||
|
params_st->params->parameter_values[0].bool_value, params_st->allocator.state);
|
||
|
params_st->params->parameter_values[0].bool_value = nullptr;
|
||
|
}
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].integer_value = &temp_int;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
params_st->params->parameter_values[0].integer_value = nullptr;
|
||
|
|
||
|
// Check double value
|
||
|
double temp_double = 42.0;
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].double_value = &temp_double;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
params_st->params->parameter_values[0].double_value = nullptr;
|
||
|
|
||
|
// Check string value
|
||
|
char temp_string[] = "stringy string";
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].string_value = temp_string;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
params_st->params->parameter_values[0].string_value = nullptr;
|
||
|
|
||
|
// Check bool_array_value array pointer is allocated
|
||
|
bool bool_array[] = {true};
|
||
|
rcl_bool_array_s temp_bool_array = {bool_array, 1};
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].bool_array_value = &temp_bool_array;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
// Check bool_array_value->values is allocated
|
||
|
set_time_bomb_allocator_malloc_count(
|
||
|
params_st->allocator, num_malloc_calls_until_copy_param + 1);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
// Check bool_array_value->values is set to null if size is 0
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, -1);
|
||
|
params_st->params->parameter_values[0].bool_array_value->size = 0u;
|
||
|
copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(nullptr, copy);
|
||
|
EXPECT_EQ(nullptr, copy->params->parameter_values[0].bool_array_value->values);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
params_st->params->parameter_values[0].bool_array_value = nullptr;
|
||
|
|
||
|
// Check integer_array array pointer is allocated
|
||
|
int64_t integer_array[] = {42};
|
||
|
rcl_int64_array_s temp_integer_array = {integer_array, 1};
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].integer_array_value = &temp_integer_array;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
// Check integer_array->values is allocated
|
||
|
set_time_bomb_allocator_malloc_count(
|
||
|
params_st->allocator, num_malloc_calls_until_copy_param + 1);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
// Check integer_array->values is set to null if size is 0
|
||
|
params_st->params->parameter_values[0].integer_array_value->size = 0u;
|
||
|
copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(nullptr, copy);
|
||
|
EXPECT_EQ(nullptr, copy->params->parameter_values[0].integer_array_value->values);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
params_st->params->parameter_values[0].integer_array_value = nullptr;
|
||
|
|
||
|
|
||
|
double double_array[] = {42.0};
|
||
|
rcl_double_array_s temp_double_array = {double_array, 1};
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].double_array_value = &temp_double_array;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
set_time_bomb_allocator_malloc_count(
|
||
|
params_st->allocator, num_malloc_calls_until_copy_param + 1);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
|
||
|
params_st->params->parameter_values[0].double_array_value->size = 0u;
|
||
|
copy = rcl_yaml_node_struct_copy(params_st);
|
||
|
EXPECT_NE(nullptr, copy);
|
||
|
EXPECT_EQ(nullptr, copy->params->parameter_values[0].double_array_value->values);
|
||
|
rcl_yaml_node_struct_fini(copy);
|
||
|
params_st->params->parameter_values[0].double_array_value = nullptr;
|
||
|
|
||
|
char s[] = "stringy string";
|
||
|
char * string_array[] = {s};
|
||
|
rcutils_string_array_t temp_string_array = {1, string_array, allocator};
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, num_malloc_calls_until_copy_param);
|
||
|
params_st->params->parameter_values[0].string_array_value = &temp_string_array;
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
params_st->params->parameter_values[0].string_array_value = nullptr;
|
||
|
|
||
|
for (int i = 0; i < 5; ++i) {
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, i);
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_copy(params_st));
|
||
|
}
|
||
|
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
}
|
||
|
// // This just tests a couple of basic failures that test_parse_yaml.cpp misses.
|
||
|
// // See that file for more thorough testing of bad yaml files
|
||
|
TEST(RclYamlParamParser, test_file) {
|
||
|
// file path is null
|
||
|
EXPECT_FALSE(rcl_parse_yaml_file(nullptr, nullptr));
|
||
|
const char bad_file_path[] = "not_a_file.yaml";
|
||
|
|
||
|
// params_st is null
|
||
|
EXPECT_FALSE(rcl_parse_yaml_file(bad_file_path, nullptr));
|
||
|
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
EXPECT_FALSE(rcl_parse_yaml_file(bad_file_path, params_st));
|
||
|
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
}
|
||
|
|
||
|
TEST(RclYamlParamParser, test_parse_yaml_value) {
|
||
|
const char node_name[] = "node name";
|
||
|
const char param_name[] = "param name";
|
||
|
const char yaml_value[] = "true";
|
||
|
const char empty_string[] = "\0";
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
EXPECT_NE(params_st, nullptr);
|
||
|
|
||
|
// Check null arguments
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(nullptr, param_name, yaml_value, params_st));
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, nullptr, yaml_value, params_st));
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, nullptr, params_st));
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, yaml_value, nullptr));
|
||
|
|
||
|
// Check strings are empty
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(empty_string, param_name, yaml_value, params_st));
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, empty_string, yaml_value, params_st));
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, empty_string, params_st));
|
||
|
|
||
|
// Check allocating params_st->node_names[*node_idx] fails
|
||
|
params_st->allocator = get_time_bomb_allocator();
|
||
|
set_time_bomb_allocator_malloc_count(params_st->allocator, 0);
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
// Check allocating node_params->parameter_names fails
|
||
|
allocator = get_time_bomb_allocator();
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, 0);
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
// Check allocating node_params->parameter_values fails
|
||
|
allocator = get_time_bomb_allocator();
|
||
|
set_time_bomb_allocator_calloc_count(params_st->allocator, 1);
|
||
|
EXPECT_FALSE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
allocator = rcutils_get_default_allocator();
|
||
|
EXPECT_TRUE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
}
|
||
|
|
||
|
TEST(RclYamlParamParser, test_yaml_node_struct_get) {
|
||
|
const char node_name[] = "node name";
|
||
|
const char param_name[] = "param name";
|
||
|
const char yaml_value[] = "true";
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
ASSERT_NE(params_st, nullptr);
|
||
|
EXPECT_TRUE(rcl_parse_yaml_value(node_name, param_name, yaml_value, params_st));
|
||
|
|
||
|
// Check null arguments
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_get(nullptr, param_name, params_st));
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_get(node_name, nullptr, params_st));
|
||
|
EXPECT_EQ(nullptr, rcl_yaml_node_struct_get(node_name, param_name, nullptr));
|
||
|
|
||
|
rcl_variant_t * result = rcl_yaml_node_struct_get(node_name, param_name, params_st);
|
||
|
ASSERT_NE(nullptr, result->bool_value);
|
||
|
EXPECT_TRUE(*result->bool_value);
|
||
|
|
||
|
EXPECT_EQ(nullptr, result->integer_value);
|
||
|
EXPECT_EQ(nullptr, result->double_value);
|
||
|
EXPECT_EQ(nullptr, result->string_value);
|
||
|
EXPECT_EQ(nullptr, result->byte_array_value);
|
||
|
EXPECT_EQ(nullptr, result->bool_array_value);
|
||
|
EXPECT_EQ(nullptr, result->integer_array_value);
|
||
|
EXPECT_EQ(nullptr, result->double_array_value);
|
||
|
EXPECT_EQ(nullptr, result->string_array_value);
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
}
|
||
|
|
||
|
// Just testing basic parameters, this is exercised more in test_parse_yaml.cpp
|
||
|
TEST(RclYamlParamParser, test_yaml_node_struct_print) {
|
||
|
rcl_yaml_node_struct_print(nullptr);
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
rcl_params_t * params_st = rcl_yaml_node_struct_init(allocator);
|
||
|
rcl_yaml_node_struct_print(params_st);
|
||
|
rcl_yaml_node_struct_fini(params_st);
|
||
|
}
|
||
|
|
||
|
TEST(RclYamlParamParser, test_parse_file_with_bad_allocator) {
|
||
|
char cur_dir[1024];
|
||
|
rcutils_reset_error();
|
||
|
EXPECT_TRUE(rcutils_get_cwd(cur_dir, 1024)) << rcutils_get_error_string().str;
|
||
|
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||
|
char * test_path = rcutils_join_path(cur_dir, "test", allocator);
|
||
|
ASSERT_TRUE(NULL != test_path) << rcutils_get_error_string().str;
|
||
|
OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT(
|
||
|
{
|
||
|
allocator.deallocate(test_path, allocator.state);
|
||
|
});
|
||
|
char * path = rcutils_join_path(test_path, "correct_config.yaml", allocator);
|
||
|
ASSERT_TRUE(NULL != path) << rcutils_get_error_string().str;
|
||
|
OSRF_TESTING_TOOLS_CPP_SCOPE_EXIT(
|
||
|
{
|
||
|
allocator.deallocate(path, allocator.state);
|
||
|
});
|
||
|
ASSERT_TRUE(rcutils_exists(path)) << "No test YAML file found at " << path;
|
||
|
|
||
|
rcl_params_t * params_hdl = rcl_yaml_node_struct_init(allocator);
|
||
|
EXPECT_TRUE(rcl_parse_yaml_file(path, params_hdl));
|
||
|
rcl_yaml_node_struct_fini(params_hdl);
|
||
|
params_hdl = NULL;
|
||
|
|
||
|
// Check sporadic failing malloc calls
|
||
|
for (int i = 0; i < 100; ++i) {
|
||
|
params_hdl = rcl_yaml_node_struct_init(get_time_bomb_allocator());
|
||
|
ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str;
|
||
|
|
||
|
set_time_bomb_allocator_malloc_count(params_hdl->allocator, i);
|
||
|
bool res = rcl_parse_yaml_file(path, params_hdl);
|
||
|
// Not verifying res is true or false here, because eventually it will come back with an ok
|
||
|
// result. We're just trying to make sure that bad allocations are properly handled
|
||
|
if (res) {
|
||
|
// This is already freed in the case of a non-ok error in rcl_parse_yaml_file
|
||
|
rcl_yaml_node_struct_fini(params_hdl);
|
||
|
params_hdl = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check sporadic failing calloc calls
|
||
|
for (int i = 0; i < 100; ++i) {
|
||
|
params_hdl = rcl_yaml_node_struct_init(get_time_bomb_allocator());
|
||
|
ASSERT_TRUE(NULL != params_hdl) << rcutils_get_error_string().str;
|
||
|
|
||
|
set_time_bomb_allocator_calloc_count(params_hdl->allocator, i);
|
||
|
|
||
|
bool res = rcl_parse_yaml_file(path, params_hdl);
|
||
|
// Not verifying res is true or false here, because eventually it will come back with an ok
|
||
|
// result. We're just trying to make sure that bad allocations are properly handled
|
||
|
if (res) {
|
||
|
// This is already freed in the case of a non-ok error in rcl_parse_yaml_file
|
||
|
rcl_yaml_node_struct_fini(params_hdl);
|
||
|
params_hdl = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int32_t main(int32_t argc, char ** argv)
|
||
|
{
|
||
|
::testing::InitGoogleTest(&argc, argv);
|
||
|
return RUN_ALL_TESTS();
|
||
|
}
|