Files
nfs/internal/nfs4/ops42_test.go
petrbalvin a9b8039ef7
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

153 lines
5.4 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package nfs4
import (
"bytes"
"testing"
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
)
func TestOps42Shapes(t *testing.T) {
t.Parallel()
st := Stateid{2, 'S'}
// SEEK args and res.
seek := AppendSeekArgs(nil, st, 8, ContentHole)
d := xdr.NewDecoder(seek[4:])
if raw, err := d.Raw(16); err != nil || !bytes.Equal(raw, st[:]) {
t.Fatalf("stateid % x: %v", raw, err)
}
if off, err := d.Uint64(); err != nil || off != 8 {
t.Fatalf("offset %d: %v", off, err)
}
if what, err := d.Uint32(); err != nil || what != ContentHole {
t.Fatalf("what %d: %v", what, err)
}
res := AppendSeekRes(nil, true, 4096)
if n, err := resBodyLen(OpSeek, ErrOK, res); err != nil || n != len(res) {
t.Fatalf("seek res measured %d of %d: %v", n, len(res), err)
}
// COPY res measures through the write response and requirements.
verifier := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
copyRes := AppendCopyRes(nil, st, false, 128, NfsSyncFileSync, verifier, true, true)
if n, err := resBodyLen(OpCopy, ErrOK, copyRes); err != nil || n != len(copyRes) {
t.Fatalf("copy res measured %d of %d: %v", n, len(copyRes), err)
}
// READ_PLUS res with one data segment.
rp := AppendReadPlusDataRes(nil, false, 0, []byte("data"))
if n, err := resBodyLen(OpReadPlus, ErrOK, rp); err != nil || n != len(rp) {
t.Fatalf("read plus res measured %d of %d: %v", n, len(rp), err)
}
rph := AppendReadPlusHoleRes(nil, true, 0, 4096)
if n, err := resBodyLen(OpReadPlus, ErrOK, rph); err != nil || n != len(rph) {
t.Fatalf("read plus hole res measured %d of %d: %v", n, len(rph), err)
}
// WRITE_SAME args decode through the adb fields. The block number
// is a count4 on the wire, RFC 7862.
ws := AppendWriteSameArgs(nil, st, StableFileSync, 8, 512, 2, 0, 0, 0, []byte("ab"))
d = xdr.NewDecoder(ws[4:])
if raw, err := d.Raw(16); err != nil || !bytes.Equal(raw, st[:]) {
t.Fatalf("stateid % x: %v", raw, err)
}
if stable, err := d.Uint32(); err != nil || stable != StableFileSync {
t.Fatalf("stable %d: %v", stable, err)
}
if off, err := d.Uint64(); err != nil || off != 8 {
t.Fatalf("offset %d: %v", off, err)
}
for _, want := range []uint64{512, 2, 0} {
if v, err := d.Uint64(); err != nil || v != want {
t.Fatalf("adb field %d: %v", v, err)
}
}
if bn, err := d.Uint32(); err != nil || bn != 0 {
t.Fatalf("adb block num %d: %v", bn, err)
}
if rp, err := d.Uint64(); err != nil || rp != 0 {
t.Fatalf("adb rel off pattern %d: %v", rp, err)
}
if p, err := d.VarOpaque(); err != nil || string(p) != "ab" {
t.Fatalf("pattern %q: %v", p, err)
}
wsr := AppendWriteSameRes(nil, 1024, NfsSyncFileSync, verifier)
if n, err := resBodyLen(OpWriteSame, ErrOK, wsr); err != nil || n != len(wsr) {
t.Fatalf("write same res measured %d of %d: %v", n, len(wsr), err)
}
// The small ops: allocate, io advise, offload status.
al := AppendAllocateArgs(nil, st, 0, 100)
d = xdr.NewDecoder(al[4:])
if _, err := d.Raw(16); err != nil {
t.Fatal(err)
}
if _, err := d.Uint64(); err != nil {
t.Fatal(err)
}
if l, err := d.Uint64(); err != nil || l != 100 {
t.Fatalf("length %d: %v", l, err)
}
hints := AppendIoAdviseRes(nil, Bitmap{}.With(2))
if n, err := resBodyLen(OpIoAdvise, ErrOK, hints); err != nil || n != len(hints) {
t.Fatalf("io advise res measured %d of %d: %v", n, len(hints), err)
}
os := AppendOffloadStatusRes(nil, 64, OffloadSuccess)
if n, err := resBodyLen(OpOffloadStatus, ErrOK, os); err != nil || n != len(os) {
t.Fatalf("offload status res measured %d of %d: %v", n, len(os), err)
}
clone := AppendCloneArgs(nil, st, st, 1, 2, 3)
d = xdr.NewDecoder(clone[4:])
if _, err := d.Raw(16); err != nil {
t.Fatal(err)
}
if _, err := d.Raw(16); err != nil {
t.Fatal(err)
}
for _, want := range []uint64{1, 2, 3} {
if v, err := d.Uint64(); err != nil || v != want {
t.Fatalf("clone field %d: %v", v, err)
}
}
}
func TestOps42RemainingShapes(t *testing.T) {
t.Parallel()
st := Stateid{4, 'C'}
// The remaining arg encoders: op code in front, fields in order.
bodies := map[string][]byte{
"deallocate": AppendDeallocateArgs(nil, st, 1, 2),
"ioadvise": AppendIoAdviseArgs(nil, st, 1, 2, Bitmap{}.With(1)),
"copy": AppendCopyArgs(nil, st, st, 1, 2, 3, true, false, nil),
"copynotify": AppendCopyNotifyArgs(nil, st, 3, "", NetAddr{Netid: "tcp", Uaddr: "1.2.3.4.8.1"}),
"cancel": AppendOffloadCancelArgs(nil, st),
"status": AppendOffloadStatusArgs(nil, st),
"layerr": AppendLayoutErrorArgs(nil, 1, 2, st, layoutTestDevice, ErrBadStateid, OpRead),
"laystats": AppendLayoutStatsArgs(nil, 1, 2, st, 1, 2, 3, 4, layoutTestDevice, 0, nil),
"readplus": AppendReadPlusArgs(nil, st, 1, 512),
}
for _, body := range bodies {
d := xdr.NewDecoder(body[4:])
if _, err := d.Raw(16); err != nil {
t.Fatal(err)
}
}
cn := AppendCopyNotifyRes(nil, 90, st, []CopySourceServer{{Type: 2, Name: "dst"}})
if n, err := resBodyLen(OpCopyNotify, ErrOK, cn); err != nil || n != len(cn) {
t.Fatalf("copy notify res measured %d of %d: %v", n, len(cn), err)
}
// A COPY result whose write response carries the callback stateid
// arm measures through the same shape.
wr := AppendCopyRes(nil, st, true, 10, NfsSyncDataSync, [8]byte{}, false, true)
if n, err := resBodyLen(OpCopy, ErrOK, wr); err != nil || n != len(wr) {
t.Fatalf("copy res with callback measured %d of %d: %v", n, len(wr), err)
}
}
var layoutTestDevice = [16]byte{1, 2, 3}