//
// Copyright 2019-2020 Nestybox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package sysio_test

import (
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/nestybox/sysbox-fs/domain"
	"github.com/nestybox/sysbox-fs/sysio"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
)

var ios domain.IOServiceIface

func TestMain(m *testing.M) {

	// Disable log generation during UT.
	logrus.SetOutput(ioutil.Discard)

	ios = sysio.NewIOService(domain.IOMemFileService)

	m.Run()
}

func TestIOnodeFile_Open(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Open operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("content for file 0123456789"))
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			if err := i.Open(); (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Open() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestIOnodeFile_Read(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	type args struct {
		p []byte
	}

	var a1 = args{
		p: make([]byte, len("content for file 0123456789")),
	}

	tests := []struct {
		name    string
		fields  fields
		args    args
		wantN   int
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Read operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			args:    a1,
			wantN:   len(a1.p),
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("content for file 0123456789"))

				// Open file as Read() expects it to be already opened.
				i.Open()
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			gotN, err := i.Read(tt.args.p)
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Read() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotN != tt.wantN {
				t.Errorf("IOnodeFile.Read() = %v, want %v", gotN, tt.wantN)
			}
		})
	}
}

func TestIOnodeFile_Write(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	type args struct {
		p []byte
	}

	var a1 = args{
		p: []byte("content for file 0123456789"),
	}

	tests := []struct {
		name    string
		fields  fields
		args    args
		wantN   int
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Write operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			args:    a1,
			wantN:   len(a1.p),
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("no content"))

				// Open file as Write() expects it to be already opened.
				i.SetOpenFlags(int(os.O_WRONLY))
				i.Open()

			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to write
			// is not present -- missing file-descriptor.
			//
			name:    "2",
			fields:  f1,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			gotN, err := i.Write(tt.args.p)
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Write() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotN != tt.wantN {
				t.Errorf("IOnodeFile.Write() = %v, want %v", gotN, tt.wantN)
			}
		})
	}
}

func TestIOnodeFile_Close(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Close operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("file content 0123456789"))

				// Open file as Close() expects it to be already opened.
				i.Open()
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			if err := i.Close(); (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Close() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestIOnodeFile_ReadAt(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	type args struct {
		p   []byte
		off int64
	}

	var bytesToRead = 5
	var a1 = args{
		p:   make([]byte, bytesToRead),
		off: int64(len("file content 0123456789") - bytesToRead),
	}

	var a2 = args{
		p:   make([]byte, bytesToRead),
		off: int64(len("file content 0123456789") - bytesToRead + 1),
	}

	tests := []struct {
		name    string
		fields  fields
		args    args
		wantN   int
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular ReadAt operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			args:    a1,
			wantN:   bytesToRead,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("file content 0123456789"))

				// Open file as Close() expects it to be already opened.
				i.Open()
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			args:    a1,
			wantN:   0,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
		{
			//
			// Test-case 3: Verify proper behavior when there's no enough data
			// to read (offset too large). No errors expected.
			//
			name:    "3",
			fields:  f1,
			args:    a2,
			wantN:   bytesToRead - 1,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("file content 0123456789"))

				// Open file as Read() expects it to be already opened.
				i.Open()
			},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			gotN, err := i.ReadAt(tt.args.p, tt.args.off)
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.ReadAt() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if gotN != tt.wantN {
				t.Errorf("IOnodeFile.ReadAt() = %v, want %v", gotN, tt.wantN)
			}
		})
	}
}

func TestIOnodeFile_ReadDirAll(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "net",
		path: "/proc/sys/net",
		mode: 0600,
	}

	// In this case we need to Wipe out the memory-based fs built in
	// previous test-cases.
	ios.RemoveAllIOnodes()

	// Build expected-response slice.
	var expectedResult = []os.FileInfo{
		&domain.FileInfo{
			Fname:  "ipv4",
			FisDir: true,
		},
		&domain.FileInfo{
			Fname:  "ipv6",
			FisDir: true,
		},
	}

	// Create memfs entries corresponding to above expectedResult.
	base := ios.NewIOnode(f1.name, f1.path, 0)
	if err := base.Mkdir(); err != nil {
		t.Errorf("Could not create base-dir %s element", base.Path())
	}
	for _, v := range expectedResult {
		i := ios.NewIOnode(v.Name(), base.Path()+"/"+v.Name(), 0)
		if err := i.Mkdir(); err != nil {
			t.Errorf("Could not create expectedResult %s element", i.Path())
		}
	}

	tests := []struct {
		name    string
		fields  fields
		want    []os.FileInfo
		wantErr bool
		prepare func(i domain.IOnodeIface)
		unwind  func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular ReadDirAll operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			want:    expectedResult,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			want:    nil,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			got, err := i.ReadDirAll()
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.ReadDirAll() error = %v, wantErr %v",
					err, tt.wantErr)
				return
			}

			assert.Equal(t, len(tt.want), len(got))
			for i, v := range got {
				assert.Equal(t, v.Name(), tt.want[i].Name())
				assert.Equal(t, v.ModTime(), tt.want[i].ModTime())
				assert.Equal(t, v.IsDir(), tt.want[i].IsDir())
			}

			// Wipe out memfs entries.
			ios.RemoveAllIOnodes()
		})
	}
}

