FileStreamOneTestImpl: 
PROGRAM
IMPORTS
FileStreamTestInterface,
IO,
I: Basics,
PrincOpsUtils,
Process,
RAN: Random,
VM
 
EXPORTS
FileStreamTestInterface
 
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
StopChase: BOOL ← FALSE ;
realStream: STREAM;
randomDataBlockSize: CARDINAL = 1000;
dataBlockForRandomPutGetBlock: REF TEXT;
dataBlockForRandomUnsafePutGetBlock: I.UnsafeBlock ;
UnsafeBlockInterval: VM.Interval;
maxFileBytes: LONG CARDINAL ← VM.BytesForPages[140] + 1;
simFileInterval: VM.Interval;
simFileData: LONG POINTER; --TO PACKED ARRAY OF CHAR
simFileCharLength: LONG CARDINAL;
maxSimFileCharLength: LONG CARDINAL;
simStreamCharPointer: LONG CARDINAL;
MainTestCases: DESCRIPTOR FOR ARRAY OF ActionCase = DESCRIPTOR[MainTestCasesBody];
MainTestCasesBody: 
ARRAY [0..11) 
OF ActionCase ← [
[170, RandomPutChar, "RandomPutChar", 0],
[140, RandomGetChar, "RandomGetChar", 0],
[70, RandomPutBlock, "RandomPutBlock", 0],
[70, RandomGetBlock, "RandomGetBlock", 0],
[90, RandomUnsafePutBlock, "RandomUnsafePutBlock", 0],
[90, RandomUnsafeGetBlock, "RandomUnsafeGetBlock", 0],
[70, RandomSetPosition, "RandomSetPosition", 0],
[70, RandomGetPosition, "RandomGetPosition", 0],
[90, RandomLengthen, "RandomLengthen", 0],
[70, RandomShorten, "RandomShorten", 0],
[70, RandomGetLength, "RandomGetLength", 0]];
 
tick: LONG CARDINAL ← 37777777777B; --we begin with a Tick[] to make this 0
tickTarget: LONG CARDINAL ← 37777777777B;
ticksPerTest: LONG CARDINAL ← 4000;
tickLimit: LONG CARDINAL;
randomSeed: CARDINAL ← 12314B;
chaseCount: INT = 800000 ;
backupChaseCount: INT = 30000 ;
debugTrace: BOOL ← FALSE; 
GoSlow: BOOL ← FALSE;
Alarm: SIGNAL = CODE;
TickStop: SIGNAL = CODE;
RandomStream: RAN.RandomStream;
specific test code
OneTest: 
PUBLIC 
SAFE 
PROC [file: 
ROPE] = 
TRUSTED {
s: STREAM;
FileStreamTestInterface.dpy.PutF["\n\n\nOneTest: ticks will start at %gD\n\n", IO.card[tick]];
tickLimit ← tick + ticksPerTest;
FileStreamTestInterface.dpy.PutF["\ninitializing random numbers with %bB\n", IO.card[randomSeed]];
RandomStream ← RAN.Create[seed: randomSeed];
s ← FileStreamTestInterface.StreamOpen[fileName: file, accessOptions: create, createByteCount: maxFileBytes];
InitStreamModel[s, maxFileBytes];
Tick[];
WHILE tick < tickLimit 
DO
IF GoSlow THEN Process.Pause[6];
IF TryOneRandomAction[MainTestCases] THEN --did something-- Tick[];
ENDLOOP;
 
s.Close[];
s ← NIL ;
FileStreamTestInterface.dpy.PutF["\n\n\nOneTest: test completed\n\n\n"];
};
 
PrintStatistics: 
PROC = {
PrintCaseArrayStatistics[MainTestCases]
};
 
random actions
RandomPutChar: 
PROC [] 
RETURNS [
BOOL] = {
c: CHAR;
IF simStreamCharPointer >= maxSimFileCharLength THEN RETURN[FALSE];
SimPutChar[c ← LOOPHOLE[I.LowByte[RandomStream.NextInt[]]]];
RETURN[TRUE] };
 
RandomGetChar: 
PROC [] 
RETURNS [
BOOL] = {
c: CHAR;
IF simStreamCharPointer >= simFileCharLength THEN RETURN[FALSE];
c ← SimGetChar[];
RETURN[TRUE] };
 
RandomPutBlock: 
PROC [] 
RETURNS [
BOOL] = {
longBytesLeft: LONG CARDINAL ← maxSimFileCharLength - simStreamCharPointer;
maxBytesToXfer: 
NAT ← 
IF longBytesLeft >= randomDataBlockSize 
THEN randomDataBlockSize
ELSE I.LowHalf[longBytesLeft];
 
bytesToXfer: NAT ← RandomStream.ChooseInt[0, maxBytesToXfer];
FOR index: 
NAT 
IN [0..bytesToXfer) 
DO
bitbit: I.BYTE = RandomStream.ChooseInt[0, 377B];
dataBlockForRandomPutGetBlock[index] ← LOOPHOLE[bitbit, CHAR]
ENDLOOP;
 
dataBlockForRandomPutGetBlock.length ← bytesToXfer;
SimPutBlock[dataBlockForRandomPutGetBlock];
RETURN[TRUE] };
 
