Files
container/Tests/ContainerBuildTests/BuilderExtensionsTests.swift
Kathryn Baldauf d6f052d206 Update license header on all files to include the current year (#1024)
## Motivation and Context
Now that we're in 2026, we need to update the license headers on all the
files. Unfortunately, Hawkeye doesn't have an attribute for the current
year to help us avoid this in the future. Instead, I had to work around
this by doing the following:

1. Update licenserc.toml with:
     ```
      [properties]
       ... (other properties)
       currentYear = "2026"
     ```
 
2. Update scripts/license-header.txt with
    ```
Copyright ©{{ " " }}{%- set created = attrs.git_file_created_year or
attrs.disk_file_created_year -%}{%- set modified = props["currentYear"]
-%}{%- if created != modified -%} {{created}}-{{modified}}{%- else
-%}{{created}}{%- endif -%}{{ " " }}{{ props["copyrightOwner"] }}.
    ```

Then I removed these two changes before committing. After this PR is
merged, all files will have recently had git updates, so the existing
code for setting the modified year should work as intended.

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
2026-01-05 13:09:34 -08:00

338 lines
13 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the container 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 Testing
@testable import ContainerBuild
@Suite class URLExtensionFileSystemTests {
private var baseTempURL: URL!
private let fileManager = FileManager.default
init() throws {
baseTempURL = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent("URLExtensionTests-\(UUID().uuidString)")
try fileManager.createDirectory(at: baseTempURL, withIntermediateDirectories: true, attributes: nil)
}
deinit {
if let baseTempURL = baseTempURL {
try? fileManager.removeItem(at: baseTempURL)
}
}
// MARK: - Helpers
private func createDirectory(at url: URL) throws {
try fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
}
private func createFile(at url: URL, content: String = "") throws {
try fileManager.createDirectory(
at: url.deletingLastPathComponent(),
withIntermediateDirectories: true,
attributes: nil)
#expect(
fileManager.createFile(
atPath: url.path,
contents: content.data(using: .utf8),
attributes: nil))
}
// MARK: - parentOf Tests
@Test func testParentOfDirectParent() throws {
let parentDir = baseTempURL.appendingPathComponent("dir1")
let childDir = parentDir.appendingPathComponent("dir2")
try createDirectory(at: childDir)
#expect(parentDir.parentOf(childDir))
}
@Test func testParentOfGrandparent() throws {
let grandParent = baseTempURL.appendingPathComponent("dir3").appendingPathComponent("test")
let childDir = grandParent.appendingPathComponent("dir4").appendingPathComponent("dir2")
try createDirectory(at: childDir)
#expect(grandParent.parentOf(childDir))
}
@Test func testParentOfBaseTemp() throws {
let childDir = baseTempURL.appendingPathComponent("dir4").appendingPathComponent("dir2")
try createDirectory(at: childDir)
#expect(baseTempURL.parentOf(childDir))
}
@Test func testParentOfRoot() throws {
let rootURL = URL(fileURLWithPath: "/")
let childDir = baseTempURL.appendingPathComponent("dir4")
try createDirectory(at: childDir)
#expect(rootURL.parentOf(childDir))
#expect(rootURL.parentOf(baseTempURL))
}
@Test func testParentOfSamePath() throws {
let dir = baseTempURL.appendingPathComponent("dir4")
try createDirectory(at: dir)
let sameDir = URL(fileURLWithPath: dir.path)
#expect(dir.parentOf(sameDir))
#expect(sameDir.parentOf(dir))
}
@Test func testParentOfRootToRoot() {
let root1 = URL(fileURLWithPath: "/")
let root2 = URL(fileURLWithPath: "/")
#expect(root1.parentOf(root2))
}
@Test func testParentOfDifferentPaths() throws {
let dir1 =
baseTempURL
.appendingPathComponent("dir3")
.appendingPathComponent("test")
.appendingPathComponent("dir4")
let dir2 =
baseTempURL
.appendingPathComponent("dir3")
.appendingPathComponent("another")
.appendingPathComponent("file")
try createDirectory(at: dir1)
try createDirectory(at: dir2)
#expect(false == dir1.parentOf(dir2))
#expect(false == dir2.parentOf(dir1))
}
@Test func testParentOfSiblingPaths() throws {
let parentDir = baseTempURL.appendingPathComponent("dir3").appendingPathComponent("test")
let sibling1 = parentDir.appendingPathComponent("dir4")
let sibling2 = parentDir.appendingPathComponent("dir5")
try createDirectory(at: sibling1)
try createDirectory(at: sibling2)
#expect(false == sibling1.parentOf(sibling2))
#expect(false == sibling2.parentOf(sibling1))
}
@Test func testParentOfChildIsParentFalse() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let childDir = parentDir.appendingPathComponent("dir2")
try createDirectory(at: childDir)
#expect(false == childDir.parentOf(parentDir))
}
@Test func testParentOfPartialNameMatch() throws {
let partial = baseTempURL.appendingPathComponent("Doc")
let actualDir = baseTempURL.appendingPathComponent("dir4")
try createDirectory(at: actualDir)
#expect(false == partial.parentOf(actualDir))
}
@Test func testParentOfPathNormalization() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let childDir = parentDir.appendingPathComponent("dir2")
try createDirectory(at: childDir)
let normalized =
baseTempURL
.appendingPathComponent("dir8")
.appendingPathComponent("..")
.appendingPathComponent("dir4")
#expect(normalized.parentOf(childDir))
}
@Test func testParentOfChildWithNormalization() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let targetChildDir = parentDir.appendingPathComponent("dir2")
try createDirectory(at: targetChildDir)
let normalizedChild =
parentDir
.appendingPathComponent("dir9")
.appendingPathComponent("..")
.appendingPathComponent("dir2")
#expect(parentDir.parentOf(normalizedChild))
}
@Test func testParentOfPercentEncoding() throws {
let parentDir = baseTempURL.appendingPathComponent("My dir4")
let childDir = parentDir.appendingPathComponent("dir2 X")
try createDirectory(at: childDir)
let parentEncoded = URL(fileURLWithPath: baseTempURL.path + "/My%20dir4")
let childEncoded = URL(fileURLWithPath: baseTempURL.path + "/My%20dir4/dir2%20X")
#expect(parentDir.parentOf(childDir))
#expect(parentEncoded.parentOf(childEncoded))
#expect(parentDir.parentOf(childEncoded))
#expect(parentEncoded.parentOf(childDir))
}
@Test func testParentOfNonFileURL() throws {
let httpURL = URL(string: "http://example.com/path")!
let fileURL = baseTempURL.appendingPathComponent("file")
try createFile(at: fileURL)
#expect(false == httpURL.parentOf(fileURL))
#expect(false == fileURL.parentOf(httpURL))
}
@Test func testRelativeChildPathDirectChild() throws {
let parentDir = baseTempURL.appendingPathComponent("dir1")
let childFile = parentDir.appendingPathComponent("dir2").appendingPathComponent("file")
try createFile(at: childFile)
let relative = try childFile.relativeChildPath(to: parentDir)
#expect(relative == "dir2/file")
}
@Test func testRelativeChildPathDeeperChild() throws {
let parentDir = baseTempURL.appendingPathComponent("dir3").appendingPathComponent("test")
let childFile = parentDir.appendingPathComponent("dir4/dir2/file")
try createFile(at: childFile)
let relative = try childFile.relativeChildPath(to: parentDir)
#expect(relative == "dir4/dir2/file")
}
@Test func testRelativeChildPathDirectlyInsideBase() throws {
let childFile = baseTempURL.appendingPathComponent("file")
try createFile(at: childFile)
let relative = try childFile.relativeChildPath(to: baseTempURL)
#expect(relative == "file")
}
@Test func testRelativeChildPathSamePath() throws {
let dir = baseTempURL.appendingPathComponent("dir4")
try createDirectory(at: dir)
let dirCopy = URL(fileURLWithPath: dir.path)
#expect(try dir.relativeChildPath(to: dirCopy) == "")
#expect(try dirCopy.relativeChildPath(to: dir) == "")
}
@Test func testRelativeChildPathRootChild() throws {
let rootURL = URL(fileURLWithPath: "/")
let childDir = baseTempURL.appendingPathComponent("dir4")
try createDirectory(at: childDir)
// Compare only the portion that comes after "/"
let expected =
baseTempURL
.standardizedFileURL
.pathComponents
.dropFirst() // remove "/"
.joined(separator: "/") + "/dir4"
let relative = try childDir.relativeChildPath(to: rootURL)
#expect(relative == expected)
}
@Test func testRelativeChildPathRootToRootIsEmpty() throws {
let root1 = URL(fileURLWithPath: "/")
let root2 = URL(fileURLWithPath: "/")
#expect(try root1.relativeChildPath(to: root2) == "")
}
@Test func testRelativeChildPathNormalization() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let childFile = parentDir.appendingPathComponent("dir2/file")
try createFile(at: childFile)
let normalizedParent =
baseTempURL
.appendingPathComponent("dir8")
.appendingPathComponent("..")
.appendingPathComponent("dir4")
#expect(try childFile.relativeChildPath(to: normalizedParent) == "dir2/file")
}
@Test func testRelativeChildPathNormalizedChild() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let childFile = parentDir.appendingPathComponent("dir2/file")
try createFile(at: childFile)
let normalizedChild =
parentDir
.appendingPathComponent("dir9")
.appendingPathComponent("..")
.appendingPathComponent("dir2")
.appendingPathComponent("file")
#expect(try normalizedChild.relativeChildPath(to: parentDir) == "dir2/file")
}
@Test func testRelativeChildPathPercentEncoding() throws {
let parentDir = baseTempURL.appendingPathComponent("My dir4")
let childFile = parentDir.appendingPathComponent("dir2 X/file1")
try createFile(at: childFile)
#expect(try childFile.relativeChildPath(to: parentDir) == "dir2 X/file1")
let parentEncoded = URL(fileURLWithPath: baseTempURL.path + "/My%20dir4")
let childEncoded = URL(fileURLWithPath: baseTempURL.path + "/My%20dir4/dir2%20X/file1")
#expect(try childEncoded.relativeChildPath(to: parentDir) == "dir2 X/file1")
#expect(try childEncoded.relativeChildPath(to: parentEncoded) == "dir2 X/file1")
}
// MARK: - relativeChildPath Error Tests
@Test func testRelativeChildPathThrowsWhenNotAChild() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let otherDir = baseTempURL.appendingPathComponent("dir7/file")
try createDirectory(at: parentDir)
try createDirectory(at: otherDir)
#expect(throws: (BuildFSSync.Error.pathIsNotChild(otherDir.cleanPath, parentDir.cleanPath)).self) {
try otherDir.relativeChildPath(to: parentDir)
}
}
@Test func testRelativeChildPathThrowsForSiblings() throws {
let parentDir = baseTempURL.appendingPathComponent("dir3/test")
let sibling1 = parentDir.appendingPathComponent("dir4")
let sibling2 = parentDir.appendingPathComponent("dir5")
try createDirectory(at: sibling1)
try createDirectory(at: sibling2)
#expect(throws: (BuildFSSync.Error.pathIsNotChild(sibling2.cleanPath, sibling1.cleanPath)).self) {
try sibling2.relativeChildPath(to: sibling1)
}
}
@Test func testRelativeChildPathParentAsChildThrows() throws {
let parentDir = baseTempURL.appendingPathComponent("dir4")
let childDir = parentDir.appendingPathComponent("dir2")
try createDirectory(at: childDir)
#expect(throws: (BuildFSSync.Error.pathIsNotChild(parentDir.cleanPath, childDir.cleanPath)).self) {
try parentDir.relativeChildPath(to: childDir)
}
}
// MARK: - cleanPath Tests
@Test func testCleanPathSimple() throws {
let file = baseTempURL.appendingPathComponent("file")
try createFile(at: file)
#expect(file.cleanPath.hasSuffix("/file"))
#expect(file.cleanPath.contains(baseTempURL.lastPathComponent))
}
@Test func testCleanPathWithSpaces() throws {
let file = baseTempURL.appendingPathComponent("my file with spaces")
try createFile(at: file)
#expect(file.cleanPath.hasSuffix("/my file with spaces"))
#expect(file.cleanPath.contains(baseTempURL.lastPathComponent))
}
@Test func testCleanPathWithPercentEncoding() throws {
let fileWithSpace = baseTempURL.appendingPathComponent("my file")
try createFile(at: fileWithSpace)
let encodedPathString = baseTempURL.path + "/my%20file"
let urlFromString = URL(fileURLWithPath: encodedPathString)
#expect(urlFromString.cleanPath == fileWithSpace.cleanPath)
#expect(urlFromString.cleanPath.hasSuffix("/my file"))
}
}