First Commit
This commit is contained in:
commit
ae0fab9400
424 files changed
+71006
No files matched your search
@@ -0,0 +1,25 @@
|
||||
= attributed strings
|
||||
attribute lengths are rounded to complete unicode codepoints
|
||||
zero-length attributes are elided
|
||||
consecutive attributes of the same type and value are merged
|
||||
overlapping attributes of different types do not split each other
|
||||
overlapping attributes of the same type but different values do split
|
||||
empty string is allowed
|
||||
empty string cannot have attributes
|
||||
font family names are case-insensitive both in attributes and in descriptors
|
||||
attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes
|
||||
define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions and constructors
|
||||
does uiAttributeFamily() return a normalized string
|
||||
should uiNewAttributeBackground() be renamed to uiNewAttributeBackgroundColor() and likewise for the type constant
|
||||
should underline colors just ignore non-custom component arguments
|
||||
should any color getter function accept a NULL pointer
|
||||
what should uiAttributeUnderlineColor() do if the color type isn't Custom but the other pointers are non-NULL
|
||||
should uiOpenTypeFeaturesGet() accept a NULL value pointer
|
||||
what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer
|
||||
should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum
|
||||
should uiNewFeaturesAttribute() accept NULL
|
||||
should uiNewFamilyAttribute() accept NULL
|
||||
it is an error in ForEach too
|
||||
invalid values for uiDrawTextAlign
|
||||
empty text layouts have one line
|
||||
TODO figure out what to do if any field (particularly the font family name) in uiFontDescriptor is unset
|
||||
@@ -0,0 +1,115 @@
|
||||
// 27 february 2018
|
||||
#ifndef TODO_TEST
|
||||
#error TODO this is where libui itself goes
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include "testing.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
typedef struct uiOpenTypeFeatures uiOpenTypeFeatures;
|
||||
typedef int uiForEach;
|
||||
enum { uiForEachContinue, uiForEachStop };
|
||||
typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data);
|
||||
#define uiprivNew(x) ((x *) malloc(sizeof (x)))
|
||||
#define uiprivAlloc(x,y) malloc(x)
|
||||
#define uiprivRealloc(x,y,z) realloc(x,y)
|
||||
#define uiprivFree free
|
||||
#include "opentype.c"
|
||||
|
||||
static void freeOpenType(void *otf)
|
||||
{
|
||||
uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) otf);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesAddGet)
|
||||
{
|
||||
uiOpenTypeFeatures *otf;
|
||||
int got;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
got = uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
|
||||
|
||||
if (!got)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added");
|
||||
else if (value != 12345)
|
||||
testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesRemove)
|
||||
{
|
||||
uiOpenTypeFeatures *otf;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
uiOpenTypeFeaturesRemove(otf, 'a', 'b', 'c', 'd');
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() succeeded in getting deleted feature; value %" PRIu32, value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneAdd)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesAdd(otf2, 'q', 'w', 'e', 'r', 56789);
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf, 'q', 'w', 'e', 'r', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original succeeded in getting feature added to clone; value %" PRIu32, value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneModify)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesAdd(otf2, 'a', 'b', 'c', 'd', 56789);
|
||||
|
||||
uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
|
||||
if (value != 12345)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original: got %" PRIu32 ", want 12345", value);
|
||||
uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value);
|
||||
if (value != 56789)
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone: got %" PRIu32 ", want 56789", value);
|
||||
}
|
||||
|
||||
testingTest(OpenTypeFeaturesCloneRemove)
|
||||
{
|
||||
uiOpenTypeFeatures *otf, *otf2;
|
||||
uint32_t value;
|
||||
|
||||
otf = uiNewOpenTypeFeatures();
|
||||
testingTDefer(t, freeOpenType, otf);
|
||||
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
|
||||
otf2 = uiOpenTypeFeaturesClone(otf);
|
||||
testingTDefer(t, freeOpenType, otf2);
|
||||
uiOpenTypeFeaturesRemove(otf2, 'a', 'b', 'c', 'd');
|
||||
|
||||
if (uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone succeeded in getting feature removed from clone; value %" PRIu32, value);
|
||||
if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
|
||||
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original failed to get feature removed from clone");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
return testingMain();
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
// 27 february 2018
|
||||
|
||||
// TODO
|
||||
// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 https://blogs.msdn.microsoft.com/oldnewthing/20181109-00/?p=100175
|
||||
// - also in the above: note the unspecified order of data in the sub-segments...
|
||||
|
||||
#ifndef testingprivIncludeGuard_testing_h
|
||||
#define testingprivIncludeGuard_testing_h
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#undef testingprivBadLanguageVersion
|
||||
#ifdef __cplusplus
|
||||
// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong...
|
||||
// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)?
|
||||
#if __cplusplus < 201103L
|
||||
#define testingprivBadLanguageVersion
|
||||
#endif
|
||||
#elif !defined(__STDC_VERSION__)
|
||||
#define testingprivBadLanguageVersion
|
||||
#elif __STDC_VERSION__ < 199901L
|
||||
#define testingprivBadLanguageVersion
|
||||
#endif
|
||||
#ifdef testingprivBadLanguageVersion
|
||||
#error sorry, TODO requires either C99 or C++11; cannot continue
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define testingprivMkScaffold(name) \
|
||||
static inline void testingprivScaffold ## name(testingT *t) \
|
||||
{ \
|
||||
bool failedNow = false, skippedNow = false; \
|
||||
try { name(t); } \
|
||||
catch (testingprivFailNowException e) { failedNow = true; } \
|
||||
catch (testingprivSkipNowException e) { skippedNow = true; } \
|
||||
/* TODO see if we should catch other exceptions too */ \
|
||||
/* don't call these in the catch blocks as they call longjmp() */ \
|
||||
if (failedNow) testingprivTDoFailNow(t); \
|
||||
if (skippedNow) testingprivTDoSkipNow(t); \
|
||||
}
|
||||
#else
|
||||
#define testingprivMkScaffold(name) \
|
||||
static inline void testingprivScaffold ## name(testingT *t) { name(t); }
|
||||
#endif
|
||||
|
||||
// references:
|
||||
// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h
|
||||
// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c
|
||||
// - https://msdn.microsoft.com/en-us/library/bb918180.aspx
|
||||
#if defined(__cplusplus)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name);
|
||||
#elif defined(__GNUC__)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
__attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
|
||||
#elif defined(_MSC_VER)
|
||||
#define testingprivMkCtorPrototype(name, reg) \
|
||||
static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \
|
||||
__pragma(section(".CRT$XCU",read)) \
|
||||
__declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name;
|
||||
#elif defined(__SUNPRO_C)
|
||||
#define testingprivMkCtor(name, reg) \
|
||||
_Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
|
||||
#else
|
||||
#error unknown compiler for making constructors in C; cannot continue
|
||||
#endif
|
||||
|
||||
#define testingTest(Name) \
|
||||
void Test ## Name(testingT *t); \
|
||||
testingprivMkScaffold(Test ## Name) \
|
||||
testingprivMkCtor(Test ## Name, testingprivRegisterTest) \
|
||||
void Test ## Name(testingT *t)
|
||||
|
||||
extern int testingMain(void);
|
||||
|
||||
typedef struct testingT testingT;
|
||||
#define testingTLogf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__))
|
||||
#define testingTLogvf(t, format, ap) \
|
||||
testingprivTLogvfThen((void), t, format, ap)
|
||||
#define testingTErrorf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__))
|
||||
#define testingTErrorvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTFail, t, format, ap)
|
||||
#define testingTFatalf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__))
|
||||
#define testingTFatalvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTFailNow, t, format, ap)
|
||||
#define testingTSkipf(t, ...) \
|
||||
testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__))
|
||||
#define testingTSkipvf(t, format, ap) \
|
||||
testingprivTLogvfThen(testingTSkipNow, t, format, ap)
|
||||
extern void testingTFail(testingT *t);
|
||||
#ifdef __cplusplus
|
||||
#define testingTFailNow(t) (throw testingprivFailNowException())
|
||||
#define testingTSkipNow(t) (throw testingprivSkipNowException())
|
||||
#else
|
||||
#define testingTFailNow(t) (testingprivTDoFailNow(t))
|
||||
#define testingTSkipNow(t) (testingprivTDoSkipNow(t))
|
||||
#endif
|
||||
// TODO should the defered function also have t passed to it?
|
||||
extern void testingTDefer(testingT *t, void (*f)(void *data), void *data);
|
||||
|
||||
// TODO IEEE 754 helpers
|
||||
// references:
|
||||
// - https://www.sourceware.org/ml/libc-alpha/2009-04/msg00005.html
|
||||
// - https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
|
||||
// - https://stackoverflow.com/questions/5085533/is-a-c-preprocessor-identical-to-a-c-preprocessor
|
||||
|
||||
// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int?
|
||||
extern void testingprivRegisterTest(const char *, void (*)(testingT *));
|
||||
// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc
|
||||
#define testingprivExpand(x) x
|
||||
#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t)))
|
||||
#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t)))
|
||||
extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...);
|
||||
extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list);
|
||||
extern void testingprivTDoFailNow(testingT *);
|
||||
extern void testingprivTDoSkipNow(testingT *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
namespace {
|
||||
class testingprivFailNowException {};
|
||||
class testingprivSkipNowException {};
|
||||
class testingprivRegisterTestClass {
|
||||
public:
|
||||
testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
// 27 february 2018
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <setjmp.h>
|
||||
#include "testing.h"
|
||||
|
||||
#define testingprivNew(T) ((T *) malloc(sizeof (T)))
|
||||
|
||||
struct defer {
|
||||
void (*f)(void *);
|
||||
void *data;
|
||||
struct defer *next;
|
||||
};
|
||||
|
||||
struct testingT {
|
||||
const char *name;
|
||||
void (*f)(testingT *);
|
||||
int failed;
|
||||
int skipped;
|
||||
jmp_buf returnNowBuf;
|
||||
struct defer *defers;
|
||||
int defersRun;
|
||||
testingT *next;
|
||||
};
|
||||
|
||||
static testingT *tests = NULL;
|
||||
|
||||
void testingprivRegisterTest(const char *name, void (*f)(testingT *))
|
||||
{
|
||||
testingT *t;
|
||||
|
||||
t = testingprivNew(testingT);
|
||||
t->name = name;
|
||||
t->f = f;
|
||||
t->failed = 0;
|
||||
t->skipped = 0;
|
||||
t->defers = NULL;
|
||||
t->defersRun = 0;
|
||||
// TODO add in the order called
|
||||
t->next = tests;
|
||||
tests = t;
|
||||
}
|
||||
|
||||
static void runDefers(testingT *t)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
if (t->defersRun)
|
||||
return;
|
||||
t->defersRun = 1;
|
||||
for (d = t->defers; d != NULL; d = d->next)
|
||||
(*(d->f))(d->data);
|
||||
}
|
||||
|
||||
int testingMain(void)
|
||||
{
|
||||
testingT *t;
|
||||
int anyFailed;
|
||||
const char *status;
|
||||
|
||||
// TODO see if this should run if all tests are skipped
|
||||
if (tests == NULL) {
|
||||
fprintf(stderr, "warning: no tests to run\n");
|
||||
// imitate Go here (TODO confirm this)
|
||||
return 0;
|
||||
}
|
||||
|
||||
anyFailed = 0;
|
||||
for (t = tests; t != NULL; t = t->next) {
|
||||
printf("=== RUN %s\n", t->name);
|
||||
if (setjmp(t->returnNowBuf) == 0)
|
||||
(*(t->f))(t);
|
||||
runDefers(t);
|
||||
status = "PASS";
|
||||
if (t->failed) {
|
||||
status = "FAIL";
|
||||
anyFailed = 1;
|
||||
} else if (t->skipped)
|
||||
// note that failed overrides skipped
|
||||
status = "SKIP";
|
||||
printf("--- %s: %s (%s)\n", status, t->name, "TODO");
|
||||
}
|
||||
|
||||
if (anyFailed) {
|
||||
printf("FAIL\n");
|
||||
return 1;
|
||||
}
|
||||
printf("PASS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
testingprivTLogvfFull(t, file, line, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap)
|
||||
{
|
||||
// TODO extract filename from file
|
||||
printf("\t%s:%d: ", file, line);
|
||||
// TODO split into lines separated by \n\t\t and trimming trailing empty lines
|
||||
vprintf(format, ap);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void testingTFail(testingT *t)
|
||||
{
|
||||
t->failed = 1;
|
||||
}
|
||||
|
||||
static void returnNow(testingT *t)
|
||||
{
|
||||
// run defers before calling longjmp() just to be safe
|
||||
runDefers(t);
|
||||
longjmp(t->returnNowBuf, 1);
|
||||
}
|
||||
|
||||
void testingprivTDoFailNow(testingT *t)
|
||||
{
|
||||
testingTFail(t);
|
||||
returnNow(t);
|
||||
}
|
||||
|
||||
void testingprivTDoSkipNow(testingT *t)
|
||||
{
|
||||
t->skipped = 1;
|
||||
returnNow(t);
|
||||
}
|
||||
|
||||
void testingTDefer(testingT *t, void (*f)(void *data), void *data)
|
||||
{
|
||||
struct defer *d;
|
||||
|
||||
d = testingprivNew(struct defer);
|
||||
d->f = f;
|
||||
d->data = data;
|
||||
// add to the head of the list so defers are run in reverse order of how they were added
|
||||
d->next = t->defers;
|
||||
t->defers = d;
|
||||
}
|
||||
Reference in new issue
Block a user