RandomUnsafePutBlock: 
PROC [] 
RETURNS [
BOOL] = {
longBytesLeft: LONG CARDINAL ← maxSimFileCharLength - simStreamCharPointer;
maxBytesToXfer: 
NAT ← 
IF longBytesLeft >= randomDataBlockSize 
THEN randomDataBlockSize
ELSE I.LowHalf[longBytesLeft];
 
bytesToXfer: NAT ← RandomStream.ChooseInt[0, maxBytesToXfer];
FOR index: 
NAT 
IN [0..bytesToXfer) 
DO
bitbit: I.BYTE = RandomStream.ChooseInt[0, 377B];
dataBlockForRandomUnsafePutGetBlock.base[index] ← bitbit;
ENDLOOP;
 
dataBlockForRandomUnsafePutGetBlock.count ← bytesToXfer;
SimUnsafePutBlock[dataBlockForRandomUnsafePutGetBlock];
RETURN[TRUE] };
 
RandomGetBlock: 
PROC [] 
RETURNS [
BOOL] = {
longBytesLeft: LONG CARDINAL ← simFileCharLength - simStreamCharPointer;
maxBytesToXfer: 
NAT ← 
IF longBytesLeft >= randomDataBlockSize 
THEN randomDataBlockSize
ELSE I.LowHalf[longBytesLeft];
 
bytesToXfer: NAT ← RandomStream.ChooseInt[0, maxBytesToXfer];
SimGetBlock[dataBlockForRandomPutGetBlock, bytesToXfer];
RETURN[TRUE] };
 
RandomUnsafeGetBlock: 
PROC [] 
RETURNS [
BOOL] = {
longBytesLeft: LONG CARDINAL ← simFileCharLength - simStreamCharPointer;
maxBytesToXfer: 
NAT ← 
IF longBytesLeft >= randomDataBlockSize 
THEN randomDataBlockSize
ELSE I.LowHalf[longBytesLeft];
 
bytesToXfer: NAT ← RandomStream.ChooseInt[0, maxBytesToXfer];
dataBlockForRandomUnsafePutGetBlock.count ← bytesToXfer ;
SimUnsafeGetBlock[dataBlockForRandomUnsafePutGetBlock, bytesToXfer];
RETURN[TRUE] };
 
RandomSetPosition: 
PROC [] 
RETURNS [
BOOL] = {
pos: 
LONG 
CARDINAL ←
LOOPHOLE[I.LongNumber[num[lowbits: RandomStream.NextInt[], highbits: RandomStream.NextInt[]]],
LONG CARDINAL] MOD (simFileCharLength + 1);
 
 
SimSetPosition[pos];
RETURN[TRUE] };
 
RandomGetPosition: 
PROC [] 
RETURNS [
BOOL] = {
pos: LONG CARDINAL ← SimGetPosition[];
RETURN[TRUE] };
 
RandomLengthen: 
PROC [] 
RETURNS [
BOOL] = {
increase: LONG CARDINAL;
maxIncrease: LONG CARDINAL ← maxSimFileCharLength - simFileCharLength;
IF maxIncrease = 0 THEN RETURN[FALSE];
increase ← 
LOOPHOLE[I.LongNumber[num[lowbits: RandomStream.NextInt[], highbits: RandomStream.NextInt[]]],
LONG CARDINAL] MOD maxIncrease;
 
SimSetLength[simFileCharLength + increase];
RETURN[TRUE] };
 
RandomShorten: 
PROC [] 
RETURNS [
BOOL] = {
newSize: LONG CARDINAL;
IF simFileCharLength = 0 THEN RETURN[FALSE];
newSize ← 
LOOPHOLE[I.LongNumber[num[lowbits: RandomStream.NextInt[], highbits: RandomStream.NextInt[]]],
LONG CARDINAL] MOD simFileCharLength;
 
SimSetLength[newSize];
RETURN[TRUE] };
 
RandomGetLength: 
PROC [] 
RETURNS [
BOOL] = {
len: LONG CARDINAL ← SimGetLength[];
RETURN[TRUE] };
 
