This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 483720cd9 chore(bindings): remove `Scheme` enum from some bindings
(#6776)
483720cd9 is described below
commit 483720cd9106260fc572330fa35097a58db757be
Author: Qinxuan Chen <[email protected]>
AuthorDate: Thu Dec 4 17:46:20 2025 +0800
chore(bindings): remove `Scheme` enum from some bindings (#6776)
* refactor(bindings): remove Scheme enum from cpp bindings
* refactor(bindings): remove Scheme enum from lua binding
* refactor(bindings): remove Scheme enum from ruby binding
* refactor(bindings): remove Scheme enum from dart binding
* refactor(bindings): remove Scheme enum from dotnet binding
* refactor(bindings): remove Scheme enum from ocaml binding
* refactor(bindings): remove Scheme enum from haskell binding
* refactor(bindings): remove Scheme enum from php binding
* refactor(bindings): remove Scheme enum from nodejs binding
* refactor(bindings): remove Scheme enum from c binding
* fix go binding
* revert some changes
* refactor(bindings): remove Scheme enum from java binding
---------
Signed-off-by: Xuanwo <[email protected]>
Co-authored-by: Xuanwo <[email protected]>
---
bindings/c/CMakeLists.txt | 5 +-
bindings/c/src/operator.rs | 12 +-
bindings/c/tests/Makefile | 7 +-
bindings/c/tests/example_test.cpp | 60 ++---
bindings/c/tests/test_framework.cpp | 216 +++++++++++-------
bindings/c/tests/test_runner.cpp | 62 +++---
bindings/c/tests/test_suites_basic.cpp | 126 ++++++-----
bindings/c/tests/test_suites_list.cpp | 165 ++++++++------
bindings/c/tests/test_suites_reader_writer.cpp | 247 ++++++++++++---------
bindings/cpp/src/async.rs | 3 -
bindings/cpp/src/lib.rs | 3 -
bindings/dart/rust/src/api/opendal_api.rs | 4 +-
bindings/dotnet/src/lib.rs | 12 +-
bindings/go/operator.go | 5 +-
bindings/go/tests/behavior_tests/opendal_test.go | 4 +-
bindings/haskell/src/lib.rs | 11 +-
bindings/java/Cargo.toml | 43 ++--
bindings/java/src/async_operator.rs | 4 +-
.../opendal/test/behavior/BehaviorExtension.java | 3 +-
bindings/java/src/utility.rs | 131 ++++++++++-
bindings/lua/src/lib.rs | 14 +-
bindings/nodejs/src/lib.rs | 7 -
bindings/nodejs/tests/utils.mjs | 8 +-
bindings/ocaml/src/lib.rs | 4 +-
bindings/php/src/lib.rs | 4 +-
bindings/ruby/src/operator.rs | 7 -
26 files changed, 690 insertions(+), 477 deletions(-)
diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt
index 77add47d6..aa75eae59 100644
--- a/bindings/c/CMakeLists.txt
+++ b/bindings/c/CMakeLists.txt
@@ -95,7 +95,10 @@ set(GTEST_SRCS
tests/reader.cpp
)
add_executable(tests ${GTEST_SRCS})
-target_link_libraries(tests opendal_c_shared gtest_main uuid)
+target_link_libraries(tests PRIVATE opendal_c_shared gtest_main)
+if(UNIX AND NOT APPLE)
+ target_link_libraries(tests PRIVATE uuid)
+endif()
if (TEST_ENABLE_ASAN)
target_compile_options(tests PRIVATE -fsanitize=address)
target_link_options(tests PRIVATE -fsanitize=address)
diff --git a/bindings/c/src/operator.rs b/bindings/c/src/operator.rs
index fb6c2571e..df2e65dea 100644
--- a/bindings/c/src/operator.rs
+++ b/bindings/c/src/operator.rs
@@ -18,7 +18,6 @@
use std::collections::HashMap;
use std::ffi::c_void;
use std::os::raw::c_char;
-use std::str::FromStr;
use std::sync::LazyLock;
use ::opendal as core;
@@ -86,7 +85,7 @@ impl opendal_operator {
}
fn build_operator(
- schema: core::Scheme,
+ schema: &str,
map: HashMap<String, String>,
) -> core::Result<core::blocking::Operator> {
let op = core::Operator::via_iter(schema,
map)?.layer(core::layers::RetryLayer::new());
@@ -144,15 +143,6 @@ pub unsafe extern "C" fn opendal_operator_new(
let scheme = std::ffi::CStr::from_ptr(scheme)
.to_str()
.expect("malformed scheme");
- let scheme = match core::Scheme::from_str(scheme) {
- Ok(s) => s,
- Err(e) => {
- return opendal_result_operator_new {
- op: std::ptr::null_mut(),
- error: opendal_error::new(e),
- };
- }
- };
let mut map = HashMap::<String, String>::default();
if !options.is_null() {
diff --git a/bindings/c/tests/Makefile b/bindings/c/tests/Makefile
index 543b3e8a3..11344eba8 100644
--- a/bindings/c/tests/Makefile
+++ b/bindings/c/tests/Makefile
@@ -19,7 +19,10 @@
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O2 -g
INCLUDES = -I../include -I.
-LIBS = -lopendal_c -luuid
+LIBS = -lopendal_c
+ifneq ($(shell uname), Darwin)
+LIBS += -luuid
+endif
# Source files
FRAMEWORK_SOURCES = test_framework.cpp
@@ -102,4 +105,4 @@ test_framework.o: test_framework.h
test_suites_basic.o: test_framework.h
test_suites_list.o: test_framework.h
test_suites_reader_writer.o: test_framework.h
-test_runner.o: test_framework.h
\ No newline at end of file
+test_runner.o: test_framework.h
diff --git a/bindings/c/tests/example_test.cpp
b/bindings/c/tests/example_test.cpp
index b559d870c..bb9453194 100644
--- a/bindings/c/tests/example_test.cpp
+++ b/bindings/c/tests/example_test.cpp
@@ -25,101 +25,101 @@
#include "test_framework.h"
// Simple test function
-void simple_test() {
+void simple_test()
+{
printf("Running simple test example...\n");
-
+
// Initialize test configuration
opendal_test_config* config = opendal_test_config_new();
if (!config) {
printf("Failed to create test config\n");
return;
}
-
+
printf("Testing with service: %s\n", config->scheme);
-
+
// Test basic operator functionality
opendal_error* error = opendal_operator_check(config->operator_instance);
if (error) {
printf("Operator check failed: %d\n", error->code);
if (error->message.data) {
- printf("Error: %.*s\n", (int)error->message.len,
(char*)error->message.data);
+ printf("Error: %.*s\n", (int)error->message.len,
+ (char*)error->message.data);
}
opendal_error_free(error);
opendal_test_config_free(config);
return;
}
-
+
printf("Operator check passed!\n");
-
+
// Test basic write/read if supported
opendal_operator_info* info =
opendal_operator_info_new(config->operator_instance);
if (info) {
opendal_capability cap =
opendal_operator_info_get_full_capability(info);
-
+
if (cap.write && cap.read) {
printf("Testing write/read operations...\n");
-
+
const char* test_path = "simple_test.txt";
const char* test_content = "Hello, OpenDAL!";
-
+
// Write test data
- opendal_bytes data = {
- .data = (uint8_t*)test_content,
+ opendal_bytes data = { .data = (uint8_t*)test_content,
.len = strlen(test_content),
- .capacity = strlen(test_content)
- };
-
+ .capacity = strlen(test_content) };
+
error = opendal_operator_write(config->operator_instance,
test_path, &data);
if (error) {
printf("Write failed: %d\n", error->code);
opendal_error_free(error);
} else {
printf("Write successful!\n");
-
+
// Read test data back
opendal_result_read result =
opendal_operator_read(config->operator_instance, test_path);
if (result.error) {
printf("Read failed: %d\n", result.error->code);
opendal_error_free(result.error);
} else {
- printf("Read successful! Content: %.*s\n",
- (int)result.data.len, (char*)result.data.data);
-
+ printf("Read successful! Content: %.*s\n",
(int)result.data.len,
+ (char*)result.data.data);
+
// Verify content
- if (result.data.len == strlen(test_content) &&
- memcmp(result.data.data, test_content,
result.data.len) == 0) {
+ if (result.data.len == strlen(test_content) &&
memcmp(result.data.data, test_content, result.data.len) == 0) {
printf("Content verification passed!\n");
} else {
printf("Content verification failed!\n");
}
-
+
opendal_bytes_free(&result.data);
}
-
+
// Cleanup
opendal_operator_delete(config->operator_instance, test_path);
}
} else {
printf("Write/read not supported by this service\n");
}
-
+
opendal_operator_info_free(info);
}
-
+
printf("Simple test completed!\n");
opendal_test_config_free(config);
}
-int main() {
+int main()
+{
printf("OpenDAL C Binding Test Framework Example\n");
printf("========================================\n\n");
-
+
simple_test();
-
+
printf("\nTo run the full test suite, use:\n");
printf(" make test # Run all tests\n");
printf(" ./opendal_test_runner # Run test runner directly\n");
printf(" ./opendal_test_runner --help # See all options\n");
-
+
return 0;
-}
\ No newline at end of file
+}
diff --git a/bindings/c/tests/test_framework.cpp
b/bindings/c/tests/test_framework.cpp
index 0b1a1d8c5..ea714386c 100644
--- a/bindings/c/tests/test_framework.cpp
+++ b/bindings/c/tests/test_framework.cpp
@@ -25,32 +25,60 @@ int total_tests = 0;
int passed_tests = 0;
int failed_tests = 0;
-opendal_test_config* opendal_test_config_new() {
+// 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;
-
+ if (!config)
+ return NULL;
+
// Read environment variables for configuration
const char* scheme = getenv("OPENDAL_TEST");
if (!scheme) {
- scheme = "memory"; // Default to memory for testing
+ 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 = strdup(scheme);
+
+ 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) {
+ 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, '=');
@@ -58,24 +86,24 @@ opendal_test_config* opendal_test_config_new() {
*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);
@@ -84,33 +112,36 @@ opendal_test_config* opendal_test_config_new() {
*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);
+ 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;
-
+void opendal_test_config_free(opendal_test_config* config)
+{
+ if (!config)
+ return;
+
if (config->operator_instance) {
opendal_operator_free(config->operator_instance);
}
@@ -126,41 +157,59 @@ void opendal_test_config_free(opendal_test_config*
config) {
free(config);
}
-bool opendal_check_capability(const opendal_operator* op,
opendal_required_capability required) {
+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;
-
+ 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.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;
+
opendal_operator_info_free(info);
return result;
}
-char* opendal_generate_random_path(const char* base_root) {
+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;
-
+
+ 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
@@ -170,58 +219,65 @@ char* opendal_generate_random_path(const char* base_root)
{
// 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* opendal_generate_random_content(size_t length)
+{
char* content = (char*)malloc(length + 1);
- if (!content) return NULL;
-
+ 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) {
+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) {
+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);
+ 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
+ .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");
@@ -231,13 +287,14 @@ void opendal_run_test_case(opendal_test_case* test_case,
opendal_test_config* co
}
}
-void opendal_print_test_summary() {
+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 {
@@ -245,24 +302,29 @@ void opendal_print_test_summary() {
}
}
-opendal_test_data* opendal_test_data_new(const char* path, const char*
content) {
+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;
-
+ 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;
-
+void opendal_test_data_free(opendal_test_data* data)
+{
+ if (!data)
+ return;
+
if (data->path) {
free(data->path);
}
@@ -270,4 +332,4 @@ void opendal_test_data_free(opendal_test_data* data) {
free(data->content.data);
}
free(data);
-}
\ No newline at end of file
+}
diff --git a/bindings/c/tests/test_runner.cpp b/bindings/c/tests/test_runner.cpp
index 24dc5848f..62e013549 100644
--- a/bindings/c/tests/test_runner.cpp
+++ b/bindings/c/tests/test_runner.cpp
@@ -18,8 +18,8 @@
*/
#include "test_framework.h"
-#include <vector>
#include <string>
+#include <vector>
// External test suite declarations
extern opendal_test_suite basic_suite;
@@ -35,7 +35,8 @@ static opendal_test_suite* all_suites[] = {
static const size_t num_suites = sizeof(all_suites) / sizeof(all_suites[0]);
-void print_usage(const char* program_name) {
+void print_usage(const char* program_name)
+{
printf("Usage: %s [options]\n", program_name);
printf("\nOptions:\n");
printf(" -h, --help Show this help message\n");
@@ -54,7 +55,8 @@ void print_usage(const char* program_name) {
printf(" OPENDAL_TEST=fs OPENDAL_FS_ROOT=/tmp %s # Test with filesystem
service\n", program_name);
}
-void list_suites() {
+void list_suites()
+{
printf("Available test suites:\n");
for (size_t i = 0; i < num_suites; i++) {
printf(" %s (%zu tests)\n", all_suites[i]->name,
all_suites[i]->test_count);
@@ -64,7 +66,8 @@ void list_suites() {
}
}
-bool run_specific_test(opendal_test_config* config, const char* suite_name,
const char* test_name) {
+bool run_specific_test(opendal_test_config* config, const char* suite_name,
const char* test_name)
+{
for (size_t i = 0; i < num_suites; i++) {
opendal_test_suite* suite = all_suites[i];
if (strcmp(suite->name, suite_name) == 0) {
@@ -83,7 +86,8 @@ bool run_specific_test(opendal_test_config* config, const
char* suite_name, cons
return false;
}
-bool run_specific_suite(opendal_test_config* config, const char* suite_name) {
+bool run_specific_suite(opendal_test_config* config, const char* suite_name)
+{
for (size_t i = 0; i < num_suites; i++) {
if (strcmp(all_suites[i]->name, suite_name) == 0) {
opendal_run_test_suite(all_suites[i], config);
@@ -94,19 +98,21 @@ bool run_specific_suite(opendal_test_config* config, const
char* suite_name) {
return false;
}
-void run_all_suites(opendal_test_config* config) {
+void run_all_suites(opendal_test_config* config)
+{
printf("Running all test suites...\n");
-
+
for (size_t i = 0; i < num_suites; i++) {
opendal_run_test_suite(all_suites[i], config);
}
}
-int main(int argc, char* argv[]) {
+int main(int argc, char* argv[])
+{
bool verbose = false;
const char* suite_to_run = nullptr;
const char* test_to_run = nullptr;
-
+
// Parse command line arguments
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
@@ -139,7 +145,7 @@ int main(int argc, char* argv[]) {
return 1;
}
}
-
+
// Initialize test configuration
printf("Initializing OpenDAL test framework...\n");
opendal_test_config* config = opendal_test_config_new();
@@ -147,12 +153,12 @@ int main(int argc, char* argv[]) {
printf("Failed to initialize test configuration\n");
return 1;
}
-
+
printf("Service: %s\n", config->scheme);
if (config->random_root) {
printf("Random root: %s\n", config->random_root);
}
-
+
// Check operator capabilities first
opendal_operator_info* info =
opendal_operator_info_new(config->operator_instance);
if (!info) {
@@ -160,9 +166,9 @@ int main(int argc, char* argv[]) {
opendal_test_config_free(config);
return 1;
}
-
+
opendal_capability cap = opendal_operator_info_get_full_capability(info);
-
+
// Check operator availability - only perform list-based check if list is
supported
if (cap.list) {
opendal_error* check_error =
opendal_operator_check(config->operator_instance);
@@ -180,18 +186,18 @@ int main(int argc, char* argv[]) {
// For KV adapters that don't support list, we'll do a basic
capability check instead
printf("Note: Operator doesn't support list operations (KV adapter),
skipping standard check\n");
}
-
+
printf("Operator is ready!\n");
printf("Capabilities: read=%s, write=%s, list=%s, stat=%s, delete=%s\n",
- cap.read ? "yes" : "no",
- cap.write ? "yes" : "no",
- cap.list ? "yes" : "no",
- cap.stat ? "yes" : "no",
- cap.delete_ ? "yes" : "no");
-
+ cap.read ? "yes" : "no",
+ cap.write ? "yes" : "no",
+ cap.list ? "yes" : "no",
+ cap.stat ? "yes" : "no",
+ cap.delete_ ? "yes" : "no");
+
opendal_operator_info_free(info);
printf("\n");
-
+
// Run tests based on command line arguments
if (test_to_run) {
// Parse suite::test format
@@ -203,14 +209,14 @@ int main(int argc, char* argv[]) {
opendal_test_config_free(config);
return 1;
}
-
+
*delimiter = '\0';
const char* suite_name = test_spec;
const char* test_name = delimiter + 2;
-
+
bool success = run_specific_test(config, suite_name, test_name);
free(test_spec);
-
+
if (!success) {
opendal_test_config_free(config);
return 1;
@@ -224,13 +230,13 @@ int main(int argc, char* argv[]) {
} else {
run_all_suites(config);
}
-
+
// Print test summary
opendal_print_test_summary();
-
+
// Cleanup
opendal_test_config_free(config);
-
+
// Return appropriate exit code
return (failed_tests > 0) ? 1 : 0;
}
diff --git a/bindings/c/tests/test_suites_basic.cpp
b/bindings/c/tests/test_suites_basic.cpp
index af57ed9ab..a64454b62 100644
--- a/bindings/c/tests/test_suites_basic.cpp
+++ b/bindings/c/tests/test_suites_basic.cpp
@@ -20,162 +20,178 @@
#include "test_framework.h"
// Test: Basic check operation
-void test_check(opendal_test_context* ctx) {
+void test_check(opendal_test_context* ctx)
+{
// Get operator capabilities first
opendal_operator_info* info =
opendal_operator_info_new(ctx->config->operator_instance);
OPENDAL_ASSERT_NOT_NULL(info, "Should be able to get operator info");
-
+
opendal_capability cap = opendal_operator_info_get_full_capability(info);
-
+
if (cap.list) {
// Only perform the standard check if list operations are supported
opendal_error* error =
opendal_operator_check(ctx->config->operator_instance);
OPENDAL_ASSERT_NO_ERROR(error, "Check operation should succeed");
} else {
- // For KV adapters that don't support list, the operator creation
itself is the check
- // If we got here, the operator is working properly
+ // For KV adapters that don't support list, the operator creation
itself is
+ // the check If we got here, the operator is working properly
printf("Note: Skipping list-based check for KV adapter\n");
}
-
+
opendal_operator_info_free(info);
}
// Test: Basic write and read operation
-void test_write_read(opendal_test_context* ctx) {
+void test_write_read(opendal_test_context* ctx)
+{
const char* path = "test_write_read.txt";
const char* content = "Hello, OpenDAL!";
-
+
// Write data
opendal_bytes data;
data.data = (uint8_t*)content;
data.len = strlen(content);
data.capacity = strlen(content);
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Read data back
opendal_result_read result =
opendal_operator_read(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(result.error, "Read operation should succeed");
- OPENDAL_ASSERT_EQ(strlen(content), result.data.len, "Read data length
should match written data");
-
+ OPENDAL_ASSERT_EQ(strlen(content), result.data.len,
+ "Read data length should match written data");
+
// Verify content
- OPENDAL_ASSERT(memcmp(content, result.data.data, result.data.len) == 0,
"Read content should match written content");
-
+ OPENDAL_ASSERT(memcmp(content, result.data.data, result.data.len) == 0,
+ "Read content should match written content");
+
// Cleanup
opendal_bytes_free(&result.data);
opendal_operator_delete(ctx->config->operator_instance, path);
}
// Test: Exists operation
-void test_exists(opendal_test_context* ctx) {
+void test_exists(opendal_test_context* ctx)
+{
const char* path = "test_exists.txt";
const char* content = "test";
-
+
// File should not exist initially
opendal_result_exists result =
opendal_operator_exists(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(result.error, "Exists operation should succeed");
OPENDAL_ASSERT(!result.exists, "File should not exist initially");
-
+
// Write file
opendal_bytes data;
data.data = (uint8_t*)content;
data.len = strlen(content);
data.capacity = strlen(content);
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// File should exist now
result = opendal_operator_exists(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(result.error, "Exists operation should succeed");
OPENDAL_ASSERT(result.exists, "File should exist after write");
-
+
// Cleanup
opendal_operator_delete(ctx->config->operator_instance, path);
}
// Test: Stat operation
-void test_stat(opendal_test_context* ctx) {
+void test_stat(opendal_test_context* ctx)
+{
const char* path = "test_stat.txt";
const char* content = "Hello, World!";
-
+
// Write file
opendal_bytes data;
data.data = (uint8_t*)content;
data.len = strlen(content);
data.capacity = strlen(content);
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Stat file
opendal_result_stat result =
opendal_operator_stat(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(result.error, "Stat operation should succeed");
OPENDAL_ASSERT_NOT_NULL(result.meta, "Metadata should not be null");
-
+
// Check file properties
- OPENDAL_ASSERT(opendal_metadata_is_file(result.meta), "Should be
identified as file");
- OPENDAL_ASSERT(!opendal_metadata_is_dir(result.meta), "Should not be
identified as directory");
- OPENDAL_ASSERT_EQ(strlen(content),
opendal_metadata_content_length(result.meta), "Content length should match");
-
+ OPENDAL_ASSERT(opendal_metadata_is_file(result.meta),
+ "Should be identified as file");
+ OPENDAL_ASSERT(!opendal_metadata_is_dir(result.meta),
+ "Should not be identified as directory");
+ OPENDAL_ASSERT_EQ(strlen(content),
+ opendal_metadata_content_length(result.meta),
+ "Content length should match");
+
// Cleanup
opendal_metadata_free(result.meta);
opendal_operator_delete(ctx->config->operator_instance, path);
}
// Test: Delete operation
-void test_delete(opendal_test_context* ctx) {
+void test_delete(opendal_test_context* ctx)
+{
const char* path = "test_delete.txt";
const char* content = "to be deleted";
-
+
// Write file
opendal_bytes data;
data.data = (uint8_t*)content;
data.len = strlen(content);
data.capacity = strlen(content);
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Verify file exists
opendal_result_exists exists_result =
opendal_operator_exists(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(exists_result.error, "Exists operation should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(exists_result.error,
+ "Exists operation should succeed");
OPENDAL_ASSERT(exists_result.exists, "File should exist before deletion");
-
+
// Delete file
error = opendal_operator_delete(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(error, "Delete operation should succeed");
-
+
// Verify file no longer exists
exists_result = opendal_operator_exists(ctx->config->operator_instance,
path);
- OPENDAL_ASSERT_NO_ERROR(exists_result.error, "Exists operation should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(exists_result.error,
+ "Exists operation should succeed");
OPENDAL_ASSERT(!exists_result.exists, "File should not exist after
deletion");
-
+
// Delete should be idempotent
error = opendal_operator_delete(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(error, "Delete operation should be idempotent");
}
// Test: Create directory operation
-void test_create_dir(opendal_test_context* ctx) {
+void test_create_dir(opendal_test_context* ctx)
+{
const char* dir_path = "test_dir/";
-
+
// Create directory
opendal_error* error =
opendal_operator_create_dir(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(error, "Create dir operation should succeed");
-
+
// Verify directory exists
opendal_result_exists result =
opendal_operator_exists(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(result.error, "Exists operation should succeed");
OPENDAL_ASSERT(result.exists, "Directory should exist after creation");
-
+
// Stat directory
opendal_result_stat stat_result =
opendal_operator_stat(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(stat_result.error, "Stat operation should
succeed");
- OPENDAL_ASSERT(opendal_metadata_is_dir(stat_result.meta), "Should be
identified as directory");
- OPENDAL_ASSERT(!opendal_metadata_is_file(stat_result.meta), "Should not be
identified as file");
-
+ OPENDAL_ASSERT(opendal_metadata_is_dir(stat_result.meta),
+ "Should be identified as directory");
+ OPENDAL_ASSERT(!opendal_metadata_is_file(stat_result.meta),
+ "Should not be identified as file");
+
// Cleanup
opendal_metadata_free(stat_result.meta);
opendal_operator_delete(ctx->config->operator_instance, dir_path);
@@ -183,16 +199,16 @@ void test_create_dir(opendal_test_context* ctx) {
// Define the basic test suite
opendal_test_case basic_tests[] = {
- {"check", test_check, NO_CAPABILITY},
- {"write_read", test_write_read, make_capability_read_write()},
- {"exists", test_exists, make_capability_write()},
- {"stat", test_stat, make_capability_write_stat()},
- {"delete", test_delete, make_capability_write_delete()},
- {"create_dir", test_create_dir, make_capability_create_dir()},
+ { "check", test_check, NO_CAPABILITY },
+ { "write_read", test_write_read, make_capability_read_write() },
+ { "exists", test_exists, make_capability_write() },
+ { "stat", test_stat, make_capability_write_stat() },
+ { "delete", test_delete, make_capability_write_delete() },
+ { "create_dir", test_create_dir, make_capability_create_dir() },
};
opendal_test_suite basic_suite = {
- "Basic Operations", // name
- basic_tests, // tests
- sizeof(basic_tests) / sizeof(basic_tests[0]) // test_count
-};
\ No newline at end of file
+ "Basic Operations", // name
+ basic_tests, // tests
+ sizeof(basic_tests) / sizeof(basic_tests[0]) // test_count
+};
diff --git a/bindings/c/tests/test_suites_list.cpp
b/bindings/c/tests/test_suites_list.cpp
index b347525dc..4c6e56615 100644
--- a/bindings/c/tests/test_suites_list.cpp
+++ b/bindings/c/tests/test_suites_list.cpp
@@ -22,31 +22,35 @@
#include <string>
// Test: Basic list operation
-void test_list_basic(opendal_test_context* ctx) {
+void test_list_basic(opendal_test_context* ctx)
+{
const char* dir_path = "test_list_dir/";
-
+
// Create directory
opendal_error* error =
opendal_operator_create_dir(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(error, "Create dir operation should succeed");
-
+
// Create some test files
- const char* test_files[] = {"test_list_dir/file1.txt",
"test_list_dir/file2.txt", "test_list_dir/file3.txt"};
+ const char* test_files[] = { "test_list_dir/file1.txt",
+ "test_list_dir/file2.txt",
+ "test_list_dir/file3.txt" };
const size_t num_files = sizeof(test_files) / sizeof(test_files[0]);
-
+
for (size_t i = 0; i < num_files; i++) {
opendal_bytes data;
data.data = (uint8_t*)"test content";
data.len = 12;
data.capacity = 12;
- error = opendal_operator_write(ctx->config->operator_instance,
test_files[i], &data);
+ error = opendal_operator_write(ctx->config->operator_instance,
+ test_files[i], &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
}
-
+
// List directory
opendal_result_list list_result =
opendal_operator_list(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(list_result.error, "List operation should
succeed");
OPENDAL_ASSERT_NOT_NULL(list_result.lister, "Lister should not be null");
-
+
// Collect all entries
std::set<std::string> found_paths;
while (true) {
@@ -55,32 +59,34 @@ void test_list_basic(opendal_test_context* ctx) {
OPENDAL_ASSERT_NO_ERROR(next_result.error, "Lister next should not
fail");
break;
}
-
+
if (!next_result.entry) {
// End of list
break;
}
-
+
char* path = opendal_entry_path(next_result.entry);
OPENDAL_ASSERT_NOT_NULL(path, "Entry path should not be null");
found_paths.insert(std::string(path));
-
+
free(path);
opendal_entry_free(next_result.entry);
}
-
+
// Check if the directory itself is included in the listing
bool dir_included = found_paths.count(dir_path) > 0;
-
+
// Verify we found all files, and optionally the directory itself
size_t expected_count = num_files + (dir_included ? 1 : 0);
- OPENDAL_ASSERT_EQ(expected_count, found_paths.size(), "Should find all
files and optionally the directory");
-
+ OPENDAL_ASSERT_EQ(expected_count, found_paths.size(),
+ "Should find all files and optionally the directory");
+
// All files should be present
for (size_t i = 0; i < num_files; i++) {
- OPENDAL_ASSERT(found_paths.count(test_files[i]) > 0, "Should find all
test files");
+ OPENDAL_ASSERT(found_paths.count(test_files[i]) > 0,
+ "Should find all test files");
}
-
+
// Cleanup
opendal_lister_free(list_result.lister);
for (size_t i = 0; i < num_files; i++) {
@@ -90,18 +96,19 @@ void test_list_basic(opendal_test_context* ctx) {
}
// Test: List empty directory
-void test_list_empty_dir(opendal_test_context* ctx) {
+void test_list_empty_dir(opendal_test_context* ctx)
+{
const char* dir_path = "test_empty_dir/";
-
+
// Create directory
opendal_error* error =
opendal_operator_create_dir(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(error, "Create dir operation should succeed");
-
+
// List directory
opendal_result_list list_result =
opendal_operator_list(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(list_result.error, "List operation should
succeed");
OPENDAL_ASSERT_NOT_NULL(list_result.lister, "Lister should not be null");
-
+
// Collect entries
std::set<std::string> found_paths;
while (true) {
@@ -110,61 +117,66 @@ void test_list_empty_dir(opendal_test_context* ctx) {
OPENDAL_ASSERT_NO_ERROR(next_result.error, "Lister next should not
fail");
break;
}
-
+
if (!next_result.entry) {
break;
}
-
+
char* path = opendal_entry_path(next_result.entry);
found_paths.insert(std::string(path));
free(path);
opendal_entry_free(next_result.entry);
}
-
+
// Some services include the directory itself, others don't
bool dir_included = found_paths.count(dir_path) > 0;
size_t expected_count = dir_included ? 1 : 0;
- OPENDAL_ASSERT_EQ(expected_count, found_paths.size(), "Should find empty
listing or just the directory");
-
+ OPENDAL_ASSERT_EQ(expected_count, found_paths.size(),
+ "Should find empty listing or just the directory");
+
if (dir_included) {
- OPENDAL_ASSERT(found_paths.count(dir_path) > 0, "Should find the
directory itself if it's included");
+ OPENDAL_ASSERT(found_paths.count(dir_path) > 0,
+ "Should find the directory itself if it's included");
}
-
+
// Cleanup
opendal_lister_free(list_result.lister);
opendal_operator_delete(ctx->config->operator_instance, dir_path);
}
// Test: List nested directories
-void test_list_nested(opendal_test_context* ctx) {
+void test_list_nested(opendal_test_context* ctx)
+{
const char* base_dir = "test_nested/";
const char* sub_dir = "test_nested/subdir/";
const char* file_in_base = "test_nested/base_file.txt";
const char* file_in_sub = "test_nested/subdir/sub_file.txt";
-
+
// Create directories
opendal_error* error =
opendal_operator_create_dir(ctx->config->operator_instance, base_dir);
OPENDAL_ASSERT_NO_ERROR(error, "Create base dir should succeed");
-
+
error = opendal_operator_create_dir(ctx->config->operator_instance,
sub_dir);
OPENDAL_ASSERT_NO_ERROR(error, "Create sub dir should succeed");
-
+
// Create files
opendal_bytes data;
data.data = (uint8_t*)"test content";
data.len = 12;
data.capacity = 12;
-
- error = opendal_operator_write(ctx->config->operator_instance,
file_in_base, &data);
+
+ error = opendal_operator_write(ctx->config->operator_instance,
file_in_base,
+ &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write to base dir should succeed");
-
- error = opendal_operator_write(ctx->config->operator_instance,
file_in_sub, &data);
+
+ error = opendal_operator_write(ctx->config->operator_instance, file_in_sub,
+ &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write to sub dir should succeed");
-
+
// List base directory
opendal_result_list list_result =
opendal_operator_list(ctx->config->operator_instance, base_dir);
OPENDAL_ASSERT_NO_ERROR(list_result.error, "List operation should
succeed");
-
+
std::set<std::string> found_paths;
while (true) {
opendal_result_lister_next next_result =
opendal_lister_next(list_result.lister);
@@ -172,34 +184,39 @@ void test_list_nested(opendal_test_context* ctx) {
OPENDAL_ASSERT_NO_ERROR(next_result.error, "Lister next should not
fail");
break;
}
-
+
if (!next_result.entry) {
break;
}
-
+
char* path = opendal_entry_path(next_result.entry);
found_paths.insert(std::string(path));
free(path);
opendal_entry_free(next_result.entry);
}
-
+
// Should find base dir, sub dir, and file in base
bool base_dir_included = found_paths.count(base_dir) > 0;
- size_t expected_count = 2 + (base_dir_included ? 1 : 0); // sub_dir +
file_in_base + optionally base_dir
- OPENDAL_ASSERT_EQ(expected_count, found_paths.size(), "Should find correct
number of items in base directory");
-
+ size_t expected_count = 2 + (base_dir_included ? 1 : 0); // sub_dir +
file_in_base + optionally base_dir
+ OPENDAL_ASSERT_EQ(expected_count, found_paths.size(),
+ "Should find correct number of items in base directory");
+
// These should always be present
OPENDAL_ASSERT(found_paths.count(sub_dir) > 0, "Should find sub
directory");
- OPENDAL_ASSERT(found_paths.count(file_in_base) > 0, "Should find file in
base directory");
-
+ OPENDAL_ASSERT(found_paths.count(file_in_base) > 0,
+ "Should find file in base directory");
+
// Base directory may or may not be included depending on the service
if (base_dir_included) {
- OPENDAL_ASSERT(found_paths.count(base_dir) > 0, "Should find base
directory if it's included");
+ OPENDAL_ASSERT(found_paths.count(base_dir) > 0,
+ "Should find base directory if it's included");
}
-
- // Should NOT find file in subdirectory when listing base directory
non-recursively
- OPENDAL_ASSERT(found_paths.count(file_in_sub) == 0, "Should not find file
in subdirectory");
-
+
+ // Should NOT find file in subdirectory when listing base directory
+ // non-recursively
+ OPENDAL_ASSERT(found_paths.count(file_in_sub) == 0,
+ "Should not find file in subdirectory");
+
// Cleanup
opendal_lister_free(list_result.lister);
opendal_operator_delete(ctx->config->operator_instance, file_in_sub);
@@ -209,25 +226,26 @@ void test_list_nested(opendal_test_context* ctx) {
}
// Test: Entry name vs path
-void test_entry_name_path(opendal_test_context* ctx) {
+void test_entry_name_path(opendal_test_context* ctx)
+{
const char* dir_path = "test_entry_names/";
const char* file_path = "test_entry_names/test_file.txt";
-
+
// Create directory and file
opendal_error* error =
opendal_operator_create_dir(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(error, "Create dir should succeed");
-
+
opendal_bytes data;
data.data = (uint8_t*)"test";
data.len = 4;
data.capacity = 4;
error = opendal_operator_write(ctx->config->operator_instance, file_path,
&data);
OPENDAL_ASSERT_NO_ERROR(error, "Write should succeed");
-
+
// List directory
opendal_result_list list_result =
opendal_operator_list(ctx->config->operator_instance, dir_path);
OPENDAL_ASSERT_NO_ERROR(list_result.error, "List operation should
succeed");
-
+
bool found_file = false;
while (true) {
opendal_result_lister_next next_result =
opendal_lister_next(list_result.lister);
@@ -235,27 +253,29 @@ void test_entry_name_path(opendal_test_context* ctx) {
OPENDAL_ASSERT_NO_ERROR(next_result.error, "Lister next should not
fail");
break;
}
-
+
if (!next_result.entry) {
break;
}
-
+
char* path = opendal_entry_path(next_result.entry);
char* name = opendal_entry_name(next_result.entry);
-
+
if (strcmp(path, file_path) == 0) {
found_file = true;
- OPENDAL_ASSERT_STR_EQ("test_file.txt", name, "Entry name should be
just the filename");
- OPENDAL_ASSERT_STR_EQ(file_path, path, "Entry path should be the
full path");
+ OPENDAL_ASSERT_STR_EQ("test_file.txt", name,
+ "Entry name should be just the filename");
+ OPENDAL_ASSERT_STR_EQ(file_path, path,
+ "Entry path should be the full path");
}
-
+
free(path);
free(name);
opendal_entry_free(next_result.entry);
}
-
+
OPENDAL_ASSERT(found_file, "Should have found the test file");
-
+
// Cleanup
opendal_lister_free(list_result.lister);
opendal_operator_delete(ctx->config->operator_instance, file_path);
@@ -264,14 +284,15 @@ void test_entry_name_path(opendal_test_context* ctx) {
// Define the list test suite
opendal_test_case list_tests[] = {
- {"list_basic", test_list_basic, make_capability_write_create_dir_list()},
- {"list_empty_dir", test_list_empty_dir, make_capability_create_dir_list()},
- {"list_nested", test_list_nested, make_capability_write_create_dir_list()},
- {"entry_name_path", test_entry_name_path,
make_capability_write_create_dir_list()},
+ { "list_basic", test_list_basic, make_capability_write_create_dir_list() },
+ { "list_empty_dir", test_list_empty_dir, make_capability_create_dir_list()
},
+ { "list_nested", test_list_nested, make_capability_write_create_dir_list()
},
+ { "entry_name_path", test_entry_name_path,
+ make_capability_write_create_dir_list() },
};
opendal_test_suite list_suite = {
- "List Operations", // name
- list_tests, // tests
- sizeof(list_tests) / sizeof(list_tests[0]) // test_count
-};
\ No newline at end of file
+ "List Operations", // name
+ list_tests, // tests
+ sizeof(list_tests) / sizeof(list_tests[0]) // test_count
+};
diff --git a/bindings/c/tests/test_suites_reader_writer.cpp
b/bindings/c/tests/test_suites_reader_writer.cpp
index 1987c0035..0163f2024 100644
--- a/bindings/c/tests/test_suites_reader_writer.cpp
+++ b/bindings/c/tests/test_suites_reader_writer.cpp
@@ -20,170 +20,188 @@
#include "test_framework.h"
// Test: Basic reader operations
-void test_reader_basic(opendal_test_context* ctx) {
+void test_reader_basic(opendal_test_context* ctx)
+{
const char* path = "test_reader.txt";
const char* content = "Hello, OpenDAL Reader!";
size_t content_len = strlen(content);
-
+
// Write test data first
opendal_bytes data = {
- .data = (uint8_t*)content,
- .len = content_len,
- .capacity = content_len
+ .data = (uint8_t*)content, .len = content_len, .capacity = content_len
};
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Create reader
opendal_result_operator_reader reader_result =
opendal_operator_reader(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(reader_result.error, "Reader creation should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(reader_result.error,
+ "Reader creation should succeed");
OPENDAL_ASSERT_NOT_NULL(reader_result.reader, "Reader should not be null");
-
+
// Read entire content
uint8_t buffer[100];
opendal_result_reader_read read_result =
opendal_reader_read(reader_result.reader, buffer, sizeof(buffer));
OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read operation should
succeed");
- OPENDAL_ASSERT_EQ(content_len, read_result.size, "Read size should match
content length");
-
+ OPENDAL_ASSERT_EQ(content_len, read_result.size,
+ "Read size should match content length");
+
// Verify content
- OPENDAL_ASSERT(memcmp(content, buffer, content_len) == 0, "Read content
should match written content");
-
+ OPENDAL_ASSERT(memcmp(content, buffer, content_len) == 0,
+ "Read content should match written content");
+
// Cleanup
opendal_reader_free(reader_result.reader);
opendal_operator_delete(ctx->config->operator_instance, path);
}
// Test: Reader seek operations
-void test_reader_seek(opendal_test_context* ctx) {
+void test_reader_seek(opendal_test_context* ctx)
+{
const char* path = "test_reader_seek.txt";
const char* content = "0123456789ABCDEFGHIJ";
size_t content_len = strlen(content);
-
+
// Write test data
opendal_bytes data = {
- .data = (uint8_t*)content,
- .len = content_len,
- .capacity = content_len
+ .data = (uint8_t*)content, .len = content_len, .capacity = content_len
};
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Create reader
opendal_result_operator_reader reader_result =
opendal_operator_reader(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(reader_result.error, "Reader creation should
succeed");
-
+ OPENDAL_ASSERT_NO_ERROR(reader_result.error,
+ "Reader creation should succeed");
+
// Test seek from current position
opendal_result_reader_seek seek_result =
opendal_reader_seek(reader_result.reader, 5, OPENDAL_SEEK_CUR);
- OPENDAL_ASSERT_NO_ERROR(seek_result.error, "Seek from current should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(seek_result.error,
+ "Seek from current should succeed");
OPENDAL_ASSERT_EQ(5, seek_result.pos, "Position should be 5");
-
+
// Read after seek
uint8_t buffer[5];
opendal_result_reader_read read_result =
opendal_reader_read(reader_result.reader, buffer, 5);
OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read after seek should
succeed");
OPENDAL_ASSERT_EQ(5, read_result.size, "Should read 5 bytes");
- OPENDAL_ASSERT(memcmp("56789", buffer, 5) == 0, "Should read correct
content after seek");
-
+ OPENDAL_ASSERT(memcmp("56789", buffer, 5) == 0,
+ "Should read correct content after seek");
+
// Test seek from beginning
seek_result = opendal_reader_seek(reader_result.reader, 0,
OPENDAL_SEEK_SET);
- OPENDAL_ASSERT_NO_ERROR(seek_result.error, "Seek from beginning should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(seek_result.error,
+ "Seek from beginning should succeed");
OPENDAL_ASSERT_EQ(0, seek_result.pos, "Position should be 0");
-
+
// Read from beginning
read_result = opendal_reader_read(reader_result.reader, buffer, 5);
- OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read from beginning should
succeed");
- OPENDAL_ASSERT(memcmp("01234", buffer, 5) == 0, "Should read correct
content from beginning");
-
+ OPENDAL_ASSERT_NO_ERROR(read_result.error,
+ "Read from beginning should succeed");
+ OPENDAL_ASSERT(memcmp("01234", buffer, 5) == 0,
+ "Should read correct content from beginning");
+
// Test seek from end
seek_result = opendal_reader_seek(reader_result.reader, -5,
OPENDAL_SEEK_END);
OPENDAL_ASSERT_NO_ERROR(seek_result.error, "Seek from end should succeed");
- OPENDAL_ASSERT_EQ(content_len - 5, seek_result.pos, "Position should be
content_len - 5");
-
+ OPENDAL_ASSERT_EQ(content_len - 5, seek_result.pos,
+ "Position should be content_len - 5");
+
// Read from near end
read_result = opendal_reader_read(reader_result.reader, buffer, 5);
- OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read from near end should
succeed");
- OPENDAL_ASSERT(memcmp("FGHIJ", buffer, 5) == 0, "Should read correct
content from near end");
-
+ OPENDAL_ASSERT_NO_ERROR(read_result.error,
+ "Read from near end should succeed");
+ OPENDAL_ASSERT(memcmp("FGHIJ", buffer, 5) == 0,
+ "Should read correct content from near end");
+
// Cleanup
opendal_reader_free(reader_result.reader);
opendal_operator_delete(ctx->config->operator_instance, path);
}
// Test: Basic writer operations
-void test_writer_basic(opendal_test_context* ctx) {
+void test_writer_basic(opendal_test_context* ctx)
+{
const char* path = "test_writer.txt";
const char* content1 = "Hello, ";
const char* content2 = "OpenDAL Writer!";
-
+
// Create writer
opendal_result_operator_writer writer_result =
opendal_operator_writer(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(writer_result.error, "Writer creation should
succeed");
+ OPENDAL_ASSERT_NO_ERROR(writer_result.error,
+ "Writer creation should succeed");
OPENDAL_ASSERT_NOT_NULL(writer_result.writer, "Writer should not be null");
-
+
// Write first part
opendal_bytes data1;
data1.data = (uint8_t*)content1;
data1.len = strlen(content1);
data1.capacity = strlen(content1);
-
+
opendal_result_writer_write write_result =
opendal_writer_write(writer_result.writer, &data1);
OPENDAL_ASSERT_NO_ERROR(write_result.error, "First write should succeed");
- OPENDAL_ASSERT_EQ(strlen(content1), write_result.size, "Write size should
match content length");
-
+ OPENDAL_ASSERT_EQ(strlen(content1), write_result.size,
+ "Write size should match content length");
+
// Write second part - handle OneShotWriter limitation
opendal_bytes data2;
data2.data = (uint8_t*)content2;
data2.len = strlen(content2);
data2.capacity = strlen(content2);
-
+
write_result = opendal_writer_write(writer_result.writer, &data2);
-
+
// Check if this is a OneShotWriter limitation
- if (write_result.error != NULL &&
- write_result.error->message.data != NULL &&
- strstr((char*)write_result.error->message.data, "OneShotWriter doesn't
support multiple write") != NULL) {
+ if (write_result.error != NULL && write_result.error->message.data != NULL
&& strstr((char*)write_result.error->message.data, "OneShotWriter doesn't
support multiple write") != NULL) {
printf("Note: Service uses OneShotWriter, skipping multiple write
test\n");
-
+
// Close current writer and verify single write worked
opendal_error* error = opendal_writer_close(writer_result.writer);
OPENDAL_ASSERT_NO_ERROR(error, "Writer close should succeed");
-
+
// Verify first write content
opendal_result_read read_result =
opendal_operator_read(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read should succeed");
- OPENDAL_ASSERT_EQ(strlen(content1), read_result.data.len, "Content
length should match first write");
- OPENDAL_ASSERT(memcmp(content1, read_result.data.data,
read_result.data.len) == 0,
- "Content should match first write");
-
+ OPENDAL_ASSERT_EQ(strlen(content1), read_result.data.len,
+ "Content length should match first write");
+ OPENDAL_ASSERT(
+ memcmp(content1, read_result.data.data, read_result.data.len) == 0,
+ "Content should match first write");
+
// Cleanup
opendal_bytes_free(&read_result.data);
opendal_writer_free(writer_result.writer);
opendal_operator_delete(ctx->config->operator_instance, path);
return;
}
-
+
OPENDAL_ASSERT_NO_ERROR(write_result.error, "Second write should succeed");
- OPENDAL_ASSERT_EQ(strlen(content2), write_result.size, "Write size should
match content length");
-
+ OPENDAL_ASSERT_EQ(strlen(content2), write_result.size,
+ "Write size should match content length");
+
// Close writer
opendal_error* error = opendal_writer_close(writer_result.writer);
OPENDAL_ASSERT_NO_ERROR(error, "Writer close should succeed");
-
+
// Verify written content
opendal_result_read read_result =
opendal_operator_read(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read should succeed");
-
+
size_t expected_len = strlen(content1) + strlen(content2);
- OPENDAL_ASSERT_EQ(expected_len, read_result.data.len, "Total content
length should match");
-
+ OPENDAL_ASSERT_EQ(expected_len, read_result.data.len,
+ "Total content length should match");
+
// Verify combined content
char expected_content[100];
- snprintf(expected_content, sizeof(expected_content), "%s%s", content1,
content2);
- OPENDAL_ASSERT(memcmp(expected_content, read_result.data.data,
read_result.data.len) == 0,
- "Combined content should match");
-
+ snprintf(expected_content, sizeof(expected_content), "%s%s", content1,
+ content2);
+ OPENDAL_ASSERT(memcmp(expected_content, read_result.data.data,
+ read_result.data.len)
+ == 0,
+ "Combined content should match");
+
// Cleanup
opendal_bytes_free(&read_result.data);
opendal_writer_free(writer_result.writer);
@@ -191,62 +209,65 @@ void test_writer_basic(opendal_test_context* ctx) {
}
// Test: Writer with large data
-void test_writer_large_data(opendal_test_context* ctx) {
+void test_writer_large_data(opendal_test_context* ctx)
+{
const char* path = "test_writer_large.txt";
const size_t chunk_size = 1024;
const size_t num_chunks = 10;
-
+
// Create writer
opendal_result_operator_writer writer_result =
opendal_operator_writer(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(writer_result.error, "Writer creation should
succeed");
-
+ OPENDAL_ASSERT_NO_ERROR(writer_result.error,
+ "Writer creation should succeed");
+
// Generate and write chunks
char* chunk_data = opendal_generate_random_content(chunk_size);
OPENDAL_ASSERT_NOT_NULL(chunk_data, "Chunk data generation should
succeed");
-
+
opendal_bytes chunk;
chunk.data = (uint8_t*)chunk_data;
chunk.len = chunk_size;
chunk.capacity = chunk_size;
-
+
size_t total_written = 0;
bool is_one_shot_writer = false;
-
+
for (size_t i = 0; i < num_chunks; i++) {
opendal_result_writer_write write_result =
opendal_writer_write(writer_result.writer, &chunk);
-
+
// Check for OneShotWriter limitation on subsequent writes
- if (i > 0 && write_result.error != NULL &&
- write_result.error->message.data != NULL &&
- strstr((char*)write_result.error->message.data, "OneShotWriter
doesn't support multiple write") != NULL) {
+ if (i > 0 && write_result.error != NULL &&
write_result.error->message.data != NULL &&
strstr((char*)write_result.error->message.data, "OneShotWriter doesn't support
multiple write") != NULL) {
printf("Note: Service uses OneShotWriter, completed %zu
write(s)\n", i);
is_one_shot_writer = true;
break;
}
-
+
OPENDAL_ASSERT_NO_ERROR(write_result.error, "Write should succeed");
- OPENDAL_ASSERT_EQ(chunk_size, write_result.size, "Write size should
match chunk size");
+ OPENDAL_ASSERT_EQ(chunk_size, write_result.size,
+ "Write size should match chunk size");
total_written += write_result.size;
}
-
+
// Close writer
opendal_error* error = opendal_writer_close(writer_result.writer);
OPENDAL_ASSERT_NO_ERROR(error, "Writer close should succeed");
-
+
// Verify total size - adjust expectations for OneShotWriter
opendal_result_stat stat_result =
opendal_operator_stat(ctx->config->operator_instance, path);
OPENDAL_ASSERT_NO_ERROR(stat_result.error, "Stat should succeed");
-
+
if (is_one_shot_writer) {
// For OneShotWriter, we expect only one chunk to be written
- OPENDAL_ASSERT_EQ(chunk_size,
opendal_metadata_content_length(stat_result.meta),
- "OneShotWriter should have written one chunk");
+ OPENDAL_ASSERT_EQ(chunk_size,
+ opendal_metadata_content_length(stat_result.meta),
+ "OneShotWriter should have written one chunk");
} else {
// For normal writers, we expect all chunks
- OPENDAL_ASSERT_EQ(chunk_size * num_chunks,
opendal_metadata_content_length(stat_result.meta),
- "Total file size should match all chunks");
+ OPENDAL_ASSERT_EQ(chunk_size * num_chunks,
+ opendal_metadata_content_length(stat_result.meta),
+ "Total file size should match all chunks");
}
-
+
// Cleanup
free(chunk_data);
opendal_metadata_free(stat_result.meta);
@@ -255,46 +276,49 @@ void test_writer_large_data(opendal_test_context* ctx) {
}
// Test: Reader partial read
-void test_reader_partial_read(opendal_test_context* ctx) {
+void test_reader_partial_read(opendal_test_context* ctx)
+{
const char* path = "test_reader_partial.txt";
const char* content = "0123456789ABCDEFGHIJ0123456789";
size_t content_len = strlen(content);
-
+
// Write test data
opendal_bytes data;
data.data = (uint8_t*)content;
data.len = content_len;
data.capacity = content_len;
-
+
opendal_error* error =
opendal_operator_write(ctx->config->operator_instance, path, &data);
OPENDAL_ASSERT_NO_ERROR(error, "Write operation should succeed");
-
+
// Create reader
opendal_result_operator_reader reader_result =
opendal_operator_reader(ctx->config->operator_instance, path);
- OPENDAL_ASSERT_NO_ERROR(reader_result.error, "Reader creation should
succeed");
-
+ OPENDAL_ASSERT_NO_ERROR(reader_result.error,
+ "Reader creation should succeed");
+
// Read in small chunks
const size_t chunk_size = 5;
uint8_t buffer[chunk_size];
size_t total_read = 0;
-
+
while (total_read < content_len) {
opendal_result_reader_read read_result =
opendal_reader_read(reader_result.reader, buffer, chunk_size);
OPENDAL_ASSERT_NO_ERROR(read_result.error, "Read should succeed");
-
+
if (read_result.size == 0) {
- break; // EOF
+ break; // EOF
}
-
+
// Verify chunk content
- OPENDAL_ASSERT(memcmp(content + total_read, buffer, read_result.size)
== 0,
- "Chunk content should match");
-
+ OPENDAL_ASSERT(memcmp(content + total_read, buffer, read_result.size)
== 0,
+ "Chunk content should match");
+
total_read += read_result.size;
}
-
- OPENDAL_ASSERT_EQ(content_len, total_read, "Total read should match
content length");
-
+
+ OPENDAL_ASSERT_EQ(content_len, total_read,
+ "Total read should match content length");
+
// Cleanup
opendal_reader_free(reader_result.reader);
opendal_operator_delete(ctx->config->operator_instance, path);
@@ -302,15 +326,16 @@ void test_reader_partial_read(opendal_test_context* ctx) {
// Define the reader/writer test suite
opendal_test_case reader_writer_tests[] = {
- {"reader_basic", test_reader_basic, make_capability_read_write()},
- {"reader_seek", test_reader_seek, make_capability_read_write()},
- {"writer_basic", test_writer_basic, make_capability_read_write()},
- {"writer_large_data", test_writer_large_data,
make_capability_write_stat()},
- {"reader_partial_read", test_reader_partial_read,
make_capability_read_write()},
+ { "reader_basic", test_reader_basic, make_capability_read_write() },
+ { "reader_seek", test_reader_seek, make_capability_read_write() },
+ { "writer_basic", test_writer_basic, make_capability_read_write() },
+ { "writer_large_data", test_writer_large_data,
make_capability_write_stat() },
+ { "reader_partial_read", test_reader_partial_read,
+ make_capability_read_write() },
};
opendal_test_suite reader_writer_suite = {
- "Reader and Writer Operations", // name
- reader_writer_tests, // tests
- sizeof(reader_writer_tests) / sizeof(reader_writer_tests[0]) // test_count
-};
\ No newline at end of file
+ "Reader and Writer Operations", // name
+ reader_writer_tests, // tests
+ sizeof(reader_writer_tests) / sizeof(reader_writer_tests[0]) // test_count
+};
diff --git a/bindings/cpp/src/async.rs b/bindings/cpp/src/async.rs
index d0a3d2d5d..29038589c 100644
--- a/bindings/cpp/src/async.rs
+++ b/bindings/cpp/src/async.rs
@@ -23,7 +23,6 @@ use std::collections::HashMap;
use std::future::Future;
use std::ops::Deref;
use std::pin::Pin;
-use std::str::FromStr;
use std::sync::{Arc, OnceLock};
#[cxx::bridge(namespace = opendal::ffi::async_op)]
@@ -148,8 +147,6 @@ fn get_lister_counter() -> &'static Mutex<usize> {
}
fn new_operator(scheme: &str, configs: Vec<ffi::HashMapValue>) ->
Result<Box<Operator>> {
- let scheme = od::Scheme::from_str(scheme)?;
-
let map: HashMap<String, String> = configs
.into_iter()
.map(|value| (value.key, value.value))
diff --git a/bindings/cpp/src/lib.rs b/bindings/cpp/src/lib.rs
index 409606e17..ae05423b3 100644
--- a/bindings/cpp/src/lib.rs
+++ b/bindings/cpp/src/lib.rs
@@ -22,7 +22,6 @@ mod reader;
mod types;
use std::collections::HashMap;
-use std::str::FromStr;
use std::sync::LazyLock;
use anyhow::Result;
@@ -161,8 +160,6 @@ mod ffi {
pub struct Operator(od::blocking::Operator);
fn new_operator(scheme: &str, configs: Vec<ffi::HashMapValue>) -> Result<*mut
Operator> {
- let scheme = od::Scheme::from_str(scheme)?;
-
let map: HashMap<String, String> = configs
.into_iter()
.map(|value| (value.key, value.value))
diff --git a/bindings/dart/rust/src/api/opendal_api.rs
b/bindings/dart/rust/src/api/opendal_api.rs
index 8030abe0b..70add349d 100644
--- a/bindings/dart/rust/src/api/opendal_api.rs
+++ b/bindings/dart/rust/src/api/opendal_api.rs
@@ -20,7 +20,6 @@ use flutter_rust_bridge::frb;
use ::opendal as od;
use std::collections::HashMap;
-use std::str::FromStr;
use std::sync::LazyLock;
static RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
@@ -38,8 +37,7 @@ pub struct Operator {
impl Operator {
#[frb(sync)]
- pub fn new(scheme_str: String, map: HashMap<String, String>) -> Operator {
- let scheme: od::Scheme = od::Scheme::from_str(&scheme_str).unwrap();
+ pub fn new(scheme: String, map: HashMap<String, String>) -> Operator {
let async_op = od::Operator::via_iter(scheme, map).unwrap();
let handle = RUNTIME.handle();
let _enter = handle.enter();
diff --git a/bindings/dotnet/src/lib.rs b/bindings/dotnet/src/lib.rs
index 51988b028..2bce2a9c1 100644
--- a/bindings/dotnet/src/lib.rs
+++ b/bindings/dotnet/src/lib.rs
@@ -17,7 +17,6 @@
use std::collections::HashMap;
use std::os::raw::c_char;
-use std::str::FromStr;
use std::sync::LazyLock;
static RUNTIME: LazyLock<tokio::runtime::Runtime> = LazyLock::new(|| {
@@ -38,14 +37,9 @@ pub unsafe extern "C" fn blocking_operator_construct(
return std::ptr::null();
}
- let scheme = match opendal::Scheme::from_str(
- unsafe { std::ffi::CStr::from_ptr(scheme) }
- .to_str()
- .unwrap(),
- ) {
- Ok(scheme) => scheme,
- Err(_) => return std::ptr::null(),
- };
+ let scheme = unsafe { std::ffi::CStr::from_ptr(scheme) }
+ .to_str()
+ .unwrap();
let mut map = HashMap::<String, String>::default();
map.insert("root".to_string(), "/tmp".to_string());
diff --git a/bindings/go/operator.go b/bindings/go/operator.go
index 46a996033..ec5e65f09 100644
--- a/bindings/go/operator.go
+++ b/bindings/go/operator.go
@@ -21,6 +21,7 @@ package opendal
import (
"context"
+ "strings"
"unsafe"
"github.com/jupiterrider/ffi"
@@ -103,8 +104,10 @@ var ffiOperatorNew = newFFI(ffiOpts{
aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer},
}, func(ctx context.Context, ffiCall ffiCall) func(scheme Scheme, opts
*operatorOptions) (op *opendalOperator, err error) {
return func(scheme Scheme, opts *operatorOptions) (op *opendalOperator,
err error) {
+ // This is a temporary fix; it can be removed once we fix the
template generation code in opendal-go-services.
+ normalizedSchemeName := strings.ReplaceAll(scheme.Name(), "_",
"-")
var byteName *byte
- byteName, err = BytePtrFromString(scheme.Name())
+ byteName, err = BytePtrFromString(normalizedSchemeName)
if err != nil {
return nil, err
}
diff --git a/bindings/go/tests/behavior_tests/opendal_test.go
b/bindings/go/tests/behavior_tests/opendal_test.go
index 5d00303f7..d05a162eb 100644
--- a/bindings/go/tests/behavior_tests/opendal_test.go
+++ b/bindings/go/tests/behavior_tests/opendal_test.go
@@ -97,7 +97,9 @@ func newOperator() (op *opendal.Operator, closeFunc func(),
err error) {
test := os.Getenv("OPENDAL_TEST")
var scheme opendal.Scheme
for _, s := range schemes {
- if s.Name() != test {
+ // This is a temporary fix; it can be removed once we fix the
template generation code in opendal-go-services.
+ normalizedSchemeName := strings.ReplaceAll(test, "_", "-")
+ if s.Name() != test && s.Name() != normalizedSchemeName {
continue
}
err = s.LoadOnce()
diff --git a/bindings/haskell/src/lib.rs b/bindings/haskell/src/lib.rs
index 96ed7d561..a142fe603 100644
--- a/bindings/haskell/src/lib.rs
+++ b/bindings/haskell/src/lib.rs
@@ -24,7 +24,6 @@ use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::os::raw::c_char;
-use std::str::FromStr;
use std::sync::LazyLock;
use ::opendal as od;
@@ -68,7 +67,7 @@ pub unsafe extern "C" fn via_map_ffi(
result: *mut FFIResult<od::blocking::Operator>,
) {
unsafe {
- let scheme_str = match CStr::from_ptr(scheme).to_str() {
+ let scheme = match CStr::from_ptr(scheme).to_str() {
Ok(s) => s,
Err(_) => {
*result = FFIResult::err("Failed to convert scheme to string");
@@ -76,14 +75,6 @@ pub unsafe extern "C" fn via_map_ffi(
}
};
- let scheme = match od::Scheme::from_str(scheme_str) {
- Ok(s) => s,
- Err(_) => {
- *result = FFIResult::err("Failed to parse scheme");
- return;
- }
- };
-
let keys_vec = std::slice::from_raw_parts(keys, len);
let values_vec = std::slice::from_raw_parts(values, len);
diff --git a/bindings/java/Cargo.toml b/bindings/java/Cargo.toml
index 6fc300e75..a671f6c61 100644
--- a/bindings/java/Cargo.toml
+++ b/bindings/java/Cargo.toml
@@ -92,70 +92,73 @@ services-all = [
"services-yandex-disk",
]
-# Default services provided by opendal.
+# Optional services provided by opendal.
services-aliyun-drive = ["opendal/services-aliyun-drive"]
+services-alluxio = ["opendal/services-alluxio"]
services-azblob = ["opendal/services-azblob"]
services-azdls = ["opendal/services-azdls"]
-services-cos = ["opendal/services-cos"]
-services-fs = ["opendal/services-fs"]
-services-gcs = ["opendal/services-gcs"]
-services-ghac = ["opendal/services-ghac"]
-services-http = ["opendal/services-http"]
-services-ipmfs = ["opendal/services-ipmfs"]
-services-memory = ["opendal/services-memory"]
-services-obs = ["opendal/services-obs"]
-services-oss = ["opendal/services-oss"]
-services-s3 = ["opendal/services-s3"]
-services-webdav = ["opendal/services-webdav"]
-services-webhdfs = ["opendal/services-webhdfs"]
-
-# Optional services provided by opendal.
-services-alluxio = ["opendal/services-alluxio"]
services-azfile = ["opendal/services-azfile"]
services-b2 = ["opendal/services-b2"]
services-cacache = ["opendal/services-cacache"]
+services-cloudflare-kv = ["opendal/services-cloudflare-kv"]
+services-compfs = ["opendal/services-compfs"]
+services-cos = ["opendal/services-cos"]
services-dashmap = ["opendal/services-dashmap"]
+services-dbfs = ["opendal/services-dbfs"]
services-dropbox = ["opendal/services-dropbox"]
services-etcd = ["opendal/services-etcd"]
services-foundationdb = ["opendal/services-foundationdb"]
+services-fs = ["opendal/services-fs"]
services-ftp = ["opendal/services-ftp"]
+services-gcs = ["opendal/services-gcs"]
services-gdrive = ["opendal/services-gdrive"]
+services-ghac = ["opendal/services-ghac"]
+services-github = ["opendal/services-github"]
services-gridfs = ["opendal/services-gridfs"]
services-hdfs = ["opendal/services-hdfs"]
services-hdfs-native = ["opendal/services-hdfs-native"]
+services-http = ["opendal/services-http"]
services-huggingface = ["opendal/services-huggingface"]
services-ipfs = ["opendal/services-ipfs"]
+services-ipmfs = ["opendal/services-ipmfs"]
services-koofr = ["opendal/services-koofr"]
+services-lakefs = ["opendal/services-lakefs"]
services-memcached = ["opendal/services-memcached"]
+services-memory = ["opendal/services-memory"]
services-mini-moka = ["opendal/services-mini-moka"]
services-moka = ["opendal/services-moka"]
services-mongodb = ["opendal/services-mongodb"]
services-monoiofs = ["opendal/services-monoiofs"]
services-mysql = ["opendal/services-mysql"]
+services-obs = ["opendal/services-obs"]
services-onedrive = ["opendal/services-onedrive"]
+services-oss = ["opendal/services-oss"]
+services-pcloud = ["opendal/services-pcloud"]
services-persy = ["opendal/services-persy"]
services-postgresql = ["opendal/services-postgresql"]
services-redb = ["opendal/services-redb"]
services-redis = ["opendal/services-redis"]
services-rocksdb = ["opendal/services-rocksdb"]
+services-s3 = ["opendal/services-s3"]
services-seafile = ["opendal/services-seafile"]
services-sftp = ["opendal/services-sftp"]
services-sled = ["opendal/services-sled"]
services-sqlite = ["opendal/services-sqlite"]
+services-surrealdb = ["opendal/services-surrealdb"]
services-swift = ["opendal/services-swift"]
services-tikv = ["opendal/services-tikv"]
services-upyun = ["opendal/services-upyun"]
services-vercel-artifacts = ["opendal/services-vercel-artifacts"]
+services-vercel-blob = ["opendal/services-vercel-blob"]
+services-webdav = ["opendal/services-webdav"]
+services-webhdfs = ["opendal/services-webhdfs"]
services-yandex-disk = ["opendal/services-yandex-disk"]
-services-compfs = ["opendal/services-compfs"]
[dependencies]
anyhow = { version = "1.0.100" }
jni = { version = "0.21.1" }
# this crate won't be published, we always use the local version
-opendal = { version = ">=0", path = "../../core", features = [
- "blocking",
-] }
+opendal = { version = ">=0", path = "../../core", features = ["blocking"] }
tokio = { version = "1.28.1", features = ["full"] }
# This is not optimal. See also the Cargo issue:
diff --git a/bindings/java/src/async_operator.rs
b/bindings/java/src/async_operator.rs
index 5675175bd..0494e7895 100644
--- a/bindings/java/src/async_operator.rs
+++ b/bindings/java/src/async_operator.rs
@@ -15,7 +15,6 @@
// specific language governing permissions and limitations
// under the License.
-use std::str::FromStr;
use std::time::Duration;
use jni::JNIEnv;
@@ -30,7 +29,6 @@ use jni::sys::jobject;
use jni::sys::jsize;
use opendal::Entry;
use opendal::Operator;
-use opendal::Scheme;
use opendal::blocking;
use crate::Result;
@@ -60,7 +58,7 @@ pub extern "system" fn
Java_org_apache_opendal_AsyncOperator_constructor(
}
fn intern_constructor(env: &mut JNIEnv, scheme: JString, map: JObject) ->
Result<jlong> {
- let scheme = Scheme::from_str(jstring_to_string(env, &scheme)?.as_str())?;
+ let scheme = jstring_to_string(env, &scheme)?;
let map = jmap_to_hashmap(env, &map)?;
let op = Operator::via_iter(scheme, map)?;
Ok(Box::into_raw(Box::new(op)) as jlong)
diff --git
a/bindings/java/src/test/java/org/apache/opendal/test/behavior/BehaviorExtension.java
b/bindings/java/src/test/java/org/apache/opendal/test/behavior/BehaviorExtension.java
index d48c645a4..367e79c66 100644
---
a/bindings/java/src/test/java/org/apache/opendal/test/behavior/BehaviorExtension.java
+++
b/bindings/java/src/test/java/org/apache/opendal/test/behavior/BehaviorExtension.java
@@ -64,7 +64,8 @@ public class BehaviorExtension implements BeforeAllCallback,
AfterAllCallback, T
config.put("root", root);
}
- @Cleanup final AsyncOperator op = AsyncOperator.of(scheme, config);
+ @Cleanup
+ final AsyncOperator op =
AsyncOperator.of(scheme.toLowerCase().replace('_', '-'), config);
this.asyncOperator = op.layer(RetryLayer.builder().build());
this.operator = this.asyncOperator.blocking();
diff --git a/bindings/java/src/utility.rs b/bindings/java/src/utility.rs
index 00e9388b4..6696aae74 100644
--- a/bindings/java/src/utility.rs
+++ b/bindings/java/src/utility.rs
@@ -15,12 +15,13 @@
// specific language governing permissions and limitations
// under the License.
+use std::collections::HashSet;
+
use jni::JNIEnv;
use jni::objects::JClass;
use jni::objects::JObject;
use jni::sys::jobjectArray;
use jni::sys::jsize;
-use opendal::Scheme;
use crate::Result;
use crate::convert::string_to_jstring;
@@ -40,11 +41,133 @@ pub unsafe extern "system" fn
Java_org_apache_opendal_OpenDAL_loadEnabledService
}
fn intern_load_enabled_services(env: &mut JNIEnv) -> Result<jobjectArray> {
- let services = Scheme::enabled();
+ let services = HashSet::from([
+ #[cfg(feature = "services-aliyun-drive")]
+ opendal::services::ALIYUN_DRIVE_SCHEME,
+ #[cfg(feature = "services-alluxio")]
+ opendal::services::ALLUXIO_SCHEME,
+ #[cfg(feature = "services-azblob")]
+ opendal::services::AZBLOB_SCHEME,
+ #[cfg(feature = "services-azdls")]
+ opendal::services::AZDLS_SCHEME,
+ #[cfg(feature = "services-azfile")]
+ opendal::services::AZFILE_SCHEME,
+ #[cfg(feature = "services-b2")]
+ opendal::services::B2_SCHEME,
+ #[cfg(feature = "services-cacache")]
+ opendal::services::CACACHE_SCHEME,
+ #[cfg(feature = "services-cloudflare-kv")]
+ opendal::services::CLOUDFLARE_KV_SCHEME,
+ #[cfg(feature = "services-compfs")]
+ opendal::services::COMPFS_SCHEME,
+ #[cfg(feature = "services-cos")]
+ opendal::services::COS_SCHEME,
+ #[cfg(feature = "services-dashmap")]
+ opendal::services::DASHMAP_SCHEME,
+ #[cfg(feature = "services-dbfs")]
+ opendal::services::DBFS_SCHEME,
+ #[cfg(feature = "services-dropbox")]
+ opendal::services::DROPBOX_SCHEME,
+ #[cfg(feature = "services-etcd")]
+ opendal::services::ETCD_SCHEME,
+ #[cfg(feature = "services-foundationdb")]
+ opendal::services::FOUNDATIONDB_SCHEME,
+ #[cfg(feature = "services-fs")]
+ opendal::services::FS_SCHEME,
+ #[cfg(feature = "services-ftp")]
+ opendal::services::FTP_SCHEME,
+ #[cfg(feature = "services-gcs")]
+ opendal::services::GCS_SCHEME,
+ #[cfg(feature = "services-gdrive")]
+ opendal::services::GDRIVE_SCHEME,
+ #[cfg(feature = "services-ghac")]
+ opendal::services::GHAC_SCHEME,
+ #[cfg(feature = "services-github")]
+ opendal::services::GITHUB_SCHEME,
+ #[cfg(feature = "services-gridfs")]
+ opendal::services::GRIDFS_SCHEME,
+ #[cfg(feature = "services-hdfs")]
+ opendal::services::HDFS_SCHEME,
+ #[cfg(feature = "services-hdfs-native")]
+ opendal::services::HDFS_NATIVE_SCHEME,
+ #[cfg(feature = "services-http")]
+ opendal::services::HTTP_SCHEME,
+ #[cfg(feature = "services-huggingface")]
+ opendal::services::HUGGINGFACE_SCHEME,
+ #[cfg(feature = "services-ipfs")]
+ opendal::services::IPFS_SCHEME,
+ #[cfg(feature = "services-ipmfs")]
+ opendal::services::IPMFS_SCHEME,
+ #[cfg(feature = "services-koofr")]
+ opendal::services::KOOFR_SCHEME,
+ #[cfg(feature = "services-lakefs")]
+ opendal::services::LAKEFS_SCHEME,
+ #[cfg(feature = "services-memcached")]
+ opendal::services::MEMCACHED_SCHEME,
+ #[cfg(feature = "services-memory")]
+ opendal::services::MEMORY_SCHEME,
+ #[cfg(feature = "services-mini-moka")]
+ opendal::services::MINI_MOKA_SCHEME,
+ #[cfg(feature = "services-moka")]
+ opendal::services::MOKA_SCHEME,
+ #[cfg(feature = "services-mongodb")]
+ opendal::services::MONGODB_SCHEME,
+ #[cfg(feature = "services-monoiofs")]
+ opendal::services::MONOIOFS_SCHEME,
+ #[cfg(feature = "services-mysql")]
+ opendal::services::MYSQL_SCHEME,
+ #[cfg(feature = "services-obs")]
+ opendal::services::OBS_SCHEME,
+ #[cfg(feature = "services-onedrive")]
+ opendal::services::ONEDRIVE_SCHEME,
+ #[cfg(feature = "services-oss")]
+ opendal::services::OSS_SCHEME,
+ #[cfg(feature = "services-pcloud")]
+ opendal::services::PCLOUD_SCHEME,
+ #[cfg(feature = "services-persy")]
+ opendal::services::PERSY_SCHEME,
+ #[cfg(feature = "services-postgresql")]
+ opendal::services::POSTGRESQL_SCHEME,
+ #[cfg(feature = "services-redb")]
+ opendal::services::REDB_SCHEME,
+ #[cfg(feature = "services-redis")]
+ opendal::services::REDIS_SCHEME,
+ #[cfg(feature = "services-rocksdb")]
+ opendal::services::ROCKSDB_SCHEME,
+ #[cfg(feature = "services-s3")]
+ opendal::services::S3_SCHEME,
+ #[cfg(feature = "services-seafile")]
+ opendal::services::SEAFILE_SCHEME,
+ #[cfg(feature = "services-sftp")]
+ opendal::services::SFTP_SCHEME,
+ #[cfg(feature = "services-sled")]
+ opendal::services::SLED_SCHEME,
+ #[cfg(feature = "services-sqlite")]
+ opendal::services::SQLITE_SCHEME,
+ #[cfg(feature = "services-surrealdb")]
+ opendal::services::SURREALDB_SCHEME,
+ #[cfg(feature = "services-swift")]
+ opendal::services::SWIFT_SCHEME,
+ #[cfg(feature = "services-tikv")]
+ opendal::services::TIKV_SCHEME,
+ #[cfg(feature = "services-upyun")]
+ opendal::services::UPYUN_SCHEME,
+ #[cfg(feature = "services-vercel-artifacts")]
+ opendal::services::VERCEL_ARTIFACTS_SCHEME,
+ #[cfg(feature = "services-vercel-blob")]
+ opendal::services::VERCEL_BLOB_SCHEME,
+ #[cfg(feature = "services-webdav")]
+ opendal::services::WEBDAV_SCHEME,
+ #[cfg(feature = "services-webhdfs")]
+ opendal::services::WEBHDFS_SCHEME,
+ #[cfg(feature = "services-yandex-disk")]
+ opendal::services::YANDEX_DISK_SCHEME,
+ ]);
+
let res = env.new_object_array(services.len() as jsize,
"java/lang/String", JObject::null())?;
- for (idx, service) in services.iter().enumerate() {
- let srv = string_to_jstring(env, Some(service.as_ref()))?;
+ for (idx, service) in services.into_iter().enumerate() {
+ let srv = string_to_jstring(env, Some(service))?;
env.set_object_array_element(&res, idx as jsize, srv)?;
}
diff --git a/bindings/lua/src/lib.rs b/bindings/lua/src/lib.rs
index 5b55015f1..c930b79e3 100644
--- a/bindings/lua/src/lib.rs
+++ b/bindings/lua/src/lib.rs
@@ -16,7 +16,6 @@
// under the License.
use std::collections::HashMap;
-use std::str::FromStr;
use std::sync::LazyLock;
use ::opendal as od;
@@ -46,10 +45,10 @@ impl UserData for ODMetadata {}
fn operator_new<'a>(
lua: &'a Lua,
- (schema, option): (String, LuaTable<'a>),
+ (scheme, option): (String, LuaTable<'a>),
) -> LuaResult<LuaTable<'a>> {
- if schema.is_empty() {
- return Err(LuaError::external("schema is empty"));
+ if scheme.is_empty() {
+ return Err(LuaError::external("scheme is empty"));
}
let mut map = HashMap::<String, String>::default();
@@ -58,12 +57,7 @@ fn operator_new<'a>(
map.insert(key, value);
}
- let od_schema = match od::Scheme::from_str(&schema) {
- Ok(s) => s,
- Err(e) => return Err(LuaError::external(e)),
- };
-
- let op = match od::Operator::via_iter(od_schema, map) {
+ let op = match od::Operator::via_iter(scheme, map) {
Ok(o) => o,
Err(e) => return Err(LuaError::external(e)),
};
diff --git a/bindings/nodejs/src/lib.rs b/bindings/nodejs/src/lib.rs
index de7ca38bc..510b425eb 100644
--- a/bindings/nodejs/src/lib.rs
+++ b/bindings/nodejs/src/lib.rs
@@ -21,7 +21,6 @@ extern crate napi_derive;
use std::collections::HashMap;
use std::fmt::Display;
use std::io::Read;
-use std::str::FromStr;
use std::time::Duration;
use futures::AsyncReadExt;
@@ -51,12 +50,6 @@ impl Operator {
/// Note that the current options key is snake_case.
#[napi(constructor, async_runtime)]
pub fn new(scheme: String, options: Option<HashMap<String, String>>) ->
Result<Self> {
- let scheme = opendal::Scheme::from_str(&scheme)
- .map_err(|err| {
- opendal::Error::new(opendal::ErrorKind::Unexpected, "not
supported scheme")
- .set_source(err)
- })
- .map_err(format_napi_error)?;
let options = options.unwrap_or_default();
let async_op = opendal::Operator::via_iter(scheme,
options).map_err(format_napi_error)?;
diff --git a/bindings/nodejs/tests/utils.mjs b/bindings/nodejs/tests/utils.mjs
index bf685612e..1e3caf352 100644
--- a/bindings/nodejs/tests/utils.mjs
+++ b/bindings/nodejs/tests/utils.mjs
@@ -30,7 +30,9 @@ export function generateFixedBytes(size) {
}
export function loadTestSchemeFromEnv() {
- return process.env.OPENDAL_TEST
+ const scheme = process.env.OPENDAL_TEST
+ // Normalize scheme by replacing underscores with hyphens
+ return scheme ? scheme.replace(/_/g, '-') : scheme
}
export function checkRandomRootEnabled() {
@@ -44,7 +46,9 @@ export function generateRandomRoot(baseRoot) {
export function loadConfigFromEnv(scheme) {
if (!scheme) return {}
- const prefix = `opendal_${scheme}_`
+ // Convert scheme to underscore format for environment variable prefix
+ const schemeKey = scheme.replace(/-/g, '_')
+ const prefix = `opendal_${schemeKey}_`
return Object.fromEntries(
Object.entries(process.env)
diff --git a/bindings/ocaml/src/lib.rs b/bindings/ocaml/src/lib.rs
index 4ea8e6000..219c3c86c 100644
--- a/bindings/ocaml/src/lib.rs
+++ b/bindings/ocaml/src/lib.rs
@@ -17,7 +17,6 @@
use std::collections::BTreeMap;
use std::collections::HashMap;
-use std::str::FromStr;
use std::sync::LazyLock;
use ::opendal as od;
@@ -33,11 +32,10 @@ static RUNTIME: LazyLock<tokio::runtime::Runtime> =
LazyLock::new(|| {
});
pub fn new_operator(
- scheme_str: String,
+ scheme: String,
map: BTreeMap<String, String>,
) -> Result<od::Operator, od::Error> {
let hm: HashMap<String, String> = map.into_iter().collect();
- let scheme: od::Scheme = od::Scheme::from_str(&scheme_str)?;
od::Operator::via_iter(scheme, hm)
}
diff --git a/bindings/php/src/lib.rs b/bindings/php/src/lib.rs
index 8243f644e..30801b00c 100644
--- a/bindings/php/src/lib.rs
+++ b/bindings/php/src/lib.rs
@@ -16,7 +16,6 @@
// under the License.
use std::collections::HashMap;
-use std::str::FromStr;
use std::sync::LazyLock;
use ::opendal as od;
@@ -39,8 +38,7 @@ pub struct Operator(od::blocking::Operator);
#[php_impl(rename_methods = "none")]
impl Operator {
- pub fn __construct(scheme_str: String, config: HashMap<String, String>) ->
PhpResult<Self> {
- let scheme =
od::Scheme::from_str(&scheme_str).map_err(format_php_err)?;
+ pub fn __construct(scheme: String, config: HashMap<String, String>) ->
PhpResult<Self> {
let op = od::Operator::via_iter(scheme,
config).map_err(format_php_err)?;
let handle = RUNTIME.handle();
diff --git a/bindings/ruby/src/operator.rs b/bindings/ruby/src/operator.rs
index f36bacaf3..bb92a7b4b 100644
--- a/bindings/ruby/src/operator.rs
+++ b/bindings/ruby/src/operator.rs
@@ -23,7 +23,6 @@
#![allow(rustdoc::bare_urls, reason = "YARD's syntax for documentation")]
use std::collections::HashMap;
-use std::str::FromStr;
use magnus::Error;
use magnus::RHash;
@@ -88,12 +87,6 @@ impl Operator {
scheme: String,
options: Option<HashMap<String, String>>,
) -> Result<Self, Error> {
- let scheme = ocore::Scheme::from_str(&scheme)
- .map_err(|err| {
- ocore::Error::new(ocore::ErrorKind::Unexpected, "unsupported
scheme")
- .set_source(err)
- })
- .map_err(|err| Error::new(ruby.exception_runtime_error(),
err.to_string()))?;
let options = options.unwrap_or_default();
let op = ocore::Operator::via_iter(scheme, options)