summaryrefslogtreecommitdiffstats
path: root/test/testskiplist.c
diff options
context:
space:
mode:
Diffstat (limited to 'test/testskiplist.c')
-rw-r--r--test/testskiplist.c546
1 files changed, 546 insertions, 0 deletions
diff --git a/test/testskiplist.c b/test/testskiplist.c
new file mode 100644
index 0000000..ca4b435
--- /dev/null
+++ b/test/testskiplist.c
@@ -0,0 +1,546 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_skiplist.h"
+#if APR_HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_STRING_H
+#include <string.h>
+#endif
+
+static apr_pool_t *ptmp = NULL;
+static apr_skiplist *skiplist = NULL;
+
+static int skiplist_get_size(abts_case *tc, apr_skiplist *sl)
+{
+ size_t size = 0;
+ apr_skiplistnode *n;
+ for (n = apr_skiplist_getlist(sl); n; apr_skiplist_next(sl, &n)) {
+ ++size;
+ }
+ ABTS_TRUE(tc, size == apr_skiplist_size(sl));
+ return size;
+}
+
+static void skiplist_init(abts_case *tc, void *data)
+{
+ apr_time_t now = apr_time_now();
+ srand((unsigned int)(((now >> 32) ^ now) & 0xffffffff));
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&skiplist, p));
+ ABTS_PTR_NOTNULL(tc, skiplist);
+ apr_skiplist_set_compare(skiplist, (apr_skiplist_compare)strcmp,
+ (apr_skiplist_compare)strcmp);
+}
+
+static void skiplist_find(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "baton"));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+}
+
+static void skiplist_dontfind(abts_case *tc, void *data)
+{
+ const char *val;
+
+ val = apr_skiplist_find(skiplist, "keynotthere", NULL);
+ ABTS_PTR_EQUAL(tc, NULL, (void *)val);
+}
+
+static void skiplist_insert(abts_case *tc, void *data)
+{
+ const char *val;
+ int i, height = 0;
+
+ for (i = 0; i < 10; ++i) {
+ ABTS_PTR_EQUAL(tc, NULL, apr_skiplist_insert(skiplist, "baton"));
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ if (height == 0) {
+ height = apr_skiplist_height(skiplist);
+ }
+ else {
+ ABTS_INT_EQUAL(tc, height, apr_skiplist_height(skiplist));
+ }
+ }
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "foo"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "foo", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "foo", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "atfirst"));
+ ABTS_TRUE(tc, 3 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "atfirst", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "atfirst", val);
+}
+
+#define NUM_ADDS 100
+static void skiplist_add(abts_case *tc, void *data)
+{
+ const char *val;
+ size_t i, n = 0;
+
+ apr_skiplist_remove_all(skiplist, NULL);
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ for (i = 0; i < NUM_ADDS; ++i) {
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "daton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "daton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "daton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "caton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "caton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "caton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "aaton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "aaton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "aaton", val);
+ }
+}
+
+static void skiplist_replace(abts_case *tc, void *data)
+{
+ const char *val;
+ size_t n = skiplist_get_size(tc, skiplist);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "daton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "daton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "daton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "baton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "caton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "caton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "caton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "aaton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "aaton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "aaton", val);
+
+ ABTS_TRUE(tc, n == 4);
+}
+
+static void skiplist_destroy(abts_case *tc, void *data)
+{
+ apr_skiplist_destroy(skiplist, NULL);
+ ABTS_TRUE(tc, 0 == apr_skiplist_size(skiplist));
+ ABTS_TRUE(tc, 1 == apr_skiplist_height(skiplist));
+ ABTS_TRUE(tc, NULL == apr_skiplist_getlist(skiplist));
+}
+
+static void skiplist_size(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "abc"));
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "ghi"));
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "def"));
+ val = apr_skiplist_find(skiplist, "abc", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "abc", val);
+ val = apr_skiplist_find(skiplist, "ghi", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "ghi", val);
+ val = apr_skiplist_find(skiplist, "def", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "def", val);
+
+ ABTS_TRUE(tc, 3 == skiplist_get_size(tc, skiplist));
+ apr_skiplist_destroy(skiplist, NULL);
+}
+
+static void skiplist_remove(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_TRUE(tc, apr_skiplist_remove(skiplist, "baton", NULL) != 0);
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ /* remove all "baton"s */
+ while (apr_skiplist_remove(skiplist, "baton", NULL))
+ ;
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_EQUAL(tc, NULL, val);
+}
+
+#define NUM_RAND (100)
+#define NUM_FIND (3 * NUM_RAND)
+static void skiplist_random_loop(abts_case *tc, void *data)
+{
+ char **batons;
+ apr_skiplist *sl;
+ const char *val;
+ int i;
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&sl, ptmp));
+ apr_skiplist_set_compare(sl, (apr_skiplist_compare)strcmp,
+ (apr_skiplist_compare)strcmp);
+ apr_skiplist_set_preheight(sl, 7);
+ ABTS_INT_EQUAL(tc, 7, apr_skiplist_preheight(sl));
+
+ batons = apr_palloc(ptmp, NUM_FIND * sizeof(char*));
+
+ for (i = 0; i < NUM_FIND; ++i) {
+ if (i < NUM_RAND) {
+ batons[i] = apr_psprintf(ptmp, "%.6u", rand() % 1000000);
+ }
+ else {
+ batons[i] = apr_pstrdup(ptmp, batons[i % NUM_RAND]);
+ }
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(sl, batons[i]));
+ val = apr_skiplist_find(sl, batons[i], NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, batons[i], val);
+ }
+
+ apr_pool_clear(ptmp);
+}
+
+typedef struct elem {
+ int b;
+ int a;
+} elem;
+
+
+static void add_int_to_skiplist(abts_case *tc, apr_skiplist *list, int n){
+ int* a = apr_skiplist_alloc(list, sizeof(int));
+ *a = n;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, a));
+}
+
+static void add_elem_to_skiplist(abts_case *tc, apr_skiplist *list, elem n){
+ elem* a = apr_skiplist_alloc(list, sizeof(elem));
+ *a = n;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, a));
+}
+
+static int comp(void *a, void *b){
+ return *((int*) a) - *((int*) b);
+}
+
+static int scomp(void *a, void *b){
+ return ((elem*) a)->a - ((elem*) b)->a;
+}
+
+static int ecomp(void *a, void *b)
+{
+ elem const * const e1 = a;
+ elem const * const e2 = b;
+ if (e1->a < e2->a) {
+ return -1;
+ }
+ else if (e1->a > e2->a) {
+ return +1;
+ }
+ else if (e1->b < e2->b) {
+ return -1;
+ }
+ else if (e1->b > e2->b) {
+ return +1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* Some tests below add multiple duplicates and then try to remove each one
+ * individually, in arbitrary order.
+ *
+ * Using apr_skiplist_remove_compare(..., scomp, NULL) would not work because
+ * it will likely remove any duplicate (the first one) encountered on the path,
+ * hence possibly not the expected one.
+ *
+ * Using apr_skiplist_remove_compare(..., ecomp, NULL) works provided all the
+ * duplicates (same a) don't also have the same b (which is the case in the
+ * test below), hence uniqueness is cooked in the elem itself.
+ *
+ * Another possibility is to rely on unique pointers, and then cook a remove
+ * function, like the following skiplist_remove_scomp(), which will go straight
+ * to the last duplicate (using scomp) and then iterate on the previous elems
+ * until pointers match.
+ *
+ * Providing uniqueness in the elem itself is the more clean/efficient option,
+ * but if all you have is a unique pointer the pattern in the function may be
+ * worth it ( or it's just a way to test several skiplist functionalities :)
+ */
+static void skiplist_remove_scomp(abts_case *tc, apr_skiplist *list, elem *n)
+{
+ elem *e;
+ apr_skiplistnode *iter = NULL;
+ e = apr_skiplist_last(list, n, &iter);
+ while (e && e != n) {
+ ABTS_INT_EQUAL(tc, 0, scomp(n, e));
+ e = apr_skiplist_previous(list, &iter);
+ }
+ ABTS_PTR_EQUAL(tc, n, apr_skiplist_element(iter));
+ apr_skiplist_remove_node(list, iter, NULL);
+}
+
+static void skiplist_test(abts_case *tc, void *data) {
+ int test_elems = 10;
+ int i = 0, j = 0;
+ int *val = NULL;
+ elem *val2 = NULL;
+ apr_skiplist * list = NULL;
+ apr_skiplist * list2 = NULL;
+ apr_skiplist * list3 = NULL;
+ apr_skiplist * list4 = NULL;
+ int first_forty_two = 42,
+ second_forty_two = 42;
+ apr_array_header_t *array;
+ elem t1, t2, t3, t4, t5;
+ t1.a = 1; t1.b = 1;
+ t2.a = 42; t2.b = 1;
+ t3.a = 42; t3.b = 2;
+ t4.a = 42; t4.b = 3;
+ t5.a = 142; t5.b = 1;
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list, ptmp));
+ apr_skiplist_set_compare(list, comp, comp);
+
+ /* insert 10 objects */
+ for (i = 0; i < test_elems; ++i){
+ add_int_to_skiplist(tc, list, i);
+ }
+
+ /* remove all objects */
+ while ((val = apr_skiplist_pop(list, NULL))){
+ ABTS_INT_EQUAL(tc, *val, j++);
+ }
+
+ /* insert 10 objects again */
+ for (i = test_elems; i < test_elems+test_elems; ++i){
+ add_int_to_skiplist(tc, list, i);
+ }
+
+ j = test_elems;
+ while ((val = apr_skiplist_pop(list, NULL))){
+ ABTS_INT_EQUAL(tc, *val, j++);
+ }
+
+ /* empty */
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, NULL);
+
+ add_int_to_skiplist(tc, list, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_INT_EQUAL(tc, *val, 42);
+
+ /* empty */
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, NULL);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, &first_forty_two));
+ add_int_to_skiplist(tc, list, 1);
+ add_int_to_skiplist(tc, list, 142);
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, &second_forty_two));
+ val = apr_skiplist_peek(list);
+ ABTS_INT_EQUAL(tc, *val, 1);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_INT_EQUAL(tc, *val, 1);
+ val = apr_skiplist_peek(list);
+ ABTS_PTR_EQUAL(tc, val, &first_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, &first_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, &second_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_peek(list);
+ ABTS_INT_EQUAL(tc, *val, 142);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list2, ptmp));
+ apr_skiplist_set_compare(list2, scomp, scomp);
+ add_elem_to_skiplist(tc, list2, t2);
+ add_elem_to_skiplist(tc, list2, t1);
+ add_elem_to_skiplist(tc, list2, t3);
+ add_elem_to_skiplist(tc, list2, t5);
+ add_elem_to_skiplist(tc, list2, t4);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 1);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 1);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 2);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 3);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 142);
+ ABTS_INT_EQUAL(tc, val2->b, 1);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list3, ptmp));
+ apr_skiplist_set_compare(list3, ecomp, ecomp);
+ array = apr_array_make(ptmp, 10, sizeof(elem *));
+ for (i = 0; i < 10; ++i) {
+ elem *e = apr_palloc(ptmp, sizeof *e);
+ e->a = 4224;
+ e->b = i;
+ APR_ARRAY_PUSH(array, elem *) = e;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(list3, e));
+ }
+ for (i = 0; i < 5; ++i) {
+ elem *e = APR_ARRAY_IDX(array, i, elem *);
+ val2 = apr_skiplist_find(list3, e, NULL);
+ ABTS_PTR_EQUAL(tc, e, val2);
+ ABTS_TRUE(tc, apr_skiplist_remove(list3, e, NULL) != 0);
+ }
+ for (i = 0; i < 5; ++i) {
+ elem *e = APR_ARRAY_IDX(array, 9 - i, elem *);
+ val2 = apr_skiplist_find(list3, e, NULL);
+ ABTS_PTR_EQUAL(tc, e, val2);
+ ABTS_TRUE(tc, apr_skiplist_remove(list3, e, NULL) != 0);
+ }
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list4, ptmp));
+ apr_skiplist_set_compare(list4, scomp, scomp);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t1);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t2);
+ }
+ apr_skiplist_add(list4, &t2);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t2);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t3);
+ }
+ apr_skiplist_add(list4, &t3);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t3);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t4);
+ }
+ apr_skiplist_add(list4, &t4);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t4);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t5);
+ }
+ skiplist_remove_scomp(tc, list4, &t2);
+ skiplist_remove_scomp(tc, list4, &t3);
+ skiplist_remove_scomp(tc, list4, &t4);
+
+ apr_pool_clear(ptmp);
+}
+
+
+abts_suite *testskiplist(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ apr_pool_create(&ptmp, p);
+
+ abts_run_test(suite, skiplist_init, NULL);
+ abts_run_test(suite, skiplist_find, NULL);
+ abts_run_test(suite, skiplist_dontfind, NULL);
+ abts_run_test(suite, skiplist_insert, NULL);
+ abts_run_test(suite, skiplist_add, NULL);
+ abts_run_test(suite, skiplist_replace, NULL);
+ abts_run_test(suite, skiplist_destroy, NULL);
+ abts_run_test(suite, skiplist_size, NULL);
+ abts_run_test(suite, skiplist_remove, NULL);
+ abts_run_test(suite, skiplist_random_loop, NULL);
+
+ abts_run_test(suite, skiplist_test, NULL);
+
+ apr_pool_destroy(ptmp);
+
+ return suite;
+}
+