Skip to content
Open
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
2 changes: 2 additions & 0 deletions native-schema-registry/c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ set(TEST_TARGETS
glue_schema_registry_error_test
glue_schema_registry_serializer_test
glue_schema_registry_deserializer_test
# There are memory leaks in graal_create_isolate. Uncomment after fixing.
# schema_registry_memory_leak_test
Comment on lines +73 to +74
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be more descriptive as to where and what the memory leak is from?

)

# Manual quality gates target (always available)
Expand Down
5 changes: 4 additions & 1 deletion native-schema-registry/c/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ list(
read_only_byte_array_test
mutable_byte_array_test
glue_schema_registry_error_test
# There are memory leaks in graal_create_isolate. Uncomment after fixing.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graal_create is outside of our projects scope correct? Additionally if there are leaks in graalvm then we should raise and issue on their repo.

# schema_registry_memory_leak_test
)

#TODO: These tests don't work on Windows and OSX due to CMake not linking the mock library.
Expand Down Expand Up @@ -53,6 +55,7 @@ foreach (test ${tests})
add_test(NAME "${test}" COMMAND "${test}" ${CMAKE_CURRENT_BINARY_DIR})
endforeach ()


add_custom_target(copy-libs-for-tests ALL
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${DATA_TYPES_MODULE_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${SERDE_MODULE_NAME}> ${CMAKE_CURRENT_BINARY_DIR}/
Expand All @@ -67,4 +70,4 @@ if (CMAKE_SYSTEM_NAME MATCHES "^(Linux)$")
include(cmake/CodeCoverage.cmake)
set(min_code_coverage 100)
setup_target_for_coverage(coverage "ctest ." coverage ${min_code_coverage})
endif()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: unneeded formatting changes. Only fix if second revision is needed.

endif()
254 changes: 254 additions & 0 deletions native-schema-registry/c/test/schema_registry_memory_leak_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include "glue_schema_registry_serializer.h"
#include "glue_schema_registry_deserializer.h"
#include "glue_schema_registry_schema.h"
#include "mutable_byte_array.h"
#include "read_only_byte_array.h"
#include <stdlib.h>
#include "cmocka.h"
#include "glue_schema_registry_test_helper.h"
#include "libnativeschemaregistry_mock.h"

#define TEST_SCHEMA_NAME "Employee.proto"
#define TEST_DATA_FORMAT "PROTOBUF"
#define TEST_SCHEMA_DEF "message Employee { string name = 1; int32 rank = 2;}"
#define TEST_TRANSPORT_NAME "test-transport"
#define NUM_SERDE_ITERATIONS 10000

static void test_serializer_lifecycle_memory_management(void **state) {
// Test the complete serializer lifecycle: create, encode, dispose
set_mock_state(GRAAL_VM_INIT_SUCCESS);

glue_schema_registry_error **p_err = new_glue_schema_registry_error_holder();

// 1. CREATE: Initialize serializer
glue_schema_registry_serializer *serializer = new_glue_schema_registry_serializer(NULL, "memory-test-agent", p_err);
assert_non_null(serializer);
assert_null(*p_err);

// Create test data
glue_schema_registry_schema *schema = new_glue_schema_registry_schema(
TEST_SCHEMA_NAME,
TEST_SCHEMA_DEF,
TEST_DATA_FORMAT,
NULL,
p_err
);
assert_non_null(schema);
assert_null(*p_err);

read_only_byte_array *input_data = get_read_only_byte_array_fixture();
assert_non_null(input_data);

// 2. ENCODE: Use the serializer
mutable_byte_array *encoded_data = glue_schema_registry_serializer_encode(
serializer,
input_data,
TEST_TRANSPORT_NAME,
schema,
p_err
);
assert_non_null(encoded_data);
assert_null(*p_err);

// 3. DISPOSE: Clean up all resources
delete_mutable_byte_array(encoded_data);
delete_read_only_byte_array(input_data);
delete_glue_schema_registry_schema(schema);
delete_glue_schema_registry_serializer(serializer);
delete_glue_schema_registry_error_holder(p_err);

clear_mock_state();
}

static void test_deserializer_lifecycle_memory_management(void **state) {
// Test the complete deserializer lifecycle: create, decode, dispose
set_mock_state(GRAAL_VM_INIT_SUCCESS);

glue_schema_registry_error **p_err = new_glue_schema_registry_error_holder();

// 1. CREATE: Initialize deserializer
glue_schema_registry_deserializer *deserializer = new_glue_schema_registry_deserializer(NULL, "memory-test-agent", p_err);
assert_non_null(deserializer);
assert_null(*p_err);

// Create test encoded data
read_only_byte_array *encoded_data = get_read_only_byte_array_fixture();
assert_non_null(encoded_data);

// 2. DECODE: Use the deserializer
mutable_byte_array *decoded_data = glue_schema_registry_deserializer_decode(
deserializer,
encoded_data,
p_err
);
assert_non_null(decoded_data);
assert_null(*p_err);

// Also test schema decoding
glue_schema_registry_schema *decoded_schema = glue_schema_registry_deserializer_decode_schema(
deserializer,
encoded_data,
p_err
);
assert_non_null(decoded_schema);
assert_null(*p_err);

// 3. DISPOSE: Clean up all resources
delete_mutable_byte_array(decoded_data);
delete_glue_schema_registry_schema(decoded_schema);
delete_read_only_byte_array(encoded_data);
delete_glue_schema_registry_deserializer(deserializer);
delete_glue_schema_registry_error_holder(p_err);

clear_mock_state();
}

static void test_full_serialization_deserialization_cycle(void **state) {
// Test complete round-trip: serialize then deserialize
set_mock_state(GRAAL_VM_INIT_SUCCESS);

glue_schema_registry_error **p_err = new_glue_schema_registry_error_holder();

// CREATE: Initialize both serializer and deserializer
glue_schema_registry_serializer *serializer = new_glue_schema_registry_serializer(NULL, "cycle-test-agent", p_err);
assert_non_null(serializer);
assert_null(*p_err);

glue_schema_registry_deserializer *deserializer = new_glue_schema_registry_deserializer(NULL, "cycle-test-agent", p_err);
assert_non_null(deserializer);
assert_null(*p_err);

// Create test data and schema
glue_schema_registry_schema *original_schema = new_glue_schema_registry_schema(
TEST_SCHEMA_NAME,
TEST_SCHEMA_DEF,
TEST_DATA_FORMAT,
NULL,
p_err
);
assert_non_null(original_schema);

read_only_byte_array *original_data = get_read_only_byte_array_fixture();
assert_non_null(original_data);

// ENCODE: Serialize the data
mutable_byte_array *encoded_data = glue_schema_registry_serializer_encode(
serializer,
original_data,
TEST_TRANSPORT_NAME,
original_schema,
p_err
);
assert_non_null(encoded_data);
assert_null(*p_err);

// Convert to read-only for deserialization
read_only_byte_array *encoded_readonly = new_read_only_byte_array(
encoded_data->data,
encoded_data->max_len,
p_err
);
assert_non_null(encoded_readonly);

// DECODE: Deserialize the data back
mutable_byte_array *decoded_data = glue_schema_registry_deserializer_decode(
deserializer,
encoded_readonly,
p_err
);
assert_non_null(decoded_data);
assert_null(*p_err);

// DISPOSE: Clean up all resources in proper order
delete_mutable_byte_array(decoded_data);
delete_read_only_byte_array(encoded_readonly);
delete_mutable_byte_array(encoded_data);
delete_read_only_byte_array(original_data);
delete_glue_schema_registry_schema(original_schema);
delete_glue_schema_registry_deserializer(deserializer);
delete_glue_schema_registry_serializer(serializer);
delete_glue_schema_registry_error_holder(p_err);

clear_mock_state();
}

static void test_multiple_operations_memory_management(void **state) {
// Test multiple encode/decode operations with same instances
set_mock_state(GRAAL_VM_INIT_SUCCESS);

glue_schema_registry_error **p_err = new_glue_schema_registry_error_holder();

// CREATE: Initialize serializer and deserializer once
glue_schema_registry_serializer *serializer = new_glue_schema_registry_serializer(NULL, "multi-test-agent", p_err);
assert_non_null(serializer);

glue_schema_registry_deserializer *deserializer = new_glue_schema_registry_deserializer(NULL, "multi-test-agent", p_err);
assert_non_null(deserializer);

// Perform multiple operations to test for cumulative leaks
for (int iteration = 0; iteration < NUM_SERDE_ITERATIONS; iteration++) {
// Create fresh data for each iteration
glue_schema_registry_schema *schema = new_glue_schema_registry_schema(
TEST_SCHEMA_NAME,
TEST_SCHEMA_DEF,
TEST_DATA_FORMAT,
NULL,
p_err
);
assert_non_null(schema);

read_only_byte_array *input_data = get_read_only_byte_array_fixture();
assert_non_null(input_data);

// ENCODE
mutable_byte_array *encoded = glue_schema_registry_serializer_encode(
serializer,
input_data,
TEST_TRANSPORT_NAME,
schema,
p_err
);
assert_non_null(encoded);

// Convert for decoding
read_only_byte_array *encoded_readonly = new_read_only_byte_array(
encoded->data,
encoded->max_len,
p_err
);
assert_non_null(encoded_readonly);

// DECODE
mutable_byte_array *decoded = glue_schema_registry_deserializer_decode(
deserializer,
encoded_readonly,
p_err
);
assert_non_null(decoded);

// Clean up iteration resources
delete_mutable_byte_array(decoded);
delete_read_only_byte_array(encoded_readonly);
delete_mutable_byte_array(encoded);
delete_read_only_byte_array(input_data);
delete_glue_schema_registry_schema(schema);
}

// DISPOSE: Clean up persistent resources
delete_glue_schema_registry_deserializer(deserializer);
delete_glue_schema_registry_serializer(serializer);
delete_glue_schema_registry_error_holder(p_err);

clear_mock_state();
}

int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_serializer_lifecycle_memory_management),
cmocka_unit_test(test_deserializer_lifecycle_memory_management),
cmocka_unit_test(test_full_serialization_deserialization_cycle),
cmocka_unit_test(test_multiple_operations_memory_management)
};

return cmocka_run_group_tests(tests, NULL, NULL);
}
Loading