Fix duplicate localized resource files (#363)
Resolves https://github.com/tuist/tuist/issues/361 ### Short description Localized resources can up getting duplicated within the resources build phase and the project files elements. The reason this was occurring: - Give a path to a resource e.g. `/resources/en.lproj/foo.png` - `/resources/en.lproj/` is used detected - All files within `/resources/en.lproj/` are enumerated and added - When another path to a resource is encountered e.g. `/resources/en.lproj/bar.png` - The same process is repeated, resulting in duplicate file elements! ### Solution - Update project files element logic to better accommodate multiple localized files - Add a cache to keep track of which build files were added the resources build phase to prevent adding duplicates. - Improved file element missing file warnings for directories e.g. `resources: ["Resources/en.lproj"]` would yield the following warnings: ``` Warning: 'Resources/en.lproj' is a directory, try using: 'Resources/en.lproj/**' to list its files ``` ### Test Plan - Verify unit tests pass via `swift tests` - Verify acceptance tests pass via `bundle rake exec features` - Manually generate `fixtures/ios_app_with_framework_and_resources` via `tuist generate` - Verify the generated project includes the localized string files without any duplicates
This commit is contained in:
parent
d32dcf0b70
commit
c87a1cda62
|
@ -22,6 +22,7 @@ Please, check out guidelines: https://keepachangelog.com/en/1.0.0/
|
||||||
- Fixing generation failures due to asset catalog & `**/*.png` glob patterns handling https://github.com/tuist/tuist/pull/346 by @kwridan
|
- Fixing generation failures due to asset catalog & `**/*.png` glob patterns handling https://github.com/tuist/tuist/pull/346 by @kwridan
|
||||||
- Supporting bundle target dependencies that reside in different projects (in `TuistGenerator`) https://github.com/tuist/tuist/pull/348 by @kwridan
|
- Supporting bundle target dependencies that reside in different projects (in `TuistGenerator`) https://github.com/tuist/tuist/pull/348 by @kwridan
|
||||||
- Fixing header paths including folders and non-header files https://github.com/tuist/tuist/pull/356 by @kwridan
|
- Fixing header paths including folders and non-header files https://github.com/tuist/tuist/pull/356 by @kwridan
|
||||||
|
- Fix duplicate localized resource files https://github.com/tuist/tuist/pull/363 by @kwridan
|
||||||
|
|
||||||
## 0.14.0
|
## 0.14.0
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
|
||||||
fileElements: ProjectFileElements,
|
fileElements: ProjectFileElements,
|
||||||
pbxproj: PBXProj,
|
pbxproj: PBXProj,
|
||||||
resourcesBuildPhase: PBXResourcesBuildPhase) throws {
|
resourcesBuildPhase: PBXResourcesBuildPhase) throws {
|
||||||
|
var buildFilesCache = Set<AbsolutePath>()
|
||||||
try files.forEach { buildFilePath in
|
try files.forEach { buildFilePath in
|
||||||
let pathString = buildFilePath.pathString
|
let pathString = buildFilePath.pathString
|
||||||
let pathRange = NSRange(location: 0, length: pathString.count)
|
let pathRange = NSRange(location: 0, length: pathString.count)
|
||||||
|
@ -185,7 +186,7 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var element: PBXFileElement?
|
var element: (element: PBXFileElement, path: AbsolutePath)?
|
||||||
|
|
||||||
if isLocalized {
|
if isLocalized {
|
||||||
let name = buildFilePath.components.last!
|
let name = buildFilePath.components.last!
|
||||||
|
@ -193,18 +194,18 @@ final class BuildPhaseGenerator: BuildPhaseGenerating {
|
||||||
guard let group = fileElements.group(path: path) else {
|
guard let group = fileElements.group(path: path) else {
|
||||||
throw BuildPhaseGenerationError.missingFileReference(buildFilePath)
|
throw BuildPhaseGenerationError.missingFileReference(buildFilePath)
|
||||||
}
|
}
|
||||||
element = group
|
element = (group, path)
|
||||||
|
|
||||||
} else if !isLproj {
|
} else if !isLproj {
|
||||||
guard let fileReference = fileElements.file(path: buildFilePath) else {
|
guard let fileReference = fileElements.file(path: buildFilePath) else {
|
||||||
throw BuildPhaseGenerationError.missingFileReference(buildFilePath)
|
throw BuildPhaseGenerationError.missingFileReference(buildFilePath)
|
||||||
}
|
}
|
||||||
element = fileReference
|
element = (fileReference, buildFilePath)
|
||||||
}
|
}
|
||||||
if let element = element {
|
if let element = element, buildFilesCache.contains(element.path) == false {
|
||||||
let pbxBuildFile = PBXBuildFile(file: element)
|
let pbxBuildFile = PBXBuildFile(file: element.element)
|
||||||
pbxproj.add(object: pbxBuildFile)
|
pbxproj.add(object: pbxBuildFile)
|
||||||
resourcesBuildPhase.files?.append(pbxBuildFile)
|
resourcesBuildPhase.files?.append(pbxBuildFile)
|
||||||
|
buildFilesCache.insert(element.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,11 +304,16 @@ class ProjectFileElements {
|
||||||
|
|
||||||
// Add the file element
|
// Add the file element
|
||||||
if isLocalized(path: absolutePath) {
|
if isLocalized(path: absolutePath) {
|
||||||
addVariantGroup(from: from,
|
// Localized container (e.g. /path/to/en.lproj) we don't add it directly
|
||||||
absolutePath: absolutePath,
|
// an element will get added once the next path component is evaluated
|
||||||
relativePath: relativePath,
|
//
|
||||||
toGroup: toGroup,
|
// note: assumption here is a path to a nested resource is always provided
|
||||||
pbxproj: pbxproj)
|
return (element: toGroup, path: absolutePath)
|
||||||
|
} else if isLocalized(path: from) {
|
||||||
|
// Localized file (e.g. /path/to/en.lproj/foo.png)
|
||||||
|
addLocalizedFile(localizedFile: absolutePath,
|
||||||
|
toGroup: toGroup,
|
||||||
|
pbxproj: pbxproj)
|
||||||
return nil
|
return nil
|
||||||
} else if isVersionGroup(path: absolutePath) {
|
} else if isVersionGroup(path: absolutePath) {
|
||||||
return addVersionGroupElement(from: from,
|
return addVersionGroupElement(from: from,
|
||||||
|
@ -335,36 +340,60 @@ class ProjectFileElements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addVariantGroup(from: AbsolutePath,
|
func addLocalizedFile(localizedFile: AbsolutePath,
|
||||||
absolutePath: AbsolutePath,
|
toGroup: PBXGroup,
|
||||||
relativePath _: RelativePath,
|
pbxproj: PBXProj) {
|
||||||
toGroup: PBXGroup,
|
// e.g.
|
||||||
pbxproj: PBXProj) {
|
// from: resources/en.lproj/
|
||||||
// /path/to/*.lproj/*
|
// localizedFile: resources/en.lproj/App.strings
|
||||||
absolutePath.glob("*").sorted().forEach { localizedFile in
|
|
||||||
let localizedName = localizedFile.components.last!
|
|
||||||
|
|
||||||
// Variant group
|
// Variant Group
|
||||||
let variantGroupPath = absolutePath.parentDirectory.appending(component: localizedName)
|
let localizedName = localizedFile.basename // e.g. App.strings
|
||||||
var variantGroup: PBXVariantGroup! = elements[variantGroupPath] as? PBXVariantGroup
|
let localizedContainer = localizedFile.parentDirectory // e.g. resources/en.lproj
|
||||||
if variantGroup == nil {
|
let variantGroupPath = localizedContainer
|
||||||
variantGroup = PBXVariantGroup(children: [], sourceTree: .group, name: localizedName)
|
.parentDirectory
|
||||||
pbxproj.add(object: variantGroup)
|
.appending(component: localizedName) // e.g. resources/App.strings
|
||||||
toGroup.children.append(variantGroup)
|
|
||||||
elements[variantGroupPath] = variantGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
// Localized element
|
let variantGroup = addVariantGroup(variantGroupPath: variantGroupPath,
|
||||||
let localizedFilePath = "\(absolutePath.components.last!)/\(localizedName)" // e.g: en.lproj/Main.storyboard
|
localizedName: localizedName,
|
||||||
let lastKnownFileType = Xcode.filetype(extension: localizedName) // e.g. Main.storyboard
|
toGroup: toGroup,
|
||||||
let name = absolutePath.components.last!.split(separator: ".").first! // e.g. en
|
pbxproj: pbxproj)
|
||||||
let localizedFileReference = PBXFileReference(sourceTree: .group,
|
|
||||||
name: String(name),
|
// Localized element
|
||||||
lastKnownFileType: lastKnownFileType,
|
addLocalizedFileElement(localizedFile: localizedFile,
|
||||||
path: localizedFilePath)
|
variantGroup: variantGroup,
|
||||||
pbxproj.add(object: localizedFileReference)
|
localizedContainer: localizedContainer,
|
||||||
variantGroup.children.append(localizedFileReference)
|
pbxproj: pbxproj)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addVariantGroup(variantGroupPath: AbsolutePath,
|
||||||
|
localizedName: String,
|
||||||
|
toGroup: PBXGroup,
|
||||||
|
pbxproj: PBXProj) -> PBXVariantGroup {
|
||||||
|
if let variantGroup = elements[variantGroupPath] as? PBXVariantGroup {
|
||||||
|
return variantGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let variantGroup = PBXVariantGroup(children: [], sourceTree: .group, name: localizedName)
|
||||||
|
pbxproj.add(object: variantGroup)
|
||||||
|
toGroup.children.append(variantGroup)
|
||||||
|
elements[variantGroupPath] = variantGroup
|
||||||
|
return variantGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addLocalizedFileElement(localizedFile: AbsolutePath,
|
||||||
|
variantGroup: PBXVariantGroup,
|
||||||
|
localizedContainer: AbsolutePath,
|
||||||
|
pbxproj: PBXProj) {
|
||||||
|
let localizedFilePath = localizedFile.relative(to: localizedContainer.parentDirectory)
|
||||||
|
let lastKnownFileType = Xcode.filetype(extension: localizedFile.basename)
|
||||||
|
let name = localizedContainer.basename.split(separator: ".").first
|
||||||
|
let localizedFileReference = PBXFileReference(sourceTree: .group,
|
||||||
|
name: name.map { String($0) },
|
||||||
|
lastKnownFileType: lastKnownFileType,
|
||||||
|
path: localizedFilePath.pathString)
|
||||||
|
pbxproj.add(object: localizedFileReference)
|
||||||
|
variantGroup.children.append(localizedFileReference)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addVersionGroupElement(from: AbsolutePath,
|
func addVersionGroupElement(from: AbsolutePath,
|
||||||
|
|
|
@ -110,7 +110,11 @@ extension TuistGenerator.FileElement {
|
||||||
.filter(includeFiles)
|
.filter(includeFiles)
|
||||||
|
|
||||||
if files.isEmpty {
|
if files.isEmpty {
|
||||||
printer.print(warning: "No files found at: \(string)")
|
if fileHandler.isFolder(path.appending(RelativePath(string))) {
|
||||||
|
printer.print(warning: "'\(string)' is a directory, try using: '\(string)/**' to list its files")
|
||||||
|
} else {
|
||||||
|
printer.print(warning: "No files found at: \(string)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return files
|
return files
|
||||||
|
|
|
@ -162,27 +162,37 @@ final class BuildPhaseGeneratorTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_generateResourcesBuildPhase_whenLocalizedFile() throws {
|
func test_generateResourcesBuildPhase_whenLocalizedFile() throws {
|
||||||
let path = AbsolutePath("/en.lproj/Main.storyboard")
|
// Given
|
||||||
let target = Target.test(resources: [.file(path: path)])
|
let files: [AbsolutePath] = [
|
||||||
let fileElements = ProjectFileElements()
|
"/path/resources/en.lproj/Main.storyboard",
|
||||||
let pbxproj = PBXProj()
|
"/path/resources/en.lproj/App.strings",
|
||||||
let group = PBXVariantGroup()
|
"/path/resources/fr.lproj/Main.storyboard",
|
||||||
pbxproj.add(object: group)
|
"/path/resources/fr.lproj/App.strings",
|
||||||
fileElements.elements[AbsolutePath("/Main.storyboard")] = group
|
]
|
||||||
let nativeTarget = PBXNativeTarget(name: "Test")
|
|
||||||
|
|
||||||
|
let resources = files.map { FileElement.file(path: $0) }
|
||||||
|
let fileElements = createLocalizedResourceFileElements(for: [
|
||||||
|
"/path/resources/Main.storyboard",
|
||||||
|
"/path/resources/App.strings",
|
||||||
|
])
|
||||||
|
|
||||||
|
let nativeTarget = PBXNativeTarget(name: "Test")
|
||||||
|
let pbxproj = PBXProj()
|
||||||
|
|
||||||
|
// When
|
||||||
try subject.generateResourcesBuildPhase(path: "/path",
|
try subject.generateResourcesBuildPhase(path: "/path",
|
||||||
target: target,
|
target: .test(resources: resources),
|
||||||
graph: Graph.test(),
|
graph: Graph.test(),
|
||||||
pbxTarget: nativeTarget,
|
pbxTarget: nativeTarget,
|
||||||
fileElements: fileElements,
|
fileElements: fileElements,
|
||||||
pbxproj: pbxproj)
|
pbxproj: pbxproj)
|
||||||
|
|
||||||
let pbxBuildPhase: PBXBuildPhase? = nativeTarget.buildPhases.first
|
// Then
|
||||||
XCTAssertNotNil(pbxBuildPhase)
|
let buildPhase = nativeTarget.buildPhases.first
|
||||||
XCTAssertTrue(pbxBuildPhase is PBXResourcesBuildPhase)
|
XCTAssertEqual(buildPhase?.files?.map { $0.file }, [
|
||||||
let pbxBuildFile: PBXBuildFile? = pbxBuildPhase?.files?.first
|
fileElements.elements["/path/resources/Main.storyboard"],
|
||||||
XCTAssertEqual(pbxBuildFile?.file, group)
|
fileElements.elements["/path/resources/App.strings"],
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_generateResourcesBuildPhase_whenCoreDataModel() throws {
|
func test_generateResourcesBuildPhase_whenCoreDataModel() throws {
|
||||||
|
@ -317,4 +327,12 @@ final class BuildPhaseGeneratorTests: XCTestCase {
|
||||||
})
|
})
|
||||||
return fileElements
|
return fileElements
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func createLocalizedResourceFileElements(for files: [AbsolutePath]) -> ProjectFileElements {
|
||||||
|
let fileElements = ProjectFileElements()
|
||||||
|
fileElements.elements = Dictionary(uniqueKeysWithValues: files.map {
|
||||||
|
($0, PBXVariantGroup())
|
||||||
|
})
|
||||||
|
return fileElements
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,8 +132,7 @@ final class ProjectFileElementsTests: XCTestCase {
|
||||||
func test_addElement_xcassets() throws {
|
func test_addElement_xcassets() throws {
|
||||||
// Given
|
// Given
|
||||||
let element = GroupFileElement(path: "/path/myfolder/resources/assets.xcassets/foo/bar.png",
|
let element = GroupFileElement(path: "/path/myfolder/resources/assets.xcassets/foo/bar.png",
|
||||||
group: .group(name: "Project"),
|
group: .group(name: "Project"))
|
||||||
isReference: true)
|
|
||||||
|
|
||||||
// When
|
// When
|
||||||
try subject.generate(fileElement: element,
|
try subject.generate(fileElement: element,
|
||||||
|
@ -158,8 +157,7 @@ final class ProjectFileElementsTests: XCTestCase {
|
||||||
]
|
]
|
||||||
let elements = resouces.map {
|
let elements = resouces.map {
|
||||||
GroupFileElement(path: AbsolutePath($0),
|
GroupFileElement(path: AbsolutePath($0),
|
||||||
group: .group(name: "Project"),
|
group: .group(name: "Project"))
|
||||||
isReference: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When
|
// When
|
||||||
|
@ -177,6 +175,44 @@ final class ProjectFileElementsTests: XCTestCase {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_addElement_lproj_multiple_files() throws {
|
||||||
|
// Given
|
||||||
|
let resouces = try fileHandler.createFiles([
|
||||||
|
"resources/en.lproj/App.strings",
|
||||||
|
"resources/en.lproj/Extension.strings",
|
||||||
|
"resources/fr.lproj/App.strings",
|
||||||
|
"resources/fr.lproj/Extension.strings",
|
||||||
|
])
|
||||||
|
|
||||||
|
let elements = resouces.map {
|
||||||
|
GroupFileElement(path: $0,
|
||||||
|
group: .group(name: "Project"),
|
||||||
|
isReference: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
try elements.forEach {
|
||||||
|
try subject.generate(fileElement: $0,
|
||||||
|
groups: groups,
|
||||||
|
pbxproj: pbxproj,
|
||||||
|
sourceRootPath: fileHandler.currentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then
|
||||||
|
let projectGroup = groups.main.group(named: "Project")
|
||||||
|
XCTAssertEqual(projectGroup?.debugChildPaths, [
|
||||||
|
"resources/App.strings/en",
|
||||||
|
"resources/App.strings/fr",
|
||||||
|
"resources/Extension.strings/en",
|
||||||
|
"resources/Extension.strings/fr",
|
||||||
|
])
|
||||||
|
|
||||||
|
XCTAssertEqual(projectGroup?.debugVariantGroupPaths, [
|
||||||
|
"resources/App.strings",
|
||||||
|
"resources/Extension.strings",
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
func test_targetProducts() {
|
func test_targetProducts() {
|
||||||
let target = Target.test()
|
let target = Target.test()
|
||||||
let products = subject.targetProducts(target: target).sorted()
|
let products = subject.targetProducts(target: target).sorted()
|
||||||
|
@ -362,32 +398,23 @@ final class ProjectFileElementsTests: XCTestCase {
|
||||||
XCTAssertEqual(file.sourceTree, .group)
|
XCTAssertEqual(file.sourceTree, .group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_addVariantGroup() throws {
|
func test_addLocalizedFile() throws {
|
||||||
let fileName = "localizable.strings"
|
// Given
|
||||||
let dir = try TemporaryDirectory(removeTreeOnDeinit: true)
|
|
||||||
let localizedDir = dir.path.appending(component: "en.lproj")
|
|
||||||
try fileHandler.createFolder(localizedDir)
|
|
||||||
try "test".write(to: localizedDir.appending(component: fileName).url, atomically: true, encoding: .utf8)
|
|
||||||
let from = dir.path.parentDirectory
|
|
||||||
let absolutePath = localizedDir
|
|
||||||
let relativePath = RelativePath("en.lproj")
|
|
||||||
let group = PBXGroup()
|
|
||||||
let pbxproj = PBXProj()
|
let pbxproj = PBXProj()
|
||||||
pbxproj.add(object: group)
|
let group = PBXGroup()
|
||||||
subject.addVariantGroup(from: from,
|
let file: AbsolutePath = "/path/to/resources/en.lproj/App.strings"
|
||||||
absolutePath: absolutePath,
|
|
||||||
relativePath: relativePath,
|
|
||||||
toGroup: group,
|
|
||||||
pbxproj: pbxproj)
|
|
||||||
let variantGroupPath = dir.path.appending(component: fileName)
|
|
||||||
let variantGroup: PBXVariantGroup = subject.group(path: variantGroupPath) as! PBXVariantGroup
|
|
||||||
XCTAssertEqual(variantGroup.name, fileName)
|
|
||||||
XCTAssertEqual(variantGroup.sourceTree, .group)
|
|
||||||
|
|
||||||
let fileReference: PBXFileReference? = variantGroup.children.first as? PBXFileReference
|
// When
|
||||||
XCTAssertEqual(fileReference?.name, "en")
|
subject.addLocalizedFile(localizedFile: file,
|
||||||
XCTAssertEqual(fileReference?.sourceTree, .group)
|
toGroup: group,
|
||||||
XCTAssertEqual(fileReference?.path, "en.lproj/\(fileName)")
|
pbxproj: pbxproj)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
let variantGroup = group.children.first as? PBXVariantGroup
|
||||||
|
XCTAssertEqual(variantGroup?.name, "App.strings")
|
||||||
|
XCTAssertNil(variantGroup?.path)
|
||||||
|
XCTAssertEqual(variantGroup?.children.map { $0.name }, ["en"])
|
||||||
|
XCTAssertEqual(variantGroup?.children.map { $0.path }, ["en.lproj/App.strings"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_addVersionGroupElement() throws {
|
func test_addVersionGroupElement() throws {
|
||||||
|
@ -527,4 +554,18 @@ private extension PBXGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retuns all the child variant groups (recursively)
|
||||||
|
var debugVariantGroupPaths: [String] {
|
||||||
|
return children.flatMap { (element: PBXFileElement) -> [String] in
|
||||||
|
switch element {
|
||||||
|
case let group as PBXVariantGroup:
|
||||||
|
return [group.nameOrPath]
|
||||||
|
case let group as PBXGroup:
|
||||||
|
return group.debugVariantGroupPaths.map { group.nameOrPath + "/" + $0 }
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,70 +269,6 @@ class GeneratorModelLoaderTest: XCTestCase {
|
||||||
XCTAssertEqual(model.projects, [])
|
XCTAssertEqual(model.projects, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_loadWorkspace_withInvalidFilePaths() throws {
|
|
||||||
// Given
|
|
||||||
let path = fileHandler.currentPath
|
|
||||||
try fileHandler.createFolders([
|
|
||||||
"Documentation",
|
|
||||||
])
|
|
||||||
|
|
||||||
let manifests = [
|
|
||||||
path: WorkspaceManifest.test(name: "SomeWorkspace",
|
|
||||||
projects: [],
|
|
||||||
additionalFiles: [
|
|
||||||
"Documentation/**/*.md",
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
|
|
||||||
let manifestLoader = createManifestLoader(with: manifests)
|
|
||||||
let subject = GeneratorModelLoader(fileHandler: fileHandler,
|
|
||||||
manifestLoader: manifestLoader,
|
|
||||||
manifestTargetGenerator: manifestTargetGenerator,
|
|
||||||
printer: printer)
|
|
||||||
|
|
||||||
// When
|
|
||||||
let model = try subject.loadWorkspace(at: path)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
XCTAssertEqual(printer.printWarningArgs, [
|
|
||||||
"No files found at: Documentation/**/*.md",
|
|
||||||
])
|
|
||||||
XCTAssertEqual(model.additionalFiles, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
func test_loadWorkspace_withInvalidFolderReferencePaths() throws {
|
|
||||||
// Given
|
|
||||||
let path = fileHandler.currentPath
|
|
||||||
try fileHandler.createFiles([
|
|
||||||
"README.md",
|
|
||||||
])
|
|
||||||
|
|
||||||
let manifests = [
|
|
||||||
path: WorkspaceManifest.test(name: "SomeWorkspace",
|
|
||||||
projects: [],
|
|
||||||
additionalFiles: [
|
|
||||||
.folderReference(path: "Documentation"),
|
|
||||||
.folderReference(path: "README.md"),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
|
|
||||||
let manifestLoader = createManifestLoader(with: manifests)
|
|
||||||
let subject = GeneratorModelLoader(fileHandler: fileHandler,
|
|
||||||
manifestLoader: manifestLoader,
|
|
||||||
manifestTargetGenerator: manifestTargetGenerator,
|
|
||||||
printer: printer)
|
|
||||||
|
|
||||||
// When
|
|
||||||
let model = try subject.loadWorkspace(at: path)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
XCTAssertEqual(printer.printWarningArgs, [
|
|
||||||
"Documentation does not exist",
|
|
||||||
"README.md is not a directory - folder reference paths need to point to directories",
|
|
||||||
])
|
|
||||||
XCTAssertEqual(model.additionalFiles, [])
|
|
||||||
}
|
|
||||||
|
|
||||||
func test_settings() throws {
|
func test_settings() throws {
|
||||||
// Given
|
// Given
|
||||||
let debug = ConfigurationManifest(settings: ["Debug": "Debug"], xcconfig: "debug.xcconfig")
|
let debug = ConfigurationManifest(settings: ["Debug": "Debug"], xcconfig: "debug.xcconfig")
|
||||||
|
@ -474,6 +410,88 @@ class GeneratorModelLoaderTest: XCTestCase {
|
||||||
XCTAssertEqual(GeneratorModelLoaderError.missingFile("/missing/path").description, "Couldn't find file at path '/missing/path'")
|
XCTAssertEqual(GeneratorModelLoaderError.missingFile("/missing/path").description, "Couldn't find file at path '/missing/path'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func test_fileElement_warning_withDirectoryPathsAsFiles() throws {
|
||||||
|
// Given
|
||||||
|
let path = fileHandler.currentPath
|
||||||
|
try fileHandler.createFiles([
|
||||||
|
"Documentation/README.md",
|
||||||
|
"Documentation/USAGE.md",
|
||||||
|
])
|
||||||
|
|
||||||
|
let manifest = ProjectDescription.FileElement.glob(pattern: "Documentation")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let model = TuistGenerator.FileElement.from(manifest: manifest,
|
||||||
|
path: path,
|
||||||
|
fileHandler: fileHandler,
|
||||||
|
printer: printer,
|
||||||
|
includeFiles: { !self.fileHandler.isFolder($0) })
|
||||||
|
|
||||||
|
// Then
|
||||||
|
XCTAssertEqual(printer.printWarningArgs, [
|
||||||
|
"'Documentation' is a directory, try using: 'Documentation/**' to list its files",
|
||||||
|
])
|
||||||
|
XCTAssertEqual(model, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_fileElement_warning_withMisingPaths() throws {
|
||||||
|
// Given
|
||||||
|
let path = fileHandler.currentPath
|
||||||
|
let manifest = ProjectDescription.FileElement.glob(pattern: "Documentation/**")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let model = TuistGenerator.FileElement.from(manifest: manifest,
|
||||||
|
path: path,
|
||||||
|
fileHandler: fileHandler,
|
||||||
|
printer: printer)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
XCTAssertEqual(printer.printWarningArgs, [
|
||||||
|
"No files found at: Documentation/**",
|
||||||
|
])
|
||||||
|
XCTAssertEqual(model, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_fileElement_warning_withInvalidFolderReference() throws {
|
||||||
|
// Given
|
||||||
|
let path = fileHandler.currentPath
|
||||||
|
try fileHandler.createFiles([
|
||||||
|
"README.md",
|
||||||
|
])
|
||||||
|
|
||||||
|
let manifest = ProjectDescription.FileElement.folderReference(path: "README.md")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let model = TuistGenerator.FileElement.from(manifest: manifest,
|
||||||
|
path: path,
|
||||||
|
fileHandler: fileHandler,
|
||||||
|
printer: printer)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
XCTAssertEqual(printer.printWarningArgs, [
|
||||||
|
"README.md is not a directory - folder reference paths need to point to directories",
|
||||||
|
])
|
||||||
|
XCTAssertEqual(model, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_fileElement_warning_withMissingFolderReference() throws {
|
||||||
|
// Given
|
||||||
|
let path = fileHandler.currentPath
|
||||||
|
let manifest = ProjectDescription.FileElement.folderReference(path: "Documentation")
|
||||||
|
|
||||||
|
// When
|
||||||
|
let model = TuistGenerator.FileElement.from(manifest: manifest,
|
||||||
|
path: path,
|
||||||
|
fileHandler: fileHandler,
|
||||||
|
printer: printer)
|
||||||
|
|
||||||
|
// Then
|
||||||
|
XCTAssertEqual(printer.printWarningArgs, [
|
||||||
|
"Documentation does not exist",
|
||||||
|
])
|
||||||
|
XCTAssertEqual(model, [])
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
func createManifestLoader(with projects: [AbsolutePath: ProjectDescription.Project]) -> GraphManifestLoading {
|
func createManifestLoader(with projects: [AbsolutePath: ProjectDescription.Project]) -> GraphManifestLoading {
|
||||||
|
|
|
@ -76,6 +76,10 @@ Scenario: The project is an iOS application that has resources (ios_app_with_fra
|
||||||
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'Examples/list.json'
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'Examples/list.json'
|
||||||
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'Assets.car'
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'Assets.car'
|
||||||
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'resource.txt'
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'resource.txt'
|
||||||
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'en.lproj/App.strings'
|
||||||
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'en.lproj/Greetings.strings'
|
||||||
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'fr.lproj/App.strings'
|
||||||
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'fr.lproj/Greetings.strings'
|
||||||
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'resource_without_extension'
|
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'resource_without_extension'
|
||||||
Then the product 'App.app' with destination 'Debug-iphoneos' does not contain resource 'do_not_include.dat'
|
Then the product 'App.app' with destination 'Debug-iphoneos' does not contain resource 'do_not_include.dat'
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ let project = Project(
|
||||||
"Resources/**/*.png",
|
"Resources/**/*.png",
|
||||||
"Resources/*.xcassets",
|
"Resources/*.xcassets",
|
||||||
"Resources/**/*.txt",
|
"Resources/**/*.txt",
|
||||||
|
"Resources/**/*.strings",
|
||||||
"Resources/resource_without_extension",
|
"Resources/resource_without_extension",
|
||||||
.folderReference(path: "Examples")
|
.folderReference(path: "Examples")
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
"morning" = "Good Morning";
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
"morning" = "Bonjour";
|
Loading…
Reference in New Issue