Copying Resource Files For Xcode SPM Tests

吃可爱长大的小学妹 提交于 2019-12-23 04:07:09

问题


I am new to the Swift Package Manager but with its integration into Xcode 11 it is time to give it a try. I have a new application and SPM library within a new workspace. I have a working library with tests and have successfully imported the library into the application.

I need to extend the SPM library with new tests that parse json files. I have learned that a resources directory feature is not supported. The only workable scheme seems to be a file copy step added to the library build process so that the resource files can be discovered by the executable.

I could figure out how to do this from the command line but not with Xcode running the build and test. There is no Copy Bundle Resources, build phase for swift packages. In fact everything appears to be hidden by Xcode.

I have looked within the SPM for Makefile type files that would allow me to edit default command line actions thereby circumventing Xcode; but I am not seeing them.

Is there some way to interact/control how Xcode 11 builds SPM targets so that I can copy non-code files to test targets?


回答1:


Got it working!!!

struct Resource {
  let name: String
  let type: String
  let url: URL

  init(name: String, type: String, sourceFile: StaticString = #file) throws {
    self.name = name
    self.type = type

    // The following assumes that your test source files are all in the same directory, and the resources are one directory down and over
    // <Some folder>
    //  - Resources
    //      - <resource files>
    //  - <Some test source folder>
    //      - <test case files>
    let testCaseURL = URL(fileURLWithPath: "\(sourceFile)", isDirectory: false)
    let testsFolderURL = testCaseURL.deletingLastPathComponent()
    let resourcesFolderURL = testsFolderURL.deletingLastPathComponent().appendingPathComponent("Resources", isDirectory: true)
    self.url = resourcesFolderURL.appendingPathComponent("\(name).\(type)", isDirectory: false)
  }
}

Usage:

final class SPMTestDataTests: XCTestCase {
  func testExample() throws {
    // This is an example of a functional test case.
    // Use XCTAssert and related functions to verify your tests produce the correct
    // results.
    XCTAssertEqual(SPMTestData().text, "Hello, World!")

    let file = try Resource(name: "image", type: "png")
    let image = UIImage(contentsOfFile: file.url.path)
    print(image)
  }
}

I found the key of using #file here




回答2:


This is another workaround to provide access to test resources. Hopefully an answer to the OP's question will be forthcoming.

Using the code below, an extension is created to allow callers to create URL's to test resources like this.

let url = URL(forResource: "payload", type: "json")

This code requires that all resource files be located in a flat directory named "Resources" just under the test target.

// MARK: - ./Resources/ Workaround
// URL of the directory containing non-code, test resource fi;es.
//
// It is required that a directory named "Resources" be contained immediately below the test target.
// Root
//   Package.swift
//   Tests
//     (target)
//       Resources
//
fileprivate let _resources: URL = {
    func packageRoot(of file: String) -> URL? {
        func isPackageRoot(_ url: URL) -> Bool {
            let filename = url.appendingPathComponent("Package.swift", isDirectory: false)
            return FileManager.default.fileExists(atPath: filename.path)
        }

        var url = URL(fileURLWithPath: file, isDirectory: false)
        repeat {
            url = url.deletingLastPathComponent()
            if url.pathComponents.count <= 1 {
                return nil
            }
        } while !isPackageRoot(url)
        return url
    }

    guard let root = packageRoot(of: #file) else {
        fatalError("\(#file) must be contained in a Swift Package Manager project.")
    }
    let fileComponents = URL(fileURLWithPath: #file, isDirectory: false).pathComponents
    let rootComponenets = root.pathComponents
    let trailingComponents = Array(fileComponents.dropFirst(rootComponenets.count))
    let resourceComponents = rootComponenets + trailingComponents[0...1] + ["Resources"]
    return URL(fileURLWithPath: resourceComponents.joined(separator: "/"), isDirectory: true)
}()

extension URL {
    init(forResource name: String, type: String) {
        let url = _resources.appendingPathComponent("\(name).\(type)", isDirectory: false)
        self = url
    }
}


来源:https://stackoverflow.com/questions/57693818/copying-resource-files-for-xcode-spm-tests

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!