Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Sources/_InternalTestSupport/CombinationsWithRepetition.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation
package struct CombinationsWithRepetition<C: Collection> : Sequence {

let base: C
let length: Int

init(of base: C, length: Int) {
self.base = base
self.length = length
}

package struct Iterator : IteratorProtocol {
let base: C

var firstIteration = true
var finished: Bool
var positions: [C.Index]

package init(of base: C, length: Int) {
self.base = base
finished = base.isEmpty
positions = Array(repeating: base.startIndex, count: length)
}

package mutating func next() -> [C.Element]? {
if firstIteration {
firstIteration = false
} else {
// Update indices for next combination.
finished = true
for i in positions.indices.reversed() {
base.formIndex(after: &positions[i])
if positions[i] != base.endIndex {
finished = false
break
} else {
positions[i] = base.startIndex
}
}

}
return finished ? nil : positions.map { base[$0] }
}
}

package func makeIterator() -> Iterator {
return Iterator(of: base, length: length)
}
}
37 changes: 37 additions & 0 deletions Sources/_InternalTestSupport/ProcessInfo+hostutils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
This source file is part of the Swift.org open source project

Copyright (c) 2025 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import Foundation

extension ProcessInfo {
public static func isHostAmazonLinux2(_ content: String? = nil) -> Bool {
let contentString: String
if let content {
contentString = content
} else {
let osReleasePath = "/etc/os-release"
do {
contentString = try String(contentsOfFile: osReleasePath, encoding: .utf8)
} catch {
return false
}
}
let lines = contentString.components(separatedBy: .newlines)
for line in lines {
if line.starts(with: "ID=") {
let id = line.replacingOccurrences(of: "ID=", with: "").trimmingCharacters(in: .whitespacesAndNewlines)
if id == "amzn" { // ID for Amazon Linux is "amzn"
return true
}
}
}
return false
}

}
2 changes: 1 addition & 1 deletion Tests/BasicsTests/Environment/EnvironmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ struct EnvironmentTests {
/// Important: This test is inherently race-prone, if it is proven to be
/// flaky, it should run in a singled threaded environment/removed entirely.
@Test(
.disabled(if: isInCiEnvironment || CiEnvironment.runningInSelfHostedPipeline, "This test can disrupt other tests running in parallel."),
.disabled(if: CiEnvironment.runningInSmokeTestPipeline || CiEnvironment.runningInSelfHostedPipeline, "This test can disrupt other tests running in parallel."),
)
func makeCustomPathEnv() async throws {
let customEnvironment: Environment = .current
Expand Down
81 changes: 81 additions & 0 deletions Tests/BasicsTests/ProcessInfoTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Foundation
import Basics
import Testing
@testable import struct _InternalTestSupport.CombinationsWithRepetition

fileprivate let d = [
[],
[""],
["line1"],
["line1", "line2"],
["line1", "line2", "line3"],
]
fileprivate let prefixAndSuffixData = CombinationsWithRepetition(of: d, length: 2).map( {data in
// Content(prefix: data.0, suffix: data.1)
Content(prefix: data[0], suffix: data[1])
})

fileprivate struct Content {
let prefix: [String]
let suffix: [String]

init(prefix pre: [String], suffix: [String]) {
self.prefix = pre
self.suffix = suffix
}

func getContent(_ value: String) -> String {
let contentArray: [String] = self.prefix + [value] + self.suffix
let content = contentArray.joined(separator: "\n")
return content
}
}

@Suite
struct ProcessInfoExtensionTests {

@Suite
struct isAmazonLinux2 {
@Test(
arguments: [
(contentUT: "", expected: false),
(contentUT: "ID=", expected: false),
(contentUT: "ID=foo", expected: false),
(contentUT: "ID=amzn", expected: true),
(contentUT: " ID=amzn", expected: false),
], prefixAndSuffixData,
)
fileprivate func isAmazonLinux2ReturnsExpectedValue(
data: (contentUT: String, expected: Bool),
content: Content,
) async throws {
let content = content.getContent(data.contentUT)

let actual = ProcessInfo.isHostAmazonLinux2(content)

#expect(actual == data.expected, "Content is: '\(content)'")
}

@Test(
"isHostAmazonLinux2 returns false when not executed on Linux",
.skipHostOS(.linux),
.tags(Tag.TestSize.medium),
)
func isAmazonLinux2ReturnsFalseWhenNotRunOnLinux() {
let actual = ProcessInfo.isHostAmazonLinux2()

#expect(actual == false)
}
}
}
40 changes: 28 additions & 12 deletions Tests/CommandsTests/APIDiffTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ struct APIDiffTests {
}
}

@Test(.requiresAPIDigester, arguments: SupportedBuildSystemOnAllPlatforms)
@Test(
.requiresAPIDigester,
.issue("https://github.com/swiftlang/swift-package-manager/issues/8926", relationship: .defect),
arguments: SupportedBuildSystemOnAllPlatforms,
)
func testMultiTargetAPIDiff(buildSystem: BuildSystemProvider.Kind) async throws {
try await fixture(name: "Miscellaneous/APIDiff/") { fixturePath in
let packageRoot = fixturePath.appending("Bar")
Expand All @@ -116,11 +120,15 @@ struct APIDiffTests {
string: "public class Qux<T, U> { private let x = 1 }"
)
try await expectThrowsCommandExecutionError(try await execute(["diagnose-api-breaking-changes", "1.2.3"], packagePath: packageRoot, buildSystem: buildSystem)) { error in
#expect(error.stdout.contains("2 breaking changes detected in Qux"))
#expect(error.stdout.contains("💔 API breakage: class Qux has generic signature change from <T> to <T, U>"))
#expect(error.stdout.contains("💔 API breakage: var Qux.x has been removed"))
#expect(error.stdout.contains("1 breaking change detected in Baz"))
#expect(error.stdout.contains("💔 API breakage: func bar() has been removed"))
withKnownIssue {
#expect(error.stdout.contains("2 breaking changes detected in Qux"))
#expect(error.stdout.contains("💔 API breakage: class Qux has generic signature change from <T> to <T, U>"))
#expect(error.stdout.contains("💔 API breakage: var Qux.x has been removed"))
#expect(error.stdout.contains("1 breaking change detected in Baz"))
#expect(error.stdout.contains("💔 API breakage: func bar() has been removed"))
} when: {
buildSystem == .swiftbuild && ProcessInfo.isHostAmazonLinux2()
}
}
}
}
Expand Down Expand Up @@ -156,7 +164,11 @@ struct APIDiffTests {
}
}

