main.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. * Copyright 2021 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import ArgumentParser
  17. import FirebaseManifest
  18. import Foundation
  19. import Utils
  20. struct PodspecsTester: ParsableCommand {
  21. /// The root of the Firebase git repo.
  22. @Option(help: "The root of the firebase-ios-sdk checked out git repo.",
  23. transform: URL.init(fileURLWithPath:))
  24. var gitRoot: URL
  25. /// A targeted testing pod, e.g. FirebaseAuth.podspec
  26. @Option(help: "A podspec that will be tested.")
  27. var podspec: String
  28. /// The root of the Firebase git repo.
  29. @Option(help: "Spec testing log dir", transform: URL.init(fileURLWithPath:))
  30. var tempLogDir: URL?
  31. @Flag(help: "Skip unit tests.")
  32. var skipTests: Bool
  33. mutating func validate() throws {
  34. guard FileManager.default.fileExists(atPath: gitRoot.path) else {
  35. throw ValidationError("git-root does not exist: \(gitRoot.path)")
  36. }
  37. }
  38. /// Trigger spec test with `spec` under the `workingDir` and return an error
  39. /// code and log.
  40. ///
  41. /// - Parameters:
  42. /// - spec: The podspec name, e.g. FirebaseAnalytics.podspec.json.
  43. /// - workingDir: The dir of the testing spec.
  44. /// - args: A dict including options with its value or/and flags with nil.
  45. /// - Returns: A tuple with an error code and log.
  46. func specTest(spec: String, workingDir: URL,
  47. args: [String: String?]) -> (code: Int32, output: String) {
  48. var exitCode: Int32 = 0
  49. var logOutput = ""
  50. // If value is nil, the key will be a flag.
  51. let arguments = args.map { key, value in
  52. if let v = value {
  53. return "--\(key)=\(v)"
  54. } else {
  55. return "--\(key)"
  56. }
  57. }.joined(separator: " ")
  58. let command =
  59. "pod spec lint \(spec) \(arguments) --sources=https://github.com/firebase/SpecsTesting,https://cdn.cocoapods.org/"
  60. print(command)
  61. let result = Shell.executeCommandFromScript(
  62. command,
  63. outputToConsole: false,
  64. workingDir: workingDir
  65. )
  66. switch result {
  67. case let .error(code, output):
  68. print("---- Failed Spec Testing: \(spec) Start ----")
  69. print("\(output)")
  70. print("---- Failed Spec Testing: \(spec) End ----")
  71. exitCode = code
  72. logOutput = output
  73. case let .success(output):
  74. print("\(spec) passed validation.")
  75. exitCode = 0
  76. logOutput = output
  77. }
  78. if let logDir = tempLogDir {
  79. do {
  80. try logOutput.write(
  81. to: logDir.appendingPathComponent("\(spec).txt"),
  82. atomically: true,
  83. encoding: String.Encoding.utf8
  84. )
  85. } catch {
  86. print(error)
  87. }
  88. }
  89. return (exitCode, logOutput)
  90. }
  91. func run() throws {
  92. let startDate = Date()
  93. var exitCode: Int32 = 0
  94. print("Started at: \(startDate.dateTimeString())")
  95. InitializeSpecTesting.setupRepo(sdkRepoURL: gitRoot)
  96. let manifest = FirebaseManifest.shared
  97. var minutes = 0
  98. var timer: DispatchSourceTimer = {
  99. let t = DispatchSource.makeTimerSource()
  100. t.schedule(deadline: .now(), repeating: 60)
  101. t.setEventHandler(handler: {
  102. print("Tests have run \(minutes) min(s).")
  103. minutes += 1
  104. })
  105. return t
  106. }()
  107. timer.resume()
  108. let testingPod = podspec.components(separatedBy: ".")[0]
  109. for pod in manifest.pods {
  110. if testingPod == pod.name {
  111. var args: [String: String?] = [:]
  112. args["platforms"] = pod.platforms.joined(separator: ",")
  113. if pod.allowWarnings {
  114. args.updateValue(nil, forKey: "allow-warnings")
  115. }
  116. if skipTests {
  117. args.updateValue(nil, forKey: "skip-tests")
  118. }
  119. let code = specTest(spec: podspec, workingDir: gitRoot, args: args).code
  120. exitCode = code
  121. }
  122. }
  123. timer.cancel()
  124. let finishDate = Date()
  125. print("Finished at: \(finishDate.dateTimeString()). " +
  126. "Duration: \(startDate.formattedDurationSince(finishDate))")
  127. Foundation.exit(exitCode)
  128. }
  129. }
  130. // Start the parsing and run the tool.
  131. PodspecsTester.main()