зеркало из https://github.com/Azure/c-pal.git
thandle ptr helper (#336)
* it all compuiles * no leaks * it all compiles so far no leaks, files moved around, started writing specs * added specs * added tests with incomplete/complete/and file hosted types * added unittests
This commit is contained in:
Родитель
f3e991a7f7
Коммит
751e5bec5e
|
@ -0,0 +1,91 @@
|
|||
# thandle_ptr_requirements
|
||||
|
||||
## Overview
|
||||
|
||||
`thandle_ptr` is a module that builds on `THANDLE` and provides a "move existing pointer under THANDLE" functionality.
|
||||
|
||||
`THANDLE` has as requirement to "only call malloc once". `THANDLE_PTR` retains that requirement. Note: the existing pointer must have been allocated "somehow" before. Even pointers to stack variables can be moved under `THANDLE_PTR` with the regular gotchas that deallocation happens when leaving the scope of the variable.
|
||||
|
||||
`THANDLE_PTR` should be used when the pointer already exists. In all the other situations, where the pointer hasn't been created yet, regular `THANDLE` should be used.
|
||||
|
||||
`THANDLE_PTR` works by declaring under the hood a struct as below which captures the parameters passed to `THANDLE_PTR_CREATE_WITH_MOVE` and...
|
||||
|
||||
```c
|
||||
typedef struct PTR_STRUCT_TAG_TYPE_NAME(T)
|
||||
{
|
||||
T pointer;
|
||||
THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) the_free;
|
||||
} PTR(T);
|
||||
```
|
||||
|
||||
... and then declaring a regular `THANDLE` over the above defined structure type.
|
||||
|
||||
In this document and code implementation "T" is a previously defined type that has pointer semantics and is a C identifier. For example "PS" introduced as: `typedef struct S_TAG* PS;`
|
||||
|
||||
## Usage
|
||||
|
||||
Usage of `THANDLE_PTR` is very similar to `THANDLE`.
|
||||
|
||||
In a C header, introduce the following declaration:
|
||||
```c
|
||||
THANDLE_PTR_DECLARE(A_S_PTR);
|
||||
```
|
||||
|
||||
In a C source file, introduce the following declaration:
|
||||
```c
|
||||
THANDLE_PTR_DEFINE(A_S_PTR);
|
||||
```
|
||||
|
||||
Then use it like:
|
||||
```c
|
||||
THANDLE(PTR(A_S_PTR)) one = THANDLE_PTR_CREATE_WITH_MOVE(A_S_PTR)(a_s, dispose);
|
||||
```
|
||||
|
||||
`a_s` is a pointer of type `A_S_PTR`.
|
||||
|
||||
The above code "moves" ("captures") the pointer `a_s` and produces the `THANDLE` `one`. `dispose` is a user function that takes the original `a_s` pointer and frees all resource used by `a_s`.
|
||||
|
||||
`one->pointer` produces the original `a_s` pointer. Other fields of the `one` pointer should not be used (it also captures the `dispose` function for example).
|
||||
|
||||
## Exposed API
|
||||
|
||||
`THANDLE_PTR` introduces several new types and APIs.
|
||||
|
||||
Types are:
|
||||
|
||||
`PTR(T)` - is a structure that contains the field `pointer` which captures the original `pointer` passed to `THANDLE_PTR_CREATE_WITH_MOVE`.
|
||||
|
||||
`THANDLE(PTR(T))` is the `PTR(T)` above with `THANDLE` semantics.
|
||||
|
||||
`THANDLE_PTR_FREE_FUNC_TYPE(T)` is the function pointer type of the `dispose` function. It takes `T` and frees it. `T` is already a pointer.
|
||||
|
||||
In addition all `THANDLE` regular APIs apply to `PTR(T)` type. For example
|
||||
```c
|
||||
THANDLE_ASSING(PTR(T))(&some_ptr_t, NULL);
|
||||
```
|
||||
|
||||
APIs are:
|
||||
|
||||
### THANDLE_PTR_CREATE_WITH_MOVE(T)
|
||||
```c
|
||||
THANDLE(PTR(T)) THANDLE_PTR_CREATE_WITH_MOVE(T)(T pointer, THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) dispose );
|
||||
```
|
||||
|
||||
|
||||
`THANDLE_PTR_CREATE_WITH_MOVE(T)` will "move" `pointer` to a newly created `THANDLE(PTR(T))` which then can be accessed by using the field "pointer" of the structure.
|
||||
|
||||
|
||||
**SRS_THANDLE_PTR_02_001: [** `THANDLE_PTR_CREATE_WITH_MOVE(T)` shall return what `THANDLE_CREATE_FROM_CONTENT(PTR(T))(THANDLE_PTR_DISPOSE(T))` returns. **]**
|
||||
|
||||
|
||||
### THANDLE_PTR_DISPOSE(T)
|
||||
```
|
||||
static void THANDLE_PTR_DISPOSE(T)(PTR(T)* ptr)
|
||||
```
|
||||
|
||||
`THANDLE_PTR_DISPOSE` is a static function that calls the original `dispose` passed to `THANDLE_PTR_CREATE_WITH_MOVE(T)`.
|
||||
|
||||
**SRS_THANDLE_PTR_02_002: [** If the original `dispose` is non-`NULL` then `THANDLE_PTR_DISPOSE(T)` shall call `dispose`. **]**
|
||||
|
||||
**SRS_THANDLE_PTR_02_003: [** `THANDLE_PTR_DISPOSE(T)` shall return. **]**
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef THANDLE_PTR_H
|
||||
#define THANDLE_PTR_H
|
||||
|
||||
#include "c_pal/thandle.h"
|
||||
#include "c_pal/thandle_ll.h" // for THANDLE
|
||||
|
||||
#include "macro_utils/macro_utils.h" // for MU_COUNT_ARG_0, MU_DISPATCH_EMP...
|
||||
|
||||
/*all the macros in this file depend on "T". T is "a pointer to a type", so it looks like "COORD_STRUCT*". */
|
||||
|
||||
/*this introduces a new *name* for a type that is a typedef of struct THANDLE_PTR_STRUCT_TAG_NAME {...}*/
|
||||
#define PTR(T) MU_C2(PTR_STRUCT_, T)
|
||||
|
||||
/*this introduces a new *name* for a structure type that contains "T pointer"*/
|
||||
#define PTR_STRUCT_TAG_TYPE_NAME(T) MU_C2(PTR(T), _TAG)
|
||||
|
||||
/*this introduces a new *name* for a function pointer type that takes a T and frees it*/
|
||||
#define THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) MU_C2(THANDLE_PTR_FREE_FUNC_, T)
|
||||
|
||||
/*this introduces a new *type* which is a pointer to the free type for T*/
|
||||
#define THANDLE_PTR_FREE_FUNC_TYPE(T) typedef void (*THANDLE_PTR_FREE_FUNC_TYPE_NAME(T))(T arg);
|
||||
|
||||
/*this introduces a new *type* which is a structure with a field of type T"*/
|
||||
#define PTR_STRUCT_TYPE_TYPEDEF(T) \
|
||||
typedef struct PTR_STRUCT_TAG_TYPE_NAME(T) \
|
||||
{ \
|
||||
T pointer; /*original pointer passed to THANDLE_PTR_CREATE_WITH_MOVE*/ \
|
||||
THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) dispose; /*original dispose passed to THANDLE_PTR_CREATE_WITH_MOVE*/ \
|
||||
} PTR(T);
|
||||
|
||||
/*this introduces one new *name* for a function which is used to capture a T ptr and move it under the THANDLE(PTR(T))*/
|
||||
#define THANDLE_PTR_CREATE_WITH_MOVE(T) MU_C2(THANDLE_PTR_CREATE_WITH_MOVE_, T)
|
||||
|
||||
/*this introduces a new *name* for a function that calls the dispose as passed to THANDLE_PTR_CREATE_WITH_MOVE*/
|
||||
#define THANDLE_PTR_DISPOSE(T) MU_C2(THANDLE_PTR_DISPOSE_, T)
|
||||
|
||||
/*this introduces the declaration of a function that returns a THANDLE(PTR(T))*/
|
||||
#define THANDLE_PTR_DECLARE(T) \
|
||||
THANDLE_PTR_FREE_FUNC_TYPE(T); \
|
||||
PTR_STRUCT_TYPE_TYPEDEF(T); \
|
||||
THANDLE_TYPE_DECLARE(PTR(T)); \
|
||||
THANDLE(PTR(T)) THANDLE_PTR_CREATE_WITH_MOVE(T)(T pointer, THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) dispose ); \
|
||||
|
||||
/*this introduces the definition of the function declared above*/
|
||||
#define THANDLE_PTR_DEFINE(T) \
|
||||
THANDLE_TYPE_DEFINE(PTR(T)); \
|
||||
static void THANDLE_PTR_DISPOSE(T)(PTR(T)* ptr) \
|
||||
{ \
|
||||
/*Codes_SRS_THANDLE_PTR_02_002: [ If the original dispose is non-NULL then THANDLE_PTR_DISPOSE(T) shall call dispose. ]*/ \
|
||||
if(ptr->dispose!=NULL) \
|
||||
{ \
|
||||
ptr->dispose(ptr->pointer); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
/*Codes_SRS_THANDLE_PTR_02_003: [ THANDLE_PTR_DISPOSE(T) shall return. ]*/ \
|
||||
/*do nothing*/ \
|
||||
} \
|
||||
} \
|
||||
THANDLE(PTR(T)) THANDLE_PTR_CREATE_WITH_MOVE(T)(T pointer, THANDLE_PTR_FREE_FUNC_TYPE_NAME(T) dispose ) \
|
||||
{ \
|
||||
PTR(T) temp = \
|
||||
{ \
|
||||
.pointer = pointer, /* this is "move" */ \
|
||||
.dispose = dispose \
|
||||
}; \
|
||||
/*Codes_SRS_THANDLE_PTR_02_001: [ THANDLE_PTR_CREATE_WITH_MOVE(T) shall return what THANDLE_CREATE_FROM_CONTENT(PTR(T))(THANDLE_PTR_DISPOSE(T)) returns. ]*/ \
|
||||
return THANDLE_CREATE_FROM_CONTENT(PTR(T))(&temp, THANDLE_PTR_DISPOSE(T), NULL); \
|
||||
}
|
||||
|
||||
#endif /*THANDLE_PTR_H*/
|
|
@ -19,6 +19,7 @@ if(${run_unittests})
|
|||
build_test_folder(s_list_ut)
|
||||
build_test_folder(thandle_2_ut)
|
||||
build_test_folder(thandle_ut)
|
||||
build_test_folder(thandle_ptr_ut)
|
||||
endif()
|
||||
|
||||
if(${run_int_tests})
|
||||
|
@ -27,6 +28,7 @@ if(${run_int_tests})
|
|||
build_test_folder(interlocked_hl_int)
|
||||
build_test_folder(lazy_init_int)
|
||||
build_test_folder(sm_int)
|
||||
build_test_folder(thandle_ptr_int)
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include "umock_c/umock_c.h"
|
||||
#define ENABLE_MOCKS
|
||||
#include "c_pal/gballoc_hl.h"
|
||||
#include "c_pal/gballoc_hl.h"
|
||||
#include "c_pal/gballoc_hl_redirect.h" /*THANDLE needs malloc/malloc_flex/free to exist*/
|
||||
#include "malloc_mocks.h"
|
||||
#undef ENABLE_MOCKS
|
||||
|
@ -236,7 +236,7 @@ TEST_FUNCTION(T_ON_create_from_content_flex_with_malloc_functions_calls_var_mall
|
|||
TEST_FUNCTION(T_OFF_uses_malloc_when_no_function_is_specified_1)
|
||||
{
|
||||
///arrange
|
||||
|
||||
|
||||
STRICT_EXPECTED_CALL(malloc(IGNORED_ARG));
|
||||
|
||||
//act
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#Copyright (c) Microsoft. All rights reserved.
|
||||
#Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
set(theseTestsName thandle_ptr_int)
|
||||
|
||||
set(${theseTestsName}_test_files
|
||||
${theseTestsName}.c
|
||||
)
|
||||
|
||||
set(${theseTestsName}_c_files
|
||||
example.c
|
||||
example_incomplete_type.c
|
||||
)
|
||||
|
||||
set(${theseTestsName}_h_files
|
||||
example.h
|
||||
example_incomplete_type.h
|
||||
../../inc/c_pal/thandle_ptr.h
|
||||
)
|
||||
|
||||
build_test_artifacts(${theseTestsName} "tests/c_pal" ADDITIONAL_LIBS c_pal c_pal_reals)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "c_pal/gballoc_hl.h" // IWYU pragma: keep
|
||||
#include "c_pal/gballoc_hl_redirect.h" // IWYU pragma: keep
|
||||
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
#include "example.h"
|
||||
|
||||
THANDLE_PTR_DEFINE(EXAMPLE_COMPLETE_PTR);
|
||||
|
||||
void dispose_example_complete(EXAMPLE_COMPLETE_PTR example_complete)
|
||||
{
|
||||
free(example_complete); /*nothing else*/
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef EXAMPLE_H
|
||||
#define EXAMPLE_H
|
||||
|
||||
#include "macro_utils/macro_utils.h"
|
||||
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
/*this is a complete type*/
|
||||
typedef struct EXAMPLE_COMPLETE_TAG
|
||||
{
|
||||
int example_complete;
|
||||
}EXAMPLE_COMPLETE;
|
||||
|
||||
typedef EXAMPLE_COMPLETE* EXAMPLE_COMPLETE_PTR;
|
||||
|
||||
THANDLE_PTR_DECLARE(EXAMPLE_COMPLETE_PTR);
|
||||
|
||||
void dispose_example_complete(EXAMPLE_COMPLETE_PTR example_complete);
|
||||
|
||||
#endif /*EXAMPLE_H*/
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "macro_utils/macro_utils.h"
|
||||
|
||||
#include "c_pal/gballoc_hl.h" // IWYU pragma: keep
|
||||
#include "c_pal/gballoc_hl_redirect.h" // IWYU pragma: keep
|
||||
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
#include "example_incomplete_type.h"
|
||||
|
||||
struct EXAMPLE_INCOMPLETE
|
||||
{
|
||||
int example_incomplete;
|
||||
};
|
||||
|
||||
THANDLE_PTR_DEFINE(EXAMPLE_INCOMPLETE_PTR);
|
||||
|
||||
EXAMPLE_INCOMPLETE_PTR create_example_incomplete(void)
|
||||
{
|
||||
return malloc(sizeof(struct EXAMPLE_INCOMPLETE));
|
||||
}
|
||||
|
||||
void dispose_example_incomplete(EXAMPLE_INCOMPLETE_PTR example_incomplete)
|
||||
{
|
||||
free(example_incomplete); /*nothing else*/
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#ifndef EXAMPLE_INCOMPLETE_TYPE_H
|
||||
#define EXAMPLE_INCOMPLETE_TYPE_H
|
||||
|
||||
#include "macro_utils/macro_utils.h"
|
||||
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
/*EXAMPLE_INCOMPLETE is an incomplete type (opaque)*/
|
||||
typedef struct EXAMPLE_INCOMPLETE* EXAMPLE_INCOMPLETE_PTR;
|
||||
|
||||
THANDLE_PTR_DECLARE(EXAMPLE_INCOMPLETE_PTR);
|
||||
|
||||
EXAMPLE_INCOMPLETE_PTR create_example_incomplete(void);
|
||||
|
||||
void dispose_example_incomplete(EXAMPLE_INCOMPLETE_PTR example_incomplete);
|
||||
|
||||
#endif /*EXAMPLE_INCOMPLETE_TYPE_H*/
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "macro_utils/macro_utils.h" // IWYU pragma: keep
|
||||
|
||||
#include "c_logging/logger.h"
|
||||
|
||||
#include "testrunnerswitcher.h"
|
||||
|
||||
#include "c_pal/gballoc_hl.h" // IWYU pragma: keep
|
||||
#include "c_pal/gballoc_hl_redirect.h" // IWYU pragma: keep
|
||||
#include "c_pal/thandle.h"
|
||||
#include "c_pal/string_utils.h"
|
||||
|
||||
#include "c_pal/thandle_ll.h"
|
||||
#include "c_pal/thandle.h" // IWYU pragma: keep
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
#include "example.h"
|
||||
#include "example_incomplete_type.h"
|
||||
|
||||
typedef struct A_S_TAG
|
||||
{
|
||||
int a;
|
||||
char* s;
|
||||
}A_S;
|
||||
|
||||
typedef A_S* A_S_PTR;
|
||||
|
||||
static void dispose(A_S_PTR a_s)
|
||||
{
|
||||
free(a_s->s);
|
||||
free(a_s);
|
||||
}
|
||||
|
||||
THANDLE_PTR_DECLARE(A_S_PTR);
|
||||
THANDLE_PTR_DEFINE(A_S_PTR);
|
||||
|
||||
|
||||
typedef struct A_S_CONST_TAG
|
||||
{
|
||||
const int a;
|
||||
const char* s;
|
||||
}A_S_CONST;
|
||||
|
||||
typedef A_S_CONST* A_S_CONST_PTR;
|
||||
|
||||
static void dispose_const(A_S_CONST_PTR a_s)
|
||||
{
|
||||
free((void*)a_s->s);
|
||||
free(a_s);
|
||||
}
|
||||
|
||||
THANDLE_PTR_DECLARE(A_S_CONST_PTR);
|
||||
THANDLE_PTR_DEFINE(A_S_CONST_PTR);
|
||||
|
||||
BEGIN_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
|
||||
|
||||
TEST_SUITE_INITIALIZE(it_does_something)
|
||||
{
|
||||
ASSERT_ARE_EQUAL(int, 0, gballoc_hl_init(NULL, NULL));
|
||||
}
|
||||
|
||||
TEST_SUITE_CLEANUP(TestClassCleanup)
|
||||
{
|
||||
gballoc_hl_deinit();
|
||||
}
|
||||
|
||||
TEST_FUNCTION_INITIALIZE(f)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_FUNCTION_CLEANUP(cleans)
|
||||
{
|
||||
}
|
||||
|
||||
/*the test will
|
||||
1. allocate on the heap some structure that needs a special "destroy" function
|
||||
2. move the pointer to the structure into a THANDLE(PTR())
|
||||
3. verify that the pointer in the THANDLE is the same as the original pointer
|
||||
3.1 verify that the pointer has the same qualifiers as the original pointer (in this case fields are not const)
|
||||
4. THANDLE_ASSIGN(PTR, NULL) so that memory is freed (the THANDLE memory and the original "destroy"
|
||||
*/
|
||||
TEST_FUNCTION(thandle_int_works_with_both_declare_and_define_in_this_file)
|
||||
{
|
||||
///arrange
|
||||
LogInfo("1. allocate on the heap some structure that needs a special \"destroy\" function.");
|
||||
A_S_PTR a_s = malloc(sizeof(A_S));
|
||||
ASSERT_IS_NOT_NULL(a_s);
|
||||
a_s->a = 42;
|
||||
a_s->s = sprintf_char("%s", "3333333333333333333333_here_some_string_3333333333333333333333");
|
||||
ASSERT_IS_NOT_NULL(a_s->s);
|
||||
|
||||
///act
|
||||
LogInfo("2. move the pointer to the structure into a THANDLE(PTR())");
|
||||
THANDLE(PTR(A_S_PTR)) one = THANDLE_PTR_CREATE_WITH_MOVE(A_S_PTR)(a_s, dispose);
|
||||
|
||||
///assert
|
||||
LogInfo("3. verify that the pointer in the THANDLE is the same as the original pointer");
|
||||
ASSERT_IS_NOT_NULL(one);
|
||||
ASSERT_ARE_EQUAL(void_ptr, a_s, one->pointer);
|
||||
|
||||
///assert
|
||||
LogInfo("3.1 verify that the pointer has the same qualifiers as the original pointer (in this case fields are not const)");
|
||||
one->pointer->a = 3;
|
||||
one->pointer->s[0] = 'a';
|
||||
|
||||
///cleanup
|
||||
LogInfo("4. THANDLE_ASSIGN(PTR, NULL) so that memory is freed (the THANDLE memory and the original \"destroy\"");
|
||||
THANDLE_ASSIGN(PTR(A_S_PTR))(&one, NULL);
|
||||
}
|
||||
|
||||
/*the test will
|
||||
1. allocate on the heap some structure that needs a special "destroy" function
|
||||
2. move the pointer to the structure into a THANDLE(PTR())
|
||||
3. verify that the pointer in the THANDLE is the same as the original pointer
|
||||
3.1 verify that the pointer has the same qualifiers as the original pointer (in this case fields are const)
|
||||
4. THANDLE_ASSIGN(PTR, NULL) so that memory is freed (the THANDLE memory and the original "destroy"
|
||||
*/
|
||||
TEST_FUNCTION(thandle_int_works_with_both_declare_and_define_in_this_file_for_const)
|
||||
{
|
||||
///arrange
|
||||
LogInfo("1. allocate on the heap some structure that needs a special \"destroy\" function.");
|
||||
A_S_CONST_PTR a_s = malloc(sizeof(A_S_CONST));
|
||||
ASSERT_IS_NOT_NULL(a_s);
|
||||
/*cast the const away... just for a tiny momnet*/
|
||||
*(int*)& a_s->a = 42;
|
||||
*(char**)&a_s->s = sprintf_char("%s", "3333333333333333333333_here_some_string_3333333333333333333333");
|
||||
ASSERT_IS_NOT_NULL(a_s->s);
|
||||
|
||||
///act
|
||||
LogInfo("2. move the pointer to the structure into a THANDLE(PTR())");
|
||||
THANDLE(PTR(A_S_CONST_PTR)) one = THANDLE_PTR_CREATE_WITH_MOVE(A_S_CONST_PTR)(a_s, dispose_const);
|
||||
|
||||
///assert
|
||||
LogInfo("3. verify that the pointer in the THANDLE is the same as the original pointer");
|
||||
ASSERT_IS_NOT_NULL(one);
|
||||
ASSERT_ARE_EQUAL(void_ptr, a_s, one->pointer);
|
||||
|
||||
///assert
|
||||
LogInfo("3.1 verify that the pointer has the same qualifiers as the original pointer (in this case fields are const)");
|
||||
/*one->pointer->a = 3;*/ /*error C2166: l-value specifies const object*/
|
||||
/*one->pointer->s[0] = 'a';*/ /*error C2166: l-value specifies const object*/
|
||||
|
||||
/*ENTER C GENERICS - with a shout out to "compatible types..." Note: it seems that "const int" and "int" are "compatible", however, int* and const int* are not.*/
|
||||
ASSERT_ARE_EQUAL(int, 1, _Generic(&one->pointer->a, const int *: 1, int*: 2, default : 0));
|
||||
ASSERT_ARE_EQUAL(int, 1, _Generic(&(one->pointer->s), const char** : 1, default: 0));
|
||||
|
||||
///cleanup
|
||||
LogInfo("4. THANDLE_ASSIGN(PTR, NULL) so that memory is freed (the THANDLE memory and the original \"destroy\"");
|
||||
THANDLE_ASSIGN(PTR(A_S_CONST_PTR))(&one, NULL);
|
||||
}
|
||||
|
||||
TEST_FUNCTION(thandle_int_works_with_both_declare_and_define_in_different_files)
|
||||
{
|
||||
///arrange
|
||||
EXAMPLE_COMPLETE_PTR p = malloc(sizeof(EXAMPLE_COMPLETE));
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
p->example_complete= 2;
|
||||
|
||||
///act
|
||||
THANDLE(PTR(EXAMPLE_COMPLETE_PTR)) example_complete = THANDLE_PTR_CREATE_WITH_MOVE(EXAMPLE_COMPLETE_PTR)(p, dispose_example_complete);
|
||||
|
||||
///assert
|
||||
ASSERT_IS_NOT_NULL(example_complete);
|
||||
ASSERT_ARE_EQUAL(void_ptr, p, example_complete->pointer);
|
||||
ASSERT_ARE_EQUAL(int, 2, example_complete->pointer->example_complete);
|
||||
|
||||
///cleanup
|
||||
THANDLE_ASSIGN(PTR(EXAMPLE_COMPLETE_PTR))(&example_complete, NULL);
|
||||
|
||||
}
|
||||
|
||||
TEST_FUNCTION(thandle_int_works_with_incomplete_types)
|
||||
{
|
||||
///arrange
|
||||
EXAMPLE_INCOMPLETE_PTR p = create_example_incomplete();
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
|
||||
///act
|
||||
THANDLE(PTR(EXAMPLE_INCOMPLETE_PTR)) example_incomplete = THANDLE_PTR_CREATE_WITH_MOVE(EXAMPLE_INCOMPLETE_PTR)(p, dispose_example_incomplete);
|
||||
|
||||
///assert
|
||||
ASSERT_IS_NOT_NULL(example_incomplete);
|
||||
ASSERT_ARE_EQUAL(void_ptr, p, example_incomplete->pointer);
|
||||
|
||||
///cleanup
|
||||
THANDLE_ASSIGN(PTR(EXAMPLE_INCOMPLETE_PTR))(&example_incomplete, NULL);
|
||||
|
||||
}
|
||||
|
||||
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#Copyright (c) Microsoft. All rights reserved.
|
||||
#Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
set(theseTestsName thandle_ptr_ut)
|
||||
|
||||
set(${theseTestsName}_test_files
|
||||
${theseTestsName}.c
|
||||
)
|
||||
|
||||
set(${theseTestsName}_c_files
|
||||
)
|
||||
|
||||
set(${theseTestsName}_h_files
|
||||
../../inc/c_pal/thandle_ptr.h
|
||||
)
|
||||
|
||||
build_test_artifacts(${theseTestsName} "tests/c_pal" ADDITIONAL_LIBS c_pal c_pal_reals)
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "macro_utils/macro_utils.h" // IWYU pragma: keep
|
||||
|
||||
#include "testrunnerswitcher.h"
|
||||
|
||||
#include "c_pal/thandle_ll.h" // for THANDLE, THANDLE_ASSIGN
|
||||
|
||||
#define ENABLE_MOCKS
|
||||
#include "umock_c/umock_c.h"
|
||||
#include "c_pal/gballoc_hl.h"
|
||||
#include "c_pal/gballoc_hl_redirect.h"
|
||||
|
||||
#undef ENABLE_MOCKS
|
||||
|
||||
#include "real_gballoc_hl.h"
|
||||
|
||||
#include "c_pal/thandle_ptr.h"
|
||||
|
||||
MU_DEFINE_ENUM_STRINGS(UMOCK_C_ERROR_CODE, UMOCK_C_ERROR_CODE_VALUES)
|
||||
|
||||
static void on_umock_c_error(UMOCK_C_ERROR_CODE error_code)
|
||||
{
|
||||
ASSERT_FAIL("umock_c reported error :%" PRI_MU_ENUM "", MU_ENUM_VALUE(UMOCK_C_ERROR_CODE, error_code));
|
||||
}
|
||||
|
||||
|
||||
typedef struct UNDER_TEST_TAG
|
||||
{
|
||||
int a;
|
||||
}UNDER_TEST;
|
||||
|
||||
typedef UNDER_TEST* UNDER_TEST_PTR;
|
||||
|
||||
THANDLE_PTR_DECLARE(UNDER_TEST_PTR);
|
||||
THANDLE_PTR_DEFINE(UNDER_TEST_PTR);
|
||||
|
||||
|
||||
BEGIN_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
|
||||
|
||||
TEST_SUITE_INITIALIZE(it_does_something)
|
||||
{
|
||||
ASSERT_ARE_EQUAL(int, 0, real_gballoc_hl_init(NULL, NULL));
|
||||
|
||||
umock_c_init(on_umock_c_error);
|
||||
|
||||
REGISTER_GBALLOC_HL_GLOBAL_MOCK_HOOK();
|
||||
}
|
||||
|
||||
TEST_SUITE_CLEANUP(TestClassCleanup)
|
||||
{
|
||||
umock_c_deinit();
|
||||
|
||||
real_gballoc_hl_deinit();
|
||||
}
|
||||
|
||||
TEST_FUNCTION_INITIALIZE(f)
|
||||
{
|
||||
umock_c_reset_all_calls();
|
||||
}
|
||||
|
||||
TEST_FUNCTION_CLEANUP(cleans)
|
||||
{
|
||||
}
|
||||
|
||||
static void just_call_free(UNDER_TEST_PTR p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
/*Tests_SRS_THANDLE_PTR_02_001: [ THANDLE_PTR_CREATE_WITH_MOVE(T) shall return what THANDLE_CREATE_FROM_CONTENT(PTR(T))(THANDLE_PTR_DISPOSE(T)) returns. ]*/
|
||||
TEST_FUNCTION(THANDLE_PTR_CREATE_WITH_MOVE_happy_path)
|
||||
{
|
||||
///arrange
|
||||
UNDER_TEST_PTR p = real_gballoc_hl_malloc(sizeof(UNDER_TEST));
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
umock_c_reset_all_calls();
|
||||
|
||||
STRICT_EXPECTED_CALL(malloc_flex(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG)); /*this is THANDLE_CREATE_FROM_CONTENT...*/
|
||||
|
||||
///act
|
||||
THANDLE(PTR(UNDER_TEST_PTR)) t = THANDLE_PTR_CREATE_WITH_MOVE(UNDER_TEST_PTR)(p, just_call_free);
|
||||
|
||||
///assert
|
||||
ASSERT_IS_NOT_NULL(t);
|
||||
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
|
||||
|
||||
///clean
|
||||
THANDLE_ASSIGN(PTR(UNDER_TEST_PTR))(&t, NULL);
|
||||
|
||||
}
|
||||
|
||||
/*Tests_SRS_THANDLE_PTR_02_001: [ THANDLE_PTR_CREATE_WITH_MOVE(T) shall return what THANDLE_CREATE_FROM_CONTENT(PTR(T))(THANDLE_PTR_DISPOSE(T)) returns. ]*/
|
||||
TEST_FUNCTION(THANDLE_PTR_CREATE_WITH_MOVE_unhappy_path)
|
||||
{
|
||||
///arrange
|
||||
UNDER_TEST_PTR p = real_gballoc_hl_malloc(sizeof(UNDER_TEST));
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
umock_c_reset_all_calls();
|
||||
|
||||
STRICT_EXPECTED_CALL(malloc_flex(IGNORED_ARG, IGNORED_ARG, IGNORED_ARG)) /*this is THANDLE_CREATE_FROM_CONTENT...*/
|
||||
.SetReturn(NULL);
|
||||
|
||||
///act
|
||||
THANDLE(PTR(UNDER_TEST_PTR)) t = THANDLE_PTR_CREATE_WITH_MOVE(UNDER_TEST_PTR)(p, just_call_free);
|
||||
|
||||
///assert
|
||||
ASSERT_IS_NULL(t);
|
||||
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls());
|
||||
|
||||
///clean
|
||||
just_call_free(p);
|
||||
}
|
||||
|
||||
/*Tests_SRS_THANDLE_PTR_02_002: [ If the original dispose is non-NULL then THANDLE_PTR_DISPOSE(T) shall call dispose. ]*/
|
||||
TEST_FUNCTION(THANDLE_PTR_DISPOSE_call_dispose)
|
||||
{
|
||||
///arrange
|
||||
UNDER_TEST_PTR p = real_gballoc_hl_malloc(sizeof(UNDER_TEST));
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
|
||||
THANDLE(PTR(UNDER_TEST_PTR)) t = THANDLE_PTR_CREATE_WITH_MOVE(UNDER_TEST_PTR)(p, just_call_free);
|
||||
ASSERT_IS_NOT_NULL(t);
|
||||
|
||||
umock_c_reset_all_calls();
|
||||
|
||||
STRICT_EXPECTED_CALL(free(p)); /*this is freeing "p"*/
|
||||
STRICT_EXPECTED_CALL(free(IGNORED_ARG)); /*this is freeing the THANDLE storage space*/
|
||||
|
||||
///act
|
||||
THANDLE_ASSIGN(PTR(UNDER_TEST_PTR))(&t, NULL);
|
||||
|
||||
///assert
|
||||
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls()); /*this is "shall return"... */
|
||||
}
|
||||
|
||||
/*Tests_SRS_THANDLE_PTR_02_003: [ THANDLE_PTR_DISPOSE(T) shall return. ]*/
|
||||
TEST_FUNCTION(THANDLE_PTR_DISPOSE_NULL_does_not_call_anything)
|
||||
{
|
||||
///arrange
|
||||
UNDER_TEST local = { .a = 42 };
|
||||
UNDER_TEST_PTR p = &local; /*note: no function needed to be called to dispose of local*/
|
||||
ASSERT_IS_NOT_NULL(p);
|
||||
|
||||
THANDLE(PTR(UNDER_TEST_PTR)) t = THANDLE_PTR_CREATE_WITH_MOVE(UNDER_TEST_PTR)(p, NULL);
|
||||
ASSERT_IS_NOT_NULL(t);
|
||||
|
||||
umock_c_reset_all_calls();
|
||||
|
||||
STRICT_EXPECTED_CALL(free(IGNORED_ARG)); /*this is freeing the THANDLE storage space*/
|
||||
|
||||
///act
|
||||
THANDLE_ASSIGN(PTR(UNDER_TEST_PTR))(&t, NULL);
|
||||
|
||||
///assert
|
||||
ASSERT_ARE_EQUAL(char_ptr, umock_c_get_expected_calls(), umock_c_get_actual_calls()); /*this is "shall return"... */
|
||||
}
|
||||
|
||||
END_TEST_SUITE(TEST_SUITE_NAME_FROM_CMAKE)
|
||||
|
Загрузка…
Ссылка в новой задаче