[svn-commits] file: branch file/bucket r395557 - in /team/file/bucket: include/asterisk/ ma...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jul 26 15:36:18 CDT 2013


Author: file
Date: Fri Jul 26 15:36:17 2013
New Revision: 395557

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=395557
Log:
Add progress so far!

The API is a work in progress, some stuff will be changed for sure.
Bucket level operations are occurring with unit tests confirming it. Retrieve
isn't yet implemented in the unit test, so it's sad.

Added:
    team/file/bucket/include/asterisk/bucket.h   (with props)
    team/file/bucket/main/bucket.c   (with props)
    team/file/bucket/tests/test_bucket.c   (with props)
Modified:
    team/file/bucket/main/asterisk.c

Added: team/file/bucket/include/asterisk/bucket.h
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/include/asterisk/bucket.h?view=auto&rev=395557
==============================================================================
--- team/file/bucket/include/asterisk/bucket.h (added)
+++ team/file/bucket/include/asterisk/bucket.h Fri Jul 26 15:36:17 2013
@@ -1,0 +1,225 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Bucket File API
+ * \author Joshua Colp <jcolp at digium.com>
+ * \ref AstBucket
+ */
+
+/*!
+ * \page AstBucket Bucket File API
+ *
+ * Bucket is an API which provides directory and file access in a generic fashion. It is
+ * implemented as a thin wrapper over the sorcery data access layer API and is written in
+ * a pluggable fashion to allow different backend storage mechanisms.
+  */
+
+#ifndef _ASTERISK_BUCKET_H
+#define _ASTERISK_BUCKET_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/sorcery.h"
+
+/*! \brief Bucket metadata structure, AO2 key value pair */
+struct ast_bucket_metadata {
+	/*! \brief Name of the attribute */
+	const char *name;
+	/*! \brief Value of the attribute */
+	const char *value;
+	/*! \brief Actual blob of data */
+	char data[0];
+};
+
+/*! \brief Bucket structure, contains other buckets and files */
+struct ast_bucket {
+	/*! \brief Sorcery object information */
+	SORCERY_OBJECT(details);
+	/*! \brief Stringfields */
+	AST_DECLARE_STRING_FIELDS(
+		/*! \brief Full URI to the bucket */
+		AST_STRING_FIELD(uri);
+		/*! \brief Scheme in use */
+		AST_STRING_FIELD(scheme);
+	);
+	/*! \brief When this bucket was created */
+	struct timeval created;
+	/*! \brief When this bucket was last modified */
+	struct timeval modified;
+	/*! \brief Container of string URIs of buckets within this bucket */
+	struct ao2_container *buckets;
+	/*! \brief Container of string URIs of files within this bucket */
+	struct ao2_container *files;
+};
+
+/*! \brief Bucket file structure, contains reference to file and information about it */
+struct ast_bucket_file {
+	/*! \brief Sorcery object information */
+	SORCERY_OBJECT(details);
+	/*! \brief When this file was created */
+	struct timeval created;
+	/*! \brief When this file was last modified */
+	struct timeval modified;
+	/*! \brief Container of metadata attributes about file */
+	struct ao2_container *metadata;
+	/*! \brief Local path to this file */
+	char path[PATH_MAX];
+};
+
+/*!
+ * \brief Initialize bucket support
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_init(void);
+
+/*!
+ * \brief Register support for a specific scheme
+ *
+ * \param name Name of the scheme, used to find based on scheme in URIs
+ * \param bucket Sorcery wizard used for buckets
+ * \param file Sorcery wizard used for files
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bucket,
+	struct ast_sorcery_wizard *file);
+
+/*!
+ * \brief Unregister support for a specific scheme
+ *
+ * \param name Name of the scheme
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_scheme_unregister(const char *name);
+
+/*!
+ * \brief Set a metadata attribute on a file to a specific value
+ *
+ * \param file The bucket file
+ * \param name Name of the attribute
+ * \param value Value of the attribute
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note This function will overwrite an existing attribute of the same name, unless an error
+ * occurs. If an error occurs the existing attribute is left alone.
+ */
+int ast_bucket_file_metadata_set(struct ast_bucket_file *file, const char *name, const char *value);
+
+/*!
+ * \brief Unset a specific metadata attribute on a file
+ *
+ * \param file The bucket file
+ * \param name Name of the attribute
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_file_metadata_unset(struct ast_bucket_file *file, const char *name);
+
+/*!
+ * \brief Retrieve a metadata attribute from a file
+ *
+ * \param file The bucket file
+ * \param name Name of the attribute
+ *
+ * \retval non-NULL if found
+ * \retval NULL if not found
+ *
+ * \note The object is returned with reference count increased
+ */
+struct ast_bucket_metadata *ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name);
+
+/*!
+ * \brief Allocate a new bucket
+ *
+ * \param uri Complete URI for the bucket
+ *
+ * \param non-NULL success
+ * \param NULL failure
+ *
+ * \note This only creates a local bucket object, to persist in backend storage you must call
+ * ast_bucket_create
+ */
+struct ast_bucket *ast_bucket_alloc(const char *uri);
+
+/*!
+ * \brief Create a new bucket in backend storage
+ *
+ * \param bucket The bucket
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_create(struct ast_bucket *bucket);
+
+/*!
+ * \brief Delete a bucket from backend storage
+ *
+ * \param bucket The bucket
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_bucket_delete(struct ast_bucket *bucket);
+
+/*!
+ * \brief Retrieve information about a bucket
+ *
+ * \param uri Complete URI of the bucket
+ *
+ * \retval non-NULL if found
+ * \retval NULL if not found
+ *
+ * \note The object is returned with reference count increased
+ */
+struct ast_bucket *ast_bucket_retrieve(const char *uri);
+
+/*!
+ * \brief Add an observer for bucket creation and deletion operations
+ *
+ * \param callbacks Implementation of the sorcery observer interface
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note You must be ready to accept observer invocations before this function is called
+ */
+int ast_bucket_observer_add(const struct ast_sorcery_observer *callbacks);
+
+/*!
+ * \brief Remove an observer from bucket creation and deletion
+ *
+ * \param callbacks Implementation of the sorcery observer interface
+ */
+void ast_bucket_observer_remove(struct ast_sorcery_observer *callbacks);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BUCKET_H */

