/* * Copyright 2021 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import ArgumentParser import FirebaseManifest import Foundation import Utils struct PodspecsTester: ParsableCommand { /// The root of the Firebase git repo. @Option(help: "The root of the firebase-ios-sdk checked out git repo.", transform: URL.init(fileURLWithPath:)) var gitRoot: URL /// A targeted testing pod, e.g. FirebaseAuth.podspec @Option(help: "A podspec that will be tested.") var podspec: String /// The root of the Firebase git repo. @Option(help: "Spec testing log dir", transform: URL.init(fileURLWithPath:)) var tempLogDir: URL? @Flag(help: "Skip unit tests.") var skipTests: Bool mutating func validate() throws { guard FileManager.default.fileExists(atPath: gitRoot.path) else { throw ValidationError("git-root does not exist: \(gitRoot.path)") } } /// Trigger spec test with `spec` under the `workingDir` and return an error /// code and log. /// /// - Parameters: /// - spec: The podspec name, e.g. FirebaseAnalytics.podspec.json. /// - workingDir: The dir of the testing spec. /// - args: A dict including options with its value or/and flags with nil. /// - Returns: A tuple with an error code and log. func specTest(spec: String, workingDir: URL, args: [String: String?]) -> (code: Int32, output: String) { var exitCode: Int32 = 0 var logOutput = "" // If value is nil, the key will be a flag. let arguments = args.map { key, value in if let v = value { return "--\(key)=\(v)" } else { return "--\(key)" } }.joined(separator: " ") let command = "pod spec lint \(spec) \(arguments) --sources=https://github.com/firebase/SpecsTesting,https://cdn.cocoapods.org/" print(command) let result = Shell.executeCommandFromScript( command, outputToConsole: false, workingDir: workingDir ) switch result { case let .error(code, output): print("---- Failed Spec Testing: \(spec) Start ----") print("\(output)") print("---- Failed Spec Testing: \(spec) End ----") exitCode = code logOutput = output case let .success(output): print("\(spec) passed validation.") exitCode = 0 logOutput = output } if let logDir = tempLogDir { do { try logOutput.write( to: logDir.appendingPathComponent("\(spec).txt"), atomically: true, encoding: String.Encoding.utf8 ) } catch { print(error) } } return (exitCode, logOutput) } func run() throws { let startDate = Date() var exitCode: Int32 = 0 print("Started at: \(startDate.dateTimeString())") InitializeSpecTesting.setupRepo(sdkRepoURL: gitRoot) let manifest = FirebaseManifest.shared var minutes = 0 var timer: DispatchSourceTimer = { let t = DispatchSource.makeTimerSource() t.schedule(deadline: .now(), repeating: 60) t.setEventHandler(handler: { print("Tests have run \(minutes) min(s).") minutes += 1 }) return t }() timer.resume() let testingPod = podspec.components(separatedBy: ".")[0] for pod in manifest.pods { if testingPod == pod.name { var args: [String: String?] = [:] args["platforms"] = pod.platforms.joined(separator: ",") if pod.allowWarnings { args.updateValue(nil, forKey: "allow-warnings") } if skipTests { args.updateValue(nil, forKey: "skip-tests") } let code = specTest(spec: podspec, workingDir: gitRoot, args: args).code exitCode = code } } timer.cancel() let finishDate = Date() print("Finished at: \(finishDate.dateTimeString()). " + "Duration: \(startDate.formattedDurationSince(finishDate))") Foundation.exit(exitCode) } } // Start the parsing and run the tool. PodspecsTester.main()