Commit 7f1f647b authored by Linshizhi's avatar Linshizhi

Init with unittests.

parents
cmake_minimum_required(VERSION 3.16)
enable_testing()
include(ExternalProject)
project(FFmpeg_Protos)
option(DEBUG "Only enable during development" OFF)
option(STATICLIB "Generate Static Library" OFF)
set(SRCs ${CMAKE_SOURCE_DIR}/src)
# Basics
include_directories(${CMAKE_SOURCE_DIR}/src)
set(SRC_FILES
${SRCs}/proto.c
${SRCs}/basic/list/list.c
${SRCs}/MovMem/mm.c)
if (STATICLIB)
add_library(ffmpegprotos STATIC ${SRC_FILES})
else()
add_library(ffmpegprotos SHARED ${SRC_FILES})
target_link_libraries(ffmpegprotos
avformat avcodec avdevice avfilter avutil
swresample swscale postproc z x264 lzma)
endif()
install(TARGETS ffmpegprotos
DESTINATION ${CMAKE_INSTALL_LIBDIR})
if(DEBUG)
ExternalProject_Add(
libav
GIT_REPOSITORY https://github.com/FFmpeg/FFmpeg.git
GIT_TAG n4.3.3
PREFIX ${PROJECT_SOURCE_DIR}/lib/.build/${LIBAV}
CONFIGURE_COMMAND ../${LIBAV}/configure --disable-x86asm --enable-libx264 --enable-debug --extra-cflags=-g
--extra-ldflags=-g --enable-gpl --shlibdir=${PROJECT_SOURCE_DIR}/lib/
--disable-ffmpeg --disable-ffprobe --disable-ffplay --prefix=${PROJECT_SOURCE_DIR}/lib/
--enable-shared
BUILD_COMMAND make -j
INSTALL_COMMAND make install
UPDATE_COMMAND ""
)
# Tests
set(Tests ${CMAKE_SOURCE_DIR}/tests)
set(TestCases
${SRCs}/basic/list/list.c
${Tests}/main.c
${Tests}/listTestCases.c)
add_executable(unittest ${TestCases} ${SRC_Files})
add_dependencies(unittest libav)
add_custom_command(TARGET unittest PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/resources/ $<TARGET_FILE_DIR:unittest>/resources)
add_test(
NAME UNITTEST_DIRECTLY
COMMAND valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-fil=valgrind-log.txt ./unittest)
# Link Google Test
target_link_libraries(unittest
${CMAKE_SOURCE_DIR}/lib/lib/libavformat.a
${CMAKE_SOURCE_DIR}/lib/lib/libavcodec.a
${CMAKE_SOURCE_DIR}/lib/lib/libavdevice.a
${CMAKE_SOURCE_DIR}/lib/lib/libavfilter.a
${CMAKE_SOURCE_DIR}/lib/lib/libavutil.a
${CMAKE_SOURCE_DIR}/lib/lib/libswresample.a
${CMAKE_SOURCE_DIR}/lib/lib/libswscale.a
${CMAKE_SOURCE_DIR}/lib/lib/libpostproc.a
)
target_link_libraries(unittest z)
target_link_libraries(unittest x264)
target_link_libraries(unittest lzma)
endif()
#include "mm.h"
static int mmRead(void *priv, uint8_t *buf, int size) {
return 0;
}
static int mmWrite(void *priv, uint8_t *buf, int size) {
}
MMProto* createMMProto(void) {
MMProto *p = (MMProto*)malloc(sizeof(MMProto));
p->Base = createProto_(mmRead, mmWrite, NULL, p);
return p;
}
#include "../proto.h"
#include <libavformat/avformat.h>
#ifndef MM_H
#define MM_H
typedef struct MMProto {
Proto Base;
} MMProto;
/* Prototypes */
MMProto* createMMProto(void);
#endif /* MM_H */
#include <string.h>
#include <malloc.h>
#include "list.h"
List* createList() {
List *l = (List*)malloc(sizeof(List));
memset(l, 0, sizeof(List));
return l;
}
void destroyList(List **l_) {
List *l = *l_;
Node *n = listTail(l), *next;
while (n) {
next = nodePrev(n);
if (l->destructor) {
destroyNodeVal(l, n);
free(n);
}
n = next;
}
free(l);
*l_ = NULL;
}
void listPush(List *l, void *val) {
Node *n = (Node*)malloc(sizeof(Node));
n->val = val;
n->prev = NULL;
n->next = NULL;
if (listEmpty(l)) {
listSetHead(l, n);
listSetTail(l, n);
} else {
setNodeNext(listTail(l), n);
setNodePrev(n, listTail(l));
listSetTail(l, n);
}
l->size++;
}
void* listPop(List *l) {
Node *n = listHead(l);
if (n == NULL) {
return n;
}
listSetHead(l, n->next);
if (listEmpty(l)) {
listSetTail(l, NULL);
} else {
setNodePrev(listHead(l), NULL);
}
void *v = n->val;
free(n);
l->size--;
return v;
}
#ifndef LIST_H
#define LIST_H
typedef void (*NodeDestructor)(void *val);
typedef void (*NodeDuplicate)(void *val);
typedef struct Node {
struct Node *prev;
struct Node *next;
void *val;
} Node;
typedef struct List {
Node head;
Node *tail;
/* If destructor is NULL then do nothing */
NodeDestructor destructor;
/* Only copyable when duplicate is not NULL */
NodeDuplicate duplicate;
int size;
} List;
/* Member function implement as macros */
#define listSize(L) ((L)->size)
#define listHead(L) ((L)->head.next)
#define listSetHead(L, N) ((L)->head.next = (N))
#define listTail(L) ((L)->tail)
#define listSetTail(L, N) ((L)->tail = (N))
#define listEmpty(L) ((L)->head.next == NULL)
#define listSetDestructor(L, D) ((L)->destructor=(D))
#define listSetDuplicate(L, Dup) ((L)->duplicate=(Dup))
#define nodePrev(N) ((N)->prev)
#define setNodePrev(NL, NR) ((NL)->prev = (NR))
#define nodeNext(N) ((N)->next)
#define setNodeNext(NL, NR) ((NL)->next = (NR))
#define destroyNodeVal(L, N) ((L)->destructor((N)->val))
#define duplicateNodeVal(L, N) ((L)->duplicate((N)->val))
/* Prototypes */
List* createList(void);
void destroyList(List **l);
void listPush(List *l, void *val);
void* listPop(List *l);
#endif /* LIST_H */
#include "proto.h"
Proto* createProto(ReadFunc rf, WriteFunc wf, SeekFunc sf, void *priv) {
Proto *p = (Proto*)malloc(sizeof(Proto));
p->read = rf;
p->write = wf;
p->seek = sf;
p->priv = priv;
p->avioBuffer = NULL;
return p;
}
Proto createProto_(ReadFunc rf, WriteFunc wf, SeekFunc sf, void *priv) {
Proto p = {
.read = rf,
.write = wf,
.seek = sf,
.priv = priv,
.avioBuffer = NULL,
};
return p;
}
AVIOContext* proto2AVIO(Proto *p, size_t bufSize) {
p->avioBuffer = (unsigned char *)av_malloc(bufSize);
AVIOContext *ioctx = avio_alloc_context(
p->avioBuffer, bufSize, 0,
(void *)p,
p->read,
p->write,
p->seek);
return ioctx;
}
#include <stdint.h>
#include <libavformat/avformat.h>
#ifndef PROTO_H
#define PROTO_H
typedef int (*ReadFunc)(void *priv, uint8_t *buf, int size);
typedef int (*WriteFunc)(void *priv, uint8_t *buf, int size);
typedef int64_t (*SeekFunc)(void *opaque, int64_t offset, int whence);
typedef struct Proto {
int (*read)(void *priv, uint8_t *buf, int size);
int (*write)(void *priv, uint8_t *buf, int size);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
void *priv;
unsigned char *avioBuffer;
} Proto;
/* Member function implement as macros */
#define read(P, priv, buf, size) ((P)->read((priv), (buf), (size)))
#define write(P, priv, buf, size) ((P)->write((priv), (buf), (size)))
#define seek(P, opaque, offset, whence) ((P)->seek((opaque), (offset), (whence)))
/* Prototypes */
Proto* createProto(ReadFunc rf, WriteFunc wr, SeekFunc sf, void *priv);
Proto createProto_(ReadFunc rf, WriteFunc wr, SeekFunc sf, void *priv);
AVIOContext* proto2AVIO(Proto *p, size_t bufSize);
#endif /* PROTO_H */
/* Copyright 2011-2022 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
#include "ctest.h"
#include "basic/list/list.h"
#include <malloc.h>
void LIST_EMPTY_STATUS_CHECK(List *l) {
ASSERT_TRUE(listEmpty(l));
ASSERT_TRUE(l->head.next == NULL);
ASSERT_TRUE(l->head.prev == NULL);
ASSERT_TRUE(l->tail == NULL);
ASSERT_TRUE(listSize(l) == 0);
}
CTEST_DATA(LIST_TEST) {
List *l;
};
CTEST_SETUP(LIST_TEST) {
data->l = createList();
listSetDestructor(data->l, (NodeDestructor)free);
}
CTEST_TEARDOWN(LIST_TEST) {
destroyList(&data->l);
}
CTEST2(LIST_TEST, PUSH) {
int *val = NULL;
for (int i = 0; i < 1000; ++i) {
val = (int*)malloc(sizeof(int));
*val = i;
listPush(data->l, val);
}
ASSERT_EQUAL(listSize(data->l), 1000);
Node *n = data->l->head.next;
for (int i = 0; i < 1000; ++i) {
ASSERT_EQUAL(*(int*)n->val, i);
n = nodeNext(n);
}
}
CTEST2(LIST_TEST, POP) {
int *val = NULL;
for (int i = 0; i < 1000; ++i) {
val = (int*)malloc(sizeof(int));
*val = i;
listPush(data->l, val);
}
for (int i = 0; i < 1000; ++i) {
val = listPop(data->l);
ASSERT_EQUAL(*(int*)val, i);
free(val);
}
LIST_EMPTY_STATUS_CHECK(data->l);
}
CTEST2(LIST_TEST, POP_FROM_EMPTY_LIST) {
void *v = NULL;
for (int i = 0; i < 1000; ++i) {
v = listPop(data->l);
ASSERT_NULL(v);
}
LIST_EMPTY_STATUS_CHECK(data->l);
}
#include <stdio.h>
#define CTEST_MAIN
// uncomment lines below to enable/disable features. See README.md for details
#define CTEST_SEGFAULT
//#define CTEST_NO_COLORS
//#define CTEST_COLOR_OK
#include "ctest.h"
int main(int argc, const char *argv[])
{
int result = ctest_main(argc, argv);
return result;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment