blob: 94434acf9fb4e8cb07a34f7d5f939074ab3c9027 [file]
/*
* 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 "test_framework.h"
#include <uuid/uuid.h>
// Global test counters
int total_tests = 0;
int passed_tests = 0;
int failed_tests = 0;
// Normalize scheme by replacing underscores with hyphens
static char* normalize_scheme(const char* scheme)
{
if (!scheme)
return NULL;
char* normalized = strdup(scheme);
if (!normalized)
return NULL;
for (char* p = normalized; *p; ++p) {
if (*p == '_') {
*p = '-';
}
}
return normalized;
}
opendal_test_config* opendal_test_config_new()
{
opendal_test_config* config = (opendal_test_config*)malloc(sizeof(opendal_test_config));
if (!config)
return NULL;
// Read environment variables for configuration
const char* scheme = getenv("OPENDAL_TEST");
if (!scheme) {
scheme = "memory"; // Default to memory for testing
}
// Normalize the scheme (replace underscores with hyphens)
char* normalized_scheme = normalize_scheme(scheme);
if (!normalized_scheme) {
free(config);
return NULL;
}
config->scheme = normalized_scheme;
config->options = opendal_operator_options_new();
// Read configuration from environment variables
// Format: OPENDAL_{SCHEME}_{CONFIG_KEY}
char env_prefix[256];
snprintf(env_prefix, sizeof(env_prefix), "OPENDAL_%s_", scheme);
// Convert to uppercase
for (char* p = env_prefix; *p; ++p) {
*p = toupper(*p);
}
// Look for environment variables with this prefix
extern char** environ;
for (char** env = environ; *env; ++env) {
if (strncmp(*env, env_prefix, strlen(env_prefix)) == 0) {
char* key_value = strdup(*env + strlen(env_prefix));
char* equals = strchr(key_value, '=');
if (equals) {
*equals = '\0';
char* key = key_value;
char* value = equals + 1;
// Convert key to lowercase
for (char* p = key; *p; ++p) {
*p = tolower(*p);
}
opendal_operator_options_set(config->options, key, value);
}
free(key_value);
}
}
// Generate random root if not disabled
const char* disable_random_root = getenv("OPENDAL_DISABLE_RANDOM_ROOT");
if (!disable_random_root || strcmp(disable_random_root, "true") != 0) {
// Get existing root configuration if any
const char* existing_root = NULL;
// Check if root was already set from environment variables
char root_env_var[256];
snprintf(root_env_var, sizeof(root_env_var), "OPENDAL_%s_ROOT", scheme);
// Convert to uppercase
for (char* p = root_env_var; *p; ++p) {
*p = toupper(*p);
}
existing_root = getenv(root_env_var);
// Generate random path based on existing root
config->random_root = opendal_generate_random_path(existing_root);
opendal_operator_options_set(config->options, "root", config->random_root);
} else {
config->random_root = NULL;
}
// Create operator
opendal_result_operator_new result = opendal_operator_new(config->scheme, config->options);
if (result.error) {
printf("Failed to create operator: error code %d\n", result.error->code);
if (result.error->message.data) {
printf("Error message: %.*s\n", (int)result.error->message.len,
(char*)result.error->message.data);
}
opendal_error_free(result.error);
opendal_test_config_free(config);
return NULL;
}
config->operator_instance = result.op;
return config;
}
void opendal_test_config_free(opendal_test_config* config)
{
if (!config)
return;
if (config->operator_instance) {
opendal_operator_free(config->operator_instance);
}
if (config->options) {
opendal_operator_options_free(config->options);
}
if (config->scheme) {
free((void*)config->scheme);
}
if (config->random_root) {
free(config->random_root);
}
free(config);
}
bool opendal_check_capability(const opendal_operator* op,
opendal_required_capability required)
{
opendal_operator_info* info = opendal_operator_info_new(op);
if (!info)
return false;
opendal_capability cap = opendal_operator_info_get_full_capability(info);
bool result = true;
if (required.stat && !cap.stat)
result = false;
if (required.read && !cap.read)
result = false;
if (required.write && !cap.write)
result = false;
if (required.delete_ && !cap.delete_)
result = false;
if (required.list && !cap.list)
result = false;
if (required.list_with_start_after && !cap.list_with_start_after)
result = false;
if (required.list_with_recursive && !cap.list_with_recursive)
result = false;
if (required.copy && !cap.copy)
result = false;
if (required.rename && !cap.rename)
result = false;
if (required.create_dir && !cap.create_dir)
result = false;
if (required.presign && !cap.presign)
result = false;
if (required.presign_read && !cap.presign_read)
result = false;
if (required.presign_write && !cap.presign_write)
result = false;
if (required.presign_stat && !cap.presign_stat)
result = false;
if (required.presign_delete && !cap.presign_delete)
result = false;
opendal_operator_info_free(info);
return result;
}
char* opendal_generate_random_path(const char* base_root)
{
uuid_t uuid;
char uuid_str[37];
uuid_generate(uuid);
uuid_unparse(uuid, uuid_str);
char* path = (char*)malloc(512); // Increase size to accommodate longer paths
if (!path)
return NULL;
if (base_root && strlen(base_root) > 0) {
// If base_root is provided, append the random UUID to it
// Ensure proper path separator handling
const char* separator = (base_root[strlen(base_root) - 1] == '/') ? "" : "/";
snprintf(path, 512, "%s%stest_%s/", base_root, separator, uuid_str);
} else {
// If no base_root, use /tmp as a safe default instead of filesystem root
snprintf(path, 512, "/tmp/test_%s/", uuid_str);
}
return path;
}
char* opendal_generate_random_content(size_t length)
{
char* content = (char*)malloc(length + 1);
if (!content)
return NULL;
srand(time(NULL));
for (size_t i = 0; i < length; i++) {
content[i] = 'a' + (rand() % 26);
}
content[length] = '\0';
return content;
}
void opendal_run_test_suite(opendal_test_suite* suite,
opendal_test_config* config)
{
printf("\n=== Running Test Suite: %s ===\n", suite->name);
for (size_t i = 0; i < suite->test_count; i++) {
opendal_run_test_case(&suite->tests[i], config, suite->name);
}
printf("=== Test Suite %s Completed ===\n", suite->name);
}
void opendal_run_test_case(opendal_test_case* test_case,
opendal_test_config* config,
const char* suite_name)
{
total_tests++;
// Check capabilities
if (!opendal_check_capability(config->operator_instance,
test_case->capability)) {
printf("SKIPPED: %s::%s (missing required capabilities)\n", suite_name,
test_case->name);
return;
}
printf("RUNNING: %s::%s ... ", suite_name, test_case->name);
fflush(stdout);
// Create test context
opendal_test_context ctx = {
.config = config, .test_name = test_case->name, .suite_name = suite_name
};
// Capture stdout to detect assertion failures
int saved_failed_count = failed_tests;
// Run the test
test_case->func(&ctx);
// Check if test passed or failed
if (failed_tests > saved_failed_count) {
printf("FAILED\n");
} else {
printf("PASSED\n");
passed_tests++;
}
}
void opendal_print_test_summary()
{
printf("\n=== Test Summary ===\n");
printf("Total tests: %d\n", total_tests);
printf("Passed: %d\n", passed_tests);
printf("Failed: %d\n", failed_tests);
printf("Skipped: %d\n", total_tests - passed_tests - failed_tests);
if (failed_tests > 0) {
printf("\nTests FAILED!\n");
} else {
printf("\nAll tests PASSED!\n");
}
}
opendal_test_data* opendal_test_data_new(const char* path,
const char* content)
{
opendal_test_data* data = (opendal_test_data*)malloc(sizeof(opendal_test_data));
if (!data)
return NULL;
data->path = strdup(path);
size_t content_len = strlen(content);
data->content.data = (uint8_t*)malloc(content_len);
data->content.len = content_len;
data->content.capacity = content_len;
memcpy(data->content.data, content, content_len);
return data;
}
void opendal_test_data_free(opendal_test_data* data)
{
if (!data)
return;
if (data->path) {
free(data->path);
}
if (data->content.data) {
free(data->content.data);
}
free(data);
}