Propchange: team/file/bucket/include/asterisk/bucket.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/file/bucket/include/asterisk/bucket.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/file/bucket/include/asterisk/bucket.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/file/bucket/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/main/asterisk.c?view=diff&rev=395557&r1=395556&r2=395557
==============================================================================
--- team/file/bucket/main/asterisk.c (original)
+++ team/file/bucket/main/asterisk.c Fri Jul 26 15:36:17 2013
@@ -240,6 +240,7 @@
 #include "asterisk/aoc.h"
 #include "asterisk/uuid.h"
 #include "asterisk/sorcery.h"
+#include "asterisk/bucket.h"
 #include "asterisk/stasis.h"
 #include "asterisk/json.h"
 #include "asterisk/stasis_endpoints.h"
@@ -4168,6 +4169,11 @@
 		exit(1);
 	}
 
+	if (ast_bucket_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
 #ifdef AST_XML_DOCS
 	/* Load XML documentation. */
 	ast_xmldoc_load_documentation();

Added: team/file/bucket/main/bucket.c
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/main/bucket.c?view=auto&rev=395557
==============================================================================
--- team/file/bucket/main/bucket.c (added)
+++ team/file/bucket/main/bucket.c Fri Jul 26 15:36:17 2013
@@ -1,0 +1,445 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Bucket File API
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/bucket.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h"
+
+/*! \brief Default scheme for when one is not specified */
+#define DEFAULT_UNSPECIFIED_SCHEME "local"
+
+/*! \brief Number of buckets for the container of schemes */
+#define SCHEME_BUCKETS 53
+
+/*! \brief Number of buckets for the container of bucket URIs in a bucket */
+#define BUCKET_BUCKETS 53
+
+/*! \brief Number of buckets for the container of file URIs in a bucket */
+#define FILE_BUCKETS 53
+
+/*! \brief Sorcery instance for all bucket operations */
+static struct ast_sorcery *bucket_sorcery;
+
+/*! \brief Container of registered schemes */
+static struct ao2_container *schemes;
+
+/*! \brief Structure for available schemes */
+struct bucket_scheme {
+	/*! \brief Wizard for buckets */
+	struct ast_sorcery_wizard *bucket;
+	/*! \brief Wizard for files */
+	struct ast_sorcery_wizard *file;
+	/*! \brief Name of the scheme */
+	char name[0];
+};
+
+/*! \brief Callback function for creating a bucket */
+static int bucket_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct ast_bucket *bucket = object;
+	RAII_VAR(struct bucket_scheme *, scheme, ao2_find(schemes, bucket->scheme, OBJ_KEY), ao2_cleanup);
+
+	if (!scheme) {
+		return -1;
+	}
+
+	return scheme->bucket->create(sorcery, data, object);
+}
+
+/*! \brief Callback function for retrieving a bucket */
+static void *bucket_wizard_retrieve(const struct ast_sorcery *sorcery, void *data, const char *type,
+	const char *id)
+{
+	char *full_uri, *scheme_name = DEFAULT_UNSPECIFIED_SCHEME, *path, *tmp;
+	RAII_VAR(struct bucket_scheme *, scheme, NULL, ao2_cleanup);
+
+	if (!(full_uri = ast_strdupa(id))) {
+		return NULL;
+	}
+
+	/* Assume no scheme until proven otherwise */
+	path = full_uri;
+	if ((tmp = strstr(full_uri, "://"))) {
+		scheme_name = full_uri;
+		*tmp++ = '\0';
+		path = tmp;
+	}
+
+	if (ast_strlen_zero(scheme_name) || ast_strlen_zero(path)) {
+		return NULL;
+	}
+
+	scheme = ao2_find(schemes, scheme_name, OBJ_KEY);
+	if (!scheme) {
+		return NULL;
+	}
+
+	return scheme->bucket->retrieve_id(sorcery, data, type, path);
+}
+
+/*! \brief Callback function for deleting a bucket */
+static int bucket_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	struct ast_bucket *bucket = object;
+	RAII_VAR(struct bucket_scheme *, scheme, ao2_find(schemes, bucket->scheme, OBJ_KEY), ao2_cleanup);
+
+	if (!scheme) {
+		return -1;
+	}
+
+	return scheme->bucket->delete(sorcery, data, object);
+}
+
+/*! \brief Intermediary bucket wizard */
+static struct ast_sorcery_wizard bucket_wizard = {
+	.name = "bucket",
+	.create = bucket_wizard_create,
+	.retrieve_id = bucket_wizard_retrieve,
+	.delete = bucket_wizard_delete,
+};
+
+/*! \brief Intermediary file wizard */
+static struct ast_sorcery_wizard bucket_file_wizard = {
+	.name = "bucket_file",
+};
+
+int ast_bucket_scheme_register(const char *name, struct ast_sorcery_wizard *bucket,
+	struct ast_sorcery_wizard *file)
+{
+	SCOPED_AO2WRLOCK(lock, schemes);
+	struct bucket_scheme *scheme;
+
+	if (ast_strlen_zero(name) || !bucket || !file) {
+		return -1;
+	}
+
+	scheme = ao2_find(schemes, name, OBJ_KEY | OBJ_NOLOCK);
+	if (scheme) {
+		return -1;
+	}
+
+	scheme = ao2_alloc(sizeof(*scheme) + strlen(name) + 1, NULL);
+	if (!scheme) {
+		return -1;
+	}
+
+	strcpy(scheme->name, name);
+	scheme->bucket = bucket;
+	scheme->file = file;
+
+	ao2_link_flags(schemes, scheme, OBJ_NOLOCK);
+
+	ast_verb(2, "Registered bucket scheme '%s'\n", name);
+
+	return 0;
+}
+
+int ast_bucket_scheme_unregister(const char *name)
+{
+	SCOPED_AO2WRLOCK(lock, schemes);
+	struct bucket_scheme *scheme;
+
+	scheme = ao2_find(schemes, name, OBJ_KEY | OBJ_NOLOCK | OBJ_UNLINK);
+	if (!scheme) {
+		return -1;
+	}
+	ao2_ref(scheme, -1);
+
+	ast_verb(2, "Unregistered bucket scheme '%s'\n", name);
+
+	return 0;
+}
+
+/*! \brief Allocator for metadata attributes */
+static struct ast_bucket_metadata *bucket_metadata_alloc(const char *name, const char *value)
+{
+	int name_len = strlen(name) + 1, value_len = strlen(value) + 1;
+	struct ast_bucket_metadata *metadata = ao2_alloc(sizeof(*metadata) + name_len + value_len, NULL);
+	char *dst;
+
+	if (!metadata) {
+		return NULL;
+	}
+
+	dst = metadata->data;
+	metadata->name = strcpy(dst, name);
+	dst += name_len;
+	metadata->value = strcpy(dst, value);
+
+	return metadata;
+}
+
+int ast_bucket_file_metadata_set(struct ast_bucket_file *file, const char *name, const char *value)
+{
+	struct ast_bucket_metadata *metadata = bucket_metadata_alloc(name, value);
+
+	if (!metadata) {
+		return -1;
+	}
+
+	ao2_find(file->metadata, name, OBJ_NODATA | OBJ_UNLINK | OBJ_KEY);
+	ao2_link(file->metadata, metadata);
+	ao2_ref(metadata, -1);
+
+	return 0;
+}
+
+int ast_bucket_file_metadata_unset(struct ast_bucket_file *file, const char *name)
+{
+	struct ast_bucket_metadata *metadata = ao2_find(file->metadata, name, OBJ_UNLINK | OBJ_KEY);
+
+	if (!metadata) {
+		return -1;
+	}
+
+	ao2_ref(metadata, -1);
+	return 0;
+}
+
+struct ast_bucket_metadata *ast_bucket_file_metadata_get(struct ast_bucket_file *file, const char *name)
+{
+	return ao2_find(file->metadata, name, OBJ_KEY);
+}
+
+/*! \brief Destructor for buckets */
+static void bucket_destroy(void *obj)
+{
+	struct ast_bucket *bucket = obj;
+
+	ast_string_field_free_memory(bucket);
+	ao2_cleanup(bucket->buckets);
+	ao2_cleanup(bucket->files);
+}
+
+/*! \brief Allocator for buckets */
+static void *bucket_alloc(const char *name)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+
+	bucket = ast_sorcery_generic_alloc(sizeof(*bucket), bucket_destroy);
+	if (!bucket) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(bucket, 128)) {
+		return NULL;
+	}
+
+	bucket->buckets = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, BUCKET_BUCKETS);
+	if (!bucket->buckets) {
+		return NULL;
+	}
+
+	bucket->files = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, FILE_BUCKETS);
+	if (!bucket->files) {
+		return NULL;
+	}
+
+	ao2_ref(bucket, +1);
+	return bucket;
+}
+
+struct ast_bucket *ast_bucket_alloc(const char *uri)
+{
+	char *full_uri, *scheme = DEFAULT_UNSPECIFIED_SCHEME, *name, *tmp;
+	struct ast_bucket *bucket;
+
+	if (ast_strlen_zero(uri) || !(full_uri = ast_strdupa(uri))) {
+		return NULL;
+	}
+
+	/* Assume the naming starts at the front until proven otherwise */
+	name = full_uri;
+
+	/* Determine the scheme from the provided URI */
+	if ((tmp = strstr(full_uri, "://"))) {
+		scheme = full_uri;
+		*tmp++ = '\0';
+		name = tmp;
+	}
+
+	/* Determine the name of the bucket */
+	if ((tmp = strrchr(name, '/'))) {
+		name = tmp + 1;
+	}
+
+	/* If no scheme and name are available the URI is invalid */
+	if (ast_strlen_zero(scheme) || ast_strlen_zero(name)) {
+		return NULL;
+	}
+
+	if (!(bucket = ast_sorcery_alloc(bucket_sorcery, "bucket", name))) {
+		return NULL;
+	}
+
+	ast_string_field_set(bucket, uri, uri);
+	ast_string_field_set(bucket, scheme, scheme);
+
+	return bucket;
+}
+
+int ast_bucket_create(struct ast_bucket *bucket)
+{
+	return ast_sorcery_create(bucket_sorcery, bucket);
+}
+
+struct ast_bucket *ast_bucket_retrieve(const char *uri)
+{
+	if (ast_strlen_zero(uri)) {
+		return NULL;
+	}
+
+	return ast_sorcery_retrieve_by_id(bucket_sorcery, "bucket", uri);
+}
+
+int ast_bucket_observer_add(const struct ast_sorcery_observer *callbacks)
+{
+	return ast_sorcery_observer_add(bucket_sorcery, "bucket", callbacks);
+}
+
+void ast_bucket_observer_remove(struct ast_sorcery_observer *callbacks)
+{
+	ast_sorcery_observer_remove(bucket_sorcery, "bucket", callbacks);
+}
+
+int ast_bucket_delete(struct ast_bucket *bucket)
+{
+	return ast_sorcery_delete(bucket_sorcery, bucket);
+}
+
+/*! \brief Destructor for bucket files */
+static void bucket_file_destroy(void *obj)
+{
+	struct ast_bucket_file *file = obj;
+
+	ao2_cleanup(file->metadata);
+}
+
+/*! \brief Allocator for bucket files */
+static void *bucket_file_alloc(const char *name)
+{
+	struct ast_bucket_file *file = ast_sorcery_generic_alloc(sizeof(*file), bucket_file_destroy);
+
+	if (!file) {
+		return NULL;
+	}
+
+	return file;
+}
+
+/*! \brief Hashing function for scheme container */
+static int bucket_scheme_hash(const void *obj, const int flags)
+{
+	const struct bucket_scheme *scheme = obj;
+	const char *name = obj;
+
+	return ast_str_hash(flags & OBJ_KEY ? name : scheme->name);
+}
+
+/*! \brief Comparison function for scheme container */
+static int bucket_scheme_cmp(void *obj, void *arg, int flags)
+{
+	struct bucket_scheme *scheme1 = obj, *scheme2 = arg;
+	const char *name = arg;
+
+	return !strcmp(scheme1->name, flags & OBJ_KEY ? name : scheme2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \brief Cleanup function for graceful shutdowns */
+static void bucket_cleanup(void)
+{
+	if (bucket_sorcery) {
+		ast_sorcery_unref(bucket_sorcery);
+	}
+
+	ast_sorcery_wizard_unregister(&bucket_wizard);
+	ast_sorcery_wizard_unregister(&bucket_file_wizard);
+
+	ao2_cleanup(schemes);
+}
+
+/*! \brief Initialize bucket support */
+int ast_bucket_init(void)
+{
+	schemes = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, SCHEME_BUCKETS, bucket_scheme_hash,
+		bucket_scheme_cmp);
+	if (!schemes) {
+		ast_log(LOG_ERROR, "Failed to create container for Bucket schemes\n");
+		goto failure;
+	}
+
+	if (__ast_sorcery_wizard_register(&bucket_wizard, NULL)) {
+		ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'bucket' intermediary\n");
+		goto failure;
+	}
+
+	if (__ast_sorcery_wizard_register(&bucket_file_wizard, NULL)) {
+		ast_log(LOG_ERROR, "Failed to register sorcery wizard for 'file' intermediary\n");
+		goto failure;
+	}
+
+	if (!(bucket_sorcery = ast_sorcery_open())) {
+		ast_log(LOG_ERROR, "Failed to create sorcery instance for Bucket support\n");
+		goto failure;
+	}
+
+	if (ast_sorcery_apply_default(bucket_sorcery, "bucket", "bucket", NULL)) {
+		ast_log(LOG_ERROR, "Failed to apply intermediary for 'bucket' object type in Bucket sorcery\n");
+		goto failure;
+	}
+
+	if (ast_sorcery_object_register(bucket_sorcery, "bucket", bucket_alloc, NULL, NULL)) {
+		ast_log(LOG_ERROR, "Failed to register 'bucket' object type in Bucket sorcery\n");
+		goto failure;
+	}
+
+	if (ast_sorcery_apply_default(bucket_sorcery, "file", "bucket_file", NULL)) {
+		ast_log(LOG_ERROR, "Failed to apply intermediary for 'file' object type in Bucket sorcery\n");
+		goto failure;
+	}
+
+	if (ast_sorcery_object_register(bucket_sorcery, "file", bucket_file_alloc, NULL, NULL)) {
+		ast_log(LOG_ERROR, "Failed to register 'file' object type in Bucket sorcery\n");
+		goto failure;
+	}
+
+	ast_register_cleanup(bucket_cleanup);
+
+	return 0;
+
+failure:
+	bucket_cleanup();
+	return -1;
+}

Propchange: team/file/bucket/main/bucket.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/file/bucket/main/bucket.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/file/bucket/main/bucket.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/file/bucket/tests/test_bucket.c
URL: http://svnview.digium.com/svn/asterisk/team/file/bucket/tests/test_bucket.c?view=auto&rev=395557
==============================================================================
--- team/file/bucket/tests/test_bucket.c (added)
+++ team/file/bucket/tests/test_bucket.c Fri Jul 26 15:36:17 2013
@@ -1,0 +1,337 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Bucket Unit Tests
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ *
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "")
+
+#include "asterisk/test.h"
+#include "asterisk/module.h"
+#include "asterisk/bucket.h"
+#include "asterisk/logger.h"
+#include "asterisk/json.h"
+
+/*! \brief Test state structure for scheme wizards */
+struct bucket_test_state {
+	/*! \brief Whether the object has been created or not */
+	unsigned int created:1;
+	/*! \brief Whether the object has been updated or not */
+	unsigned int updated:1;
+
+	/*! \brief Whether the object has been deleted or not */
+	unsigned int deleted:1;
+};
+
+/*! \brief Global scope structure for testing bucket wizards */
+static struct bucket_test_state bucket_test_wizard_state;
+
+static void bucket_test_wizard_clear(void)
+{
+	bucket_test_wizard_state.created = 0;
+	bucket_test_wizard_state.updated = 0;
+	bucket_test_wizard_state.deleted = 0;
+}
+
+static int bucket_test_wizard_create(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	if (bucket_test_wizard_state.created) {
+		return -1;
+	}
+
+	bucket_test_wizard_state.created = 1;
+
+	return 0;
+}
+
+static int bucket_test_wizard_delete(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+	if (bucket_test_wizard_state.deleted) {
+		return -1;
+	}
+
+	bucket_test_wizard_state.deleted = 1;
+
+	return 0;
+}
+
+static struct ast_sorcery_wizard bucket_test_wizard = {
+	.name = "test",
+	.create = bucket_test_wizard_create,
+	.delete = bucket_test_wizard_delete,
+};
+
+static struct ast_sorcery_wizard bucket_file_test_wizard = {
+	.name = "test",
+	.create = bucket_test_wizard_create,
+	.delete = bucket_test_wizard_delete,
+};
+
+AST_TEST_DEFINE(bucket_scheme_register_unregister)
+{
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_scheme_register_unregister";
+		info->category = "/main/bucket/";
+		info->summary = "bucket scheme registration/unregistration unit test";
+		info->description =
+			"Test registration and unregistration of bucket scheme";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!ast_bucket_scheme_register("", NULL, NULL)) {
+		ast_test_status_update(test, "Successfully registered a Bucket scheme without name or wizards\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_bucket_scheme_register("test", &bucket_test_wizard, &bucket_file_test_wizard)) {
+		ast_test_status_update(test, "Could not register a perfectly good Bucket scheme\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!ast_bucket_scheme_register("test", &bucket_test_wizard, &bucket_file_test_wizard)) {
+		ast_test_status_update(test, "Successfully registered a Bucket scheme twice\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_bucket_scheme_unregister("test")) {
+		ast_test_status_update(test, "Could not unregister a registered Bucket scheme\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!ast_bucket_scheme_unregister("test")) {
+		ast_test_status_update(test, "Successfully unregistered a Bucket scheme twice\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_alloc)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_alloc";
+		info->category = "/main/bucket/";
+		info->summary = "bucket allocation unit test";
+		info->description =
+			"Test allocation of buckets";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if ((bucket = ast_bucket_alloc(""))) {
+		ast_test_status_update(test, "Allocated a bucket with no URI provided\n");
+		return AST_TEST_FAIL;
+	}
+
+	if ((bucket = ast_bucket_alloc("goat://"))) {
+		ast_test_status_update(test, "Allocated a bucket with no name\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!(bucket = ast_bucket_alloc("goat:///tmp/bob"))) {
+		ast_test_status_update(test, "Failed to allocate bucket\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (strcmp(bucket->uri, "goat:///tmp/bob")) {
+		ast_test_status_update(test, "URI within allocated bucket is '%s' and should be goat:///tmp/bob\n",
+			bucket->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (strcmp(bucket->scheme, "goat")) {
+		ast_test_status_update(test, "Scheme within allocated bucket is '%s' and should be goat\n",
+			bucket->scheme);
+		return AST_TEST_FAIL;
+	}
+
+	if (strcmp(ast_sorcery_object_get_id(bucket), "bob")) {
+		ast_test_status_update(test, "Bucket id is '%s' and should be bob\n",
+			ast_sorcery_object_get_id(bucket));
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+/*! \brief Constructor function for registering test scheme */
+static void *bucket_test_scheme_register(void)
+{
+	ast_bucket_scheme_register("test", &bucket_test_wizard, &bucket_file_test_wizard);
+	return NULL;
+}
+
+/*! \brief Destructor function for unregistering test scheme */
+static void bucket_test_scheme_unregister(void *obj)
+{
+	ast_bucket_scheme_unregister("test");
+}
+
+AST_TEST_DEFINE(bucket_create)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+	RAII_VAR(void *, dummy, bucket_test_scheme_register(), bucket_test_scheme_unregister);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_create";
+		info->category = "/main/bucket/";
+		info->summary = "bucket creation unit test";
+		info->description =
+			"Test creation of buckets";
+		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",
+			bucket->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (!bucket_test_wizard_state.created) {
+		ast_test_status_update(test, "Successfully returned bucket was created, but it was not\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!ast_bucket_create(bucket)) {
+		ast_test_status_update(test, "Successfully created bucket with URI '%s' twice\n",
+			bucket->uri);
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_delete)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+	RAII_VAR(void *, dummy, bucket_test_scheme_register(), bucket_test_scheme_unregister);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_delete";
+		info->category = "/main/bucket/";
+		info->summary = "bucket deletion unit test";
+		info->description =
+			"Test deletion of buckets";
+		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_delete(bucket)) {
+		ast_test_status_update(test, "Failed to delete bucket with URI '%s'\n",
+			bucket->uri);
+		return AST_TEST_FAIL;
+	}
+
+	if (!bucket_test_wizard_state.deleted) {
+		ast_test_status_update(test, "Successfully returned bucket was deleted, but it was not\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!ast_bucket_delete(bucket)) {
+		ast_test_status_update(test, "Successfully deleted bucket with URI '%s' twice\n",
+			bucket->uri);
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(bucket_retrieve)
+{
+	RAII_VAR(struct ast_bucket *, bucket, NULL, ao2_cleanup);
+	RAII_VAR(void *, dummy, bucket_test_scheme_register(), bucket_test_scheme_unregister);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "bucket_retrieve";
+		info->category = "/main/bucket/";
+		info->summary = "bucket retrieval unit test";
+		info->description =
+			"Test retrieval of buckets";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(bucket = ast_bucket_retrieve("test://tmp/bob"))) {
+		ast_test_status_update(test, "Failed to retrieve known valid bucket\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(bucket_scheme_register_unregister);
+	AST_TEST_UNREGISTER(bucket_alloc);
+	AST_TEST_UNREGISTER(bucket_create);
+	AST_TEST_UNREGISTER(bucket_delete);
+	AST_TEST_UNREGISTER(bucket_retrieve);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(bucket_scheme_register_unregister);
+	AST_TEST_REGISTER(bucket_alloc);
+	AST_TEST_REGISTER(bucket_create);
+	AST_TEST_REGISTER(bucket_delete);
+	AST_TEST_REGISTER(bucket_retrieve);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Bucket test module");

Propchange: team/file/bucket/tests/test_bucket.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/file/bucket/tests/test_bucket.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/file/bucket/tests/test_bucket.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the svn-commits mailing list