Skip to content

Commit 68f0ae5

Browse files
committed
Add streaming integration test
1 parent 3e1017a commit 68f0ae5

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

FirebaseAI/Tests/TestApp/Tests/Integration/GenerateContentIntegrationTests.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,73 @@ struct GenerateContentIntegrationTests {
489489
#expect(response == expectedResponse)
490490
}
491491

492+
@Test(arguments: [
493+
(InstanceConfig.vertexAI_v1beta, ModelNames.gemini2FlashPreviewImageGeneration),
494+
(InstanceConfig.vertexAI_v1beta_global, ModelNames.gemini2FlashPreviewImageGeneration),
495+
(InstanceConfig.vertexAI_v1beta_global, ModelNames.gemini2_5_FlashImagePreview),
496+
(InstanceConfig.googleAI_v1beta, ModelNames.gemini2FlashPreviewImageGeneration),
497+
(InstanceConfig.googleAI_v1beta, ModelNames.gemini2_5_FlashImagePreview),
498+
// Note: The following configs are commented out for easy one-off manual testing.
499+
// (InstanceConfig.googleAI_v1beta_staging, ModelNames.gemini2FlashPreviewImageGeneration)
500+
// (InstanceConfig.googleAI_v1beta_freeTier, ModelNames.gemini2FlashPreviewImageGeneration),
501+
// (
502+
// InstanceConfig.googleAI_v1beta_freeTier_bypassProxy,
503+
// ModelNames.gemini2FlashPreviewImageGeneration
504+
// ),
505+
])
506+
func generateImageStreaming(_ config: InstanceConfig, modelName: String) async throws {
507+
let generationConfig = GenerationConfig(
508+
temperature: 0.0,
509+
topP: 0.0,
510+
topK: 1,
511+
responseModalities: [.text, .image]
512+
)
513+
let safetySettings = safetySettings.filter {
514+
// HARM_CATEGORY_CIVIC_INTEGRITY is deprecated in Vertex AI but only rejected when using the
515+
// 'gemini-2.0-flash-preview-image-generation' model.
516+
$0.harmCategory != .civicIntegrity
517+
}
518+
let model = FirebaseAI.componentInstance(config).generativeModel(
519+
modelName: modelName,
520+
generationConfig: generationConfig,
521+
safetySettings: safetySettings
522+
)
523+
let prompt = "Generate an image of a cute cartoon kitten playing with a ball of yarn"
524+
525+
let stream = try model.generateContentStream(prompt)
526+
527+
var inlineDataParts = [InlineDataPart]()
528+
for try await response in stream {
529+
let candidate = try #require(response.candidates.first)
530+
let inlineDataPart = candidate.content.parts.first { $0 is InlineDataPart } as? InlineDataPart
531+
if let inlineDataPart {
532+
inlineDataParts.append(inlineDataPart)
533+
let inlineDataPartsViaAccessor = response.inlineDataParts
534+
#expect(inlineDataPartsViaAccessor.count == 1)
535+
#expect(inlineDataPartsViaAccessor == response.inlineDataParts)
536+
}
537+
let textPart = candidate.content.parts.first { $0 is TextPart } as? TextPart
538+
#expect(
539+
inlineDataPart != nil || textPart != nil || candidate.finishReason == .stop,
540+
"No text or image found in the candidate"
541+
)
542+
}
543+
544+
#expect(inlineDataParts.count == 1)
545+
let inlineDataPart = try #require(inlineDataParts.first)
546+
#expect(inlineDataPart.mimeType == "image/png")
547+
#expect(inlineDataPart.data.count > 0)
548+
#if canImport(UIKit)
549+
let uiImage = try #require(UIImage(data: inlineDataPart.data))
550+
// Gemini 2.0 Flash Experimental returns images sized to fit within a 1024x1024 pixel box but
551+
// dimensions may vary depending on the aspect ratio.
552+
#expect(uiImage.size.width <= 1024)
553+
#expect(uiImage.size.width >= 500)
554+
#expect(uiImage.size.height <= 1024)
555+
#expect(uiImage.size.height >= 500)
556+
#endif // canImport(UIKit)
557+
}
558+
492559
// MARK: - App Check Tests
493560

494561
@Test(arguments: InstanceConfig.appCheckNotConfiguredConfigs)

0 commit comments

Comments
 (0)