From 2d08f30671042e4b499df7cd5e3055a2e9b72af6 Mon Sep 17 00:00:00 2001
From: Dmitri Pal <dpal@dpal.csb>
Date: Sun, 9 Nov 2014 19:44:38 -0500
Subject: [PATCH 01/16] [COLLECTION] Function to return one of the dups

The internal search functoins we not able to deal with
the duplicates that might be scattered across a collection.
This change creates a new search function that is more robust.
The old function becomes a wrapper.
The code is adjusted in the cases where the functionality
of the more advanced functions is needed.
---
 collection/collection.c      |  210 +++++++++++++++++++++++---
 collection/collection.h      |   98 ++++++++++++
 collection/collection_ut.c   |  338 +++++++++++++++++++++++++++++++++++++++---
 collection/libcollection.sym |    6 +
 4 files changed, 610 insertions(+), 42 deletions(-)

diff --git a/collection/collection.c b/collection/collection.c
index fa33cb84fe3946104b34110dcf39a438d0f3432f..d155419cedd9316db51628c3ba6b0f850db829a0 100644
--- a/collection/collection.c
+++ b/collection/collection.c
@@ -31,6 +31,7 @@
 #include "collection_priv.h"
 #include "collection.h"
 
+#define INTERRUPT(d) ((d == COL_DSP_LASTDUPNS) || (d == COL_DSP_NDUPNS)) ? 0 : 1
 
 /* Internal constants defined to denote actions that can be performed by find handler */
 #define COLLECTION_ACTION_FIND       1
@@ -305,21 +306,28 @@ struct property_search {
     int found;
     int use_type;
     int type;
+    int interrupt;
+    int exact;
 };
 
 /* Find the parent of the item with given name */
