Skip to content

Commit

Permalink
Integration tests to verify symlink and stat operations on local file (
Browse files Browse the repository at this point in the history
…#1384)

* integration tests to verify symlink and stat operations on local file

rebase changes

* rebase changes

* review comments
  • Loading branch information
ashmeenkaur authored and gargnitingoogle committed Nov 1, 2023
1 parent 3bab599 commit a15fee5
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 12 deletions.
83 changes: 83 additions & 0 deletions tools/integration_tests/local_file/stat_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2023 Google Inc. All Rights Reserved.
//
// 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.

// Provides integration tests for stat operation on local files.
package local_file_test

import (
"os"
"testing"

"github.com/googlecloudplatform/gcsfuse/internal/fs/inode"
. "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/client"
"github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/operations"
"github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/setup"
)

func TestStatOnLocalFile(t *testing.T) {
testDirPath = setup.SetupTestDirectory(testDirName)
// Create a local file.
filePath, fh := CreateLocalFileInTestDir(ctx, storageClient, testDirPath, FileName1, t)

// Stat the local file.
operations.VerifyStatFile(filePath, 0, FilePerms, t)

// Writing contents to local file shouldn't create file on GCS.
WritingToLocalFileShouldNotWriteToGCS(ctx, storageClient, fh, testDirName, FileName1, t)

// Stat the local file again to check if new content is written.
operations.VerifyStatFile(filePath, SizeOfFileContents, FilePerms, t)

// Close the file and validate that the file is created on GCS.
CloseFileAndValidateContentFromGCS(ctx, storageClient, fh, testDirName,
FileName1, FileContents, t)
}

func TestStatOnLocalFileWithConflictingFileNameSuffix(t *testing.T) {
testDirPath = setup.SetupTestDirectory(testDirName)
// Create a local file.
filePath, fh := CreateLocalFileInTestDir(ctx, storageClient, testDirPath, FileName1, t)

// Stat the local file.
operations.VerifyStatFile(filePath+inode.ConflictingFileNameSuffix, 0, FilePerms, t)

// Close the file and validate that the file is created on GCS.
CloseFileAndValidateContentFromGCS(ctx, storageClient, fh, testDirName,
FileName1, "", t)
}

func TestTruncateLocalFile(t *testing.T) {
testDirPath = setup.SetupTestDirectory(testDirName)
// Create a local file.
filePath, fh := CreateLocalFileInTestDir(ctx, storageClient, testDirPath, FileName1, t)
// Writing contents to local file .
WritingToLocalFileShouldNotWriteToGCS(ctx, storageClient, fh, testDirName, FileName1, t)

// Stat the file to validate if new contents are written.
operations.VerifyStatFile(filePath, SizeOfFileContents, FilePerms, t)

// Truncate the file to update the file size.
err := os.Truncate(filePath, SizeTruncate)
if err != nil {
t.Fatalf("os.Truncate err: %v", err)
}
ValidateObjectNotFoundErrOnGCS(ctx, storageClient, testDirName, FileName1, t)

// Stat the file to validate if file is truncated correctly.
operations.VerifyStatFile(filePath, SizeTruncate, FilePerms, t)

// Close the file and validate that the file is created on GCS.
CloseFileAndValidateContentFromGCS(ctx, storageClient, fh, testDirName,
FileName1, "testS", t)
}
63 changes: 63 additions & 0 deletions tools/integration_tests/local_file/sym_link_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2023 Google Inc. All Rights Reserved.
//
// 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.
//
// Provides integration tests for symlink operation on local files.
package local_file_test

import (
"os"
"path"
"strings"
"testing"

. "github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/client"
"github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/operations"
"github.com/googlecloudplatform/gcsfuse/tools/integration_tests/util/setup"
)

func createAndVerifySymLink(t *testing.T) (filePath, symlink string, fh *os.File) {
testDirPath = setup.SetupTestDirectory(testDirName)
// Create a local file.
filePath, fh = CreateLocalFileInTestDir(ctx, storageClient, testDirPath, FileName1, t)
WritingToLocalFileShouldNotWriteToGCS(ctx, storageClient, fh, testDirName, FileName1, t)

// Create the symlink.
symlink = path.Join(testDirPath, "bar")
operations.CreateSymLink(filePath, symlink, t)

// Read the link.
operations.VerifyReadLink(filePath, symlink, t)
operations.VerifyReadFile(symlink, FileContents, t)
return
}

func TestCreateSymlinkForLocalFile(t *testing.T) {
_, _, fh := createAndVerifySymLink(t)
CloseFileAndValidateContentFromGCS(ctx, storageClient, fh, testDirName,
FileName1, FileContents, t)
}

func TestReadSymlinkForDeletedLocalFile(t *testing.T) {
filePath, symlink, fh := createAndVerifySymLink(t)
// Remove filePath and then close the fileHandle to avoid syncing to GCS.
operations.RemoveFile(filePath)
operations.CloseFileShouldNotThrowError(fh, t)
ValidateObjectNotFoundErrOnGCS(ctx, storageClient, testDirName, FileName1, t)

// Reading symlink should fail.
_, err := os.Stat(symlink)
if err == nil || !strings.Contains(err.Error(), "no such file or directory") {
t.Fatalf("Reading symlink for deleted local file did not fail.")
}
}
27 changes: 15 additions & 12 deletions tools/integration_tests/util/client/gcs_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@ import (
)

const (
FileName1 = "foo1"
FileName2 = "foo2"
ExplicitDirName = "explicit"
ExplicitFileName1 = "explicitFile1"
ImplicitDirName = "implicit"
ImplicitFileName1 = "implicitFile1"
FileContents = "testString"
GCSFileContent = "GCSteststring"
GCSFileSize = 13
FilePerms = 0644
ReadSize = 1024
FileName1 = "foo1"
FileName2 = "foo2"
ExplicitDirName = "explicit"
ExplicitFileName1 = "explicitFile1"
ImplicitDirName = "implicit"
ImplicitFileName1 = "implicitFile1"
FileContents = "testString"
SizeOfFileContents = 10
GCSFileContent = "GCSteststring"
GCSFileSize = 13
FilePerms = 0644
ReadSize = 1024
SizeTruncate = 5
)

func CreateImplicitDir(ctx context.Context, storageClient *storage.Client,
Expand Down Expand Up @@ -91,7 +93,8 @@ func getDirName(testDirPath string) string {
return dirName
}

func WritingToLocalFileShouldNotWriteToGCS(ctx context.Context, storageClient *storage.Client, fh *os.File, testDirName, fileName string, t *testing.T) {
func WritingToLocalFileShouldNotWriteToGCS(ctx context.Context, storageClient *storage.Client,
fh *os.File, testDirName, fileName string, t *testing.T) {
operations.WriteWithoutClose(fh, FileContents, t)
ValidateObjectNotFoundErrOnGCS(ctx, storageClient, testDirName, fileName, t)
}
Expand Down
54 changes: 54 additions & 0 deletions tools/integration_tests/util/operations/file_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"log"
"os"
"os/exec"
"path"
"strconv"
"strings"
"syscall"
Expand Down Expand Up @@ -459,6 +460,47 @@ func CreateFile(filePath string, filePerms os.FileMode, t *testing.T) (f *os.Fil
return
}

func CreateSymLink(filePath, symlink string, t *testing.T) {
err := os.Symlink(filePath, symlink)

// Verify os.Symlink operation succeeds.
if err != nil {
t.Fatalf("os.Symlink(%s, %s): %v", filePath, symlink, err)
}
}

func VerifyStatFile(filePath string, fileSize int64, filePerms os.FileMode, t *testing.T) {
fi, err := os.Stat(filePath)

if err != nil {
t.Fatalf("os.Stat err: %v", err)
}

if fi.Name() != path.Base(filePath) {
t.Fatalf("File name mismatch in stat call. Expected: %s, Got: %s", path.Base(filePath), fi.Name())
}

if fi.Size() != fileSize {
t.Fatalf("File size mismatch in stat call. Expected: %d, Got: %d", fileSize, fi.Size())
}

if fi.Mode() != filePerms {
t.Fatalf("File permissions mismatch in stat call. Expected: %v, Got: %v", filePerms, fi.Mode())
}
}

func VerifyReadFile(filePath, expectedContent string, t *testing.T) {
gotContent, err := os.ReadFile(filePath)

// Verify os.ReadFile operation succeeds.
if err != nil {
t.Fatalf("os.ReadFile(%s): %v", filePath, err)
}
if expectedContent != string(gotContent) {
t.Fatalf("Content mismatch. Expected: %s, Got: %s", expectedContent, gotContent)
}
}

func VerifyFileEntry(entry os.DirEntry, fileName string, size int64, t *testing.T) {
if entry.IsDir() {
t.Fatalf("Expected: file entry, Got: directory entry.")
Expand All @@ -475,6 +517,18 @@ func VerifyFileEntry(entry os.DirEntry, fileName string, size int64, t *testing.
}
}

func VerifyReadLink(expectedTarget, symlinkName string, t *testing.T) {
gotTarget, err := os.Readlink(symlinkName)

// Verify os.Readlink operation succeeds.
if err != nil {
t.Fatalf("os.Readlink(%s): %v", symlinkName, err)
}
if expectedTarget != gotTarget {
t.Fatalf("Symlink target mismatch. Expected: %s, Got: %s", expectedTarget, gotTarget)
}
}

func WriteWithoutClose(fh *os.File, content string, t *testing.T) {
_, err := fh.Write([]byte(content))
if err != nil {
Expand Down

0 comments on commit a15fee5

Please sign in to comment.