basic model code
InitStreamModel: 
PROC [s: 
STREAM, maxFileBytes: 
LONG 
CARDINAL] = {
simFilePages: VM.PageCount = VM.PagesForBytes[maxFileBytes];
unsafePages: VM.PageCount = VM.PagesForBytes[randomDataBlockSize];
simFileInterval ← VM.Allocate[count: simFilePages];
simFileData ← VM.AddressForPageNumber[simFileInterval.page];
PrincOpsUtils.LongZero[where: simFileData, nwords: VM.WordsForPages[simFilePages]];
simFileCharLength ← 0;
simStreamCharPointer ← 0;
maxSimFileCharLength ← maxFileBytes;
realStream ← s;
dataBlockForRandomPutGetBlock ← 
NEW[
TEXT[randomDataBlockSize] ← [
length: 0, text: NULL]];
 
UnsafeBlockInterval ← VM.Allocate[count: unsafePages];
dataBlockForRandomUnsafePutGetBlock.base ←
VM.AddressForPageNumber[UnsafeBlockInterval.page];
 
};
 
following procedures affect model and also real stream
Pair: TYPE = MACHINE DEPENDENT RECORD [highHalf(0: 0..7): CHAR, lowHalf(0: 8..15): CHAR];
AssignToSimFile: 
PROC [i: 
LONG 
CARDINAL, c: 
CHAR] = 
INLINE {
ptr: LONG POINTER TO Pair ← LOOPHOLE[simFileData + i/2];
IF I.
BITAND[
LOOPHOLE[i, I.LongNumber[num]].lowbits,1] = 0 
THEN 
--high char
ptr.highHalf ← c
 
ELSE ptr.lowHalf ← c };
 
FetchFromSimFile: 
PROC [i: 
LONG 
CARDINAL] 
RETURNS [
CHAR] = 
INLINE {
ptr: LONG POINTER TO Pair ← LOOPHOLE[simFileData + i/2];
IF I.
BITAND[
LOOPHOLE[i, I.LongNumber[num]].lowbits,1] = 0 
THEN 
--high char
RETURN[ptr.highHalf]
 
ELSE RETURN[ptr.lowHalf] };
 
SimPutChar: 
PROC [c: 
CHAR] = {
IF c = 0C THEN c ← 1C;
IF simStreamCharPointer > simFileCharLength THEN Alarm[];
IF simStreamCharPointer = maxSimFileCharLength THEN Alarm[];
AssignToSimFile[simStreamCharPointer, c];
simStreamCharPointer ← simStreamCharPointer + 1;
IF simStreamCharPointer > simFileCharLength THEN simFileCharLength ← simStreamCharPointer;
Debug["PutChar"];
realStream.PutChar[c] };
 
