PartTests.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // Copyright 2024 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 Foundation
  15. import XCTest
  16. @testable import FirebaseAILogic
  17. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
  18. final class PartTests: XCTestCase {
  19. let decoder = JSONDecoder()
  20. let encoder = JSONEncoder()
  21. override func setUp() {
  22. encoder.outputFormatting = .init(
  23. arrayLiteral: .prettyPrinted, .sortedKeys, .withoutEscapingSlashes
  24. )
  25. }
  26. // MARK: - Part Decoding
  27. func testDecodeTextPart() throws {
  28. let expectedText = "Hello, world!"
  29. let json = """
  30. {
  31. "text" : "\(expectedText)"
  32. }
  33. """
  34. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  35. let part = try decoder.decode(TextPart.self, from: jsonData)
  36. XCTAssertEqual(part.text, expectedText)
  37. }
  38. func testDecodeInlineDataPart() throws {
  39. let imageBase64 = try PartTests.blueSquareImage()
  40. let mimeType = "image/png"
  41. let json = """
  42. {
  43. "inlineData" : {
  44. "data" : "\(imageBase64)",
  45. "mimeType" : "\(mimeType)"
  46. }
  47. }
  48. """
  49. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  50. let part = try decoder.decode(InlineDataPart.self, from: jsonData)
  51. XCTAssertEqual(part.data, Data(base64Encoded: imageBase64))
  52. XCTAssertEqual(part.mimeType, mimeType)
  53. }
  54. func testDecodeFunctionResponsePart() throws {
  55. let functionName = "test-function-name"
  56. let resultParameter = "test-result-parameter"
  57. let resultValue = "test-result-value"
  58. let json = """
  59. {
  60. "functionResponse" : {
  61. "name" : "\(functionName)",
  62. "response" : {
  63. "\(resultParameter)" : "\(resultValue)"
  64. }
  65. }
  66. }
  67. """
  68. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  69. let part = try decoder.decode(FunctionResponsePart.self, from: jsonData)
  70. let functionResponse = part.functionResponse
  71. XCTAssertEqual(functionResponse.name, functionName)
  72. XCTAssertEqual(functionResponse.response, [resultParameter: .string(resultValue)])
  73. }
  74. func testDecodeExecutableCodePart() throws {
  75. let json = """
  76. {
  77. "executableCode": {
  78. "language": "PYTHON",
  79. "code": "print('hello')"
  80. }
  81. }
  82. """
  83. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  84. let part = try decoder.decode(ExecutableCodePart.self, from: jsonData)
  85. XCTAssertEqual(part.language, .python)
  86. XCTAssertEqual(part.code, "print('hello')")
  87. }
  88. func testDecodeExecutableCodePart_missingLanguage() throws {
  89. let json = """
  90. {
  91. "executableCode": {
  92. "code": "print('hello')"
  93. }
  94. }
  95. """
  96. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  97. let part = try decoder.decode(ExecutableCodePart.self, from: jsonData)
  98. XCTAssertEqual(part.language.description, "LANGUAGE_UNSPECIFIED")
  99. XCTAssertEqual(part.code, "print('hello')")
  100. }
  101. func testDecodeExecutableCodePart_missingCode() throws {
  102. let json = """
  103. {
  104. "executableCode": {
  105. "language": "PYTHON"
  106. }
  107. }
  108. """
  109. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  110. let part = try decoder.decode(ExecutableCodePart.self, from: jsonData)
  111. XCTAssertEqual(part.language, .python)
  112. XCTAssertEqual(part.code, "")
  113. }
  114. func testDecodeExecutableCodePart_missingLanguageAndCode() throws {
  115. let json = """
  116. {
  117. "executableCode": {}
  118. }
  119. """
  120. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  121. let part = try decoder.decode(ExecutableCodePart.self, from: jsonData)
  122. XCTAssertEqual(part.language.description, "LANGUAGE_UNSPECIFIED")
  123. XCTAssertEqual(part.code, "")
  124. }
  125. func testDecodeCodeExecutionResultPart() throws {
  126. let json = """
  127. {
  128. "codeExecutionResult": {
  129. "outcome": "OUTCOME_OK",
  130. "output": "hello"
  131. }
  132. }
  133. """
  134. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  135. let part = try decoder.decode(CodeExecutionResultPart.self, from: jsonData)
  136. XCTAssertEqual(part.outcome, .ok)
  137. XCTAssertEqual(part.output, "hello")
  138. }
  139. func testDecodeCodeExecutionResultPart_missingOutcome() throws {
  140. let json = """
  141. {
  142. "codeExecutionResult": {
  143. "output": "hello"
  144. }
  145. }
  146. """
  147. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  148. let part = try decoder.decode(CodeExecutionResultPart.self, from: jsonData)
  149. XCTAssertEqual(part.outcome.description, "OUTCOME_UNSPECIFIED")
  150. XCTAssertEqual(part.output, "hello")
  151. }
  152. func testDecodeCodeExecutionResultPart_missingOutput() throws {
  153. let json = """
  154. {
  155. "codeExecutionResult": {
  156. "outcome": "OUTCOME_OK"
  157. }
  158. }
  159. """
  160. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  161. let part = try decoder.decode(CodeExecutionResultPart.self, from: jsonData)
  162. XCTAssertEqual(part.outcome, .ok)
  163. XCTAssertNil(part.output)
  164. }
  165. func testDecodeCodeExecutionResultPart_missingOutcomeAndOutput() throws {
  166. let json = """
  167. {
  168. "codeExecutionResult": {}
  169. }
  170. """
  171. let jsonData = try XCTUnwrap(json.data(using: .utf8))
  172. let part = try decoder.decode(CodeExecutionResultPart.self, from: jsonData)
  173. XCTAssertEqual(part.outcome.description, "OUTCOME_UNSPECIFIED")
  174. XCTAssertNil(part.output)
  175. }
  176. // MARK: - Part Encoding
  177. func testEncodeTextPart() throws {
  178. let expectedText = "Hello, world!"
  179. let textPart = TextPart(expectedText)
  180. let jsonData = try encoder.encode(textPart)
  181. let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8))
  182. XCTAssertEqual(json, """
  183. {
  184. "text" : "\(expectedText)"
  185. }
  186. """)
  187. }
  188. func testEncodeInlineDataPart() throws {
  189. let mimeType = "image/png"
  190. let imageBase64 = try PartTests.blueSquareImage()
  191. let imageBase64Data = Data(base64Encoded: imageBase64)
  192. let inlineDataPart = InlineDataPart(data: imageBase64Data!, mimeType: mimeType)
  193. let jsonData = try encoder.encode(inlineDataPart)
  194. let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8))
  195. XCTAssertEqual(json, """
  196. {
  197. "inlineData" : {
  198. "data" : "\(imageBase64)",
  199. "mimeType" : "\(mimeType)"
  200. }
  201. }
  202. """)
  203. }
  204. func testEncodeFileDataPart() throws {
  205. let mimeType = "image/jpeg"
  206. let fileURI = "gs://test-bucket/image.jpg"
  207. let fileDataPart = FileDataPart(uri: fileURI, mimeType: mimeType)
  208. let jsonData = try encoder.encode(fileDataPart)
  209. let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8))
  210. XCTAssertEqual(json, """
  211. {
  212. "fileData" : {
  213. "fileUri" : "\(fileURI)",
  214. "mimeType" : "\(mimeType)"
  215. }
  216. }
  217. """)
  218. }
  219. func testEncodeExecutableCodePart() throws {
  220. let executableCodePart = ExecutableCodePart(language: .python, code: "print('hello')")
  221. let jsonData = try encoder.encode(executableCodePart)
  222. let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8))
  223. XCTAssertEqual(json, """
  224. {
  225. "executableCode" : {
  226. "code" : "print('hello')",
  227. "language" : "PYTHON"
  228. }
  229. }
  230. """)
  231. }
  232. func testEncodeCodeExecutionResultPart() throws {
  233. let codeExecutionResultPart = CodeExecutionResultPart(outcome: .ok, output: "hello")
  234. let jsonData = try encoder.encode(codeExecutionResultPart)
  235. let json = try XCTUnwrap(String(data: jsonData, encoding: .utf8))
  236. XCTAssertEqual(json, """
  237. {
  238. "codeExecutionResult" : {
  239. "outcome" : "OUTCOME_OK",
  240. "output" : "hello"
  241. }
  242. }
  243. """)
  244. }
  245. // MARK: - Helpers
  246. private static func bundle() -> Bundle {
  247. #if SWIFT_PACKAGE
  248. return Bundle.module
  249. #else // SWIFT_PACKAGE
  250. return Bundle(for: Self.self)
  251. #endif // SWIFT_PACKAGE
  252. }
  253. private static func blueSquareImage() throws -> String {
  254. let imageURL = bundle().url(forResource: "blue", withExtension: "png")!
  255. let imageData = try Data(contentsOf: imageURL)
  256. return imageData.base64EncodedString()
  257. }
  258. }