-static int col_find_property(struct collection_item *collection,
-                             const char *refprop,
-                             int idx,
-                             int use_type,
-                             int type,
-                             struct collection_item **parent)
+static int col_find_property_sub(struct collection_item *collection,
+                                 const char *subcollection,
+                                 int interrupt,
+                                 const char *refprop,
+                                 int idx,
+                                 int exact,
+                                 int use_type,
+                                 int type,
+                                 struct collection_item **parent)
 {
     struct property_search ps;
     int i = 0;
     unsigned depth = 0;
+    struct collection_item *sub = NULL;
+    int error = EOK;
 
-    TRACE_FLOW_STRING("col_find_property", "Entry.");
+    TRACE_FLOW_ENTRY();
 
     *parent = NULL;
 
@@ -331,6 +339,8 @@ static int col_find_property(struct collection_item *collection,
     ps.found = 0;
     ps.use_type = use_type;
     ps.type = type;
+    ps.interrupt = interrupt;
+    ps.exact = exact;
 
     /* Create hash of the string to search */
     while(refprop[i] != 0) {
@@ -339,8 +349,35 @@ static int col_find_property(struct collection_item *collection,
         i++;
     }
 
+    /* Add item to collection */
+    if (subcollection == NULL) {
+        sub = collection;
+    }
+    else {
+        TRACE_INFO_STRING("Subcollection is not null, searching",
+                          subcollection);
+        error = col_find_item_and_do(collection, subcollection,
+                                     COL_TYPE_COLLECTIONREF,
+                                     COL_TRAVERSE_DEFAULT,
+                                     col_get_subcollection, (void *)(&sub),
+                                     COLLECTION_ACTION_FIND);
+        if (error) {
+            TRACE_ERROR_NUMBER("Search for subcollection returned error:",
+                                error);
+            /* Not found */
+            return 0;
+        }
+
+        if (sub == NULL) {
+            TRACE_ERROR_STRING("Search for subcollection returned NULL", "");
+            /* Not found */
+            return 0;
+        }
+
+    }
+
     /* We do not care about error here */
-    (void)col_walk_items(collection, COL_TRAVERSE_ONELEVEL,
+    (void)col_walk_items(sub, COL_TRAVERSE_ONELEVEL,
                          col_parent_traverse_handler,
                          (void *)parent, NULL, (void *)&ps,
                          &depth);
@@ -353,10 +390,83 @@ static int col_find_property(struct collection_item *collection,
 
     /* Item is not found */
     TRACE_FLOW_STRING("col_find_property", "Exit - item NOT found");
-    return EOK;
+    return 0;
 }
 
+/* Backward compatible wrapper */
+static int col_find_property(struct collection_item *collection,
+                             const char *refprop,
+                             int idx,
+                             int use_type,
+                             int type,
+                             struct collection_item **parent)
+{
+    int error = EOK;
 
+    TRACE_FLOW_ENTRY();
+
+    error = col_find_property_sub(collection,
+                                  NULL,
+                                  1,
+                                  refprop,
+                                  idx,
+                                  0,
+                                  use_type,
+                                  type,
+                                  parent);
+
+    TRACE_FLOW_RETURN(error);
+    return error;
+}
+
+
+
+/* Find a duplicate item */
+int col_get_dup_item(struct collection_item *ci,
+                     const char *subcollection,
+                     const char *property_to_find,
+                     int type,
+                     int idx,
+                     int exact,
+                     struct collection_item **item)
+{
+    int error = EOK;
+    struct collection_item *parent = NULL;
+
+    TRACE_FLOW_ENTRY();
+
+    if (!ci) {
+        TRACE_ERROR_STRING("Passed in collection is invalid", "");
+        return EINVAL;
+    }
+
+    if (!item) {
+        TRACE_ERROR_STRING("Result storage is invalid", "");
+        return EINVAL;
+    }
+
+    if (!property_to_find) {
+        TRACE_ERROR_STRING("Passed in property to find is NULL", "");
+        return EINVAL;
+    }
+
+    /* Find the corresponding duplicate item */
+    if (col_find_property_sub(ci,
+                              subcollection,
+                              0,
+                              property_to_find,
+                              idx,
+                              exact,
+                              (type == COL_TYPE_ANY) ? 0 : 1,
+                              type,
+                              &parent)) {
+        *item = parent->next;
+    }
+    else error = ENOENT;
+
+    TRACE_FLOW_EXIT();
+    return error;
+}
 
 /* Insert item into the current collection */
 int col_insert_item_into_current(struct collection_item *collection,
@@ -584,13 +694,25 @@ int col_insert_item_into_current(struct collection_item *collection,
     case COL_DSP_FIRSTDUP:
     case COL_DSP_LASTDUP:
     case COL_DSP_NDUP:
+    case COL_DSP_LASTDUPNS:
+    case COL_DSP_NDUPNS:
 
                             if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
-                            else if (disposition == COL_DSP_LASTDUP) refindex = -1;
+                            else if ((disposition == COL_DSP_LASTDUP) ||
+                                     (disposition == COL_DSP_LASTDUPNS))
+                                        refindex = -1;
                             else refindex = idx;
 
                             /* We need to find property based on index */
-                            if (col_find_property(collection, item->property, refindex, 0, 0, &parent)) {
+                            if (col_find_property_sub(collection,
+                                                      NULL,
+                                                      INTERRUPT(disposition),
+                                                      item->property,
+                                                      refindex,
+                                                      0,
+                                                      0,
+                                                      0,
+                                                      &parent)) {
                                 item->next = parent->next;
                                 parent->next = item;
                                 header->count++;
@@ -764,13 +886,25 @@ int col_extract_item_from_current(struct collection_item *collection,
     case COL_DSP_FIRSTDUP:
     case COL_DSP_LASTDUP:
     case COL_DSP_NDUP:
+    case COL_DSP_LASTDUPNS:
+    case COL_DSP_NDUPNS:
 
                             if (disposition == COL_DSP_FIRSTDUP) refindex = 0;
-                            else if (disposition == COL_DSP_LASTDUP) refindex = -2;
+                            else if ((disposition == COL_DSP_LASTDUP) ||
+                                     (disposition == COL_DSP_LASTDUPNS))
+                                        refindex = -2;
                             else refindex = idx;
 
                             /* We need to find property based on index */
-                            if (col_find_property(collection, refprop, refindex, use_type, type, &parent)) {
+                            if (col_find_property_sub(collection,
+                                                      NULL,
+                                                      INTERRUPT(disposition),
+                                                      refprop,
+                                                      refindex,
+                                                      1,
+                                                      use_type,
+                                                      type,
+                                                      &parent)) {
                                 *ret_ref = parent->next;
                                 parent->next = (*ret_ref)->next;
                                 /* If we removed the last element adjust header */
@@ -1588,8 +1722,8 @@ static int col_find_item_and_do(struct collection_item *ci,
 
     /* Make sure that there is anything to search */
     type &= COL_TYPE_ANY;
-    if ((type == 0) && 
-        ((property_to_find == NULL) || 
+    if ((type == 0) &&
+        ((property_to_find == NULL) ||
          ((property_to_find != NULL) && (*property_to_find == '\0')))) {
         TRACE_ERROR_NUMBER("No item search criteria specified - returning error!", ENOENT);
         return ENOENT;
@@ -1805,15 +1939,43 @@ static int col_parent_traverse_handler(struct collection_item *head,
         *((struct collection_item **)traverse_data) = previous;
     }
     else {
-        /* As soon as we found first non matching one but there was a match
-         * return the parent of the last found item.
-         */
-        if (((!match) || (current->next == NULL)) && (to_find->index != 0) && (to_find->found)) {
-            *stop = 1;
-            if (to_find->index == -2)
-                *((struct collection_item **)traverse_data) = to_find->parent;
-            else
-                *((struct collection_item **)traverse_data) = to_find->parent->next;
+        if (to_find->interrupt) {
+            /* As soon as we found first non matching one but there was a match
+             * return the parent of the last found item.
+             */
+            if (((!match) || (current->next == NULL)) &&
+                 (to_find->index != 0) && (to_find->found)) {
+                *stop = 1;
+                if (to_find->index == -2)
+                    *((struct collection_item **)traverse_data) =
+                                                 to_find->parent;
+                else
+                    if (to_find->exact) {
+                        /* This means that we need to match the exact
+                         * index but we did not */
+                        to_find->parent = NULL;
+                        to_find->found = 0;
+                    }
+                    else
+                        *((struct collection_item **)traverse_data) =
+                                                 to_find->parent->next;
+            }
+        }
+        else if ((current->next == NULL) && (to_find->found)) {
+                *stop = 1;
+                if (to_find->index == -2)
+                    *((struct collection_item **)traverse_data) =
+                                                 to_find->parent;
+                else
+                    if (to_find->exact) {
+                        /* This means that we need to match the exact
+                         * index but we did not */
+                        to_find->parent = NULL;
+                        to_find->found = 0;
+                    }
+                    else
+                        *((struct collection_item **)traverse_data) =
+                                                 to_find->parent->next;
         }
     }
 
diff --git a/collection/collection.h b/collection/collection.h
index 0dd2f9000e718eaacb4500b9cf9a6423485a4c95..5cb56b37625307993031faa8ffe33ecd180fc2a9 100644
--- a/collection/collection.h
+++ b/collection/collection.h
@@ -801,6 +801,54 @@ int col_get_item(struct collection_item *ci,
                  struct collection_item **item);
 
 /**
+ * @brief Search function to get one of the duplicate items.
+ *
+ * Convenience function to get an individual item out of the list of duplicates.
+ * Caller should be aware that this is not a copy of the item
+ * but the pointer to actual item stored in the collection.
+ * The returned pointer should never be altered or freed by caller
+ * of the function.
+ * The caller should be sure that the collection does not go out of scope
+ * while the pointer to its data is in use.
+ * Working with the internals of the collection item structure directly
+ * may cause problems in future if the internal implementation changes.
+ * If collection to search or property to find is NULL function returns NULL.
+ *
+ * Use \ref getitem "item management" functions to work with the item.
+ *
+ * @param[in]  ci               Collection object to traverse.
+ * @param[in]  subcollection    Name of the sub collection to find
+ *                              item in. If NULL, the top level collection
+ *                              is used. One can use "foo!bar!baz"
+ *                              notation to identify the sub collection.
+ * @param[in]  property_to_find Name of the property to find.
+ * @param[in]  type             Type filter. Only properties
+ *                              of the given type will match.
+ *                              Can be 0 to indicate that all
+ *                              types should be evaluated.
+ * @param[in]  idx              Index of the duplicate to find.
+ *                              0 - any first instance
+ *                              positive - N-th instance (index is 0-based)
+ *                              negative - last instance
+ * @param[in]  exact            If 0 then if index above is greater than
+ *                              actual number of duplicates the last duplicate
+ *                              if be returned.
+ *                              If non-zero the funtion will return ENOENT
+ *                              in case the index is greater than actual
+ *                              number of duplicates.
+ * @return 0                    No errors.
+ * @return EINVAL               Invalid argument.
+ * @return ENOENT               Item is not found.
+ */
+int col_get_dup_item(struct collection_item *ci,
+                     const char *subcollection,
+                     const char *property_to_find,
+                     int type,
+                     int idx,
+                     int exact,
+                     struct collection_item **item);
+
+/**
  * @brief Sort collection.
  *
  * If the sub collections are included in sorting
@@ -2314,6 +2362,56 @@ int col_update_property(struct collection_item *ci,
  */
 #define COL_DSP_NDUP            7
 /**
+ * @brief Use last among nonsequential duplicates
+ *
+ * This mode applies to the list of duplicates that might be scattered
+ * across the collection
+ *
+ * For "insert":
+ * - Add property as the last dup of the given property.
+ *   The property name is taken from the item
+ *   and the value refprop is ignored.
+ *
+ * For "extract" or "delete":
+ * - Delete or extract the last duplicate of the property.
+ *   The property name is taken from the refprop.
+ *   Extracts or deletes last duplicate property in the uninterrupted
+ *   sequence of properties with the same name.
+ *   The property will be extracted or deleted if found
+ *   regardless of whether there are any duplicates or not.
+ */
+#define COL_DSP_LASTDUPNS        8
+/**
+ * @brief Use N-th among nonsequential duplicates
+ *
+ * This mode applies only to the list of duplicate
+ * properties that are going one after another.
+ *
+ * For "insert":
+ * - Add property as a N-th dup of the given property.
+ *   The property name is taken from the item
+ *   and the value refprop is ignored.
+ *   Index is zero based.
+ *   The COL_DSP_NDUPNS is used in case of the multi value property
+ *   to add a new property with the same name into specific place
+ *   in the list of properties with the same name.
+ *   The index of 0 will mean to add the property before the first
+ *   instance of the property with the same name.
+ *   If the property does not exist ENOENT will be returned.
+ *   If the index is greater than the last property with the same
+ *   name the item will be added immediately after last
+ *   property with the same name.
+ *
+ * For "extract" or "delete":
+ * - Delete or extract N-th duplicate property.
+ *   Index is zero based.
+ *   The property name is taken from the refprop.
+ *   If index is greater than number of duplicate
+ *   properties in the sequence ENOENT is returned.
+ *
+ */
+#define COL_DSP_NDUPNS           9
+/**
  * @}
  */
 
diff --git a/collection/collection_ut.c b/collection/collection_ut.c
index 965daf408bf91767ea45f070dc33eba6b08e560d..bba790006fe2f995fc632e7df26c0adde6491962 100644
--- a/collection/collection_ut.c
+++ b/collection/collection_ut.c
@@ -37,6 +37,82 @@ int verbose = 0;
         if (verbose) foo; \
     } while(0)
 
+static int dup_test(void)
+{
+    int error = EOK;
+    struct collection_item *dup = NULL;
+    struct collection_item *item = NULL;
+    int i;
+    const char *results[] = { "value5",
+                              "value5",
+                              "value0",
+                              "value1",
+                              "value2",
+                              "value3",
+                              "value4",
+                              "value5" };
+
+    TRACE_FLOW_ENTRY();
+
+    COLOUT(printf("\n\nDUP TEST!!!.\n\n\n"));
+    COLOUT(printf("Creating DUP collection.\n"));
+
+    if ((error = col_create_collection(&dup, "dup", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value0", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value1", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value2", 0)) ||
+        (error = col_add_str_property(dup, NULL, "foo", "bar", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value3", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value4", 0)) ||
+        (error = col_add_str_property(dup, NULL, "key", "value5", 0))) {
+        printf("Failed to add property. Error %d\n", error);
+        col_destroy_collection(dup);
+        return error;
+    }
+
+    COLOUT(col_debug_collection(dup, COL_TRAVERSE_DEFAULT));
+
+    if (!col_get_dup_item(NULL, NULL, "key", COL_TYPE_ANY, 10000, 0, &item) ||
+        !col_get_dup_item(dup, NULL, NULL, COL_TYPE_ANY, 10000, 0, &item) ||
+        !col_get_dup_item(dup, NULL, "key", COL_TYPE_ANY, 10000, 1, &item) ||
+        !col_get_dup_item(dup, NULL, "key", COL_TYPE_STRING, 0, 1, NULL)) {
+        /* Expected error but did not get it */
+        printf("Expected error but got success.\n");
+        col_destroy_collection(dup);
+        return EINVAL;
+    }
+
+    for (i = 0; i < 6; i++) {
+        error = col_get_dup_item(dup, NULL, "key",
+                                 COL_TYPE_STRING, i, 1, &item);
+        if (error) {
+            printf("Search returned error %d.\n", error);
+            col_destroy_collection(dup);
+            return error;
+        }
+
+        if (!item) {
+            printf("Item should be found - but in reality is NULL.\n");
+            col_destroy_collection(dup);
+            return EINVAL;
+        }
+
+        if (strcmp((char *)col_get_item_data(item), results[i+2]) != 0) {
+            printf("Expected %s got %s.\n", results[i+2],
+                   (char *)col_get_item_data(item));
+            col_destroy_collection(dup);
+            return EINVAL;
+        }
+    }
+
+    col_destroy_collection(dup);
+
+    TRACE_FLOW_EXIT();
+
+    COLOUT(printf("\n\nEND OF DUP TEST!!!.\n\n\n"));
+
+    return EOK;
+}
 
 
 static int ref_collection_test(void)
@@ -1340,6 +1416,82 @@ static int iterator_test(void)
     return EOK;
 }
 
+static int validate_collection(struct collection_item *col,
+                               const char *varray[][2])
+{
+    struct collection_iterator *iterator = NULL;
+    int error = EOK;
+    struct collection_item *item = NULL;
+    int count = -1;
+
+    COLOUT(printf("\n\n==== Validating collection ====\n\n"));
+
+    /* Bind iterator */
+    error =  col_bind_iterator(&iterator, col, COL_TRAVERSE_DEFAULT);
+    if (error) {
+        COLOUT(printf("Error (bind): %d\n", error));
+        return error;
+    }
+
+    do {
+
+        count++;
+
+        /* Loop through a collection */
+        error = col_iterate_collection(iterator, &item);
+        if (error) {
+            COLOUT(printf("Error (iterate): %d\n", error));
+            col_unbind_iterator(iterator);
+            return error;
+        }
+
+        /* Are we done ? */
+        if (item == NULL) break;
+
+        /* Set count when we skip header */
+        if (col_get_item_type(item) == COL_TYPE_COLLECTION) {
+            count = -1;
+            continue;
+        }
+
+        if (varray[count][0] == NULL) {
+            /* We have reached the end of the array but not the collection.
+             * This means the test failed. Return error.
+             */
+            COLOUT(printf("Got extra item in the collection: %s=%s\n",
+                          col_get_item_property(item, NULL),
+                          (char *)(col_get_item_data(item))));
+            col_unbind_iterator(iterator);
+            return -1;
+        }
+
+        if ((strcmp(col_get_item_property(item, NULL),
+                    varray[count][0]) != 0) ||
+            (strcmp((char *)(col_get_item_data(item)),
+                    varray[count][1]) != 0 )) {
+                COLOUT(printf("Expected %s=\"%s\" got %s=\"%s\" \n",
+                              varray[count][0],
+                              varray[count][1],
+                              col_get_item_property(item, NULL),
+                              (char *)(col_get_item_data(item))));
+                col_unbind_iterator(iterator);
+                return -1;
+        }
+    }
+    while(1);
+
+    col_unbind_iterator(iterator);
+
+    if (varray[count][0] != NULL) {
+        COLOUT(printf("Expected end got %s=\"%s\"\n", varray[count][0],
+                                                      varray[count][1]));
+        return -1;
+    }
+
+    COLOUT(printf("\n\n==== Validating collection - OK ====\n\n"));
+    return EOK;
+}
+
 
 static int insert_extract_test(void)
 {
@@ -1347,6 +1499,34 @@ static int insert_extract_test(void)
     struct collection_item *col2;
     int error = EOK;
     struct collection_item *item = NULL;
+    const char *varray1[][2] = { { "property_-1", "value_-1" },
+                           { "property0", "value0lastdup" },
+                           { "property0_5", "value0_5" },
+                           { "property1", "value1update" },
+                           { "second", "second" },
+                           { "property1_5", "value1_5" },
+                           { "property2", "value2" },
+                           { "property10", "value10" },
+                           { "property10", "value10lastdup" },
+                           { "property_-2", "value-2moved_to_bottom" },
+                           { "property0", "extra_1" },
+                           { "property100", "value100" },
+                           { NULL, NULL } };
+
+    const char *varray2[][2] = { { "property_-1_5","value_-1_5" },
+                           { "property1_6", "value_1_6_moved_to_front" },
+                           { "property0", "very_last" },
+                           { "property10", "value10dup" },
+                           { "property_-0_5", "value_-0_5" },
+                           { "property0", "value0firstdupupdate" },
+                           { "property0", "value0" },
+                           { "property0", "before 0_5" },
+                           { "property0", "value0middledup" },
+                           { "property0", "extra_2" },
+                           { "property0", "after_extra2" },
+                           { "property0", "before_extra2" },
+                           { NULL, NULL } };
+
 
     COLOUT(printf("\n\n==== INSERTION TEST ====\n\n"));
 
@@ -1398,13 +1578,15 @@ static int insert_extract_test(void)
                                          "property0", "value0middledup", 0)) ||
         (error = col_insert_str_property(col, NULL, 0,
                                          NULL, 0, COL_INSERT_DUPOVER ,
-                                         "property0", "value0firstdupupdate", 0)) ||
+                                         "property0",
+                                         "value0firstdupupdate", 0)) ||
         (error = col_insert_str_property(col, NULL, 0,
                                          NULL, 0, COL_INSERT_DUPOVERT,
                                          "property1", "value1update", 0)) ||
         ((error = col_insert_str_property(col, NULL, 0,
                                           NULL, 0, COL_INSERT_DUPERROR,
-                                          "property0", "does not matter", 0)) != EEXIST) ||
+                                          "property0",
+                                          "does not matter", 0)) != EEXIST) ||
          (error = col_insert_str_property(col, NULL, COL_DSP_NDUP,
                                           NULL, 5, COL_INSERT_NOCHECK,
                                           "property10", "value10dup", 0)) ||
@@ -1413,12 +1595,32 @@ static int insert_extract_test(void)
                                           "property10", "value10lastdup", 0)) ||
          (error = col_insert_str_property(col, NULL, COL_DSP_END,
                                           NULL, 0, COL_INSERT_DUPMOVET,
-                                          "property_-2", "value-2moved_to_bottom", 0)) ||
+                                          "property_-2",
+                                          "value-2moved_to_bottom", 0)) ||
          (error = col_insert_str_property(col, NULL, COL_DSP_FRONT,
                                           NULL, 0, COL_INSERT_DUPMOVE,
-                                          "property1_6", "value_1_6_moved_moved_to_front", 0))) {
-
-        printf("ERROR in the ITERATION TEST\n");
+                                          "property1_6",
+                                          "value_1_6_moved_to_front", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_END,
+                                          NULL, 0, COL_INSERT_NOCHECK,
+                                          "property0", "extra_1", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_END,
+                                          NULL, 0, COL_INSERT_NOCHECK,
+                                         "property0", "extra_2", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_NDUP,
+                                          "property0", 10, COL_INSERT_NOCHECK,
+                                          "property0", "before 0_5", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_NDUPNS,
+                                          "property0", 10, COL_INSERT_NOCHECK,
+                                          "property0", "after_extra2", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_NDUPNS,
+                                          "property0", 6, COL_INSERT_NOCHECK,
+                                          "property0", "before_extra2", 0)) ||
+         (error = col_insert_str_property(col, NULL, COL_DSP_LASTDUPNS,
+                                          "property0", 0, COL_INSERT_NOCHECK,
+                                          "property0", "very_last", 0))) {
+
+        printf("ERROR in the ITERATION TEST %d\n", error);
         col_debug_collection(col, COL_TRAVERSE_DEFAULT);
         col_destroy_collection(col);
         return error;
@@ -1432,81 +1634,165 @@ static int insert_extract_test(void)
 
     if ((error = col_create_collection(&col2, "extraction", 0)) ||
 
+        /* Extracting "property 1_6" value "value_1_6_moved_to_front" */
         (error = col_extract_item(col, NULL, COL_DSP_FRONT,
                                   NULL, 0, 0, &item)) ||
 
         (error = col_insert_item(col2, NULL, item, COL_DSP_FRONT,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting "property_-1_5" value "value_-1_5" */
         (error = col_extract_item(col, NULL, COL_DSP_FRONT,
                                   NULL, 0, 0, &item)) ||
 
+        /* Putting it in front */
         (error = col_insert_item(col2, NULL, item, COL_DSP_FRONT,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting "property0" value "very_last" */
         (error = col_extract_item(col, NULL, COL_DSP_END,
                                   NULL, 0, 0, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Inserting extra property to the end of first collection */
         (error = col_insert_str_property(col, NULL, COL_DSP_INDEX,
                                          NULL, 100, COL_INSERT_NOCHECK,
                                          "property100", "value100", 0)) ||
 
+        /* This will extract "property10" with value "value10dup" */
         (error = col_extract_item(col, NULL, COL_DSP_AFTER,
                                   "property10", 0, COL_TYPE_STRING, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting "property_-0_5" value "value_-0_5" */
         (error = col_extract_item(col, NULL, COL_DSP_BEFORE,
                                   "property0", 0, COL_TYPE_STRING, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        /* Printing initial collection to see its state */
+        ((verbose) && (error = col_debug_collection(col,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Going for the second item in the collection which is
+         * at this moment "property0" with value "value0firstdupupdate"
+         */
         (error = col_extract_item(col, NULL, COL_DSP_INDEX,
                                   NULL, 1, 0, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting first sequextial duplicate of property0 whic is value
+         * "value0"
+         */
         (error = col_extract_item(col, NULL, COL_DSP_NDUP,
                                   "property0", 1, 0, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting last sequential dup of "property0"
+         * which is at this moment "before 0_5"
+         */
         (error = col_extract_item(col, NULL, COL_DSP_LASTDUP,
                                   "property0", 0, 0, &item)) ||
 
+        /* Putting it to the end */
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT))) ||
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
 
+        /* Extracting first dup of "property0"
+         * which is at this moment "value0middledup"
+         */
         (error = col_extract_item(col, NULL, COL_DSP_FIRSTDUP,
                                   "property0", 0, 0, &item)) ||
 
         (error = col_insert_item(col2, NULL, item, COL_DSP_END,
                                  NULL, 0, COL_INSERT_NOCHECK)) ||
 
-        ((verbose) && (error = col_debug_collection(col2, COL_TRAVERSE_DEFAULT)))) {
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Printing initial collection to see its state */
+        ((verbose) && (error = col_debug_collection(col,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Should extract extra_2 */
+        (error = col_extract_item(col, NULL, COL_DSP_NDUPNS,
+                                  "property0", 3, 0, &item)) ||
+
+        (error = col_insert_item(col2, NULL, item, COL_DSP_END,
+                                 NULL, 0, COL_INSERT_NOCHECK)) ||
+
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Printing initial collection to see its state */
+        ((verbose) && (error = col_debug_collection(col,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Try extracting something that is out of index, expect ENOENT */
+        ((error = col_extract_item(col, NULL, COL_DSP_NDUPNS,
+                                  "property0", 10, 0, &item)) != ENOENT) ||
+
+        /* Should extract after_extra_2 */
+        (error = col_extract_item(col, NULL, COL_DSP_NDUPNS,
+                                  "property0", 3, 0, &item)) ||
+
+        (error = col_insert_item(col2, NULL, item, COL_DSP_END,
+                                 NULL, 0, COL_INSERT_NOCHECK)) ||
+
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Printing initial collection to see its state */
+        ((verbose) && (error = col_debug_collection(col,
+                                                    COL_TRAVERSE_DEFAULT))) ||
+
+        /* Should extract before_extra_2 */
+        (error = col_extract_item(col, NULL, COL_DSP_LASTDUPNS,
+                                  "property0", 0, 0, &item)) ||
+
+        (error = col_insert_item(col2, NULL, item, COL_DSP_END,
+                                 NULL, 0, COL_INSERT_NOCHECK)) ||
+
+        ((verbose) && (error = col_debug_collection(col2,
+                                                    COL_TRAVERSE_DEFAULT)))) {
 
         COLOUT(printf("ERROR in the EXTRACTION TEST\n"));
         COLOUT(printf("Collection 1\n"));
@@ -1523,10 +1809,25 @@ static int insert_extract_test(void)
     COLOUT(printf("Collection 2\n"));
     COLOUT(col_debug_collection(col2, COL_TRAVERSE_DEFAULT));
 
+    error = validate_collection(col, varray1);
+    if (error) {
+        COLOUT(printf("Collection 1 validation failed.\n"));
+        col_destroy_collection(col);
+        col_destroy_collection(col2);
+        return error;
+    }
+
+    error = validate_collection(col2, varray2);
+    if (error) {
+        COLOUT(printf("Collection 2 validation failed.\n"));
+        col_destroy_collection(col);
+        col_destroy_collection(col2);
+        return error;
+    }
+
     col_destroy_collection(col2);
     col_destroy_collection(col);
 
-
     return EOK;
 }
 
@@ -1747,7 +2048,7 @@ static int search_test(void)
         }
         else {
             printf("Found unexpected item with NULL & 0. Error %d\n", error);
-            error = EINVAL; 
+            error = EINVAL;
         }
         return error;
     }
@@ -1765,7 +2066,7 @@ static int search_test(void)
         }
         else {
             printf("Found unexpected item with \"\" & 0. Error %d\n", error);
-            error = EINVAL; 
+            error = EINVAL;
         }
         return error;
     }
@@ -1936,6 +2237,7 @@ int main(int argc, char *argv[])
                         delete_test,
                         search_test,
                         sort_test,
+                        dup_test,
                         NULL };
     test_fn t;
     int i = 0;
diff --git a/collection/libcollection.sym b/collection/libcollection.sym
index 9110cae513482564cf43e1b7892a5a208d32a2d0..fab025b0a8dbbe6dfa7a9e2ff2b6935640ad52e6 100644
--- a/collection/libcollection.sym
+++ b/collection/libcollection.sym
@@ -143,3 +143,9 @@ global:
 local:
     *;
 };
+
+COLLECTION_0.7 {
+global:
+    /* collection.h */
+    col_get_dup_item;
+};
-- 
1.7.1

