-- EditTestImpl.mesa
-- written by Bill Paxton, April 1981
-- last edit by Bill Paxton, 2-Nov-81 10:29:03
-- This package provides random testing for editing Text nodes
DIRECTORY
EditTest,
EditSpan,
TextEdit,
TextFind,
TextLooks,
TextLooksSupport,
UndoEvent,
RopeEdit,
GetTree,
TreeCheck,
TextNode,
EditNotify,
RunReader,
RopeReader,
Rope,
RopeInline,
CheckNode,
RandomCard,
RandomLongInt,
Inline;
EditTestImpl: PROGRAM
IMPORTS EditTest, EditSpan, TextEdit, RopeEdit, EditNotify, TextLooks, TextLooksSupport, TextFind,
RunReader, RopeReader, Rope, TextNode, UndoEvent, GetTree, TreeCheck,
CheckNode, RandomCard, RandomLongInt, Inline
EXPORTS EditTest =
BEGIN
OPEN
rI:Rope,
EditTest,
spanI:EditSpan,
editI:TextEdit,
findI:TextFind,
looksI:TextLooks,
looksSI:TextLooksSupport,
undoI:UndoEvent,
ropeI:RopeEdit,
nodeI:TextNode,
runrdrI:RunReader,
roperdrI:RopeReader,
checkI:CheckNode,
notify:EditNotify,
randC:RandomCard,
randLI:RandomLongInt;
alpha, beta: PUBLIC Node;
alphaTree, betaTree: PUBLIC Ref;
runrdr1, runrdr2: PUBLIC runrdrI.Ref;
roperdr1, roperdr2: PUBLIC roperdrI.Ref;
numTests: NAT = 28;
testCountArray: ARRAY [0..numTests) OF NAT;
-- number of times each test is executed by Run
testProcArray: ARRAY [0..numTests) OF PROC = [
ReplaceText, DeleteText, CopyText, MoveText, MoveTextOnto, TransposeText,
ReplaceByChar, InsertChar, AppendChar,
ReplaceByString, InsertString, AppendString,
ReplaceByRope, InsertRope, AppendRope,
Find, BackwardsFind,
ChangeLooks, AddLooks, RemoveLooks, SetLooks, ClearLooks,
PutGet, PutGetRope, GetBigFile, PutGetTree, PutGetToRope, PutGetToStream
];
numNodeTests: NAT = 11;
nodeTestCountArray: ARRAY [0..numNodeTests) OF NAT;
nodeTestProcArray: ARRAY [0..numNodeTests) OF PROC = [
ReplaceNodes, DeleteNodes, CopyNodes, MoveNodes, MoveNodesOnto, TransposeNodes,
SplitNode, MergeNodes, NestNodes, UnNestNodes, InsertNode
];
shortest: Offset = 20; -- don't let nodes get shorter than this
longest: Offset = 2000; -- don't let nodes get longer than this
-- ***** Looks Edit operations
noLooks: Looks = looksI.noLooks;
allLooks: Looks = looksI.allLooks;
ChangeLooks: PUBLIC PROC = {
node: Node ← PickNode[];
start, end: Offset;
rem, add: Looks;
rem ← PickLooks[]; add ← PickLooks[];
[start,end] ← PickTwo[node];
IF start=end THEN RETURN;
BeforeUndo[node];
spanI.ChangeLooks[TextSpan[node,start,end-start],rem,add,event];
CheckLooks[node,rem,add,start,end-start] };
AddLooks: PUBLIC PROC = {
node: Node ← PickNode[];
start, end: Offset;
add: Looks ← PickLooks[];
[start,end] ← PickTwo[node];
IF start=end THEN RETURN;
BeforeUndo[node];
spanI.AddLooks[TextSpan[node,start,end-start],add,event];
CheckLooks[node,noLooks,add,start,end-start] };
RemoveLooks: PUBLIC PROC = {
node: Node ← PickNode[];
start, end: Offset;
rem: Looks ← PickLooks[];
[start,end] ← PickTwo[node];
IF start=end THEN RETURN;
BeforeUndo[node];
spanI.RemoveLooks[TextSpan[node,start,end-start],rem,event];
CheckLooks[node,rem,noLooks,start,end-start] };
SetLooks: PUBLIC PROC = {
node: Node ← PickNode[];
start, end: Offset;
new: Looks ← PickLooks[];
[start,end] ← PickTwo[node];
IF start=end THEN RETURN;
BeforeUndo[node];
spanI.SetLooks[TextSpan[node,start,end-start],new,event];
CheckLooks[node,allLooks,new,start,end-start] };
ClearLooks: PUBLIC PROC = {
node: Node ← PickNode[];
start, end: Offset;
[start,end] ← PickTwo[node];
IF start=end THEN RETURN;
BeforeUndo[node];
spanI.ClearLooks[TextSpan[node,start,end-start],event];
CheckLooks[node,allLooks,noLooks,start,end-start] };
CheckLooks: PROC [
node: Node, remove, add: Looks, start, len: Offset] = {
out: Looks ← looksSI.LooksAND[remove,looksSI.LooksNOT[add]];
-- out contains looks that we are removing and not adding
loc, runLen: Offset ← start;
end: Offset ← start+len;
runs: Runs ← editI.GetRuns[node];
IF runs=NIL THEN {
IF add # noLooks THEN ERROR;
RETURN };
runrdrI.SetPosition[runrdr1,runs,start];
UNTIL loc >= end DO -- check the runs in the changed section
looks: Looks;
[runLen,looks] ← runrdrI.Get[runrdr1];
IF looksSI.LooksAND[looks,add] # add THEN ERROR;
IF looksSI.LooksAND[looks,out] # noLooks THEN ERROR;
loc ← loc+runLen;
ENDLOOP;
TestUndo[] };
-- ***** Display text node as string
nodeText: STRING ← [2000];
ShowNode: PROC [n: Node] RETURNS [s: STRING] = { -- for debugging
s ← nodeText; s.length ← 0; AppendNode[n,s,0] };
AppendNode: PROC [n: Node, s: STRING, indent: NAT] = {
THROUGH [0..indent) DO s[s.length] ← ' ; s.length ← s.length+1; ENDLOOP;
roperdrI.SetPosition[roperdr1,n.rope];
FOR i:Offset ← editI.Size[n], i-1 UNTIL i=0 DO
s[s.length] ← roperdrI.Get[roperdr1];
s.length ← s.length+1; ENDLOOP;
s[s.length] ← 'M-100B; s.length ← s.length+1;
FOR c:editI.Ref ← n.child, nodeI.Next[c] UNTIL c=NIL DO
WITH cc:c SELECT FROM
text => AppendNode[@cc,s,indent+4];
ENDCASE;
ENDLOOP };
-- ***** Find and BackwardsFind
backFindFlag: BOOLEAN ← FALSE;
BackwardsFind: PUBLIC PROC = {
DoFind: PROC = {
[found,at,atEnd,before,after] ←
findI.BackwardsFind[pattern: pattern, text: text,
literal: FALSE, word: FALSE,
ignoreLooks: FALSE, ignoreCase: TRUE,
looksExact: FALSE] };
pattern, text: Node;
found: BOOLEAN;
patternLooks, textLooks: Looks;
at, atEnd, before, after: Offset;
IF backFindFlag THEN RETURN;
backFindFlag ← TRUE; -- only do this once
text ← editI.FromString["abc DEF .ghi. xjkly lmn ghi"];
-- 0 5 0 5 0 5
-- first just simple test, no special characters
pattern ← editI.FromString["DEF"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- test for only lower case
pattern ← editI.FromString["'D'E'F"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- pattern in lowercase, target in upper case
pattern ← editI.FromString["def"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use # inside pattern
pattern ← editI.FromString["D#F"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use * inside pattern to match 0 characters
pattern ← editI.FromString["D*EF"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use * inside pattern to match 1 character
pattern ← editI.FromString["D*F"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use @ inside pattern
pattern ← editI.FromString["D@F"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use & inside pattern to match 0 characters
pattern ← editI.FromString["D&EF"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use & inside pattern to match 1 character
pattern ← editI.FromString["D&F"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at OR after#atEnd THEN ERROR;
-- use ! and {}'s
pattern ← editI.FromString["!{DEF}!"];
DoFind;
IF ~found OR at#4 OR atEnd#7 OR before#at-1 OR after#atEnd+1 THEN ERROR;
-- use word search
pattern ← editI.FromString["DEF"];
[found,at,atEnd,before,after] ← findI.BackwardsFind[pattern: pattern, text: text,
literal: FALSE, word: TRUE,
ignoreLooks: FALSE, ignoreCase: TRUE,
looksExact: FALSE];
IF ~found OR at#4 OR atEnd#7 OR before#at-1 OR after#atEnd+1 THEN ERROR;
-- use ~ and {}'s
pattern ← editI.FromString["DEF~{GHI}~x"];
DoFind;
IF ~found OR at#9 OR atEnd#12 THEN ERROR;
-- use | at start
pattern ← editI.FromString["|abc"];
DoFind;
IF ~found OR at#0 OR atEnd#3 THEN ERROR;
-- use | at end
pattern ← editI.FromString["ghi|"];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
-- use | at start and end
pattern ← editI.FromString["|abc*{ghi}|"];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
-- requires unwinding wildcard stack
pattern ← editI.FromString["b{YX}~YX&."];
text ← editI.FromString["bYX-YXaYXa."];
DoFind;
IF ~found OR at#1 OR atEnd#3 THEN ERROR;
-- test looks in patterns
patternLooks ← PickLooks[];
textLooks ← looksSI.LooksOR[PickLooks[],patternLooks];
text ← editI.FromString["abc DEF .ghi. xjkly lmn ghi"];
pattern ← editI.FromString["DEF"];
editI.AddLooks[pattern,patternLooks];
editI.AddLooks[text,textLooks,4,3];
DoFind;
IF ~found OR at#4 OR atEnd#7 THEN ERROR;
pattern ← editI.FromString["D#@"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#4 OR atEnd#7 THEN ERROR;
pattern ← editI.FromString["D*@"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#4 OR atEnd#7 THEN ERROR;
pattern ← editI.FromString["D&@"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#4 OR atEnd#7 THEN ERROR;
pattern ← editI.FromString["D@@"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#4 OR atEnd#7 THEN ERROR;
};
findFlag: BOOLEAN ← FALSE;
Find: PUBLIC PROC = {
DoFind: PROC = {
[found,at,atEnd,before,after] ←
findI.Find[pattern: pattern, text: text,
literal: FALSE, word: FALSE,
ignoreLooks: FALSE, ignoreCase: TRUE,
looksExact: FALSE] };
pattern, text: Node;
found: BOOLEAN;
patternLooks, textLooks: Looks;
at, atEnd, before, after: Offset;
IF findFlag THEN RETURN;
findFlag ← TRUE; -- only do this once
text ← editI.FromString["abc DEF .ghi. xjkly lmn ghi"];
-- 0 5 0 5 0 5
-- first just simple test, no special characters
pattern ← editI.FromString["ghi"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- test for only lower case
pattern ← editI.FromString["'g'h'i"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- pattern in uppercase, target in lower case
pattern ← editI.FromString["GHI"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use # inside pattern
pattern ← editI.FromString["G#I"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use * inside pattern to match 0 characters
pattern ← editI.FromString["G*HI"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use * inside pattern to match 1 character
pattern ← editI.FromString["G*I"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use @ inside pattern
pattern ← editI.FromString["G@I"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use & inside pattern to match 0 characters
pattern ← editI.FromString["G&HI"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use & inside pattern to match 1 character
pattern ← editI.FromString["G&I"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at OR after#atEnd THEN ERROR;
-- use ! and {}'s
pattern ← editI.FromString["!{GHI}!"];
DoFind;
IF ~found OR at#9 OR atEnd#12 OR before#at-1 OR after#atEnd+1 THEN ERROR;
-- use word search
pattern ← editI.FromString["GHI"];
[found,at,atEnd,before,after] ← findI.Find[pattern: pattern, text: text,
literal: FALSE, word: TRUE,
ignoreLooks: FALSE, ignoreCase: TRUE,
looksExact: FALSE];
IF ~found OR at#9 OR atEnd#12 OR before#at-1 OR after#atEnd+1 THEN ERROR;
-- use ~ and {}'s
pattern ← editI.FromString["DEF~{GHI}~x"];
DoFind;
IF ~found OR at#9 OR atEnd#12 THEN ERROR;
-- use | at start
pattern ← editI.FromString["|abc"];
DoFind;
IF ~found OR at#0 OR atEnd#3 THEN ERROR;
-- use | at end
pattern ← editI.FromString["ghi|"];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
-- use | at start and end
pattern ← editI.FromString["|abc*{ghi}|"];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
-- requires unwinding wildcard stack
pattern ← editI.FromString[".&XY~{XY}b"];
text ← editI.FromString[".aXYaXY-XYb"];
DoFind;
IF ~found OR at#8 OR atEnd#10 THEN ERROR;
-- test looks in patterns
patternLooks ← PickLooks[];
textLooks ← looksSI.LooksOR[PickLooks[],patternLooks];
text ← editI.FromString["abc DEF .ghi. xjkly lmn ghi"];
pattern ← editI.FromString["ghi"];
editI.AddLooks[pattern,patternLooks];
editI.AddLooks[text,textLooks,24,3];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
pattern ← editI.FromString["@#i"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
pattern ← editI.FromString["@*i"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
pattern ← editI.FromString["@&i"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
pattern ← editI.FromString["@@i"];
editI.AddLooks[pattern,patternLooks];
DoFind;
IF ~found OR at#24 OR atEnd#27 THEN ERROR;
};
-- ***** Support routines for node edits
LocateSpan: PUBLIC PROC [span: Span] RETURNS [start, len, size: Offset] = {
first: Ref ← span.start.node;
last: Ref ← span.end.node;
root: Ref ← nodeI.Root[first];
size ← 0; start ← len ← -1;
FOR n: Ref ← nodeI.FirstChild[root], nodeI.StepForward[n] DO
SELECT n FROM
NIL => RETURN;
first => { start ← size; IF n=last THEN len ← 1 };
last => len ← size-start+1;
ENDCASE;
size ← size+1;
ENDLOOP };
LocateNode: PUBLIC PROC [node: Ref] RETURNS [loc, size: Offset] = {
root: Ref ← nodeI.Root[node];
size ← 0; loc ← -1;
FOR n: Ref ← nodeI.FirstChild[root], nodeI.StepForward[n] DO
SELECT n FROM
NIL => RETURN;
node => loc ← size;
ENDCASE;
size ← size+1;
ENDLOOP };
PickTrees: PUBLIC PROC RETURNS [tree1, tree2: Ref] = {
RETURN [PickTree[], PickTree[]] };
PickTree: PUBLIC PROC RETURNS [tree: Ref] = {
RETURN [IF RandomBoolean[] THEN alphaTree ELSE betaTree] };
PickPlace: PUBLIC PROC RETURNS [Place] = {
RETURN [SELECT randLI.Choose[1,3] FROM
1 => before, 2 => after, 3 => child, ENDCASE => ERROR] };
PickNodeSpan: PUBLIC PROC [tree: Ref] RETURNS [span: Span, start, len, size: Offset] = {
end: Offset;
first, last: Ref;
IF (size ← TreeSize[tree]) = 0 THEN RETURN [nodeI.nullSpan, 0, 0, 0];
DO [start, end] ← ChooseTwo[0,size];
IF (len ← end-start) > 0 THEN EXIT;
ENDLOOP;
first ← FindFirstNode[tree, start];
last ← FindLastNode[first, len];
span ← nodeI.MakeNodeSpan[first, last] };
PickNodeLoc: PUBLIC PROC [tree: Ref] RETURNS [dest: Location, loc, size: Offset] = {
node: Ref;
IF (size ← TreeSize[tree]) = 0 THEN RETURN [nodeI.nullLocation, 0, 0];
loc ← Choose[0,size-1];
node ← FindTreeNode[tree, loc];
dest ← nodeI.MakeNodeLoc[node] };
FindTreeNode, FindFirstNode: PUBLIC PROC [tree: Ref, loc: Offset] RETURNS [node: Ref] = {
place: Offset ← 0;
FOR n: Ref ← nodeI.FirstChild[tree], nodeI.StepForward[n] DO
IF place=loc OR n=NIL THEN RETURN [n];
place ← place+1;
ENDLOOP };
FindLastNode: PUBLIC PROC [first: Ref, len: Offset] RETURNS [last: Ref] = {
IF len <= 0 THEN RETURN [NIL];
FOR n: Ref ← first, nodeI.StepForward[n] DO
IF (len←len-1)=0 OR n=NIL THEN RETURN [n];
ENDLOOP };
TreeSize: PUBLIC PROC [tree: Ref] RETURNS [size: Offset] = {
size ← 0;
FOR n: Ref ← nodeI.FirstChild[tree], nodeI.StepForward[n] UNTIL n=NIL DO
size ← size+1; ENDLOOP };
biggestTree: Offset = 200;
smallestTree: Offset = 50;
AdjustTree: PUBLIC PROC [tree: Ref, size: Offset] = {
DO SELECT size FROM
> biggestTree => size ← DeleteFromTree[tree, size, size-biggestTree];
< smallestTree => size ← InsertInTree[tree, size, smallestTree-size];
ENDCASE => RETURN;
ENDLOOP };
-- ***** Support routines
Choose: PUBLIC PROC [min,max: Offset] RETURNS [Offset] = {
IF min >= max THEN RETURN [max];
RETURN [SELECT randLI.Choose[1,8] FROM
1 => min, 2 => max, ENDCASE => LOOPHOLE[randLI.Choose[min,max]]] };
ChooseTwo: PUBLIC PROC [min,max: Offset] RETURNS [first,second: Offset] = {
temp: Offset;
first ← Choose[min,max]; second ← Choose[min,max];
IF first > second THEN { temp←first; first←second; second←temp }};
ChooseNAT: PUBLIC PROC [min,max: NAT] RETURNS [NAT] = {
maxNAT: NAT = LAST[NAT];
IF min >= max THEN RETURN [max];
RETURN [SELECT randLI.Choose[1,8] FROM
1 => min, 2 => max,
ENDCASE => LOOPHOLE[Inline.LowHalf[randLI.Choose[min,max]]]] };
ChooseTwoNATs: PUBLIC PROC [min,max: NAT] RETURNS [first,second: NAT] = {
temp: NAT;
first ← ChooseNAT[min,max]; second ← ChooseNAT[min,max];
IF first > second THEN { temp←first; first←second; second←temp }};
RandomBoolean: PUBLIC PROC RETURNS [BOOLEAN] =
{ RETURN [randC.Choose[0,1]=0] };
OneInN: PUBLIC PROC [n: NAT] RETURNS [BOOLEAN] =
{ RETURN [randC.Choose[1,n]=1] };
PickNode: PUBLIC PROC RETURNS [Node] = { RETURN [
IF RandomBoolean[] THEN alpha ELSE beta] };
PickNodes: PUBLIC PROC RETURNS [source,dest: Node] = {
RETURN [PickNode[], PickNode[]] };
PickOne: PUBLIC PROC [node: Node] RETURNS [loc: Offset] = {
-- picks a random places within node
RETURN [Choose[0,editI.Size[node]]] };
PickTwo: PUBLIC PROC [node: Node] RETURNS [start, end: Offset] = {
-- picks two random places within node
-- orders them such that start <= end
size, temp: Offset;
IF (size ← editI.Size[node]) = 0 THEN RETURN;
start ← Choose[0,size]; end ← Choose[0,size];
IF start > end THEN { temp←start; start←end; end←temp }};
PickLooks: PUBLIC PROC RETURNS [Looks] = {
RETURN [LOOPHOLE[randLI.Random[]]] };
PickRope: PUBLIC PROC RETURNS [Rope] = {
source: Node ← PickNode[];
sourceStart, sourceEnd: Offset;
[sourceStart,sourceEnd] ← PickTwo[source];
RETURN [ropeI.Substr[editI.GetRope[source],sourceStart,sourceEnd-sourceStart]] };
AdjustLengths: PUBLIC PROC = { AdjustLength[alpha]; AdjustLength[beta] };
AdjustLength: PUBLIC PROC [node: Node] = {
DO SELECT editI.Size[node] FROM
> longest => DeleteFromNode[node];
< shortest => InsertStringInNode[node];
ENDCASE => RETURN;
ENDLOOP };
event: PUBLIC Event ← undoI.Create[];
undoEvent: PUBLIC Event ← undoI.Create[];
txt1, txt2: Node;
t1Rope, t2Rope: rI.Ref;
t1Runs, t2Runs: looksI.Runs;
BeforeUndo: PUBLIC PROC [n1, n2: Node ← NIL] = {
txt1 ← n1;
t1Rope ← txt1.rope; t1Runs ← txt1.runs;
IF n2 = NIL OR n2 = n1 THEN txt2 ← NIL
ELSE { txt2 ← n2; t2Rope ← txt2.rope; t2Runs ← txt2.runs };
undoI.Reset[event] };
TestUndo: PUBLIC PROC = { --
t1RopeBefore, t2RopeBefore, t1RopeAfter, t2RopeAfter: rI.Ref;
t1RunsBefore, t2RunsBefore, t1RunsAfter, t2RunsAfter: looksI.Runs;
oldSize1, oldSize2, newSize1, newSize2: Offset;
node1, node2: nodeI.RefTextNode;
t1RopeBefore ← t1Rope; t2RopeBefore ← t2Rope; -- set by PreEdit
t1RunsBefore ← t1Runs; t2RunsBefore ← t2Runs; -- set by PreEdit
IF (node1 ← txt1) # NIL THEN { t1RopeAfter ← node1.rope; t1RunsAfter ← node1.runs };
IF (node2 ← txt2) # NIL THEN { t2RopeAfter ← node2.rope; t2RunsAfter ← node2.runs };
undoI.Reset[undoEvent];
undoI.Undo[event,undoEvent];
IF node1 # NIL THEN {
oldSize1 ← ropeI.Size[t1RopeBefore];
IF ropeI.Size[node1.rope] # oldSize1 THEN ERROR;
CheckRopes[node1.rope,0,t1RopeBefore,0,oldSize1];
CheckRuns[node1.runs,0,t1RunsBefore,0,oldSize1] };
IF node2 # NIL THEN {
oldSize2 ← ropeI.Size[t2RopeBefore];
IF ropeI.Size[node2.rope] # oldSize2 THEN ERROR;
CheckRopes[node2.rope,0,t2RopeBefore,0,oldSize2];
CheckRuns[node2.runs,0,t2RunsBefore,0,oldSize2] };
undoI.Undo[undoEvent]; -- undo the previous undo
IF node1 # NIL THEN {
newSize1 ← ropeI.Size[t1RopeAfter];
IF ropeI.Size[node1.rope] # newSize1 THEN ERROR;
CheckRopes[node1.rope,0,t1RopeAfter,0,newSize1];
CheckRuns[node1.runs,0,t1RunsAfter,0,newSize1] };
IF node2 # NIL THEN {
newSize2 ← ropeI.Size[t2RopeAfter];
IF ropeI.Size[node2.rope] # newSize2 THEN ERROR;
CheckRopes[node2.rope,0,t2RopeAfter,0,newSize2];
CheckRuns[node2.runs,0,t2RunsAfter,0,newSize2] }};
-- ***** Initialization
numEdits: PUBLIC NAT ← 0;
PreEdit: PROC [t1, t2: nodeI.Ref, change: REF READONLY notify.Change] = {
IF t1 = NIL THEN ERROR;
IF t1.kind # text THEN ERROR;
IF t2 # NIL AND t2.kind # text THEN ERROR };
PostEdit: PROC [t1, t2: nodeI.Ref, change: REF READONLY notify.Change] = {
OPEN checkI;
IF t1 # NIL THEN WITH x:t1 SELECT FROM
text => CheckTextNode[@x];
ENDCASE => ERROR;
IF t2 # NIL THEN WITH x:t2 SELECT FROM
text => CheckTextNode[@x];
ENDCASE => ERROR;
};
Flatten: PROC [text: Node] = { -- flatten if more than pieceMax pieces
pieceMax: Offset = 250;
oldRope: Rope ← text.rope;
oldRuns: Runs ← text.runs;
newRope: Rope;
newRuns: Runs;
oldSize: Offset ← ropeI.Size[oldRope];
ropePieces: Offset ← ropeI.CountPieces[oldRope];
IF ropePieces > pieceMax THEN {
text.rope ← newRope ← ropeI.Flatten[oldRope];
text.runs ← newRuns ← looksI.Flatten[oldRuns];
checkI.CheckTextNode[text];
CheckRopes[newRope,0,oldRope,0,oldSize];
CheckRuns[newRuns,0,oldRuns,0,oldSize];
text.count ← 0 }
ELSE { text.count ← text.count/2 }};
InitReaders: PROC = {
runrdr1 ← runrdrI.Create[]; runrdr2 ← runrdrI.Create[];
roperdr1 ← roperdrI.Create[]; roperdr2 ← roperdrI.Create[] };
InitNodes: PROC = {
alpha ← editI.FromRope[rI.FromString[
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"]];
beta ← editI.FromRope[rI.FromString[
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"]];
AdjustLengths[];
};
InitTrees: PROC = {
treeFileName: Rope ← rI.FromString["TestTree.Text"];
alphaTree ← GetTree.ReadIndent[treeFileName];
TreeCheck.Verify[alphaTree];
betaTree ← GetTree.ReadIndent[treeFileName];
TreeCheck.Verify[betaTree];
};
brkAt: NAT ← LAST[NAT];
testNum: NAT;
Run: PUBLIC PROC [shuffle: NAT ← 0, numberOfTests: NAT ← LAST[NAT],
mode: RunMode ← both] = {
[] ← randC.InitRandom[];
[] ← randLI.InitRandom[];
notify.AddNotifyProc[PreEdit,before];
notify.AddNotifyProc[PostEdit,after];
InitReaders[]; InitNodes[]; InitTrees[];
FOR i:CARDINAL IN [0..numTests) DO
testCountArray[i] ← 0; ENDLOOP;
FOR i:CARDINAL IN [0..numNodeTests) DO
nodeTestCountArray[i] ← 0; ENDLOOP;
THROUGH [0..shuffle) DO [] ← randC.Choose[0,numTests-1]; ENDLOOP;
UNTIL numEdits >= numberOfTests DO
IF mode=nodesOnly OR (mode=both AND RandomBoolean[]) THEN { -- do a node test this time
testNum ← randC.Choose[0,numNodeTests-1];
nodeTestProcArray[testNum][];
nodeTestCountArray[testNum] ← nodeTestCountArray[testNum]+1 }
ELSE { -- do a text test
testNum ← randC.Choose[0,numTests-1];
testProcArray[testNum][];
testCountArray[testNum] ← testCountArray[testNum]+1;
IF alpha.count >= 30 THEN Flatten[alpha];
IF beta.count >= 30 THEN Flatten[beta] };
IF (numEdits ← numEdits+1) >= brkAt THEN { -- break here
foo: NAT ← 0; foo ← foo+1 };
ENDLOOP };
END.