From 064f3db23cfce4b02cc9f21e9608b41f15857d38 Mon Sep 17 00:00:00 2001 From: saehejkang <20051028+saehejkang@users.noreply.github.com> Date: Fri, 22 May 2026 15:46:44 -0700 Subject: [PATCH] add filepathops, absolute path and tests --- Sources/ContainerizationOS/FilePathOps.swift | 40 +++++++++++++++++ .../FilePathOpsTests.swift | 43 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 Sources/ContainerizationOS/FilePathOps.swift create mode 100644 Tests/ContainerizationOSTests/FilePathOpsTests.swift diff --git a/Sources/ContainerizationOS/FilePathOps.swift b/Sources/ContainerizationOS/FilePathOps.swift new file mode 100644 index 00000000..1e1d33e4 --- /dev/null +++ b/Sources/ContainerizationOS/FilePathOps.swift @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2026 Apple Inc. and the Containerization project authors. +// +// 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 +// +// https://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. +//===----------------------------------------------------------------------===// + +import Foundation +import SystemPackage + +/// Static utility functions for path operations. +/// +/// The type is never instantiated; it exists solely as a namespace. +public struct FilePathOps { + private init() {} + + /// Returns an absolute version of `path`. + /// + /// This is a purely lexical operation: it does not resolve symlinks + /// and does not access the file system. If `path` is already absolute, + /// this returns `path` unchanged. + public static func absolutePath(_ path: FilePath) -> FilePath { + guard !path.isAbsolute else { + return path + } + + return FilePath(FileManager.default.currentDirectoryPath) + .appending(path.components) + .lexicallyNormalized() + } +} diff --git a/Tests/ContainerizationOSTests/FilePathOpsTests.swift b/Tests/ContainerizationOSTests/FilePathOpsTests.swift new file mode 100644 index 00000000..f5d5c05d --- /dev/null +++ b/Tests/ContainerizationOSTests/FilePathOpsTests.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2026 Apple Inc. and the Containerization project authors. +// +// 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 +// +// https://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. +//===----------------------------------------------------------------------===// + +import Foundation +import SystemPackage +import Testing + +@testable import ContainerizationOS + +struct FilePathOpsTests { + @Test("absolutePath returns absolute inputs unchanged") + func absolutePathPreservesAbsolutePath() { + let absolute = FilePath("/tmp/containerization/file.tar") + let resolved = FilePathOps.absolutePath(absolute) + + #expect(resolved == absolute) + } + + @Test("absolutePath resolves relative paths against cwd and normalizes lexically") + func absolutePathResolvesRelativePath() { + let relative = FilePath("./images/../image.tar") + let expected = FilePath(FileManager.default.currentDirectoryPath) + .appending(relative.components) + .lexicallyNormalized() + let resolved = FilePathOps.absolutePath(relative) + + #expect(resolved == expected) + #expect(resolved.isAbsolute) + } +}