Копирование файлов ресурсов для тестов Xcode SPM

Я новичок в Swift Package Manager, но с его интеграцией в Xcode 11 пришло время попробовать. У меня есть новое приложение и библиотека SPM в новом рабочем пространстве. У меня есть рабочая библиотека с тестами, и я успешно импортировал библиотеку в приложение.

Мне нужно расширить библиотеку SPM новыми тестами, которые анализируют файлы json. Я узнал, что функция каталога ресурсов не поддерживается. Единственной работоспособной схемой, по-видимому, является этап копирования файла, добавленный в процесс сборки библиотеки, чтобы файлы ресурсов могли быть обнаружены исполняемым файлом.

Я мог понять, как это сделать из командной строки, но не с Xcode, выполняющим сборку и тестирование. Нет ресурсов Copy Bundle, фаза сборки для быстрых пакетов. Фактически, все кажется скрытым Xcode.

Я искал в SPM файлы типа Makefile, которые позволили бы мне редактировать действия командной строки по умолчанию, тем самым обходя Xcode; но я их не вижу.

Есть ли способ взаимодействовать / контролировать, как Xcode 11 создает целевые объекты SPM, чтобы я мог копировать файлы без кода для тестирования целей?


person Price Ringo    schedule 28.08.2019    source источник
comment
Я как раз собирался спросить то же самое. AFAIK вы не можете копировать не код ни в какие цели SPM, поскольку они являются статическими библиотеками. Но похоже, что вы должны просто создать папку в папке тестов вашего проекта, а затем получить ссылку на нее при запуске тестов.   -  person jamone    schedule 28.08.2019
comment
Исполняемый файл теста не входит в поставленную библиотеку. Я могу использовать Bundle (for :), чтобы получить абсолютный путь к местоположению тестового каталога и оттуда построить путь к моему каталогу ресурсов. Но ложка дегтя в том, что мои файлы json нужно копировать туда во время процесса сборки.   -  person Price Ringo    schedule 28.08.2019
comment
Из того, что я вижу, тестовый каталог находится в производных данных. Таким образом, нет способа перейти оттуда в исходный каталог для ссылки на файлы данных, кроме жесткого кодирования пути, но это не работает для других разработчиков или CI.   -  person jamone    schedule 29.08.2019


Ответы (2)


Получил работу !!!

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)
  }
}

Использование:

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)
  }
}

введите здесь описание изображения

Я нашел ключ использования #file здесь

person jamone    schedule 29.08.2019

Это еще один обходной путь для предоставления доступа к тестовым ресурсам. Надеюсь, ответ на вопрос ОП будет.

Используя приведенный ниже код, создается расширение, позволяющее вызывающим абонентам создавать URL-адреса для тестирования подобных ресурсов.

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

Этот код требует, чтобы все файлы ресурсов располагались в плоском каталоге с именем «Ресурсы» прямо под тестовой целью.

// 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
    }
}
person Price Ringo    schedule 30.08.2019
comment
Спасибо за это. Немного отредактировал его и заключил в модуль SPM: github.com/eonist/ResourceHelper - person Sentry.co; 04.01.2020