diff --git a/doc/controls.md b/doc/controls.md index 65f118b2..95845f16 100644 --- a/doc/controls.md +++ b/doc/controls.md @@ -69,6 +69,8 @@ This function is intended to be used to implement a macro that converts an arbit #define uiButton(c) ((uiButton *) uiCheckControlType((c), uiButtonType())) ``` +(TODO document passing uiControlType() to this, or even make doing so unnecessary) + ### `uiNewControl()` ```c @@ -109,7 +111,7 @@ This function is used by the implementation of a container control to actually e This function can only be used to set the parent of an unparented control or to remove its parent. It may not be used to change the parent of an already parented control. It is a programmer error to set the parent of a control that already has a parent to something other than `NULL` (even if to the same parent), or to set the parent of a control with no parent to `NULL`. (The idea here is to reinforce the concept of container implementations being responsible for setting their children properly, not the user.) -It is a programmer error to pass `NULL` or a non-control for `c`. +It is a programmer error to pass `NULL` for `c`. (TODO non-uiControl for either c or parent?) It is a programmer error to introduce a cycle when changing the parent of a control. By extension, it is a programmer error to make a control its own parent. @@ -130,4 +132,4 @@ void *uiControlImplData(uiControl *c); This function is meant to be used by control implementations only. There is in general no guarantee as to the size or format of this pointer. Normal users should not call `uiControlImplData()`. -It is a programmer error to pass `NULL` or a non-`uiControl` for `c`. +It is a programmer error to pass `NULL` for `c`. (TODO non-uiControl?) diff --git a/test/controls.c b/test/controls.c index 7663d4e0..9f46e108 100644 --- a/test/controls.c +++ b/test/controls.c @@ -21,6 +21,7 @@ Test(ControlImplDataIsClearedOnNewControl) uiControl *c; char *implData; + memset(&vt, 0, sizeof (uiControlVtable)); vt.Size = sizeof (uiControlVtable); vt.Init = vtableNopInit; vt.Free = vtableNopFree; @@ -33,6 +34,23 @@ Test(ControlImplDataIsClearedOnNewControl) uiControlFree(c); } +Test(ZeroSizeImplDataIsNULL) +{ + uiControlVtable vt; + uint32_t type; + uiControl *c; + + memset(&vt, 0, sizeof (uiControlVtable)); + vt.Size = sizeof (uiControlVtable); + vt.Init = vtableNopInit; + vt.Free = vtableNopFree; + 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; @@ -399,6 +417,66 @@ Test(ReparentingAlreadyParentedControlToSameParentIsProgrammerError) endCheckProgrammerError(ctx); } +Test(ControlParentCyclesDisallowed_TwoControls) +{ + uiControl *c, *d; + void *ctx; + + ctx = beginCheckProgrammerError("TODO"); + + c = uiNewControl(testControlType(), NULL); + d = uiNewControl(testControlType(), NULL); + + // this should fail + uiControlSetParent(c, d); + uiControlSetParent(d, c); + + // this should not (cleanup) + uiControlSetParent(c, NULL); + uiControlFree(d); + uiControlFree(c); + + endCheckProgrammerError(ctx); +} + +Test(ControlParentCyclesDisallowed_ThreeControls) +{ + uiControl *c, *d, *e; + void *ctx; + + ctx = beginCheckProgrammerError("TODO"); + + c = uiNewControl(testControlType(), NULL); + d = uiNewControl(testControlType(), NULL); + e = uiNewControl(testControlType(), NULL); + + // this should fail + uiControlSetParent(c, d); + uiControlSetParent(d, e); + uiControlSetParent(e, c); + + // this should not (cleanup) + uiControlSetParent(d, NULL); + uiControlSetParent(c, NULL); + uiControlFree(e); + uiControlFree(d); + uiControlFree(c); + + endCheckProgrammerError(ctx); +} + +Test(ControlCannotBeItsOwnParent) +{ + uiControl *c; + void *ctx; + + ctx = beginCheckProgrammerError("TODO"); + c = uiNewControl(testControlType(), NULL); + uiControlSetParent(c, c); + uiControlFree(c); + endCheckProgrammerError(ctx); +} + Test(GettingImplDataOfNullControlIsProgrammerError) { void *ctx;