-
-
Notifications
You must be signed in to change notification settings - Fork 116
test: Add testing for FileCopyMethod #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
aea1160
4e36e6f
b1f4e85
ae5d1d1
3314eec
1c0a1c6
207b8f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| # Some platform-specific file copy syscalls (e.g. creating reflinks) are only | ||
| # supported on some platforms, and only with specific filesystems. These | ||
| # syscalls are used by different FileCopyMethod implementations. | ||
| # | ||
| # This workflow sets up the conditions needed for those syscalls to work, | ||
| # and then runs the tests with the different FileCopyMethods. | ||
|
|
||
| name: FileCopyMethod | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main, develop ] | ||
| pull_request: | ||
| branches: [ main, develop ] | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| test-linux: | ||
| name: Test Linux | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| matrix: | ||
| environment: | ||
| - filesystem: btrfs | ||
| filecopymethod: CopyBytes | ||
| steps: | ||
| - name: Set up Go | ||
| uses: actions/setup-go@v4 | ||
| with: | ||
| go-version: 'stable' | ||
|
|
||
| - name: Check out code into the Go module directory | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Get dependencies | ||
| run: go get -v -t -d ./... | ||
|
|
||
| - name: Build | ||
| run: go build -v . | ||
|
|
||
| - name: Set up filesystem | ||
| run: |- | ||
| mkdir -p ./test/filesystem | ||
| IMAGE_PATH="./test/filesystem/contents.img" | ||
| MOUNT_PATH="./test/filesystem/mounted" | ||
|
|
||
| truncate -s 500m "$IMAGE_PATH" | ||
| mkfs -t "${{matrix.environment.filesystem}}" "$IMAGE_PATH" | ||
| mkdir "$MOUNT_PATH" | ||
| sudo mount -o loop "$IMAGE_PATH" "$MOUNT_PATH" | ||
| sudo chown -R "$(id -u):$(id -g)" "$MOUNT_PATH" | ||
|
|
||
| - name: Copy files to mounted filesystem | ||
| run: |- | ||
| rsync -av --exclude=".*" --exclude "test/filesystem" . "test/filesystem/mounted" | ||
|
|
||
| - name: Test | ||
| working-directory: test/filesystem/mounted | ||
| run: go test -v | ||
| env: | ||
| TEST_FILECOPYMETHOD: ${{matrix.environment.filecopymethod}} | ||
|
|
||
|
|
||
| test-macos: | ||
| name: Test MacOS | ||
| runs-on: macos-latest | ||
| strategy: | ||
| matrix: | ||
| environment: | ||
| - filesystem: APFS | ||
| filecopymethod: CopyBytes | ||
| steps: | ||
| - name: Set up Go | ||
| uses: actions/setup-go@v4 | ||
| with: | ||
| go-version: 'stable' | ||
|
|
||
| - name: Check out code into the Go module directory | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Get dependencies | ||
| run: go get -v -t -d ./... | ||
|
|
||
| - name: Build | ||
| run: go build -v . | ||
|
|
||
| - name: Set up filesystem (MacOS) | ||
| run: |- | ||
| mkdir -p ./test/filesystem | ||
| IMAGE_PATH="./test/filesystem/contents.dmg" | ||
| MOUNT_PATH="./test/filesystem/mounted" | ||
|
|
||
| hdiutil create -size 500m -fs "${{matrix.environment.filesystem}}" "$IMAGE_PATH" | ||
| hdiutil attach -mountpoint "$MOUNT_PATH" "$IMAGE_PATH" | ||
|
|
||
| - name: Copy files to mounted filesystem | ||
| run: |- | ||
| rsync -av --exclude=".*" --exclude "test/filesystem" . "test/filesystem/mounted" | ||
|
|
||
| - name: Test | ||
| working-directory: test/filesystem/mounted | ||
| run: go test -v | ||
| env: | ||
| TEST_FILECOPYMETHOD: ${{matrix.environment.filecopymethod}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| test/data.copy | ||
| test/filesystem | ||
| test/owned-by-root | ||
| coverage.txt | ||
| vendor | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,8 +17,27 @@ import ( | |
| //go:embed test/data/case18/assets | ||
| var assets embed.FS | ||
|
|
||
| var currentFileCopyMethod FileCopyMethod | ||
|
|
||
| func setupFileCopyMethod(m *testing.M) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need this func. |
||
| switch os.Getenv("TEST_FILECOPYMETHOD") { | ||
| case "": | ||
| currentFileCopyMethod = getDefaultOptions("", "").FileCopyMethod // Should be CopyBytes | ||
| case "CopyBytes": | ||
| currentFileCopyMethod = CopyBytes | ||
| } | ||
|
|
||
| // Allow running all the tests with a different FileCopyMethod. | ||
| // We want to re-use tests designed for CopyBytes with other copy methods | ||
| // to make sure that they behave the same way (where possible). | ||
| overrideDefaultOptions_FOR_TESTS = func(defopt *Options) { | ||
| defopt.FileCopyMethod = currentFileCopyMethod | ||
| } | ||
| } | ||
|
|
||
| func TestMain(m *testing.M) { | ||
| setup(m) | ||
| setupFileCopyMethod(m) | ||
| code := m.Run() | ||
| teardown(m) | ||
| os.Exit(code) | ||
|
|
@@ -351,7 +370,6 @@ func TestOptions_PreserveOwner(t *testing.T) { | |
| } | ||
|
|
||
| func TestOptions_CopyRateLimit(t *testing.T) { | ||
|
|
||
| file, err := os.Create("test/data/case16/large.file") | ||
| if err != nil { | ||
| t.Errorf("failed to create test file: %v", err) | ||
|
|
@@ -372,8 +390,13 @@ func TestOptions_CopyRateLimit(t *testing.T) { | |
| start := time.Now() | ||
| err = Copy("test/data/case16", "test/data.copy/case16", opt) | ||
| elapsed := time.Since(start) | ||
| Expect(t, err).ToBe(nil) | ||
| Expect(t, elapsed > 5*time.Second).ToBe(true) | ||
| if currentFileCopyMethod.supportsOptWrapReader { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding Options{
WrapReader: func(src io.Reader) io.Reader { ... },
FileCopyMethod: CopyBytes,
}Thus, we don't need |
||
| Expect(t, err).ToBe(nil) | ||
| Expect(t, elapsed > 5*time.Second).ToBe(true) | ||
| } else { | ||
| Expect(t, err).Not().ToBe(nil) | ||
| Expect(t, errors.Is(err, ErrUnsupportedCopyMethod)).ToBe(true) | ||
| } | ||
| } | ||
|
|
||
| func TestOptions_OnFileError(t *testing.T) { | ||
|
|
@@ -422,7 +445,12 @@ func TestOptions_FS(t *testing.T) { | |
| FS: assets, | ||
| PermissionControl: AddPermission(200), // FIXME | ||
| }) | ||
| Expect(t, err).ToBe(nil) | ||
| if currentFileCopyMethod.supportsOptFS { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||
| Expect(t, err).ToBe(nil) | ||
| } else { | ||
| Expect(t, err).Not().ToBe(nil) | ||
| Expect(t, errors.Is(err, ErrUnsupportedCopyMethod)).ToBe(true) | ||
| } | ||
| } | ||
|
|
||
| type SleepyReader struct { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,8 @@ var ErrUnsupportedCopyMethod = errors.New( | |
| // CopyBytes copies the file contents by reading the source file into a buffer, | ||
| // then writing the buffer back to the destination file. | ||
| var CopyBytes = FileCopyMethod{ | ||
| supportsOptFS: true, | ||
| supportsOptWrapReader: true, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as mentioned above, we don't need these |
||
| fcopy: func(src, dest string, info os.FileInfo, opt Options) (err error, skipFile bool) { | ||
| var readcloser io.ReadCloser | ||
| if opt.FS != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -132,12 +132,15 @@ const ( | |
| // FileCopyMethod represents one of the ways that a regular file can be copied. | ||
| type FileCopyMethod struct { | ||
| fcopy func(src, dest string, info os.FileInfo, opt Options) (err error, skipFile bool) | ||
|
|
||
| supportsOptFS bool | ||
| supportsOptWrapReader bool | ||
| } | ||
|
|
||
| // getDefaultOptions provides default options, | ||
| // which would be modified by usage-side. | ||
| func getDefaultOptions(src, dest string) Options { | ||
| return Options{ | ||
| defopt := Options{ | ||
| OnSymlink: func(string) SymlinkAction { | ||
| return Shallow // Do shallow copy | ||
| }, | ||
|
|
@@ -154,8 +157,19 @@ func getDefaultOptions(src, dest string) Options { | |
| WrapReader: nil, // Do not wrap src files, use them as they are. | ||
| intent: intent{src, dest, nil, nil}, | ||
| } | ||
|
|
||
| if overrideDefaultOptions_FOR_TESTS != nil { | ||
| overrideDefaultOptions_FOR_TESTS(&defopt) | ||
| } | ||
|
|
||
| return defopt | ||
| } | ||
|
|
||
| // overrideDefaultOptions_FOR_TESTS allows the copy package tests to replace | ||
| // the default options. This allows existing test code to be reused with | ||
| // different settings as a way to check that behavior is consistent. | ||
| var overrideDefaultOptions_FOR_TESTS func(*Options) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not include any test-specific logic inside main package.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in my other comments: there is no perfect way to use the existing tests with any other What we tried beforeWrapping the Copy function inside testsWith #160, I made a
Creating a
|
||
|
|
||
| // assureOptions struct, should be called only once. | ||
| // All optional values MUST NOT BE nil/zero after assured. | ||
| func assureOptions(src, dest string, opts ...Options) Options { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need this.