123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- MODULE DiskTests; (** AUTHOR "staubesv"; PURPOSE "Simple block device tests"; *)
- (**
- * Usage:
- *
- * DiskTests.WriteTestData dev#part ~ fills the specified partition with test data
- * DiskTests.VerifyTestData dev#part ~ checks whether the test data can be correctly read
- * DiskTests.WriteZeros dev#part ~ fills the specifed partition with zeros
- * DiskTests.Test dev#part ~ tests the specified partition
- * DiskTests.TransferBlocks dev#part "READ"|"WRITE" block numblocks ~ (TUI only)
- *
- * WMPartitions.Open ~ opens the graphical front-end
- *
- * History:
- *
- * 28.02.2006 First release (staubesv)
- *)
- IMPORT
- Streams, Random, Kernel, Commands, Disks, Partitions, Lib := PartitionsLib, Strings;
- TYPE
- TestDataBase = OBJECT(Lib.Operation);
- VAR
- buffer : POINTER TO ARRAY OF CHAR;
- sectorsPerTransfer : LONGINT;
- PROCEDURE SetParameters*(sectorsPerTransfer : LONGINT);
- BEGIN
- SELF.sectorsPerTransfer := sectorsPerTransfer;
- END SetParameters;
- PROCEDURE ValidParameters*() : BOOLEAN;
- BEGIN
- IF sectorsPerTransfer < 1 THEN ReportError("SectorsPerTransfer must be >= 1"); RETURN FALSE; END;
- IF disk.device.blockSize MOD 256 # 0 THEN ReportError("Device blocksize MOD 256 MUST BE 0"); RETURN FALSE; END;
- RETURN TRUE;
- END ValidParameters;
- END TestDataBase;
- TYPE
- (** Fills partition with test data *)
- TestDataWriter* = OBJECT(TestDataBase);
- PROCEDURE FillWithTestData*(VAR buffer : ARRAY OF CHAR);
- VAR i : LONGINT;
- BEGIN
- FOR i := 0 TO LEN(buffer) - 1 DO buffer[i] := CHR(i MOD 256); END;
- END FillWithTestData;
- PROCEDURE DoOperation*;
- VAR pos, num, nbrOfBlocks, blocksWritten, res : LONGINT; temp : ARRAY 256 OF CHAR;
- BEGIN
- SetStatus(state.status, "Writing test data...", 0, 0, disk.table[partition].size, TRUE);
- NEW(buffer, disk.device.blockSize * sectorsPerTransfer);
- FillWithTestData(buffer^);
- pos := disk.table[partition].start; num := sectorsPerTransfer; nbrOfBlocks := disk.table[partition].size;
- LOOP
- IF num > nbrOfBlocks - blocksWritten THEN num := nbrOfBlocks - blocksWritten; END;
- IF ~alive OR (num = 0) THEN EXIT; END;
- disk.device.Transfer(Disks.Write, pos, num, buffer^, 0, res);
- IF res # Disks.Ok THEN Lib.GetTransferError(disk.device, Disks.Write, pos, res, temp); ReportError(temp); END;
- INC(pos, num); INC(blocksWritten, num);
- SetCurrentProgress(blocksWritten);
- END;
- IF alive THEN
- result.String("Test data written to partition "); result.String(diskpartString);
- ELSE
- result.String("Operation aborted");
- END;
- END DoOperation;
- PROCEDURE &Init*(disk :Lib.Disk; partition : LONGINT; out : Streams.Writer);
- BEGIN
- Init^(disk, partition, out);
- name := "WriteTestData"; desc := "Write test data to partition"; locktype := Lib.WriterLock;
- END Init;
- END TestDataWriter;
- TYPE
- (** Checks whether the test data written by the WriteTestData object can be read back correctly *)
- TestDataChecker* = OBJECT(TestDataBase);
- PROCEDURE DoOperation*;
- VAR
- pos, num, nbrOfBlocks, blocksRead, res : LONGINT; string, nbr : ARRAY 128 OF CHAR;
- expected, found, foundAt : LONGINT;
- BEGIN
- SetStatus(state.status, "Verifying test data...", 0, 0, disk.table[partition].size, TRUE);
- NEW(buffer, disk.device.blockSize * sectorsPerTransfer);
- pos := disk.table[partition].start; num := sectorsPerTransfer; nbrOfBlocks := disk.table[partition].size;
- LOOP
- IF num > nbrOfBlocks - blocksRead THEN num := nbrOfBlocks - blocksRead; END;
- IF ~alive OR (num = 0) THEN EXIT; END;
- disk.device.Transfer(Disks.Read, pos, num, buffer^, 0, res);
- IF res # Disks.Ok THEN
- Lib.GetTransferError(disk.device, Disks.Read, pos, res, string); ReportError(string);
- ELSIF ~TestDataIsCorrect(0, num, disk.device.blockSize, buffer^, expected, found, foundAt) THEN
- string := "Verification of block at pos "; Strings.IntToStr(pos, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ", Expected value: "); Strings.IntToStr(expected, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ", found: "); Strings.IntToStr(found, nbr); Strings.Append(string, nbr);
- Strings.Append(string, " at index: "); Strings.IntToStr(foundAt, nbr); Strings.Append(string, nbr);
- ReportError(string);
- END;
- INC(pos, num); INC(blocksRead, num);
- SetCurrentProgress(blocksRead);
- END;
- IF alive THEN
- result.String("Test data verified on partition "); result.String(diskpartString); result.String(" - ");
- IF state.errorCount = 0 THEN result.String("No "); END;
- result.String("Errors found.");
- END;
- END DoOperation;
- PROCEDURE &Init*(disk :Lib.Disk; partition : LONGINT; out : Streams.Writer);
- BEGIN
- Init^(disk, partition, out);
- name := "CheckTestData"; desc := "Verify test data on partition"; locktype := Lib.ReaderLock;
- END Init;
- END TestDataChecker;
- TYPE
- ZeroWriter* = OBJECT(TestDataWriter);
- PROCEDURE FillWithTestData*(VAR buffer : ARRAY OF CHAR);
- VAR i : LONGINT;
- BEGIN
- FOR i := 0 TO LEN(buffer) - 1 DO buffer[i] := 0X; END;
- END FillWithTestData;
- PROCEDURE & Init*(disk : Lib.Disk; partition : LONGINT; out : Streams.Writer);
- BEGIN
- Init^(disk, partition, out);
- name := "ZeroWriter"; desc := "Fill with zeros partition"; locktype := Lib.WriterLock;
- END Init;
- END ZeroWriter;
- TYPE
- (**
- * Test a partition
- *)
- DiskTest* = OBJECT(Lib.Operation)
- VAR
- (* parameters *)
- doRead, doWrite, testData : BOOLEAN;
- nbrOfTests, maxNbrOfSectors, maxOffset : LONGINT;
- start, size : LONGINT; (* First block of partition and size of the partition *)
- offset : LONGINT; (* currently used offset into client buffer *)
- (* Coverage information *)
- testCount : LONGINT;
- testedOffsets : POINTER TO ARRAY OF BOOLEAN;
- testedSectors : POINTER TO ARRAY OF BOOLEAN;
- blocksRead : HUGEINT;
- buffer : POINTER TO ARRAY OF CHAR;
- random : Random.Generator;
- PROCEDURE SetParameters*(doRead, doWrite, testData : BOOLEAN; nbrOfTests, maxNbrOfSectors, maxOffset : LONGINT);
- BEGIN
- SELF.doRead := doRead; SELF.doWrite := doWrite; SELF.testData := testData;
- SELF.nbrOfTests := nbrOfTests; SELF.maxNbrOfSectors := maxNbrOfSectors; SELF.maxOffset := maxOffset;
- END SetParameters;
- PROCEDURE ValidParameters*() : BOOLEAN;
- BEGIN
- IF ~doRead & ~doWrite THEN ReportError("Either read or write tests must be done"); RETURN FALSE; END;
- IF maxNbrOfSectors < 1 THEN ReportError("MaxNbrOfSectors must be >= 1"); RETURN FALSE; END;
- IF maxOffset < 0 THEN ReportError("MaxOffset must be >= 0"); RETURN FALSE; END;
- RETURN TRUE;
- END ValidParameters;
- PROCEDURE WriteTestSettings;
- BEGIN
- info.String("Test Settings:"); info.Ln;
- info.String(" Number of Tests: "); IF nbrOfTests > 0 THEN info.Int(nbrOfTests, 0); ELSE info.String("Endless Loop Mode"); END; info.Ln;
- info.String(" Read Tests: "); IF doRead THEN info.String("Yes"); ELSE info.String("No"); END; info.Ln;
- info.String(" Write Tests: "); IF doWrite THEN info.String("Yes"); ELSE info.String("No"); END; info.Ln;
- info.String(" Verify Reads using Test Data: "); IF testData THEN info.String("Yes"); ELSE info.String("No"); END; info.Ln;
- info.String(" Max. Sectors per Transfer: "); info.Int(maxNbrOfSectors, 0); info.Ln;
- info.String(" Max. Offset into Client Buffer: "); info.Int(maxOffset, 0); info.Ln;
- info.Ln;
- END WriteTestSettings;
- PROCEDURE WriteSummary;
- VAR i, val : LONGINT;
- PROCEDURE WriteB(b: HUGEINT; w : Streams.Writer);
- VAR suffix: ARRAY 3 OF CHAR;
- BEGIN
- IF b > 1024*1024*1024 THEN suffix := "GB"; b := b DIV (1024*1024*1024);
- ELSIF b > 1024*1024 THEN suffix := "MB"; b := b DIV (1024*1024);
- ELSIF b > 1024 THEN suffix := "KB"; b := b DIV 1024;
- ELSE suffix := "B";
- END;
- w.Int(SHORT(b), 0); w.String(suffix);
- END WriteB;
- BEGIN
- info.String("Test Summary:"); info.Ln;
- info.String(" "); info.Int(testCount, 0); info.String(" Test Runs done"); info.Ln;
- IF testedOffsets # NIL THEN
- val := 0; FOR i := 0 TO LEN(testedOffsets)-1 DO IF testedOffsets[i] THEN INC(val); END; END;
- info.String(" Offset Coverage: "); info.FloatFix(100.0 * val / LEN(testedOffsets), 5, 2, 0); info.Char("%"); info.Ln;
- END;
- IF testedSectors # NIL THEN
- val := 0; FOR i := 0 TO LEN(testedSectors)-1 DO IF testedSectors[i] THEN INC(val); END; END;
- info.String(" Transfer Sizes Coverage: "); info.FloatFix(100.0 * val / LEN(testedSectors), 5, 2, 0); info.Char("%"); info.Ln;
- END;
- info.String(" Total amount of data read: "); WriteB(blocksRead * disk.device.blockSize, info); info.Ln;
- END WriteSummary;
- PROCEDURE PerformStep;
- VAR pos, num, res, expected, found, foundAt : LONGINT; string, nbr : ARRAY 128 OF CHAR;
- BEGIN
- num := random.Dice(maxNbrOfSectors) + 1;
- IF maxNbrOfSectors > 1 THEN testedSectors[num - 1] := TRUE; END;
- pos := start + random.Dice(size - num);
- disk.device.Transfer(Disks.Read, pos, num, buffer^, offset, res);
- IF res # Disks.Ok THEN
- Lib.GetTransferError(disk.device, Disks.Write, pos, res, string); ReportError(string);
- ELSE
- INC (blocksRead, num);
- IF testData & ~TestDataIsCorrect(offset, num, disk.device.blockSize, buffer^, expected, found, foundAt) THEN
- string := "Data Verification failed (Pos: "; Strings.IntToStr(pos, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ", Num: "); Strings.IntToStr(num, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ", Offset: "); Strings.IntToStr(offset, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ": ");
- Strings.Append(string, "Expected value: "); Strings.IntToStr(expected, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ", found value: "); Strings.IntToStr(found, nbr); Strings.Append(string, nbr);
- Strings.Append(string, " at index: "); Strings.IntToStr(foundAt, nbr); Strings.Append(string, nbr);
- Strings.Append(string, ")");
- ReportError(string);
- END;
- END;
- END PerformStep;
- PROCEDURE DoOperation*;
- BEGIN
- start := disk.table[partition].start; size := disk.table[partition].size;
- NEW(buffer, maxNbrOfSectors * disk.device.blockSize + maxOffset);
- WriteTestSettings;
- IF nbrOfTests > 0 THEN SetStatus(state.status, "Testing...", 0, 0, nbrOfTests, TRUE);
- ELSE SetStatus(state.status, "Testing (loop mode)...", 0, 0, 0, FALSE);
- END;
- IF maxOffset > 0 THEN NEW(testedOffsets, maxOffset + 1); END;
- IF maxNbrOfSectors > 1 THEN NEW(testedSectors, maxNbrOfSectors); END;
- testCount := 0; offset := 0;
- LOOP
- IF ~alive THEN EXIT END;
- IF nbrOfTests > 0 THEN
- SetCurrentProgress(testCount);
- IF testCount >= nbrOfTests THEN EXIT; END;
- END;
- PerformStep;
- IF maxOffset > 0 THEN testedOffsets[offset] := TRUE; offset := (offset + 1) MOD (maxOffset + 1); END;
- INC(testCount);
- END;
- WriteSummary;
- IF alive THEN
- result.String("Finished testing partition "); result.String(diskpartString); result.String(" - ");
- IF state.errorCount = 0 THEN result.String("No "); END;
- result.String("Errors found");
- END;
- END DoOperation;
- PROCEDURE &Init*(disk :Lib.Disk; partition : LONGINT; out : Streams.Writer);
- BEGIN
- Init^(disk, partition, out);
- name := "DiskTester"; desc := "Perform disk test on partition"; locktype := Lib.ReaderLock;
- NEW(random); random.InitSeed(Kernel.GetTicks());
- END Init;
- END DiskTest;
- PROCEDURE TestDataIsCorrect*(offset, numblocks, blocksize : LONGINT; CONST buffer : ARRAY OF CHAR; VAR expected, found, foundAt : LONGINT) : BOOLEAN;
- VAR i : LONGINT;
- BEGIN
- ASSERT(LEN(buffer) >= numblocks * blocksize + offset);
- ASSERT(blocksize MOD 256 = 0); (* Otherwise test data used will not work *)
- FOR i := 0 TO numblocks * blocksize - 1 DO
- IF ORD(buffer[i + offset]) # i MOD 256 THEN
- expected := i MOD 256; found := ORD(buffer[i + offset]); foundAt := i;
- RETURN FALSE;
- END;
- END;
- RETURN TRUE;
- END TestDataIsCorrect;
- (** Fill partition with test data *)
- PROCEDURE WriteTestData*(context : Commands.Context); (** dev#part ~ *)
- VAR selection : Lib.Selection; testDataWriter : TestDataWriter;
- BEGIN
- IF Partitions.GetSelection(context, FALSE, selection) THEN
- NEW(testDataWriter, selection.disk, selection.partition, context.out);
- testDataWriter.SetParameters(1);
- testDataWriter.SetStart;
- ELSE (* skip; error written to <w> by ScanOpenPart *)
- END;
- END WriteTestData;
- (** Fill partition with test data *)
- PROCEDURE VerifyTestData*(context : Commands.Context); (** dev#part ~ *)
- VAR selection : Lib.Selection; testDataChecker : TestDataChecker;
- BEGIN
- IF Partitions.GetSelection(context, FALSE, selection) THEN
- NEW(testDataChecker, selection.disk, selection.partition, context.out);
- testDataChecker.SetParameters(1);
- testDataChecker.SetStart;
- ELSE (* skip; error written to <w> by ScanOpenPart *)
- END;
- END VerifyTestData;
- (** Fill partition with zeros *)
- PROCEDURE WriteZeros*(context : Commands.Context); (** dev#part ~ *)
- VAR selection : Lib.Selection; zeroWriter : ZeroWriter;
- BEGIN
- IF Partitions.GetSelection(context, FALSE, selection) THEN
- NEW(zeroWriter, selection.disk, selection.partition, context.out);
- zeroWriter.SetParameters(1);
- zeroWriter.SetStart;
- ELSE (* skip; error written to <w> by ScanOpenPart *)
- END;
- END WriteZeros;
- (** Test the specified partition *)
- PROCEDURE Test*(context : Commands.Context); (** dev#part ~ *)
- VAR selection : Lib.Selection; diskTest : DiskTest;
- BEGIN
- IF Partitions.GetSelection(context, FALSE, selection) THEN
- NEW(diskTest, selection.disk, selection.partition, context.out);
- diskTest.SetParameters(TRUE, FALSE, FALSE, 100, 100, 0);
- diskTest.SetStart;
- ELSE (* skip; error written to <w> by ScanOpenPart *)
- END;
- END Test;
- (** Read/write the specified number of sectors from/to the specified paritition starting at the specified sector *)
- PROCEDURE TransferBlocks*(context : Commands.Context); (** dev#part "READ"|"WRITE" block numblocks ~ *)
- VAR
- selection : Lib.Selection;
- string : ARRAY 32 OF CHAR; dev : Disks.Device;
- op, block, numblocks, res : LONGINT;
- buffer : POINTER TO ARRAY OF CHAR;
- BEGIN
- IF Partitions.GetSelection(context, FALSE, selection) THEN
- context.arg.SkipWhitespace; context.arg.String(string);
- IF string = "READ" THEN op := Disks.Read;
- ELSIF string = "WRITE" THEN op := Disks.Write;
- ELSE context.error.String("DiskTests: Expected READ|WRITE parameter."); context.error.Ln; RETURN;
- END;
- IF ~context.arg.GetInteger(block, FALSE) OR (block < 0) THEN context.error.String("DiskTests: Expected block parameter."); context.error.Ln; RETURN; END;
- IF ~context.arg.GetInteger(numblocks, FALSE) OR (block < 0) THEN context.error.String("DiskTests: Expected numblocks parameter."); context.error.Ln; RETURN; END;
- dev := selection.disk.device;
- context.out.String("DiskTests: ");
- IF op = Disks.Read THEN context.out.String("Reading "); ELSE context.out.String(" Writing "); END;
- context.out.Int(numblocks, 0); context.out.String(" blocks at offset "); context.out.Int(block, 0);
- IF op = Disks.Read THEN context.out.String(" from "); ELSE context.out.String(" to "); END;
- context.out.String(" partition "); context.out.String(dev.name); context.out.String("#"); context.out.Int(selection.partition, 0);
- context.out.String("... "); context.out.Update;
- dev.Open(res);
- IF res = Disks.Ok THEN
- IF dev.table[selection.partition].size - block < numblocks THEN
- context.error.String("DiskTests: Numblocks too big. Would cross partition. Aborting test."); context.error.Ln;
- ELSE
- NEW(buffer, numblocks * dev.blockSize);
- dev.Transfer(op, block, numblocks, buffer^, 0, res);
- ShowDiskres(res, context.out); context.error.Ln;
- END;
- dev.Close(res); (* ignore res *)
- ELSE
- context.error.String("DiskTests: Could not open device "); context.error.String(dev.name);
- context.error.String(": "); ShowDiskres(res, context.out); context.error.Ln;
- END;
- ELSE context.error.String("DiskTests: TransferBlocks: Device not found."); context.error.Ln;
- END;
- END TransferBlocks;
- PROCEDURE ShowDiskres(res : LONGINT; out : Streams.Writer);
- BEGIN
- IF res = Disks.Ok THEN out.String("Ok");
- ELSIF res = Disks.MediaChanged THEN out.String("MediaChanged");
- ELSIF res = Disks.WriteProtected THEN out.String("WriteProtected");
- ELSIF res = Disks.Unsupported THEN out.String("Unsupported");
- ELSIF res = Disks.DeviceInUse THEN out.String("DeviceInUse");
- ELSIF res = Disks.MediaMissing THEN out.String("MediaMissing");
- ELSE out.String("Unknown (res: "); out.Int(res, 0); out.String(")");
- END;
- END ShowDiskres;
- END DiskTests.
- DiskTests.WriteTestData USB0#1 ~ SystemTools.Free DiskTests ~
- DiskTests.VerifyTestData USB0#1 ~
- DiskTests.Test USB0#1 ~
- DiskTests.TransferBlocks USB0#1 READ 0 6 ~
- UsbInfo.TraceOn Custom~
- UsbInfo.TraceNone ~
- Partitions.ShowOps ~
- Partitions.ShowOps detail ~
- Partitions.Abort 1 ~
|