Now with tests
This commit is contained in:
parent
4893b45e1e
commit
6372eaddca
|
@ -2,8 +2,17 @@ cmake_minimum_required(VERSION 3.16)
|
||||||
project(getch C)
|
project(getch C)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
|
|
||||||
add_library(getch SHARED getch.c getch.h)
|
add_library(getch SHARED getch.c getch.h)
|
||||||
add_executable(example example.c )
|
add_executable(example example.c )
|
||||||
|
add_executable(getchTest tests/test.c tests/getchTests.c)
|
||||||
|
add_test(NAME getchTests
|
||||||
|
COMMAND getchTest
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_link_libraries(getchTest -L./build getch)
|
||||||
target_link_libraries(example -L./build getch)
|
target_link_libraries(example -L./build getch)
|
||||||
|
if (ENABLE_TESTS EQUAL 1)
|
||||||
|
enable_testing()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,10 @@ int main() {
|
||||||
|
|
||||||
int key = _getch();
|
int key = _getch();
|
||||||
|
|
||||||
printf("Key %d\n", key);
|
printf("Key %d\n", key);
|
||||||
|
|
||||||
if (key == 0 || key == 224) {
|
if (key == 0 || key == 224) {
|
||||||
key = _getch();
|
key = _getch();
|
||||||
printf("Special Key : %d\n", key);
|
printf("Special Key : %d\n", key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
71
getch.c
71
getch.c
|
@ -21,27 +21,24 @@
|
||||||
|
|
||||||
static struct termios oldTermAttributes;
|
static struct termios oldTermAttributes;
|
||||||
|
|
||||||
static void setRawMode(void);
|
static int discardRead(unsigned int length)
|
||||||
static void setNormalMode(void);
|
|
||||||
|
|
||||||
inline static int discardRead(unsigned int length)
|
|
||||||
{
|
{
|
||||||
char buffer[length];
|
char buffer[length];
|
||||||
ssize_t bytes_read;
|
ssize_t bytesRead;
|
||||||
|
|
||||||
int flags = fcntl(STDIN_FILENO, F_GETFL);
|
int flags = fcntl(STDIN_FILENO, F_GETFL);
|
||||||
fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
|
fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
|
||||||
|
|
||||||
if( (bytes_read = fread(buffer, sizeof(char), length, stdin)) == -1) {
|
if( (bytesRead = fread(buffer, sizeof(char), length, stdin)) == -1) {
|
||||||
perror("discardRead");
|
perror("discardRead");
|
||||||
}
|
}
|
||||||
|
|
||||||
fcntl(STDIN_FILENO, F_SETFL, flags);
|
fcntl(STDIN_FILENO, F_SETFL, flags);
|
||||||
|
|
||||||
return (int)bytes_read;
|
return (int)bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getEventDevice(const char * device)
|
static int getEventDevice(const char * device)
|
||||||
{
|
{
|
||||||
glob_t search;
|
glob_t search;
|
||||||
|
|
||||||
|
@ -77,28 +74,33 @@ int getEventDevice(const char * device)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short getScanCode()
|
static unsigned short getScanCode()
|
||||||
{
|
{
|
||||||
struct input_event inputEvent[5];
|
struct input_event inputEvent[3];
|
||||||
int fd;
|
int eventDevice;
|
||||||
|
|
||||||
const char device[FILENAME_MAX];
|
const char device[FILENAME_MAX];
|
||||||
if(-1 == getEventDevice(device)) {
|
if(getEventDevice(device) == -1) {
|
||||||
|
perror("getEventDevice");
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( (fd = open(device, O_RDONLY)) == -1 ) {
|
if( ( eventDevice = open(device, O_RDONLY)) == -1 ) {
|
||||||
|
perror("open");
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
};
|
};
|
||||||
|
|
||||||
if( read(fd, &inputEvent, sizeof (inputEvent)) == -1 ) {
|
|
||||||
|
|
||||||
|
if( read(eventDevice, &inputEvent, sizeof(inputEvent)) == -1) {
|
||||||
|
close(eventDevice);
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
close(eventDevice);
|
||||||
|
|
||||||
for(int i = 0; i<=5; i++) {
|
for(int i = 0;i<3;i++) {
|
||||||
if(inputEvent[i].type == EV_KEY) {
|
if(inputEvent[i].type == EV_KEY && inputEvent[i].code != KEY_ENTER) {
|
||||||
return inputEvent[i].code;
|
return inputEvent[i].code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,37 +108,16 @@ unsigned short getScanCode()
|
||||||
return KEY_RESERVED;
|
return KEY_RESERVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions"
|
|
||||||
inline static char *reverseString(char *str)
|
|
||||||
{
|
|
||||||
char *p1, *p2;
|
|
||||||
|
|
||||||
if (! str || ! *str)
|
|
||||||
return str;
|
|
||||||
for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
|
|
||||||
{
|
|
||||||
*p1 ^= *p2;
|
|
||||||
*p2 ^= *p1;
|
|
||||||
*p1 ^= *p2;
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma ide diagnostic ignored "bugprone-reserved-identifier"
|
|
||||||
inline static void pushStdin(int c) {
|
inline static void pushStdin(int c) {
|
||||||
ungetc(c, stdin);
|
ungetc(c, stdin);
|
||||||
}
|
}
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
inline static void setNormalMode(void)
|
static void setNormalMode(void)
|
||||||
{
|
{
|
||||||
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermAttributes);
|
tcsetattr(STDIN_FILENO, TCSANOW, &oldTermAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline static void setRawMode(void)
|
static void setRawMode(void)
|
||||||
{
|
{
|
||||||
tcgetattr(STDIN_FILENO, &oldTermAttributes);
|
tcgetattr(STDIN_FILENO, &oldTermAttributes);
|
||||||
|
|
||||||
|
@ -147,7 +128,7 @@ inline static void setRawMode(void)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short resolveScanCode(unsigned short key)
|
static unsigned short resolveScanCode(unsigned short key)
|
||||||
{
|
{
|
||||||
switch(key) {
|
switch(key) {
|
||||||
case KEY_DOWN: return KEY_KP2;
|
case KEY_DOWN: return KEY_KP2;
|
||||||
|
@ -164,16 +145,15 @@ unsigned short resolveScanCode(unsigned short key)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int readKey(void) {
|
static int readKey(void) {
|
||||||
|
|
||||||
unsigned short scanCode;
|
|
||||||
int key = getchar();
|
int key = getchar();
|
||||||
|
|
||||||
if (key == 27) {
|
if (key == 27) {
|
||||||
|
|
||||||
scanCode = getScanCode();
|
unsigned short scanCode = getScanCode();
|
||||||
|
|
||||||
if (scanCode == KEY_ESC) {
|
if (scanCode == KEY_ESC || scanCode == KEY_RESERVED) {
|
||||||
return 27;
|
return 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,6 +206,7 @@ int readKey(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pushStdin(scanCode);
|
pushStdin(scanCode);
|
||||||
|
|
||||||
return returnResult;
|
return returnResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,590 @@
|
||||||
|
/* Copyright 2011-2021 Bas van den Berg
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CTEST_H
|
||||||
|
#define CTEST_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b)))
|
||||||
|
#else
|
||||||
|
#define CTEST_IMPL_FORMAT_PRINTF(a, b)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <inttypes.h> /* intmax_t, uintmax_t, PRI* */
|
||||||
|
#include <stddef.h> /* size_t */
|
||||||
|
|
||||||
|
typedef void (*ctest_nullary_run_func)(void);
|
||||||
|
typedef void (*ctest_unary_run_func)(void*);
|
||||||
|
typedef void (*ctest_setup_func)(void*);
|
||||||
|
typedef void (*ctest_teardown_func)(void*);
|
||||||
|
|
||||||
|
union ctest_run_func_union {
|
||||||
|
ctest_nullary_run_func nullary;
|
||||||
|
ctest_unary_run_func unary;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CTEST_IMPL_PRAGMA(x) _Pragma (#x)
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#if defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
|
||||||
|
/* the GCC argument will work for both gcc and clang */
|
||||||
|
#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \
|
||||||
|
CTEST_IMPL_PRAGMA(GCC diagnostic push) \
|
||||||
|
CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w)
|
||||||
|
#define CTEST_IMPL_DIAG_POP() \
|
||||||
|
CTEST_IMPL_PRAGMA(GCC diagnostic pop)
|
||||||
|
#else
|
||||||
|
/* the push/pop functionality wasn't in gcc until 4.6, fallback to "ignored" */
|
||||||
|
#define CTEST_IMPL_DIAG_PUSH_IGNORED(w) \
|
||||||
|
CTEST_IMPL_PRAGMA(GCC diagnostic ignored "-W" #w)
|
||||||
|
#define CTEST_IMPL_DIAG_POP()
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
/* leave them out entirely for non-GNUC compilers */
|
||||||
|
#define CTEST_IMPL_DIAG_PUSH_IGNORED(w)
|
||||||
|
#define CTEST_IMPL_DIAG_POP()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ctest {
|
||||||
|
const char* ssname; // suite name
|
||||||
|
const char* ttname; // test name
|
||||||
|
union ctest_run_func_union run;
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
ctest_setup_func* setup;
|
||||||
|
ctest_teardown_func* teardown;
|
||||||
|
|
||||||
|
int skip;
|
||||||
|
|
||||||
|
unsigned int magic;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CTEST_IMPL_NAME(name) ctest_##name
|
||||||
|
#define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run)
|
||||||
|
#define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname)
|
||||||
|
#define CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_NAME(sname##_data)
|
||||||
|
#define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data)
|
||||||
|
#define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup)
|
||||||
|
#define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr)
|
||||||
|
#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr)
|
||||||
|
#define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown)
|
||||||
|
#define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr)
|
||||||
|
#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr)
|
||||||
|
|
||||||
|
#define CTEST_IMPL_MAGIC (0xdeadbeef)
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#define CTEST_IMPL_SECTION __attribute__ ((used, section ("__DATA, .ctest"), aligned(1)))
|
||||||
|
#else
|
||||||
|
#define CTEST_IMPL_SECTION __attribute__ ((used, section (".ctest"), aligned(1)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \
|
||||||
|
static struct ctest CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_SECTION = { \
|
||||||
|
#sname, \
|
||||||
|
#tname, \
|
||||||
|
{ (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \
|
||||||
|
tdata, \
|
||||||
|
(ctest_setup_func*) tsetup, \
|
||||||
|
(ctest_teardown_func*) tteardown, \
|
||||||
|
tskip, \
|
||||||
|
CTEST_IMPL_MAGIC, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
#define CTEST_SETUP(sname) \
|
||||||
|
template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#define CTEST_TEARDOWN(sname) \
|
||||||
|
template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#define CTEST_DATA(sname) \
|
||||||
|
template <typename T> void CTEST_IMPL_SETUP_FNAME(sname)(T* data) { } \
|
||||||
|
template <typename T> void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* data) { } \
|
||||||
|
struct CTEST_IMPL_DATA_SNAME(sname)
|
||||||
|
|
||||||
|
#define CTEST_IMPL_CTEST(sname, tname, tskip) \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(void); \
|
||||||
|
CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(void)
|
||||||
|
|
||||||
|
#define CTEST_IMPL_CTEST2(sname, tname, tskip) \
|
||||||
|
static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \
|
||||||
|
static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname)<struct CTEST_IMPL_DATA_SNAME(sname)>; \
|
||||||
|
static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname)<struct CTEST_IMPL_DATA_SNAME(sname)>; \
|
||||||
|
CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define CTEST_SETUP(sname) \
|
||||||
|
static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \
|
||||||
|
static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \
|
||||||
|
static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#define CTEST_TEARDOWN(sname) \
|
||||||
|
static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \
|
||||||
|
static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \
|
||||||
|
static void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#define CTEST_DATA(sname) \
|
||||||
|
struct CTEST_IMPL_DATA_SNAME(sname); \
|
||||||
|
static void (*CTEST_IMPL_SETUP_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \
|
||||||
|
static void (*CTEST_IMPL_TEARDOWN_FPNAME(sname))(struct CTEST_IMPL_DATA_SNAME(sname)*); \
|
||||||
|
struct CTEST_IMPL_DATA_SNAME(sname)
|
||||||
|
|
||||||
|
#define CTEST_IMPL_CTEST(sname, tname, tskip) \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(void); \
|
||||||
|
CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(void)
|
||||||
|
|
||||||
|
#define CTEST_IMPL_CTEST2(sname, tname, tskip) \
|
||||||
|
static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \
|
||||||
|
CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \
|
||||||
|
static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void CTEST_LOG(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2);
|
||||||
|
void CTEST_ERR(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); // doesn't return
|
||||||
|
|
||||||
|
#define CTEST(sname, tname) CTEST_IMPL_CTEST(sname, tname, 0)
|
||||||
|
#define CTEST_SKIP(sname, tname) CTEST_IMPL_CTEST(sname, tname, 1)
|
||||||
|
|
||||||
|
#define CTEST2(sname, tname) CTEST_IMPL_CTEST2(sname, tname, 0)
|
||||||
|
#define CTEST2_SKIP(sname, tname) CTEST_IMPL_CTEST2(sname, tname, 1)
|
||||||
|
|
||||||
|
|
||||||
|
void assert_str(const char* exp, const char* real, const char* caller, int line);
|
||||||
|
#define ASSERT_STR(exp, real) assert_str(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_wstr(const wchar_t *exp, const wchar_t *real, const char* caller, int line);
|
||||||
|
#define ASSERT_WSTR(exp, real) assert_wstr(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_data(const unsigned char* exp, size_t expsize,
|
||||||
|
const unsigned char* real, size_t realsize,
|
||||||
|
const char* caller, int line);
|
||||||
|
#define ASSERT_DATA(exp, expsize, real, realsize) \
|
||||||
|
assert_data(exp, expsize, real, realsize, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_equal(intmax_t exp, intmax_t real, const char* caller, int line);
|
||||||
|
#define ASSERT_EQUAL(exp, real) assert_equal(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line);
|
||||||
|
#define ASSERT_EQUAL_U(exp, real) assert_equal_u(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_not_equal(intmax_t exp, intmax_t real, const char* caller, int line);
|
||||||
|
#define ASSERT_NOT_EQUAL(exp, real) assert_not_equal(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_not_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line);
|
||||||
|
#define ASSERT_NOT_EQUAL_U(exp, real) assert_not_equal_u(exp, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line);
|
||||||
|
#define ASSERT_INTERVAL(exp1, exp2, real) assert_interval(exp1, exp2, real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_null(void* real, const char* caller, int line);
|
||||||
|
#define ASSERT_NULL(real) assert_null((void*)real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_not_null(const void* real, const char* caller, int line);
|
||||||
|
#define ASSERT_NOT_NULL(real) assert_not_null(real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_true(int real, const char* caller, int line);
|
||||||
|
#define ASSERT_TRUE(real) assert_true(real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_false(int real, const char* caller, int line);
|
||||||
|
#define ASSERT_FALSE(real) assert_false(real, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_fail(const char* caller, int line);
|
||||||
|
#define ASSERT_FAIL() assert_fail(__FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_dbl_near(double exp, double real, double tol, const char* caller, int line);
|
||||||
|
#define ASSERT_DBL_NEAR(exp, real) assert_dbl_near(exp, real, 1e-4, __FILE__, __LINE__)
|
||||||
|
#define ASSERT_DBL_NEAR_TOL(exp, real, tol) assert_dbl_near(exp, real, tol, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void assert_dbl_far(double exp, double real, double tol, const char* caller, int line);
|
||||||
|
#define ASSERT_DBL_FAR(exp, real) assert_dbl_far(exp, real, 1e-4, __FILE__, __LINE__)
|
||||||
|
#define ASSERT_DBL_FAR_TOL(exp, real, tol) assert_dbl_far(exp, real, tol, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#ifdef CTEST_MAIN
|
||||||
|
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
static size_t ctest_errorsize;
|
||||||
|
static char* ctest_errormsg;
|
||||||
|
#define MSG_SIZE 4096
|
||||||
|
static char ctest_errorbuffer[MSG_SIZE];
|
||||||
|
static jmp_buf ctest_err;
|
||||||
|
static int color_output = 1;
|
||||||
|
static const char* suite_name;
|
||||||
|
|
||||||
|
typedef int (*ctest_filter_func)(struct ctest*);
|
||||||
|
|
||||||
|
#define ANSI_BLACK "\033[0;30m"
|
||||||
|
#define ANSI_RED "\033[0;31m"
|
||||||
|
#define ANSI_GREEN "\033[0;32m"
|
||||||
|
#define ANSI_YELLOW "\033[0;33m"
|
||||||
|
#define ANSI_BLUE "\033[0;34m"
|
||||||
|
#define ANSI_MAGENTA "\033[0;35m"
|
||||||
|
#define ANSI_CYAN "\033[0;36m"
|
||||||
|
#define ANSI_GREY "\033[0;37m"
|
||||||
|
#define ANSI_DARKGREY "\033[01;30m"
|
||||||
|
#define ANSI_BRED "\033[01;31m"
|
||||||
|
#define ANSI_BGREEN "\033[01;32m"
|
||||||
|
#define ANSI_BYELLOW "\033[01;33m"
|
||||||
|
#define ANSI_BBLUE "\033[01;34m"
|
||||||
|
#define ANSI_BMAGENTA "\033[01;35m"
|
||||||
|
#define ANSI_BCYAN "\033[01;36m"
|
||||||
|
#define ANSI_WHITE "\033[01;37m"
|
||||||
|
#define ANSI_NORMAL "\033[0m"
|
||||||
|
|
||||||
|
CTEST(suite, test) { }
|
||||||
|
|
||||||
|
static void vprint_errormsg(const char* const fmt, va_list ap) CTEST_IMPL_FORMAT_PRINTF(1, 0);
|
||||||
|
static void print_errormsg(const char* const fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2);
|
||||||
|
|
||||||
|
static void vprint_errormsg(const char* const fmt, va_list ap) {
|
||||||
|
// (v)snprintf returns the number that would have been written
|
||||||
|
const int ret = vsnprintf(ctest_errormsg, ctest_errorsize, fmt, ap);
|
||||||
|
if (ret < 0) {
|
||||||
|
ctest_errormsg[0] = 0x00;
|
||||||
|
} else {
|
||||||
|
const size_t size = (size_t) ret;
|
||||||
|
const size_t s = (ctest_errorsize <= size ? size -ctest_errorsize : size);
|
||||||
|
// ctest_errorsize may overflow at this point
|
||||||
|
ctest_errorsize -= s;
|
||||||
|
ctest_errormsg += s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_errormsg(const char* const fmt, ...) {
|
||||||
|
va_list argp;
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vprint_errormsg(fmt, argp);
|
||||||
|
va_end(argp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_start(const char* color, const char* title) {
|
||||||
|
if (color_output) {
|
||||||
|
print_errormsg("%s", color);
|
||||||
|
}
|
||||||
|
print_errormsg(" %s: ", title);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msg_end(void) {
|
||||||
|
if (color_output) {
|
||||||
|
print_errormsg(ANSI_NORMAL);
|
||||||
|
}
|
||||||
|
print_errormsg("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTEST_LOG(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list argp;
|
||||||
|
msg_start(ANSI_BLUE, "LOG");
|
||||||
|
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vprint_errormsg(fmt, argp);
|
||||||
|
va_end(argp);
|
||||||
|
|
||||||
|
msg_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
CTEST_IMPL_DIAG_PUSH_IGNORED(missing-noreturn)
|
||||||
|
|
||||||
|
void CTEST_ERR(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list argp;
|
||||||
|
msg_start(ANSI_YELLOW, "ERR");
|
||||||
|
|
||||||
|
va_start(argp, fmt);
|
||||||
|
vprint_errormsg(fmt, argp);
|
||||||
|
va_end(argp);
|
||||||
|
|
||||||
|
msg_end();
|
||||||
|
longjmp(ctest_err, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTEST_IMPL_DIAG_POP()
|
||||||
|
|
||||||
|
void assert_str(const char* exp, const char* real, const char* caller, int line) {
|
||||||
|
if ((exp == NULL && real != NULL) ||
|
||||||
|
(exp != NULL && real == NULL) ||
|
||||||
|
(exp && real && strcmp(exp, real) != 0)) {
|
||||||
|
CTEST_ERR("%s:%d expected '%s', got '%s'", caller, line, exp, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_wstr(const wchar_t *exp, const wchar_t *real, const char* caller, int line) {
|
||||||
|
if ((exp == NULL && real != NULL) ||
|
||||||
|
(exp != NULL && real == NULL) ||
|
||||||
|
(exp && real && wcscmp(exp, real) != 0)) {
|
||||||
|
CTEST_ERR("%s:%d expected '%ls', got '%ls'", caller, line, exp, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_data(const unsigned char* exp, size_t expsize,
|
||||||
|
const unsigned char* real, size_t realsize,
|
||||||
|
const char* caller, int line) {
|
||||||
|
size_t i;
|
||||||
|
if (expsize != realsize) {
|
||||||
|
CTEST_ERR("%s:%d expected %" PRIuMAX " bytes, got %" PRIuMAX, caller, line, (uintmax_t) expsize, (uintmax_t) realsize);
|
||||||
|
}
|
||||||
|
for (i=0; i<expsize; i++) {
|
||||||
|
if (exp[i] != real[i]) {
|
||||||
|
CTEST_ERR("%s:%d expected 0x%02x at offset %" PRIuMAX " got 0x%02x",
|
||||||
|
caller, line, exp[i], (uintmax_t) i, real[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_equal(intmax_t exp, intmax_t real, const char* caller, int line) {
|
||||||
|
if (exp != real) {
|
||||||
|
CTEST_ERR("%s:%d expected %" PRIdMAX ", got %" PRIdMAX, caller, line, exp, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line) {
|
||||||
|
if (exp != real) {
|
||||||
|
CTEST_ERR("%s:%d expected %" PRIuMAX ", got %" PRIuMAX, caller, line, exp, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_not_equal(intmax_t exp, intmax_t real, const char* caller, int line) {
|
||||||
|
if ((exp) == (real)) {
|
||||||
|
CTEST_ERR("%s:%d should not be %" PRIdMAX, caller, line, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_not_equal_u(uintmax_t exp, uintmax_t real, const char* caller, int line) {
|
||||||
|
if ((exp) == (real)) {
|
||||||
|
CTEST_ERR("%s:%d should not be %" PRIuMAX, caller, line, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_interval(intmax_t exp1, intmax_t exp2, intmax_t real, const char* caller, int line) {
|
||||||
|
if (real < exp1 || real > exp2) {
|
||||||
|
CTEST_ERR("%s:%d expected %" PRIdMAX "-%" PRIdMAX ", got %" PRIdMAX, caller, line, exp1, exp2, real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_dbl_near(double exp, double real, double tol, const char* caller, int line) {
|
||||||
|
double diff = exp - real;
|
||||||
|
double absdiff = diff;
|
||||||
|
/* avoid using fabs and linking with a math lib */
|
||||||
|
if(diff < 0) {
|
||||||
|
absdiff *= -1;
|
||||||
|
}
|
||||||
|
if (absdiff > tol) {
|
||||||
|
CTEST_ERR("%s:%d expected %0.3e, got %0.3e (diff %0.3e, tol %0.3e)", caller, line, exp, real, diff, tol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_dbl_far(double exp, double real, double tol, const char* caller, int line) {
|
||||||
|
double diff = exp - real;
|
||||||
|
double absdiff = diff;
|
||||||
|
/* avoid using fabs and linking with a math lib */
|
||||||
|
if(diff < 0) {
|
||||||
|
absdiff *= -1;
|
||||||
|
}
|
||||||
|
if (absdiff <= tol) {
|
||||||
|
CTEST_ERR("%s:%d expected %0.3e, got %0.3e (diff %0.3e, tol %0.3e)", caller, line, exp, real, diff, tol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_null(void* real, const char* caller, int line) {
|
||||||
|
if ((real) != NULL) {
|
||||||
|
CTEST_ERR("%s:%d should be NULL", caller, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_not_null(const void* real, const char* caller, int line) {
|
||||||
|
if (real == NULL) {
|
||||||
|
CTEST_ERR("%s:%d should not be NULL", caller, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_true(int real, const char* caller, int line) {
|
||||||
|
if ((real) == 0) {
|
||||||
|
CTEST_ERR("%s:%d should be true", caller, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_false(int real, const char* caller, int line) {
|
||||||
|
if ((real) != 0) {
|
||||||
|
CTEST_ERR("%s:%d should be false", caller, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void assert_fail(const char* caller, int line) {
|
||||||
|
CTEST_ERR("%s:%d shouldn't come here", caller, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int suite_all(struct ctest* t) {
|
||||||
|
(void) t; // fix unused parameter warning
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int suite_filter(struct ctest* t) {
|
||||||
|
return strncmp(suite_name, t->ssname, strlen(suite_name)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t getCurrentTime(void) {
|
||||||
|
struct timeval now;
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
uint64_t now64 = (uint64_t) now.tv_sec;
|
||||||
|
now64 *= 1000000;
|
||||||
|
now64 += ((uint64_t) now.tv_usec);
|
||||||
|
return now64;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void color_print(const char* color, const char* text) {
|
||||||
|
if (color_output)
|
||||||
|
printf("%s%s" ANSI_NORMAL "\n", color, text);
|
||||||
|
else
|
||||||
|
printf("%s\n", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CTEST_SEGFAULT
|
||||||
|
#include <signal.h>
|
||||||
|
static void sighandler(int signum)
|
||||||
|
{
|
||||||
|
const char msg_color[] = ANSI_BRED "[SIGSEGV: Segmentation fault]" ANSI_NORMAL "\n";
|
||||||
|
const char msg_nocolor[] = "[SIGSEGV: Segmentation fault]\n";
|
||||||
|
|
||||||
|
const char* msg = color_output ? msg_color : msg_nocolor;
|
||||||
|
write(STDOUT_FILENO, msg, strlen(msg));
|
||||||
|
|
||||||
|
/* "Unregister" the signal handler and send the signal back to the process
|
||||||
|
* so it can terminate as expected */
|
||||||
|
signal(signum, SIG_DFL);
|
||||||
|
kill(getpid(), signum);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ctest_main(int argc, const char *argv[]);
|
||||||
|
|
||||||
|
__attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
static int total = 0;
|
||||||
|
static int num_ok = 0;
|
||||||
|
static int num_fail = 0;
|
||||||
|
static int num_skip = 0;
|
||||||
|
static int idx = 1;
|
||||||
|
static ctest_filter_func filter = suite_all;
|
||||||
|
|
||||||
|
#ifdef CTEST_SEGFAULT
|
||||||
|
signal(SIGSEGV, sighandler);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
suite_name = argv[1];
|
||||||
|
filter = suite_filter;
|
||||||
|
}
|
||||||
|
#ifdef CTEST_NO_COLORS
|
||||||
|
color_output = 0;
|
||||||
|
#else
|
||||||
|
color_output = isatty(1);
|
||||||
|
#endif
|
||||||
|
uint64_t t1 = getCurrentTime();
|
||||||
|
|
||||||
|
struct ctest* ctest_begin = &CTEST_IMPL_TNAME(suite, test);
|
||||||
|
struct ctest* ctest_end = &CTEST_IMPL_TNAME(suite, test);
|
||||||
|
// find begin and end of section by comparing magics
|
||||||
|
while (1) {
|
||||||
|
struct ctest* t = ctest_begin-1;
|
||||||
|
if (t->magic != CTEST_IMPL_MAGIC) break;
|
||||||
|
ctest_begin--;
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
struct ctest* t = ctest_end+1;
|
||||||
|
if (t->magic != CTEST_IMPL_MAGIC) break;
|
||||||
|
ctest_end++;
|
||||||
|
}
|
||||||
|
ctest_end++; // end after last one
|
||||||
|
|
||||||
|
static struct ctest* test;
|
||||||
|
for (test = ctest_begin; test != ctest_end; test++) {
|
||||||
|
if (test == &CTEST_IMPL_TNAME(suite, test)) continue;
|
||||||
|
if (filter(test)) total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (test = ctest_begin; test != ctest_end; test++) {
|
||||||
|
if (test == &CTEST_IMPL_TNAME(suite, test)) continue;
|
||||||
|
if (filter(test)) {
|
||||||
|
ctest_errorbuffer[0] = 0;
|
||||||
|
ctest_errorsize = MSG_SIZE-1;
|
||||||
|
ctest_errormsg = ctest_errorbuffer;
|
||||||
|
printf("TEST %d/%d %s:%s ", idx, total, test->ssname, test->ttname);
|
||||||
|
fflush(stdout);
|
||||||
|
if (test->skip) {
|
||||||
|
color_print(ANSI_BYELLOW, "[SKIPPED]");
|
||||||
|
num_skip++;
|
||||||
|
} else {
|
||||||
|
int result = setjmp(ctest_err);
|
||||||
|
if (result == 0) {
|
||||||
|
if (test->setup && *test->setup) (*test->setup)(test->data);
|
||||||
|
if (test->data)
|
||||||
|
test->run.unary(test->data);
|
||||||
|
else
|
||||||
|
test->run.nullary();
|
||||||
|
if (test->teardown && *test->teardown) (*test->teardown)(test->data);
|
||||||
|
// if we got here it's ok
|
||||||
|
#ifdef CTEST_COLOR_OK
|
||||||
|
color_print(ANSI_BGREEN, "[OK]");
|
||||||
|
#else
|
||||||
|
printf("[OK]\n");
|
||||||
|
#endif
|
||||||
|
num_ok++;
|
||||||
|
} else {
|
||||||
|
color_print(ANSI_BRED, "[FAIL]");
|
||||||
|
num_fail++;
|
||||||
|
}
|
||||||
|
if (ctest_errorsize != MSG_SIZE-1) printf("%s", ctest_errorbuffer);
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint64_t t2 = getCurrentTime();
|
||||||
|
|
||||||
|
const char* color = (num_fail) ? ANSI_BRED : ANSI_GREEN;
|
||||||
|
char results[80];
|
||||||
|
snprintf(results, sizeof(results), "RESULTS: %d tests (%d ok, %d failed, %d skipped) ran in %" PRIu64 " ms", total, num_ok, num_fail, num_skip, (t2 - t1)/1000);
|
||||||
|
color_print(color, results);
|
||||||
|
return num_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
#include "ctest.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <glob.h>
|
||||||
|
|
||||||
|
#include "../getch.h"
|
||||||
|
|
||||||
|
#define EV_PRESSED 1
|
||||||
|
#define EV_RELEASED 0
|
||||||
|
|
||||||
|
inline static void writeToEventDevice(__u16 code)
|
||||||
|
{
|
||||||
|
glob_t globResult;
|
||||||
|
glob("/dev/input/by-path/*-event-kbd", GLOB_NOSORT, NULL, &globResult);
|
||||||
|
|
||||||
|
if(globResult.gl_pathc == 0) {
|
||||||
|
fprintf(stderr, "Failed to find input device.");
|
||||||
|
globfree(&globResult);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
char device[strlen(globResult.gl_pathv[0])];
|
||||||
|
strncpy(device, globResult.gl_pathv[0], strlen(globResult.gl_pathv[0]) + 1);
|
||||||
|
globfree(&globResult);
|
||||||
|
|
||||||
|
struct input_event inputEvent[2];
|
||||||
|
int eventDevice = open(device, O_WRONLY);
|
||||||
|
|
||||||
|
if(eventDevice == -1) {
|
||||||
|
perror("open");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i=0; i<2;i++) {
|
||||||
|
gettimeofday(&inputEvent[i].time, NULL);
|
||||||
|
inputEvent[i].type = EV_KEY;
|
||||||
|
inputEvent[i].code = code;
|
||||||
|
inputEvent[i].value = i ? EV_RELEASED : EV_PRESSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(eventDevice, &inputEvent, sizeof(inputEvent));
|
||||||
|
|
||||||
|
|
||||||
|
close(eventDevice);
|
||||||
|
|
||||||
|
sync();
|
||||||
|
|
||||||
|
fflush(NULL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void reverseString(char * str)
|
||||||
|
{
|
||||||
|
if (str)
|
||||||
|
{
|
||||||
|
char * end = str + strlen(str) - 1;
|
||||||
|
|
||||||
|
// swap the values in the two given variables
|
||||||
|
// XXX: fails when a and b refer to same memory location
|
||||||
|
# define XOR_SWAP(a,b) do\
|
||||||
|
{\
|
||||||
|
(a) ^= (b);\
|
||||||
|
(b) ^= (a);\
|
||||||
|
(a) ^= (b);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
// walk inwards from both ends of the string,
|
||||||
|
// swapping until we get to the middle
|
||||||
|
while (str < end)
|
||||||
|
{
|
||||||
|
XOR_SWAP(*str, *end);
|
||||||
|
str++;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
# undef XOR_SWAP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void writeStringToStdin(char * string)
|
||||||
|
{
|
||||||
|
size_t stringLength = strlen(string);
|
||||||
|
char reversed[stringLength];
|
||||||
|
strncpy(reversed, string, stringLength);
|
||||||
|
|
||||||
|
reverseString(reversed);
|
||||||
|
|
||||||
|
for(int i = 0; i<stringLength;i++) {
|
||||||
|
ungetc(reversed[i], stdin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *asciiCodes[3] = {
|
||||||
|
"\x1bOPP",
|
||||||
|
"\x1b[H",
|
||||||
|
"\x1bOJ"
|
||||||
|
};
|
||||||
|
|
||||||
|
static int scanCodes[3] = { KEY_F1, KEY_HOME, KEY_F5 };
|
||||||
|
|
||||||
|
CTEST(getchTests, simpleGetchSetReturn_q)
|
||||||
|
{
|
||||||
|
int ungetch = _ungetch('q');
|
||||||
|
int getch = _getch();
|
||||||
|
|
||||||
|
ASSERT_EQUAL(getch, ungetch);
|
||||||
|
ASSERT_EQUAL(113, ungetch);
|
||||||
|
ASSERT_EQUAL(113, getch);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTEST(getchTests, SpecialKey)
|
||||||
|
{
|
||||||
|
|
||||||
|
writeStringToStdin(asciiCodes[0]);
|
||||||
|
writeToEventDevice(scanCodes[0]);
|
||||||
|
|
||||||
|
int key = _getch();
|
||||||
|
int code = _getch();
|
||||||
|
|
||||||
|
ASSERT_EQUAL(0, key);
|
||||||
|
ASSERT_EQUAL(scanCodes[0], code);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTEST(specialKeyTests, Home)
|
||||||
|
{
|
||||||
|
|
||||||
|
writeStringToStdin(asciiCodes[1]);
|
||||||
|
writeToEventDevice(KEY_HOME);
|
||||||
|
|
||||||
|
int key = _getch();
|
||||||
|
int code = _getch();
|
||||||
|
|
||||||
|
ASSERT_EQUAL(224, key);
|
||||||
|
ASSERT_EQUAL(KEY_KP7, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTEST(escapeReturnsTest, Esc)
|
||||||
|
{
|
||||||
|
ungetc(27, stdin);
|
||||||
|
writeToEventDevice(KEY_ESC);
|
||||||
|
int key = _getch();
|
||||||
|
|
||||||
|
ASSERT_EQUAL(27, key);
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#define CTEST_MAIN
|
||||||
|
|
||||||
|
#include "ctest.h"
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[])
|
||||||
|
{
|
||||||
|
if(argc == 1) {
|
||||||
|
argv[1] = "getchTests";
|
||||||
|
argc = 2;
|
||||||
|
}
|
||||||
|
int result = ctest_main(argc, argv);
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in New Issue