File: EndToEndTests.swift

package info (click to toggle)
swiftlang 6.1.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,791,532 kB
  • sloc: cpp: 9,901,743; ansic: 2,201,431; asm: 1,091,827; python: 308,252; objc: 82,166; f90: 80,126; lisp: 38,358; pascal: 25,559; sh: 20,429; ml: 5,058; perl: 4,745; makefile: 4,484; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (190 lines) | stat: -rw-r--r-- 8,090 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Foundation
import Logging
import SystemPackage
import XCTest

@testable import SwiftSDKGenerator

extension FileManager {
  func withTemporaryDirectory<T>(logger: Logger, cleanup: Bool = true, body: (URL) async throws -> T) async throws -> T {
    // Create a temporary directory using a UUID.  Throws if the directory already exists.
    // The docs suggest using FileManager.url(for: .itemReplacementDirectory, ...) to create a temporary directory,
    // but on Linux the directory name contains spaces, which means we need to be careful to quote it everywhere:
    //
    //     `(A Document Being Saved By \(name))`
    //
    // https://github.com/swiftlang/swift-corelibs-foundation/blob/21b3196b33a64d53a0989881fc9a486227b4a316/Sources/Foundation/FileManager.swift#L152
    var logger = logger

    let temporaryDirectory = self.temporaryDirectory.appendingPathComponent(UUID().uuidString)
    logger[metadataKey: "temporaryDirectory"] = "\(temporaryDirectory.path)"

    try createDirectory(at: temporaryDirectory, withIntermediateDirectories: false)
    defer {
        // Best effort cleanup.
        do {
            if cleanup {
                try removeItem(at: temporaryDirectory)
                logger.info("Removed temporary directory")
            } else {
                logger.info("Keeping temporary directory")
            }
        } catch {}
    }

    logger.info("Created temporary directory")
    return try await body(temporaryDirectory)
  }
}

final class EndToEndTests: XCTestCase {
  private let testcases = [
    #"""
      // Default program generated by swift package init
      print("Hello, world!")
    """#,
    #"""
      // Check that libc_nonshared.a is linked properly
      import Foundation

      func fin() -> Void {
        print("exiting")
      }

      atexit(fin)
    """#,
  ]

  private let logger = Logger(label: "swift-sdk-generator")

  // Building an SDK requires running the sdk-generator with `swift run swift-sdk-generator`.
  // This takes a lock on `.build`, but if the tests are being run by `swift test` the outer Swift Package Manager
  // instance will already hold this lock, causing the test to deadlock.   We can work around this by giving
  // the `swift run swift-sdk-generator` instance its own scratch directory.
  func buildSDK(inDirectory packageDirectory: FilePath, scratchPath: String, withArguments runArguments: String) async throws -> String {
    let generatorOutput = try await Shell.readStdout(
      "cd \(packageDirectory) && swift run --scratch-path \"\(scratchPath)\" swift-sdk-generator make-linux-sdk \(runArguments)"
    )

    let installCommand = try XCTUnwrap(generatorOutput.split(separator: "\n").first {
      $0.contains("swift experimental-sdk install")
    })

    let bundleName = try XCTUnwrap(
      FilePath(String(XCTUnwrap(installCommand.split(separator: " ").last))).components.last
    ).stem

    let installedSDKs = try await Shell.readStdout("swift experimental-sdk list").components(separatedBy: "\n")

    // Make sure this bundle hasn't been installed already.
    if installedSDKs.contains(bundleName) {
      try await Shell.run("swift experimental-sdk remove \(bundleName)")
    }

    let installOutput = try await Shell.readStdout(String(installCommand))
    XCTAssertTrue(installOutput.contains("successfully installed"))

    return bundleName
  }

  func testPackageInitExecutable() async throws {
    if ProcessInfo.processInfo.environment.keys.contains("JENKINS_URL") {
      throw XCTSkip("EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145")
    }

    var packageDirectory = FilePath(#filePath)
    packageDirectory.removeLastComponent()
    packageDirectory.removeLastComponent()

    // Do multiple runs with different sets of arguments.
    // Test with no arguments by default:
    var possibleArguments = [""]
    do {
      try await Shell.run("docker ps")
      possibleArguments.append("--with-docker --linux-distribution-name rhel --linux-distribution-version ubi9")
    } catch {
      self.logger.warning("Docker CLI does not seem to be working, skipping tests that involve Docker.")
    }

    for runArguments in possibleArguments {
      if runArguments.contains("rhel") {
        // Temporarily skip the RHEL-based SDK.  XCTSkip() is not suitable as it would skipping the entire test case
        logger.warning("RHEL-based SDKs currently do not work with Swift 6.0: https://github.com/swiftlang/swift-sdk-generator/issues/138")
        continue
      }

      let bundleName = try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in
        try await buildSDK(inDirectory: packageDirectory, scratchPath: tempDir.path, withArguments: runArguments)
      }

      for testcase in self.testcases {
        try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in
          let testPackageURL = tempDir.appendingPathComponent("swift-sdk-generator-test")
          let testPackageDir = FilePath(testPackageURL.path)
          try FileManager.default.createDirectory(atPath: testPackageDir.string, withIntermediateDirectories: true)

          try await Shell.run("swift package --package-path \(testPackageDir) init --type executable")
          let main_swift = testPackageURL.appendingPathComponent("Sources/main.swift")
          try testcase.write(to: main_swift, atomically: true, encoding: .utf8)

          var buildOutput = try await Shell.readStdout(
            "swift build --package-path \(testPackageDir) --experimental-swift-sdk \(bundleName)"
          )
          XCTAssertTrue(buildOutput.contains("Build complete!"))

          try await Shell.run("rm -rf \(testPackageDir.appending(".build"))")

          buildOutput = try await Shell.readStdout(
            "swift build --package-path \(testPackageDir) --experimental-swift-sdk \(bundleName) --static-swift-stdlib"
          )
          XCTAssertTrue(buildOutput.contains("Build complete!"))
        }
      }
    }
  }

  func testRepeatedSDKBuilds() async throws {
    if ProcessInfo.processInfo.environment.keys.contains("JENKINS_URL") {
      throw XCTSkip("EndToEnd tests cannot currently run in CI: https://github.com/swiftlang/swift-sdk-generator/issues/145")
    }

    var packageDirectory = FilePath(#filePath)
    packageDirectory.removeLastComponent()
    packageDirectory.removeLastComponent()

    // Test that an existing SDK can be rebuilt without cleaning up.
    // Test with no arguments by default:
    var possibleArguments = [""]
    do {
      try await Shell.run("docker ps")
      possibleArguments.append("--with-docker --linux-distribution-name rhel --linux-distribution-version ubi9")
    } catch {
      self.logger.warning("Docker CLI does not seem to be working, skipping tests that involve Docker.")
    }

    for runArguments in possibleArguments {
      if runArguments.contains("rhel") {
        // Temporarily skip the RHEL-based SDK.  XCTSkip() is not suitable as it would skipping the entire test case
        logger.warning("RHEL-based SDKs currently do not work with Swift 6.0: https://github.com/swiftlang/swift-sdk-generator/issues/138")
        continue
      }

      try await FileManager.default.withTemporaryDirectory(logger: logger) { tempDir in
        let _ = try await buildSDK(inDirectory: packageDirectory, scratchPath: tempDir.path, withArguments: runArguments)
        let _ = try await buildSDK(inDirectory: packageDirectory, scratchPath: tempDir.path, withArguments: runArguments)
      }
    }
  }
}