Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/cxong/tinydir.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCong <congusbongus@gmail.com>2016-07-15 16:56:10 +0300
committerCong <congusbongus@gmail.com>2016-07-15 16:56:10 +0300
commit1122ff910a0a1d9396e48827ec473090781b3750 (patch)
treef65316d1d2c042e421eaf4d533dda09deaf12c53
parentfcfbb0e3d2ee2cc67418ce4ceb6297289256f0b9 (diff)
Add test
-rw-r--r--tests/.gitignore43
-rw-r--r--tests/CMakeLists.txt30
-rw-r--r--tests/cbehave/CMakeLists.txt11
-rw-r--r--tests/cbehave/LICENSE13
-rw-r--r--tests/cbehave/README.md78
-rw-r--r--tests/cbehave/apr_ring.h516
-rw-r--r--tests/cbehave/cbehave.c419
-rw-r--r--tests/cbehave/cbehave.h270
-rw-r--r--tests/cbehave/rlutil/rlutil.h728
-rw-r--r--tests/file_open_test.c23
10 files changed, 2131 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..75e9d66
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,43 @@
+# Object files
+*.o
+
+# Libraries
+*.lib
+*.a
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*_sample
+
+# CMake
+CMakeFiles/
+CMakeCache.txt
+*.dir/
+cmake_install.cmake
+CTestTestfile.cmake
+
+# Visual Studio
+Debug/
+Win32/
+*.opensdf
+*.sdf
+*.suo
+*.vcxproj.user
+*.vcxproj
+*.vcxproj.filters
+*.sln
+
+# Linux
+Makefile
+
+# OS X
+CMakeScripts/
+*.xcodeproj
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..8322d7b
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(tinydir_tests C)
+
+INCLUDE_DIRECTORIES(..)
+include_directories(cbehave)
+add_subdirectory(cbehave)
+
+################################
+# Add definitions
+
+if(MSVC)
+ add_definitions(-W4 -WX -wd"4127" -wd"4102" -wd"4996")
+else()
+ add_definitions(-fsigned-char -Wall -W -Wshadow -Wpointer-arith -Wcast-qual -Winline -Werror -Wno-unused-label -Wno-unused-parameter)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ if("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ else()
+ add_definitions(-Wno-pointer-to-int-cast)
+ endif()
+endif()
+
+################################
+# Add tests
+enable_testing()
+
+add_executable(file_open_test file_open_test.c)
+target_link_libraries(file_open_test cbehave)
+add_test(NAME file_open_test COMMAND file_open_test)
diff --git a/tests/cbehave/CMakeLists.txt b/tests/cbehave/CMakeLists.txt
new file mode 100644
index 0000000..9f5bf9b
--- /dev/null
+++ b/tests/cbehave/CMakeLists.txt
@@ -0,0 +1,11 @@
+include_directories(.)
+
+if(MSVC)
+ add_definitions(-wd"4127" -wd"4102" -wd"4996")
+else()
+ if(NOT BEOS AND NOT HAIKU)
+ add_definitions(-Wno-unused-label)
+ endif()
+endif()
+
+add_library(cbehave STATIC cbehave.h cbehave.c apr_ring.h rlutil/rlutil.h)
diff --git a/tests/cbehave/LICENSE b/tests/cbehave/LICENSE
new file mode 100644
index 0000000..182f1af
--- /dev/null
+++ b/tests/cbehave/LICENSE
@@ -0,0 +1,13 @@
+ Copyright 2012 Tony Bai
+
+ Licensed 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. \ No newline at end of file
diff --git a/tests/cbehave/README.md b/tests/cbehave/README.md
new file mode 100644
index 0000000..62a8453
--- /dev/null
+++ b/tests/cbehave/README.md
@@ -0,0 +1,78 @@
+cbehave - A Behavior Driven Development Framework for C
+=======
+[![Build Status](https://travis-ci.org/cxong/cbehave.svg?branch=master)](https://travis-ci.org/cxong/cbehave)
+
+A demonstration using real C code:
+
+ #include "cbehave.h"
+
+ // Step 1: define your functions
+ int add(int a, int b);
+
+ // Step 2: describe behaviour and the function calls
+ FEATURE(addition, "Addition")
+ SCENARIO("Add two numbers")
+ GIVEN("we have two numbers 50 and 70")
+ int a = 50;
+ int b = 70;
+ WHEN("we add them together")
+ int r = add(a, b);
+ THEN("the result should be 120")
+ SHOULD_INT_EQUAL(r, 120);
+ SCENARIO_END
+ FEATURE_END
+
+ // Step 3: write empty implementations of functions
+ int add(int a, int b)
+ {
+ // Step 5: write code to make the behaviour pass
+ return a + b;
+ }
+
+ // Step 4: run tests and watch them fail (and succeed later)
+ CBEHAVE_RUN("Calculator Features are as below:", TEST_FEATURE(addition))
+
+Introduction
+-------------
+CBehave - A Behavior Driven Development Framework for C.
+
+Main Features
+-------------
+
+ - use the "feature + scenario" structure (inspired by Cucumber)
+ - use classical "given-when-then" template to describe behavior scenarios
+ - support mock
+
+Example Output
+-------------
+
+ *******************************************************************
+ CBEHAVE -- A Behavior Driven Development Framework for C
+ By Tony Bai
+ *******************************************************************
+ Strstr Features are as belows:
+ Feature: strstr
+ Scenario: The strstr finds the first occurrence of the substring in the source string
+ Given A source string: Lionel Messi is a great football player
+ When we use strstr to find the first occurrence of [football]
+ Then We should get the string: [football player]
+ Scenario: If strstr could not find the first occurrence of the substring, it will return NULL
+ Given A source string: FC Barcelona is a great football club.
+ When we use strstr to find the first occurrence of [AC Milan]
+ Then We should get no string but a NULL
+ Summary:
+ features: [1/1]
+ scenarios: [2/2]
+
+Build
+------
+
+To run the examples:
+
+ - Clone the project
+ - cmake cbehave/examples
+
+To use cbehave in your CMake project:
+
+- include the cbehave directory
+- link against `cbehave`
diff --git a/tests/cbehave/apr_ring.h b/tests/cbehave/apr_ring.h
new file mode 100644
index 0000000..670690f
--- /dev/null
+++ b/tests/cbehave/apr_ring.h
@@ -0,0 +1,516 @@
+/* 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.
+ */
+
+/*
+ * This code draws heavily from the 4.4BSD <sys/queue.h> macros
+ * and Dean Gaudet's "splim/ring.h".
+ * <http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/sys/queue.h>
+ * <http://www.arctic.org/~dean/splim/>
+ *
+ * We'd use Dean's code directly if we could guarantee the
+ * availability of inline functions.
+ */
+
+#ifndef APR_RING_H
+#define APR_RING_H
+
+/**
+ * @file apr_ring.h
+ * @brief APR Rings
+ */
+
+/*
+ * for offsetof()
+ *
+ * !here the apr_general.h has been removed, Tony Bai
+ */
+#include <stddef.h>
+#define APR_OFFSETOF(s_type,field) offsetof(s_type,field)
+
+/**
+ * @defgroup apr_ring Ring Macro Implementations
+ * @ingroup APR
+ * A ring is a kind of doubly-linked list that can be manipulated
+ * without knowing where its head is.
+ * @{
+ */
+
+/**
+ * The Ring Element
+ *
+ * A ring element struct is linked to the other elements in the ring
+ * through its ring entry field, e.g.
+ * <pre>
+ * struct my_element_t {
+ * APR_RING_ENTRY(my_element_t) link;
+ * int foo;
+ * char *bar;
+ * };
+ * </pre>
+ *
+ * An element struct may be put on more than one ring if it has more
+ * than one APR_RING_ENTRY field. Each APR_RING_ENTRY has a corresponding
+ * APR_RING_HEAD declaration.
+ *
+ * @warning For strict C standards compliance you should put the APR_RING_ENTRY
+ * first in the element struct unless the head is always part of a larger
+ * object with enough earlier fields to accommodate the offsetof() used
+ * to compute the ring sentinel below. You can usually ignore this caveat.
+ */
+#define APR_RING_ENTRY(elem) \
+ struct { \
+ struct elem * volatile next; \
+ struct elem * volatile prev; \
+ }
+
+/**
+ * The Ring Head
+ *
+ * Each ring is managed via its head, which is a struct declared like this:
+ * <pre>
+ * APR_RING_HEAD(my_ring_t, my_element_t);
+ * struct my_ring_t ring, *ringp;
+ * </pre>
+ *
+ * This struct looks just like the element link struct so that we can
+ * be sure that the typecasting games will work as expected.
+ *
+ * The first element in the ring is next after the head, and the last
+ * element is just before the head.
+ */
+#define APR_RING_HEAD(head, elem) \
+ struct head { \
+ struct elem *next; \
+ struct elem *prev; \
+ }
+
+/**
+ * The Ring Sentinel
+ *
+ * This is the magic pointer value that occurs before the first and
+ * after the last elements in the ring, computed from the address of
+ * the ring's head. The head itself isn't an element, but in order to
+ * get rid of all the special cases when dealing with the ends of the
+ * ring, we play typecasting games to make it look like one.
+ *
+ * Here is a diagram to illustrate the arrangements of the next and
+ * prev pointers of each element in a single ring. Note that they point
+ * to the start of each element, not to the APR_RING_ENTRY structure.
+ *
+ * <pre>
+ * +->+------+<-+ +->+------+<-+ +->+------+<-+
+ * | |struct| | | |struct| | | |struct| |
+ * / | elem | \/ | elem | \/ | elem | \
+ * ... | | /\ | | /\ | | ...
+ * +------+ | | +------+ | | +------+
+ * ...--|prev | | +--|ring | | +--|prev |
+ * | next|--+ | entry|--+ | next|--...
+ * +------+ +------+ +------+
+ * | etc. | | etc. | | etc. |
+ * : : : : : :
+ * </pre>
+ *
+ * The APR_RING_HEAD is nothing but a bare APR_RING_ENTRY. The prev
+ * and next pointers in the first and last elements don't actually
+ * point to the head, they point to a phantom place called the
+ * sentinel. Its value is such that last->next->next == first because
+ * the offset from the sentinel to the head's next pointer is the same
+ * as the offset from the start of an element to its next pointer.
+ * This also works in the opposite direction.
+ *
+ * <pre>
+ * last first
+ * +->+------+<-+ +->sentinel<-+ +->+------+<-+
+ * | |struct| | | | | |struct| |
+ * / | elem | \/ \/ | elem | \
+ * ... | | /\ /\ | | ...
+ * +------+ | | +------+ | | +------+
+ * ...--|prev | | +--|ring | | +--|prev |
+ * | next|--+ | head|--+ | next|--...
+ * +------+ +------+ +------+
+ * | etc. | | etc. |
+ * : : : :
+ * </pre>
+ *
+ * Note that the offset mentioned above is different for each kind of
+ * ring that the element may be on, and each kind of ring has a unique
+ * name for its APR_RING_ENTRY in each element, and has its own type
+ * for its APR_RING_HEAD.
+ *
+ * Note also that if the offset is non-zero (which is required if an
+ * element has more than one APR_RING_ENTRY), the unreality of the
+ * sentinel may have bad implications on very perverse implementations
+ * of C -- see the warning in APR_RING_ENTRY.
+ *
+ * @param hp The head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_SENTINEL(hp, elem, link) \
+ (struct elem *)((char *)(&(hp)->next) - APR_OFFSETOF(struct elem, link))
+
+/**
+ * The first element of the ring
+ * @param hp The head of the ring
+ */
+#define APR_RING_FIRST(hp) (hp)->next
+/**
+ * The last element of the ring
+ * @param hp The head of the ring
+ */
+#define APR_RING_LAST(hp) (hp)->prev
+/**
+ * The next element in the ring
+ * @param ep The current element
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_NEXT(ep, link) (ep)->link.next
+/**
+ * The previous element in the ring
+ * @param ep The current element
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_PREV(ep, link) (ep)->link.prev
+
+
+/**
+ * Initialize a ring
+ * @param hp The head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_INIT(hp, elem, link) do { \
+ APR_RING_FIRST((hp)) = APR_RING_SENTINEL((hp), elem, link); \
+ APR_RING_LAST((hp)) = APR_RING_SENTINEL((hp), elem, link); \
+ } while (0)
+
+/**
+ * Determine if a ring is empty
+ * @param hp The head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ * @return true or false
+ */
+#define APR_RING_EMPTY(hp, elem, link) \
+ (APR_RING_FIRST((hp)) == APR_RING_SENTINEL((hp), elem, link))
+
+/**
+ * Initialize a singleton element
+ * @param ep The element
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_ELEM_INIT(ep, link) do { \
+ APR_RING_NEXT((ep), link) = (ep); \
+ APR_RING_PREV((ep), link) = (ep); \
+ } while (0)
+
+
+/**
+ * Splice the sequence ep1..epN into the ring before element lep
+ * (..lep.. becomes ..ep1..epN..lep..)
+ * @warning This doesn't work for splicing before the first element or on
+ * empty rings... see APR_RING_SPLICE_HEAD for one that does
+ * @param lep Element in the ring to splice before
+ * @param ep1 First element in the sequence to splice in
+ * @param epN Last element in the sequence to splice in
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_SPLICE_BEFORE(lep, ep1, epN, link) do { \
+ APR_RING_NEXT((epN), link) = (lep); \
+ APR_RING_PREV((ep1), link) = APR_RING_PREV((lep), link); \
+ APR_RING_NEXT(APR_RING_PREV((lep), link), link) = (ep1); \
+ APR_RING_PREV((lep), link) = (epN); \
+ } while (0)
+
+/**
+ * Splice the sequence ep1..epN into the ring after element lep
+ * (..lep.. becomes ..lep..ep1..epN..)
+ * @warning This doesn't work for splicing after the last element or on
+ * empty rings... see APR_RING_SPLICE_TAIL for one that does
+ * @param lep Element in the ring to splice after
+ * @param ep1 First element in the sequence to splice in
+ * @param epN Last element in the sequence to splice in
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_SPLICE_AFTER(lep, ep1, epN, link) do { \
+ APR_RING_PREV((ep1), link) = (lep); \
+ APR_RING_NEXT((epN), link) = APR_RING_NEXT((lep), link); \
+ APR_RING_PREV(APR_RING_NEXT((lep), link), link) = (epN); \
+ APR_RING_NEXT((lep), link) = (ep1); \
+ } while (0)
+
+/**
+ * Insert the element nep into the ring before element lep
+ * (..lep.. becomes ..nep..lep..)
+ * @warning This doesn't work for inserting before the first element or on
+ * empty rings... see APR_RING_INSERT_HEAD for one that does
+ * @param lep Element in the ring to insert before
+ * @param nep Element to insert
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_INSERT_BEFORE(lep, nep, link) \
+ APR_RING_SPLICE_BEFORE((lep), (nep), (nep), link)
+
+/**
+ * Insert the element nep into the ring after element lep
+ * (..lep.. becomes ..lep..nep..)
+ * @warning This doesn't work for inserting after the last element or on
+ * empty rings... see APR_RING_INSERT_TAIL for one that does
+ * @param lep Element in the ring to insert after
+ * @param nep Element to insert
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_INSERT_AFTER(lep, nep, link) \
+ APR_RING_SPLICE_AFTER((lep), (nep), (nep), link)
+
+
+/**
+ * Splice the sequence ep1..epN into the ring before the first element
+ * (..hp.. becomes ..hp..ep1..epN..)
+ * @param hp Head of the ring
+ * @param ep1 First element in the sequence to splice in
+ * @param epN Last element in the sequence to splice in
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_SPLICE_HEAD(hp, ep1, epN, elem, link) \
+ APR_RING_SPLICE_AFTER(APR_RING_SENTINEL((hp), elem, link), \
+ (ep1), (epN), link)
+
+/**
+ * Splice the sequence ep1..epN into the ring after the last element
+ * (..hp.. becomes ..ep1..epN..hp..)
+ * @param hp Head of the ring
+ * @param ep1 First element in the sequence to splice in
+ * @param epN Last element in the sequence to splice in
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_SPLICE_TAIL(hp, ep1, epN, elem, link) \
+ APR_RING_SPLICE_BEFORE(APR_RING_SENTINEL((hp), elem, link), \
+ (ep1), (epN), link)
+
+/**
+ * Insert the element nep into the ring before the first element
+ * (..hp.. becomes ..hp..nep..)
+ * @param hp Head of the ring
+ * @param nep Element to insert
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_INSERT_HEAD(hp, nep, elem, link) \
+ APR_RING_SPLICE_HEAD((hp), (nep), (nep), elem, link)
+
+/**
+ * Insert the element nep into the ring after the last element
+ * (..hp.. becomes ..nep..hp..)
+ * @param hp Head of the ring
+ * @param nep Element to insert
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_INSERT_TAIL(hp, nep, elem, link) \
+ APR_RING_SPLICE_TAIL((hp), (nep), (nep), elem, link)
+
+/**
+ * Concatenate ring h2 onto the end of ring h1, leaving h2 empty.
+ * @param h1 Head of the ring to concatenate onto
+ * @param h2 Head of the ring to concatenate
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_CONCAT(h1, h2, elem, link) do { \
+ if (!APR_RING_EMPTY((h2), elem, link)) { \
+ APR_RING_SPLICE_BEFORE(APR_RING_SENTINEL((h1), elem, link), \
+ APR_RING_FIRST((h2)), \
+ APR_RING_LAST((h2)), link); \
+ APR_RING_INIT((h2), elem, link); \
+ } \
+ } while (0)
+
+/**
+ * Prepend ring h2 onto the beginning of ring h1, leaving h2 empty.
+ * @param h1 Head of the ring to prepend onto
+ * @param h2 Head of the ring to prepend
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_PREPEND(h1, h2, elem, link) do { \
+ if (!APR_RING_EMPTY((h2), elem, link)) { \
+ APR_RING_SPLICE_AFTER(APR_RING_SENTINEL((h1), elem, link), \
+ APR_RING_FIRST((h2)), \
+ APR_RING_LAST((h2)), link); \
+ APR_RING_INIT((h2), elem, link); \
+ } \
+ } while (0)
+
+/**
+ * Unsplice a sequence of elements from a ring
+ * @warning The unspliced sequence is left with dangling pointers at either end
+ * @param ep1 First element in the sequence to unsplice
+ * @param epN Last element in the sequence to unsplice
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_UNSPLICE(ep1, epN, link) do { \
+ APR_RING_NEXT(APR_RING_PREV((ep1), link), link) = \
+ APR_RING_NEXT((epN), link); \
+ APR_RING_PREV(APR_RING_NEXT((epN), link), link) = \
+ APR_RING_PREV((ep1), link); \
+ } while (0)
+
+/**
+ * Remove a single element from a ring
+ * @warning The unspliced element is left with dangling pointers at either end
+ * @param ep Element to remove
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_REMOVE(ep, link) \
+ APR_RING_UNSPLICE((ep), (ep), link)
+
+/**
+ * Iterate over a ring
+ * @param ep The current element
+ * @param head The head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_FOREACH(ep, head, elem, link) \
+ for (ep = APR_RING_FIRST(head); \
+ ep != APR_RING_SENTINEL(head, elem, link); \
+ ep = APR_RING_NEXT(ep, link))
+
+/**
+ * Iterate over a ring safe against removal of the current element
+ * @param ep1 The current element
+ * @param ep2 Iteration cursor
+ * @param head The head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_FOREACH_SAFE(ep1, ep2, head, elem, link) \
+ for (ep1 = APR_RING_FIRST(head), ep2 = APR_RING_NEXT(ep1, link); \
+ ep1 != APR_RING_SENTINEL(head, elem, link); \
+ ep1 = ep2, ep2 = APR_RING_NEXT(ep1, link))
+
+/* Debugging tools: */
+
+#ifdef APR_RING_DEBUG
+#include <stdio.h>
+#include <assert.h>
+
+#define APR_RING_CHECK_ONE(msg, ptr) \
+ fprintf(stderr, "*** %s %p\n", msg, ptr)
+
+#define APR_RING_CHECK(hp, elem, link, msg) \
+ APR_RING_CHECK_ELEM(APR_RING_SENTINEL(hp, elem, link), elem, link, msg)
+
+#define APR_RING_CHECK_ELEM(ep, elem, link, msg) do { \
+ struct elem *start = (ep); \
+ struct elem *here = start; \
+ fprintf(stderr, "*** ring check start -- %s\n", msg); \
+ do { \
+ fprintf(stderr, "\telem %p\n", here); \
+ fprintf(stderr, "\telem->next %p\n", \
+ APR_RING_NEXT(here, link)); \
+ fprintf(stderr, "\telem->prev %p\n", \
+ APR_RING_PREV(here, link)); \
+ fprintf(stderr, "\telem->next->prev %p\n", \
+ APR_RING_PREV(APR_RING_NEXT(here, link), link)); \
+ fprintf(stderr, "\telem->prev->next %p\n", \
+ APR_RING_NEXT(APR_RING_PREV(here, link), link)); \
+ if (APR_RING_PREV(APR_RING_NEXT(here, link), link) != here) { \
+ fprintf(stderr, "\t*** elem->next->prev != elem\n"); \
+ break; \
+ } \
+ if (APR_RING_NEXT(APR_RING_PREV(here, link), link) != here) { \
+ fprintf(stderr, "\t*** elem->prev->next != elem\n"); \
+ break; \
+ } \
+ here = APR_RING_NEXT(here, link); \
+ } while (here != start); \
+ fprintf(stderr, "*** ring check end\n"); \
+ } while (0)
+
+#define APR_RING_CHECK_CONSISTENCY(hp, elem, link) \
+ APR_RING_CHECK_ELEM_CONSISTENCY(APR_RING_SENTINEL(hp, elem, link),\
+ elem, link)
+
+#define APR_RING_CHECK_ELEM_CONSISTENCY(ep, elem, link) do { \
+ struct elem *start = (ep); \
+ struct elem *here = start; \
+ do { \
+ assert(APR_RING_PREV(APR_RING_NEXT(here, link), link) == here); \
+ assert(APR_RING_NEXT(APR_RING_PREV(here, link), link) == here); \
+ here = APR_RING_NEXT(here, link); \
+ } while (here != start); \
+ } while (0)
+
+#else
+/**
+ * Print a single pointer value to STDERR
+ * (This is a no-op unless APR_RING_DEBUG is defined.)
+ * @param msg Descriptive message
+ * @param ptr Pointer value to print
+ */
+#define APR_RING_CHECK_ONE(msg, ptr)
+/**
+ * Dump all ring pointers to STDERR, starting with the head and looping all
+ * the way around the ring back to the head. Aborts if an inconsistency
+ * is found.
+ * (This is a no-op unless APR_RING_DEBUG is defined.)
+ * @param hp Head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ * @param msg Descriptive message
+ */
+#define APR_RING_CHECK(hp, elem, link, msg)
+/**
+ * Loops around a ring and checks all the pointers for consistency. Pops
+ * an assertion if any inconsistency is found. Same idea as APR_RING_CHECK()
+ * except that it's silent if all is well.
+ * (This is a no-op unless APR_RING_DEBUG is defined.)
+ * @param hp Head of the ring
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_CHECK_CONSISTENCY(hp, elem, link)
+/**
+ * Dump all ring pointers to STDERR, starting with the given element and
+ * looping all the way around the ring back to that element. Aborts if
+ * an inconsistency is found.
+ * (This is a no-op unless APR_RING_DEBUG is defined.)
+ * @param ep The element
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ * @param msg Descriptive message
+ */
+#define APR_RING_CHECK_ELEM(ep, elem, link, msg)
+/**
+ * Loops around a ring, starting with the given element, and checks all
+ * the pointers for consistency. Pops an assertion if any inconsistency
+ * is found. Same idea as APR_RING_CHECK_ELEM() except that it's silent
+ * if all is well.
+ * (This is a no-op unless APR_RING_DEBUG is defined.)
+ * @param ep The element
+ * @param elem The name of the element struct
+ * @param link The name of the APR_RING_ENTRY in the element struct
+ */
+#define APR_RING_CHECK_ELEM_CONSISTENCY(ep, elem, link)
+#endif
+
+/** @} */
+
+#endif /* !APR_RING_H */
diff --git a/tests/cbehave/cbehave.c b/tests/cbehave/cbehave.c
new file mode 100644
index 0000000..8db60b3
--- /dev/null
+++ b/tests/cbehave/cbehave.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
+ *
+ * Licensed 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <rlutil/rlutil.h>
+
+#include "cbehave.h"
+
+
+cbehave_scope_e cbehave_scope;
+
+static cbehave_symbol_head_t _symbol_list;
+
+static cbehave_symbol_t* lookup_symbol(const char *symbol_name, int obj_type);
+static void add_value(cbehave_symbol_t *s, void *value, int count);
+static cbehave_value_t* get_value(cbehave_symbol_t *s);
+
+#ifdef __APPLE__
+#define DEFAULT_COLOR BLACK
+#else
+#define DEFAULT_COLOR GREY
+#endif
+
+void should_int_equal(int actual, int expected,
+ void *state, const char *file,
+ int line) {
+ int *_scenario_state = (int*)state;
+ if ((expected) != (actual)) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: expected[%d], but actual[%d].\n",
+ file,
+ line,
+ expected,
+ actual);
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void should_int_gt(int val1, int val2,
+ void *state,
+ const char *file, int line) {
+ int *_scenario_state = (int*)state;
+ if ((val1) <= (val2)) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: [%d] not greater than [%d].\n",
+ file,
+ line,
+ val1,
+ val2);
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void should_int_lt(int val1, int val2,
+ void *state,
+ const char *file, int line) {
+ int *_scenario_state = (int*)state;
+ if ((val1) >= (val2)) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: [%d] not less than [%d].\n",
+ file,
+ line,
+ val1,
+ val2);
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void should_int_ge(int val1, int val2,
+ void *state,
+ const char *file, int line) {
+ int *_scenario_state = (int*)state;
+ if ((val1) < (val2)) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: [%d] not greater than or equal to [%d].\n",
+ file,
+ line,
+ val1,
+ val2);
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void should_int_le(int val1, int val2,
+ void *state,
+ const char *file, int line) {
+ int *_scenario_state = (int*)state;
+ if ((val1) > (val2)) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: [%d] not less than or equal to [%d].\n",
+ file,
+ line,
+ val1,
+ val2);
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void should_str_equal(const char *actual, const char *expected, void *state,
+ const char *file, int line) {
+
+ int *_scenario_state = (int*)state;
+ /*
+ * both pointers are NULL or pointing to the same memory
+ */
+ if (expected == actual) return;
+
+ if (expected && actual) {
+ if (!strcmp(expected, actual)) {
+ return;
+ }
+ }
+
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: expected[%s], but actual[%s].\n",
+ file, line,
+ expected ? expected : "NULL",
+ actual ? actual : "NULL");
+ setColor(DEFAULT_COLOR);
+}
+
+void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
+ const char *file, int line) {
+
+ int *_scenario_state = (int*)state;
+ /*
+ * both pointers are NULL or pointing to the same memory
+ */
+ if (expected == actual) return;
+
+ if (expected && actual) {
+ if (!memcmp(expected, actual, size)) {
+ return;
+ }
+ }
+
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: memory does not equal.\n", file, line);
+ setColor(DEFAULT_COLOR);
+}
+
+void should_be_bool(bool actual, bool expected, void *state, const char *file, int line) {
+ int *_scenario_state = (int*)state;
+ if (actual != expected) {
+ (*_scenario_state) = 1;
+ setColor(RED);
+ printf("\t\t\t%s:%d: Failed: actual[%d] is not a %s value.\n",
+ file,
+ line,
+ actual,
+ expected ? "true" : "false");
+ setColor(DEFAULT_COLOR);
+ }
+}
+
+void cbehave_given_entry(const char *prompt, const char *str, void *state) {
+ (void)(state);
+ printf("\t\t%s %s\n", prompt, str);
+}
+
+void cbehave_when_entry(const char *prompt, const char *str, void *state) {
+ (void)(state);
+ printf("\t\t%s %s\n", prompt, str);
+}
+
+void cbehave_then_entry(const char *prompt, const char *str, void *state) {
+ (void)(state);
+ printf("\t\t%s %s\n", prompt, str);
+}
+
+void cbehave_scenario_entry(const char *str, void *state) {
+ cbehave_state *cur = (cbehave_state*)state;
+ cur->total_scenarios++;
+
+ printf("\tScenario: %s\n", str);
+}
+
+void cbehave_feature_entry(const char *str, void *old_state, void *state) {
+ cbehave_state *cur = (cbehave_state*)state;
+ cbehave_state *old = (cbehave_state*)old_state;
+
+ cur->total_features++;
+ memcpy(old, state, sizeof(*cur));
+
+ printf("\nFeature: %s\n", str);
+}
+
+void cbehave_given_exit(void *state) {
+ (void)(state);
+}
+
+void cbehave_when_exit(void *state) {
+ (void)(state);
+}
+
+void cbehave_then_exit(void *state) {
+ (void)(state);
+}
+
+void cbehave_scenario_exit(void *scenario_state, void *state) {
+ int *_scenario_state = (int*)scenario_state;
+ cbehave_state *cur = (cbehave_state*)state;
+
+ if ((*_scenario_state) == 1) {
+ cur->failed_scenarios++;
+ }
+}
+
+void cbehave_feature_exit(void *old_state, void *state) {
+ cbehave_state *cur = (cbehave_state*)state;
+ cbehave_state *old = (cbehave_state*)old_state;
+
+ if (cur->failed_scenarios > old->failed_scenarios) {
+ cur->failed_features++;
+ }
+}
+
+void cbehave_feature_return(const char *file, int line, int ret, void *state) {
+ cbehave_state *cur = (cbehave_state*)state;
+
+ cur->failed_scenarios++;
+
+ setColor(RED);
+ printf("\t\t\t%s:%d: Exception occurred, error code: %d.\n",
+ file,
+ line,
+ ret);
+ setColor(DEFAULT_COLOR);
+}
+
+
+int _cbehave_runner(const char *description, const cbehave_feature *features, int count) {
+ cbehave_state *state = NULL;
+ int i;
+ int ret;
+
+ printf("%s\n", CBEHAVE_LOGO);
+ printf("%s\n", description);
+
+ state = (cbehave_state*)malloc(sizeof(*state));
+ if (!state) {
+ setColor(RED);
+ printf("\t%s:%d: Failed to alloc memory, error code: %d.\n",
+ __FILE__, __LINE__, errno);
+ setColor(DEFAULT_COLOR);
+ return -1;
+ }
+ memset(state, 0, sizeof(*state));
+
+ APR_RING_INIT(&_symbol_list, cbehave_symbol_t, link);
+
+ for (i = 0; i < count; i++) {
+ features[i].func(state);
+ }
+
+ printf("\nSummary: \n");
+ if (state->failed_features) {
+ setColor(RED);
+ } else {
+ setColor(GREEN);
+ }
+ printf("\tfeatures: [%d/%d]\n",
+ state->total_features - state->failed_features, state->total_features);
+ setColor(DEFAULT_COLOR);
+
+ if (state->failed_scenarios) {
+ setColor(RED);
+ } else {
+ setColor(GREEN);
+ }
+ printf("\tscenarios: [%d/%d]\n", state->total_scenarios - state->failed_scenarios, state->total_scenarios);
+ setColor(DEFAULT_COLOR);
+
+ ret = (state->failed_features == 0) ? 0 : 1;
+
+ if (state) {
+ free(state);
+ }
+ return ret;
+}
+
+void* cbehave_mock_obj(const char *fcname,
+ int lineno,
+ const char *fname,
+ int obj_type) {
+ cbehave_symbol_t *s = NULL;
+ cbehave_value_t *v = NULL;
+ void *p;
+
+ s = lookup_symbol(fcname, obj_type);
+ if (!s) {
+ printf("\t[CBEHAVE]: can't find the symbol: <%s> which is being mocked!, %d line in file %s\n",
+ fcname, lineno, fname);
+ exit(EXIT_FAILURE);
+ }
+
+ if (s->always_return_flag) return s->value;
+
+ v = get_value(s);
+ if (!v) {
+ printf("\t[CBEHAVE]: you have not set the value of mock obj <%s>!, %d line in file %s\n",
+ fcname, lineno, fname);
+ exit(EXIT_FAILURE);
+ }
+
+ p = v->value;
+
+ APR_RING_REMOVE(v, link);
+ free(v);
+
+ return p;
+}
+
+
+void cbehave_mock_obj_return(const char *symbol_name,
+ void *value,
+ const char *fcname,
+ int lineno,
+ const char *fname,
+ int obj_type,
+ int count) {
+
+ cbehave_symbol_t *s = lookup_symbol(symbol_name, obj_type);
+ (void)(fcname);
+ (void)(lineno);
+ (void)(fname);
+ if (!s) {
+ errno = 0;
+ s = (cbehave_symbol_t*)malloc(sizeof(*s));
+ if (!s) {
+ printf("\t[CBEHAVE]: malloc error!, errcode[%d]\n", errno);
+ exit(EXIT_FAILURE);
+ }
+ memset(s, 0, sizeof(*s));
+ strcpy(s->desc, symbol_name);
+ s->obj_type = obj_type;
+ APR_RING_INIT(&(s->value_list), cbehave_value_t, link);
+ APR_RING_INSERT_TAIL(&_symbol_list, s, cbehave_symbol_t, link);
+ }
+
+ add_value(s, value, count);
+}
+
+static cbehave_symbol_t* lookup_symbol(const char *symbol_name, int obj_type) {
+ cbehave_symbol_t *s = NULL;
+
+ APR_RING_FOREACH(s, &_symbol_list, cbehave_symbol_t, link) {
+ if (s != NULL) {
+ if ((s->obj_type == obj_type)
+ && (!strcmp(s->desc, symbol_name))) {
+ return s;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static void add_value(cbehave_symbol_t *s, void *value, int count) {
+ cbehave_value_t *v = NULL;
+ int i;
+
+ /*
+ * make the obj always to return one same value
+ * until another cbehave_mock_obj_return invoking
+ */
+ if (count == -1) {
+ s->always_return_flag = 1;
+ s->value = value;
+ return;
+ }
+
+ s->always_return_flag = 0;
+
+ for (i = 0; i < count; i++) {
+ errno = 0;
+ v = (cbehave_value_t*)malloc(sizeof(*v));
+ if (!v) {
+ printf("\t[CBEHAVE]: malloc error!, errcode[%d]\n", errno);
+ exit(EXIT_FAILURE);
+ }
+ memset(v, 0, sizeof(*v));
+ v->value = value;
+
+ APR_RING_INSERT_TAIL(&(s->value_list), v, cbehave_value_t, link);
+ }
+}
+
+static cbehave_value_t* get_value(cbehave_symbol_t *s) {
+ cbehave_value_t *v = NULL;
+
+ v = APR_RING_FIRST(&(s->value_list));
+ return v;
+}
+
diff --git a/tests/cbehave/cbehave.h b/tests/cbehave/cbehave.h
new file mode 100644
index 0000000..aeaea99
--- /dev/null
+++ b/tests/cbehave/cbehave.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2011 Tony Bai <bigwhite.cn@gmail.com>
+ *
+ * Licensed 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.
+ */
+
+/*
+ * @file cbehave.h
+ *
+ */
+
+#ifndef _CBEHAVE_H
+#define _CBEHAVE_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif
+
+#ifndef _cplusplus
+#include <stdbool.h>
+#endif
+
+#include "apr_ring.h"
+
+#define CBEHAVE_LOGO \
+ "*******************************************************************\n\
+\tCBEHAVE -- A Behavior Driven Development Framework for C\n\
+\t\t\t By Tony Bai\n\
+*******************************************************************"
+
+#define CBEHAVE_MAX_NAME_LEN 128
+
+typedef struct cbehave_state {
+ int total_features;
+ int failed_features;
+ int total_scenarios;
+ int failed_scenarios;
+} cbehave_state;
+
+typedef enum {
+ CBEHAVE_SCOPE_NONE,
+ CBEHAVE_SCOPE_GIVEN,
+ CBEHAVE_SCOPE_WHEN,
+ CBEHAVE_SCOPE_THEN
+} cbehave_scope_e;
+extern cbehave_scope_e cbehave_scope;
+
+#define END_SCOPE \
+ if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
+ cbehave_given_exit(_state); \
+ } else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
+ cbehave_when_exit(_state); \
+ } else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
+ cbehave_then_exit(_state); \
+ }
+#define GIVEN_IMPL(x, _prompt) \
+ END_SCOPE \
+ cbehave_scope = CBEHAVE_SCOPE_GIVEN; \
+ cbehave_given_entry(_prompt, x, _state);
+#define GIVEN(x) GIVEN_IMPL(x, "Given")
+#define GIVEN_END
+
+#define WHEN_IMPL(x, _prompt) \
+ END_SCOPE \
+ cbehave_scope = CBEHAVE_SCOPE_WHEN; \
+ cbehave_when_entry(_prompt, x, _state);
+#define WHEN(x) WHEN_IMPL(x, "When")
+#define WHEN_END
+
+#define THEN_IMPL(x, _prompt) \
+ END_SCOPE \
+ cbehave_scope = CBEHAVE_SCOPE_THEN; \
+ cbehave_then_entry(_prompt, x, _state);
+#define THEN(x) THEN_IMPL(x, "Then")
+#define THEN_END
+
+#define AND(x) \
+ if (cbehave_scope == CBEHAVE_SCOPE_GIVEN) { \
+ GIVEN_IMPL(x, "And") \
+ } else if (cbehave_scope == CBEHAVE_SCOPE_WHEN) { \
+ WHEN_IMPL(x, "And") \
+ } else if (cbehave_scope == CBEHAVE_SCOPE_THEN) { \
+ THEN_IMPL(x, "And") \
+ }
+
+#define SCENARIO(x) { \
+ int _scenario_state = 0; \
+ cbehave_scenario_entry(x, _state); \
+ cbehave_scope = CBEHAVE_SCOPE_NONE;
+
+#define SCENARIO_END \
+ END_SCOPE \
+ cbehave_scenario_exit(&_scenario_state, _state); \
+}
+
+#define FEATURE(idx, x) static void _cbehave_feature_##idx(void *_state) { \
+ cbehave_state _old_state; \
+ cbehave_feature_entry(x, &_old_state, _state);
+
+#define FEATURE_END \
+_feature_over: \
+ cbehave_feature_exit(&_old_state, _state); \
+}
+
+#define ASSERT(cond, ret) \
+if (!(cond)) {\
+ cbehave_feature_return(__FILE__, __LINE__, ret, _state); \
+ goto _feature_over; \
+}\
+
+#define TEST_FEATURE(name) {_cbehave_feature_##name}
+
+#define SHOULD_INT_EQUAL(actual, expected) do { \
+ should_int_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_INT_GT(val1, val2) do { \
+ should_int_gt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_INT_LT(val1, val2) do { \
+ should_int_lt((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_INT_GE(val1, val2) do { \
+ should_int_ge((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_INT_LE(val1, val2) do { \
+ should_int_le((val1), (val2), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_STR_EQUAL(actual, expected) do { \
+ should_str_equal((actual), (expected), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_MEM_EQUAL(actual, expected, size) do { \
+ should_mem_equal((actual), (expected), (size), &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_BE_TRUE(actual) do { \
+ should_be_bool((actual), true, &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define SHOULD_BE_FALSE(actual) do { \
+ should_be_bool((actual), false, &_scenario_state, __FILE__, __LINE__); \
+} while(0)
+
+#define CBEHAVE_RUN(_description, ...)\
+int main(int argc, char *argv[]) {\
+ cbehave_feature _cfeatures[] = {__VA_ARGS__};\
+ return cbehave_runner(_description, _cfeatures);\
+}
+
+#define cbehave_runner(str, features) \
+ _cbehave_runner(str, features, sizeof(features)/sizeof(features[0]))
+
+typedef struct cbehave_feature {
+ void (*func)(void *state);
+} cbehave_feature;
+
+int _cbehave_runner(const char *description, const cbehave_feature *features, int count);
+void should_int_equal(int actual, int expected,
+ void *state,
+ const char *file, int line);
+void should_int_gt(int val1, int val2,
+ void *state,
+ const char *file, int line);
+void should_int_lt(int val1, int val2,
+ void *state,
+ const char *file, int line);
+void should_int_ge(int val1, int val2,
+ void *state,
+ const char *file, int line);
+void should_int_le(int val1, int val2,
+ void *state,
+ const char *file, int line);
+void should_str_equal(const char *actual, const char *expected, void *state,
+ const char *file, int line);
+void should_mem_equal(const void *actual, const void *expected, size_t size, void *state,
+ const char *file, int line);
+void should_be_bool(bool actual, bool expected, void *state, const char *file, int line);
+
+void cbehave_given_entry(const char *prompt, const char *str, void *state);
+void cbehave_when_entry(const char *prompt, const char *str, void *state);
+void cbehave_then_entry(const char *prompt, const char *str, void *state);
+void cbehave_scenario_entry(const char *str, void *state);
+void cbehave_feature_entry(const char *str, void *old_state, void *state);
+
+void cbehave_given_exit(void *state);
+void cbehave_when_exit(void *state);
+void cbehave_then_exit(void *state);
+void cbehave_scenario_exit(void *scenario_state, void *state);
+void cbehave_feature_exit(void *old_state, void *state);
+void cbehave_feature_return(const char *file, int line, int ret, void *state);
+
+/*
+ * mock symbol list
+ *
+ * ------------
+ * | symbol-#0|-> value_list
+ * ------------
+ * | symbol-#1|-> value_list
+ * ------------
+ * | symbol-#2|-> value_list
+ * ------------
+ * | ... ... |-> value_list
+ * ------------
+ * | symbol-#n|-> value_list
+ * ------------
+ */
+
+typedef struct cbehave_value_t {
+ APR_RING_ENTRY(cbehave_value_t) link;
+ void *value;
+} cbehave_value_t;
+typedef APR_RING_HEAD(cbehave_value_head_t, cbehave_value_t) cbehave_value_head_t;
+
+typedef struct cbehave_symbol_t {
+ APR_RING_ENTRY(cbehave_symbol_t) link;
+ char desc[CBEHAVE_MAX_NAME_LEN];
+ int obj_type;
+ int always_return_flag; /* 1: always return the same value; 0(default) */
+ void* value;
+ cbehave_value_head_t value_list;
+} cbehave_symbol_t;
+typedef APR_RING_HEAD(cbehave_symbol_head_t, cbehave_symbol_t) cbehave_symbol_head_t;
+
+void* cbehave_mock_obj(const char *fcname, int lineno, const char *fname, int obj_type);
+void cbehave_mock_obj_return(const char *symbol_name, void *value, const char *fcname,
+ int lineno, const char *fname, int obj_type, int count);
+
+#define MOCK_ARG 0x0
+#define MOCK_RETV 0x1
+
+#define CBEHAVE_MOCK_ARG() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_ARG)
+#define CBEHAVE_MOCK_RETV() cbehave_mock_obj(__FUNCTION__, __LINE__, __FILE__, MOCK_RETV)
+
+#define CBEHAVE_ARG_RETURN(fcname, value) do { \
+ cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, 1); \
+} while(0);
+
+#define CBEHAVE_ARG_RETURN_COUNT(fcname, value, count) do { \
+ cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_ARG, count); \
+} while(0);
+
+#define CBEHAVE_RETV_RETURN(fcname, value) do { \
+ cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, 1); \
+} while(0);
+
+#define CBEHAVE_RETV_RETURN_COUNT(fcname, value, count) do { \
+ cbehave_mock_obj_return(#fcname, (void*)value, __FUNCTION__, __LINE__, __FILE__, MOCK_RETV, count); \
+} while(0);
+
+
+#ifdef _cplusplus
+}
+#endif
+
+#endif /* _CBEHAVE_H */
diff --git a/tests/cbehave/rlutil/rlutil.h b/tests/cbehave/rlutil/rlutil.h
new file mode 100644
index 0000000..1d17983
--- /dev/null
+++ b/tests/cbehave/rlutil/rlutil.h
@@ -0,0 +1,728 @@
+#pragma once
+/**
+ * File: rlutil.h
+ *
+ * About: Description
+ * This file provides some useful utilities for console mode
+ * roguelike game development with C and C++. It is aimed to
+ * be cross-platform (at least Windows and Linux).
+ *
+ * About: Copyright
+ * (C) 2010 Tapio Vierros
+ *
+ * About: Licensing
+ * See <License>
+ */
+
+
+/// Define: RLUTIL_USE_ANSI
+/// Define this to use ANSI escape sequences also on Windows
+/// (defaults to using WinAPI instead).
+#if 0
+#define RLUTIL_USE_ANSI
+#endif
+
+/// Define: RLUTIL_STRING_T
+/// Define/typedef this to your preference to override rlutil's string type.
+///
+/// Defaults to std::string with C++ and char* with C.
+#if 0
+#define RLUTIL_STRING_T char*
+#endif
+
+#ifndef RLUTIL_INLINE
+ #ifdef _MSC_VER
+ #define RLUTIL_INLINE __inline
+ #else
+ #define RLUTIL_INLINE static __inline__
+ #endif
+#endif
+
+#ifdef __cplusplus
+ /// Common C++ headers
+ #include <iostream>
+ #include <string>
+ #include <cstdio> // for getch()
+ /// Namespace forward declarations
+ namespace rlutil {
+ RLUTIL_INLINE void locate(int x, int y);
+ }
+#else
+ #include <stdio.h> // for getch() / printf()
+ #include <string.h> // for strlen()
+ RLUTIL_INLINE void locate(int x, int y); // Forward declare for C to avoid warnings
+#endif // __cplusplus
+
+#ifdef _WIN32
+ #include <windows.h> // for WinAPI and Sleep()
+ #define _NO_OLDNAMES // for MinGW compatibility
+ #include <conio.h> // for getch() and kbhit()
+ #define getch _getch
+ #define kbhit _kbhit
+#else
+ #include <termios.h> // for getch() and kbhit()
+ #include <unistd.h> // for getch(), kbhit() and (u)sleep()
+ #include <sys/ioctl.h> // for getkey()
+ #include <sys/types.h> // for kbhit()
+ #include <sys/time.h> // for kbhit()
+
+/// Function: getch
+/// Get character without waiting for Return to be pressed.
+/// Windows has this in conio.h
+RLUTIL_INLINE int getch(void) {
+ // Here be magic.
+ struct termios oldt, newt;
+ int ch;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ ch = getchar();
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+ return ch;
+}
+
+/// Function: kbhit
+/// Determines if keyboard has been hit.
+/// Windows has this in conio.h
+RLUTIL_INLINE int kbhit(void) {
+ // Here be dragons.
+ static struct termios oldt, newt;
+ int cnt = 0;
+ tcgetattr(STDIN_FILENO, &oldt);
+ newt = oldt;
+ newt.c_lflag &= ~(ICANON | ECHO);
+ newt.c_iflag = 0; // input mode
+ newt.c_oflag = 0; // output mode
+ newt.c_cc[VMIN] = 1; // minimum time to wait
+ newt.c_cc[VTIME] = 1; // minimum characters to wait for
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+ ioctl(0, FIONREAD, &cnt); // Read count
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100;
+ select(STDIN_FILENO+1, NULL, NULL, NULL, &tv); // A small time delay
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+ return cnt; // Return number of characters
+}
+#endif // _WIN32
+
+#ifndef gotoxy
+/// Function: gotoxy
+/// Same as <rlutil.locate>.
+RLUTIL_INLINE void gotoxy(int x, int y) {
+ #ifdef __cplusplus
+ rlutil::
+ #endif
+ locate(x,y);
+}
+#endif // gotoxy
+
+#ifdef __cplusplus
+/// Namespace: rlutil
+/// In C++ all functions except <getch>, <kbhit> and <gotoxy> are arranged
+/// under namespace rlutil. That is because some platforms have them defined
+/// outside of rlutil.
+namespace rlutil {
+#endif
+
+/**
+ * Defs: Internal typedefs and macros
+ * RLUTIL_STRING_T - String type depending on which one of C or C++ is used
+ * RLUTIL_PRINT(str) - Printing macro independent of C/C++
+ */
+
+#ifdef __cplusplus
+ #ifndef RLUTIL_STRING_T
+ typedef std::string RLUTIL_STRING_T;
+ #endif // RLUTIL_STRING_T
+
+ #define RLUTIL_PRINT(st) do { std::cout << st; } while(false)
+#else // __cplusplus
+ #ifndef RLUTIL_STRING_T
+ typedef const char* RLUTIL_STRING_T;
+ #endif // RLUTIL_STRING_T
+
+ #define RLUTIL_PRINT(st) printf("%s", st)
+#endif // __cplusplus
+
+/**
+ * Enums: Color codes
+ *
+ * BLACK - Black
+ * BLUE - Blue
+ * GREEN - Green
+ * CYAN - Cyan
+ * RED - Red
+ * MAGENTA - Magenta / purple
+ * BROWN - Brown / dark yellow
+ * GREY - Grey / dark white
+ * DARKGREY - Dark grey / light black
+ * LIGHTBLUE - Light blue
+ * LIGHTGREEN - Light green
+ * LIGHTCYAN - Light cyan
+ * LIGHTRED - Light red
+ * LIGHTMAGENTA - Light magenta / light purple
+ * YELLOW - Yellow (bright)
+ * WHITE - White (bright)
+ */
+enum {
+ BLACK,
+ BLUE,
+ GREEN,
+ CYAN,
+ RED,
+ MAGENTA,
+ BROWN,
+ GREY,
+ DARKGREY,
+ LIGHTBLUE,
+ LIGHTGREEN,
+ LIGHTCYAN,
+ LIGHTRED,
+ LIGHTMAGENTA,
+ YELLOW,
+ WHITE
+};
+
+/**
+ * Consts: ANSI escape strings
+ *
+ * ANSI_CLS - Clears screen
+ * ANSI_ATTRIBUTE_RESET - Resets all attributes
+ * ANSI_CURSOR_HIDE - Hides the cursor
+ * ANSI_CURSOR_SHOW - Shows the cursor
+ * ANSI_CURSOR_HOME - Moves the cursor home (0,0)
+ * ANSI_BLACK - Black
+ * ANSI_RED - Red
+ * ANSI_GREEN - Green
+ * ANSI_BROWN - Brown / dark yellow
+ * ANSI_BLUE - Blue
+ * ANSI_MAGENTA - Magenta / purple
+ * ANSI_CYAN - Cyan
+ * ANSI_GREY - Grey / dark white
+ * ANSI_DARKGREY - Dark grey / light black
+ * ANSI_LIGHTRED - Light red
+ * ANSI_LIGHTGREEN - Light green
+ * ANSI_YELLOW - Yellow (bright)
+ * ANSI_LIGHTBLUE - Light blue
+ * ANSI_LIGHTMAGENTA - Light magenta / light purple
+ * ANSI_LIGHTCYAN - Light cyan
+ * ANSI_WHITE - White (bright)
+ * ANSI_BACKGROUND_BLACK - Black background
+ * ANSI_BACKGROUND_RED - Red background
+ * ANSI_BACKGROUND_GREEN - Green background
+ * ANSI_BACKGROUND_YELLOW - Yellow background
+ * ANSI_BACKGROUND_BLUE - Blue background
+ * ANSI_BACKGROUND_MAGENTA - Magenta / purple background
+ * ANSI_BACKGROUND_CYAN - Cyan background
+ * ANSI_BACKGROUND_WHITE - White background
+ */
+const RLUTIL_STRING_T ANSI_CLS = "\033[2J\033[3J";
+const RLUTIL_STRING_T ANSI_ATTRIBUTE_RESET = "\033[0m";
+const RLUTIL_STRING_T ANSI_CURSOR_HIDE = "\033[?25l";
+const RLUTIL_STRING_T ANSI_CURSOR_SHOW = "\033[?25h";
+const RLUTIL_STRING_T ANSI_CURSOR_HOME = "\033[H";
+const RLUTIL_STRING_T ANSI_BLACK = "\033[22;30m";
+const RLUTIL_STRING_T ANSI_RED = "\033[22;31m";
+const RLUTIL_STRING_T ANSI_GREEN = "\033[22;32m";
+const RLUTIL_STRING_T ANSI_BROWN = "\033[22;33m";
+const RLUTIL_STRING_T ANSI_BLUE = "\033[22;34m";
+const RLUTIL_STRING_T ANSI_MAGENTA = "\033[22;35m";
+const RLUTIL_STRING_T ANSI_CYAN = "\033[22;36m";
+const RLUTIL_STRING_T ANSI_GREY = "\033[22;37m";
+const RLUTIL_STRING_T ANSI_DARKGREY = "\033[01;30m";
+const RLUTIL_STRING_T ANSI_LIGHTRED = "\033[01;31m";
+const RLUTIL_STRING_T ANSI_LIGHTGREEN = "\033[01;32m";
+const RLUTIL_STRING_T ANSI_YELLOW = "\033[01;33m";
+const RLUTIL_STRING_T ANSI_LIGHTBLUE = "\033[01;34m";
+const RLUTIL_STRING_T ANSI_LIGHTMAGENTA = "\033[01;35m";
+const RLUTIL_STRING_T ANSI_LIGHTCYAN = "\033[01;36m";
+const RLUTIL_STRING_T ANSI_WHITE = "\033[01;37m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_BLACK = "\033[40m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_RED = "\033[41m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_GREEN = "\033[42m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_YELLOW = "\033[43m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_BLUE = "\033[44m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_MAGENTA = "\033[45m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_CYAN = "\033[46m";
+const RLUTIL_STRING_T ANSI_BACKGROUND_WHITE = "\033[47m";
+// Remaining colors not supported as background colors
+
+/**
+ * Enums: Key codes for keyhit()
+ *
+ * KEY_ESCAPE - Escape
+ * KEY_ENTER - Enter
+ * KEY_SPACE - Space
+ * KEY_INSERT - Insert
+ * KEY_HOME - Home
+ * KEY_END - End
+ * KEY_DELETE - Delete
+ * KEY_PGUP - PageUp
+ * KEY_PGDOWN - PageDown
+ * KEY_UP - Up arrow
+ * KEY_DOWN - Down arrow
+ * KEY_LEFT - Left arrow
+ * KEY_RIGHT - Right arrow
+ * KEY_F1 - F1
+ * KEY_F2 - F2
+ * KEY_F3 - F3
+ * KEY_F4 - F4
+ * KEY_F5 - F5
+ * KEY_F6 - F6
+ * KEY_F7 - F7
+ * KEY_F8 - F8
+ * KEY_F9 - F9
+ * KEY_F10 - F10
+ * KEY_F11 - F11
+ * KEY_F12 - F12
+ * KEY_NUMDEL - Numpad del
+ * KEY_NUMPAD0 - Numpad 0
+ * KEY_NUMPAD1 - Numpad 1
+ * KEY_NUMPAD2 - Numpad 2
+ * KEY_NUMPAD3 - Numpad 3
+ * KEY_NUMPAD4 - Numpad 4
+ * KEY_NUMPAD5 - Numpad 5
+ * KEY_NUMPAD6 - Numpad 6
+ * KEY_NUMPAD7 - Numpad 7
+ * KEY_NUMPAD8 - Numpad 8
+ * KEY_NUMPAD9 - Numpad 9
+ */
+enum {
+ KEY_ESCAPE = 0,
+ KEY_ENTER = 1,
+ KEY_SPACE = 32,
+
+ KEY_INSERT = 2,
+ KEY_HOME = 3,
+ KEY_PGUP = 4,
+ KEY_DELETE = 5,
+ KEY_END = 6,
+ KEY_PGDOWN = 7,
+
+ KEY_UP = 14,
+ KEY_DOWN = 15,
+ KEY_LEFT = 16,
+ KEY_RIGHT = 17,
+
+ KEY_F1 = 18,
+ KEY_F2 = 19,
+ KEY_F3 = 20,
+ KEY_F4 = 21,
+ KEY_F5 = 22,
+ KEY_F6 = 23,
+ KEY_F7 = 24,
+ KEY_F8 = 25,
+ KEY_F9 = 26,
+ KEY_F10 = 27,
+ KEY_F11 = 28,
+ KEY_F12 = 29,
+
+ KEY_NUMDEL = 30,
+ KEY_NUMPAD0 = 31,
+ KEY_NUMPAD1 = 127,
+ KEY_NUMPAD2 = 128,
+ KEY_NUMPAD3 = 129,
+ KEY_NUMPAD4 = 130,
+ KEY_NUMPAD5 = 131,
+ KEY_NUMPAD6 = 132,
+ KEY_NUMPAD7 = 133,
+ KEY_NUMPAD8 = 134,
+ KEY_NUMPAD9 = 135
+};
+
+/// Function: getkey
+/// Reads a key press (blocking) and returns a key code.
+///
+/// See <Key codes for keyhit()>
+///
+/// Note:
+/// Only Arrows, Esc, Enter and Space are currently working properly.
+RLUTIL_INLINE int getkey(void) {
+ #ifndef _WIN32
+ int cnt = kbhit(); // for ANSI escapes processing
+ #endif
+ int k = getch();
+ switch(k) {
+ case 0: {
+ int kk;
+ switch (kk = getch()) {
+ case 71: return KEY_NUMPAD7;
+ case 72: return KEY_NUMPAD8;
+ case 73: return KEY_NUMPAD9;
+ case 75: return KEY_NUMPAD4;
+ case 77: return KEY_NUMPAD6;
+ case 79: return KEY_NUMPAD1;
+ case 80: return KEY_NUMPAD4;
+ case 81: return KEY_NUMPAD3;
+ case 82: return KEY_NUMPAD0;
+ case 83: return KEY_NUMDEL;
+ default: return kk-59+KEY_F1; // Function keys
+ }}
+ case 224: {
+ int kk;
+ switch (kk = getch()) {
+ case 71: return KEY_HOME;
+ case 72: return KEY_UP;
+ case 73: return KEY_PGUP;
+ case 75: return KEY_LEFT;
+ case 77: return KEY_RIGHT;
+ case 79: return KEY_END;
+ case 80: return KEY_DOWN;
+ case 81: return KEY_PGDOWN;
+ case 82: return KEY_INSERT;
+ case 83: return KEY_DELETE;
+ default: return kk-123+KEY_F1; // Function keys
+ }}
+ case 13: return KEY_ENTER;
+#ifdef _WIN32
+ case 27: return KEY_ESCAPE;
+#else // _WIN32
+ case 155: // single-character CSI
+ case 27: {
+ // Process ANSI escape sequences
+ if (cnt >= 3 && getch() == '[') {
+ switch (k = getch()) {
+ case 'A': return KEY_UP;
+ case 'B': return KEY_DOWN;
+ case 'C': return KEY_RIGHT;
+ case 'D': return KEY_LEFT;
+ }
+ } else return KEY_ESCAPE;
+ }
+#endif // _WIN32
+ default: return k;
+ }
+}
+
+/// Function: nb_getch
+/// Non-blocking getch(). Returns 0 if no key was pressed.
+RLUTIL_INLINE int nb_getch(void) {
+ if (kbhit()) return getch();
+ else return 0;
+}
+
+/// Function: getANSIColor
+/// Return ANSI color escape sequence for specified number 0-15.
+///
+/// See <Color Codes>
+RLUTIL_INLINE RLUTIL_STRING_T getANSIColor(const int c) {
+ switch (c) {
+ case BLACK : return ANSI_BLACK;
+ case BLUE : return ANSI_BLUE; // non-ANSI
+ case GREEN : return ANSI_GREEN;
+ case CYAN : return ANSI_CYAN; // non-ANSI
+ case RED : return ANSI_RED; // non-ANSI
+ case MAGENTA : return ANSI_MAGENTA;
+ case BROWN : return ANSI_BROWN;
+ case GREY : return ANSI_GREY;
+ case DARKGREY : return ANSI_DARKGREY;
+ case LIGHTBLUE : return ANSI_LIGHTBLUE; // non-ANSI
+ case LIGHTGREEN : return ANSI_LIGHTGREEN;
+ case LIGHTCYAN : return ANSI_LIGHTCYAN; // non-ANSI;
+ case LIGHTRED : return ANSI_LIGHTRED; // non-ANSI;
+ case LIGHTMAGENTA: return ANSI_LIGHTMAGENTA;
+ case YELLOW : return ANSI_YELLOW; // non-ANSI
+ case WHITE : return ANSI_WHITE;
+ default: return "";
+ }
+}
+
+/// Function: getANSIBackgroundColor
+/// Return ANSI background color escape sequence for specified number 0-15.
+///
+/// See <Color Codes>
+RLUTIL_INLINE RLUTIL_STRING_T getANSIBackgroundColor(const int c) {
+ switch (c) {
+ case BLACK : return ANSI_BACKGROUND_BLACK;
+ case BLUE : return ANSI_BACKGROUND_BLUE;
+ case GREEN : return ANSI_BACKGROUND_GREEN;
+ case CYAN : return ANSI_BACKGROUND_CYAN;
+ case RED : return ANSI_BACKGROUND_RED;
+ case MAGENTA: return ANSI_BACKGROUND_MAGENTA;
+ case BROWN : return ANSI_BACKGROUND_YELLOW;
+ case GREY : return ANSI_BACKGROUND_WHITE;
+ default: return "";
+ }
+}
+
+/// Function: setColor
+/// Change color specified by number (Windows / QBasic colors).
+/// Don't change the background color
+///
+/// See <Color Codes>
+RLUTIL_INLINE void setColor(int c) {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+
+ SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFFF0) | (WORD)c); // Foreground colors take up the least significant byte
+#else
+ RLUTIL_PRINT(getANSIColor(c));
+#endif
+}
+
+/// Function: setBackgroundColor
+/// Change background color specified by number (Windows / QBasic colors).
+/// Don't change the foreground color
+///
+/// See <Color Codes>
+RLUTIL_INLINE void setBackgroundColor(int c) {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+
+ SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0xFF0F) | (((WORD)c) << 4)); // Background colors take up the second-least significant byte
+#else
+ RLUTIL_PRINT(getANSIBackgroundColor(c));
+#endif
+}
+
+/// Function: saveDefaultColor
+/// Call once to preserve colors for use in resetColor()
+/// on Windows without ANSI, no-op otherwise
+///
+/// See <Color Codes>
+/// See <resetColor>
+RLUTIL_INLINE int saveDefaultColor() {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ static char initialized = 0; // bool
+ static WORD attributes;
+
+ if (!initialized) {
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
+ attributes = csbi.wAttributes;
+ initialized = 1;
+ }
+ return (int)attributes;
+#else
+ return -1;
+#endif
+}
+
+/// Function: resetColor
+/// Reset color to default
+/// Requires a call to saveDefaultColor() to set the defaults
+///
+/// See <Color Codes>
+/// See <setColor>
+/// See <saveDefaultColor>
+RLUTIL_INLINE void resetColor() {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), (WORD)saveDefaultColor());
+#else
+ RLUTIL_PRINT(ANSI_ATTRIBUTE_RESET);
+#endif
+}
+
+/// Function: cls
+/// Clears screen, resets all attributes and moves cursor home.
+RLUTIL_INLINE void cls(void) {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ // Based on https://msdn.microsoft.com/en-us/library/windows/desktop/ms682022%28v=vs.85%29.aspx
+ const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ const COORD coordScreen = {0, 0};
+ DWORD cCharsWritten;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+ const DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+ FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen, &cCharsWritten);
+
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+ FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
+
+ SetConsoleCursorPosition(hConsole, coordScreen);
+#else
+ RLUTIL_PRINT(ANSI_CLS);
+ RLUTIL_PRINT(ANSI_CURSOR_HOME);
+#endif
+}
+
+/// Function: locate
+/// Sets the cursor position to 1-based x,y.
+RLUTIL_INLINE void locate(int x, int y) {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ COORD coord;
+ // TODO: clamping/assert for x/y <= 0?
+ coord.X = (SHORT)(x - 1);
+ coord.Y = (SHORT)(y - 1); // Windows uses 0-based coordinates
+ SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
+#else // _WIN32 || USE_ANSI
+ #ifdef __cplusplus
+ RLUTIL_PRINT("\033[" << y << ";" << x << "H");
+ #else // __cplusplus
+ char buf[32];
+ sprintf(buf, "\033[%d;%df", y, x);
+ RLUTIL_PRINT(buf);
+ #endif // __cplusplus
+#endif // _WIN32 || USE_ANSI
+}
+
+/// Function: setString
+/// Prints the supplied string without advancing the cursor
+#ifdef __cplusplus
+RLUTIL_INLINE void setString(const RLUTIL_STRING_T & str_) {
+ const char * const str = str_.data();
+ unsigned int len = str_.size();
+#else // __cplusplus
+RLUTIL_INLINE void setString(RLUTIL_STRING_T str) {
+ unsigned int len = strlen(str);
+#endif // __cplusplus
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD numberOfCharsWritten;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+ GetConsoleScreenBufferInfo(hConsoleOutput, &csbi);
+ WriteConsoleOutputCharacter(hConsoleOutput, str, len, csbi.dwCursorPosition, &numberOfCharsWritten);
+#else // _WIN32 || USE_ANSI
+ RLUTIL_PRINT(str);
+ #ifdef __cplusplus
+ RLUTIL_PRINT("\033[" << len << 'D');
+ #else // __cplusplus
+ char buf[3 + 20 + 1]; // 20 = max length of 64-bit unsigned int when printed as dec
+ sprintf(buf, "\033[%uD", len);
+ RLUTIL_PRINT(buf);
+ #endif // __cplusplus
+#endif // _WIN32 || USE_ANSI
+}
+
+/// Function: setChar
+/// Sets the character at the cursor without advancing the cursor
+RLUTIL_INLINE void setChar(char ch) {
+ const char buf[] = {ch, 0};
+ setString(buf);
+}
+
+/// Function: setCursorVisibility
+/// Shows/hides the cursor.
+RLUTIL_INLINE void setCursorVisibility(char visible) {
+#if defined(_WIN32) && !defined(RLUTIL_USE_ANSI)
+ HANDLE hConsoleOutput = GetStdHandle( STD_OUTPUT_HANDLE );
+ CONSOLE_CURSOR_INFO structCursorInfo;
+ GetConsoleCursorInfo( hConsoleOutput, &structCursorInfo ); // Get current cursor size
+ structCursorInfo.bVisible = (visible ? TRUE : FALSE);
+ SetConsoleCursorInfo( hConsoleOutput, &structCursorInfo );
+#else // _WIN32 || USE_ANSI
+ RLUTIL_PRINT((visible ? ANSI_CURSOR_SHOW : ANSI_CURSOR_HIDE));
+#endif // _WIN32 || USE_ANSI
+}
+
+/// Function: hidecursor
+/// Hides the cursor.
+RLUTIL_INLINE void hidecursor(void) {
+ setCursorVisibility(0);
+}
+
+/// Function: showcursor
+/// Shows the cursor.
+RLUTIL_INLINE void showcursor(void) {
+ setCursorVisibility(1);
+}
+
+/// Function: msleep
+/// Waits given number of milliseconds before continuing.
+RLUTIL_INLINE void msleep(unsigned int ms) {
+#ifdef _WIN32
+ Sleep(ms);
+#else
+ // usleep argument must be under 1 000 000
+ if (ms > 1000) sleep(ms/1000000);
+ usleep((ms % 1000000) * 1000);
+#endif
+}
+
+/// Function: trows
+/// Get the number of rows in the terminal window or -1 on error.
+RLUTIL_INLINE int trows(void) {
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
+ return -1;
+ else
+ return csbi.srWindow.Bottom - csbi.srWindow.Top + 1; // Window height
+ // return csbi.dwSize.Y; // Buffer height
+#else
+#ifdef TIOCGSIZE
+ struct ttysize ts;
+ ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
+ return ts.ts_lines;
+#elif defined(TIOCGWINSZ)
+ struct winsize ts;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
+ return ts.ws_row;
+#else // TIOCGSIZE
+ return -1;
+#endif // TIOCGSIZE
+#endif // _WIN32
+}
+
+/// Function: tcols
+/// Get the number of columns in the terminal window or -1 on error.
+RLUTIL_INLINE int tcols(void) {
+#ifdef _WIN32
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi))
+ return -1;
+ else
+ return csbi.srWindow.Right - csbi.srWindow.Left + 1; // Window width
+ // return csbi.dwSize.X; // Buffer width
+#else
+#ifdef TIOCGSIZE
+ struct ttysize ts;
+ ioctl(STDIN_FILENO, TIOCGSIZE, &ts);
+ return ts.ts_cols;
+#elif defined(TIOCGWINSZ)
+ struct winsize ts;
+ ioctl(STDIN_FILENO, TIOCGWINSZ, &ts);
+ return ts.ws_col;
+#else // TIOCGSIZE
+ return -1;
+#endif // TIOCGSIZE
+#endif // _WIN32
+}
+
+/// Function: anykey
+/// Waits until a key is pressed.
+/// In C++, it either takes no arguments
+/// or a template-type-argument-deduced
+/// argument.
+/// In C, it takes a const char* representing
+/// the message to be displayed, or NULL
+/// for no message.
+#ifdef __cplusplus
+RLUTIL_INLINE void anykey() {
+ getch();
+}
+
+template <class T> void anykey(const T& msg) {
+ RLUTIL_PRINT(msg);
+#else
+RLUTIL_INLINE void anykey(RLUTIL_STRING_T msg) {
+ if (msg)
+ RLUTIL_PRINT(msg);
+#endif // __cplusplus
+ getch();
+}
+
+// Classes are here at the end so that documentation is pretty.
+
+#ifdef __cplusplus
+/// Class: CursorHider
+/// RAII OOP wrapper for <rlutil.hidecursor>.
+/// Hides the cursor and shows it again
+/// when the object goes out of scope.
+struct CursorHider {
+ CursorHider() { hidecursor(); }
+ ~CursorHider() { showcursor(); }
+};
+
+} // namespace rlutil
+#endif
diff --git a/tests/file_open_test.c b/tests/file_open_test.c
new file mode 100644
index 0000000..0132ba0
--- /dev/null
+++ b/tests/file_open_test.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <tinydir.h>
+#include "cbehave.h"
+
+FEATURE(file_open, "File open")
+ SCENARIO("Open file in current directory")
+ GIVEN("a file in the current directory")
+ char name[] = "fileXXXXXX";
+ int fd = mkstemp(name);
+ close(fd);
+ WHEN("we open it")
+ tinydir_file file;
+ int r = tinydir_file_open(&file, name);
+ THEN("the result should be successful")
+ SHOULD_INT_EQUAL(r, 0);
+ remove(name);
+ SCENARIO_END
+FEATURE_END
+
+CBEHAVE_RUN("File open:", TEST_FEATURE(file_open))