[Asterisk-code-review] bucket: Add a clone operation for ast bucket/ast bucket file (asterisk[master])

Matt Jordan asteriskteam at digium.com
Sat Jun 20 17:42:15 CDT 2015


Matt Jordan has uploaded a new change for review.

  https://gerrit.asterisk.org/672

Change subject: bucket: Add a clone operation for ast_bucket/ast_bucket_file
......................................................................

bucket: Add a clone operation for ast_bucket/ast_bucket_file

In the bucket API, the ast_bucket and ast_bucket_file instances are
immutable. As a result, any update operation must first clone the
existing instances before executing. Prior to this patch, there was no
provided clone operation for the ast_bucket/ast_bucket_file objects.
This patch adds those operations. Note that this makes use of the
ast_sorcery_copy operation, along with the copy callback handler on the
"bucket" and "file" object types for the bucket sorcery instance.

Unit tests have also been added to cover the respective
ast_bucket/ast_bucket_file clone operations.

Change-Id: Ib0240ba915ece313f1678a085a716021d75d6b4a
---
M include/asterisk/bucket.h
M main/bucket.c
M tests/test_bucket.c
3 files changed, 172 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/72/672/1

diff --git a/include/asterisk/bucket.h b/include/asterisk/bucket.h
index da83759..ab92243 100644
--- a/include/asterisk/bucket.h
+++ b/include/asterisk/bucket.h
@@ -218,6 +218,19 @@
 int ast_bucket_create(struct ast_bucket *bucket);
 
 /*!
+ * \brief Clone a bucket
+ *
+ * \param file The bucket to clone
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This operation should be called prior to updating a bucket
+ * object, as \c ast_bucket instances are immutable
+ */
+struct ast_bucket *ast_bucket_clone(struct ast_bucket *bucket);
+
+/*!
  * \brief Delete a bucket from backend storage
  *
  * \param bucket The bucket
@@ -309,6 +322,19 @@
 struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri);
 
 /*!
+ * \brief Clone a bucket file
+ *
+ * \param file The bucket file to clone
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ *
+ * \note This operation should be called prior to updating a bucket file
+ * object, as \c ast_bucket_file instances are immutable
+ */
+struct ast_bucket_file *ast_bucket_file_clone(struct ast_bucket_file *file);
+
+/*!
  * \brief Update an existing bucket file in backend storage
  *
  * \param file The bucket file
diff --git a/main/bucket.c b/main/bucket.c
index e93c66b..7d65f76 100644
--- a/main/bucket.c
+++ b/main/bucket.c
@@ -459,6 +459,28 @@
 	return ast_sorcery_create(bucket_sorcery, bucket);
 }
 
+/*!
+ * \internal
+ * \brief Sorcery object type copy handler for \c ast_bucket
+ */
+static int bucket_copy_handler(const void *src, void *dst)
+{
+	const struct ast_bucket *src_bucket = src;
+	struct ast_bucket *dst_bucket = dst;
+
+	dst_bucket->scheme_impl = ao2_bump(src_bucket->scheme_impl);
+	ast_string_field_set(dst_bucket, scheme, src_bucket->scheme);
+	dst_bucket->created = src_bucket->created;
+	dst_bucket->modified = src_bucket->modified;
+
+	return 0;
+}
+
+struct ast_bucket *ast_bucket_clone(struct ast_bucket *bucket)
+{
+	return ast_sorcery_copy(bucket_sorcery, bucket);
+}
+
 struct ast_bucket *ast_bucket_retrieve(const char *uri)
 {
 	if (ast_strlen_zero(uri)) {
@@ -730,6 +752,29 @@
 	return 0;	/* success */
 }
 
+/*!
+ * \internal
+ * \brief Sorcery object type copy handler for \c ast_bucket_file
+ */
+static int bucket_file_copy_handler(const void *src, void *dst)
+{
+	const struct ast_bucket_file *src_file = src;
+	struct ast_bucket_file *dst_file = dst;
+
+	dst_file->scheme_impl = ao2_bump(src_file->scheme_impl);
+	ast_string_field_set(dst_file, scheme, src_file->scheme);
+	dst_file->created = src_file->created;
+	dst_file->modified = src_file->modified;
+	strcpy(dst_file->path, src_file->path); /* safe */
+
+	dst_file->metadata = ao2_container_clone(src_file->metadata, 0);
+	if (!dst_file->metadata) {
+		return -1;
+	}
+
+	return 0;
+}
+
 struct ast_bucket_file *ast_bucket_file_copy(struct ast_bucket_file *file, const char *uri)
 {
 	RAII_VAR(struct ast_bucket_file *, copy, ast_bucket_file_alloc(uri), ao2_cleanup);
@@ -747,6 +792,11 @@
 
 	ao2_ref(copy, +1);
 	return copy;
+}
+
+struct ast_bucket_file *ast_bucket_file_clone(struct ast_bucket_file *file)
+{
+	return ast_sorcery_copy(bucket_sorcery, file);
 }
 
 struct ast_bucket_file *ast_bucket_file_retrieve(const char *uri)
@@ -945,6 +995,7 @@
 	ast_sorcery_object_field_register(bucket_sorcery, "bucket", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket, scheme));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, created));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "bucket", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket, modified));
