FileStreamOneTestImpl:
PROGRAM
IMPORTS
FileStreamTestInterface,
IO,
I: Basics,
PrincOpsUtils,
Process,
RAN: RandomCard,
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;
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]];
[] ← RAN.Init[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[RAN.Next[]]]];
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 ← RAN.Choose[0, maxBytesToXfer];
FOR
I:
NAT
IN [0..bytesToXfer)
DO
dataBlockForRandomPutGetBlock[I] ← LOOPHOLE[RAN.Next[],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 ← RAN.Choose[0, maxBytesToXfer];
FOR
I:
NAT
IN [0..bytesToXfer)
DO
dataBlockForRandomUnsafePutGetBlock.base[I] ← LOOPHOLE[RAN.Next[],CHAR]
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 ← RAN.Choose[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 ← RAN.Choose[0, maxBytesToXfer];
dataBlockForRandomUnsafePutGetBlock.count ← bytesToXfer ;
SimUnsafeGetBlock[dataBlockForRandomUnsafePutGetBlock, bytesToXfer];
RETURN[TRUE] };
RandomSetPosition:
PROC []
RETURNS [
BOOL] = {
pos:
LONG
CARDINAL ←
LOOPHOLE[
I.LongNumber[num[lowbits:
RAN.Next[], highbits:
RAN.Next[]]],
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:
RAN.Next[], highbits:
RAN.Next[]]],
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:
RAN.Next[], highbits:
RAN.Next[]]],
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] = 0C THEN unsafeBlock.base[I] ← 1C;
AssignToSimFile[simStreamCharPointer, 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 ← FetchFromSimFile[simStreamCharPointer];
IF c # 0C AND c # 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 ← RAN.Choose[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[] };