-- File [Ivy]<Nelson>Lupine>LupineMarshalTestDriver.mesa.
-- Last edited by BZM on 12-May-82 19:58:06.


DIRECTORY
    Environment USING [wordsPerPage],
    Heap USING [systemZone],
    LupineExerciser USING [
	Counter, BadExercise, Delay,
	Exercise, ExerciseProc, Handle, PerformExercises,
	Start, Stamp, Stop,
	StartPrecisionTiming, StopPrecisionTiming, String ],
    LupineMarshalTest --USING [ALL]--,
    Process USING [Detach],
    Rope USING [Flatten, Length, Text];


LupineMarshalTestDriver: PROGRAM
  IMPORTS Heap, LupineExerciser, Test: LupineMarshalTest, Process, Rope
  SHARES Rope
  = BEGIN OPEN LupineExerciser;



  PerformMarshalExercises: PROC =
    BEGIN
    -- PerformMarshalExercises must be forked so that all of the local constants
    -- remain valid in the process required by LupineExerciser.PerformExercises.
    Exercises: ARRAY [0..23) OF Exercise ← [
	[routine: TimingOverhead, name: "timing overhead"L],
	[routine: NullProcedure, name: "the null procedure"L],
	[routine: BitsToSequence, name: "packed descriptor to packed sequence"L],
	[routine: OneInteger, name: "1 integer procedure"L],
	[routine: FourIntegers, name: "4 integer procedure"L],
	[routine: TenIntegers, name: "10 integer procedure"L],
	[routine: TenArray, name: "10 integer array procedure"L],
	[routine: FortyArray, name: "40 integer array procedure"L],
	[routine: HundredArray, name: "100 integer array procedure"L],
	[routine: SignalReturn, name: "signal test (no signalling)"L],
	[routine: SignalResume, name: "signal test (SIGNAL & RESUME)"L],
	[routine: SignalUnwind, name: "signal test (ERROR & UNWIND)"L],
	[routine: SimpleArithmetic, name: "perform simple VAR arithmetic"L],
	[routine: FillArray, name: "fill a call-by-result array"L],
	[routine: Read1PageFastFrame, name: "transfer 1 page (fast frame)"L],
	[routine: Read1PageSlowFrame, name: "transfer 1 page (slow frame)"L],
	[routine: Read2Pages, name: "transfer 2 pages"L],
	[routine: Read4Pages, name: "transfer 4 pages"L],
	[routine: Read8Pages, name: "transfer 8 pages"L],
	[routine: CreateList, name: "create a list of integers"L],
	[routine: StringCopy, name: "copy a string to a VAR string"L],
	[routine: CharToVariantString, name: "variant record of string types"L],
	[routine: StringsToAtoms, name: "descriptor of strings to list of atoms"L]
									];
    -- Cause remote binding now, so the transient won't affect timings.
    Test.Null[];  Test.Null[];  Test.Null[];
    PerformExercises[
      nameOfExercisedInterface: "MarshalTest"L,
      exercises: DESCRIPTOR[Exercises] ];
    END;


  -- For periodically causing glitches (eg, NIL) that Lupine claims to handle.

  Periodically: PROC [test: Counter] RETURNS [--yes:-- BOOLEAN] =
    INLINE {RETURN[ (test MOD 31) = 0 ]};




  TimingOverhead: ExerciseProc =
    BEGIN
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	--exerciser.Delay;
	exerciser.StartPrecisionTiming;
	NULL;
	exerciser.StopPrecisionTiming;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  NullProcedure: ExerciseProc =
    BEGIN
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	Test.Null[];
	exerciser.StopPrecisionTiming;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  OneInteger: ExerciseProc =
    BEGIN
    a: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	a ← Test.One[test];
	exerciser.StopPrecisionTiming;
	IF checkResults AND a#test THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  FourIntegers: ExerciseProc =
    BEGIN
    a,b,c,d: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	[a,b,c,d] ← Test.Four[test+0,test+1,test+2,test+3];
	exerciser.StopPrecisionTiming;
	IF checkResults AND (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3) 
	  THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  TenIntegers: ExerciseProc =
    BEGIN
    a,b,c,d,e,f,g,h,i,j: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	[a,b,c,d,e,f,g,h,i,j] ← Test.Ten[
	    test+0,test+1,test+2,test+3,test+4,
	    test+5,test+6,test+7,test+8,test+9 ];
	exerciser.StopPrecisionTiming;
	IF checkResults AND
	    (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3 OR e#test+4
	     OR f#test+5 OR g#test+6 OR h#test+7 OR i#test+8 OR j#test+9)
	  THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  TenArray: ExerciseProc =
    BEGIN
    array10: Test.Array10;
    ten: Test.Array10 = ALL[10];
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	array10 ← Test.TenArray[ten];
	exerciser.StopPrecisionTiming;
	IF checkResults AND ten#array10 THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  FortyArray: ExerciseProc =
    BEGIN
    array40: Test.Array40;
    forty: Test.Array40 = ALL [40];
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	array40 ← Test.FortyArray[forty];
	exerciser.StopPrecisionTiming;
	IF checkResults AND forty#array40 THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  HundredArray: ExerciseProc =
    BEGIN
    array100: Test.Array100;
    hundred: Test.Array100 = ALL[100];
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	array100 ← Test.HundredArray[hundred];
	exerciser.StopPrecisionTiming;
	IF checkResults AND hundred#array100 THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  SignalReturn: ExerciseProc =
    BEGIN
    out: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	out ← Test.SignalTest[ in: test, action: neither
	  ! Test.Signal => {BadExercise; CONTINUE} ];
	exerciser.StopPrecisionTiming;
	IF checkResults AND out#test THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  SignalResume: ExerciseProc =
    BEGIN
    out: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	out ← Test.SignalTest[ in: test, action: signal
	  ! Test.Signal => {
	      IF checkResults AND in#test THEN BadExercise;
	      RESUME[out: 2*in] } ];
	exerciser.StopPrecisionTiming;
	IF checkResults AND out#2*test THEN BadExercise;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  SignalUnwind: ExerciseProc =
    BEGIN
    out: INTEGER;
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: Counter IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	out ← Test.SignalTest[ in: test, action: error
	  ! Test.Signal => {
	      IF checkResults AND in#test THEN BadExercise;
	      CONTINUE } ];
	exerciser.StopPrecisionTiming;
	--Checking is never valid after an ERROR since out is undefined.
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;


  SimpleArithmetic: ExerciseProc =
    BEGIN 
    sketchPad: Test.VARArithmetic;
    sketchPadRecord: Test.ArithmeticRecord;

    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	sketchPadRecord.in ← test;
	sketchPad ← IF Periodically[test] THEN NIL ELSE @sketchPadRecord;
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	Test.SimpleArithmetic[pad: sketchPad];
	exerciser.StopPrecisionTiming;
	IF checkResults AND ~Periodically[test] THEN BEGIN
	  IF sketchPad↑ # Test.ArithmeticRecord[test, test+1, test*2]
	    THEN BadExercise;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;


  FillArray: ExerciseProc =
    BEGIN
    MaxVectorLength: INTEGER = 4*252;  -- 252 is onePkt maximum.
    vector: Test.RESULTArray;
    vectorBody: ARRAY [0..MaxVectorLength) OF INTEGER;
    vectorLength: INTEGER;

    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	vectorLength ← test MOD (MaxVectorLength+1);
	vector ← IF Periodically[test]
	  THEN NIL ELSE DESCRIPTOR[BASE[vectorBody], vectorLength];
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	Test.FillArray[in: test, out: vector];
	exerciser.StopPrecisionTiming;
	IF checkResults AND ~Periodically[test] THEN BEGIN
	  IF vectorLength # LENGTH[vector] THEN BadExercise;
	  FOR i: INTEGER IN [0..vectorLength) DO
	    IF vector[i] # test-i THEN BadExercise; ENDLOOP;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  Read1PageFastFrame: ExerciseProc =
    {ReadPages[1, exerciser, name, trials, testsPerTrial, checkResults]};

  Read1PageSlowFrame: ExerciseProc =
    BEGIN
    GrabFrames: PROC [grab: INTEGER] =
      BEGIN
      grabSpace: ARRAY [0..350) OF WORD;
      IF grab > 0
        THEN {grabSpace ← ALL[NULL]; GrabFrames[grab-1]}
	ELSE ReadPages[
	  1, exerciser, name, trials, testsPerTrial, checkResults];
      END;
    GrabFrames[3];
    END;

  Read2Pages: ExerciseProc =
    {ReadPages[2, exerciser, name, trials, testsPerTrial, checkResults]};

  Read4Pages: ExerciseProc =
    {ReadPages[4, exerciser, name, trials, testsPerTrial, checkResults]};

  Read8Pages: ExerciseProc =
    {ReadPages[8, exerciser, name, trials, testsPerTrial, checkResults]};


  ReadPages: --Pages plus ExerciseProc-- PROCEDURE [
  	pages: Counter,
	exerciser: Handle,
	name: String,
	trials: Counter,
	testsPerTrial: Counter,
	checkResults: BOOLEAN ] =
    BEGIN
    Buffer: TYPE = RECORD[SEQUENCE words: CARDINAL OF Test.Item];
    bufferPtr: LONG POINTER TO Buffer ←
      Heap.systemZone.NEW[Buffer[pages*Environment.wordsPerPage]];
    buffer: Test.RESULTPages = DESCRIPTOR[bufferPtr↑];

    BEGIN ENABLE UNWIND => Heap.systemZone.FREE[@bufferPtr];
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	Test.ReadPages[item: test, buffer: buffer];
	exerciser.StopPrecisionTiming;
	IF checkResults THEN BEGIN
	  IF bufferPtr.words # LENGTH[buffer] THEN BadExercise;
	  FOR i: CARDINAL IN [0..LENGTH[buffer]) DO
	    IF buffer[i] # test THEN BadExercise; ENDLOOP;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;  -- ENABLE UNWIND.
    Heap.systemZone.FREE[@bufferPtr];
    END;

  CreateList: ExerciseProc =
    BEGIN
    MaxListLength: INTEGER = 2*127;  -- 127 is onePkt maximum.
    listLength: INTEGER;
    outList: Test.IntList;

    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	listLength ← test MOD (MaxListLength+1);
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	outList ← Test.CreateList[in: test, length: listLength];
	exerciser.StopPrecisionTiming;
	IF checkResults THEN BEGIN
	  outLength: INTEGER ← 0;
	  FOR int: Test.IntList ← outList, int.rest UNTIL int = NIL DO
	    IF int.first # test-outLength THEN BadExercise;
	    outLength ← outLength + 1;
	    ENDLOOP;
	  IF outLength # listLength THEN BadExercise;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  StringCopy: ExerciseProc =
    BEGIN
    MaxStringLength: CARDINAL = 4*Test.MaxReadonlyStringLength;
      -- MaxStringLength+1 (twoPkt threshold) tested 19-Feb-82 11:06:28.
    string: LONG STRING;
    inString: STRING = [MaxStringLength];
    outString: STRING = [MaxStringLength];

    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	inChar: CHARACTER = LOOPHOLE[test MOD 128];
	outChar: CHARACTER = LOOPHOLE[(test+11) MOD 128];
	outString.length ← MaxStringLength;
	inString.length ← test MOD (MaxStringLength+1);
	FOR i: CARDINAL IN [0..MaxStringLength) DO
	  inString[i] ← inChar;  outString[i] ← outChar;
	  ENDLOOP;
	string ← IF Periodically[test] THEN NIL ELSE inString;
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	Test.StringCopy[in: string, out: outString];
	exerciser.StopPrecisionTiming;
	IF checkResults THEN BEGIN
	  IF Periodically[test] THEN inString.length ← 0;
	  FOR i: CARDINAL IN [0..MaxStringLength) DO
	    IF outString[i] # (IF i < inString.length THEN inChar ELSE outChar)
	      THEN BadExercise;
	    ENDLOOP;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  CharToVariantString: ExerciseProc =
    BEGIN
    MaxStringLength: CARDINAL = 2*492;
      -- 492 is the onePkt TEXT maximum; 493 was checked 19-Feb-82 10:40:08.
    string: Test.StringSelection;
    ropeString: Test.StringSelection[rope] = [string: rope[]];
    textString: Test.StringSelection[text] = [string: text[]];

    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	char: CHARACTER = LOOPHOLE[test MOD 128];
	length: INTEGER = test MOD (MaxStringLength+1);
	type: Test.StringType = SELECT test MOD 13 FROM
	  0,5,8,9,11,12 => text, 1,2,4,6,7 => rope, 3,10 => nil, ENDCASE => ERROR;
	makeNil: BOOLEAN = Periodically[test];
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	string ← Test.CharToVariantString[
	  char: char, length: length, type: type, makeNil: makeNil ];
	exerciser.StopPrecisionTiming;
	IF checkResults THEN BEGIN
	  IF string.char # char OR string.length # length THEN BadExercise;
	  IF string.type # type THEN BadExercise;
	  WITH string: string SELECT FROM
	    nil => NULL;
	    rope =>
		BEGIN
		IF string.seal # ropeString.seal THEN BadExercise;
		IF ~makeNil
		  THEN BEGIN OPEN Rope;
		    textRope: Text;
		    IF Length[string.rope] # length THEN BadExercise;
		    textRope ← Flatten[string.rope];
		    FOR i: INTEGER IN [0..length) DO
		      IF textRope.text[i] # char THEN BadExercise; ENDLOOP;
		    END
		  ELSE IF string.rope # NIL THEN BadExercise;
		END;
	    text =>
		BEGIN
		IF string.seal # textString.seal THEN BadExercise;
		IF ~makeNil
		  THEN BEGIN
		    IF string.text.length # length THEN BadExercise;
		    FOR i: INTEGER IN [0..length) DO
		      IF string.text[i] # char THEN BadExercise; ENDLOOP;
		    END
		  ELSE IF string.text # NIL THEN BadExercise;
		END;
	    ENDCASE => ERROR;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;

  BitsToSequence: ExerciseProc =
    BEGIN
    MaxVectorLength: CARDINAL = 3*850;
    BitVector: TYPE = PACKED ARRAY [0..MaxVectorLength) OF Test.Bits;
    bitVectorPtr: LONG POINTER TO BitVector ← Heap.systemZone.NEW[BitVector];
    Pattern: PROC [i: INTEGER] RETURNS [Test.Bits] =
      INLINE {RETURN[ i MOD (LAST[Test.Bits]+1) ]};

    BEGIN ENABLE UNWIND => Heap.systemZone.FREE[@bitVectorPtr];
    exerciser.Start[name];
    THROUGH [0..trials) DO
      exerciser.Stamp;
      FOR test: INTEGER IN [0..testsPerTrial) DO
	length: INTEGER = test MOD (MaxVectorLength+1);
	bitsIn: Test.BitDescriptor = DESCRIPTOR[bitVectorPtr, length];
	bitsOut: REF READONLY Test.BitSequence;
	FOR i: INTEGER DECREASING IN (length..0] DO
	    bitsIn[i] ← Pattern[i];  ENDLOOP;
	exerciser.Delay;
	exerciser.StartPrecisionTiming;
	bitsOut ← Test.BitsToSequence[in: bitsIn];
	exerciser.StopPrecisionTiming;
	IF checkResults THEN BEGIN
	  IF bitsOut.length-1 # length THEN BadExercise;
	  FOR i: INTEGER IN [0..length) DO
	    IF i < LENGTH[bitsOut.fixed] AND
	      bitsOut.fixed[i+1] # bitsIn[i] THEN BadExercise;
	    IF bitsOut.dynamic[i+1] # bitsIn[i] THEN BadExercise;
	    ENDLOOP;
	  END;
	ENDLOOP;
      ENDLOOP;
    exerciser.Stop;
    END;  -- ENABLE UNWIND.
    Heap.systemZone.FREE[@bitVectorPtr];
    END;

  StringsToAtoms: ExerciseProc = {};



-- Module initialization.

  Process.Detach[ FORK PerformMarshalExercises[] ];
  

END.  -- LupineMarshalTestDriver.