+	ast_sorcery_object_set_copy_handler(bucket_sorcery, "bucket", bucket_copy_handler);
 
 	if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL) == AST_SORCERY_APPLY_FAIL) {
 		ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n");
@@ -959,6 +1010,7 @@
 	ast_sorcery_object_field_register(bucket_sorcery, "file", "scheme", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_bucket_file, scheme));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "created", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, created));
 	ast_sorcery_object_field_register_custom(bucket_sorcery, "file", "modified", "", timeval_str2struct, timeval_struct2str, NULL, 0, FLDSET(struct ast_bucket_file, modified));
+	ast_sorcery_object_set_copy_handler(bucket_sorcery, "file", bucket_file_copy_handler);
 
 	return 0;
 }
diff --git a/tests/test_bucket.c b/tests/test_bucket.c
index 7dadd14..673bf8d 100644
--- a/tests/test_bucket.c
+++ b/tests/test_bucket.c
@@ -233,6 +233,50 @@
 	return AST_TEST_PASS;
 }
 
+AST_TEST_DEFINE(bucket_clone)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket *, clone, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_clone";
+		info->category = "/main/bucket/";
+		info->summary = "bucket clone unit test";
+		info->description =
+			"Test cloning a bucket";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(bucket = ast_bucket_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate bucket\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	if (ast_bucket_create(bucket)) {
+		ast_test_status_update(test, "Failed to create bucket with URI '%s'\n",
+			ast_sorcery_object_get_id(bucket));
+		return AST_TEST_FAIL;
+	}
+
+	clone = ast_bucket_clone(bucket);
+	if (!clone) {
+		ast_test_status_update(test, "Failed to clone bucket with URI '%s'\n",
+			ast_sorcery_object_get_id(bucket));
+		return AST_TEST_FAIL;
+	}
+
+	ast_test_validate(test, strcmp(ast_sorcery_object_get_id(bucket), ast_sorcery_object_get_id(clone)) == 0);
+	ast_test_validate(test, bucket->scheme_impl == clone->scheme_impl);
+	ast_test_validate(test, strcmp(bucket->scheme, clone->scheme) == 0);
+
+	return AST_TEST_PASS;
+}
+
 AST_TEST_DEFINE(bucket_delete)
 {
 	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
@@ -436,6 +480,52 @@
 			ast_sorcery_object_get_id(file));
 		return AST_TEST_FAIL;
 	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_file_clone)
+{
+	RAII_VAR(struct ast_bucket_file *, file, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_bucket_file *, clone, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_file_clone";
+		info->category = "/main/bucket/";
+		info->summary = "file clone unit test";
+		info->description =
+			"Test cloning a file";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(file = ast_bucket_file_alloc("test:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate file\n");
+		return AST_TEST_FAIL;
+	}
+
+	bucket_test_wizard_clear();
+
+	if (ast_bucket_file_create(file)) {
+		ast_test_status_update(test, "Failed to create file with URI '%s'\n",
+			ast_sorcery_object_get_id(file));
+		return AST_TEST_FAIL;
+	}
+	ast_bucket_file_metadata_set(file, "bob", "joe");
+
+	clone = ast_bucket_file_clone(file);
+	if (!clone) {
+		ast_test_status_update(test, "Failed to clone file with URI '%s'\n",
+			ast_sorcery_object_get_id(file));
+		return AST_TEST_FAIL;
+	}
+
+	ast_test_validate(test, strcmp(ast_sorcery_object_get_id(file), ast_sorcery_object_get_id(clone)) == 0);
+	ast_test_validate(test, file->scheme_impl == clone->scheme_impl);
+	ast_test_validate(test, strcmp(file->scheme, clone->scheme) == 0);
+	ast_test_validate(test, ao2_container_count(file->metadata) == ao2_container_count(clone->metadata));
 
 	return AST_TEST_PASS;
 }
@@ -827,11 +917,13 @@
 	AST_TEST_UNREGISTER(bucket_scheme_register);
 	AST_TEST_UNREGISTER(bucket_alloc);
 	AST_TEST_UNREGISTER(bucket_create);
+	AST_TEST_UNREGISTER(bucket_clone);
 	AST_TEST_UNREGISTER(bucket_delete);
 	AST_TEST_UNREGISTER(bucket_retrieve);
 	AST_TEST_UNREGISTER(bucket_json);
 	AST_TEST_UNREGISTER(bucket_file_alloc);
 	AST_TEST_UNREGISTER(bucket_file_create);
+	AST_TEST_UNREGISTER(bucket_file_clone);
 	AST_TEST_UNREGISTER(bucket_file_copy);
 	AST_TEST_UNREGISTER(bucket_file_retrieve);
 	AST_TEST_UNREGISTER(bucket_file_update);
@@ -854,11 +946,13 @@
 	AST_TEST_REGISTER(bucket_scheme_register);
 	AST_TEST_REGISTER(bucket_alloc);
 	AST_TEST_REGISTER(bucket_create);
+	AST_TEST_REGISTER(bucket_clone);
 	AST_TEST_REGISTER(bucket_delete);
 	AST_TEST_REGISTER(bucket_retrieve);
 	AST_TEST_REGISTER(bucket_json);
 	AST_TEST_REGISTER(bucket_file_alloc);
 	AST_TEST_REGISTER(bucket_file_create);
+	AST_TEST_REGISTER(bucket_file_clone);
 	AST_TEST_REGISTER(bucket_file_copy);
 	AST_TEST_REGISTER(bucket_file_retrieve);
 	AST_TEST_REGISTER(bucket_file_update);

-- 
To view, visit https://gerrit.asterisk.org/672
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib0240ba915ece313f1678a085a716021d75d6b4a
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Matt Jordan <mjordan at digium.com>



More information about the asterisk-code-review mailing list