Merge pull request #117 from yonaskolb/validation_tests

Add spec validation tests
This commit is contained in:
Yonas Kolb
2017-10-29 21:05:03 +01:00
committed by GitHub
7 changed files with 116 additions and 36 deletions
+2
View File
@@ -22,6 +22,8 @@ public struct Config: Equatable {
public static func ==(lhs: Config, rhs: Config) -> Bool {
return lhs.name == rhs.name && lhs.type == rhs.type
}
public static var defaultConfigs: [Config] = [Config(name: "Debug", type: .debug), Config(name: "Release", type: .release)]
}
public enum ConfigType: String {
+2 -2
View File
@@ -57,7 +57,7 @@ public struct ProjectSpec {
}
}
public init(basePath: Path, name: String, configs: [Config] = [], targets: [Target] = [], settings: Settings = .empty, settingGroups: [String: Settings] = [:], schemes: [Scheme] = [], options: Options = Options(), fileGroups: [String] = [], configFiles: [String: String] = [:], attributes: [String: Any] = [:]) {
public init(basePath: Path, name: String, configs: [Config] = Config.defaultConfigs, targets: [Target] = [], settings: Settings = .empty, settingGroups: [String: Settings] = [:], schemes: [Scheme] = [], options: Options = Options(), fileGroups: [String] = [], configFiles: [String: String] = [:], attributes: [String: Any] = [:]) {
self.basePath = basePath
self.name = name
self.targets = targets
@@ -135,7 +135,7 @@ extension ProjectSpec {
settings = jsonDictionary.json(atKeyPath: "settings") ?? .empty
settingGroups = jsonDictionary.json(atKeyPath: "settingGroups") ?? jsonDictionary.json(atKeyPath: "settingPresets") ?? [:]
let configs: [String: String] = jsonDictionary.json(atKeyPath: "configs") ?? [:]
self.configs = configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
self.configs = configs.isEmpty ? Config.defaultConfigs : configs.map { Config(name: $0, type: ConfigType(rawValue: $1)) }.sorted { $0.name < $1.name }
targets = try jsonDictionary.json(atKeyPath: "targets").sorted { $0.name < $1.name }
schemes = try jsonDictionary.json(atKeyPath: "schemes")
fileGroups = jsonDictionary.json(atKeyPath: "fileGroups") ?? []
@@ -6,21 +6,16 @@
//
import Foundation
import ProjectSpec
import PathKit
extension ProjectSpec {
public mutating func validate() throws {
public func validate() throws {
if configs.isEmpty {
configs = [Config(name: "Debug", type: .debug), Config(name: "Release", type: .release)]
}
var errors: [SpecValidationError.ValidationError] = []
var errors: [SpecValidationError.Error] = []
func validateSettings(_ settings: Settings) -> [SpecValidationError.Error] {
var errors: [SpecValidationError.Error] = []
func validateSettings(_ settings: Settings) -> [SpecValidationError.ValidationError] {
var errors: [SpecValidationError.ValidationError] = []
for group in settings.groups {
if let settings = settingGroups[group] {
errors += validateSettings(settings)
@@ -30,12 +25,14 @@ extension ProjectSpec {
}
for config in settings.configSettings.keys {
if !configs.contains(where: { $0.name.lowercased().contains(config.lowercased())}) {
errors.append(.invalidConfigReference(config))
errors.append(.invalidBuildSettingConfig(config))
}
}
return errors
}
errors += validateSettings(settings)
for fileGroup in fileGroups {
if !(basePath + fileGroup).exists {
errors.append(.invalidFileGroup(fileGroup))
@@ -46,6 +43,9 @@ extension ProjectSpec {
if !(basePath + configFile).exists {
errors.append(.invalidConfigFile(configFile: configFile, config: config))
}
if getConfig(config) == nil {
errors.append(.invalidConfigFileConfig(config))
}
}
for settings in settingGroups.values {
@@ -61,13 +61,10 @@ extension ProjectSpec {
for (config, configFile) in target.configFiles {
if !(basePath + configFile).exists {
errors.append(.invalidTargetConfigFile(configFile: configFile, config: config, target: target.name))
errors.append(.invalidTargetConfigFile(target: target.name, configFile: configFile, config: config))
}
}
for config in target.settings.configSettings.keys {
if getConfig(config) == nil {
errors.append(.invalidBuildSettingConfig(config))
errors.append(.invalidConfigFileConfig(config))
}
}
@@ -110,7 +107,7 @@ extension ProjectSpec {
if case let .path(pathString) = script.script {
let scriptPath = basePath + pathString
if !scriptPath.exists {
errors.append(.invalidBuildScriptPath(target: target.name, path: pathString))
errors.append(.invalidBuildScriptPath(target: target.name, name: script.name, path: pathString))
}
}
}
@@ -149,39 +146,39 @@ extension ProjectSpec {
public struct SpecValidationError: Error, CustomStringConvertible {
public var errors: [Error]
public var errors: [ValidationError]
public enum Error: CustomStringConvertible {
public enum ValidationError: Error, CustomStringConvertible {
case invalidTargetDependency(target: String, dependency: String)
case missingTargetSource(target: String, source: String)
case invalidTargetConfigFile(target: String, configFile: String, config: String)
case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType)
case invalidTargetSchemeTest(target: String, testTarget: String)
case invalidSchemeTarget(scheme: String, target: String)
case invalidSchemeConfig(scheme: String, config: String)
case invalidConfigFile(configFile: String, config: String)
case invalidTargetConfigFile(configFile: String, config: String, target: String)
case invalidBuildSettingConfig(String)
case invalidSettingsGroup(String)
case missingTargetSource(target: String, source: String)
case invalidBuildScriptPath(target: String, path: String)
case invalidTargetSchemeConfigVariant(target: String, configVariant: String, configType: ConfigType)
case invalidTargetSchemeTest(target: String, testTarget: String)
case invalidBuildScriptPath(target: String, name: String?, path: String)
case invalidFileGroup(String)
case invalidConfigReference(String)
case invalidConfigFileConfig(String)
case missingConfigTypeForGeneratedTargetScheme(target: String, configType: ConfigType)
public var description: String {
switch self {
case let .invalidTargetDependency(target, dependency): return "Target \(target.quoted) has invalid dependency: \(dependency.quoted)"
case let .invalidTargetConfigFile(configFile, config, target): return "Target \(target.quoted) has invalid config file \(configFile.quoted) for config \(config.quoted)"
case let .invalidTargetConfigFile(target, configFile, config): return "Target \(target.quoted) has invalid config file \(configFile.quoted) for config \(config.quoted)"
case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)"
case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has an invalid scheme config variant which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
case let .invalidTargetSchemeTest(target, test): return "Target \(target.quoted) scheme has invalid test \(test.quoted)"
case let .invalidConfigFile(configFile, config): return "Invalid config file \(configFile.quoted) for config \(config.quoted)"
case let .invalidSchemeTarget(scheme, target): return "Scheme \(scheme.quoted) has invalid build target \(target.quoted)"
case let .invalidSchemeConfig(scheme, config): return "Scheme \(scheme.quoted) has invalid build configuration \(config.quoted)"
case let .invalidBuildSettingConfig(config): return "Build setting has invalid build configuration \(config.quoted)"
case let .missingTargetSource(target, source): return "Target \(target.quoted) has a missing source directory \(source.quoted)"
case let .invalidSettingsGroup(group): return "Invalid settings group \(group.quoted)"
case let .invalidBuildScriptPath(target, path): return "Target \(target.quoted) has a script path that doesn't exist \(path.quoted)"
case let .invalidTargetSchemeConfigVariant(target, configVariant, configType): return "Target \(target.quoted) has an invalid scheme config variant which requires a config that has a \(configType.rawValue.quoted) type and contains the name \(configVariant.quoted)"
case let .invalidTargetSchemeTest(target, test): return "Target \(target.quoted) scheme has invalid test \(test.quoted)"
case let .invalidBuildScriptPath(target, name, path): return "Target \(target.quoted) has a script \(name != nil ? "\(name!.quoted) which has a " : "")path that doesn't exist \(path.quoted)"
case let .invalidFileGroup(group): return "Invalid file group \(group.quoted)"
case let .invalidConfigReference(config): return "Invalid config reference \(config.quoted)"
case let .invalidConfigFileConfig(config): return "Config file has invalid config \(config.quoted)"
case let .missingConfigTypeForGeneratedTargetScheme(target, configType): return "Target \(target.quoted) is missing a config of type \(configType.rawValue) to generate its scheme"
}
}
+2 -2
View File
@@ -135,8 +135,8 @@ extension Target: Equatable {
}
public struct TargetScheme {
public let testTargets: [String]
public let configVariants: [String]
public var testTargets: [String]
public var configVariants: [String]
public init(testTargets: [String] = [], configVariants: [String] = []) {
self.testTargets = testTargets
+1 -1
View File
@@ -15,7 +15,7 @@ import ProjectSpec
public class ProjectGenerator {
var spec: ProjectSpec
let spec: ProjectSpec
let currentXcodeVersion = "0900"
public init(spec: ProjectSpec) {
@@ -24,5 +24,86 @@ func projectSpecTests() {
try expect(dynamicLibrary.type.isLibrary).to.beTrue()
}
}
func expectValidationError(_ spec: ProjectSpec, _ expectedError: SpecValidationError.ValidationError) throws {
do {
try spec.validate()
} catch let error as SpecValidationError {
if !error.errors.contains( where: { $0.description == expectedError.description }) {
throw failure("Supposed to fail with:\n\(expectedError)\nbut got:\n\(error.errors.map { $0.description }.joined(separator: "\n"))")
}
return
} catch {
throw failure("Supposed to fail with \"\(expectedError)\"")
}
throw failure("Supposed to fail with \"\(expectedError)\"")
}
$0.describe("Validation") {
let baseSpec = ProjectSpec(basePath: "", name: "", configs: [Config(name: "invalid")])
let invalidSettings = Settings(configSettings: ["invalidConfig": [:]],
groups: ["invalidSettingGroup"])
$0.it("fails with invalid project") {
var spec = baseSpec
spec.settings = invalidSettings
spec.configFiles = ["invalidConfig": "invalidConfigFile"]
spec.fileGroups = ["invalidFileGroup"]
spec.settingGroups = ["settingGroup1": Settings(configSettings: ["invalidSettingGroupConfig": [:]],
groups: ["invalidSettingGroupSettingGroup"])]
try expectValidationError(spec, .invalidConfigFileConfig("invalidConfig"))
try expectValidationError(spec, .invalidBuildSettingConfig("invalidConfig"))
try expectValidationError(spec, .invalidConfigFile(configFile: "invalidConfigFile", config: "invalidConfig"))
try expectValidationError(spec, .invalidSettingsGroup("invalidSettingGroup"))
try expectValidationError(spec, .invalidFileGroup("invalidFileGroup"))
try expectValidationError(spec, .invalidSettingsGroup("invalidSettingGroupSettingGroup"))
try expectValidationError(spec, .invalidBuildSettingConfig("invalidSettingGroupConfig"))
}
$0.it("fails with invalid target") {
var spec = baseSpec
spec.targets = [Target(name: "target1",
type: .application,
platform: .iOS,
settings: invalidSettings,
configFiles: ["invalidConfig": "invalidConfigFile"],
sources: ["invalidSource"],
dependencies: [Dependency(type: .target, reference: "invalidDependency")],
prebuildScripts: [BuildScript(script: .path("invalidPrebuildScript"), name: "prebuildScript1", inputFiles:
[], outputFiles: [])],
postbuildScripts: [BuildScript(script: .path("invalidPostbuildScript"), inputFiles:
[], outputFiles: [])],
scheme: TargetScheme(testTargets: ["invalidTarget"])
)]
try expectValidationError(spec, .invalidTargetDependency(target: "target1", dependency: "invalidDependency"))
try expectValidationError(spec, .invalidTargetConfigFile(target: "target1", configFile: "invalidConfigFile", config: "invalidConfig"))
try expectValidationError(spec, .invalidTargetSchemeTest(target: "target1", testTarget: "invalidTarget"))
try expectValidationError(spec, .missingTargetSource(target: "target1", source: "invalidSource"))
try expectValidationError(spec, .invalidBuildSettingConfig("invalidConfig"))
try expectValidationError(spec, .invalidSettingsGroup("invalidSettingGroup"))
try expectValidationError(spec, .invalidBuildScriptPath(target:"target1", name: "prebuildScript1", path: "invalidPrebuildScript"))
try expectValidationError(spec, .invalidBuildScriptPath(target:"target1", name: nil, path: "invalidPostbuildScript"))
try expectValidationError(spec, .missingConfigTypeForGeneratedTargetScheme(target:"target1", configType: .debug))
try expectValidationError(spec, .missingConfigTypeForGeneratedTargetScheme(target:"target1", configType: .release))
spec.targets[0].scheme?.configVariants = ["invalidVariant"]
try expectValidationError(spec, .invalidTargetSchemeConfigVariant(target: "target1", configVariant: "invalidVariant", configType: .debug))
}
$0.it("fails with invalid scheme") {
var spec = baseSpec
spec.schemes = [Scheme(name: "scheme1",
targets: [Scheme.BuildTarget(target: "invalidTarget")],
debugConfig: "debugInvalid",
releaseConfig: "releaseInvalid")]
try expectValidationError(spec, .invalidSchemeTarget(scheme: "scheme1", target: "invalidTarget"))
try expectValidationError(spec, .invalidSchemeConfig(scheme: "scheme1", config: "debugInvalid"))
try expectValidationError(spec, .invalidSchemeConfig(scheme: "scheme1", config: "releaseInvalid"))
}
}
}
}
@@ -48,7 +48,7 @@ func specLoadingTests() {
}
}
describe("Project Spec") {
describe("Project Spec Parser") {
$0.it("fails with incorrect platform") {
var target = validTarget