From 4e03de7f237aa12743468e597072b77e695cbedd Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 17 Oct 2025 12:15:00 -0400 Subject: [PATCH 1/3] [Firebase AI] Refactor SPT integration tests to Swift Testing --- .../TestApp/Resources/TestApp.entitlements | 2 + ...ServerPromptTemplateIntegrationTests.swift | 106 ++++++++++-------- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/FirebaseAI/Tests/TestApp/Resources/TestApp.entitlements b/FirebaseAI/Tests/TestApp/Resources/TestApp.entitlements index ee95ab7e582..225aa48bc8c 100644 --- a/FirebaseAI/Tests/TestApp/Resources/TestApp.entitlements +++ b/FirebaseAI/Tests/TestApp/Resources/TestApp.entitlements @@ -6,5 +6,7 @@ com.apple.security.network.client + keychain-access-groups + diff --git a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift index deaa0985310..1c754fc78d3 100644 --- a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift +++ b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift @@ -12,19 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import XCTest - import FirebaseAI - -final class ServerPromptTemplateIntegrationTests: XCTestCase { - override func setUp() { - super.setUp() - continueAfterFailure = false - } - - func testGenerateContentWithText() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI(location: "global")) - .templateGenerativeModel() +import Testing +#if canImport(UIKit) + import UIKit +#endif + +struct ServerPromptTemplateIntegrationTests { + private static let testConfigs: [InstanceConfig] = [.vertexAI_v1beta_global] + private static let imageGenerationTestConfigs: [InstanceConfig] = [.vertexAI_v1beta] + + @Test(arguments: testConfigs) + func generateContentWithText(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() let userName = "paul" let response = try await model.generateContent( template: "greeting2", @@ -33,12 +33,13 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { "language": "Spanish", ] ) - let text = try XCTUnwrap(response.text) - XCTAssert(text.contains("Paul")) + let text = try #require(response.text) + #expect(text.contains("Paul")) } - func testGenerateContentStream() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI()).templateGenerativeModel() + @Test(arguments: testConfigs) + func generateContentStream(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() let userName = "paul" let stream = try model.generateContentStream( template: "greeting.prompt", @@ -53,11 +54,12 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { resultText += text } } - XCTAssert(resultText.contains("Paul")) + #expect(resultText.contains("Paul")) } - func testGenerateImages() async throws { - let imagenModel = FirebaseAI.firebaseAI(backend: .vertexAI()).templateImagenModel() + @Test(arguments: imageGenerationTestConfigs) + func generateImages(_ config: InstanceConfig) async throws { + let imagenModel = FirebaseAI.componentInstance(config).templateImagenModel() let imagenPrompt = "A cat picture" let response = try await imagenModel.generateImages( template: "generate_images.prompt", @@ -65,13 +67,17 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { "prompt": imagenPrompt, ] ) - XCTAssertEqual(response.images.count, 3) + #expect(response.images.count == 3) } - func testGenerateContentWithMedia() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI()).templateGenerativeModel() - let image = UIImage(systemName: "photo")! - if let imageBytes = image.jpegData(compressionQuality: 0.8) { + #if canImport(UIKit) + @Test(arguments: testConfigs) + func generateContentWithMedia(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() + let image = UIImage(systemName: "photo")! + let imageBytes = try #require( + image.jpegData(compressionQuality: 0.8), "Could not get image data." + ) let base64Image = imageBytes.base64EncodedString() let response = try await model.generateContent( @@ -84,16 +90,19 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { ], ] ) - XCTAssert(response.text?.isEmpty == false) - } else { - XCTFail("Could not get image data.") + let text = try #require(response.text) + #expect(text.isEmpty == false) } - } - - func testGenerateContentStreamWithMedia() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI()).templateGenerativeModel() - let image = UIImage(systemName: "photo")! - if let imageBytes = image.jpegData(compressionQuality: 0.8) { + #endif // canImport(UIKit) + + #if canImport(UIKit) + @Test(arguments: testConfigs) + func generateContentStreamWithMedia(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() + let image = UIImage(systemName: "photo")! + let imageBytes = try #require( + image.jpegData(compressionQuality: 0.8), "Could not get image data." + ) let base64Image = imageBytes.base64EncodedString() let stream = try model.generateContentStream( @@ -112,14 +121,13 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { resultText += text } } - XCTAssert(resultText.isEmpty == false) - } else { - XCTFail("Could not get image data.") + #expect(resultText.isEmpty == false) } - } + #endif // canImport(UIKit) - func testChat() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI()).templateGenerativeModel() + @Test(arguments: testConfigs) + func chat(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() let initialHistory = [ ModelContent(role: "user", parts: "Hello!"), ModelContent(role: "model", parts: "Hi there! How can I help?"), @@ -132,13 +140,16 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { userMessage, variables: ["message": userMessage] ) - XCTAssert(response.text?.isEmpty == false) - XCTAssertEqual(chatSession.history.count, 4) - XCTAssertEqual((chatSession.history[2].parts.first as? TextPart)?.text, userMessage) + let text = try #require(response.text) + #expect(text.isEmpty == false) + #expect(chatSession.history.count == 4) + let textPart = try #require(chatSession.history[2].parts.first as? TextPart) + #expect(textPart.text == userMessage) } - func testChatStream() async throws { - let model = FirebaseAI.firebaseAI(backend: .vertexAI()).templateGenerativeModel() + @Test(arguments: testConfigs) + func chatStream(_ config: InstanceConfig) async throws { + let model = FirebaseAI.componentInstance(config).templateGenerativeModel() let initialHistory = [ ModelContent(role: "user", parts: "Hello!"), ModelContent(role: "model", parts: "Hi there! How can I help?"), @@ -157,8 +168,9 @@ final class ServerPromptTemplateIntegrationTests: XCTestCase { resultText += text } } - XCTAssert(resultText.isEmpty == false) - XCTAssertEqual(chatSession.history.count, 4) - XCTAssertEqual((chatSession.history[2].parts.first as? TextPart)?.text, userMessage) + #expect(resultText.isEmpty == false) + #expect(chatSession.history.count == 4) + let textPart = try #require(chatSession.history[2].parts.first as? TextPart) + #expect(textPart.text == userMessage) } } From 296814a8ae4a52bb6987f0d1d3a639d22fbcfb70 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 17 Oct 2025 12:23:45 -0400 Subject: [PATCH 2/3] Swift readability fixes --- .../ServerPromptTemplateIntegrationTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift index 1c754fc78d3..ad523ee5902 100644 --- a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift +++ b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift @@ -91,7 +91,7 @@ struct ServerPromptTemplateIntegrationTests { ] ) let text = try #require(response.text) - #expect(text.isEmpty == false) + #expect(!text.isEmpty) } #endif // canImport(UIKit) @@ -121,7 +121,7 @@ struct ServerPromptTemplateIntegrationTests { resultText += text } } - #expect(resultText.isEmpty == false) + #expect(!resultText.isEmpty) } #endif // canImport(UIKit) @@ -141,7 +141,7 @@ struct ServerPromptTemplateIntegrationTests { variables: ["message": userMessage] ) let text = try #require(response.text) - #expect(text.isEmpty == false) + #expect(!text.isEmpty) #expect(chatSession.history.count == 4) let textPart = try #require(chatSession.history[2].parts.first as? TextPart) #expect(textPart.text == userMessage) @@ -168,7 +168,7 @@ struct ServerPromptTemplateIntegrationTests { resultText += text } } - #expect(resultText.isEmpty == false) + #expect(!resultText.isEmpty) #expect(chatSession.history.count == 4) let textPart = try #require(chatSession.history[2].parts.first as? TextPart) #expect(textPart.text == userMessage) From 47e9386b148ef8b28d470a8078b28e936b996065 Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Fri, 17 Oct 2025 12:29:16 -0400 Subject: [PATCH 3/3] Run tests on `us-central1` and `global` --- .../ServerPromptTemplateIntegrationTests.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift index ad523ee5902..b9c266f7ca3 100644 --- a/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift +++ b/FirebaseAI/Tests/TestApp/Tests/Integration/ServerPromptTemplateIntegrationTests.swift @@ -19,10 +19,16 @@ import Testing #endif struct ServerPromptTemplateIntegrationTests { - private static let testConfigs: [InstanceConfig] = [.vertexAI_v1beta_global] + private static let testConfigs: [InstanceConfig] = [ + .vertexAI_v1beta, + .vertexAI_v1beta_global, + ] private static let imageGenerationTestConfigs: [InstanceConfig] = [.vertexAI_v1beta] - @Test(arguments: testConfigs) + @Test(arguments: [ + // The "greeting2" template is only available in the `global` location. + InstanceConfig.vertexAI_v1beta_global, + ]) func generateContentWithText(_ config: InstanceConfig) async throws { let model = FirebaseAI.componentInstance(config).templateGenerativeModel() let userName = "paul"