diff --git a/libs/dfi/gtest/CMakeLists.txt b/libs/dfi/gtest/CMakeLists.txt index 9d3def82c..187cc4404 100644 --- a/libs/dfi/gtest/CMakeLists.txt +++ b/libs/dfi/gtest/CMakeLists.txt @@ -42,11 +42,13 @@ if (EI_TESTS) add_executable(test_dfi_with_ei src/dyn_interface_ei_tests.cc src/dyn_common_ei_tests.cc + src/dyn_type_ei_tests.cc ) target_link_libraries(test_dfi_with_ei PRIVATE dfi_cut Celix::malloc_ei Celix::stdio_ei + Celix::string_ei GTest::gtest GTest::gtest_main ) add_test(NAME run_test_dfi_with_ei COMMAND test_dfi_with_ei) diff --git a/libs/dfi/gtest/src/dyn_type_ei_tests.cc b/libs/dfi/gtest/src/dyn_type_ei_tests.cc new file mode 100644 index 000000000..7a2deb622 --- /dev/null +++ b/libs/dfi/gtest/src/dyn_type_ei_tests.cc @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "dyn_type.h" +#include "celix_err.h" +#include "malloc_ei.h" +#include "stdio_ei.h" +#include "string_ei.h" + +#include +#include +#include + +class DynTypeErrorInjectionTestSuite : public ::testing::Test { +public: + DynTypeErrorInjectionTestSuite() { + } + + ~DynTypeErrorInjectionTestSuite() override { + celix_ei_expect_strdup(nullptr, 0, nullptr); + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_ei_expect_fmemopen(nullptr, 0, nullptr); + } + // delete other constructors and assign operators + DynTypeErrorInjectionTestSuite(DynTypeErrorInjectionTestSuite const&) = delete; + DynTypeErrorInjectionTestSuite(DynTypeErrorInjectionTestSuite&&) = delete; + DynTypeErrorInjectionTestSuite& operator=(DynTypeErrorInjectionTestSuite const&) = delete; + DynTypeErrorInjectionTestSuite& operator=(DynTypeErrorInjectionTestSuite&&) = delete; +}; + +TEST_F(DynTypeErrorInjectionTestSuite, ParseTypeErrors) { + dyn_type *type = NULL; + const char* descriptor = "{D{DD b_1 b_2}I a b c}"; + + // fail to open memory as stream + celix_ei_expect_fmemopen((void*)dynType_parseWithStr, 0, nullptr); + int status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + std::string msg = "Error creating mem stream for descriptor string. "; + msg += strerror(ENOMEM); + ASSERT_STREQ(msg.c_str(), celix_err_popLastError()); + + // fail to allocate dyn_type + celix_ei_expect_calloc((void*)dynType_parseWithStr, 2, nullptr); + status = dynType_parseWithStr(descriptor, NULL, NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error allocating memory for type", celix_err_popLastError()); + + // fail to duplicate type name + celix_ei_expect_strdup((void*)dynType_parseWithStr, 1, nullptr); + status = dynType_parseWithStr(descriptor, "hello", NULL, &type); + ASSERT_NE(0, status); + ASSERT_STREQ("Error strdup'ing name 'hello'", celix_err_popLastError()); +} diff --git a/libs/dfi/include/dyn_type.h b/libs/dfi/include/dyn_type.h index 1ab2da416..635004b27 100644 --- a/libs/dfi/include/dyn_type.h +++ b/libs/dfi/include/dyn_type.h @@ -145,6 +145,12 @@ struct meta_entry { */ CELIX_DFI_EXPORT int dynType_parse(FILE* descriptorStream, const char* name, const struct types_head* refTypes, dyn_type** type); +/** + * @brief Parses a descriptor stream and creates a dyn_type (dynamic type) of a given name. + * Similar to dynType_parse except that the ownership of the given name is taken by the dyn type. + */ +CELIX_DFI_EXPORT int dynType_parseOfName(FILE* descriptorStream, char* name, const struct types_head* refTypes, dyn_type** type); + /** * Parses a descriptor string and creates a dyn_type (dynamic type). * If successful the type output argument points to the newly created dyn type. diff --git a/libs/dfi/src/dyn_interface.c b/libs/dfi/src/dyn_interface.c index 6ac50a8a7..4d105f649 100644 --- a/libs/dfi/src/dyn_interface.c +++ b/libs/dfi/src/dyn_interface.c @@ -177,7 +177,7 @@ static int dynInterface_parseTypes(dyn_interface_type *intf, FILE *stream) { } celix_autoptr(dyn_type) type = NULL; - if ((status = dynType_parse(stream, name, &intf->types, &type)) != OK) { + if ((status = dynType_parseOfName(stream, celix_steal_ptr(name), &intf->types, &type)) != OK) { return status; } if ((status = dynCommon_eatChar(stream, '\n')) != OK) { diff --git a/libs/dfi/src/dyn_message.c b/libs/dfi/src/dyn_message.c index 886428bdc..a8b49bd39 100644 --- a/libs/dfi/src/dyn_message.c +++ b/libs/dfi/src/dyn_message.c @@ -19,6 +19,7 @@ #include "dyn_message.h" #include "celix_err.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -179,7 +180,7 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { while (peek != ':' && peek != EOF) { ungetc(peek, stream); - char *name = NULL; + celix_autofree char* name = NULL; status = dynCommon_parseName(stream, &name); if (status == OK) { @@ -188,7 +189,7 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { dyn_type *type = NULL; if (status == OK) { - status = dynType_parse(stream, name, &msg->types, &type); + status = dynType_parseOfName(stream, celix_steal_ptr(name), &msg->types, &type); } if (status == OK) { @@ -207,10 +208,6 @@ static int dynMessage_parseTypes(dyn_message_type *msg, FILE *stream) { } } - if (name != NULL) { - free(name); - } - if (status != OK) { if (type != NULL) { dynType_destroy(type); diff --git a/libs/dfi/src/dyn_type.c b/libs/dfi/src/dyn_type.c index c533b8f39..9b995423e 100644 --- a/libs/dfi/src/dyn_type.c +++ b/libs/dfi/src/dyn_type.c @@ -19,9 +19,10 @@ #include "dyn_type.h" #include "dyn_type_common.h" -#include "dyn_type_common.h" #include "dyn_common.h" #include "celix_err.h" +#include "celix_stdio_cleanup.h" +#include "celix_stdlib_cleanup.h" #include #include @@ -35,6 +36,7 @@ static const int MEM_ERROR = 2; static const int PARSE_ERROR = 3; static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result); static void dynType_clear(dyn_type* type); static void dynType_clearComplex(dyn_type* type); static void dynType_clearSequence(dyn_type* type); @@ -84,56 +86,59 @@ int dynType_parse(FILE* descriptorStream, const char* name, const struct types_h return dynType_parseWithStream(descriptorStream, name, NULL, refTypes, type); } + +int dynType_parseOfName(FILE* descriptorStream, char* name, const struct types_head* refTypes, dyn_type** type) { + return dynType_parseWithStreamOfName(descriptorStream, name, NULL, refTypes, type); +} + int dynType_parseWithStr(const char* descriptor, const char* name, const struct types_head* refTypes, dyn_type** type) { int status = OK; - FILE* stream = fmemopen((char *)descriptor, strlen(descriptor) + 1, "r"); - if (stream != NULL) { - status = dynType_parseWithStream(stream, name, NULL, refTypes, type); - if (status == OK) { - int c = fgetc(stream); - if (c != '\0' && c != EOF) { - status = PARSE_ERROR; - dynType_destroy(*type); - *type = NULL; - celix_err_pushf("Expected EOF got %c", c); - } - } - fclose(stream); - } else { - status = ERROR; + celix_autoptr(FILE) stream = fmemopen((char *)descriptor, strlen(descriptor), "r"); + if (stream == NULL) { celix_err_pushf("Error creating mem stream for descriptor string. %s", strerror(errno)); + return ERROR; + } + celix_autoptr(dyn_type) result = NULL; + if ((status = dynType_parseWithStream(stream, name, NULL, refTypes, &result)) != OK) { + return status; } + if (dynCommon_eatChar(stream, EOF) != 0) { + return PARSE_ERROR; + } + *type = celix_steal_ptr(result); return status; } static int dynType_parseWithStream(FILE* stream, const char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { - int status = OK; - dyn_type* type = calloc(1, sizeof(*type)); - if (type != NULL) { - type->parent = parent; - type->type = DYN_TYPE_INVALID; - type->referenceTypes = refTypes; - TAILQ_INIT(&type->nestedTypesHead); - TAILQ_INIT(&type->metaProperties); - if (name != NULL) { - type->name = strdup(name); - if (type->name == NULL) { - status = MEM_ERROR; - celix_err_pushf("Error strdup'ing name '%s'\n", name); - } - } - if (status == OK) { - status = dynType_parseAny(stream, type); - } - if (status == OK) { - *result = type; - } else { - dynType_destroy(type); + char* typeName = NULL; + if (name != NULL) { + typeName = strdup(name); + if (typeName == NULL) { + celix_err_pushf("Error strdup'ing name '%s'", name); + return MEM_ERROR; } - } else { - status = MEM_ERROR; - celix_err_pushf("Error allocating memory for type"); } + return dynType_parseWithStreamOfName(stream, typeName, parent, refTypes, result); +} + +static int dynType_parseWithStreamOfName(FILE* stream, char* name, dyn_type* parent, const struct types_head* refTypes, dyn_type** result) { + int status = OK; + celix_autofree char* typeName = name; + celix_autoptr(dyn_type) type = calloc(1, sizeof(*type)); + if (type == NULL) { + celix_err_push("Error allocating memory for type"); + return MEM_ERROR; + } + type->parent = parent; + type->type = DYN_TYPE_INVALID; + type->referenceTypes = refTypes; + TAILQ_INIT(&type->nestedTypesHead); + TAILQ_INIT(&type->metaProperties); + type->name = celix_steal_ptr(typeName); + if ((status = dynType_parseAny(stream, type)) != OK) { + return status; + } + *result = celix_steal_ptr(type); return status; } diff --git a/libs/error_injector/stdio/CMakeLists.txt b/libs/error_injector/stdio/CMakeLists.txt index 667969093..2e53d8099 100644 --- a/libs/error_injector/stdio/CMakeLists.txt +++ b/libs/error_injector/stdio/CMakeLists.txt @@ -32,5 +32,6 @@ target_link_options(stdio_ei INTERFACE LINKER:--wrap,fputs LINKER:--wrap,fclose LINKER:--wrap,fgetc + LINKER:--wrap,fmemopen ) add_library(Celix::stdio_ei ALIAS stdio_ei) diff --git a/libs/error_injector/stdio/include/stdio_ei.h b/libs/error_injector/stdio/include/stdio_ei.h index 4ea80e0cc..f8ca53b49 100644 --- a/libs/error_injector/stdio/include/stdio_ei.h +++ b/libs/error_injector/stdio/include/stdio_ei.h @@ -48,6 +48,8 @@ CELIX_EI_DECLARE(fclose, int); CELIX_EI_DECLARE(fgetc, int); +CELIX_EI_DECLARE(fmemopen, FILE *); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/stdio/src/stdio_ei.cc b/libs/error_injector/stdio/src/stdio_ei.cc index 4a0c5d936..fca838384 100644 --- a/libs/error_injector/stdio/src/stdio_ei.cc +++ b/libs/error_injector/stdio/src/stdio_ei.cc @@ -21,54 +21,54 @@ #include "stdio_ei.h" extern "C" { -FILE* __real_fopen(const char* __filename, const char* __modes); +FILE *__real_fopen(const char *__filename, const char *__modes); CELIX_EI_DEFINE(fopen, FILE*) -FILE* __wrap_fopen(const char* __filename, const char* __modes) { +FILE *__wrap_fopen(const char *__filename, const char *__modes) { errno = EMFILE; CELIX_EI_IMPL(fopen); errno = 0; return __real_fopen(__filename, __modes); } -size_t __real_fwrite(const void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s); +size_t __real_fwrite(const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); CELIX_EI_DEFINE(fwrite, size_t) -size_t __wrap_fwrite(const void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s) { +size_t __wrap_fwrite(const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s) { errno = ENOSPC; CELIX_EI_IMPL(fwrite); errno = 0; return __real_fwrite(__ptr, __size, __n, __s); } -int __real_remove(const char* __filename); +int __real_remove(const char *__filename); CELIX_EI_DEFINE(remove, int) -int __wrap_remove(const char* __filename) { +int __wrap_remove(const char *__filename) { errno = EACCES; CELIX_EI_IMPL(remove); errno = 0; return __real_remove(__filename); } -FILE* __real_open_memstream(char** __bufloc, size_t* __sizeloc); +FILE *__real_open_memstream(char **__bufloc, size_t *__sizeloc); CELIX_EI_DEFINE(open_memstream, FILE*) -FILE* __wrap_open_memstream(char** __bufloc, size_t* __sizeloc) { +FILE *__wrap_open_memstream(char **__bufloc, size_t *__sizeloc) { errno = ENOMEM; CELIX_EI_IMPL(open_memstream); errno = 0; return __real_open_memstream(__bufloc, __sizeloc); } -int __real_fseek(FILE* __stream, long int __off, int __whence); +int __real_fseek(FILE *__stream, long int __off, int __whence); CELIX_EI_DEFINE(fseek, int) -int __wrap_fseek(FILE* __stream, long int __off, int __whence) { +int __wrap_fseek(FILE *__stream, long int __off, int __whence) { errno = EACCES; CELIX_EI_IMPL(fseek); errno = 0; return __real_fseek(__stream, __off, __whence); } -long __real_ftell(FILE* __stream); +long __real_ftell(FILE *__stream); CELIX_EI_DEFINE(ftell, long) -long __wrap_ftell(FILE* __stream) { +long __wrap_ftell(FILE *__stream) { if (ftell_ret == -1) { errno = EACCES; } @@ -77,30 +77,30 @@ long __wrap_ftell(FILE* __stream) { return __real_ftell(__stream); } -size_t __real_fread(void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s); +size_t __real_fread(void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); CELIX_EI_DEFINE(fread, size_t) -size_t __wrap_fread(void* __restrict __ptr, size_t __size, size_t __n, FILE* __restrict __s) { +size_t __wrap_fread(void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s) { CELIX_EI_IMPL(fread); return __real_fread(__ptr, __size, __n, __s); } -int __real_fputc(int __c, FILE* __stream); +int __real_fputc(int __c, FILE *__stream); CELIX_EI_DEFINE(fputc, int) -int __wrap_fputc(int __c, FILE* __stream) { +int __wrap_fputc(int __c, FILE *__stream) { CELIX_EI_IMPL(fputc); return __real_fputc(__c, __stream); } -int __real_fputs(const char* __s, FILE* __stream); +int __real_fputs(const char *__s, FILE *__stream); CELIX_EI_DEFINE(fputs, int) -int __wrap_fputs(const char* __s, FILE* __stream) { +int __wrap_fputs(const char *__s, FILE *__stream) { CELIX_EI_IMPL(fputs); return __real_fputs(__s, __stream); } -int __real_fclose(FILE* __stream); +int __real_fclose(FILE *__stream); CELIX_EI_DEFINE(fclose, int) -int __wrap_fclose(FILE* __stream) { +int __wrap_fclose(FILE *__stream) { int rc = __real_fclose(__stream); //note always call real fclose to ensure the stream is closed. errno = ENOSPC; CELIX_EI_IMPL(fclose); @@ -108,11 +108,20 @@ int __wrap_fclose(FILE* __stream) { return rc; } -int __real_fgetc(FILE* __stream); +int __real_fgetc(FILE *__stream); CELIX_EI_DEFINE(fgetc, int) -int __wrap_fgetc(FILE* __stream) { +int __wrap_fgetc(FILE *__stream) { CELIX_EI_IMPL(fgetc); return __real_fgetc(__stream); } +FILE* __real_fmemopen(void *__s, size_t __len, const char *__modes); +CELIX_EI_DEFINE(fmemopen, FILE*) +FILE* __wrap_fmemopen(void *__s, size_t __len, const char *__modes) { + errno = ENOMEM; + CELIX_EI_IMPL(fmemopen); + errno = 0; + return __real_fmemopen(__s, __len, __modes); } + +} \ No newline at end of file diff --git a/libs/error_injector/string/CMakeLists.txt b/libs/error_injector/string/CMakeLists.txt index ff53b52d3..190048570 100644 --- a/libs/error_injector/string/CMakeLists.txt +++ b/libs/error_injector/string/CMakeLists.txt @@ -22,5 +22,6 @@ target_link_libraries(string_ei PUBLIC Celix::error_injector) target_link_options(string_ei INTERFACE LINKER:--wrap,strndup + LINKER:--wrap,strdup ) add_library(Celix::string_ei ALIAS string_ei) diff --git a/libs/error_injector/string/include/string_ei.h b/libs/error_injector/string/include/string_ei.h index 85a2b3543..0ef7d9c83 100644 --- a/libs/error_injector/string/include/string_ei.h +++ b/libs/error_injector/string/include/string_ei.h @@ -29,6 +29,8 @@ extern "C" { CELIX_EI_DECLARE(strndup, char *); +CELIX_EI_DECLARE(strdup, char *); + #ifdef __cplusplus } #endif diff --git a/libs/error_injector/string/src/string_ei.cc b/libs/error_injector/string/src/string_ei.cc index 747478924..1b26f5ccd 100644 --- a/libs/error_injector/string/src/string_ei.cc +++ b/libs/error_injector/string/src/string_ei.cc @@ -22,12 +22,22 @@ #include extern "C" { -char* __real_strndup(const char* s, size_t n); +char *__real_strndup(const char *s, size_t n); CELIX_EI_DEFINE(strndup, char*) -char* __wrap_strndup(const char* s, size_t n) { +char *__wrap_strndup(const char *s, size_t n) { errno = ENOMEM; CELIX_EI_IMPL(strndup); errno = 0; return __real_strndup(s, n); } + +char *__real_strdup(const char *s); +CELIX_EI_DEFINE(strdup, char*) +char *__wrap_strdup(const char *s) { + errno = ENOMEM; + CELIX_EI_IMPL(strdup); + errno = 0; + return __real_strdup(s); +} + } \ No newline at end of file