diff --git a/Tests/CLITests/Subcommands/Containers/TestCLICopy.swift b/Tests/CLITests/Subcommands/Containers/TestCLICopy.swift index 0deab7b9f..3ad485add 100644 --- a/Tests/CLITests/Subcommands/Containers/TestCLICopy.swift +++ b/Tests/CLITests/Subcommands/Containers/TestCLICopy.swift @@ -336,4 +336,573 @@ class TestCLICopyCommand: CLITest { return } } + + // MARK: - CopyOut S1: no trailing slash + + @Test func testCopyOutFileToExistingFile() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let content = "container content" + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n '\(content)' > /tmp/source.txt"]) + + let destPath = testDir.appendingPathComponent("existing.txt") + try "old content".write(to: destPath, atomically: true, encoding: .utf8) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/source.txt", destPath.path]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destPath.path, encoding: .utf8) + #expect(result == content) + try doStop(name: name) + } catch { + Issue.record("testCopyOutFileToExistingFile failed: \(error)") + } + } + + @Test func testCopyOutDirectoryToExistingFileFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'x' > /tmp/srcdir/file.txt"]) + + let destPath = testDir.appendingPathComponent("existing.txt") + try "x".write(to: destPath, atomically: true, encoding: .utf8) + + let (_, _, _, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir", destPath.path]) + #expect(status != 0, "expected directory-to-existing-file to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryToExistingFileFails failed: \(error)") + } + } + + @Test func testCopyOutFileToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let content = "container content" + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n '\(content)' > /tmp/source.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/source.txt", destDir.path]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("source.txt").path, encoding: .utf8) + #expect(result == content) + try doStop(name: name) + } catch { + Issue.record("testCopyOutFileToExistingDirectory failed: \(error)") + } + } + + @Test func testCopyOutDirectoryToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir", destDir.path]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("srcdir").appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryToExistingDirectory failed: \(error)") + } + } + + // MARK: - CopyOut S2: trailing slash on dst + + @Test func testCopyOutFileToNonExistingTrailingSlashFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n 'x' > /tmp/source.txt"]) + + let destPath = testDir.appendingPathComponent("nonexistent").path + "/" + let (_, _, _, status) = try run(arguments: ["copy", "\(name):/tmp/source.txt", destPath]) + #expect(status != 0, "expected file-to-nonexisting/ to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyOutFileToNonExistingTrailingSlashFails failed: \(error)") + } + } + + @Test func testCopyOutDirectoryToNonExistingTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("newdir") + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir", destDir.path + "/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + var isDir: ObjCBool = false + #expect(FileManager.default.fileExists(atPath: destDir.path, isDirectory: &isDir) && isDir.boolValue) + let result = try String(contentsOfFile: destDir.appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryToNonExistingTrailingSlash failed: \(error)") + } + } + + @Test func testCopyOutFileToExistingDirectoryTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let content = "container content" + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n '\(content)' > /tmp/source.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/source.txt", destDir.path + "/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("source.txt").path, encoding: .utf8) + #expect(result == content) + try doStop(name: name) + } catch { + Issue.record("testCopyOutFileToExistingDirectoryTrailingSlash failed: \(error)") + } + } + + @Test func testCopyOutDirectoryToExistingDirectoryTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir", destDir.path + "/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("srcdir").appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryToExistingDirectoryTrailingSlash failed: \(error)") + } + } + + // MARK: - CopyOut S3: trailing slash on src + + @Test func testCopyOutDirectoryContentsToNonExisting() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir/sub && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("newdir") + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir/", destDir.path]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + var isDir: ObjCBool = false + #expect(FileManager.default.fileExists(atPath: destDir.appendingPathComponent("sub").path, isDirectory: &isDir) && isDir.boolValue) + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryContentsToNonExisting failed: \(error)") + } + } + + @Test func testCopyOutDirectoryContentsToExistingFileFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'x' > /tmp/srcdir/file.txt"]) + + let destPath = testDir.appendingPathComponent("existing.txt") + try "x".write(to: destPath, atomically: true, encoding: .utf8) + + let (_, _, _, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir/", destPath.path]) + #expect(status != 0, "expected directory/-to-existing-file to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryContentsToExistingFileFails failed: \(error)") + } + } + + @Test func testCopyOutDirectoryContentsToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir/", destDir.path]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("srcdir").appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryContentsToExistingDirectory failed: \(error)") + } + } + + // MARK: - CopyOut S4: trailing slash on both src and dst + + @Test func testCopyOutDirectoryContentsToNonExistingTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("newdir") + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir/", destDir.path + "/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryContentsToNonExistingTrailingSlash failed: \(error)") + } + } + + @Test func testCopyOutDirectoryContentsToExistingDirectoryTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + _ = try doExec(name: name, cmd: ["sh", "-c", "mkdir -p /tmp/srcdir && echo -n 'hello' > /tmp/srcdir/file.txt"]) + + let destDir = testDir.appendingPathComponent("dstdir") + try FileManager.default.createDirectory(at: destDir, withIntermediateDirectories: true) + + let (_, _, error, status) = try run(arguments: ["copy", "\(name):/tmp/srcdir/", destDir.path + "/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try String(contentsOfFile: destDir.appendingPathComponent("srcdir").appendingPathComponent("file.txt").path, encoding: .utf8) + #expect(result == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyOutDirectoryContentsToExistingDirectoryTrailingSlash failed: \(error)") + } + } + + // MARK: - CopyIn S1: no trailing slash + + @Test func testCopyInFileToExistingFile() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let content = "new content" + let srcFile = testDir.appendingPathComponent("source.txt") + try content.write(to: srcFile, atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n 'old content' > /tmp/existing.txt"]) + + let (_, _, error, status) = try run(arguments: ["copy", srcFile.path, "\(name):/tmp/existing.txt"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/existing.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == content) + try doStop(name: name) + } catch { + Issue.record("testCopyInFileToExistingFile failed: \(error)") + } + } + + @Test func testCopyInDirectoryToExistingFileFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "x".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n 'x' > /tmp/existing.txt"]) + + let (_, _, _, status) = try run(arguments: ["copy", srcDir.path, "\(name):/tmp/existing.txt"]) + #expect(status != 0, "expected directory-to-existing-file to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryToExistingFileFails failed: \(error)") + } + } + + @Test func testCopyInFileToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let content = "host content" + let srcFile = testDir.appendingPathComponent("source.txt") + try content.write(to: srcFile, atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["mkdir", "-p", "/tmp/dstdir"]) + + let (_, _, error, status) = try run(arguments: ["copy", srcFile.path, "\(name):/tmp/dstdir"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/dstdir/source.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == content) + try doStop(name: name) + } catch { + Issue.record("testCopyInFileToExistingDirectory failed: \(error)") + } + } + + @Test func testCopyInDirectoryToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["mkdir", "-p", "/tmp/dstdir"]) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path, "\(name):/tmp/dstdir"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/dstdir/srcdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryToExistingDirectory failed: \(error)") + } + } + + // MARK: - CopyIn S2: trailing slash on dst + + @Test func testCopyInFileToNonExistingTrailingSlashFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcFile = testDir.appendingPathComponent("source.txt") + try "x".write(to: srcFile, atomically: true, encoding: .utf8) + + let (_, _, _, status) = try run(arguments: ["copy", srcFile.path, "\(name):/tmp/nonexistent/"]) + #expect(status != 0, "expected file-to-nonexisting/ to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyInFileToNonExistingTrailingSlashFails failed: \(error)") + } + } + + @Test func testCopyInDirectoryToNonExistingTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path, "\(name):/tmp/newdir/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/newdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryToNonExistingTrailingSlash failed: \(error)") + } + } + + // MARK: - CopyIn S3: trailing slash on src + + @Test func testCopyInDirectoryContentsToNonExisting() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + let subDir = srcDir.appendingPathComponent("sub") + try FileManager.default.createDirectory(at: subDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path + "/", "\(name):/tmp/newdir"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/newdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryContentsToNonExisting failed: \(error)") + } + } + + @Test func testCopyInDirectoryContentsToExistingFileFails() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "x".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["sh", "-c", "echo -n 'x' > /tmp/existing.txt"]) + + let (_, _, _, status) = try run(arguments: ["copy", srcDir.path + "/", "\(name):/tmp/existing.txt"]) + #expect(status != 0, "expected directory/-to-existing-file to fail") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryContentsToExistingFileFails failed: \(error)") + } + } + + @Test func testCopyInDirectoryContentsToExistingDirectory() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["mkdir", "-p", "/tmp/dstdir"]) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path + "/", "\(name):/tmp/dstdir"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/dstdir/srcdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryContentsToExistingDirectory failed: \(error)") + } + } + + // MARK: - CopyIn S4: trailing slash on both src and dst + + @Test func testCopyInDirectoryContentsToNonExistingTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path + "/", "\(name):/tmp/newdir/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/newdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryContentsToNonExistingTrailingSlash failed: \(error)") + } + } + + @Test func testCopyInDirectoryContentsToExistingDirectoryTrailingSlash() throws { + do { + let name = getTestName() + try doCreate(name: name) + defer { try? doStop(name: name) } + try doStart(name: name) + try waitForContainerRunning(name) + + let srcDir = testDir.appendingPathComponent("srcdir") + try FileManager.default.createDirectory(at: srcDir, withIntermediateDirectories: true) + try "hello".write(to: srcDir.appendingPathComponent("file.txt"), atomically: true, encoding: .utf8) + _ = try doExec(name: name, cmd: ["mkdir", "-p", "/tmp/dstdir"]) + + let (_, _, error, status) = try run(arguments: ["copy", srcDir.path + "/", "\(name):/tmp/dstdir/"]) + if status != 0 { throw CLIError.executionFailed("copy failed: \(error)") } + + let result = try doExec(name: name, cmd: ["cat", "/tmp/dstdir/srcdir/file.txt"]) + #expect(result.trimmingCharacters(in: .whitespacesAndNewlines) == "hello") + try doStop(name: name) + } catch { + Issue.record("testCopyInDirectoryContentsToExistingDirectoryTrailingSlash failed: \(error)") + } + } }