feat(list): add list implementation
* a list implementation with a few test
This commit is contained in:
parent
02dacd5e70
commit
46d2e75d2f
@ -19,6 +19,7 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
endif ()
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(library/list)
|
||||
add_subdirectory(library/log)
|
||||
|
||||
if ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MODERN_CMAKE_BUILD_TESTING)
|
||||
|
||||
20
library/list/CMakeLists.txt
Normal file
20
library/list/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
|
||||
|
||||
include("project-meta-info.in")
|
||||
|
||||
project(waitui-list
|
||||
VERSION ${project_version}
|
||||
DESCRIPTION ${project_description}
|
||||
HOMEPAGE_URL ${project_homepage}
|
||||
LANGUAGES C)
|
||||
|
||||
add_library(list OBJECT)
|
||||
|
||||
target_sources(list
|
||||
PRIVATE
|
||||
"src/list.c"
|
||||
PUBLIC
|
||||
"include/waitui/list.h"
|
||||
)
|
||||
|
||||
target_include_directories(list PUBLIC "include")
|
||||
238
library/list/include/waitui/list.h
Normal file
238
library/list/include/waitui/list.h
Normal file
@ -0,0 +1,238 @@
|
||||
/**
|
||||
* @file list.h
|
||||
* @author rick
|
||||
* @date 22.07.20
|
||||
* @brief File for the List implementation
|
||||
*/
|
||||
|
||||
#ifndef WAITUI_LIST_H
|
||||
#define WAITUI_LIST_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Public types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Type representing a List node.
|
||||
*/
|
||||
typedef struct waitui_list_node waitui_list_node;
|
||||
|
||||
/**
|
||||
* @brief Type for element destroy function.
|
||||
*/
|
||||
typedef void (*waitui_list_element_destroy)(void **element);
|
||||
|
||||
/**
|
||||
* @brief Type representing a List.
|
||||
*/
|
||||
typedef struct waitui_list waitui_list;
|
||||
|
||||
/**
|
||||
* @brief Type representing a List iterator.
|
||||
*/
|
||||
typedef struct waitui_list_iter waitui_list_iter;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Public defines
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define INTERFACE_LIST_TYPEDEF(type) \
|
||||
typedef waitui_list type##_list; \
|
||||
typedef waitui_list_iter type##_list_iter
|
||||
#define IMPLEMENTATION_LIST_TYPEDEF(type)
|
||||
|
||||
#define INTERFACE_LIST_NEW(type) extern type##_list *type##_list_new()
|
||||
#define IMPLEMENTATION_LIST_NEW(type) \
|
||||
type##_list *type##_list_new() { \
|
||||
return (type##_list *) waitui_list_new( \
|
||||
(waitui_list_element_destroy) type##_destroy); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_DESTROY(type) \
|
||||
extern void type##_list_destroy(type##_list **this)
|
||||
#define IMPLEMENTATION_LIST_DESTROY(type) \
|
||||
void type##_list_destroy(type##_list **this) { \
|
||||
waitui_list_destroy((waitui_list **) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_PUSH(type) \
|
||||
extern int type##_list_push(type##_list *this, type *type##Element)
|
||||
#define IMPLEMENTATION_LIST_PUSH(type) \
|
||||
int type##_list_push(type##_list *this, type *type##Element) { \
|
||||
return waitui_list_push((waitui_list *) this, (void *) type##Element); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_POP(type) extern type *type##_list_pop(type##_list *this)
|
||||
#define IMPLEMENTATION_LIST_POP(type) \
|
||||
type *type##_list_pop(type##_list *this) { \
|
||||
return (type *) waitui_list_pop((waitui_list *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_UNSHIFT(type) \
|
||||
extern int type##_list_unshift(type##_list *this, type *type##Element)
|
||||
#define IMPLEMENTATION_LIST_UNSHIFT(type) \
|
||||
int type##_list_unshift(type##_list *this, type *type##Element) { \
|
||||
return waitui_list_unshift((waitui_list *) this, \
|
||||
(void *) type##Element); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_SHIFT(type) \
|
||||
extern type *type##_list_shift(type##_list *this)
|
||||
#define IMPLEMENTATION_LIST_SHIFT(type) \
|
||||
type *type##_list_shift(type##_list *this) { \
|
||||
return (type *) waitui_list_shift((waitui_list *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_PEEK(type) \
|
||||
extern type *type##_list_peek(type##_list *this)
|
||||
#define IMPLEMENTATION_LIST_PEEK(type) \
|
||||
type *type##_list_peek(type##_list *this) { \
|
||||
return (type *) waitui_list_peek((waitui_list *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_GET_ITERATOR(type) \
|
||||
extern type##_list_iter *type##_list_getIterator(type##_list *this)
|
||||
#define IMPLEMENTATION_LIST_GET_ITERATOR(type) \
|
||||
type##_list_iter *type##_list_getIterator(type##_list *this) { \
|
||||
return (type##_list_iter *) waitui_list_getIterator( \
|
||||
(waitui_list *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_ITER_HAS_NEXT(type) \
|
||||
extern bool type##_list_iter_hasNext(type##_list_iter *this)
|
||||
#define IMPLEMENTATION_LIST_ITER_HAS_NEXT(type) \
|
||||
bool type##_list_iter_hasNext(type##_list_iter *this) { \
|
||||
return waitui_list_iter_hasNext((waitui_list_iter *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_ITER_NEXT(type) \
|
||||
extern type *type##_list_iter_next(type##_list_iter *this)
|
||||
#define IMPLEMENTATION_LIST_ITER_NEXT(type) \
|
||||
type *type##_list_iter_next(type##_list_iter *this) { \
|
||||
return (type *) waitui_list_iter_next((waitui_list_iter *) this); \
|
||||
}
|
||||
|
||||
#define INTERFACE_LIST_ITER_DESTROY(type) \
|
||||
extern void type##_list_iter_destroy(type##_list_iter **this)
|
||||
#define IMPLEMENTATION_LIST_ITER_DESTROY(type) \
|
||||
void type##_list_iter_destroy(type##_list_iter **this) { \
|
||||
waitui_list_iter_destroy((waitui_list_iter **) this); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Define for quickly created list implementations for a value type.
|
||||
* @param[in] kind Whether to create interface list definition
|
||||
* or actual implementation
|
||||
* @param[in] type For what type to create the list
|
||||
*/
|
||||
#define CREATE_LIST_TYPE(kind, type) \
|
||||
kind##_LIST_TYPEDEF(type); \
|
||||
kind##_LIST_NEW(type); \
|
||||
kind##_LIST_DESTROY(type); \
|
||||
kind##_LIST_PUSH(type); \
|
||||
kind##_LIST_POP(type); \
|
||||
kind##_LIST_UNSHIFT(type); \
|
||||
kind##_LIST_SHIFT(type); \
|
||||
kind##_LIST_PEEK(type); \
|
||||
kind##_LIST_GET_ITERATOR(type); \
|
||||
kind##_LIST_ITER_HAS_NEXT(type); \
|
||||
kind##_LIST_ITER_NEXT(type); \
|
||||
kind##_LIST_ITER_DESTROY(type);
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Create a List.
|
||||
* @param[in] elementDestroyCallback Function to call for element destruction
|
||||
* @return A pointer to waitui_list or NULL if memory allocation failed
|
||||
*/
|
||||
extern waitui_list *
|
||||
waitui_list_new(waitui_list_element_destroy elementDestroyCallback);
|
||||
|
||||
/**
|
||||
* @brief Destroy a List.
|
||||
* @param[in,out] this The List to destroy
|
||||
* @note This will free call for every element the elementDestroyCallback
|
||||
*/
|
||||
extern void waitui_list_destroy(waitui_list **this);
|
||||
|
||||
/**
|
||||
* @brief Add the element to the end of the List.
|
||||
* @param[in,out] this The List to add the element at the end
|
||||
* @param[in] element The element to add
|
||||
* @note This function does steel the pointer to the element.
|
||||
* @retval 1 Ok
|
||||
* @retval 0 Memory allocation failed
|
||||
*/
|
||||
extern int waitui_list_push(waitui_list *this, void *element);
|
||||
|
||||
/**
|
||||
* @brief Remove the element from the end of the List and return it.
|
||||
* @param[in,out] this The List to remove the element from
|
||||
* @note The caller has to destroy element on its own.
|
||||
* @return The element or NULL if waitui_list is empty
|
||||
*/
|
||||
extern void *waitui_list_pop(waitui_list *this);
|
||||
|
||||
/**
|
||||
* @brief Add the element to the beginning of the List.
|
||||
* @param[in,out] this The List to add the element at the beginning
|
||||
* @param[in] element The element to add
|
||||
* @note This function does steel the pointer to the element.
|
||||
* @retval 1 Ok
|
||||
* @retval 0 Memory allocation failed
|
||||
*/
|
||||
extern int waitui_list_unshift(waitui_list *this, void *element);
|
||||
|
||||
/**
|
||||
* @brief Remove the element from the beginning of the List and return it.
|
||||
* @param[in,out] this The List to remove the element from
|
||||
* @note The caller has to destroy element on its own.
|
||||
* @return The element or NULL if waitui_list is empty
|
||||
*/
|
||||
extern void *waitui_list_shift(waitui_list *this);
|
||||
|
||||
/**
|
||||
* @brief Return the element from the end of the List, without removing it.
|
||||
* @param[in] this The List to get the last element from
|
||||
* @warning The caller has not to destroy element on its own.
|
||||
* @return The element or NULL if waitui_list is empty
|
||||
*/
|
||||
extern void *waitui_list_peek(waitui_list *this);
|
||||
|
||||
/**
|
||||
* @brief Return the iterator to iterate over the List.
|
||||
* @param[in] this The List to get the iterator for
|
||||
* @return A pointer to waitui_list_iter or NULL if memory allocation failed
|
||||
*/
|
||||
extern waitui_list_iter *waitui_list_getIterator(waitui_list *this);
|
||||
|
||||
/**
|
||||
* @brief Return true if the List iterator has a next element.
|
||||
* @param[in] this The List iterator to test for next element available
|
||||
* @retval true If the iterator has a next element available
|
||||
* @retval false If the iterator has no next element available
|
||||
*/
|
||||
extern bool waitui_list_iter_hasNext(waitui_list_iter *this);
|
||||
|
||||
/**
|
||||
* @brief Return the next element from the List iterator.
|
||||
* @param[in] this The List iterator to get the next element
|
||||
* @return The element or NULL if no more elements are available
|
||||
*/
|
||||
extern void *waitui_list_iter_next(waitui_list_iter *this);
|
||||
|
||||
/**
|
||||
* @brief Destroy a List iterator.
|
||||
* @param[in,out] this The List iterator to destroy
|
||||
*/
|
||||
extern void waitui_list_iter_destroy(waitui_list_iter **this);
|
||||
|
||||
#endif//WAITUI_LIST_H
|
||||
3
library/list/project-meta-info.in
Normal file
3
library/list/project-meta-info.in
Normal file
@ -0,0 +1,3 @@
|
||||
set(project_version 0.0.1)
|
||||
set(project_description "waitui list library")
|
||||
set(project_homepage "http://example.com")
|
||||
219
library/list/src/list.c
Normal file
219
library/list/src/list.c
Normal file
@ -0,0 +1,219 @@
|
||||
/**
|
||||
* @file list.c
|
||||
* @author rick
|
||||
* @date 22.07.20
|
||||
* @brief File for the List implementation
|
||||
*/
|
||||
|
||||
#include "waitui/list.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Local types
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Struct representing a List node.
|
||||
*/
|
||||
struct waitui_list_node {
|
||||
void *element;
|
||||
waitui_list_node *next;
|
||||
waitui_list_node *prev;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Struct representing a List.
|
||||
*/
|
||||
struct waitui_list {
|
||||
waitui_list_node *head;
|
||||
waitui_list_node *tail;
|
||||
waitui_list_element_destroy elementDestroyCallback;
|
||||
unsigned long long length;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Struct representing a List iterator.
|
||||
*/
|
||||
struct waitui_list_iter {
|
||||
waitui_list_node *node;
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Local functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Create a List iterator.
|
||||
* @param[in] node The node to use as a start for the iterator
|
||||
* @return A pointer to waitui_list_iter or NULL if memory allocation failed
|
||||
*/
|
||||
static waitui_list_iter *waitui_list_iter_new(waitui_list_node *node) {
|
||||
waitui_list_iter *this = NULL;
|
||||
|
||||
this = calloc(1, sizeof(*this));
|
||||
if (!this) { return NULL; }
|
||||
|
||||
this->node = node;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
waitui_list *
|
||||
waitui_list_new(waitui_list_element_destroy elementDestroyCallback) {
|
||||
waitui_list *this = NULL;
|
||||
|
||||
this = calloc(1, sizeof(*this));
|
||||
if (!this) { return NULL; }
|
||||
|
||||
this->elementDestroyCallback = elementDestroyCallback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void waitui_list_destroy(waitui_list **this) {
|
||||
if (!this || !(*this)) { return; }
|
||||
|
||||
while ((*this)->head) {
|
||||
waitui_list_node *temp = (*this)->head;
|
||||
(*this)->head = (*this)->head->next;
|
||||
|
||||
if ((*this)->elementDestroyCallback) {
|
||||
(*this)->elementDestroyCallback(&temp->element);
|
||||
}
|
||||
free(temp);
|
||||
}
|
||||
|
||||
free(*this);
|
||||
*this = NULL;
|
||||
}
|
||||
|
||||
int waitui_list_push(waitui_list *this, void *element) {
|
||||
waitui_list_node *node = NULL;
|
||||
|
||||
if (!this || !element) { return 0; }
|
||||
|
||||
node = calloc(1, sizeof(*node));
|
||||
if (!node) { return 0; }
|
||||
|
||||
node->element = element;
|
||||
|
||||
if (this->tail == NULL) {
|
||||
this->head = this->tail = node;
|
||||
} else {
|
||||
node->prev = this->tail;
|
||||
this->tail->next = node;
|
||||
this->tail = node;
|
||||
}
|
||||
|
||||
this->length++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *waitui_list_pop(waitui_list *this) {
|
||||
waitui_list_node *node = NULL;
|
||||
void *element = NULL;
|
||||
|
||||
if (!this || this->length == 0) { return NULL; }
|
||||
|
||||
node = this->tail;
|
||||
element = node->element;
|
||||
|
||||
this->tail = this->tail->prev;
|
||||
this->length--;
|
||||
if (this->tail != NULL) {
|
||||
this->tail->next = NULL;
|
||||
} else {
|
||||
this->head = NULL;
|
||||
}
|
||||
|
||||
free(node);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
int waitui_list_unshift(waitui_list *this, void *element) {
|
||||
waitui_list_node *node = NULL;
|
||||
|
||||
if (!this || !element) { return 0; }
|
||||
|
||||
node = calloc(1, sizeof(*node));
|
||||
if (!node) { return 0; }
|
||||
|
||||
node->element = element;
|
||||
|
||||
if (this->head == NULL) {
|
||||
this->head = this->tail = node;
|
||||
} else {
|
||||
node->next = this->head;
|
||||
this->head->prev = node;
|
||||
this->head = node;
|
||||
}
|
||||
|
||||
this->length++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *waitui_list_shift(waitui_list *this) {
|
||||
waitui_list_node *node = NULL;
|
||||
void *element = NULL;
|
||||
|
||||
if (!this || this->length == 0) { return NULL; }
|
||||
|
||||
node = this->head;
|
||||
element = node->element;
|
||||
|
||||
this->head = this->head->next;
|
||||
this->length--;
|
||||
if (this->head != NULL) {
|
||||
this->head->prev = NULL;
|
||||
} else {
|
||||
this->tail = NULL;
|
||||
}
|
||||
|
||||
free(node);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
void *waitui_list_peek(waitui_list *this) {
|
||||
if (!this || this->length == 0) { return NULL; }
|
||||
return this->tail->element;
|
||||
}
|
||||
|
||||
waitui_list_iter *waitui_list_getIterator(waitui_list *this) {
|
||||
if (!this) { return NULL; }
|
||||
return waitui_list_iter_new(this->head);
|
||||
}
|
||||
|
||||
bool waitui_list_iter_hasNext(waitui_list_iter *this) {
|
||||
if (!this) { return false; }
|
||||
return this->node != NULL;
|
||||
}
|
||||
|
||||
void *waitui_list_iter_next(waitui_list_iter *this) {
|
||||
waitui_list_node *node = NULL;
|
||||
|
||||
if (!this) { return NULL; }
|
||||
|
||||
node = this->node;
|
||||
this->node = this->node->next;
|
||||
|
||||
return node->element;
|
||||
}
|
||||
|
||||
void waitui_list_iter_destroy(waitui_list_iter **this) {
|
||||
if (!this || !(*this)) { return; }
|
||||
|
||||
free(*this);
|
||||
*this = NULL;
|
||||
}
|
||||
@ -1,3 +1 @@
|
||||
include(../cmake/FetchCMocka.cmake)
|
||||
|
||||
#add_subdirectory()
|
||||
add_subdirectory(library/list)
|
||||
673
tests/bdd-for-c.h
Normal file
673
tests/bdd-for-c.h
Normal file
@ -0,0 +1,673 @@
|
||||
/*!
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Dmitriy Kubyshkin <dmitriy@kubyshkin.name>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef BDD_FOR_C_H
|
||||
#define BDD_FOR_C_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <stdio.h>
|
||||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#define __BDD_IS_ATTY__() _isatty(_fileno(stdout))
|
||||
#else
|
||||
#ifndef _POSIX_C_SOURCE
|
||||
// This definition is required for `fileno` to be defined
|
||||
#define _POSIX_C_SOURCE 200809
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <term.h>
|
||||
#define __BDD_IS_ATTY__() isatty(fileno(stdout))
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#ifndef BDD_USE_COLOR
|
||||
#define BDD_USE_COLOR 1
|
||||
#endif
|
||||
|
||||
#ifndef BDD_USE_TAP
|
||||
#define BDD_USE_TAP 0
|
||||
#endif
|
||||
|
||||
#define __BDD_COLOR_RESET__ "\x1B[0m"
|
||||
#define __BDD_COLOR_RED__ "\x1B[31m"
|
||||
#define __BDD_COLOR_GREEN__ "\x1B[32m"
|
||||
#define __BDD_COLOR_YELLOW__ "\x1B[33m"
|
||||
#define __BDD_COLOR_BOLD__ "\x1B[1m" // Bold White
|
||||
#define __BDD_COLOR_MAGENTA__ "\x1B[35m"
|
||||
|
||||
typedef struct __bdd_array__ {
|
||||
void **values;
|
||||
size_t capacity;
|
||||
size_t size;
|
||||
} __bdd_array__;
|
||||
|
||||
__bdd_array__ *__bdd_array_create__() {
|
||||
__bdd_array__ *arr = malloc(sizeof(__bdd_array__));
|
||||
if (!arr) {
|
||||
perror("malloc(array)");
|
||||
abort();
|
||||
}
|
||||
arr->capacity = 4;
|
||||
arr->size = 0;
|
||||
arr->values = calloc(arr->capacity, sizeof(void *));
|
||||
return arr;
|
||||
}
|
||||
|
||||
void *__bdd_array_push__(__bdd_array__ *arr, void *item) {
|
||||
if (arr->size == arr->capacity) {
|
||||
arr->capacity *= 2;
|
||||
void *v = realloc(arr->values, sizeof(void*) * arr->capacity);
|
||||
if (!v) {
|
||||
perror("realloc(array)");
|
||||
abort();
|
||||
}
|
||||
arr->values = v;
|
||||
}
|
||||
arr->values[arr->size++] = item;
|
||||
return item;
|
||||
}
|
||||
|
||||
void *__bdd_array_last__(__bdd_array__ *arr) {
|
||||
if (arr->size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return arr->values[arr->size - 1];
|
||||
}
|
||||
|
||||
void *__bdd_array_pop__(__bdd_array__ *arr) {
|
||||
if (arr->size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
void *result = arr->values[arr->size - 1];
|
||||
--arr->size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void __bdd_array_free__(__bdd_array__ *arr) {
|
||||
free(arr->values);
|
||||
free(arr);
|
||||
}
|
||||
|
||||
typedef enum __bdd_node_type__ {
|
||||
__BDD_NODE_GROUP__ = 1,
|
||||
__BDD_NODE_TEST__ = 2,
|
||||
__BDD_NODE_INTERIM__ = 3
|
||||
} __bdd_node_type__;
|
||||
|
||||
typedef enum __bdd_node_flags__ {
|
||||
__bdd_node_flags_none__ = 0,
|
||||
__bdd_node_flags_focus__ = 1 << 0,
|
||||
__bdd_node_flags_skip__ = 1 << 1,
|
||||
} __bdd_node_flags__;
|
||||
|
||||
typedef struct __bdd_test_step__ {
|
||||
size_t level;
|
||||
int id;
|
||||
char *name;
|
||||
__bdd_node_type__ type;
|
||||
__bdd_node_flags__ flags;
|
||||
} __bdd_test_step__;
|
||||
|
||||
typedef struct __bdd_node__ {
|
||||
int id;
|
||||
int next_node_id;
|
||||
char *name;
|
||||
__bdd_node_flags__ flags;
|
||||
__bdd_node_type__ type;
|
||||
__bdd_array__ *list_before;
|
||||
__bdd_array__ *list_after;
|
||||
__bdd_array__ *list_before_each;
|
||||
__bdd_array__ *list_after_each;
|
||||
__bdd_array__ *list_children;
|
||||
} __bdd_node__;
|
||||
|
||||
enum __bdd_run_type__ {
|
||||
__BDD_INIT_RUN__ = 1,
|
||||
__BDD_TEST_RUN__ = 2
|
||||
};
|
||||
|
||||
typedef struct __bdd_config_type__ {
|
||||
enum __bdd_run_type__ run;
|
||||
int id;
|
||||
size_t test_index;
|
||||
size_t test_tap_index;
|
||||
size_t failed_test_count;
|
||||
__bdd_test_step__ *current_test;
|
||||
__bdd_array__ *node_stack;
|
||||
__bdd_array__ *nodes;
|
||||
char *error;
|
||||
char *location;
|
||||
bool use_color;
|
||||
bool use_tap;
|
||||
bool has_focus_nodes;
|
||||
} __bdd_config_type__;
|
||||
|
||||
__bdd_test_step__ *__bdd_test_step_create__(size_t level, __bdd_node__ *node) {
|
||||
__bdd_test_step__ *step = malloc(sizeof(__bdd_test_step__));
|
||||
if (!step) {
|
||||
perror("malloc(step)");
|
||||
abort();
|
||||
}
|
||||
step->id = node->id;
|
||||
step->level = level;
|
||||
step->type = node->type;
|
||||
step->name = node->name;
|
||||
step->flags = node->flags;
|
||||
return step;
|
||||
}
|
||||
|
||||
__bdd_node__ *__bdd_node_create__(int id, char *name, __bdd_node_type__ type, __bdd_node_flags__ flags) {
|
||||
__bdd_node__ *n = malloc(sizeof(__bdd_node__));
|
||||
if (!n) {
|
||||
perror("malloc(node)");
|
||||
abort();
|
||||
}
|
||||
n->id = id;
|
||||
n->next_node_id = id + 1;
|
||||
n->name = name; // node takes ownership of name
|
||||
n->type = type;
|
||||
n->flags = flags;
|
||||
n->list_before = __bdd_array_create__();
|
||||
n->list_after = __bdd_array_create__();
|
||||
n->list_before_each = __bdd_array_create__();
|
||||
n->list_after_each = __bdd_array_create__();
|
||||
n->list_children = __bdd_array_create__();
|
||||
return n;
|
||||
}
|
||||
|
||||
bool __bdd_node_is_leaf__(__bdd_node__ *node) {
|
||||
return node->list_children->size == 0;
|
||||
}
|
||||
|
||||
void __bdd_node_flatten_internal__(
|
||||
__bdd_config_type__ *config,
|
||||
size_t level,
|
||||
__bdd_node__ *node,
|
||||
__bdd_array__ *steps,
|
||||
__bdd_array__ *before_each_lists,
|
||||
__bdd_array__ *after_each_lists
|
||||
) {
|
||||
if (__bdd_node_is_leaf__(node)) {
|
||||
if (config->has_focus_nodes && !(node->flags & __bdd_node_flags_focus__)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t listIndex = 0; listIndex < before_each_lists->size; ++listIndex) {
|
||||
__bdd_array__ *list = before_each_lists->values[listIndex];
|
||||
for (size_t i = 0; i < list->size; ++i) {
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level, list->values[i]));
|
||||
}
|
||||
}
|
||||
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level, node));
|
||||
|
||||
for (size_t listIndex = 0; listIndex < after_each_lists->size; ++listIndex) {
|
||||
size_t reverseListIndex = after_each_lists->size - listIndex - 1;
|
||||
__bdd_array__ *list = after_each_lists->values[reverseListIndex];
|
||||
for (size_t i = 0; i < list->size; ++i) {
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level, list->values[i]));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level, node));
|
||||
|
||||
for (size_t i = 0; i < node->list_before->size; ++i) {
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level + 1, node->list_before->values[i]));
|
||||
}
|
||||
|
||||
__bdd_array_push__(before_each_lists, node->list_before_each);
|
||||
__bdd_array_push__(after_each_lists, node->list_after_each);
|
||||
|
||||
for (size_t i = 0; i < node->list_children->size; ++i) {
|
||||
__bdd_node_flatten_internal__(
|
||||
config, level + 1, node->list_children->values[i], steps, before_each_lists, after_each_lists
|
||||
);
|
||||
}
|
||||
|
||||
__bdd_array_pop__(before_each_lists);
|
||||
__bdd_array_pop__(after_each_lists);
|
||||
|
||||
for (size_t i = 0; i < node->list_after->size; ++i) {
|
||||
__bdd_array_push__(steps, __bdd_test_step_create__(level + 1, node->list_after->values[i]));
|
||||
}
|
||||
}
|
||||
|
||||
__bdd_array__ *__bdd_node_flatten__(__bdd_config_type__ *config, __bdd_node__ *node, __bdd_array__ *steps) {
|
||||
if (node == NULL) {
|
||||
return steps;
|
||||
}
|
||||
|
||||
__bdd_array__ *before_each_lists = __bdd_array_create__();
|
||||
__bdd_array__ *after_each_lists = __bdd_array_create__();
|
||||
__bdd_node_flatten_internal__(config, 0, node, steps, before_each_lists, after_each_lists);
|
||||
__bdd_array_free__(before_each_lists);
|
||||
__bdd_array_free__(after_each_lists);
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
void __bdd_node_free__(__bdd_node__ *n) {
|
||||
free(n->name);
|
||||
__bdd_array_free__(n->list_before);
|
||||
__bdd_array_free__(n->list_after);
|
||||
__bdd_array_free__(n->list_before_each);
|
||||
__bdd_array_free__(n->list_after_each);
|
||||
__bdd_array_free__(n->list_children);
|
||||
free(n);
|
||||
}
|
||||
|
||||
char *__bdd_spec_name__;
|
||||
void __bdd_test_main__(__bdd_config_type__ *__bdd_config__);
|
||||
char *__bdd_vformat__(const char *format, va_list va);
|
||||
|
||||
void __bdd_indent__(FILE *fp, size_t level) {
|
||||
for (size_t i = 0; i < level; ++i) {
|
||||
fprintf(fp, " ");
|
||||
}
|
||||
}
|
||||
|
||||
bool __bdd_enter_node__(__bdd_node_flags__ node_flags, __bdd_config_type__ *config, __bdd_node_type__ type, ptrdiff_t list_offset, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
char *name = __bdd_vformat__(fmt, va);
|
||||
va_end(va);
|
||||
|
||||
if (config->run == __BDD_INIT_RUN__) {
|
||||
__bdd_node__ *top = __bdd_array_last__(config->node_stack);
|
||||
__bdd_array__ *list = *(__bdd_array__ **)((unsigned char *)top + list_offset);
|
||||
|
||||
int id = config->id++;
|
||||
__bdd_node__ *node = __bdd_node_create__(id, name, type, node_flags);
|
||||
if (node_flags & __bdd_node_flags_focus__) {
|
||||
// Propagate focus to group nodes up the tree to print inly them
|
||||
top->flags |= node_flags & __bdd_node_flags_focus__;
|
||||
config->has_focus_nodes = true;
|
||||
}
|
||||
__bdd_array_push__(list, node);
|
||||
__bdd_array_push__(config->nodes, node);
|
||||
if (type == __BDD_NODE_GROUP__) {
|
||||
__bdd_array_push__(config->node_stack, node);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config->id >= (int)config->nodes->size) {
|
||||
fprintf(stderr, "non-deterministic spec\n");
|
||||
abort();
|
||||
}
|
||||
__bdd_node__ *node = config->nodes->values[config->id];
|
||||
if (node->type != type || strcmp(node->name, name) != 0) {
|
||||
fprintf(stderr, "non-deterministic spec\n");
|
||||
abort();
|
||||
}
|
||||
free(name);
|
||||
|
||||
__bdd_test_step__ *step = config->current_test;
|
||||
bool should_enter = step->id >= node->id && step->id < node->next_node_id;
|
||||
if (should_enter) {
|
||||
__bdd_array_push__(config->node_stack, node);
|
||||
config->id++;
|
||||
} else {
|
||||
config->id = node->next_node_id;
|
||||
}
|
||||
#if defined(BDD_PRINT_TRACE)
|
||||
const char *color = config->use_color ? __BDD_COLOR_MAGENTA__ : "";
|
||||
fprintf(stderr, "%s% 3d ", color, step->id);
|
||||
__bdd_indent__(stderr, config->node_stack->size - 1 - (int)should_enter);
|
||||
const char *reset = config->use_color ? __BDD_COLOR_RESET__ : "";
|
||||
fprintf(stderr,
|
||||
"%s [%d, %d) %s%s\n",
|
||||
should_enter ? ">" : "|",
|
||||
node->id,
|
||||
node->next_node_id,
|
||||
node->name,
|
||||
reset);
|
||||
#endif
|
||||
return should_enter;
|
||||
}
|
||||
|
||||
void __bdd_exit_node__(__bdd_config_type__ *config) {
|
||||
__bdd_node__ *top = __bdd_array_pop__(config->node_stack);
|
||||
if (config->run == __BDD_INIT_RUN__) {
|
||||
top->next_node_id = config->id;
|
||||
}
|
||||
}
|
||||
|
||||
void __bdd_run__(__bdd_config_type__ *config) {
|
||||
__bdd_test_step__ *step = config->current_test;
|
||||
|
||||
if (step->type == __BDD_NODE_GROUP__ && !config->use_tap) {
|
||||
if (config->has_focus_nodes && !(step->flags & __bdd_node_flags_focus__)) {
|
||||
return;
|
||||
}
|
||||
__bdd_indent__(stdout, step->level);
|
||||
printf(
|
||||
"%s%s%s\n",
|
||||
config->use_color ? __BDD_COLOR_BOLD__ : "",
|
||||
step->name,
|
||||
config->use_color ? __BDD_COLOR_RESET__ : ""
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipped = false;
|
||||
if (step->type == __BDD_NODE_TEST__) {
|
||||
if (config->has_focus_nodes && !(step->flags & __bdd_node_flags_focus__)) {
|
||||
skipped = true;
|
||||
} else if (step->flags & __bdd_node_flags_skip__) {
|
||||
skipped = true;
|
||||
}
|
||||
++config->test_tap_index;
|
||||
// Print the step name before running the test so it is visible
|
||||
// even if the test itself crashes.
|
||||
if ((!skipped || !config->has_focus_nodes) && config->run == __BDD_TEST_RUN__ && !config->use_tap) {
|
||||
__bdd_indent__(stdout, step->level);
|
||||
printf("%s ", step->name);
|
||||
}
|
||||
|
||||
if (!skipped) {
|
||||
__bdd_test_main__(config);
|
||||
}
|
||||
|
||||
if (skipped) {
|
||||
if (config->run == __BDD_TEST_RUN__) {
|
||||
if (!config->has_focus_nodes) {
|
||||
if (config->use_tap) {
|
||||
// We only to report tests and not setup / teardown success
|
||||
if (config->test_tap_index) {
|
||||
printf("skipped %zu - %s\n", config->test_tap_index, step->name);
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
"%s(SKIP)%s\n",
|
||||
config->use_color ? __BDD_COLOR_YELLOW__ : "",
|
||||
config->use_color ? __BDD_COLOR_RESET__ : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (config->error == NULL) {
|
||||
if (config->run == __BDD_TEST_RUN__) {
|
||||
if (config->use_tap) {
|
||||
// We only to report tests and not setup / teardown success
|
||||
if (config->test_tap_index) {
|
||||
printf("ok %zu - %s\n", config->test_tap_index, step->name);
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
"%s(OK)%s\n",
|
||||
config->use_color ? __BDD_COLOR_GREEN__ : "",
|
||||
config->use_color ? __BDD_COLOR_RESET__ : ""
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
++config->failed_test_count;
|
||||
if (config->use_tap) {
|
||||
// We only to report tests and not setup / teardown errors
|
||||
if (config->test_tap_index) {
|
||||
printf("not ok %zu - %s\n", config->test_tap_index, step->name);
|
||||
}
|
||||
} else {
|
||||
printf(
|
||||
"%s(FAIL)%s\n",
|
||||
config->use_color ? __BDD_COLOR_RED__ : "",
|
||||
config->use_color ? __BDD_COLOR_RESET__ : ""
|
||||
);
|
||||
__bdd_indent__(stdout, step->level + 1);
|
||||
printf("%s\n", config->error);
|
||||
__bdd_indent__(stdout, step->level + 2);
|
||||
printf("%s\n", config->location);
|
||||
}
|
||||
free(config->error);
|
||||
config->error = NULL;
|
||||
}
|
||||
} else if (!skipped) {
|
||||
__bdd_test_main__(config);
|
||||
}
|
||||
}
|
||||
|
||||
char *__bdd_vformat__(const char *format, va_list va) {
|
||||
// First we over-allocate
|
||||
const size_t size = 2048;
|
||||
char *result = calloc(size, sizeof(char));
|
||||
if (!result) {
|
||||
perror("calloc(result)");
|
||||
abort();
|
||||
}
|
||||
vsnprintf(result, size - 1, format, va);
|
||||
|
||||
// Then clip to an actual size
|
||||
void* r = realloc(result, strlen(result) + 1);
|
||||
if (!r) {
|
||||
perror("realloc(result)");
|
||||
abort();
|
||||
}
|
||||
result = r;
|
||||
return result;
|
||||
}
|
||||
|
||||
char *__bdd_format__(const char *format, ...) {
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
char *result = __bdd_vformat__(format, va);
|
||||
va_end(va);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool __bdd_is_supported_term__() {
|
||||
bool result;
|
||||
const char *term = getenv("TERM");
|
||||
result = term && strcmp(term, "") != 0;
|
||||
#ifndef _WIN32
|
||||
return result;
|
||||
#else
|
||||
if (result) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Attempt to enable virtual terminal processing on Windows.
|
||||
// See: https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(hOut, &dwMode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dwMode |= 0x4; // ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
if (!SetConsoleMode(hOut, dwMode)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct __bdd_config_type__ config = {
|
||||
.run = __BDD_INIT_RUN__,
|
||||
.id = 0,
|
||||
.test_index = 0,
|
||||
.test_tap_index = 0,
|
||||
.failed_test_count = 0,
|
||||
.node_stack = __bdd_array_create__(),
|
||||
.nodes = __bdd_array_create__(),
|
||||
.error = NULL,
|
||||
.use_color = 0,
|
||||
.use_tap = 0
|
||||
};
|
||||
|
||||
const char *tap_env = getenv("BDD_USE_TAP");
|
||||
if (BDD_USE_TAP || (tap_env && strcmp(tap_env, "") != 0 && strcmp(tap_env, "0") != 0)) {
|
||||
config.use_tap = 1;
|
||||
}
|
||||
|
||||
if (!config.use_tap && BDD_USE_COLOR && __BDD_IS_ATTY__() && __bdd_is_supported_term__()) {
|
||||
config.use_color = 1;
|
||||
}
|
||||
|
||||
__bdd_node__ *root = __bdd_node_create__(-1, __bdd_spec_name__, __BDD_NODE_GROUP__, __bdd_node_flags_none__);
|
||||
__bdd_array_push__(config.node_stack, root);
|
||||
|
||||
// During the first run we just gather the
|
||||
// count of the tests and their descriptions
|
||||
__bdd_test_main__(&config);
|
||||
|
||||
__bdd_array__ *steps = __bdd_array_create__();
|
||||
__bdd_node_flatten__(&config, root, steps);
|
||||
|
||||
size_t test_count = 0;
|
||||
for (size_t i = 0; i < steps->size; ++i) {
|
||||
__bdd_test_step__ *step = steps->values[i];
|
||||
if(step->type == __BDD_NODE_TEST__) {
|
||||
++test_count;
|
||||
}
|
||||
}
|
||||
|
||||
// Outputting the name of the suite
|
||||
if (config.use_tap) {
|
||||
printf("TAP version 13\n1..%zu\n", test_count);
|
||||
}
|
||||
|
||||
config.run = __BDD_TEST_RUN__;
|
||||
|
||||
for (size_t i = 0; i < steps->size; ++i) {
|
||||
__bdd_test_step__ *step = steps->values[i];
|
||||
config.node_stack->size = 1;
|
||||
config.id = 0;
|
||||
config.current_test = step;
|
||||
__bdd_run__(&config);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < config.nodes->size; ++i) {
|
||||
__bdd_node_free__(config.nodes->values[i]);
|
||||
}
|
||||
root->name = NULL; // name is statically allocated
|
||||
__bdd_node_free__(root);
|
||||
for (size_t i = 0; i < steps->size; ++i) {
|
||||
free(steps->values[i]);
|
||||
}
|
||||
__bdd_array_free__(config.nodes);
|
||||
__bdd_array_free__(config.node_stack);
|
||||
__bdd_array_free__(steps);
|
||||
|
||||
if (config.failed_test_count > 0) {
|
||||
if (!config.use_tap) {
|
||||
printf(
|
||||
"\n%zu test%s run, %zu failed.\n",
|
||||
test_count, test_count == 1 ? "" : "s", config.failed_test_count
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define spec(name) \
|
||||
char *__bdd_spec_name__ = (name);\
|
||||
void __bdd_test_main__ (__bdd_config_type__ *__bdd_config__)\
|
||||
|
||||
#define __BDD_NODE__(flags, node_list, type, ...)\
|
||||
for(\
|
||||
bool __bdd_has_run__ = 0;\
|
||||
(\
|
||||
!__bdd_has_run__ && \
|
||||
__bdd_enter_node__(flags, __bdd_config__, (type), offsetof(struct __bdd_node__, node_list), __VA_ARGS__) \
|
||||
);\
|
||||
__bdd_exit_node__(__bdd_config__), \
|
||||
__bdd_has_run__ = 1 \
|
||||
)
|
||||
|
||||
#define describe(...) __BDD_NODE__(__bdd_node_flags_none__, list_children, __BDD_NODE_GROUP__, __VA_ARGS__)
|
||||
#define it(...) __BDD_NODE__(__bdd_node_flags_none__, list_children, __BDD_NODE_TEST__, __VA_ARGS__)
|
||||
#define it_only(...) __BDD_NODE__(__bdd_node_flags_focus__, list_children, __BDD_NODE_TEST__, __VA_ARGS__)
|
||||
#define fit(...) it_only(__VA_ARGS__)
|
||||
#define it_skip(...) __BDD_NODE__(__bdd_node_flags_skip__, list_children, __BDD_NODE_TEST__, __VA_ARGS__)
|
||||
#define xit(...) it_skip(__VA_ARGS__)
|
||||
#define before_each() __BDD_NODE__(__bdd_node_flags_none__, list_before_each, __BDD_NODE_INTERIM__, "before_each")
|
||||
#define after_each() __BDD_NODE__(__bdd_node_flags_none__, list_after_each, __BDD_NODE_INTERIM__, "after_each")
|
||||
#define before() __BDD_NODE__(__bdd_node_flags_none__, list_before, __BDD_NODE_INTERIM__, "before")
|
||||
#define after() __BDD_NODE__(__bdd_node_flags_none__, list_after, __BDD_NODE_INTERIM__, "after")
|
||||
|
||||
#ifndef BDD_NO_CONTEXT_KEYWORD
|
||||
#define context(name) describe(name)
|
||||
#endif
|
||||
|
||||
#define __BDD_MACRO__(M, ...) __BDD_OVERLOAD__(M, __BDD_COUNT_ARGS__(__VA_ARGS__)) (__VA_ARGS__)
|
||||
#define __BDD_OVERLOAD__(macro_name, suffix) __BDD_EXPAND_OVERLOAD__(macro_name, suffix)
|
||||
#define __BDD_EXPAND_OVERLOAD__(macro_name, suffix) macro_name##suffix
|
||||
|
||||
#define __BDD_COUNT_ARGS__(...) __BDD_PATTERN_MATCH__(__VA_ARGS__,_,_,_,_,_,_,_,_,_,ONE__)
|
||||
#define __BDD_PATTERN_MATCH__(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N, ...) N
|
||||
|
||||
#define __BDD_STRING_HELPER__(x) #x
|
||||
#define __BDD_STRING__(x) __BDD_STRING_HELPER__(x)
|
||||
#define __STRING__LINE__ __BDD_STRING__(__LINE__)
|
||||
|
||||
#define __BDD_FMT_COLOR__ __BDD_COLOR_RED__ "Check failed:" __BDD_COLOR_RESET__ " %s"
|
||||
#define __BDD_FMT_PLAIN__ "Check failed: %s"
|
||||
|
||||
#define __BDD_CHECK__(condition, ...) if (!(condition))\
|
||||
{\
|
||||
char *message = __bdd_format__(__VA_ARGS__);\
|
||||
const char *fmt = __bdd_config__->use_color ? __BDD_FMT_COLOR__ : __BDD_FMT_PLAIN__;\
|
||||
__bdd_config__->location = "at " __FILE__ ":" __STRING__LINE__;\
|
||||
size_t bufflen = strlen(fmt) + strlen(message) + 1;\
|
||||
__bdd_config__->error = calloc(bufflen, sizeof(char));\
|
||||
if (__bdd_config__->use_color) {\
|
||||
snprintf(__bdd_config__->error, bufflen, __BDD_FMT_COLOR__, message);\
|
||||
} else {\
|
||||
snprintf(__bdd_config__->error, bufflen, __BDD_FMT_PLAIN__, message);\
|
||||
}\
|
||||
free(message);\
|
||||
return;\
|
||||
}
|
||||
|
||||
#define __BDD_CHECK_ONE__(condition) __BDD_CHECK__(condition, #condition)
|
||||
|
||||
#define check(...) __BDD_MACRO__(__BDD_CHECK_, __VA_ARGS__)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif //BDD_FOR_C_H
|
||||
11
tests/library/list/CMakeLists.txt
Normal file
11
tests/library/list/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_executable(waitui-test_list)
|
||||
|
||||
target_sources(waitui-test_list
|
||||
PRIVATE
|
||||
"test_list.c"
|
||||
)
|
||||
|
||||
target_link_libraries(waitui-test_list PRIVATE list)
|
||||
|
||||
add_test(waitui-test_list waitui-test_list)
|
||||
set_tests_properties(waitui-test_list PROPERTIES ENVIRONMENT "BDD_USE_TAP=1")
|
||||
84
tests/library/list/test_list.c
Normal file
84
tests/library/list/test_list.c
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @file test_list.c
|
||||
* @author rick
|
||||
* @date 09.07.21
|
||||
* @brief Test for the List implementation
|
||||
*/
|
||||
|
||||
#include "waitui/list.h"
|
||||
|
||||
#include "../../bdd-for-c.h"
|
||||
|
||||
typedef struct value {
|
||||
int count;
|
||||
int i;
|
||||
} value;
|
||||
|
||||
static value *value_new(int i) {
|
||||
value *this = calloc(1, sizeof(*this));
|
||||
if (!this) { return NULL; }
|
||||
this->i = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
static void value_destroy(value **this) {
|
||||
if (!this || !(*this)) { return; }
|
||||
free(*this);
|
||||
*this = NULL;
|
||||
}
|
||||
|
||||
CREATE_LIST_TYPE(INTERFACE, value)
|
||||
CREATE_LIST_TYPE(IMPLEMENTATION, value)
|
||||
|
||||
spec("list") {
|
||||
context("use as a stack") {
|
||||
static value_list *stack = NULL;
|
||||
|
||||
before() { stack = value_list_new(); }
|
||||
|
||||
after() { value_list_destroy(&stack); }
|
||||
|
||||
it("should have a stack") {
|
||||
check(stack != NULL, "stack should not be NULL");
|
||||
}
|
||||
|
||||
it("should allow to push a value onto the stack") {
|
||||
value *value1 = value_new(1);
|
||||
check(value1 != NULL, "value1 should not be NULL");
|
||||
check(value_list_push(stack, value1) == true,
|
||||
"value1 should be added to the stack");
|
||||
}
|
||||
|
||||
it("should allow to push a second value onto the stack") {
|
||||
value *value2 = value_new(2);
|
||||
check(value2 != NULL, "value2 should not be NULL");
|
||||
check(value_list_push(stack, value2) == true,
|
||||
"value1 should be added to the stack");
|
||||
}
|
||||
|
||||
it("should return the last added element on pop") {
|
||||
value *test = value_list_pop(stack);
|
||||
check(test != NULL, "value_list_pop should not return NULL");
|
||||
check(test->i == 2, "test should be 2");
|
||||
value_destroy(&test);
|
||||
}
|
||||
|
||||
it("should be able to peek the top element") {
|
||||
value *test = value_list_peek(stack);
|
||||
check(test != NULL, "value_list_peek should not return NULL");
|
||||
check(test->i == 1, "test should be 1");
|
||||
}
|
||||
|
||||
it("should return the first added element on pop") {
|
||||
value *test = value_list_pop(stack);
|
||||
check(test != NULL, "value_list_pop should not return NULL");
|
||||
check(test->i == 1, "test should be 1");
|
||||
value_destroy(&test);
|
||||
}
|
||||
|
||||
it("should return NULL if peek with an empty stack") {
|
||||
value *test = value_list_peek(stack);
|
||||
check(test == NULL, "value_list_peek should return NULL");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user