//go:build linux

package vfs

import (
	"archive/tar"
	"bytes"
	"errors"
	"io"
	"os"
	"path/filepath"
	"syscall"
	"testing"

	"github.com/moby/sys/mount"
	"gotest.tools/v3/assert"

	"github.com/docker/docker/daemon/graphdriver"
	"github.com/docker/docker/daemon/graphdriver/graphtest"
)

// This avoids creating a new driver for each test if all tests are run
// Make sure to put new tests between TestVfsSetup and TestVfsTeardown
func TestVfsSetup(t *testing.T) {
	graphtest.GetDriver(t, "vfs")
}

func TestVfsCreateEmpty(t *testing.T) {
	graphtest.DriverTestCreateEmpty(t, "vfs")
}

func TestVfsCreateBase(t *testing.T) {
	graphtest.DriverTestCreateBase(t, "vfs")
}

func TestVfsCreateSnap(t *testing.T) {
	graphtest.DriverTestCreateSnap(t, "vfs")
}

func TestVfsSetQuota(t *testing.T) {
	graphtest.DriverTestSetQuota(t, "vfs", false)
}

func TestVfsTeardown(t *testing.T) {
	graphtest.PutDriver(t)
}

func TestXattrUnsupportedByBackingFS(t *testing.T) {
	rootdir := t.TempDir()
	// The ramfs filesystem is unconditionally compiled into the kernel,
	// and does not support extended attributes.
	err := mount.Mount("ramfs", rootdir, "ramfs", "")
	if errors.Is(err, syscall.EPERM) {
		t.Skip("test requires the ability to mount a filesystem")
	}
	assert.NilError(t, err)
	defer mount.Unmount(rootdir)

	var buf bytes.Buffer
	tw := tar.NewWriter(&buf)
	const (
		filename = "test.txt"
		content  = "hello world\n"
	)
	assert.NilError(t, tw.WriteHeader(&tar.Header{
		Name: filename,
		Mode: 0o644,
		Size: int64(len(content)),
		PAXRecords: map[string]string{
			"SCHILY.xattr.user.test": "helloxattr",
		},
	}))
	_, err = io.WriteString(tw, content)
	assert.NilError(t, err)
	assert.NilError(t, tw.Close())
	testlayer := buf.Bytes()

	for _, tt := range []struct {
		name        string
		opts        []string
		expectErrIs error
	}{
		{
			name:        "Default",
			expectErrIs: syscall.EOPNOTSUPP,
		},
		{
			name: "vfs.xattrs=i_want_broken_containers",
			opts: []string{"vfs.xattrs=i_want_broken_containers"},
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			subdir := filepath.Join(rootdir, tt.name)
			assert.NilError(t, os.Mkdir(subdir, 0o755))
			d, err := graphdriver.New("vfs", graphdriver.Options{
				DriverOptions: tt.opts,
				Root:          subdir,
			})
			assert.NilError(t, err)
			defer d.Cleanup()

			assert.NilError(t, d.Create("test", "", nil))
			_, err = d.ApplyDiff("test", "", bytes.NewReader(testlayer))
			assert.ErrorIs(t, err, tt.expectErrIs)

			if err == nil {
				path, err := d.Get("test", "")
				assert.NilError(t, err)
				defer d.Put("test")
				actual, err := os.ReadFile(filepath.Join(path, filename))
				assert.NilError(t, err)
				assert.Equal(t, string(actual), content)
			}
		})
	}
}