SimGetChar: 
PROC 
RETURNS [c: 
CHAR] = {
fetchedC: CHAR;
IF simStreamCharPointer >= simFileCharLength THEN Alarm[];
c ← FetchFromSimFile[simStreamCharPointer];
simStreamCharPointer ← simStreamCharPointer + 1;
fetchedC ← realStream.GetChar[];
Debug["GetChar"];
IF c # 0C AND c # fetchedC THEN Alarm[] };
 
SimPutBlock: 
PROC [block: 
REF 
TEXT] = {
IF simStreamCharPointer + block.length > maxSimFileCharLength THEN Alarm[];
FOR I: 
NAT 
IN [0..block.length) 
DO
IF block[I] = 0C THEN block[I] ← 1C;
AssignToSimFile[simStreamCharPointer, block[I]];
simStreamCharPointer ← simStreamCharPointer + 1;
ENDLOOP;
 
IF simStreamCharPointer > simFileCharLength THEN simFileCharLength ← simStreamCharPointer;
Debug["PutBlock, nBytes = "];
DebugD[block.length];
realStream.PutBlock[block, 0, block.length];
};
 
SimUnsafePutBlock: 
PROC [unsafeBlock: I.UnsafeBlock] = {
count: LONG CARDINAL ← unsafeBlock.count ;
IF simStreamCharPointer + count > maxSimFileCharLength THEN Alarm[];
FOR I: 
INT 
IN [0..unsafeBlock.count) 
DO
IF unsafeBlock.base[I] = 0 THEN unsafeBlock.base[I] ← 1;
AssignToSimFile[simStreamCharPointer, LOOPHOLE[unsafeBlock.base[I]]];
simStreamCharPointer ← simStreamCharPointer + 1;
ENDLOOP;
 
IF simStreamCharPointer > simFileCharLength 
THEN
simFileCharLength ← simStreamCharPointer;
 
Debug["UnsafePutBlock, nBytes = "];
DebugD[unsafeBlock.count];
realStream.UnsafePutBlock[block: unsafeBlock];
};
 
SimGetBlock: 
PROC [block: 
REF 
TEXT, nChars: 
NAT] = {
c: CHAR;
charsLeft: LONG CARDINAL = simFileCharLength - simStreamCharPointer;
charsXferred: NAT;
nCharsToBeXferred: 
NAT =
MIN[IF charsLeft>=nChars THEN nChars ELSE I.LowHalf[charsLeft], block.maxLength]; 
 
Debug["GetBlock, nChars = "];
DebugD[nChars];
charsXferred ← realStream.GetBlock[block, 0, nChars];
IF charsXferred # 0 AND charsXferred # block.length THEN Alarm[];
IF charsXferred # nCharsToBeXferred THEN Alarm[];
FOR I: 
CARDINAL 
IN [0..charsXferred) 
DO
c ← FetchFromSimFile[simStreamCharPointer];
IF c # 0C AND c # block[I] THEN Alarm[];
simStreamCharPointer ← simStreamCharPointer + 1;
ENDLOOP };
 
 
SimUnsafeGetBlock: 
PROC [unsafeBlock: I.UnsafeBlock, nChars: 
NAT] = {
c: CHAR;
charsLeft: LONG CARDINAL = simFileCharLength - simStreamCharPointer;
charsXferred: NAT;
nCharsToBeXferred: 
NAT =
MIN[IF charsLeft>=nChars THEN nChars ELSE I.LowHalf[charsLeft], randomDataBlockSize]; 
 
Debug["UnsafeGetBlock, nChars = "];
DebugD[nChars];
charsXferred ← realStream.UnsafeGetBlock[block: unsafeBlock];
IF charsXferred # 0 AND charsXferred # unsafeBlock.count THEN Alarm[];
IF charsXferred # nCharsToBeXferred THEN Alarm[];
FOR I: 
CARDINAL 
IN [0..charsXferred) 
DO
c ← LOOPHOLE[FetchFromSimFile[simStreamCharPointer]];
IF c # 0C AND c # LOOPHOLE[unsafeBlock.base[I]] THEN Alarm[];
simStreamCharPointer ← simStreamCharPointer + 1;
ENDLOOP };
 
 
SimSetPosition: 
PROC [position: 
LONG 
CARDINAL] = {
IF position > simFileCharLength THEN Alarm[];
simStreamCharPointer ← position;
Debug["SetPosition to "];
DebugLD[position];
realStream.SetIndex[position] };
 
SimGetPosition: 
PROC 
RETURNS [
LONG 
CARDINAL] = {
pos: LONG CARDINAL;
Debug["GetPosition, it should be "];
DebugLD[simStreamCharPointer];
IF simStreamCharPointer # (pos ← realStream.GetIndex[]) THEN Alarm[];
RETURN[simStreamCharPointer] };
 
SimSetLength: 
PROC [length: 
LONG 
CARDINAL] = {
IF length > maxSimFileCharLength THEN Alarm[];
Debug["SetLength to "];
DebugLD[length];
realStream.SetLength[length];
FOR i: 
LONG 
CARDINAL 
IN [simFileCharLength..length) 
DO
AssignToSimFile[i, 0C]; -- make value undefined in new region
ENDLOOP;
 
simFileCharLength ← length;
simStreamCharPointer ← MIN[simStreamCharPointer, length] };
 
SimGetLength: 
PROC 
RETURNS [
LONG 
CARDINAL] = {
len: LONG CARDINAL;
Debug["GetLength, it should be "];
DebugLD[simFileCharLength];
IF simFileCharLength # (len ← realStream.GetLength[]) THEN Alarm[];
RETURN[simFileCharLength] };
 
Debug: 
PROC [r: 
ROPE] = {
IF debugTrace THEN FileStreamTestInterface.dpy.PutF["\nduring tick %g, %g", IO.card[tick], IO.rope[r]] };
 
DebugD: 
PROC [d: 
CARDINAL] = {
IF debugTrace THEN FileStreamTestInterface.dpy.PutF["%g", IO.card[d]] };
 
DebugLD: 
PROC [d: 
LONG 
CARDINAL] = {
IF debugTrace THEN FileStreamTestInterface.dpy.PutF["%g", IO.card[d]] };
 
testing support
ActionArray: TYPE = DESCRIPTOR FOR ARRAY OF ActionCase;
ActionCase: 
TYPE = 
RECORD [
prob: CARDINAL, -- probs must sum to 1000
action: PROC [] RETURNS [BOOL],
printName: ROPE,
count: LONG CARDINAL];
 
TryOneRandomAction: 
PUBLIC 
PROC [T: ActionArray]
RETURNS [didSomething: BOOL] = {
r: CARDINAL ← RandomStream.ChooseInt[0, 999];
FOR I: 
CARDINAL 
IN [0..
LENGTH[T]) 
DO
IF r < T[I].prob 
THEN {
IF T[I].action[] THEN { T[I].count ← T[I].count + 1; RETURN[TRUE] }
ELSE RETURN[FALSE] };
 
r ← r - T[I].prob;
ENDLOOP;
 
Alarm[] };