libui/test/controls.c

640 lines
18 KiB
C

// 8 june 2019
#include "test.h"
static bool vtableNopInit(uiControl *c, void *implData, void *initData)
{
return true;
}
static void vtableNopFree(uiControl *c, void *implData)
{
// do nothing
}
static void vtableNopParentChanging(uiControl *c, void *implData, uiControl *oldParent)
{
// do nothing
}
static void vtableNopParentChanged(uiControl *c, void *implData, uiControl *newParent)
{
// do nothing
}
void testControlLoadNopVtable(uiControlVtable *vtable)
{
memset(vtable, 0, sizeof (uiControlVtable));
vtable->Size = sizeof (uiControlVtable);
vtable->Init = vtableNopInit;
vtable->Free = vtableNopFree;
vtable->ParentChanging = vtableNopParentChanging;
vtable->ParentChanged = vtableNopParentChanged;
}
// TODO we'll have to eventually find out for real if memset(0) is sufficient to set pointers to NULL or not; C99 doesn't seem to say
Test(ControlImplDataIsClearedOnNewControl)
{
char memory[32];
uiControlVtable vt;
uint32_t type;
uiControl *c;
char *implData;
testControlLoadNopVtable(&vt);
type = uiRegisterControlType("TestControl", &vt, testOSVtable(), sizeof (memory));
c = uiNewControl(type, NULL);
implData = (char *) uiControlImplData(c);
memset(memory, 0, sizeof (memory));
if (memcmp(implData, memory, sizeof (memory)) != 0)
TestErrorf("control impl data memory not properly cleared on creation");
uiControlFree(c);
}
Test(ZeroSizeImplDataIsNULL)
{
uiControlVtable vt;
uint32_t type;
uiControl *c;
testControlLoadNopVtable(&vt);
type = uiRegisterControlType("TestControl", &vt, testOSVtable(), 0);
c = uiNewControl(type, NULL);
if (uiControlImplData(c) != NULL)
TestErrorf("control impl data is non-NULL despite being of size 0");
uiControlFree(c);
}
struct counts {
unsigned int countInit;
unsigned int countFree;
unsigned int countParentChanging;
unsigned int countParentChanged;
uiControl *oldParent;
uiControl *newParent;
};
struct testImplData {
struct counts *counts;
};
static struct counts failInit;
static void *testControlFailInit = &failInit;
static bool testVtableInit(uiControl *c, void *implData, void *initData)
{
struct testImplData *d = (struct testImplData *) implData;
struct counts *counts = (struct counts *) initData;
if (initData == testControlFailInit)
return false;
if (initData == NULL)
return true;
if (d->counts == NULL)
d->counts = counts;
d->counts->countInit++;
if (d->counts->countInit > 2)
d->counts->countInit = 2;
return true;
}
static void testVtableFree(uiControl *c, void *implData)
{
struct testImplData *d = (struct testImplData *) implData;
if (d->counts != NULL) {
d->counts->countFree++;
if (d->counts->countFree > 2)
d->counts->countFree = 2;
}
}
static void testVtableParentChanging(uiControl *c, void *implData, uiControl *oldParent)
{
struct testImplData *d = (struct testImplData *) implData;
if (d->counts != NULL) {
d->counts->oldParent = oldParent;
d->counts->countParentChanging++;
if (d->counts->countParentChanging > 3)
d->counts->countParentChanging = 3;
}
}
static void testVtableParentChanged(uiControl *c, void *implData, uiControl *newParent)
{
struct testImplData *d = (struct testImplData *) implData;
if (d->counts != NULL) {
d->counts->newParent = newParent;
d->counts->countParentChanged++;
if (d->counts->countParentChanged > 3)
d->counts->countParentChanged = 3;
}
}
static const uiControlVtable vtable = {
.Size = sizeof (uiControlVtable),
.Init = testVtableInit,
.Free = testVtableFree,
.ParentChanging = testVtableParentChanging,
.ParentChanged = testVtableParentChanged,
};
static uint32_t testControlType(void)
{
static uint32_t type = 0;
if (type == 0)
type = uiRegisterControlType("TestControl", &vtable, testOSVtable(), sizeof (struct testImplData));
return type;
}
static uint32_t testControlType2(void)
{
static uint32_t type = 0;
if (type == 0)
type = uiRegisterControlType("TestControl2", &vtable, testOSVtable(), sizeof (struct testImplData));
return type;
}
// TODO do this but for the OS-specific methods
Test(ControlMethodsCalled)
{
uiControl *c, *d;
struct counts counts;
memset(&counts, 0, sizeof (struct counts));
c = uiNewControl(testControlType(), &counts);
switch (counts.countInit) {
case 0:
TestErrorf("Init() was not called");
break;
case 1:
// do nothing; this is the expected case
break;
default:
TestErrorf("Init() called more than once");
}
if (counts.countFree != 0)
TestErrorf("Free() called unexpectedly by uiNewControl()");
// yes, the casts to void * are necessary, because the "equivalence" of data pointers to void * is really just the compiler doing conversions for you and this does not (and cannot) extend to the parameter lists of varargs functions (https://stackoverflow.com/questions/34723062, https://stackoverflow.com/questions/9053658)
if (counts.countParentChanging != 0)
TestErrorf("ParentChanging() called unexpectedly by uiNewControl(); most recent oldParent = %p", (void *) (counts.oldParent));
if (counts.countParentChanged != 0)
TestErrorf("ParentChanged() called unexpectedly by uiNewControl(); most recent newParent = %p", (void *) (counts.newParent));
d = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
switch (counts.countParentChanging) {
case 0:
TestErrorf("ParentChanging() was not called by SetParent(non-NULL)");
break;
case 1:
// do nothing; this is the expected case
break;
default:
TestErrorf("ParentChanging() called more than once by SetParent(non-NULL)");
}
if (counts.oldParent != NULL)
TestErrorf("ParentChanging() called with wrong oldParent by SetParent(non-NULL) (if called more than once, this is the most recent call):" diff("%p"),
(void *) (counts.oldParent), (void *) NULL);
switch (counts.countParentChanged) {
case 0:
TestErrorf("ParentChanged() was not called by SetParent(non-NULL)");
break;
case 1:
// do nothing; this is the expected case
break;
default:
TestErrorf("ParentChanged() called more than once by SetParent(non-NULL)");
}
if (counts.newParent != d)
TestErrorf("ParentChanged() called with wrong newParent by SetParent(non-NULL) (if called more than once, this is the most recent call):" diff("%p"),
(void *) (counts.newParent), (void *) d);
if (counts.countInit != 1)
TestErrorf("Init() called unexpectedly by uiControlSetParent(non-NULL)");
if (counts.countFree != 0)
TestErrorf("Free() called unexpectedly by uiControlSetParent(non-NULL)");
uiControlSetParent(c, NULL);
switch (counts.countParentChanging) {
case 0:
case 1:
TestErrorf("ParentChanging() was not called by SetParent(NULL)");
break;
case 2:
// do nothing; this is the expected case
break;
default:
TestErrorf("ParentChanging() called more than once by SetParent(NULL)");
}
if (counts.oldParent != d)
TestErrorf("ParentChanging() called with wrong oldParent by SetParent(NULL) (if called more than once, this is the most recent call):" diff("%p"),
(void *) (counts.oldParent), (void *) d);
switch (counts.countParentChanged) {
case 0:
case 1:
TestErrorf("ParentChanged() was not called by SetParent(NULL)");
break;
case 2:
// do nothing; this is the expected case
break;
default:
TestErrorf("ParentChanged() called more than once by SetParent(NULL)");
}
if (counts.newParent != NULL)
TestErrorf("ParentChanged() called with wrong newParent by SetParent(NULL) (if called more than once, this is the most recent call):" diff("%p"),
(void *) (counts.newParent), (void *) NULL);
if (counts.countInit != 1)
TestErrorf("Init() called unexpectedly by uiControlSetParent(NULL)");
if (counts.countFree != 0)
TestErrorf("Free() called unexpectedly by uiControlSetParent(NULL)");
uiControlFree(d);
uiControlFree(c);
switch (counts.countFree) {
case 0:
TestErrorf("Free() was not called");
break;
case 1:
// do nothing; this is the expected case
break;
default:
TestErrorf("Free() called more than once");
}
if (counts.countInit != 1)
TestErrorf("Init() called unexpectedly by uiControlFree()");
if (counts.countParentChanging != 2)
TestErrorf("ParentChanging() called unexpectedly by uiNewControl(); most recent oldParent = %p", (void *) (counts.oldParent));
if (counts.countParentChanged != 2)
TestErrorf("ParentChanged() called unexpectedly by uiNewControl(); most recent newParent = %p", (void *) (counts.newParent));
}
Test(NullControlTypeNameIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for name");
uiRegisterControlType(NULL, NULL, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(NullControlVtableIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for uiControlVtable");
uiRegisterControlType("name", NULL, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(WrongControlVtableSizeIsProgrammerError)
{
uiControlVtable vtable;
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): wrong size 1 for uiControlVtable");
memset(&vtable, 0, sizeof (uiControlVtable));
vtable.Size = 1;
uiRegisterControlType("name", &vtable, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(ControlVtableWithMissingInitMethodIsProgrammerError)
{
uiControlVtable vt;
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method Init() missing for uiControl type name");
vt = vtable;
vt.Init = NULL;
uiRegisterControlType("name", &vt, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(ControlVtableWithMissingFreeMethodIsProgrammerError)
{
uiControlVtable vt;
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method Free() missing for uiControl type name");
vt = vtable;
vt.Free = NULL;
uiRegisterControlType("name", &vt, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(ControlVtableWithMissingParentChangingMethodIsProgrammerError)
{
uiControlVtable vt;
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method ParentChanging() missing for uiControl type name");
vt = vtable;
vt.ParentChanging = NULL;
uiRegisterControlType("name", &vt, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(ControlVtableWithMissingParentChangedMethodIsProgrammerError)
{
uiControlVtable vt;
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): required uiControlVtable method ParentChanged() missing for uiControl type name");
vt = vtable;
vt.ParentChanged = NULL;
uiRegisterControlType("name", &vt, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(NullControlOSVtableIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiRegisterControlType(): invalid null pointer for uiControlOSVtable");
uiRegisterControlType("name", &vtable, NULL, 0);
endCheckProgrammerError(ctx);
}
Test(CheckingNullControlIsProgrammerError)
{
uint32_t ctrlType;
void *ctx;
ctrlType = uiControlType();
ctx = beginCheckProgrammerError("uiCheckControlType(): invalid null pointer for uiControl");
uiCheckControlType(NULL, ctrlType);
endCheckProgrammerError(ctx);
}
Test(CheckingNonControlIsProgrammerError)
{
uiControl *c;
uint32_t ctrlType;
void *ctx;
c = uiprivTestHookControlWithInvalidControlMarker();
ctrlType = uiControlType();
ctx = beginCheckProgrammerError("uiCheckControlType(): object passed in not a uiControl");
uiCheckControlType(c, ctrlType);
endCheckProgrammerError(ctx);
}
Test(CheckingControlWithAnUnknownTypeIsProgrammerError)
{
uiControl *c;
uint32_t ctrlType;
void *ctx;
c = uiprivTestHookControlWithInvalidType();
ctrlType = testControlType();
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 found in uiControl (this is likely not a real uiControl or some data is corrupt)");
uiCheckControlType(c, ctrlType);
endCheckProgrammerError(ctx);
}
Test(CheckingControlWithAnUnknownTypeIsProgrammerErrorEvenIfCheckingAgainstuiControlType)
{
uiControl *c;
uint32_t ctrlType;
void *ctx;
c = uiprivTestHookControlWithInvalidType();
ctrlType = uiControlType();
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 found in uiControl (this is likely not a real uiControl or some data is corrupt)");
uiCheckControlType(c, ctrlType);
endCheckProgrammerError(ctx);
}
Test(CheckingForUnknownControlTypeIsProgrammerError)
{
uiControl *c;
void *ctx;
c = uiNewControl(testControlType(), NULL);
ctx = beginCheckProgrammerError("uiCheckControlType(): unknown uiControl type 0 requested");
uiCheckControlType(c, 0);
endCheckProgrammerError(ctx);
uiControlFree(c);
}
Test(CheckControlTypeFailsCorrectly)
{
uiControl *c;
uint32_t ctrlType;
void *ctx;
c = uiNewControl(testControlType(), NULL);
ctrlType = testControlType2();
ctx = beginCheckProgrammerError("uiCheckControlType(): wrong uiControl type passed: got TestControl, want TestControl2");
uiCheckControlType(c, ctrlType);
endCheckProgrammerError(ctx);
uiControlFree(c);
}
Test(NewControlOfTypeControlIsProgrammerError)
{
uint32_t ctrlType;
void *ctx;
ctrlType = uiControlType();
ctx = beginCheckProgrammerError("uiNewControl(): uiControlType() passed in when specific control type needed");
uiNewControl(ctrlType, NULL);
endCheckProgrammerError(ctx);
}
Test(NewControlOfUnknownTypeIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiNewControl(): unknown uiControl type 0 requested");
uiNewControl(0, NULL);
endCheckProgrammerError(ctx);
}
Test(NewControlWithInvalidInitDataIsProgrammerError)
{
uint32_t ctrlType;
void *ctx;
ctrlType = testControlType();
ctx = beginCheckProgrammerError("uiNewControl(): invalid init data for TestControl");
uiNewControl(ctrlType, testControlFailInit);
endCheckProgrammerError(ctx);
}
Test(FreeingNullControlIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiControlFree(): invalid null pointer for uiControl");
uiControlFree(NULL);
endCheckProgrammerError(ctx);
}
Test(FreeingParentedControlIsProgrammerError)
{
uiControl *c, *d;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
ctx = beginCheckProgrammerError("uiControlFree(): cannot be called on a control with has a parent");
uiControlFree(c);
endCheckProgrammerError(ctx);
// cleanup
uiControlSetParent(c, NULL);
uiControlFree(d);
uiControlFree(c);
}
Test(SetParentWithNullControlIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiControlSetParent(): invalid null pointer for uiControl");
uiControlSetParent(NULL, NULL);
endCheckProgrammerError(ctx);
}
Test(RemovingParentFromInitiallyParentlessControlIsProgrammerError)
{
uiControl *c;
void *ctx;
c = uiNewControl(testControlType(), NULL);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with no parent to have no parent");
uiControlSetParent(c, NULL);
endCheckProgrammerError(ctx);
uiControlFree(c);
}
Test(RemovingParentFromExplicitlyParentlessControlIsProgrammerError)
{
uiControl *c, *d;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
uiControlSetParent(c, NULL);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with no parent to have no parent");
uiControlSetParent(c, NULL);
endCheckProgrammerError(ctx);
uiControlFree(d);
uiControlFree(c);
}
Test(ReparentingAlreadyParentedControlToDifferentParentIsProgrammerError)
{
uiControl *c, *d, *e;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
e = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with a parent to have another parent");
uiControlSetParent(c, e);
endCheckProgrammerError(ctx);
// cleanup
uiControlSetParent(c, NULL);
uiControlFree(e);
uiControlFree(d);
uiControlFree(c);
}
Test(ReparentingAlreadyParentedControlToSameParentIsProgrammerError)
{
uiControl *c, *d;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot set a control with a parent to have another parent");
uiControlSetParent(c, d);
endCheckProgrammerError(ctx);
// cleanup
uiControlSetParent(c, NULL);
uiControlFree(d);
uiControlFree(c);
}
Test(ControlParentCyclesDisallowed_TwoControls)
{
uiControl *c, *d;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
uiControlSetParent(d, c);
endCheckProgrammerError(ctx);
// cleanup
// TODO reformat all the other tests to have clear init, test, and cleanup sections, and also maybe remove these "// cleanup" comments
uiControlSetParent(c, NULL);
uiControlFree(d);
uiControlFree(c);
}
Test(ControlParentCyclesDisallowed_ThreeControls)
{
uiControl *c, *d, *e;
void *ctx;
c = uiNewControl(testControlType(), NULL);
d = uiNewControl(testControlType(), NULL);
e = uiNewControl(testControlType(), NULL);
uiControlSetParent(c, d);
uiControlSetParent(d, e);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
uiControlSetParent(e, c);
endCheckProgrammerError(ctx);
// cleanup
uiControlSetParent(d, NULL);
uiControlSetParent(c, NULL);
uiControlFree(e);
uiControlFree(d);
uiControlFree(c);
}
Test(ControlCannotBeItsOwnParent)
{
uiControl *c;
void *ctx;
c = uiNewControl(testControlType(), NULL);
ctx = beginCheckProgrammerError("uiControlSetParent(): cannot create a parent cycle");
uiControlSetParent(c, c);
endCheckProgrammerError(ctx);
uiControlFree(c);
}
Test(GettingImplDataOfNullControlIsProgrammerError)
{
void *ctx;
ctx = beginCheckProgrammerError("uiControlImplData(): invalid null pointer for uiControl");
uiControlImplData(NULL);
endCheckProgrammerError(ctx);
}