Files
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

1677 lines
82 KiB
Swift

import Foundation
import PathKit
import ProjectSpec
import Spectre
import XcodeProj
import XCTest
import Yams
import TestSupport
import Version
class SpecLoadingTests: XCTestCase {
func testSpecLoaderDuplicateImports() {
describe {
$0.it("merges each file only once") {
let path = fixturePath + "duplicated_include/duplicated_import_sut.yml"
let project = try loadSpec(path: path)
try expect(project.fileGroups) == ["First", "Second", "Third"]
let sutTarget = project.targets.first
try expect(sutTarget?.sources) == [TargetSource(path: "template")]
try expect(sutTarget?.preBuildScripts) == [BuildScript(script: .script("swiftlint"), name: "Swiftlint")]
}
}
}
func testSpecLoader() {
describe {
$0.it("merges includes") {
let path = fixturePath + "include_test.yml"
let project = try loadSpec(path: path, variables: [:])
try expect(project.name) == "NewName"
try expect(project.settingGroups) == [
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}"]),
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
$0.it("merges includes with additional") {
let path = fixturePath + "include_test.yml"
let project = try loadSpec(path: path, variables: ["INCLUDE_ADDITIONAL_YAML": "YES"])
try expect(project.name) == "NewName"
try expect(project.settingGroups) == [
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}", "MY_SETTING5": "ADDITIONAL"]),
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "SwiftPM"), Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
$0.it("merges includes without additional by environment variable") {
let path = fixturePath + "include_test.yml"
let project = try loadSpec(path: path, variables: ["INCLUDE_ADDITIONAL_YAML": "NO"])
try expect(project.name) == "NewName"
try expect(project.settingGroups) == [
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}"]),
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"], dependencies: [Dependency(type: .package(products: []), reference: "Yams")]),
Target(name: "NewTarget", type: .application, platform: .iOS, sources: ["template", "target"]),
]
}
$0.it("expands directories") {
let path = fixturePath + "paths_test.yml"
let project = try loadSpec(path: path)
try expect(project.configFiles) == [
"IncludedConfig": "paths_test/config",
"NewConfig": "config",
"RecursiveConfig": "paths_test/recursive_test/config",
]
try expect(project.options) == SpecOptions(
carthageBuildPath: "paths_test/recursive_test/carthage_build",
carthageExecutablePath: "carthage_executable"
)
try expect(project.projectReferences) == [
ProjectReference(name: "ProjX", path: "TestProject/Project.xcodeproj"),
]
try expect(project.aggregateTargets) == [
AggregateTarget(
name: "IncludedAggregateTarget",
targets: ["IncludedTarget"],
configFiles: ["Config": "paths_test/config"],
buildScripts: [BuildScript(script: .path("paths_test/buildScript"))]
),
AggregateTarget(
name: "NewAggregateTarget",
targets: ["NewTarget"],
configFiles: ["Config": "config"],
buildScripts: [BuildScript(script: .path("buildScript"))]
),
AggregateTarget(
name: "RecursiveAggregateTarget",
targets: ["RecursiveTarget"],
configFiles: ["Config": "paths_test/recursive_test/config"],
buildScripts: [BuildScript(script: .path("paths_test/recursive_test/buildScript"))]
),
]
try expect(project.targets) == [
Target(
name: "IncludedTarget",
type: .application,
platform: .tvOS,
configFiles: ["Config": "paths_test/config"],
sources: [
"paths_test/simplesource",
TargetSource(path: "paths_test/source", excludes: ["file"]),
],
dependencies: [Dependency(type: .framework, reference: "paths_test/Framework")],
info: Plist(path: "paths_test/info"),
entitlements: Plist(path: "paths_test/entitlements"),
preBuildScripts: [BuildScript(script: .path("paths_test/preBuildScript"))],
postCompileScripts: [BuildScript(script: .path("paths_test/postCompileScript"))],
postBuildScripts: [BuildScript(script: .path("paths_test/postBuildScript"))],
scheme: TargetScheme(testPlans: [.init(path: "paths_test/TestPlan.xctestplan")])
),
Target(
name: "NewTarget",
type: .application,
platform: .iOS,
configFiles: ["Config": "config"],
sources: [
"paths_test/template_source",
"source",
],
dependencies: [Dependency(type: .framework, reference: "Framework")],
info: Plist(path: "info"),
entitlements: Plist(path: "entitlements"),
preBuildScripts: [BuildScript(script: .path("preBuildScript"))],
postCompileScripts: [BuildScript(script: .path("postCompileScript"))],
postBuildScripts: [BuildScript(script: .path("postBuildScript"))]
),
Target(
name: "RecursiveTarget",
type: .application,
platform: .macOS,
configFiles: ["Config": "paths_test/recursive_test/config"],
sources: ["paths_test/recursive_test/source"],
dependencies: [Dependency(type: .framework, reference: "paths_test/recursive_test/Framework")],
info: Plist(path: "paths_test/recursive_test/info"),
entitlements: Plist(path: "paths_test/recursive_test/entitlements"),
preBuildScripts: [BuildScript(script: .path("paths_test/recursive_test/prebuildScript"))],
postCompileScripts: [BuildScript(script: .path("paths_test/recursive_test/postCompileScript"))],
postBuildScripts: [BuildScript(script: .path("paths_test/recursive_test/postBuildScript"))]
),
Target(
name: "app",
type: .application,
platform: .macOS,
sources: ["paths_test/same_relative_path_test/source"],
dependencies: [
Dependency(type: .target, reference: "target1"),
Dependency(type: .target, reference: "target2")
]
),
Target(
name: "target1",
type: .framework,
platform: .macOS,
sources: ["paths_test/same_relative_path_test/parent1/same/target1/source"]
),
Target(
name: "target2",
type: .framework,
platform: .macOS,
sources: ["paths_test/same_relative_path_test/parent2/same/target2/source"]
)
]
try expect(project.schemes) == [
Scheme(
name: "Scheme",
build: .init(targets: [.init(target: "NewTarget")]),
test: .init(testPlans: [.init(path: "paths_test/TestPlan.xctestplan")])
)
]
try expect(project.packages) == [
"LocalPackage": .local(path: "paths_test/relative_local_package/LocalPackage", group: nil, excludeFromProject: false),
]
try expect(project.fileGroups.contains("paths_test/relative_file_groups/TestFile.md")) == true
}
$0.it("respects directory expansion preference") {
let path = fixturePath + "legacy_paths_test.yml"
let project = try loadSpec(path: path)
try expect(project.configFiles) == [
"IncludedConfig": "config",
]
try expect(project.options) == SpecOptions(
carthageBuildPath: "carthage_build",
carthageExecutablePath: "carthage_executable"
)
try expect(project.aggregateTargets) == [
AggregateTarget(
name: "IncludedAggregateTarget",
targets: ["IncludedTarget"],
configFiles: ["Config": "config"],
buildScripts: [BuildScript(script: .path("buildScript"))]
),
]
try expect(project.targets) == [
Target(
name: "IncludedTarget",
type: .application,
platform: .tvOS,
configFiles: ["Config": "config"],
sources: ["source"],
dependencies: [Dependency(type: .framework, reference: "Framework")],
info: Plist(path: "info"),
entitlements: Plist(path: "entitlements"),
preBuildScripts: [BuildScript(script: .path("preBuildScript"))],
postCompileScripts: [BuildScript(script: .path("postCompileScript"))],
postBuildScripts: [BuildScript(script: .path("postBuildScript"))]
),
]
}
$0.it("parses yaml types") {
let path = fixturePath + "yaml.yml"
let dictionary = try loadYamlDictionary(path: path)
let expectedDictionary: [String: Any] = [
"true": true,
"false": false,
"yes": true,
"no": false,
"yesQuote": "YES",
"noQuote": "NO",
"int": 1,
"intQuote": "1",
"float": 3.2,
"floatQuote": "10.10",
"string": "hello",
"stringQuote": "hello",
"space": " ",
"empty": "",
"emptyQuote": "",
"emptyDictionary": [String: Any](),
"arrayLiteral": [1, 2],
"arrayList": [1, 2],
]
for (key, expectedValue) in expectedDictionary {
guard let parsedValue = dictionary[key] else {
throw failure("\(key) does not exist")
}
if String(describing: expectedValue) != String(describing: parsedValue) {
throw failure("\(key): \(parsedValue) does not equal \(expectedValue)")
}
}
if !(dictionary as NSDictionary).isEqual(expectedDictionary as NSDictionary) {
throw failure("parsed yaml types don't match:\n\nParsed:\n\t\(dictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))\nExpected:\n\t\(expectedDictionary.map { "\($0.key): \($0.value)" }.joined(separator: "\n\t"))")
}
}
$0.it("expands variables") {
let path = fixturePath + "variables_test.yml"
let project = try loadSpec(path: path, variables: [
"SETTING1": "ENV VALUE1",
"SETTING4": "ENV VALUE4",
"variable": "doesWin",
])
try expect(project.name) == "NewName"
try expect(project.settingGroups) == [
"test": Settings(dictionary: ["MY_SETTING1": "ENV VALUE1", "MY_SETTING2": "VALUE2", "MY_SETTING4": "ENV VALUE4"]),
"toReplace": Settings(dictionary: ["MY_SETTING1": "VALUE1"]),
]
try expect(project.targets.last?.sources) == ["SomeTarget", "doesWin", "templateVariable"]
}
}
}
func testSpecLoaderLoadingJSON() {
describe {
$0.it("merges includes") {
let path = fixturePath + "include_test.json"
let project = try loadSpec(path: path)
try expect(project.name) == "NewName"
try expect(project.settingGroups) == [
"test": Settings(dictionary: ["MY_SETTING1": "NEW VALUE", "MY_SETTING2": "VALUE2", "MY_SETTING3": "VALUE3", "MY_SETTING4": "${SETTING4}"]),
"new": Settings(dictionary: ["MY_SETTING": "VALUE"]),
"toReplace": Settings(dictionary: ["MY_SETTING2": "VALUE2"]),
]
try expect(project.targets) == [
Target(name: "IncludedTargetNew", type: .application, platform: .tvOS, sources: ["NewSource"]),
Target(name: "NewTarget", type: .application, platform: .iOS),
]
}
}
}
func testSpecWarningValidation() {
describe {
var path: Path!
$0.before {
path = Path(components: [NSTemporaryDirectory(), "\(NSUUID().uuidString).yaml"])
}
$0.after {
try? FileManager.default.removeItem(atPath: path.string)
}
$0.it("fails validating warnings for deprecated placeholder usage") {
let dictionary: [String: Any] = [
"name": "TestSpecWarningValidation",
"templates": [
"Framework": [
"type": "framework",
"sources": ["${target_name}/${platform}/Sources"],
],
],
"targets": [
"Framework": [
"type": "framework",
"platform": "iOS",
"templates": ["Framework"],
],
],
]
try? Yams.dump(object: dictionary).write(toFile: path.string, atomically: true, encoding: .utf8)
let specLoader = SpecLoader(version: "1.1.0")
do {
_ = try specLoader.loadProject(path: path)
} catch {
throw failure("\(error)")
}
}
$0.it("successfully validates warnings for new placeholder usage") {
let dictionary: [String: Any] = [
"name": "TestSpecWarningValidation",
"templates": [
"Framework": [
"type": "framework",
"sources": ["${target_name}/${platform}/Sources"],
],
],
"targets": [
"Framework": [
"type": "framework",
"platform": "iOS",
"templates": ["Framework"],
],
],
]
try? Yams.dump(object: dictionary).write(toFile: path.string, atomically: true, encoding: .utf8)
let specLoader = SpecLoader(version: "1.1.0")
do {
_ = try specLoader.loadProject(path: path)
} catch {
throw failure("\(error)")
}
do {
try specLoader.validateProjectDictionaryWarnings()
} catch {
throw failure("Expected to not throw a validation error. Got: \(error)")
}
}
}
}
func testProjectSpecParser() {
let validTarget: [String: Any] = ["type": "application", "platform": "iOS"]
let validBreakpoint: [String: Any] = ["type": "Exception", "scope": "All", "stopOnStyle": "Catch"]
let invalid = "invalid"
describe {
$0.it("fails with incorrect platform") {
var target = validTarget
target["platform"] = invalid
try expectTargetError(target, .unknownTargetPlatform(invalid))
}
$0.it("fails with incorrect product type") {
var target = validTarget
target["type"] = invalid
try expectTargetError(target, .unknownTargetType(invalid))
}
$0.it("fails with invalid dependency") {
var target = validTarget
target["dependencies"] = [[invalid: "name"]]
try expectTargetError(target, .invalidDependency([invalid: "name"]))
}
$0.it("fails with incorrect breakpoint type") {
var breakpoint = validBreakpoint
breakpoint["type"] = invalid
try expectBreakpointError(breakpoint, .unknownBreakpointType(invalid))
}
$0.it("fails with incorrect breakpoint scope") {
var target = validBreakpoint
target["scope"] = invalid
try expectBreakpointError(target, .unknownBreakpointScope(invalid))
}
$0.it("fails with incorrect breakpoint stop on style") {
var target = validBreakpoint
target["stopOnStyle"] = invalid
try expectBreakpointError(target, .unknownBreakpointStopOnStyle(invalid))
}
$0.it("fails with incorrect breakpoint action type") {
var breakpoint = validBreakpoint
breakpoint["actions"] = [["type": invalid]]
try expectBreakpointError(breakpoint, .unknownBreakpointActionType(invalid))
}
$0.it("fails with incorrect breakpoint action conveyance type") {
var breakpoint = validBreakpoint
breakpoint["actions"] = [["type": "Log", "conveyanceType": invalid]]
try expectBreakpointError(breakpoint, .unknownBreakpointActionConveyanceType(invalid))
}
$0.it("fails with incorrect breakpoint action sound name") {
var breakpoint = validBreakpoint
breakpoint["actions"] = [["type": "Sound", "sound": invalid]]
try expectBreakpointError(breakpoint, .unknownBreakpointActionSoundName(invalid))
}
$0.it("parses breakpoints") {
let breakpointDictionaries = [
["type": "File", "path": "Foo.swift", "line": 7, "column": 14, "condition": "bar == nil"],
["type": "Exception", "scope": "All", "stopOnStyle": "Catch"],
["type": "SwiftError", "enabled": false],
["type": "OpenGLError", "ignoreCount": 2],
["type": "Symbolic", "symbol": "UIViewAlertForUnsatisfiableConstraints", "module": "UIKitCore"],
["type": "IDEConstraintError", "continueAfterRunningActions": true],
["type": "IDETestFailure"],
]
let project = try getProjectSpec(["breakpoints": breakpointDictionaries])
let expectedBreakpoints = [
Breakpoint(type: .file(path: "Foo.swift", line: 7, column: 14), condition: "bar == nil"),
Breakpoint(type: .exception(.init(scope: .all, stopOnStyle: .catch))),
Breakpoint(type: .swiftError, enabled: false),
Breakpoint(type: .openGLError, ignoreCount: 2),
Breakpoint(type: .symbolic(symbol: "UIViewAlertForUnsatisfiableConstraints", module: "UIKitCore")),
Breakpoint(type: .ideConstraintError, continueAfterRunningActions: true),
Breakpoint(type: .ideTestFailure),
]
try expect(project.breakpoints) == expectedBreakpoints
}
$0.it("parses breakpoint actions") {
var breakpointDicationary = validBreakpoint
breakpointDicationary["actions"] = [
["type": "DebuggerCommand", "command": "po $arg1"],
["type": "Log", "message": "message", "conveyanceType": "speak"],
["type": "ShellCommand", "path": "script.sh", "arguments": "argument1, argument2", "waitUntilDone": true],
["type": "GraphicsTrace"],
["type": "AppleScript", "script": #"display alert "Hello!""#],
["type": "Sound", "sound": "Hero"],
]
let breakpoint = try Breakpoint(jsonDictionary: breakpointDicationary)
let expectedActions: [Breakpoint.Action] = [
.debuggerCommand("po $arg1"),
.log(.init(message: "message", conveyanceType: .speak)),
.shellCommand(path: "script.sh", arguments: "argument1, argument2", waitUntilDone: true),
.graphicsTrace,
.appleScript(#"display alert "Hello!""#),
.sound(.hero),
]
try expect(breakpoint.actions) == expectedActions
}
$0.it("parses sources") {
var targetDictionary1 = validTarget
targetDictionary1["sources"] = [
"sourceString",
["path": "sourceObject"],
["path": "sourceWithFlagsArray", "compilerFlags": ["-Werror"]],
["path": "sourceWithFlagsString", "compilerFlags": "-Werror -Wextra"],
["path": "sourceWithExcludes", "excludes": ["Foo.swift"]],
["path": "sourceWithFileType", "type": "file"],
["path": "sourceWithGroupType", "type": "group"],
["path": "sourceWithFolderType", "type": "folder"],
["path": "sourceWithResourceTags", "resourceTags": ["tag1", "tag2"]],
]
var targetDictionary2 = validTarget
targetDictionary2["sources"] = "source3"
let target1 = try Target(name: "test", jsonDictionary: targetDictionary1)
let target2 = try Target(name: "test", jsonDictionary: targetDictionary2)
let target1SourcesExpect = [
TargetSource(path: "sourceString"),
TargetSource(path: "sourceObject"),
TargetSource(path: "sourceWithFlagsArray", compilerFlags: ["-Werror"]),
TargetSource(path: "sourceWithFlagsString", compilerFlags: ["-Werror", "-Wextra"]),
TargetSource(path: "sourceWithExcludes", excludes: ["Foo.swift"]),
TargetSource(path: "sourceWithFileType", type: .file),
TargetSource(path: "sourceWithGroupType", type: .group),
TargetSource(path: "sourceWithFolderType", type: .folder),
TargetSource(path: "sourceWithResourceTags", resourceTags: ["tag1", "tag2"]),
]
try expect(target1.sources) == target1SourcesExpect
try expect(target2.sources) == ["source3"]
}
$0.it("parses target dependencies") {
var targetDictionary = validTarget
targetDictionary["dependencies"] = [
["target": "name", "embed": false, "platformFilter": "all"],
["target": "project/name", "embed": false, "platformFilter": "macOS"],
["carthage": "name", "findFrameworks": true, "platformFilter": "iOS"],
["carthage": "name", "findFrameworks": true, "linkType": "static"],
["framework": "path", "weak": true],
["sdk": "Contacts.framework"],
[
"sdk": "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework",
"root": "DEVELOPER_DIR",
],
["target": "conditionalMatch", "platforms": ["iOS"]],
["target": "conditionalMiss", "platforms": ["watchOS"]],
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
try expect(target.dependencies.count) == 8
try expect(target.dependencies[0]) == Dependency(type: .target, reference: "name", embed: false, platformFilter: .all)
try expect(target.dependencies[1]) == Dependency(type: .target, reference: "project/name", embed: false, platformFilter: .macOS)
try expect(target.dependencies[2]) == Dependency(type: .carthage(findFrameworks: true, linkType: .dynamic), reference: "name", platformFilter: .iOS)
try expect(target.dependencies[3]) == Dependency(type: .carthage(findFrameworks: true, linkType: .static), reference: "name")
try expect(target.dependencies[4]) == Dependency(type: .framework, reference: "path", weakLink: true)
try expect(target.dependencies[5]) == Dependency(type: .sdk(root: nil), reference: "Contacts.framework")
try expect(target.dependencies[6]) == Dependency(type: .sdk(root: "DEVELOPER_DIR"), reference: "Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework")
try expect(target.dependencies[7]) == Dependency(type: .target, reference: "conditionalMatch", platforms: [.iOS])
}
$0.it("parses info plist") {
var targetDictionary = validTarget
targetDictionary["info"] = [
"path": "Info.plist",
"properties": [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"],
],
]
let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.info) == Plist(path: "Info.plist", attributes: [
"CFBundleName": "MyAppName",
"UIBackgroundModes": ["fetch"],
])
}
$0.it("parses entitlement plist") {
var targetDictionary = validTarget
targetDictionary["entitlements"] = [
"path": "app.entitlements",
"properties": [
"com.apple.security.application-groups": "com.group",
],
]
let target = try Target(name: "", jsonDictionary: targetDictionary)
try expect(target.entitlements) == Plist(path: "app.entitlements", attributes: [
"com.apple.security.application-groups": "com.group",
])
}
$0.it("parses cross platform targets") {
let targetDictionary: [String: Any] = [
"platform": ["iOS", "tvOS"],
"deploymentTarget": ["iOS": 9.0, "tvOS": "10.0"],
"type": "framework",
"sources": ["Framework", "Framework ${platform}"],
"settings": ["SETTING": "value_${platform}"],
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
var target_iOS = Target(name: "Framework_iOS", type: .framework, platform: .iOS)
var target_tvOS = Target(name: "Framework_tvOS", type: .framework, platform: .tvOS)
target_iOS.sources = ["Framework", "Framework iOS"]
target_tvOS.sources = ["Framework", "Framework tvOS"]
target_iOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_iOS"]
target_tvOS.settings = ["PRODUCT_NAME": "Framework", "SETTING": "value_tvOS"]
target_iOS.deploymentTarget = Version(major: 9, minor: 0, patch: 0)
target_tvOS.deploymentTarget = Version(major: 10, minor: 0, patch: 0)
try expect(project.targets) == [target_iOS, target_tvOS]
}
$0.it("parses no platform fallbacks to auto if we are using supported destinations") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["iOS", "tvOS"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
}
$0.it("parses no platform fails if we are not using supported destinations") {
let expectedError = SpecParsingError.unknownTargetPlatform("")
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application"
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses supported destinations with macCatalyst but not iOS, we add iOS") {
let targetDictionary: [String: Any] = [
"type": "framework",
"supportedDestinations": ["macCatalyst"]
]
let project = try getProjectSpec(["targets": ["Framework": targetDictionary]])
let target = Target(name: "Framework", type: .framework, platform: .auto)
try expect(project.targets) == [target]
try expect(project.targets.first?.supportedDestinations) == [.macCatalyst, .iOS]
}
$0.it("invalid target platform because platform is an array and supported destinations is in use") {
let expectedError = SpecParsingError.invalidTargetPlatformAsArray
let projectDictionary: [String: Any] = [
"name": "test",
"targets": ["target1": [
"type": "application",
"platform": ["iOS", "tvOS"],
"supportedDestinations": ["iOS", "tvOS"]
] as [String : Any]]
]
try expectError(expectedError) {
_ = try Project(jsonDictionary: projectDictionary)
}
}
$0.it("parses target templates") {
let targetDictionary: [String: Any] = [
"deploymentTarget": "1.2.0",
"sources": ["targetSource"],
"templates": ["temp2", "temp"],
"templateAttributes": [
"source": "replacedSource",
],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"platform": "iOS",
"sources": [
"templateSource",
["path": "Sources/${target_name}"],
],
],
"temp2": [
"type": "framework",
"platform": "tvOS",
"deploymentTarget": "1.1.0",
"configFiles": [
"debug": "Configs/${target_name}/debug.xcconfig",
"release": "Configs/${target_name}/release.xcconfig",
],
"sources": ["${source}"],
],
],
])
let target = project.targets.first!
try expect(target.type) == .framework // uses value
try expect(target.platform) == .iOS // uses latest value
try expect(target.deploymentTarget) == Version("1.2.0") // keeps value
try expect(target.sources) == ["replacedSource", "templateSource", "Sources/Framework", "targetSource"] // merges array in order and replace ${target_name}
try expect(target.configFiles["debug"]) == "Configs/Framework/debug.xcconfig" // replaces $target_name
try expect(target.configFiles["release"]) == "Configs/Framework/release.xcconfig" // replaces ${target_name}
}
$0.it("parses nested target templates") {
let targetDictionary: [String: Any] = [
"deploymentTarget": "1.2.0",
"sources": ["targetSource"],
"templates": ["temp2"],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"type": "framework",
"platform": "iOS",
"sources": ["nestedTemplateSource1"],
],
"temp1": [
"type": "application",
"sources": ["nestedTemplateSource2"],
],
"temp2": [
"platform": "tvOS",
"deploymentTarget": "1.1.0",
"configFiles": ["debug": "Configs/${target_name}/debug.xcconfig"],
"templates": ["temp", "temp1"],
"sources": ["templateSource"],
],
],
])
let target = project.targets.first!
try expect(target.type) == .application // uses value of last nested template
try expect(target.platform) == .tvOS // uses latest value
try expect(target.deploymentTarget) == Version("1.2.0") // keeps value
try expect(target.sources) == ["nestedTemplateSource1", "nestedTemplateSource2", "templateSource", "targetSource"] // merges array in order
try expect(target.configFiles["debug"]) == "Configs/Framework/debug.xcconfig" // replaces $target_name
}
$0.it("parses complex nested target templates") {
let targetDictionary: [String: Any] = [
"type": "framework",
"platform": "iOS",
"templates": ["temp"],
"sources": ["target"],
"templateAttributes": [
"temp": "temp-by-target",
"a": "a-by-target",
"b": "b-by-target", // This should win over attributes defined in template "temp"
],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"templates": ["a", "d"],
"sources": ["temp", "${temp}"],
"templateAttributes": [
"b": "b-by-temp",
"c": "c-by-temp",
"d": "d-by-temp",
],
],
"a": [
"templates": ["b", "c"],
"sources": ["a", "${a}"],
"templateAttributes": [
"c": "c-by-a",
],
],
"b": [
"sources": ["b", "${b}"],
],
"c": [
"sources": ["c", "${c}"],
],
"d": [
"sources": ["d", "${d}"],
"templates": ["e"],
"templateAttributes": [
"e": "e-by-d",
],
],
"e": [
"sources": ["e", "${e}"],
],
],
])
let target = project.targets.first!
try expect(target.type) == .framework // uses value of last nested template
try expect(target.platform) == .iOS // uses latest value
try expect(target.sources) == ["b", "b-by-target",
"c", "c-by-temp",
"a", "a-by-target",
"e", "e-by-d",
"d", "d-by-temp",
"temp", "temp-by-target",
"target"] // merges array in order
}
$0.it("parses nested target templates with cycle") {
let targetDictionary: [String: Any] = [
"deploymentTarget": "1.2.0",
"sources": ["targetSource"],
"templates": ["temp2"],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"type": "framework",
"platform": "iOS",
"templates": ["temp1"],
"sources": ["nestedTemplateSource1"],
],
"temp1": [
"platform": "macOS",
"templates": ["temp2"],
"sources": ["nestedTemplateSource2"],
],
"temp2": [
"platform": "tvOS",
"deploymentTarget": "1.1.0",
"configFiles": ["debug": "Configs/${target_name}/debug.xcconfig"],
"templates": ["temp", "temp1"],
"sources": ["templateSource"],
],
],
])
let target = project.targets.first!
try expect(target.type) == .framework // uses value
try expect(target.platform) == .tvOS // uses latest value
try expect(target.deploymentTarget) == Version("1.2.0") // keeps value
try expect(target.sources) == ["nestedTemplateSource2", "nestedTemplateSource1", "templateSource", "targetSource"] // merges array in order
try expect(target.configFiles["debug"]) == "Configs/Framework/debug.xcconfig" // replaces $target_name
}
$0.it("parses cross platform target templates") {
let project = try getProjectSpec([
"targets": [
"Framework": [
"type": "framework",
"templates": ["temp"],
],
],
"targetTemplates": [
"temp": [
"platform": ["iOS", "tvOS"],
],
],
])
let iOSTarget = project.targets.first { $0.platform == .iOS }
let tvOSTarget = project.targets.first { $0.platform == .tvOS }
try expect(iOSTarget?.type) == .framework
try expect(tvOSTarget?.type) == .framework
}
$0.it("parses platform specific templates") {
let project = try getProjectSpec([
"targets": [
"Framework": [
"type": "framework",
"platform": ["iOS", "tvOS"],
"templates": ["${platform}"],
],
],
"targetTemplates": [
"iOS": [
"sources": "A",
],
"tvOS": [
"sources": "B",
],
],
])
let iOSTarget = project.targets.first { $0.platform == .iOS }
let tvOSTarget = project.targets.first { $0.platform == .tvOS }
try expect(iOSTarget?.sources) == ["A"]
try expect(tvOSTarget?.sources) == ["B"]
}
$0.it("parses aggregate targets") {
let dictionary: [String: Any] = [
"targets": ["target_1", "target_2"],
"settings": ["SETTING": "VALUE"],
"configFiles": ["debug": "file.xcconfig"],
]
let project = try getProjectSpec(["aggregateTargets": ["AggregateTarget": dictionary]])
let expectedTarget = AggregateTarget(name: "AggregateTarget", targets: ["target_1", "target_2"], settings: ["SETTING": "VALUE"], configFiles: ["debug": "file.xcconfig"])
try expect(project.aggregateTargets) == [expectedTarget]
}
$0.it("parses target schemes") {
var targetDictionary = validTarget
targetDictionary["scheme"] = [
"testTargets": ["t1", ["name": "t2"]],
"configVariants": ["dev", "app-store"],
"commandLineArguments": [
"ENV1": true,
],
"gatherCoverageData": true,
"coverageTargets": ["t1"],
"storeKitConfiguration": "Configuration.storekit",
"language": "en",
"region": "US",
"disableMainThreadChecker": true,
"stopOnEveryMainThreadCheckerIssue": true,
"disableThreadPerformanceChecker": true,
"environmentVariables": [
"TEST_VAR": "TEST_VAL",
],
"preActions": [
[
"script": "dothing",
"name": "Do Thing",
"settingsTarget": "test",
],
],
"postActions": [
[
"script": "hello",
],
],
"management": [
"shared": false,
"isShown": true,
"orderHint": 10
],
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
let scheme = TargetScheme(
testTargets: ["t1", "t2"],
configVariants: ["dev", "app-store"],
gatherCoverageData: true,
coverageTargets: ["t1"],
storeKitConfiguration: "Configuration.storekit",
language: "en",
region: "US",
disableMainThreadChecker: true,
stopOnEveryMainThreadCheckerIssue: true,
disableThreadPerformanceChecker: true,
commandLineArguments: ["ENV1": true],
environmentVariables: [XCScheme.EnvironmentVariable(variable: "TEST_VAR", value: "TEST_VAL", enabled: true)],
preActions: [.init(name: "Do Thing", script: "dothing", settingsTarget: "test")],
postActions: [.init(name: "Run Script", script: "hello")],
management: Scheme.Management(shared: false, orderHint: 10, isShown: true)
)
try expect(target.scheme) == scheme
}
$0.it("parses schemes") {
let schemeDictionary: [String: Any] = [
"build": [
"parallelizeBuild": false,
"buildImplicitDependencies": false,
"runPostActionsOnFailure": true,
"targets": [
"Target1": "all",
"Target2": "testing",
"Target3": "none",
"Target4": ["testing": true],
"Target5": ["testing": false],
"Target6": ["test", "analyze"],
"ExternalProject/Target7": ["run"],
],
"preActions": [
[
"script": "echo Before Build",
"name": "Before Build",
"settingsTarget": "Target1",
],
],
],
"run": [
"config": "debug",
"launchAutomaticallySubstyle": 2,
"enableGPUFrameCaptureMode": "disabled",
"storeKitConfiguration": "Configuration.storekit",
"disableThreadPerformanceChecker": true,
],
"test": [
"config": "debug",
"targets": [
"Target1",
[
"name": "ExternalProject/Target2",
"parallelizable": true,
"skipped": true,
"location": "test.gpx",
"randomExecutionOrder": true,
"skippedTests": ["Test/testExample()"],
],
],
"gatherCoverageData": true,
"disableMainThreadChecker": true,
"stopOnEveryMainThreadCheckerIssue": true,
"testPlans": [
[
"path": "Path/Plan.xctestplan"
],
[
"path": "Path/Plan2.xctestplan"
]
],
"preferredScreenCaptureFormat": "screenshots",
],
"management": [
"isShown": false,
"orderHint": 4
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedTargets: [Scheme.BuildTarget] = [
Scheme.BuildTarget(target: "Target1", buildTypes: BuildType.all),
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "Target3", buildTypes: []),
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
Scheme.BuildTarget(target: "Target5", buildTypes: []),
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "ExternalProject/Target7", buildTypes: [.running]),
]
try expect(scheme.name) == "Scheme"
try expect(scheme.build.targets) == expectedTargets
try expect(scheme.build.preActions.first?.script) == "echo Before Build"
try expect(scheme.build.preActions.first?.name) == "Before Build"
try expect(scheme.build.preActions.first?.settingsTarget) == "Target1"
try expect(scheme.build.parallelizeBuild) == false
try expect(scheme.build.buildImplicitDependencies) == false
try expect(scheme.build.runPostActionsOnFailure) == true
let expectedRun = Scheme.Run(
config: "debug",
enableGPUFrameCaptureMode: .disabled,
disableThreadPerformanceChecker: true,
launchAutomaticallySubstyle: "2",
storeKitConfiguration: "Configuration.storekit"
)
try expect(scheme.run) == expectedRun
let expectedTest = Scheme.Test(
config: "debug",
gatherCoverageData: true,
disableMainThreadChecker: true,
targets: [
"Target1",
Scheme.Test.TestTarget(
targetReference: "ExternalProject/Target2",
randomExecutionOrder: true,
parallelizable: true,
location: "test.gpx",
skipped: true,
skippedTests: ["Test/testExample()"]
),
],
testPlans: [
.init(path: "Path/Plan.xctestplan"),
.init(path: "Path/Plan2.xctestplan")
],
preferredScreenCaptureFormat: .screenshots
)
try expect(scheme.test) == expectedTest
let expectedManagement = Scheme.Management(shared: true, orderHint: 4, isShown: false)
try expect(scheme.management) == expectedManagement
}
$0.it("parses alternate test schemes") {
let schemeDictionary: [String: Any] = [
"build": [
"targets": ["Target1": "all"],
],
"test": [
"config": "debug",
"targets": [
"Target1",
[
"name": "ExternalProject/Target2",
"parallelizable": true,
"location": "New York, NY, USA",
"randomExecutionOrder": true,
"selectedTests": ["Test/testExample()"],
],
],
"gatherCoverageData": true,
"disableMainThreadChecker": true,
"stopOnEveryMainThreadCheckerIssue": true,
],
"management": [
"isShown": false
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedTest = Scheme.Test(
config: "debug",
gatherCoverageData: true,
disableMainThreadChecker: true,
targets: [
"Target1",
Scheme.Test.TestTarget(
targetReference: "ExternalProject/Target2",
randomExecutionOrder: true,
parallelizable: true,
location: "New York, NY, USA",
selectedTests: ["Test/testExample()"]
),
]
)
try expect(scheme.test) == expectedTest
let expectedManagement = Scheme.Management(shared: true, orderHint: nil, isShown: false)
try expect(scheme.management) == expectedManagement
}
$0.it("parses schemes variables") {
let schemeDictionary: [String: Any] = [
"build": [
"targets": ["Target1": "all"],
],
"run": [
"environmentVariables": [
["variable": "BOOL_TRUE", "value": true],
["variable": "BOOL_YES", "value": "YES"],
["variable": "ENVIRONMENT", "value": "VARIABLE"],
["variable": "OTHER_ENV_VAR", "value": "VAL", "isEnabled": false],
],
"launchAutomaticallySubstyle": "2",
"storeKitConfiguration": "Configuration.storekit",
],
"test": [
"environmentVariables": [
"BOOL_TRUE": true,
"BOOL_YES": "YES",
"TEST": "VARIABLE",
],
],
"profile": [
"config": "Release",
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let expectedRunVariables = [
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
XCScheme.EnvironmentVariable(variable: "ENVIRONMENT", value: "VARIABLE", enabled: true),
XCScheme.EnvironmentVariable(variable: "OTHER_ENV_VAR", value: "VAL", enabled: false),
]
let expectedTestVariables = [
XCScheme.EnvironmentVariable(variable: "BOOL_TRUE", value: "YES", enabled: true),
XCScheme.EnvironmentVariable(variable: "BOOL_YES", value: "YES", enabled: true),
XCScheme.EnvironmentVariable(variable: "TEST", value: "VARIABLE", enabled: true),
]
try expect(scheme.run?.environmentVariables) == expectedRunVariables
try expect(scheme.run?.launchAutomaticallySubstyle) == "2"
try expect(scheme.run?.storeKitConfiguration) == "Configuration.storekit"
try expect(scheme.test?.environmentVariables) == expectedTestVariables
try expect(scheme.profile?.config) == "Release"
try expect(scheme.profile?.environmentVariables.isEmpty) == true
}
$0.it("parses alternate schemes variables") {
let schemeDictionary: [String: Any] = [
"build": [
"targets": ["Target1": "all"],
],
"run": [
"launchAutomaticallySubstyle": 2, // Both integer and string supported
"storeKitConfiguration": "Configuration.storekit",
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
try expect(scheme.run?.launchAutomaticallySubstyle) == "2"
try expect(scheme.run?.storeKitConfiguration) == "Configuration.storekit"
}
$0.it("parses scheme templates") {
let targetDictionary: [String: Any] = [
"deploymentTarget": "1.2.0",
"sources": ["targetSource"],
"templates": ["temp2", "temp"],
"templateAttributes": [
"source": "replacedSource",
],
]
let project = try getProjectSpec([
"targets": ["Framework": targetDictionary],
"targetTemplates": [
"temp": [
"platform": "iOS",
"sources": [
"templateSource",
["path": "Sources/${target_name}"],
],
],
"temp2": [
"type": "framework",
"platform": "tvOS",
"deploymentTarget": "1.1.0",
"configFiles": [
"debug": "Configs/${target_name}/debug.xcconfig",
"release": "Configs/${target_name}/release.xcconfig",
],
"sources": ["${source}"],
],
],
"schemeTemplates": [
"base_scheme": [
"build": [
"parallelizeBuild": false,
"buildImplicitDependencies": false,
"runPostActionsOnFailure": true,
"targets": [
"Target${name_1}": "all",
"Target2": "testing",
"Target${name_3}": "none",
"Target4": ["testing": true],
"Target5": ["testing": false],
"Target6": ["test", "analyze"],
],
"preActions": [
[
"script": "${pre-action-name}",
"name": "Before Build ${scheme_name}",
"settingsTarget": "Target${name_1}",
],
],
],
"run": [
"storeKitConfiguration": "Configuration.storekit",
],
"test": [
"config": "debug",
"targets": [
"Target${name_1}",
[
"name": "Target2",
"parallelizable": true,
"randomExecutionOrder": true,
"skippedTests": ["Test/testExample()"],
],
],
"gatherCoverageData": true,
"disableMainThreadChecker": true,
"stopOnEveryMainThreadCheckerIssue": false,
],
"management": [
"shared": false,
"orderHint": 8
],
],
],
"schemes": [
"temp2": [
"templates": ["base_scheme"],
"templateAttributes": [
"pre-action-name": "modified-name",
"name_1": "FirstTarget",
"name_3": "ThirdTarget",
],
],
],
])
let scheme = project.schemes.first!
let expectedTargets: [Scheme.BuildTarget] = [
Scheme.BuildTarget(target: "TargetFirstTarget", buildTypes: BuildType.all),
Scheme.BuildTarget(target: "Target2", buildTypes: [.testing, .analyzing]),
Scheme.BuildTarget(target: "TargetThirdTarget", buildTypes: []),
Scheme.BuildTarget(target: "Target4", buildTypes: [.testing]),
Scheme.BuildTarget(target: "Target5", buildTypes: []),
Scheme.BuildTarget(target: "Target6", buildTypes: [.testing, .analyzing]),
]
try expect(scheme.name) == "temp2"
try expect(Set(scheme.build.targets)) == Set(expectedTargets)
try expect(scheme.build.preActions.first?.script) == "modified-name"
try expect(scheme.build.preActions.first?.name) == "Before Build temp2"
try expect(scheme.build.preActions.first?.settingsTarget) == "TargetFirstTarget"
try expect(scheme.build.parallelizeBuild) == false
try expect(scheme.build.buildImplicitDependencies) == false
try expect(scheme.build.runPostActionsOnFailure) == true
try expect(scheme.run?.storeKitConfiguration) == "Configuration.storekit"
let expectedTest = Scheme.Test(
config: "debug",
gatherCoverageData: true,
disableMainThreadChecker: true,
targets: [
"TargetFirstTarget",
Scheme.Test.TestTarget(
targetReference: "Target2",
randomExecutionOrder: true,
parallelizable: true,
skippedTests: ["Test/testExample()"]
),
]
)
try expect(scheme.test) == expectedTest
let expectedManagement = Scheme.Management(shared: false, orderHint: 8, isShown: nil)
try expect(scheme.management) == expectedManagement
}
$0.it("parses copy files on install") {
var targetSource = validTarget
targetSource["onlyCopyFilesOnInstall"] = true
let target = try Target(name: "Embed Frameworks", jsonDictionary: targetSource)
try expect(target.onlyCopyFilesOnInstall) == true
}
$0.it("parses put resources before Sources Build Phase") {
var targetSource = validTarget
targetSource["putResourcesBeforeSourcesBuildPhase"] = true
let target = try Target(name: "Embed Frameworks", jsonDictionary: targetSource)
try expect(target.putResourcesBeforeSourcesBuildPhase) == true
}
$0.it("parses settings") {
let project = try Project(path: fixturePath + "settings_test.yml")
let buildSettings: BuildSettings = ["SETTING": "value"]
let configSettings: [String: Settings] = ["config1": Settings(buildSettings: ["SETTING1": "value"])]
let groups = ["preset1"]
let preset1 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
let preset2 = Settings(buildSettings: [:], configSettings: configSettings, groups: [])
let preset3 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: [])
let preset4 = Settings(buildSettings: buildSettings, configSettings: [:], groups: [])
let preset5 = Settings(buildSettings: buildSettings, configSettings: [:], groups: groups)
let preset6 = Settings(buildSettings: buildSettings, configSettings: configSettings, groups: groups)
let preset7 = Settings(buildSettings: buildSettings, configSettings: ["config1": Settings(buildSettings: buildSettings, groups: groups)])
let preset8 = Settings(buildSettings: [:], configSettings: ["config1": Settings(configSettings: configSettings)])
try expect(project.settingGroups.count) == 8
try expect(project.settingGroups["preset1"]) == preset1
try expect(project.settingGroups["preset2"]) == preset2
try expect(project.settingGroups["preset3"]) == preset3
try expect(project.settingGroups["preset4"]) == preset4
try expect(project.settingGroups["preset5"]) == preset5
try expect(project.settingGroups["preset6"]) == preset6
try expect(project.settingGroups["preset7"]) == preset7
try expect(project.settingGroups["preset8"]) == preset8
}
$0.it("parses run scripts") {
var target = validTarget
let scripts: [[String: Any]] = [
["path": "script.sh"],
["script": "shell script\ndo thing", "name": "myscript", "inputFiles": ["file", "file2"], "outputFiles": ["file", "file2"], "shell": "bin/customshell", "runOnlyWhenInstalling": true],
["script": "shell script\ndo thing", "name": "myscript", "inputFiles": ["file", "file2"], "outputFiles": ["file", "file2"], "shell": "bin/customshell", "showEnvVars": false],
["script": "shell script\ndo thing", "name": "myscript", "inputFiles": ["file", "file2"], "outputFiles": ["file", "file2"], "shell": "bin/customshell", "basedOnDependencyAnalysis": false],
["script": "shell script\nwith file lists", "name": "myscript", "inputFileLists": ["inputList.xcfilelist"], "outputFileLists": ["outputList.xcfilelist"], "shell": "bin/customshell", "runOnlyWhenInstalling": true],
["script": "shell script\nwith file lists", "name": "myscript", "inputFileLists": ["inputList.xcfilelist"], "outputFileLists": ["outputList.xcfilelist"], "shell": "bin/customshell", "showEnvVars": false],
["script": "shell script\nwith file lists", "name": "myscript", "inputFileLists": ["inputList.xcfilelist"], "outputFileLists": ["outputList.xcfilelist"], "shell": "bin/customshell", "basedOnDependencyAnalysis": false],
]
target["preBuildScripts"] = scripts
target["postCompileScripts"] = scripts
target["postBuildScripts"] = scripts
let expectedScripts = [
BuildScript(script: .path("script.sh")),
BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: true, showEnvVars: true, basedOnDependencyAnalysis: true),
BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: false, showEnvVars: false, basedOnDependencyAnalysis: true),
BuildScript(script: .script("shell script\ndo thing"), name: "myscript", inputFiles: ["file", "file2"], outputFiles: ["file", "file2"], shell: "bin/customshell", runOnlyWhenInstalling: false, showEnvVars: true, basedOnDependencyAnalysis: false),
BuildScript(script: .script("shell script\nwith file lists"), name: "myscript", inputFileLists: ["inputList.xcfilelist"], outputFileLists: ["outputList.xcfilelist"], shell: "bin/customshell", runOnlyWhenInstalling: true, showEnvVars: true, basedOnDependencyAnalysis: true),
BuildScript(script: .script("shell script\nwith file lists"), name: "myscript", inputFileLists: ["inputList.xcfilelist"], outputFileLists: ["outputList.xcfilelist"], shell: "bin/customshell", runOnlyWhenInstalling: false, showEnvVars: false, basedOnDependencyAnalysis: true),
BuildScript(script: .script("shell script\nwith file lists"), name: "myscript", inputFileLists: ["inputList.xcfilelist"], outputFileLists: ["outputList.xcfilelist"], shell: "bin/customshell", runOnlyWhenInstalling: false, showEnvVars: true, basedOnDependencyAnalysis: false),
]
let parsedTarget = try Target(name: "test", jsonDictionary: target)
try expect(parsedTarget.preBuildScripts) == expectedScripts
try expect(parsedTarget.postCompileScripts) == expectedScripts
try expect(parsedTarget.postBuildScripts) == expectedScripts
}
$0.it("parses build rules") {
var target = validTarget
let buildRules: [[String: Any]] = [
[
"name": "My Rule",
"script": "my script",
"filePattern": "*.swift",
"outputFiles": ["file1", "file2"],
"outputFilesCompilerFlags": ["-a", "-b"],
],
[
"compilerSpec": "apple.tool",
"fileType": "sourcecode.swift",
],
]
target["buildRules"] = buildRules
let expectedBuildRules = [
BuildRule(fileType: .pattern("*.swift"), action: .script("my script"), name: "My Rule", outputFiles: ["file1", "file2"], outputFilesCompilerFlags: ["-a", "-b"]),
BuildRule(fileType: .type("sourcecode.swift"), action: .compilerSpec("apple.tool")),
]
let parsedTarget = try Target(name: "test", jsonDictionary: target)
try expect(parsedTarget.buildRules) == expectedBuildRules
}
$0.it("parses options") {
let options = SpecOptions(
carthageBuildPath: "../Carthage/Build",
carthageExecutablePath: "../bin/carthage",
createIntermediateGroups: true,
bundleIdPrefix: "com.test",
developmentLanguage: "ja",
deploymentTarget: DeploymentTarget(
iOS: "11.1",
tvOS: "10.0",
watchOS: "3.0",
macOS: "10.12.1"
),
fileTypes: ["abc": FileType(
file: false,
buildPhase: .sources,
attributes: ["a1", "a2"],
resourceTags: ["r1", "r2"],
compilerFlags: ["c1", "c2"])],
findCarthageFrameworks: true,
preGenCommand: "swiftgen",
postGenCommand: "pod install",
schemePathPrefix: "../"
)
let expected = Project(name: "test", options: options)
let dictionary: [String: Any] = ["options": [
"carthageBuildPath": "../Carthage/Build",
"carthageExecutablePath": "../bin/carthage",
"bundleIdPrefix": "com.test",
"createIntermediateGroups": true,
"developmentLanguage": "ja",
"deploymentTarget": ["iOS": 11.1, "tvOS": 10.0, "watchOS": "3", "macOS": "10.12.1"],
"findCarthageFrameworks": true,
"preGenCommand": "swiftgen",
"postGenCommand": "pod install",
"fileTypes": ["abc": [
"file": false,
"buildPhase": "sources",
"attributes": ["a1", "a2"],
"resourceTags": ["r1", "r2"],
"compilerFlags": ["c1", "c2"],
]],
"schemePathPrefix": "../",
]]
let parsedSpec = try getProjectSpec(dictionary)
try expect(parsedSpec) == expected
}
$0.it("parses packages") {
let project = Project(name: "spm", packages: [
"package1": .remote(url: "package.git", versionRequirement: .exact("1.2.2")),
"package2": .remote(url: "package.git", versionRequirement: .upToNextMajorVersion("1.2.2")),
"package3": .remote(url: "package.git", versionRequirement: .upToNextMinorVersion("1.2.2")),
"package4": .remote(url: "package.git", versionRequirement: .branch("master")),
"package5": .remote(url: "package.git", versionRequirement: .revision("x")),
"package6": .remote(url: "package.git", versionRequirement: .range(from: "1.2.0", to: "1.2.5")),
"package7": .remote(url: "package.git", versionRequirement: .exact("1.2.2")),
"package8": .remote(url: "package.git", versionRequirement: .upToNextMajorVersion("4.0.0-beta.5")),
"package9": .local(path: "package/package", group: nil, excludeFromProject: false),
"package10": .remote(url: "https://github.com/yonaskolb/XcodeGen", versionRequirement: .exact("1.2.2")),
"XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false),
"package11": .local(path: "../XcodeGen", group: "Packages/Feature", excludeFromProject: false),
], options: .init(localPackagesGroup: "MyPackages"))
let dictionary: [String: Any] = [
"name": "spm",
"options": [
"localPackagesGroup": "MyPackages",
],
"packages": [
"package1": ["url": "package.git", "exactVersion": "1.2.2"],
"package2": ["url": "package.git", "majorVersion": "1.2.2"],
"package3": ["url": "package.git", "minorVersion": "1.2.2"],
"package4": ["url": "package.git", "branch": "master"],
"package5": ["url": "package.git", "revision": "x"],
"package6": ["url": "package.git", "minVersion": "1.2.0", "maxVersion": "1.2.5"],
"package7": ["url": "package.git", "version": "1.2.2"],
"package8": ["url": "package.git", "majorVersion": "4.0.0-beta.5"],
"package9": ["path": "package/package"],
"package10": ["github": "yonaskolb/XcodeGen", "exactVersion": "1.2.2"],
"package11": ["path": "../XcodeGen", "group": "Packages/Feature"],
],
"localPackages": ["../XcodeGen"],
]
let parsedSpec = try getProjectSpec(dictionary)
try expect(parsedSpec) == project
}
$0.it("parses old local package format") {
let project = Project(name: "spm", packages: [
"XcodeGen": .local(path: "../XcodeGen", group: nil, excludeFromProject: false),
"Yams": .local(path: "Yams", group: nil, excludeFromProject: false),
], options: .init(localPackagesGroup: "MyPackages"))
let dictionary: [String: Any] = [
"name": "spm",
"options": [
"localPackagesGroup": "MyPackages",
],
"localPackages": ["../XcodeGen", "Yams"],
]
let parsedSpec = try getProjectSpec(dictionary)
try expect(parsedSpec) == project
}
$0.it("parses TargetScheme storeKitConfiguration as string") {
var targetDictionary = validTarget
targetDictionary["scheme"] = [
"storeKitConfiguration": "Configuration.storekit",
]
let target = try Target(name: "test", jsonDictionary: targetDictionary)
let scheme = TargetScheme(
storeKitConfiguration: "Configuration.storekit"
)
try expect(target.scheme) == scheme
}
$0.it("parses Scheme.Run storeKitConfiguration as string") {
let schemeDictionary: [String: Any] = [
"build": [
"targets": [:],
],
"run": [
"config": "debug",
"storeKitConfiguration": "Configuration.storekit",
],
]
let scheme = try Scheme(name: "Scheme", jsonDictionary: schemeDictionary)
let runAction = Scheme.Run(
config: "debug",
storeKitConfiguration: "Configuration.storekit"
)
try expect(scheme.run) == runAction
}
$0.it("parses buildToolPlugins") {
var target = validTarget
let buildToolPlugins: [[String: Any]] = [
[
"plugin": "FirstPlugin",
"package": "FirstPackage"
],
[
"plugin": "SecondPlugin",
"package": "SecondPackage"
]
]
target["buildToolPlugins"] = buildToolPlugins
let expectedBuildToolPlugins = [
BuildToolPlugin(plugin: "FirstPlugin", package: "FirstPackage"),
BuildToolPlugin(plugin: "SecondPlugin", package: "SecondPackage")
]
let parsedTarget = try Target(name: "test", jsonDictionary: target)
try expect(parsedTarget.buildToolPlugins) == expectedBuildToolPlugins
}
}
}
func testPackagesVersion() {
describe {
let invalidPackages = [
["url": "package.git", "majorVersion": "master"],
["url": "package.git", "from": "develop"],
["url": "package.git", "minVersion": "feature/swift5.2", "maxVersion": "9.1.0"],
["url": "package.git", "minorVersion": "x.1.2"],
["url": "package.git", "exactVersion": "1.2.3.1"],
["url": "package.git", "version": "foo-bar"],
]
$0.it("is an invalid package version") {
for dictionary in invalidPackages {
try expect(expression: { _ = try SwiftPackage(jsonDictionary: dictionary) }).toThrow()
}
}
}
}
func testDecoding() throws {
describe {
$0.it("decodes dots in dictionary keys") {
let dictionary: [String: Any] = [
"test": [
"one.two": true,
],
]
let booleans: [String: Bool] = try dictionary.json(atKeyPath: "test")
try expect(booleans) == ["one.two": true]
}
}
}
}
@discardableResult
private func getProjectSpec(_ project: [String: Any], file: String = #file, line: Int = #line) throws -> Project {
var projectDictionary: [String: Any] = ["name": "test"]
for (key, value) in project {
projectDictionary[key] = value
}
do {
return try Project(jsonDictionary: projectDictionary)
} catch {
throw failure("\(error)", file: file, line: line)
}
}
private func loadSpec(path: Path, variables: [String: String] = [:], file: String = #file, line: Int = #line) throws -> Project {
do {
let specLoader = SpecLoader(version: "1.1.0")
return try specLoader.loadProject(path: path, variables: variables)
} catch {
throw failure("\(error)", file: file, line: line)
}
}
private func expectSpecError(_ project: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
try expectError(expectedError, file: file, line: line) {
try getProjectSpec(project)
}
}
private func expectTargetError(_ target: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
try expectError(expectedError, file: file, line: line) {
_ = try Target(name: "test", jsonDictionary: target)
}
}
private func expectBreakpointError(_ breakpoint: [String: Any], _ expectedError: SpecParsingError, file: String = #file, line: Int = #line) throws {
try expectError(expectedError, file: file, line: line) {
_ = try Breakpoint(jsonDictionary: breakpoint)
}
}