/*
* Copyright (c) 2012-2013 Vojtech Horky
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
*
* Helper functions for working with list of items.
*/
#pragma warning(push, 0)
#include <assert.h>
#include <stdlib.h>
#pragma warning(pop)
#include "internal.h"
#include <pcut/pcut.h>
/** Find next item with actual content.
*
* @param item Head of the list.
* @return First item with actual content or NULL on end of list.
*/
pcut_item_t *pcut_get_real_next(pcut_item_t *item) {
if (item == NULL) {
return NULL;
}
do {
item = item->next;
} while ((item != NULL) && (item->kind == PCUT_KIND_SKIP));
return item;
}
/** Retrieve the first item with actual content.
*
* Unlike pcut_get_real_next(), where we always advance after the
* first item, here the @p item itself could be returned.
*
* @param item Head of the list.
* @return First item with actual content or NULL on end of list.
*/
pcut_item_t *pcut_get_real(pcut_item_t *item) {
if (item == NULL) {
return NULL;
}
if (item->kind == PCUT_KIND_SKIP) {
return pcut_get_real_next(item);
} else {
return item;
}
}
/** In-line nested lists into the parent.
*
* @param nested Head of the nested list.
*/
static void inline_nested_lists(pcut_item_t *nested) {
pcut_item_t *first;
if (nested->kind != PCUT_KIND_NESTED) {
return;
}
if (nested->nested == NULL) {
nested->kind = PCUT_KIND_SKIP;
return;
}
first = pcut_fix_list_get_real_head(nested->nested);
nested->nested->next = nested->next;
if (nested->next != NULL) {
nested->next->previous = nested->nested;
}
nested->next = first;
first->previous = nested;
nested->kind = PCUT_KIND_SKIP;
}
/** Assing unique ids to each item in the list.
*
* @param first List head.
*/
static void set_ids(pcut_item_t *first) {
int id = 1;
pcut_item_t *it;
assert(first != NULL);
if (first->kind == PCUT_KIND_SKIP) {
first = pcut_get_real_next(first);
}
for (it = first; it != NULL; it = pcut_get_real_next(it)) {
it->id = id;
id++;
}
}
/** Hide tests that are marked to be skipped.
*
* Go through all tests and those that have PCUT_EXTRA_SKIP mark
* as skipped with PCUT_KIND_SKIP.
*
* @param first Head of the list.
*/
static void detect_skipped_tests(pcut_item_t *first) {
pcut_item_t *it;
assert(first != NULL);
if (first->kind == PCUT_KIND_SKIP) {
first = pcut_get_real_next(first);
}
for (it = first; it != NULL; it = pcut_get_real_next(it)) {
pcut_extra_t *extras;
if (it->kind != PCUT_KIND_TEST) {
continue;
}
extras = it->extras;
while (extras->type != PCUT_EXTRA_LAST) {
if (extras->type == PCUT_EXTRA_SKIP) {
it->kind = PCUT_KIND_SKIP;
break;
}
extras++;
}
}
}
/** Convert the static single-linked list into a flat double-linked list.
*
* The conversion includes
* * adding forward links
* * flattening of any nested lists
* * assigning of unique ids
*
* @param last Tail of the list.
* @return Head of the fixed list.
*/
pcut_item_t *pcut_fix_list_get_real_head(pcut_item_t *last) {
pcut_item_t *next, *it;
last->next = NULL;
inline_nested_lists(last);
next = last;
it = last->previous;
while (it != NULL) {
it->next = next;
inline_nested_lists(it);
next = it;
it = it->previous;
}
detect_skipped_tests(next);
set_ids(next);
return next;
}
/** Compute the number of all tests in a list.
*
* @param it Head of the list.
* @return Number of tests.
*/
int pcut_count_tests(pcut_item_t *it) {
int count = 0;
while (it != NULL) {
if (it->kind == PCUT_KIND_TEST) {
count++;
}
it = pcut_get_real_next(it);
}
return count;
}