GenerativeModelTestUtil.swift 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright 2025 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import FirebaseAppCheckInterop
  15. import FirebaseAuthInterop
  16. import FirebaseCore
  17. import Foundation
  18. import XCTest
  19. @testable import FirebaseAI
  20. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  21. enum GenerativeModelTestUtil {
  22. /// Returns an HTTP request handler
  23. static func httpRequestHandler(forResource name: String,
  24. withExtension ext: String,
  25. subdirectory subpath: String,
  26. statusCode: Int = 200,
  27. timeout: TimeInterval = RequestOptions().timeout,
  28. appCheckToken: String? = nil,
  29. authToken: String? = nil,
  30. dataCollection: Bool = true) throws -> ((URLRequest) throws -> (
  31. URLResponse,
  32. AsyncLineSequence<URL.AsyncBytes>?
  33. )) {
  34. // Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
  35. // https://developer.apple.com/documentation/foundation/urlprotocol for details.
  36. #if os(watchOS)
  37. throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
  38. #else // os(watchOS)
  39. let bundle = BundleTestUtil.bundle()
  40. let fileURL = try XCTUnwrap(
  41. bundle.url(forResource: name, withExtension: ext, subdirectory: subpath)
  42. )
  43. return { request in
  44. let requestURL = try XCTUnwrap(request.url)
  45. XCTAssertEqual(requestURL.path.occurrenceCount(of: "models/"), 1)
  46. XCTAssertEqual(request.timeoutInterval, timeout)
  47. let apiClientTags = try XCTUnwrap(request.value(forHTTPHeaderField: "x-goog-api-client"))
  48. .components(separatedBy: " ")
  49. XCTAssert(apiClientTags.contains(GenerativeAIService.languageTag))
  50. XCTAssert(apiClientTags.contains(GenerativeAIService.firebaseVersionTag))
  51. XCTAssertEqual(request.value(forHTTPHeaderField: "X-Firebase-AppCheck"), appCheckToken)
  52. let firebaseAppID = request.value(forHTTPHeaderField: "X-Firebase-AppId")
  53. let appVersion = request.value(forHTTPHeaderField: "X-Firebase-AppVersion")
  54. let expectedAppVersion =
  55. try? XCTUnwrap(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)
  56. XCTAssertEqual(firebaseAppID, dataCollection ? "My app ID" : nil)
  57. XCTAssertEqual(appVersion, dataCollection ? expectedAppVersion : nil)
  58. if let authToken {
  59. XCTAssertEqual(
  60. request.value(forHTTPHeaderField: "Authorization"),
  61. "Firebase \(authToken)"
  62. )
  63. } else {
  64. XCTAssertNil(request.value(forHTTPHeaderField: "Authorization"))
  65. }
  66. let response = try XCTUnwrap(HTTPURLResponse(
  67. url: requestURL,
  68. statusCode: statusCode,
  69. httpVersion: nil,
  70. headerFields: nil
  71. ))
  72. return (response, fileURL.lines)
  73. }
  74. #endif // os(watchOS)
  75. }
  76. static func nonHTTPRequestHandler() throws -> ((URLRequest) -> (
  77. URLResponse,
  78. AsyncLineSequence<URL.AsyncBytes>?
  79. )) {
  80. // Skip tests using MockURLProtocol on watchOS; unsupported in watchOS 2 and later, see
  81. // https://developer.apple.com/documentation/foundation/urlprotocol for details.
  82. #if os(watchOS)
  83. throw XCTSkip("Custom URL protocols are unsupported in watchOS 2 and later.")
  84. #else // os(watchOS)
  85. return { request in
  86. // This is *not* an HTTPURLResponse
  87. let response = URLResponse(
  88. url: request.url!,
  89. mimeType: nil,
  90. expectedContentLength: 0,
  91. textEncodingName: nil
  92. )
  93. return (response, nil)
  94. }
  95. #endif // os(watchOS)
  96. }
  97. static func testFirebaseInfo(appCheck: AppCheckInterop? = nil,
  98. auth: AuthInterop? = nil,
  99. privateAppID: Bool = false) -> FirebaseInfo {
  100. let app = FirebaseApp(instanceWithName: "testApp",
  101. options: FirebaseOptions(googleAppID: "ignore",
  102. gcmSenderID: "ignore"))
  103. app.isDataCollectionDefaultEnabled = !privateAppID
  104. return FirebaseInfo(
  105. appCheck: appCheck,
  106. auth: auth,
  107. projectID: "my-project-id",
  108. apiKey: "API_KEY",
  109. firebaseAppID: "My app ID",
  110. firebaseApp: app
  111. )
  112. }
  113. }
  114. private extension String {
  115. /// Returns the number of occurrences of `substring` in the `String`.
  116. func occurrenceCount(of substring: String) -> Int {
  117. return components(separatedBy: substring).count - 1
  118. }
  119. }