minio/posix_test.go

1464 lines
48 KiB
Go
Raw Normal View History

/*
* Minio Cloud Storage, (C) 2016 Minio, 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
*
* http://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 main
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
slashpath "path"
"runtime"
"strings"
"syscall"
"testing"
)
// creates a temp dir and sets up posix layer.
// returns posix layer, temp dir path to be used for the purpose of tests.
func newPosixTestSetup() (StorageAPI, string, error) {
diskPath, err := ioutil.TempDir(os.TempDir(), "minio-")
if err != nil {
return nil, "", err
}
posixStorage, err := newPosix(diskPath)
if err != nil {
return nil, "", err
}
return posixStorage, diskPath, nil
}
// Tests posix.getDiskInfo()
func TestGetDiskInfo(t *testing.T) {
path, err := ioutil.TempDir(os.TempDir(), "minio-")
if err != nil {
t.Fatalf("Unable to create a temporary directory, %s", err)
}
defer removeAll(path)
testCases := []struct {
diskPath string
expectedErr error
}{
{path, nil},
{"/nonexistent-dir", errDiskNotFound},
}
// Check test cases.
for _, testCase := range testCases {
if _, err := getDiskInfo(testCase.diskPath); err != testCase.expectedErr {
t.Fatalf("expected: %s, got: %s", testCase.expectedErr, err)
}
}
}
// TestReadAll - Tests the functionality implemented by posix ReadAll storage API.
func TestReadAll(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Create files for the test cases.
if err = posixStorage.MakeVol("exists"); err != nil {
t.Fatalf("Unable to create a volume \"exists\", %s", err)
}
if err = posixStorage.AppendFile("exists", "as-directory/as-file", []byte("Hello, World")); err != nil {
t.Fatalf("Unable to create a file \"as-directory/as-file\", %s", err)
}
if err = posixStorage.AppendFile("exists", "as-file", []byte("Hello, World")); err != nil {
t.Fatalf("Unable to create a file \"as-file\", %s", err)
}
if err = posixStorage.AppendFile("exists", "as-file-parent", []byte("Hello, World")); err != nil {
t.Fatalf("Unable to create a file \"as-file-parent\", %s", err)
}
// Testcases to validate different conditions for ReadAll API.
testCases := []struct {
volume string
path string
err error
}{
// Test case - 1.
// Validate volume does not exist.
{
volume: "i-dont-exist",
path: "",
err: errVolumeNotFound,
},
// Test case - 2.
// Validate bad condition file does not exist.
{
volume: "exists",
path: "as-file-not-found",
err: errFileNotFound,
},
// Test case - 3.
// Validate bad condition file exists as prefix/directory and
// we are attempting to read it.
{
volume: "exists",
path: "as-directory",
err: errFileNotFound,
},
// Test case - 4.
{
volume: "exists",
path: "as-file-parent/as-file",
err: errFileNotFound,
},
// Test case - 5.
// Validate the good condition file exists and we are able to read it.
{
volume: "exists",
path: "as-file",
err: nil,
},
// Test case - 6.
// Test case with invalid volume name.
{
volume: "ab",
path: "as-file",
err: errInvalidArgument,
},
}
var dataRead []byte
// Run through all the test cases and validate for ReadAll.
for i, testCase := range testCases {
dataRead, err = posixStorage.ReadAll(testCase.volume, testCase.path)
if err != testCase.err {
t.Fatalf("Test %d: Expected err \"%s\", got err \"%s\"", i+1, testCase.err, err)
}
if err == nil {
if string(dataRead) != string([]byte("Hello, World")) {
t.Errorf("Test %d: Expected the data read to be \"%s\", but instead got \"%s\"", i+1, "Hello, World", string(dataRead))
}
}
}
// Testing for faulty disk.
// Setting ioErrCount > maxAllowedIOError.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(6)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
_, err = posixStorage.ReadAll("abcd", "efg")
if err != errFaultyDisk {
t.Errorf("Expected err \"%s\", got err \"%s\"", errFaultyDisk, err)
}
}
// TestNewPosix all the cases handled in posix storage layer initialization.
func TestNewPosix(t *testing.T) {
// Temporary dir name.
tmpDirName := os.TempDir() + "/" + "minio-" + nextSuffix()
// Temporary file name.
tmpFileName := os.TempDir() + "/" + "minio-" + nextSuffix()
f, _ := os.Create(tmpFileName)
f.Close()
defer os.Remove(tmpFileName)
// List of all tests for posix initialization.
testCases := []struct {
diskPath string
err error
}{
// Validates input argument cannot be empty.
{
"",
errInvalidArgument,
},
// Validates if the directory does not exist and
// gets automatically created.
{
tmpDirName,
nil,
},
// Validates if the disk exists as file and returns error
// not a directory.
{
tmpFileName,
syscall.ENOTDIR,
},
}
// Validate all test cases.
for i, testCase := range testCases {
_, err := newPosix(testCase.diskPath)
if err != testCase.err {
t.Fatalf("Test %d failed wanted: %s, got: %s", i+1, err, testCase.err)
}
}
}
// TestMakeVol - Test validate the logic for creation of new posix volume.
// Asserts the failures too against the expected failures.
func TestMakeVol(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
// Create a file.
if err := ioutil.WriteFile(slashpath.Join(path, "vol-as-file"), []byte{}, os.ModePerm); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
// Create a directory.
if err := os.Mkdir(slashpath.Join(path, "existing-vol"), 0777); err != nil {
t.Fatalf("Unable to create directory, %s", err)
}
testCases := []struct {
volName string
ioErrCount int
expectedErr error
}{
// Test case - 1.
// A valid case, volume creation is expected to succeed.
{
volName: "success-vol",
ioErrCount: 0,
expectedErr: nil,
},
// Test case - 2.
// Case where a file exists by the name of the volume to be created.
{
volName: "vol-as-file",
ioErrCount: 0,
expectedErr: errVolumeExists,
},
// Test case - 3.
{
volName: "existing-vol",
ioErrCount: 0,
expectedErr: errVolumeExists,
},
// Test case - 4.
// IO error > maxAllowedIOError, should fail with errFaultyDisk.
{
volName: "vol",
ioErrCount: 6,
expectedErr: errFaultyDisk,
},
// Test case - 5.
// Test case with invalid volume name.
{
volName: "ab",
ioErrCount: 0,
expectedErr: errInvalidArgument,
},
}
for i, testCase := range testCases {
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCount)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if err := posixStorage.MakeVol(testCase.volName); err != testCase.expectedErr {
t.Fatalf("Test %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
}
}
// Test for permission denied.
if runtime.GOOS != "windows" {
// Initialize posix storage layer for permission denied error.
posix, err := newPosix("/usr")
if err != nil {
t.Fatalf("Unable to initialize posix, %s", err)
}
if err := posix.MakeVol("test-vol"); err != errDiskAccessDenied {
t.Fatalf("expected: %s, got: %s", errDiskAccessDenied, err)
}
}
}
// TestDeleteVol - Validates the expected behaviour of posix.DeleteVol for various cases.
func TestDeleteVol(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
// Test failure cases.
vol := slashpath.Join(path, "nonempty-vol")
if err = os.Mkdir(vol, 0777); err != nil {
t.Fatalf("Unable to create directory, %s", err)
}
if err = ioutil.WriteFile(slashpath.Join(vol, "test-file"), []byte{}, os.ModePerm); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
testCases := []struct {
volName string
ioErrCount int
expectedErr error
}{
// Test case - 1.
// A valida case. Empty vol, should be possible to delete.
{
volName: "success-vol",
ioErrCount: 0,
expectedErr: nil,
},
// Test case - 2.
// volume is non-existent.
{
volName: "nonexistent-vol",
ioErrCount: 0,
expectedErr: errVolumeNotFound,
},
// Test case - 3.
// It shouldn't be possible to delete an non-empty volume, validating the same.
{
volName: "nonempty-vol",
ioErrCount: 0,
expectedErr: errVolumeNotEmpty,
},
// Test case - 4.
// IO error > maxAllowedIOError, should fail with errFaultyDisk.
{
volName: "my-disk",
ioErrCount: 6,
expectedErr: errFaultyDisk,
},
// Test case - 5.
// Invalid volume name.
{
volName: "ab",
ioErrCount: 0,
expectedErr: errInvalidArgument,
},
}
for i, testCase := range testCases {
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCount)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if err = posixStorage.DeleteVol(testCase.volName); err != testCase.expectedErr {
t.Fatalf("Test: %d, expected: %s, got: %s", i+1, testCase.expectedErr, err)
}
}
// Test for permission denied.
if runtime.GOOS != "windows" {
// Initialize posix storage layer for permission denied error.
posixStorage, err = newPosix("/usr")
if err != nil {
t.Fatalf("Unable to initialize posix, %s", err)
}
if err = posixStorage.DeleteVol("bin"); !os.IsPermission(err) {
t.Fatalf("expected: Permission error, got: %s", err)
}
}
posixDeletedStorage, diskPath, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
// removing the disk, used to recreate disk not found error.
removeAll(diskPath)
// Test for delete on an removed disk.
// should fail with disk not found.
err = posixDeletedStorage.DeleteVol("Del-Vol")
if err != errDiskNotFound {
t.Errorf("Expected: \"Disk not found\", got \"%s\"", err)
}
}
// TestStatVol - Tests validate the volume info returned by posix.StatVol() for various inputs.
func TestStatVol(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
testCases := []struct {
volName string
ioErrCount int
expectedErr error
}{
// Test case - 1.
{
volName: "success-vol",
ioErrCount: 0,
expectedErr: nil,
},
// Test case - 2.
{
volName: "nonexistent-vol",
ioErrCount: 0,
expectedErr: errVolumeNotFound,
},
// Test case - 3.
{
volName: "success-vol",
ioErrCount: 6,
expectedErr: errFaultyDisk,
},
// Test case - 4.
{
volName: "ab",
ioErrCount: 0,
expectedErr: errInvalidArgument,
},
}
for i, testCase := range testCases {
var volInfo VolInfo
// setting ioErrCnt from the test case.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCount)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
volInfo, err = posixStorage.StatVol(testCase.volName)
if err != testCase.expectedErr {
t.Fatalf("Test case : %d, Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
}
if err == nil {
if volInfo.Name != volInfo.Name {
t.Errorf("Test case %d: Expected the volume name to be \"%s\", instead found \"%s\"", i+1, volInfo.Name, volInfo.Name)
}
}
}
posixDeletedStorage, diskPath, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
// removing the disk, used to recreate disk not found error.
removeAll(diskPath)
// Test for delete on an removed disk.
// should fail with disk not found.
_, err = posixDeletedStorage.StatVol("Stat vol")
if err != errDiskNotFound {
t.Errorf("Expected: \"Disk not found\", got \"%s\"", err)
}
}
// TestListVols - Validates the result and the error output for posix volume listing functionality posix.ListVols().
func TestListVols(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
var volInfo []VolInfo
// Test empty list vols.
if volInfo, err = posixStorage.ListVols(); err != nil {
t.Fatalf("expected: <nil>, got: %s", err)
} else if len(volInfo) != 0 {
t.Fatalf("expected: [], got: %s", volInfo)
}
// Test non-empty list vols.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if volInfo, err = posixStorage.ListVols(); err != nil {
t.Fatalf("expected: <nil>, got: %s", err)
} else if len(volInfo) != 1 {
t.Fatalf("expected: 1, got: %d", len(volInfo))
} else if volInfo[0].Name != "success-vol" {
t.Errorf("expected: success-vol, got: %s", volInfo[0].Name)
}
// setting ioErrCnt to be > maxAllowedIOError.
// should fail with errFaultyDisk.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(6)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if _, err = posixStorage.ListVols(); err != errFaultyDisk {
t.Errorf("Expected to fail with \"%s\", but instead failed with \"%s\"", errFaultyDisk, err)
}
// removing the path and simulating disk failure
removeAll(path)
// Resetting the IO error.
// should fail with errDiskNotFound.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(0)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if _, err = posixStorage.ListVols(); err != errDiskNotFound {
t.Errorf("Expected to fail with \"%s\", but instead failed with \"%s\"", errDiskNotFound, err)
}
// creating a new posix instance.
posixStorage, path, err = newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// adding the segment of the path length of the volume to be > 255.
if posixType, ok := posixStorage.(*posix); ok {
// setting the disk Path with name whose segment length > 255.
posixType.diskPath = "my-vol-name-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if _, err = posixStorage.ListVols(); err != errFileNameTooLong {
t.Errorf("Expected to fail with \"%s\", but instead failed with \"%s\"", errFileNameTooLong, err)
}
}
// TestPosixListDir - Tests validate the directory listing functionality provided by posix.ListDir .
func TestPosixListDir(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// create posix test setup.
posixDeletedStorage, diskPath, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
// removing the disk, used to recreate disk not found error.
removeAll(diskPath)
// Setup test environment.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if err = posixStorage.AppendFile("success-vol", "abc/def/ghi/success-file", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err = posixStorage.AppendFile("success-vol", "abc/xyz/ghi/success-file", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
testCases := []struct {
srcVol string
srcPath string
ioErrCnt int
// expected result.
expectedListDir []string
expectedErr error
}{
// Test case - 1.
// valid case with existing volume and file to delete.
{
srcVol: "success-vol",
srcPath: "abc",
ioErrCnt: 0,
expectedListDir: []string{"def/", "xyz/"},
expectedErr: nil,
},
// Test case - 1.
// valid case with existing volume and file to delete.
{
srcVol: "success-vol",
srcPath: "abc/def",
ioErrCnt: 0,
expectedListDir: []string{"ghi/"},
expectedErr: nil,
},
// Test case - 1.
// valid case with existing volume and file to delete.
{
srcVol: "success-vol",
srcPath: "abc/def/ghi",
ioErrCnt: 0,
expectedListDir: []string{"success-file"},
expectedErr: nil,
},
// Test case - 2.
{
srcVol: "success-vol",
srcPath: "abcdef",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 3.
// Test case with invalid volume name.
{
srcVol: "ab",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errInvalidArgument,
},
// Test case - 4.
// Test case with io error count > max limit.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 6,
expectedErr: errFaultyDisk,
},
// Test case - 5.
// Test case with non existent volume.
{
srcVol: "non-existent-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errVolumeNotFound,
},
}
for i, testCase := range testCases {
var dirList []string
// setting ioErrCnt from the test case.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCnt)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
dirList, err = posixStorage.ListDir(testCase.srcVol, testCase.srcPath)
if err != testCase.expectedErr {
t.Fatalf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
}
if err == nil {
for _, expected := range testCase.expectedListDir {
if !strings.Contains(strings.Join(dirList, ","), expected) {
t.Errorf("Test case %d: Expected the directory listing to be \"%v\", but got \"%v\"", i+1, testCase.expectedListDir, dirList)
}
}
}
}
// Test for permission denied.
if runtime.GOOS != "windows" {
// Initialize posix storage layer for permission denied error.
posixStorage, err = newPosix("/usr")
if err != nil {
t.Errorf("Unable to initialize posix, %s", err)
}
if err = posixStorage.DeleteFile("bin", "yes"); !os.IsPermission(err) {
t.Errorf("expected: Permission error, got: %s", err)
}
}
// Test for delete on an removed disk.
// should fail with disk not found.
err = posixDeletedStorage.DeleteFile("del-vol", "my-file")
if err != errDiskNotFound {
t.Errorf("Expected: \"Disk not found\", got \"%s\"", err)
}
}
// TestDeleteFile - Series of test cases construct valid and invalid input data and validates the result and the error response.
func TestDeleteFile(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// create posix test setup
posixDeletedStorage, diskPath, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
// removing the disk, used to recreate disk not found error.
removeAll(diskPath)
// Setup test environment.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if err = posixStorage.AppendFile("success-vol", "success-file", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
testCases := []struct {
srcVol string
srcPath string
ioErrCnt int
expectedErr error
}{
// Test case - 1.
// valid case with existing volume and file to delete.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 2.
// The file was deleted in the last case, so DeleteFile should fail.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 3.
// Test case with io error count > max limit.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 6,
expectedErr: errFaultyDisk,
},
// Test case - 4.
// Test case with segment of the volume name > 255.
{
srcVol: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errInvalidArgument,
},
// Test case - 5.
// Test case with non-existent volume.
{
srcVol: "non-existent-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errVolumeNotFound,
},
// Test case - 6.
// Test case with src path segment > 255.
{
srcVol: "success-vol",
srcPath: "my-obj-del-0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
ioErrCnt: 0,
expectedErr: errFileNameTooLong,
},
}
for i, testCase := range testCases {
// setting ioErrCnt from the test case.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCnt)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if err = posixStorage.DeleteFile(testCase.srcVol, testCase.srcPath); err != testCase.expectedErr {
t.Errorf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
}
}
// Test for permission denied.
if runtime.GOOS != "windows" {
// Initialize posix storage layer for permission denied error.
posixStorage, err = newPosix("/usr")
if err != nil {
t.Errorf("Unable to initialize posix, %s", err)
}
if err = posixStorage.DeleteFile("bin", "yes"); !os.IsPermission(err) {
t.Errorf("expected: Permission error, got: %s", err)
}
}
// Test for delete on an removed disk.
// should fail with disk not found.
err = posixDeletedStorage.DeleteFile("del-vol", "my-file")
if err != errDiskNotFound {
t.Errorf("Expected: \"Disk not found\", got \"%s\"", err)
}
}
// TestReadFile - Tests posix.ReadFile with wide range of cases and asserts the result and error response.
func TestReadFile(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
volume := "success-vol"
// Setup test environment.
if err = posixStorage.MakeVol(volume); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
// Create directory to make errIsNotRegular
if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0777); err != nil {
t.Fatalf("Unable to create directory, %s", err)
}
testCases := []struct {
volume string
fileName string
offset int64
bufSize int
expectedBuf []byte
expectedErr error
}{
// Successful read at offset 0 and proper buffer size. - 1
{
volume, "myobject", 0, 5,
[]byte("hello"), nil,
},
// Success read at hierarchy. - 2
{
volume, "path/to/my/object", 0, 5,
[]byte("hello"), nil,
},
// One path segment length is 255 chars long. - 3
{
volume, "path/to/my/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
0, 5, []byte("hello"), nil},
// Whole path is 1024 characters long, success case. - 4
{
volume, "level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
0, 5, []byte("hello"),
func() error {
// On darwin HFS does not support > 1024 characters.
if runtime.GOOS == "darwin" {
return errFileNameTooLong
}
// On all other platforms return success.
return nil
}(),
},
// Object is a directory. - 5
{
volume, "object-as-dir",
0, 5, nil, errIsNotRegular},
// One path segment length is > 255 chars long. - 6
{
volume, "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
0, 5, nil, errFileNameTooLong},
// Path length is > 1024 chars long. - 7
{
volume, "level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
0, 5, nil, errFileNameTooLong},
// Buffer size greater than object size. - 8
{
volume, "myobject", 0, 16,
[]byte("hello, world"),
io.ErrUnexpectedEOF,
},
// Reading from an offset success. - 9
{
volume, "myobject", 7, 5,
[]byte("world"), nil,
},
// Reading from an object but buffer size greater. - 10
{
volume, "myobject",
7, 8,
[]byte("world"),
io.ErrUnexpectedEOF,
},
// Seeking into a wrong offset, return PathError. - 11
{
volume, "myobject",
-1, 5,
nil,
func() error {
if runtime.GOOS == "windows" {
return &os.PathError{
Op: "seek",
Path: preparePath(slashpath.Join(path, "success-vol", "myobject")),
Err: errors.New("An attempt was made to move the file pointer before the beginning of the file."),
}
}
return &os.PathError{
Op: "seek",
Path: preparePath(slashpath.Join(path, "success-vol", "myobject")),
Err: os.ErrInvalid,
}
}(),
},
// Seeking ahead returns io.EOF. - 12
{
volume, "myobject", 14, 1, nil, io.EOF,
},
// Empty volume name. - 13
{
"", "myobject", 14, 1, nil, errInvalidArgument,
},
// Empty filename name. - 14
{
volume, "", 14, 1, nil, errIsNotRegular,
},
// Non existent volume name - 15.
{
"abcd", "", 14, 1, nil, errVolumeNotFound,
},
// Non existent filename - 16.
{
volume, "abcd", 14, 1, nil, errFileNotFound,
},
}
// Create all files needed during testing.
appendFiles := testCases[:4]
// Create test files for further reading.
for i, appendFile := range appendFiles {
err = posixStorage.AppendFile(volume, appendFile.fileName, []byte("hello, world"))
if err != appendFile.expectedErr {
t.Fatalf("Creating file failed: %d %#v, expected: %s, got: %s", i+1, appendFile, appendFile.expectedErr, err)
}
}
// Following block validates all ReadFile test cases.
for i, testCase := range testCases {
var n int64
// Common read buffer.
var buf = make([]byte, testCase.bufSize)
n, err = posixStorage.ReadFile(testCase.volume, testCase.fileName, testCase.offset, buf)
if err != nil && testCase.expectedErr != nil {
// Validate if the type string of the errors are an exact match.
if err.Error() != testCase.expectedErr.Error() {
t.Errorf("Case: %d %#v, expected: %s, got: %s", i+1, testCase, testCase.expectedErr, err)
}
// Err unexpected EOF special case, where we verify we have provided a larger
// buffer than the data itself, but the results are in-fact valid. So we validate
// this error condition specifically treating it as a good condition with valid
// results. In this scenario return 'n' is always lesser than the input buffer.
if err == io.ErrUnexpectedEOF {
if !bytes.Equal(testCase.expectedBuf, buf[:n]) {
t.Errorf("Case: %d %#v, expected: \"%s\", got: \"%s\"", i+1, testCase, string(testCase.expectedBuf), string(buf[:testCase.bufSize]))
}
if n > int64(len(buf)) {
t.Errorf("Case: %d %#v, expected: %d, got: %d", i+1, testCase, testCase.bufSize, n)
}
}
}
// ReadFile has returned success, but our expected error is non 'nil'.
if err == nil && err != testCase.expectedErr {
t.Errorf("Case: %d %#v, expected: %s, got :%s", i+1, testCase, testCase.expectedErr, err)
}
// Expected error retured, proceed further to validate the returned results.
if err == nil && err == testCase.expectedErr {
if !bytes.Equal(testCase.expectedBuf, buf) {
t.Errorf("Case: %d %#v, expected: \"%s\", got: \"%s\"", i+1, testCase, string(testCase.expectedBuf), string(buf[:testCase.bufSize]))
}
if n != int64(testCase.bufSize) {
t.Errorf("Case: %d %#v, expected: %d, got: %d", i+1, testCase, testCase.bufSize, n)
}
}
}
// Test for permission denied.
if runtime.GOOS == "linux" {
// Initialize posix storage layer for permission denied error.
posixStorage, err = newPosix("/")
if err != nil {
t.Errorf("Unable to initialize posix, %s", err)
}
if err == nil {
// Common read buffer.
var buf = make([]byte, 10)
if _, err = posixStorage.ReadFile("proc", "1/fd", 0, buf); err != errFileAccessDenied {
t.Errorf("expected: %s, got: %s", errFileAccessDenied, err)
}
}
}
// Testing for faulty disk.
// setting ioErrCnt to 6.
// should fail with errFaultyDisk.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(6)
// Common read buffer.
var buf = make([]byte, 10)
_, err = posixType.ReadFile("abc", "yes", 0, buf)
if err != errFaultyDisk {
t.Fatalf("Expected \"Faulty Disk\", got: \"%s\"", err)
}
} else {
t.Fatalf("Expected the StorageAPI to be of type *posix")
}
}
// Test posix.AppendFile()
func TestAppendFile(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
if err = posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
// Create directory to make errIsNotRegular
if err = os.Mkdir(slashpath.Join(path, "success-vol", "object-as-dir"), 0777); err != nil {
t.Fatalf("Unable to create directory, %s", err)
}
testCases := []struct {
fileName string
expectedErr error
}{
{"myobject", nil},
{"path/to/my/object", nil},
// Test to append to previously created file.
{"myobject", nil},
// Test to use same path of previously created file.
{"path/to/my/testobject", nil},
// One path segment length is 255 chars long.
{"path/to/my/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", nil},
{"object-as-dir", errIsNotRegular},
// path segment uses previously uploaded object.
{"myobject/testobject", errFileAccessDenied},
// One path segment length is > 255 chars long.
{"path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong},
// path length is > 1024 chars long.
{"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", errFileNameTooLong},
}
// Add path length > 1024 test specially as OS X system does not support 1024 long path.
err = errFileNameTooLong
if runtime.GOOS != "darwin" {
err = nil
}
// path length is 1024 chars long.
testCases = append(testCases, struct {
fileName string
expectedErr error
}{"level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002/level0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003/object000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", err})
for _, testCase := range testCases {
if err = posixStorage.AppendFile("success-vol", testCase.fileName, []byte("hello, world")); err != testCase.expectedErr {
t.Errorf("Case: %s, expected: %s, got: %s", testCase, testCase.expectedErr, err)
}
}
// Test for permission denied.
if runtime.GOOS != "windows" {
// Initialize posix storage layer for permission denied error.
posixStorage, err = newPosix("/usr")
if err != nil {
t.Fatalf("Unable to initialize posix, %s", err)
}
if err = posixStorage.AppendFile("bin", "yes", []byte("hello, world")); !os.IsPermission(err) {
t.Errorf("expected: Permission error, got: %s", err)
}
}
// Test case with invalid volume name.
// A valid volume name should be atleast of size 3.
err = posixStorage.AppendFile("bn", "yes", []byte("hello, world"))
if err != errInvalidArgument {
t.Fatalf("expected: \"Invalid argument error\", got: \"%s\"", err)
}
// Test case with IO error count > max limit.
// setting ioErrCnt to 6.
// should fail with errFaultyDisk.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(6)
err = posixType.AppendFile("abc", "yes", []byte("hello, world"))
if err != errFaultyDisk {
t.Fatalf("Expected \"Faulty Disk\", got: \"%s\"", err)
}
} else {
t.Fatalf("Expected the StorageAPI to be of type *posix")
}
}
// Test posix.RenameFile()
func TestRenameFile(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
if err := posixStorage.MakeVol("src-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if err := posixStorage.MakeVol("dest-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "file1", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "file2", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "file3", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "file4", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "file5", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("src-vol", "path/to/file1", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
testCases := []struct {
srcVol string
destVol string
srcPath string
destPath string
ioErrCnt int
expectedErr error
}{
// Test case - 1.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file1",
destPath: "file-one",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 2.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "path/",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 3.
// Test to overwrite destination file.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file2",
destPath: "file-one",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 4.
// Test case with io error count set to 1.
// expected not to fail.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file3",
destPath: "file-two",
ioErrCnt: 1,
expectedErr: nil,
},
// Test case - 5.
// Test case with io error count set to maximum allowed count.
// expected not to fail.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file4",
destPath: "file-three",
ioErrCnt: 5,
expectedErr: nil,
},
// Test case - 6.
// Test case with non-existent source file.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "non-existent-file",
destPath: "file-three",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 7.
// Test to check failure of source and destination are not same type.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "path/",
destPath: "file-one",
ioErrCnt: 0,
expectedErr: errFileAccessDenied,
},
// Test case - 8.
// Test to check failure of destination directory exists.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "path/",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errFileAccessDenied,
},
// Test case - 9.
// Test case with io error count is greater than maxAllowedIOError.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "path/",
destPath: "new-path/",
ioErrCnt: 6,
expectedErr: errFaultyDisk,
},
// Test case - 10.
// Test case with source being a file and destination being a folder.
// Either both have to be files or directories.
// Expecting to fail with `errFileAccessDenied`.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errFileAccessDenied,
},
// Test case - 11.
// Test case with non-existent source volume.
// Expecting to fail with `errVolumeNotFound`.
{
srcVol: "src-vol-non-existent",
destVol: "dest-vol",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errVolumeNotFound,
},
// Test case - 12.
// Test case with non-existent destination volume.
// Expecting to fail with `errVolumeNotFound`.
{
srcVol: "src-vol",
destVol: "dest-vol-non-existent",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errVolumeNotFound,
},
// Test case - 13.
// Test case with invalid src volume name. Length should be atleast 3.
// Expecting to fail with `errInvalidArgument`.
{
srcVol: "ab",
destVol: "dest-vol-non-existent",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errInvalidArgument,
},
// Test case - 14.
// Test case with invalid destination volume name. Length should be atleast 3.
// Expecting to fail with `errInvalidArgument`.
{
srcVol: "abcd",
destVol: "ef",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errInvalidArgument,
},
// Test case - 15.
// Test case with invalid destination volume name. Length should be atleast 3.
// Expecting to fail with `errInvalidArgument`.
{
srcVol: "abcd",
destVol: "ef",
srcPath: "file4",
destPath: "new-path/",
ioErrCnt: 0,
expectedErr: errInvalidArgument,
},
// Test case - 16.
// Test case with the parent of the destination being a file.
// expected to fail with `errFileAccessDenied`.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file5",
destPath: "file-one/parent-is-file",
ioErrCnt: 0,
expectedErr: errFileAccessDenied,
},
// Test case - 17.
// Test case with segment of source file name more than 255.
// expected not to fail.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
destPath: "file-six",
ioErrCnt: 0,
expectedErr: errFileNameTooLong,
},
// Test case - 18.
// Test case with segment of destination file name more than 255.
// expected not to fail.
{
srcVol: "src-vol",
destVol: "dest-vol",
srcPath: "file6",
destPath: "path/to/my/object0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
ioErrCnt: 0,
expectedErr: errFileNameTooLong,
},
}
for i, testCase := range testCases {
// setting ioErrCnt from the test case.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCnt)
} else {
t.Fatalf("Expected the StorageAPI to be of type *posix")
}
if err := posixStorage.RenameFile(testCase.srcVol, testCase.srcPath, testCase.destVol, testCase.destPath); err != testCase.expectedErr {
t.Fatalf("Test %d: Expected the error to be : \"%v\", got: \"%v\".", i+1, testCase.expectedErr, err)
}
}
}
// Test posix.StatFile()
func TestStatFile(t *testing.T) {
// create posix test setup
posixStorage, path, err := newPosixTestSetup()
if err != nil {
t.Fatalf("Unable to create posix test setup, %s", err)
}
defer removeAll(path)
// Setup test environment.
if err := posixStorage.MakeVol("success-vol"); err != nil {
t.Fatalf("Unable to create volume, %s", err)
}
if err := posixStorage.AppendFile("success-vol", "success-file", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
if err := posixStorage.AppendFile("success-vol", "path/to/success-file", []byte("Hello, world")); err != nil {
t.Fatalf("Unable to create file, %s", err)
}
testCases := []struct {
srcVol string
srcPath string
ioErrCnt int
expectedErr error
}{
// Test case - 1.
// Test case with valid inputs, expected to pass.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 2.
// Test case with valid inputs, expected to pass.
{
srcVol: "success-vol",
srcPath: "path/to/success-file",
ioErrCnt: 0,
expectedErr: nil,
},
// Test case - 3.
// Test case with non-existent file.
{
srcVol: "success-vol",
srcPath: "nonexistent-file",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 4.
// Test case with non-existent file path.
{
srcVol: "success-vol",
srcPath: "path/2/success-file",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 5.
// Test case with path being a directory.
{
srcVol: "success-vol",
srcPath: "path",
ioErrCnt: 0,
expectedErr: errFileNotFound,
},
// Test case - 6.
// Test case with io error count > max limit.
{
srcVol: "success-vol",
srcPath: "success-file",
ioErrCnt: 6,
expectedErr: errFaultyDisk,
},
// Test case - 7.
// Test case with non existent volume.
{
srcVol: "non-existent-vol",
srcPath: "success-file",
ioErrCnt: 0,
expectedErr: errVolumeNotFound,
},
}
for i, testCase := range testCases {
// setting ioErrCnt from the test case.
if posixType, ok := posixStorage.(*posix); ok {
// setting the io error count from as specified in the test case.
posixType.ioErrCount = int32(testCase.ioErrCnt)
} else {
t.Errorf("Expected the StorageAPI to be of type *posix")
}
if _, err := posixStorage.StatFile(testCase.srcVol, testCase.srcPath); err != testCase.expectedErr {
t.Fatalf("Test case %d: Expected: \"%s\", got: \"%s\"", i+1, testCase.expectedErr, err)
}
}
}