DIRECTORY BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds], Commander USING [Register, CommandProc], CommandTool USING [ParseToList], 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, CommandTool, 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, rewriteA, rewriteB, replace, 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; numReplaces: 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; avalue: LoganBerry.AttributeValue; entry: LoganBerry.Entry; cursor: LoganBerry.Cursor; i: INT; 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; }; $rewriteA => { -- rewrite 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; }; $rewriteB => { -- verify that the rewritten 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; }; $replace => { -- replace some random entries (then restore to original values) replacement: LoganBerry.Entry; IF out # NIL THEN out.PutF["Replacing (then restoring) %g entries.\n", IO.card[numReplaces]]; THROUGH [1..numReplaces] DO i _ Random.ChooseInt[rs, 1, dbSize]; avalue _ Convert.RopeFromInt[i]; entry _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry; replacement _ LIST[[$Integer, avalue], [$Rope, "replacement value"]]; LoganBerry.WriteEntry[db: database, entry: replacement, replace: TRUE]; replacement _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry; IF NOT Rope.Equal[GetAttributeValue[replacement, $Rope], "replacement value"] THEN { IF out # NIL THEN out.PutF["Error: replacement $Rope value # expected value.\n"]; failed _ TRUE; }; LoganBerry.WriteEntry[db: database, entry: entry, replace: 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.\n"]; i _ Random.ChooseInt[rs, 1, dbSize-10]; avalue _ Convert.RopeFromInt[i]; cursor _ LoganBerry.GenerateEntries[db: database, key: $Integer, start: avalue, end: Convert.RopeFromInt[i+9]]; count _ 0; entry _ LoganBerry.NextEntry[cursor: cursor]; UNTIL entry = NIL DO count _ count + 1; entry _ LoganBerry.NextEntry[cursor: cursor]; ENDLOOP; entry _ LoganBerry.NextEntry[cursor: cursor, dir: decreasing]; UNTIL entry = NIL DO count _ count - 1; entry _ LoganBerry.NextEntry[cursor: cursor, dir: decreasing]; ENDLOOP; IF count # 1 THEN { IF out # NIL THEN out.PutF["Error: generated increasing/decreasing subrange of wrong size.\n"]; failed _ TRUE; }; LoganBerry.EndGenerate[cursor: cursor]; }; $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 caughtError: BOOLEAN _ FALSE; IF out # NIL THEN out.PutF["Checking generated errors.\n"]; [] _ LoganBerry.Open[dbName: "Nonexistent.badname" ! LoganBerry.Error => {caughtError _ TRUE; IF ec = $CantOpenSchema THEN CONTINUE ELSE REJECT};]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to open nonexistent database.\n"]; failed _ TRUE; }; caughtError _ FALSE; [] _ LoganBerry.Describe[db: 1010101 ! LoganBerry.Error => {caughtError _ TRUE; IF ec = $BadDBHandle THEN CONTINUE ELSE REJECT};]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to operate on bad database.\n"]; failed _ TRUE; }; caughtError _ FALSE; [] _ LoganBerry.ReadEntry[db: database, key: $NonIndex, value: avalue ! LoganBerry.Error => {caughtError _ TRUE; IF ec = $NoIndex THEN CONTINUE ELSE REJECT};]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to read using nonexistent index.\n"]; failed _ TRUE; }; i _ Random.ChooseInt[rs, 1, dbSize]; avalue _ Convert.RopeFromInt[i]; entry _ LoganBerry.ReadEntry[db: database, key: $Integer, value: avalue].entry; caughtError _ FALSE; [] _ LoganBerry.WriteEntry[db: database, entry: entry ! LoganBerry.Error => {caughtError _ TRUE; IF ec = $ValueNotUnique THEN CONTINUE ELSE REJECT};]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to write duplicate entry.\n"]; failed _ TRUE; }; caughtError _ FALSE; [] _ LoganBerry.WriteEntry[db: database, entry: entry.rest ! LoganBerry.Error => {caughtError _ TRUE; IF ec = $NoPrimaryKey THEN CONTINUE ELSE REJECT};]; IF NOT caughtError THEN { IF out # NIL THEN out.PutF["Error: able to write without primary key.\n"]; failed _ TRUE; }; }; 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; firstTest, lastTest: TestCase; start, stop: BasicTime.Pulses; args: LIST OF ROPE _ CommandTool.ParseToList[cmd].list; IF args # NIL AND Rope.Equal[args.first, "-f"] THEN { -- full testcase IO.PutF[cmd.out, "Running ALL tests... \n\n"]; firstTest _ FIRST[TestCase]; lastTest _ LAST[TestCase]; } ELSE { -- run all test cases except for create and compact (which take too long) firstTest _ enumerate; lastTest _ invalidOps; }; 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 [firstTest..lastTest] 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. –LoganBerryTest.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Doug Terry, October 19, 1986 11:10:20 pm PDT A test program for LoganBerry. Simply type "TestLoganBerry" to a command tool. 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. attr: LoganBerry.Attribute; atype: LoganBerry.AttributeType; somehow need to delete old database entries first [entry: LoganBerry.Entry] RETURNS [continue: BOOL] Retrieve [start..end] Note: at this point it is hard to predict count since ordering is non-numerical Retrieve (end..start] Taken from InitialCommandsImpl.Time. [cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL] 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˜—šœ 0˜@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˜—šœ @˜OKšœ˜KšžœžœžœM˜^šžœž˜Kšœ$˜$K˜ KšœO˜OKšœžœ3˜EKšœAžœ˜GKšœU˜UšžœžœHžœ˜TKšžœžœžœ@˜QKšœ žœ˜K˜—Kšœ;žœ˜AKšžœ˜—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šžœžœžœ+˜˜>šžœ žœž˜Kšœ˜Kšœ>˜>Kšžœ˜—šžœ žœ˜KšžœžœžœN˜_Kšœ žœ˜K˜—Kšœ'˜'K˜—šœ ˜'Kšžœžœžœ ˜1Kšœ%˜%K˜—šœ G˜WKšžœžœžœ,˜=Jšœ ˜ KšœA˜AKšœ-˜-šžœ žœž˜Kšœ˜Kšœ-˜-Kšžœ˜—Kšœ'˜'šžœžœ˜Kšžœžœžœ-˜>Kšœ žœ˜K˜—K˜—šœ ˜(Kšœ žœžœ˜Kšžœžœžœ+˜<šœ5˜5Kš œ#žœžœžœžœžœžœ˜^—šžœžœ žœ˜Kšžœžœžœ9˜JKšœ žœ˜K˜—Kšœžœ˜šœ'˜'Kš œ#žœžœžœžœžœžœ˜[—šžœžœ žœ˜Kšžœžœžœ7˜HKšœ žœ˜K˜—Kšœžœ˜šœH˜HKš œ#žœžœžœžœžœžœ˜W—šžœžœ žœ˜Kšžœžœžœ<˜MKšœ žœ˜K˜—Kšœ$˜$K˜ KšœO˜OKšœžœ˜šœ8˜8Kš œ#žœžœžœžœžœžœ˜^—šžœžœ žœ˜Kšžœžœžœ5˜FKšœ žœ˜K˜—Kšœžœ˜šœ=˜=Kš œ#žœžœžœžœžœžœ˜\—šžœžœ žœ˜Kšžœžœžœ9˜JKšœ žœ˜K˜—K˜—šžœ˜ Kšžœžœžœ%˜6Kšœ ž˜Kšœ˜——Kšœ"˜"Kšžœžœžœ žœ!˜TK˜K™—š‘ œžœ!žœžœ˜DKšœ$™$Kšœžœžœ˜%Kšœžœ˜Kšœžœžœ˜Kš œžœžœžœžœ˜Kšœ<˜