func TestIOnodeFile_ReadFile(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		want    []byte
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular ReadFile operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			want:    []byte("file content 0123456789"),
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("file content 0123456789"))
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			want:    nil,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			got, err := i.ReadFile()
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.ReadFile() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("IOnodeFile.ReadFile() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestIOnodeFile_ReadLine(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		want    string
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular ReadLine operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			want:    "line 1",
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("line 1\nline 2\nline 3"))
			},
		},
		{
			//
			// Test-case 2: Verify proper behavior when file where to operate
			// is not present.
			//
			name:    "2",
			fields:  f1,
			want:    "",
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			got, err := i.ReadLine()
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.ReadLine() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if got != tt.want {
				t.Errorf("IOnodeFile.ReadLine() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestIOnodeFile_WriteFile(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	type args struct {
		p []byte
	}

	var a1 = args{
		p: []byte("file content 0123456789"),
	}

	tests := []struct {
		name    string
		fields  fields
		args    args
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular WriteFile operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			args:    a1,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			if err := i.WriteFile(tt.args.p); (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.WriteFile() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}

func TestIOnodeFile_Mkdir(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "net",
		path: "/proc/sys/net",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Mkdir operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			if err := i.Mkdir(); (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Mkdir() error = %v, wantErr %v", err, tt.wantErr)
			}

			if _, err := i.Stat(); err != nil {
				t.Errorf("Directory %v was not properly created", i.Path())
			}
		})
	}
}

func TestIOnodeFile_MkdirAll(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "ipv4",
		path: "/proc/sys/net/ipv4",
		mode: 0600,
	}
	var f2 = fields{
		name: "net",
		path: "/proc/sys/net",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields1 fields
		fields2 fields
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular MkdirAll operation. No errors expected.
			//
			name:    "1",
			fields1: f1,
			fields2: f2,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i1 := ios.NewIOnode(
				tt.fields1.name,
				tt.fields1.path,
				tt.fields1.mode,
			)
			i2 := ios.NewIOnode(
				tt.fields2.name,
				tt.fields2.path,
				tt.fields2.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i1)
			}

			if err := i1.MkdirAll(); (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.MkdirAll() error = %v, wantErr %v", err, tt.wantErr)
			}

			// Verify that both "/proc/sys/net" and /proc/sys/net/ipv4" folders
			// are created in Memfs.
			if _, err := i1.Stat(); err != nil {
				t.Errorf("Directory %v was not properly created", i1.Path())
			}
			if _, err := i2.Stat(); err != nil {
				t.Errorf("Directory %v was not properly created", i2.Path())
			}
		})
	}
}

// Notice that we are mainly testing the Memfs specific code-path of this
// method, so there's not much value in having this UT.
func TestIOnodeFile_GetNsInode(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "user",
		path: "/proc/1001/ns/user",
		mode: 0600,
	}

	tests := []struct {
		name    string
		fields  fields
		want    domain.Inode
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: GetNsInode operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			want:    123456,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {

				// Create memfs file.
				i.WriteFile([]byte("123456"))
			},
		},
		{
			//
			// Test-case 2: Verify proper operation when file is not present.
			//
			name:    "2",
			fields:  f1,
			want:    0,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Initialize memory-based fs.
			ios.RemoveAllIOnodes()

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			got, err := i.GetNsInode()
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.GetNsInode() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("IOnodeFile.GetNsInode() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestIOnodeFile_Stat(t *testing.T) {
	type fields struct {
		name string
		path string
		mode os.FileMode
	}

	var f1 = fields{
		name: "node_1",
		path: "/proc/sys/net/node_1",
		mode: 0600,
	}

	// Create memfs file.
	expectedResultIOnode := ios.NewIOnode("", "/proc/sys/net/node_1", 0)
	expectedResultIOnode.WriteFile([]byte("file content 0123456789"))
	expectedResult, err := expectedResultIOnode.Stat()
	if err != nil {
		t.Errorf("Could not create expected_result attribute")
	}

	tests := []struct {
		name    string
		fields  fields
		want    os.FileInfo
		wantErr bool
		prepare func(i domain.IOnodeIface)
	}{
		{
			//
			// Test-case 1: Regular Stat operation. No errors expected.
			//
			name:    "1",
			fields:  f1,
			want:    expectedResult,
			wantErr: false,
			prepare: func(i domain.IOnodeIface) {},
		},
		{
			//
			// Test-case 2: Verify proper operation when file is not present.
			//
			name:    "2",
			fields:  f1,
			want:    nil,
			wantErr: true,
			prepare: func(i domain.IOnodeIface) {},
		},
	}

	//
	// Testcase executions.
	//
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			i := ios.NewIOnode(
				tt.fields.name,
				tt.fields.path,
				tt.fields.mode,
			)

			// Prepare the mocks.
			if tt.prepare != nil {
				tt.prepare(i)
			}

			got, err := i.Stat()
			if (err != nil) != tt.wantErr {
				t.Errorf("IOnodeFile.Stat() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(got, tt.want) {
				t.Errorf("IOnodeFile.Stat() = %v, want %v", got, tt.want)
			}

			// Re-initialize memory-based fs.
			ios.RemoveAllIOnodes()
		})
	}
}
