wincmd/ntfs/mft/mftoper_test.go

171 lines
4.7 KiB
Go
Raw Normal View History

package mft
import (
"bytes"
"encoding/binary"
"errors"
"io"
"testing"
"unicode/utf16"
)
func TestShouldPreferFileName(t *testing.T) {
tests := []struct {
current string
candidate string
want bool
}{
{current: "", candidate: "LONGNAME.TXT", want: true},
{current: "PROGRA~1", candidate: "Program Files", want: true},
{current: "Program Files", candidate: "PROGRA~1", want: false},
{current: "abc", candidate: "abcdef", want: true},
{current: "abcdef", candidate: "abc", want: false},
}
for _, tt := range tests {
if got := shouldPreferFileName(tt.current, tt.candidate); got != tt.want {
t.Fatalf("shouldPreferFileName(%q, %q) = %v, want %v", tt.current, tt.candidate, got, tt.want)
}
}
}
func TestShouldPreferFileNameWithNamespace(t *testing.T) {
if !shouldPreferFileNameWithNamespace("PROGRA~1", FileNameNamespaceDos, "Program Files", FileNameNamespaceWin32) {
t.Fatal("expected Win32 name to win over DOS name")
}
if shouldPreferFileNameWithNamespace("Program Files", FileNameNamespaceWin32, "PROGRA~1", FileNameNamespaceDos) {
t.Fatal("did not expect DOS name to replace Win32 name")
}
}
func TestFileFromRecordIncludesParent(t *testing.T) {
parent := FileReference{RecordNumber: 42, SequenceNumber: 7}.ToUint64()
record := Record{
FileReference: FileReference{RecordNumber: 100, SequenceNumber: 9},
Flags: RecordFlagInUse,
Attributes: []Attribute{
{Type: AttributeTypeFileName, Data: testFileNameData("PROGRA~1", parent, FileNameNamespaceDos)},
{Type: AttributeTypeFileName, Data: testFileNameData("Program Files", parent, FileNameNamespaceWin32)},
{Type: AttributeTypeData, ActualSize: 12, AllocatedSize: 16},
},
}
file, ok := FileFromRecord(record)
if !ok {
t.Fatal("expected file to be extracted")
}
if file.Name != "Program Files" {
t.Fatalf("file.Name = %q, want %q", file.Name, "Program Files")
}
if file.Parent != parent {
t.Fatalf("file.Parent = %d, want %d", file.Parent, parent)
}
if file.Node != record.FileReference.ToUint64() {
t.Fatalf("file.Node = %d, want %d", file.Node, record.FileReference.ToUint64())
}
if file.Size != 12 || file.Aszie != 16 {
t.Fatalf("unexpected size fields: size=%d asize=%d", file.Size, file.Aszie)
}
}
func TestCopyFilesReportsProgress(t *testing.T) {
progress := make([]float64, 0)
var dst testWriter
reader := &testChunkReader{chunks: [][]byte{{'a', 'b'}, {'c', 'd'}}}
written, err := copyFiles(&dst, reader, 4, func(_, _ int64, percent float64) {
progress = append(progress, percent)
})
if err != nil {
t.Fatalf("copyFiles failed: %v", err)
}
if written != 4 {
t.Fatalf("written = %d, want 4", written)
}
if len(progress) == 0 {
t.Fatal("expected progress callbacks")
}
if progress[len(progress)-1] != 100 {
t.Fatalf("final progress = %v, want 100", progress[len(progress)-1])
}
}
func TestWalkRecordsIgnoresPartialTail(t *testing.T) {
reader := bytes.NewReader(make([]byte, 2*defaultMFTRecordSize+17))
calls := 0
err := walkRecords(reader, int64(2*defaultMFTRecordSize+17), defaultMFTRecordSize, func(b []byte) (Record, error) {
calls++
if len(b) != int(defaultMFTRecordSize) {
t.Fatalf("parser got len=%d, want %d", len(b), defaultMFTRecordSize)
}
return Record{}, nil
}, func(Record) error {
return nil
})
if err != nil {
t.Fatalf("walkRecords returned error: %v", err)
}
if calls != 2 {
t.Fatalf("parser calls = %d, want 2", calls)
}
}
func TestWalkRecordsPropagatesVisitorError(t *testing.T) {
reader := bytes.NewReader(make([]byte, 2*defaultMFTRecordSize))
wantErr := errors.New("stop")
visited := 0
err := walkRecords(reader, int64(2*defaultMFTRecordSize), defaultMFTRecordSize, func([]byte) (Record, error) {
return Record{}, nil
}, func(Record) error {
visited++
if visited == 2 {
return wantErr
}
return nil
})
if !errors.Is(err, wantErr) {
t.Fatalf("walkRecords error = %v, want %v", err, wantErr)
}
if visited != 2 {
t.Fatalf("visited = %d, want 2", visited)
}
}
type testChunkReader struct {
chunks [][]byte
index int
}
func (r *testChunkReader) Read(p []byte) (int, error) {
if r.index >= len(r.chunks) {
return 0, io.EOF
}
chunk := r.chunks[r.index]
r.index++
copy(p, chunk)
return len(chunk), nil
}
type testWriter struct {
wrote int
}
func (w *testWriter) Write(p []byte) (int, error) {
w.wrote += len(p)
return len(p), nil
}
func testFileNameData(name string, parent uint64, namespace FileNameNamespace) []byte {
encoded := utf16.Encode([]rune(name))
data := make([]byte, 66+len(encoded)*2)
binary.LittleEndian.PutUint64(data[0:], parent)
data[0x40] = byte(len(encoded))
data[0x41] = byte(namespace)
for i, v := range encoded {
binary.LittleEndian.PutUint16(data[0x42+i*2:], v)
}
return data
}