summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorapnadkarni <apnmbx-wits@yahoo.com>2022-07-17 17:00:52 (GMT)
committerapnadkarni <apnmbx-wits@yahoo.com>2022-07-17 17:00:52 (GMT)
commitbe522119abc546c0f97b40ce6396cb9b73f35041 (patch)
tree9f31a63262e680a5c242b1ad72009c6cf67bf5a7
parenta034ecec4fd9513f2d8df0636bdbca1118fc7938 (diff)
downloadtcl-be522119abc546c0f97b40ce6396cb9b73f35041.zip
tcl-be522119abc546c0f97b40ce6396cb9b73f35041.tar.gz
tcl-be522119abc546c0f97b40ce6396cb9b73f35041.tar.bz2
Another batch of white box listrep tests
-rw-r--r--generic/tclListObj.c66
-rw-r--r--tests/listRep.test288
2 files changed, 312 insertions, 42 deletions
diff --git a/generic/tclListObj.c b/generic/tclListObj.c
index 3a3c531..8419b4e 100644
--- a/generic/tclListObj.c
+++ b/generic/tclListObj.c
@@ -1447,7 +1447,7 @@ ListRepRange(
/* Take the opportunity to garbage collect */
/* TODO - we probably do not need the preserveSrcRep here unlike later */
if (!preserveSrcRep) {
- /* T:listrep-1.{4,5,8,9} */
+ /* T:listrep-1.{4,5,8,9},2.{4,5,6,7} */
ListRepFreeUnreferenced(srcRepPtr);
}
@@ -1505,9 +1505,7 @@ ListRepRange(
} else if (ListSpanMerited(rangeLen,
srcRepPtr->storePtr->numUsed,
srcRepPtr->storePtr->numAllocated)) {
- /*
- * Option 2 - because span would be most efficient
- */
+ /* Option 2 - because span would be most efficient */
ListSizeT spanStart = ListRepStart(srcRepPtr) + rangeStart;
if (!preserveSrcRep && srcRepPtr->spanPtr
&& srcRepPtr->spanPtr->refCount <= 1) {
@@ -1516,7 +1514,8 @@ ListRepRange(
srcRepPtr->spanPtr->spanLength = rangeLen;
*rangeRepPtr = *srcRepPtr;
} else {
- /* Span not present or is shared. T:listrep-1.5 */
+ /* Span not present or is shared. */
+ /* T:listrep-1.5,2.{5,7} */
rangeRepPtr->storePtr = srcRepPtr->storePtr;
rangeRepPtr->spanPtr = ListSpanNew(spanStart, rangeLen);
}
@@ -1527,10 +1526,12 @@ ListRepRange(
* is mandated.
*/
if (!preserveSrcRep) {
+ /* T:listrep-2.{5,7} */
ListRepFreeUnreferenced(rangeRepPtr);
}
} else if (preserveSrcRep || ListRepIsShared(srcRepPtr)) {
/* Option 3 - span or modification in place not allowed/desired */
+ /* T:listrep-2.{4,6} */
ListRepElements(srcRepPtr, numSrcElems, srcElems);
/* TODO - allocate extra space? */
ListRepInit(rangeLen,
@@ -1858,7 +1859,7 @@ Tcl_ListObjAppendList(
LIST_ASSERT(listRep.storePtr->numAllocated >= finalLen);
if (toLen) {
- /* T:listrep-2.2 */
+ /* T:listrep-2.{2,9} */
ObjArrayCopy(ListRepSlotPtr(&listRep, 0), toLen, toObjv);
}
ObjArrayCopy(ListRepSlotPtr(&listRep, toLen), elemCount, elemObjv);
@@ -2129,21 +2130,24 @@ Tcl_ListObjReplace(
/* Check Case (1) - Treat pure deletes from front or back as range ops */
if (numToInsert == 0) {
if (numToDelete == 0) {
- /* Should force canonical even for no-op */
- /* T:listrep-1.10 */
+ /*
+ * Should force canonical even for no-op. Remember Tcl_Obj unshared
+ * so OK to invalidate string rep
+ */
+ /* T:listrep-1.10,2.8 */
TclInvalidateStringRep(listObj);
return TCL_OK;
}
if (first == 0) {
/* Delete from front, so return tail. */
- /* T:listrep-1.{4,5} */
+ /* T:listrep-1.{4,5},2.{4,5} */
ListRep tailRep;
ListRepRange(&listRep, numToDelete, origListLen-1, 0, &tailRep);
ListObjReplaceRepAndInvalidate(listObj, &tailRep);
return TCL_OK;
} else if ((first+numToDelete) >= origListLen) {
/* Delete from tail, so return head */
- /* T:listrep-1.{8,9} */
+ /* T:listrep-1.{8,9},2.{6,7} */
ListRep headRep;
ListRepRange(&listRep, 0, first-1, 0, &headRep);
ListObjReplaceRepAndInvalidate(listObj, &headRep);
@@ -2161,7 +2165,7 @@ Tcl_ListObjReplace(
if (numToDelete == 0) {
/* Case (2a) - Append to list. */
if (first == origListLen) {
- /* T:listrep-1.11 */
+ /* T:listrep-1.11,2.9 */
return TclListObjAppendElements(
interp, listObj, numToInsert, insertObjs);
}
@@ -2217,7 +2221,7 @@ Tcl_ListObjReplace(
* ListObjReplaceAndInvalidate below.
*/
if (numFreeSlots < lenChange && !ListRepIsShared(&listRep)) {
- /* T:listrep-1.{1,3} */
+ /* T:listrep-1.{1,3,14,18} */
ListStore *newStorePtr =
ListStoreReallocate(listRep.storePtr, origListLen + lenChange);
if (newStorePtr == NULL) {
@@ -2255,24 +2259,24 @@ Tcl_ListObjReplace(
&newRep);
toObjs = ListRepSlotPtr(&newRep, 0);
if (leadSegmentLen > 0) {
- /* T:listrep-2.{2,3} */
+ /* T:listrep-2.{2,3,13,14,15} */
ObjArrayCopy(toObjs, leadSegmentLen, listObjs);
}
if (numToInsert > 0) {
- /* T:listrep-2.{1,2,3} */
+ /* T:listrep-2.{1,2,3,10,11,12,13,14,15} */
ObjArrayCopy(&toObjs[leadSegmentLen],
numToInsert,
insertObjs);
}
if (tailSegmentLen > 0) {
- /* T:listrep-2.{1,2,3} */
+ /* T:listrep-2.{1,2,3,10,11,12,13,14,15} */
ObjArrayCopy(&toObjs[leadSegmentLen + numToInsert],
tailSegmentLen,
&listObjs[leadSegmentLen+numToDelete]);
}
newRep.storePtr->numUsed = origListLen + lenChange;
if (newRep.spanPtr) {
- /* T:listrep-2.{1,2,3} */
+ /* T:listrep-2.{1,2,3,10,11,12,13,14,15} */
newRep.spanPtr->spanLength = newRep.storePtr->numUsed;
}
LISTREP_CHECK(&newRep);
@@ -2305,15 +2309,21 @@ Tcl_ListObjReplace(
* for objects to be inserted in case there is overlap. T:listobj-11.1
*/
if (numToInsert) {
- /* T:listrep-1.{1,3} */
+ /* T:listrep-1.{1,3,12,13,14,15,16,17,18} */
ObjArrayIncrRefs(insertObjs, 0, numToInsert);
}
if (numToDelete) {
- /* T:listrep-1.{6,7} */
+ /* T:listrep-1.{6,7,12,13,14,15,16,17,18} */
ObjArrayDecrRefs(listObjs, first, numToDelete);
}
/*
+ * TODO - below the moves are optimized but this may result in needing a
+ * span allocation. Perhaps for small lists, it may be more efficient to
+ * just move everything up front and save on allocating a span.
+ */
+
+ /*
* Calculate shifts if necessary to accomodate insertions.
* NOTE: all indices are relative to listObjs which is not necessarily the
* start of the ListStore storage area.
@@ -2324,7 +2334,7 @@ Tcl_ListObjReplace(
*/
if (lenChange == 0) {
- /* Exact fit */
+ /* T:listrep-1.{12,15}. Exact fit */
leadShift = 0;
tailShift = 0;
} else if (lenChange < 0) {
@@ -2334,12 +2344,12 @@ Tcl_ListObjReplace(
*/
if (leadSegmentLen > tailSegmentLen) {
/* Tail segment smaller. Insert after lead, move tail down */
- /* T:listrep-1.7 */
+ /* T:listrep-1.{7,17} */
leadShift = 0;
tailShift = lenChange;
} else {
/* Lead segment smaller. Insert before tail, move lead up */
- /* T:listrep-1.6 */
+ /* T:listrep-1.{6,13,16} */
leadShift = -lenChange;
tailShift = 0;
}
@@ -2388,7 +2398,7 @@ Tcl_ListObjReplace(
if (finalFreeSpace > 1 && (leadSpace == 0 || leadSegmentLen == 0)) {
ListSizeT postShiftTailSpace = tailSpace - lenChange;
if (postShiftTailSpace > (finalFreeSpace/2)) {
- /* T:listrep-1.{1,3} */
+ /* T:listrep-1.{1,3,14,18} */
ListSizeT extraShift = postShiftTailSpace - (finalFreeSpace / 2);
tailShift += extraShift;
leadShift = extraShift; /* Move head to the back as well */
@@ -2432,14 +2442,14 @@ Tcl_ListObjReplace(
if (leadShift > 0) {
/* Will happen when we have to make room at bottom */
if (tailShift != 0 && tailSegmentLen != 0) {
- /* T:listrep-1.{1,3} */
+ /* T:listrep-1.{1,3,14,18} */
ListSizeT tailStart = leadSegmentLen + numToDelete;
memmove(&listObjs[tailStart + tailShift],
&listObjs[tailStart],
tailSegmentLen * sizeof(Tcl_Obj *));
}
if (leadSegmentLen != 0) {
- /* T:listrep-1.{3,6} */
+ /* T:listrep-1.{3,6,16,18} */
memmove(&listObjs[leadShift],
&listObjs[0],
leadSegmentLen * sizeof(Tcl_Obj *));
@@ -2451,7 +2461,7 @@ Tcl_ListObjReplace(
leadSegmentLen * sizeof(Tcl_Obj *));
}
if (tailShift != 0 && tailSegmentLen != 0) {
- /* T:listrep-1.7 */
+ /* T:listrep-1.{7,17} */
ListSizeT tailStart = leadSegmentLen + numToDelete;
memmove(&listObjs[tailStart + tailShift],
&listObjs[tailStart],
@@ -2460,7 +2470,7 @@ Tcl_ListObjReplace(
}
if (numToInsert) {
/* Do NOT use ObjArrayCopy here since we have already incr'ed ref counts */
- /* T:listrep-1.{1,3} */
+ /* T:listrep-1.{1,3,12,13,14,15,16,17,18} */
memmove(&listObjs[leadSegmentLen + leadShift],
insertObjs,
numToInsert * sizeof(Tcl_Obj *));
@@ -2477,10 +2487,10 @@ Tcl_ListObjReplace(
} else {
/* Need a new span record */
if (listRep.storePtr->firstUsed == 0) {
- /* T:listrep-1.7 */
+ /* T:listrep-1.{7,12,15,17} */
listRep.spanPtr = NULL;
} else {
- /* T:listrep-1.{1,3,6} */
+ /* T:listrep-1.{1,3,13,14,16,18} */
listRep.spanPtr = ListSpanNew(listRep.storePtr->firstUsed,
listRep.storePtr->numUsed);
}
diff --git a/tests/listRep.test b/tests/listRep.test
index 654ae5d..203bb39 100644
--- a/tests/listRep.test
+++ b/tests/listRep.test
@@ -63,14 +63,22 @@ proc validate {l} {
testlistrep validate $l
}
proc leadSpaceMore {l} {
- expr {[leadSpace $l] >= 2*[tailSpace $l]}
+ set leadSpace [leadSpace $l]
+ expr {$leadSpace > 0 && $leadSpace >= 2*[tailSpace $l]}
}
proc tailSpaceMore {l} {
- expr {[tailSpace $l] >= 2*[leadSpace $l]}
+ set tailSpace [tailSpace $l]
+ expr {$tailSpace > 0 && $tailSpace >= 2*[leadSpace $l]}
}
proc spaceEqual {l} {
- # 1 if lead and tail space shared (diff of 1 at most)
- set diff [expr {[leadSpace $l] - [tailSpace $l]}]
+ # 1 if lead and tail space shared (diff of 1 at most) and more than 0
+ set leadSpace [leadSpace $l]
+ set tailSpace [tailSpace $l]
+ if {$leadSpace == 0 && $tailSpace == 0} {
+ # At least one must be positive
+ return 0
+ }
+ set diff [expr {$leadSpace - $tailSpace}]
return [expr {$diff >= -1 && $diff <= 1}]
}
proc hasSpan {l args} {
@@ -153,6 +161,7 @@ if {[testConstraint testlistrep]} {
# operations completely in byte code if indices are literals
set zero 0
set one 1
+set two 2
set four 4
set end end
@@ -238,7 +247,7 @@ test listrep-1.9 {
} -result [list [irange 0 994] 0 5 0]
test listrep-1.10 {
- lreplace no-op should force a canonical list representation
+ lreplace no-op on unshared list should force a canonical list representation
} -body {
lreplace { 1 2 3 4 } $zero -1
} -result {1 2 3 4}
@@ -248,22 +257,102 @@ test listrep-1.11 {
so no free space in front
} -body {
# Note $end, not end else byte code compiler short-cuts
- set l [lreplace [freeSpaceNone 1000] $end+1 $end+1 99]
+ set l [lreplace [freeSpaceNone 1000] $end+1 $end+1 1000]
list $l [leadSpace $l] [expr {[tailSpace $l] > 0}] [hasSpan $l]
-} -result [list [linsert [irange 0 999] end+1 99] 0 1 0]
+} -result [list [irange 0 1000] 0 1 0]
+
+test listrep-1.12 {
+ Replacement of elements at front with same number elements in unshared list
+ is in-place
+} -body {
+ set l [lreplace [freeSpaceNone] $zero $one 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {10 11 2 3 4 5 6 7} 0 0]
+
+test listrep-1.13 {
+ Replacement of elements at front with fewer elements in unshared list
+ results in a spanned list with space only in front
+} -body {
+ set l [lreplace [freeSpaceNone] $zero $four 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {10 5 6 7} 4 0]
+
+test listrep-1.14 {
+ Replacement of elements at front with more elements in unshared list
+ results in a reallocated spanned list with space at front and back
+} -body {
+ set l [lreplace [freeSpaceNone] $zero $one 10 11 12]
+ list $l [spaceEqual $l]
+} -result [list {10 11 12 2 3 4 5 6 7} 1]
+
+test listrep-1.15 {
+ Replacement of elements in middle with same number elements in unshared list
+ is in-place
+} -body {
+ set l [lreplace [freeSpaceNone] $one $two 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 10 11 3 4 5 6 7} 0 0]
+
+test listrep-1.16 {
+ Replacement of elements in front half with fewer elements in unshared list
+ results in a spanned list with space only in front since smaller segment moved
+} -body {
+ set l [lreplace [freeSpaceNone] $one $four 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 10 5 6 7} 3 0]
+
+test listrep-1.17 {
+ Replacement of elements in back half with fewer elements in unshared list
+ results in a spanned list with space only at back
+} -body {
+ set l [lreplace [freeSpaceNone] end-$four end-$one 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 10 7} 0 3]
+
+test listrep-1.18 {
+ Replacement of elements in middle more elements in unshared list
+ results in a reallocated spanned list with space at front and back
+} -body {
+ set l [lreplace [freeSpaceNone] $one $two 10 11 12]
+ list $l [spaceEqual $l]
+} -result [list {0 10 11 12 3 4 5 6 7} 1]
+
+test listrep-1.19 {
+ Replacement of elements at back with same number elements in unshared list
+ is in-place
+} -body {
+ set l [lreplace [freeSpaceNone] $end-1 $end 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 10 11} 0 0]
+
+test listrep-1.20 {
+ Replacement of elements at back with fewer elements in unshared list
+ is in-place with space only at the back
+} -body {
+ set l [lreplace [freeSpaceNone] $end-2 $end 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 10} 0 2]
+
+test listrep-1.21 {
+ Replacement of elements at back with more elements in unshared list
+ allocates new representation with equal space at front and back
+} -body {
+ set l [lreplace [freeSpaceNone] $end-1 $end 10 11 12]
+ list $l [spaceEqual $l]
+} -result [list {0 1 2 3 4 5 10 11 12} 1]
#
-# listrep-2.* tests all operate on shared lists with no free space
-# The lrange construct on an variable's value will result in a listrep
-# that is shared (it's not enough that the Tcl_Obj is shared so just
-# assigning to another variable does not suffice)
+# listrep-2.* tests all operate on shared list reps with no free space. Note the
+# *list internal rep* must be shared, not only the Tcl_Obj so just assigning to
+# another variable does not suffice. The lrange construct on an variable's value
+# will do the needful.
test listrep-2.1 {
Inserts in front of shared list with no free space should reallocate with
more leading space in front
} -constraints testlistrep -body {
set a [freeSpaceNone]
- set b [lrange $a 0 end]
+ set b [lrange $a 0 end]; # Ensure shared listrep
set l [linsert $b $zero 99]
validate $l
list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
@@ -274,7 +363,7 @@ test listrep-2.2 {
more leading space in back
} -constraints testlistrep -body {
set a [freeSpaceNone]
- set b [lrange $a 0 end]
+ set b [lrange $a 0 end]; # Ensure shared listrep
set l [linsert $b $end 99]
validate $l
list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
@@ -285,12 +374,183 @@ test listrep-2.3 {
equal spacing
} -constraints testlistrep -body {
set a [freeSpaceNone]
- set b [lrange $a 0 end]
+ set b [lrange $a 0 end]; # Ensure shared listrep
set l [linsert $b $four 99]
validate $l
list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
} -result [list 2 {0 1 2 3 99 4 5 6 7} 1 1]
+test listrep-2.4 {
+ Deletes from front of small shared list with no free space should
+ allocate new list of exact size
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $zero $zero]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list 2 {1 2 3 4 5 6 7} 0 0 1]
+
+test listrep-2.5 {
+ Deletes from front of large shared list with no free space should
+ create span
+} -constraints testlistrep -body {
+ set a [freeSpaceNone 1000]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $zero $zero]
+ validate $l
+ # The listrep store should be shared among a, b, l (3 refs)
+ list [repStoreRefCount $b] $l [hasSpan $l] [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list 3 [irange 1 999] 1 0 0 3]
+
+test listrep-2.6 {
+ Deletes from back of small shared list with no free space should
+ allocate new list of exact size
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $end $end]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list 2 {0 1 2 3 4 5 6} 0 0 1]
+
+test listrep-2.7 {
+ Deletes from back of large shared list with no free space should
+ use a span
+} -constraints testlistrep -body {
+ set a [freeSpaceNone 1000]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $end $end]
+ validate $l
+ # Note lead and tail space is 0 because original list store in a,b is used
+ list [repStoreRefCount $b] $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list 3 [irange 0 998] 0 0 3]
+
+test listrep-2.8 {
+ lreplace no-op on shared list should force a canonical list representation
+ with original unchanged
+} -body {
+ set l { 1 2 3 4 }
+ list [lreplace $l $zero -1] $l
+} -result [list {1 2 3 4} { 1 2 3 4 }]
+
+test listrep-2.9 {
+ Appends to back of large shared list with no free space allocates new
+ list with space only at the back.
+} -constraints testlistrep -body {
+ set a [freeSpaceNone 1000]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $end+1 $end+1 1000]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpace $l] [expr {[tailSpace $l]>0}] [repStoreRefCount $l]
+} -result [list 2 [irange 0 1000] 0 1 1]
+
+test listrep-2.10 {
+ Replacement of elements at front with same number elements in shared list
+ results in a new list store with more space in front than back
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $zero $one 10 11]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {10 11 2 3 4 5 6 7} 1 1]
+
+test listrep-2.11 {
+ Replacement of elements at front with fewer elements in shared list
+ results in a new list store with more space in front than back
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $zero $four 10]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {10 5 6 7} 1 1]
+
+test listrep-2.12 {
+ Replacement of elements at front with more elements in shared list
+ results in a new spanned list with more space in front
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $zero $one 10 11 12]
+ validate $l
+ list [repStoreRefCount $b] $l [leadSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {10 11 12 2 3 4 5 6 7} 1 1]
+
+test listrep-2.13 {
+ Replacement of elements in middle with same number elements in shared list
+ results in a new list store with equal space in front and back
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $one $two 10 11]
+ validate $l
+ list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
+} -result [list 2 {0 10 11 3 4 5 6 7} 1 1]
+
+test listrep-2.14 {
+ Replacement of elements in middle with fewer elements in shared list
+ results in a new list store with equal space
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $one 5 10]
+ validate $l
+ list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
+} -result [list 2 {0 10 6 7} 1 1]
+
+test listrep-2.15 {
+ Replacement of elements in middle with more elements in shared list
+ results in a new spanned list with space in front and back
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b $one $two 10 11 12]
+ validate $l
+ list [repStoreRefCount $b] $l [spaceEqual $l] [repStoreRefCount $l]
+} -result [list 2 {0 10 11 12 3 4 5 6 7} 1 1]
+
+test listrep-2.16 {
+ Replacement of elements at back with same number elements in shared list
+ results in a new list store with more space in back than front
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b end-$one $end 10 11]
+ validate $l
+ list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {0 1 2 3 4 5 10 11} 1 1]
+
+test listrep-2.17 {
+ Replacement of elements at back with fewer elements in shared list
+ results in a new list store with more space in back than front
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b end-$four $end 10]
+ validate $l
+ list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {0 1 2 10} 1 1]
+
+test listrep-2.18 {
+ Replacement of elements at back with more elements in shared list
+ results in a new list store with more space in back than front
+} -constraints testlistrep -body {
+ set a [freeSpaceNone]
+ set b [lrange $a 0 end]; # Ensure shared listrep
+ set l [lreplace $b end-$four $end 10]
+ validate $l
+ list [repStoreRefCount $b] $l [tailSpaceMore $l] [repStoreRefCount $l]
+} -result [list 2 {0 1 2 10} 1 1]
+
+
+# TBD - tests on spanned lists
+# TBD - tests when tcl-obj is shared but listrep is not (lappend, lset etc.)
+# TBD - range and subrange tests
+# - spanned and unspanned
#
+# Special case - nested lremove (does seem tested even in 8.6)
+
::tcltest::cleanupTests
return