@Test(.requiresAPIDigester, arguments: SupportedBuildSystemOnAllPlatforms)
@Test(
.requiresAPIDigester,
.issue("https://github.com/swiftlang/swift-package-manager/issues/8926", relationship: .defect),
arguments: SupportedBuildSystemOnAllPlatforms,
)
func testCheckVendedModulesOnly(buildSystem: BuildSystemProvider.Kind) async throws {
try await fixture(name: "Miscellaneous/APIDiff/") { fixturePath in
let packageRoot = fixturePath.appending("NonAPILibraryTargets")
Expand All @@ -177,11 +189,15 @@ struct APIDiffTests {
string: "public class Qux<T, U> { private let x = 1 }"
)
try await expectThrowsCommandExecutionError(try await execute(["diagnose-api-breaking-changes", "1.2.3"], packagePath: packageRoot, buildSystem: buildSystem)) { error in
#expect(error.stdout.contains("💔 API breakage"))
let regex = try Regex("\\d+ breaking change(s?) detected in Foo")
#expect(error.stdout.contains(regex))
#expect(error.stdout.contains(regex))
#expect(error.stdout.contains(regex))
try withKnownIssue {
#expect(error.stdout.contains("💔 API breakage"))
let regex = try Regex("\\d+ breaking change(s?) detected in Foo")
#expect(error.stdout.contains(regex))
#expect(error.stdout.contains(regex))
#expect(error.stdout.contains(regex))
} when: {
buildSystem == .swiftbuild && ProcessInfo.isHostAmazonLinux2()
}

// Qux is not part of a library product, so any API changes should be ignored
#expect(!error.stdout.contains("Qux"))
Expand Down