package winfsp

import (
	"syscall"
	"testing"

	cgofuse "github.com/winfsp/cgofuse/fuse"
)

// The errno values are spelled out so the table stays portable and testable on
// any runner. This pins them to what cgofuse actually decodes, so a divergence
// is a failing test rather than an operation reporting an unrelated error.
func TestErrnoValuesMatchCgofuse(t *testing.T) {
	for _, c := range []struct {
		name string
		ours int
		want int
	}{
		{"EPERM", ePERM, cgofuse.EPERM},
		{"ENOENT", eNOENT, cgofuse.ENOENT},
		{"EINTR", eINTR, cgofuse.EINTR},
		{"EIO", eIO, cgofuse.EIO},
		{"ENXIO", eNXIO, cgofuse.ENXIO},
		{"EBADF", eBADF, cgofuse.EBADF},
		{"EAGAIN", eAGAIN, cgofuse.EAGAIN},
		{"ENOMEM", eNOMEM, cgofuse.ENOMEM},
		{"EACCES", eACCES, cgofuse.EACCES},
		{"EBUSY", eBUSY, cgofuse.EBUSY},
		{"EEXIST", eEXIST, cgofuse.EEXIST},
		{"EXDEV", eXDEV, cgofuse.EXDEV},
		{"ENODEV", eNODEV, cgofuse.ENODEV},
		{"ENOTDIR", eNOTDIR, cgofuse.ENOTDIR},
		{"EISDIR", eISDIR, cgofuse.EISDIR},
		{"EINVAL", eINVAL, cgofuse.EINVAL},
		{"ENFILE", eNFILE, cgofuse.ENFILE},
		{"EMFILE", eMFILE, cgofuse.EMFILE},
		{"EFBIG", eFBIG, cgofuse.EFBIG},
		{"ENOSPC", eNOSPC, cgofuse.ENOSPC},
		{"ESPIPE", eSPIPE, cgofuse.ESPIPE},
		{"EROFS", eROFS, cgofuse.EROFS},
		{"EMLINK", eMLINK, cgofuse.EMLINK},
		{"EPIPE", ePIPE, cgofuse.EPIPE},
		{"ERANGE", eRANGE, cgofuse.ERANGE},
		{"ENAMETOOLONG", eNAMETOOLONG, cgofuse.ENAMETOOLONG},
		{"ENOSYS", eNOSYS, cgofuse.ENOSYS},
		{"ENOTEMPTY", eNOTEMPTY, cgofuse.ENOTEMPTY},
		{"ELOOP", eLOOP, cgofuse.ELOOP},
		{"ENODATA", eNODATA, cgofuse.ENODATA},
		{"ENOTSUP", eNOTSUP, cgofuse.ENOTSUP},
	} {
		if c.ours != c.want {
			t.Errorf("%s = %d, cgofuse uses %d", c.name, c.ours, c.want)
		}
	}
}

// translateOpenFlags relies on cgofuse using MSVC's numbering while the raw
// filesystem reads Go's. Pin that, because a swap between O_EXCL and O_TRUNC
// would turn "fail if it exists" into "truncate it".
func TestOpenFlagTranslation(t *testing.T) {
	for _, c := range []struct {
		name string
		in   int
		want uint32
	}{
		{"O_EXCL", cgofuse.O_EXCL, uint32(syscall.O_EXCL)},
		{"O_TRUNC", cgofuse.O_TRUNC, uint32(syscall.O_TRUNC)},
		{"O_CREAT", cgofuse.O_CREAT, uint32(syscall.O_CREAT)},
		{"O_APPEND", cgofuse.O_APPEND, uint32(syscall.O_APPEND)},
	} {
		if got := translateOpenFlags(c.in); got != c.want {
			t.Errorf("translateOpenFlags(%s=%#x) = %#x, want %#x", c.name, c.in, got, c.want)
		}
	}
	if got := translateOpenFlags(cgofuse.O_RDWR); got != uint32(syscall.O_RDWR) {
		t.Errorf("access mode not preserved: %#x", got)
	}
}

// Setxattr passes cgofuse's flags straight to the raw filesystem, which is
// only correct while the two number them identically. The other side is
// pinned by TestXattrFlagValues in weed/mount, so editing either alone fails.
func TestXattrFlagValues(t *testing.T) {
	if cgofuse.XATTR_CREATE != 1 || cgofuse.XATTR_REPLACE != 2 {
		t.Fatalf("cgofuse xattr flags moved: create=%d replace=%d", cgofuse.XATTR_CREATE, cgofuse.XATTR_REPLACE)
	}
}
