From 7f9383034137ee4c7a9e95f739a03129909910aa Mon Sep 17 00:00:00 2001 From: Sean McLoughlin Date: Tue, 7 Apr 2026 11:33:12 -0400 Subject: [PATCH] fix(control-socket): use .path instead of .path() for stale socket cleanup Use URL's .path property instead of .path() method when removing stale control sockets in ControlSocket.run(). The .path() method returns only the relative component ('control.sock') while .path resolves to the full absolute path. Since the cwd change happens after the cleanup, removeItem silently failed to find the socket. Add unit tests verifying controlSocketURL resolves correctly for both absolute cleanup and relative binding use cases. Fixes: https://github.com/cirruslabs/tart/issues/1220 --- Sources/tart/ControlSocket.swift | 2 +- Tests/TartTests/ControlSocketURLTests.swift | 28 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Tests/TartTests/ControlSocketURLTests.swift diff --git a/Sources/tart/ControlSocket.swift b/Sources/tart/ControlSocket.swift index fbf1ce4..5c185d4 100644 --- a/Sources/tart/ControlSocket.swift +++ b/Sources/tart/ControlSocket.swift @@ -19,7 +19,7 @@ class ControlSocket { func run() async throws { // Remove control socket file from previous "tart run" invocations, // if any, otherwise we may get the "address already in use" error - try? FileManager.default.removeItem(atPath: controlSocketURL.path()) + try? FileManager.default.removeItem(atPath: controlSocketURL.path) // Change the current working directory to a VM's base directory // to work around Unix domain socket 104 byte limitation [1] diff --git a/Tests/TartTests/ControlSocketURLTests.swift b/Tests/TartTests/ControlSocketURLTests.swift new file mode 100644 index 0000000..34d27c2 --- /dev/null +++ b/Tests/TartTests/ControlSocketURLTests.swift @@ -0,0 +1,28 @@ +import XCTest +@testable import tart + +final class ControlSocketURLTests: XCTestCase { + func testControlSocketURLResolvesToAbsolutePath() throws { + let baseURL = URL(fileURLWithPath: "/Users/test/.tart/vms/myvm/") + let vmDir = VMDirectory(baseURL: baseURL) + + // The .path property resolves relative URLs to absolute paths, + // which is required for stale socket cleanup in ControlSocket.run() + // since it happens before the working directory is changed. + XCTAssertEqual( + vmDir.controlSocketURL.path, + "/Users/test/.tart/vms/myvm/control.sock" + ) + } + + func testControlSocketURLRelativePathIsJustFilename() throws { + let baseURL = URL(fileURLWithPath: "/Users/test/.tart/vms/myvm/") + let vmDir = VMDirectory(baseURL: baseURL) + + // The .relativePath is used for socket binding after cwd is changed + XCTAssertEqual( + vmDir.controlSocketURL.relativePath, + "control.sock" + ) + } +}