From 1122ff910a0a1d9396e48827ec473090781b3750 Mon Sep 17 00:00:00 2001 From: Cong Date: Fri, 15 Jul 2016 23:56:10 +1000 Subject: Add test --- tests/.gitignore | 43 +++ tests/CMakeLists.txt | 30 ++ tests/cbehave/CMakeLists.txt | 11 + tests/cbehave/LICENSE | 13 + tests/cbehave/README.md | 78 +++++ tests/cbehave/apr_ring.h | 516 ++++++++++++++++++++++++++++++ tests/cbehave/cbehave.c | 419 ++++++++++++++++++++++++ tests/cbehave/cbehave.h | 270 ++++++++++++++++ tests/cbehave/rlutil/rlutil.h | 728 ++++++++++++++++++++++++++++++++++++++++++ tests/file_open_test.c | 23 ++ 10 files changed, 2131 insertions(+) create mode 100644 tests/.gitignore create mode 100644 tests/CMakeLists.txt create mode 100644 tests/cbehave/CMakeLists.txt create mode 100644 tests/cbehave/LICENSE create mode 100644 tests/cbehave/README.md create mode 100644 tests/cbehave/apr_ring.h create mode 100644 tests/cbehave/cbehave.c create mode 100644 tests/cbehave/cbehave.h create mode 100644 tests/cbehave/rlutil/rlutil.h create mode 100644 tests/file_open_test.c 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 macros + * and Dean Gaudet's "splim/ring.h". + * + * + * + * 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 +#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. + *
+ *      struct my_element_t {
+ *          APR_RING_ENTRY(my_element_t) link;
+ *          int foo;
+ *          char *bar;
+ *      };
+ * 
+ * + * 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: + *
+ *      APR_RING_HEAD(my_ring_t, my_element_t);
+ *      struct my_ring_t ring, *ringp;
+ * 
+ * + * 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. + * + *
+ *     +->+------+<-+  +->+------+<-+  +->+------+<-+
+ *     |  |struct|  |  |  |struct|  |  |  |struct|  |
+ *    /   | elem |   \/   | elem |   \/   | elem |  \
+ * ...    |      |   /\   |      |   /\   |      |   ...
+ *        +------+  |  |  +------+  |  |  +------+
+ *   ...--|prev  |  |  +--|ring  |  |  +--|prev  |
+ *        |  next|--+     | entry|--+     |  next|--...
+ *        +------+        +------+        +------+
+ *        | etc. |        | etc. |        | etc. |
+ *        :      :        :      :        :      :
+ * 
+ * + * 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. + * + *
+ *        last                            first
+ *     +->+------+<-+  +->sentinel<-+  +->+------+<-+
+ *     |  |struct|  |  |            |  |  |struct|  |
+ *    /   | elem |   \/              \/   | elem |  \
+ * ...    |      |   /\              /\   |      |   ...
+ *        +------+  |  |  +------+  |  |  +------+
+ *   ...--|prev  |  |  +--|ring  |  |  +--|prev  |
+ *        |  next|--+     |  head|--+     |  next|--...
+ *        +------+        +------+        +------+
+ *        | etc. |                        | etc. |
+ *        :      :                        :      :
+ * 
+ * + * 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 +#include + +#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 + * + * 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 +#include +#include +#include + +#include + +#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 + * + * 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 +#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 + */ + + +/// 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 + #include + #include // for getch() + /// Namespace forward declarations + namespace rlutil { + RLUTIL_INLINE void locate(int x, int y); + } +#else + #include // for getch() / printf() + #include // for strlen() + RLUTIL_INLINE void locate(int x, int y); // Forward declare for C to avoid warnings +#endif // __cplusplus + +#ifdef _WIN32 + #include // for WinAPI and Sleep() + #define _NO_OLDNAMES // for MinGW compatibility + #include // for getch() and kbhit() + #define getch _getch + #define kbhit _kbhit +#else + #include // for getch() and kbhit() + #include // for getch(), kbhit() and (u)sleep() + #include // for getkey() + #include // for kbhit() + #include // 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_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 , and 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 +/// +/// 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 +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 +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 +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 +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 +/// See +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 +/// See +/// See +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 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 . +/// 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 +#include +#include + +#include +#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)) -- cgit v1.2.3