DIRECTORY BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds], Commander USING [Register, CommandProc], Convert USING [AppendChar, RopeFromInt], IO USING [STREAM, rope, PutF, PutFR, int, card, text, atom], LoganBerry, PrintEnglish USING [IntToEnglish], Random USING [RandomStream, Create, ChooseInt], Rope USING [ROPE, Equal] ; LoganBerryTest: CEDAR PROGRAM IMPORTS BasicTime, Commander, Convert, IO, LoganBerry, PrintEnglish, Random, Rope ~ BEGIN ROPE: TYPE ~ Rope.ROPE; dbFileName: ROPE _ "TestDB.schema"; database: LoganBerry.OpenDB; rs: Random.RandomStream_ Random.Create[range: 10000, seed: -1]; TestCase: TYPE = {create, enumerate, generate, deleteA, deleteB, replaceA, replaceB, readRandom, checkData, closeAndOpen, bidirectionalGenerate, invalidOps, compactA, compactB}; dbSize: CARDINAL _ 1000; -- size of test database, should be less than 10000 numReads: CARDINAL _ 100; numDeletes: CARDINAL _ 10; savedEntries: LIST OF LoganBerry.Entry; RunTest: PROC [n: TestCase, out: IO.STREAM _ NIL] RETURNS [failed: BOOLEAN _ FALSE] ~ { ENABLE LoganBerry.Error => { IF out # NIL THEN out.PutF["Error from LoganBerry: %g - %g\n", IO.atom[ec], IO.rope[explanation]]; failed _ TRUE; }; start, stop: BasicTime.Pulses; attr: LoganBerry.Attribute; atype: LoganBerry.AttributeType; avalue: LoganBerry.AttributeValue; entry: LoganBerry.Entry; cursor: LoganBerry.Cursor; i: INT; r: ROPE; count: CARDINAL; start _ BasicTime.GetClockPulses[]; SELECT n FROM $create => { -- build a LoganBerry test database in random order IF out # NIL THEN out.PutF["Creating new test database with %g entries.\n", IO.int[dbSize]]; MarkKeysUnused[1, dbSize]; THROUGH [1..dbSize] DO i _ GenerateUnusedKey[1, dbSize]; entry _ LIST[[$Integer, Convert.RopeFromInt[i]], [$Rope, PrintEnglish.IntToEnglish[i]]]; LoganBerry.WriteEntry[db: database, entry: entry]; ENDLOOP; }; $enumerate => { -- enumerate the database to get the number of entries AddOne: LoganBerry.EntryProc = { count _ count + 1; RETURN[TRUE]; }; IF out # NIL THEN out.PutF["Enumerating entries.\n"]; count _ 0; LoganBerry.EnumerateEntries[db: database, key: $Integer, proc: AddOne]; IF count # dbSize AND out # NIL THEN out.PutF["Warning: count # standard database size.\n"]; dbSize _ count; IF out # NIL THEN out.PutF["Test database contains %g entries.\n", IO.card[dbSize]]; }; $generate => { -- generate the complete database and see if the number is correct IF out # NIL THEN out.PutF["Generating entries.\n"]; count _ 0; cursor _ LoganBerry.GenerateEntries[db: database, key: $Integer]; entry _ LoganBerry.NextEntry[cursor: cursor]; UNTIL entry = NIL DO count _ count + 1; entry _ LoganBerry.NextEntry[cursor: cursor]; ENDLOOP; LoganBerry.EndGenerate[cursor: cursor]; IF count # dbSize THEN { IF out # NIL THEN out.PutF["Error: count # database size.\n"]; failed _ TRUE; }; }; $deleteA => { -- delete some entries IF out # NIL THEN out.PutF["Reading and deleting %g entries.\n", IO.card[numDeletes]]; MarkKeysUnused[1, dbSize]; savedEntries _ NIL; THROUGH [1..numDeletes] DO i _ GenerateUnusedKey[1, dbSize]; avalue _ Convert.RopeFromInt[i]; entry _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry; savedEntries _ CONS[entry, savedEntries]; LoganBerry.DeleteEntry[db: database, key: $Integer, value: avalue]; ENDLOOP; }; $deleteB => { -- verify that the deleted entries can not be read IF out # NIL THEN out.PutF["Verifying deletes.\n"]; THROUGH [1..3] DO i _ GenerateUsedKey[1, dbSize]; avalue _ Convert.RopeFromInt[i]; IF LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry # NIL THEN { IF out # NIL THEN out.PutF["Error: sucessfully read deleted entry.\n"]; failed _ TRUE; }; ENDLOOP; }; $replaceA => { -- replace the deleted entries IF out # NIL THEN out.PutF["Rewriting deleted entries.\n"]; WHILE savedEntries # NIL DO LoganBerry.WriteEntry[db: database, entry: savedEntries.first]; savedEntries _ savedEntries.rest; ENDLOOP; }; $replaceB => { -- verify that the replaced entries can be read IF out # NIL THEN out.PutF["Verifying rewrites.\n"]; THROUGH [1..3] DO i _ GenerateUsedKey[1, dbSize]; avalue _ Convert.RopeFromInt[i]; IF LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry = NIL THEN { IF out # NIL THEN out.PutF["Error: unable to read replaced entry.\n"]; failed _ TRUE; }; ENDLOOP; }; $readRandom => { -- read some random entries IF out # NIL THEN out.PutF["Reading %g entries.\n", IO.card[numReads]]; THROUGH [1..numReads] DO i _ Random.ChooseInt[rs, 1, dbSize]; avalue _ Convert.RopeFromInt[i]; IF LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry = NIL THEN { IF out # NIL THEN out.PutF["Error: unable to read entry #%g.\n", IO.rope[avalue]]; failed _ TRUE; }; ENDLOOP; }; $checkData => { -- check the consistency of the data IF out # NIL THEN out.PutF["Checking data.\n"]; THROUGH [1..10] DO i _ Random.ChooseInt[rs, 1, dbSize]; avalue _ Convert.RopeFromInt[i]; entry _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry; avalue _ GetAttributeValue[entry, $Rope]; IF NOT Rope.Equal[avalue, PrintEnglish.IntToEnglish[i]] THEN { IF out # NIL THEN out.PutF["Error: $Rope value # expected value.\n"]; failed _ TRUE; }; ENDLOOP; }; $closeAndOpen => { -- close and reopen the database caughtError: BOOLEAN _ FALSE; IF out # NIL THEN out.PutF["Closing and reopening the database.\n"]; i _ Random.ChooseInt[rs, 1, dbSize]; avalue _ Convert.RopeFromInt[i]; LoganBerry.Close[db: database]; [] _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue ! LoganBerry.Error => { caughtError _ TRUE; IF ec = $DBClosed THEN CONTINUE ELSE REJECT}; ]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to read after close.\n"]; failed _ TRUE; }; database _ LoganBerry.Open[dbName: dbFileName]; IF LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry = NIL THEN { IF out # NIL THEN out.PutF["Error: unable to read entry #%g.\n", IO.rope[avalue]]; failed _ TRUE; }; }; $bidirectionalGenerate => { -- generate a subrange of entries in both directions IF out # NIL THEN out.PutF["Advanced test of generate. -- not implemented\n"]; }; $compactA => { -- compact the database IF out # NIL THEN out.PutF["Compacting log.\n"]; LoganBerry.CompactLogs[db: database]; }; $compactB => { -- verify that the compacted database has the correct number of entries IF out # NIL THEN out.PutF["Counting number of entries.\n"]; count _ 0; cursor _ LoganBerry.GenerateEntries[db: database, key: $Integer]; entry _ LoganBerry.NextEntry[cursor: cursor]; UNTIL entry = NIL DO count _ count + 1; entry _ LoganBerry.NextEntry[cursor: cursor]; ENDLOOP; LoganBerry.EndGenerate[cursor: cursor]; IF count # dbSize THEN { IF out # NIL THEN out.PutF["Error: count # database size.\n"]; failed _ TRUE; }; }; $invalidOps => { -- try to raise errors IF out # NIL THEN out.PutF["Checking generated errors. -- not implemented\n"]; }; ENDCASE => { IF out # NIL THEN out.PutF["Error: No such test.\n"]; failed _ TRUE; }; stop _ BasicTime.GetClockPulses[]; IF out # NIL THEN out.PutF["Running time: %g\n", IO.rope[ElapsedTime[start, stop]]]; }; ElapsedTime: PROC [start, stop: BasicTime.Pulses] RETURNS [ROPE] ~ { microseconds, seconds: LONG CARDINAL; digit: CARDINAL; any: BOOL _ FALSE; text: REF TEXT _ NEW[TEXT[8]]; microseconds _ BasicTime.PulsesToMicroseconds[stop - start]; seconds _ microseconds / 1000000; microseconds _ microseconds MOD 1000000; THROUGH [0..6) DO microseconds _ microseconds * 10; digit _ microseconds / 1000000; microseconds _ microseconds MOD 1000000; IF NOT any THEN { text _ Convert.AppendChar[to: text, from: '., quote: FALSE]; any _ TRUE; }; text _ Convert.AppendChar[to: text, from: digit + '0, quote: FALSE]; IF microseconds = 0 THEN EXIT; ENDLOOP; RETURN [IO.PutFR["%r%g\n", IO.card[seconds], IO.text[text]]]; }; usedKeys: PACKED ARRAY [1..10000] OF BOOLEAN; MarkKeysUnused: PROC [min, max: INT] RETURNS [] ~ { FOR i: INT IN [min..max] DO usedKeys[i] _ FALSE; ENDLOOP; }; GenerateUnusedKey: PROC [min, max: INT] RETURNS [INT] ~ { i: INT; i _ Random.ChooseInt[rs, min, max]; WHILE usedKeys[i] DO IF i < max THEN i _ i + 1 ELSE i _ min; ENDLOOP; usedKeys[i] _ TRUE; RETURN[i]; }; GenerateUsedKey: PROC [min, max: INT] RETURNS [INT] ~ { i: INT; i _ Random.ChooseInt[rs, min, max]; WHILE NOT usedKeys[i] DO IF i < max THEN i _ i + 1 ELSE i _ min; ENDLOOP; RETURN[i]; }; GetAttributeValue: PROC [entry: LoganBerry.Entry, type: LoganBerry.AttributeType] RETURNS [LoganBerry.AttributeValue] ~ { FOR e: LoganBerry.Entry _ entry, e.rest WHILE e # NIL DO IF e.first.type = type THEN RETURN[e.first.value]; ENDLOOP; RETURN[NIL]; }; TestLoganBerry: Commander.CommandProc = { numErrors: INT _ 0; start, stop: BasicTime.Pulses; start _ BasicTime.GetClockPulses[]; database _ LoganBerry.Open[dbName: dbFileName ! LoganBerry.Error => IO.PutF[cmd.out, "Error from LoganBerry: %g - %g\n", IO.atom[ec], IO.rope[explanation]] ]; FOR t: TestCase IN [enumerate..compactA) DO IF RunTest[t, cmd.out] THEN numErrors _ numErrors + 1; ENDLOOP; stop _ BasicTime.GetClockPulses[]; IO.PutF[cmd.out, "%g Errors; elapsed time = %g\n", IO.int[numErrors], IO.rope[ElapsedTime[start, stop]]]; }; Commander.Register[key: "TestLoganBerry", proc: TestLoganBerry, doc: "Test the LoganBerry package." ]; END. NLoganBerryTest.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Doug Terry, February 28, 1986 4:21:25 pm PST A test program for LoganBerry. Simply type "TestLoganBerry" to a command tool. Atom USING [GetPName], CommandTool USING [NextArgument], LoganQuery, The set of tests must be updated whenever a new test is added The following numbers pertain to specific tests Runs and times the given test. somehow need to delete old database entries first [entry: LoganBerry.Entry] RETURNS [continue: BOOL] Taken from InitialCommandsImpl.Time. [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] run all test cases except for create and compact (which take too long) TestLoganQuery: Commander.CommandProc = { [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] NULL; }; Commander.Register[key: "TestLoganQuery", proc: TestLoganQuery, doc: "Test the LoganQuery package." ]; Doug Terry, February 27, 1986 10:43:30 am PST changes to: DIRECTORY, LoganBerryTest, IMPORTS, ~, RunTest, Proc, Proc, Proc, TestLoganBerry, TestLoganQuery Doug Terry, February 27, 1986 7:21:25 pm PST changes to: DIRECTORY, LoganBerryTest, IMPORTS, ~, RunTest, ElapsedTime, Proc, Proc, TestLoganBerry, TestLoganQuery, END, RunTest, MarkKeysUnused, GenerateUnusedKey, GenerateUsedKey, TestCase, AddOne (local of RunTest) Doug Terry, February 27, 1986 8:45:56 pm PST changes to: TestCase, ~, RunTest, GetAttributeValue, Proc, DIRECTORY, IMPORTS, TestLoganQuery, ElapsedTime, MarkKeysUnused, TestLoganBerry Doug Terry, February 27, 1986 9:35:37 pm PST changes to: TestLoganBerry Κ }˜codešœ™Kšœ Οmœ1™Kšœ žœ˜K˜—K˜—šœ ˜%KšžœžœžœF˜WKšœ˜Kšœžœ˜šžœž˜Kšœ!˜!K˜ KšœO˜OKšœžœ˜)KšœC˜CKšžœ˜—K˜—šœ 2˜AKšžœžœžœ#˜4šžœž˜Kšœ˜K˜ šžœJžœžœ˜VKšžœžœžœ6˜GKšœ žœ˜K˜—Kšžœ˜—K˜—šœ ˜.Kšžœžœžœ+˜<šžœžœž˜Kšœ?˜?Kšœ!˜!Kšžœ˜—K˜—šœ /˜?Kšžœžœžœ$˜5šžœž˜Kšœ˜K˜ šžœJžœžœ˜VKšžœžœžœ5˜FKšœ žœ˜K˜—Kšžœ˜—K˜—šœ ˜-Kšžœžœžœ7˜Hšžœž˜Kšœ$˜$K˜ šžœJžœ˜VKšžœžœžœA˜RKšœ žœ˜K˜—Kšžœ˜—K˜—šœ $˜5Kšžœžœžœ˜0šžœ ž˜Kšœ$˜$K˜ KšœO˜OKšœ)˜)šžœžœ8˜>Kšžœžœžœ4˜EKšœ žœ˜K˜—Kšžœ˜—K˜—šœ  ˜4Kšœ žœžœ˜Kšžœžœžœ4˜EKšœ$˜$K˜ Kšœ˜šœG˜Gšœ˜Kšœžœ˜Kš žœžœžœžœžœ˜-—Kšœ˜—šžœžœ žœ˜Kšžœžœžœ0˜AKšœ žœ˜K˜—K–/[conv: LoganBerry.Conv _ NIL, dbName: ROPE]šœ/˜/šžœJžœ˜VKšžœžœžœA˜RKšœ žœ˜K˜—K˜—šœ 4˜QKšžœžœžœ>˜OK˜—šœ ˜'Kšžœžœžœ ˜1Kšœ%˜%K˜—šœ G˜WKšžœžœžœ,˜=Jšœ ˜ KšœA˜AKšœ-˜-šžœ žœž˜Kšœ˜Kšœ-˜-Kšžœ˜—Kšœ'˜'šžœžœ˜Kšžœžœžœ-˜>Kšœ žœ˜K˜—K˜—šœ ˜(Kšžœžœžœ>˜OK˜—šžœ˜ Kšžœžœžœ%˜6Kšœ ž˜Kšœ˜——Kšœ"˜"Kšžœžœžœ žœ!˜TK˜K™—š‘ œžœ!žœžœ˜DKšœ$™$Kšœžœžœ˜%Kšœžœ˜Kšœžœžœ˜Kš œžœžœžœžœ˜Kšœ<˜