Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions e2e/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package e2e

import (
"fmt"
"os"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -320,6 +321,46 @@ func TestRunDryRun(t *testing.T) {
assert.Assert(t, ensureS3Object(s3client, bucket, files[2], "content"))
}

// --dry-run run (cp s3://bucket/file dir/ from stdin)
func TestRunDryRunDownloadFromStdin(t *testing.T) {
t.Parallel()

s3client, s5cmd := setup(t)

bucket := s3BucketFromTestName(t)
createBucket(t, s3client, bucket)

// Feed more than one read buffer's worth of commands so stdin is still
// being read while the first downloads complete. A dry-run download must
// hand out a file that is safe to close, i.e. one that does not alias
// stdin.
const numFiles = 200

var lines []string
expected := make(map[int]compareFunc)
for i := 0; i < numFiles; i++ {
filename := fmt.Sprintf("file%03d.txt", i)
putFile(t, s3client, bucket, filename, "content")

lines = append(lines, fmt.Sprintf("cp s3://%v/%s dir/", bucket, filename))
expected[i] = equals("cp s3://%v/%s dir/%s", bucket, filename, filename)
}

input := strings.NewReader(strings.Join(lines, "\n"))
cmd := s5cmd("--dry-run", "run")
result := icmd.RunCmd(cmd, icmd.WithStdin(input))

result.Assert(t, icmd.Success)

assertLines(t, result.Stdout(), expected, sortInput(true))

assertLines(t, result.Stderr(), map[int]compareFunc{})

// not even outermost directory should be created
_, err := os.Stat(cmd.Dir + "/dir")
assert.Assert(t, os.IsNotExist(err))
}

func TestRunFixDataRace_Issue301(t *testing.T) {
t.Parallel()

Expand Down
4 changes: 2 additions & 2 deletions storage/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (f *Filesystem) MkdirAll(path string) error {
// Create creates a new os.File.
func (f *Filesystem) Create(path string) (*os.File, error) {
if f.dryRun {
return &os.File{}, nil
return os.OpenFile(os.DevNull, os.O_WRONLY, 0)
}

return os.Create(path)
Expand All @@ -237,7 +237,7 @@ func (f *Filesystem) Open(path string) (*os.File, error) {
// CreateTemp creates a new temporary file
func (f *Filesystem) CreateTemp(dir, pattern string) (*os.File, error) {
if f.dryRun {
return &os.File{}, nil
return os.OpenFile(os.DevNull, os.O_WRONLY, 0)
}

file, err := os.CreateTemp(dir, pattern)
Expand Down
74 changes: 73 additions & 1 deletion storage/fs_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,82 @@
package storage

import "testing"
import (
"os"
"path/filepath"
"testing"
)

func TestFilesystemImplementsStorageInterface(t *testing.T) {
var i interface{} = new(Filesystem)
if _, ok := i.(Storage); !ok {
t.Errorf("expected %t to implement Storage interface", i)
}
}

func TestFilesystemCreateDryRun(t *testing.T) {
t.Parallel()

fs := &Filesystem{dryRun: true}
tmpDir := t.TempDir()

testcases := []struct {
name string
create func() (*os.File, error)
}{
{
name: "Create",
create: func() (*os.File, error) {
return fs.Create(filepath.Join(tmpDir, "should-not-exist.txt"))
},
},
{
name: "CreateTemp",
create: func() (*os.File, error) {
return fs.CreateTemp(tmpDir, "dryrun-*.txt")
},
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
f, err := tc.create()
if err != nil {
t.Fatalf("%s() returned error: %v", tc.name, err)
}
if f == nil {
t.Fatalf("%s() returned nil file", tc.name)
}

if f.Name() != os.DevNull {
t.Fatalf("expected file name %q, got %q", os.DevNull, f.Name())
}

// the handle must not alias stdin, otherwise closing it would
// close stdin as well.
if f.Fd() == 0 {
t.Fatal("expected file descriptor other than stdin")
}

if _, err := f.Write([]byte("content")); err != nil {
t.Fatalf("Write() returned error: %v", err)
}

if err := f.Close(); err != nil {
t.Fatalf("Close() returned error: %v", err)
}

if _, err := os.Stdin.Stat(); err != nil {
t.Fatalf("stdin is not usable after Close(): %v", err)
}
})
}

// no file should be created on disk during dry run
entries, err := os.ReadDir(tmpDir)
if err != nil {
t.Fatalf("ReadDir failed: %v", err)
}
if len(entries) != 0 {
t.Fatalf("expected no files in temp dir during dry run, got %d", len(entries))
}
}
Loading