From 6786df470c2fcba3ba1a51c7d9e2cb80b632f7f7 Mon Sep 17 00:00:00 2001 From: riteshshukla04 Date: Wed, 10 Jun 2026 14:24:26 +0530 Subject: [PATCH] chore: soemthi --- .../visioncamera.camera-view.harness.tsx | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/apps/simple-camera/__tests__/visioncamera.camera-view.harness.tsx b/apps/simple-camera/__tests__/visioncamera.camera-view.harness.tsx index 6c238adfcf..eb467024a1 100644 --- a/apps/simple-camera/__tests__/visioncamera.camera-view.harness.tsx +++ b/apps/simple-camera/__tests__/visioncamera.camera-view.harness.tsx @@ -8,13 +8,16 @@ import { expect, it, render, + waitUntil, } from 'react-native-harness' import type { CameraDevice, CameraRef, Point } from 'react-native-vision-camera' import { Camera, CommonResolutions, + useFrameOutput, VisionCamera, } from 'react-native-vision-camera' +import { createSynchronizable } from 'react-native-worklets' import { deferred, withTimeout } from './test-utils' interface Layout { @@ -55,6 +58,65 @@ function expectPreviewGeometry(camera: CameraRef, layout: Layout) { expect(meteringPoint.normalizedY).toBeLessThanOrEqual(1) } +type FrameCounter = ReturnType> + +interface MountCycleCameraProps { + mounted: boolean + device: CameraDevice + frameCounter: FrameCounter + onStarted: () => void + onError: (error: Error) => void +} + + +function MountCycleCamera({ + mounted, + device, + frameCounter, + onStarted, + onError, +}: MountCycleCameraProps) { + const frameOutput = useFrameOutput({ + pixelFormat: 'yuv', + onFrame(frame) { + 'worklet' + try { + frameCounter.setBlocking((prev) => prev + 1) + } finally { + frame.dispose() + } + }, + }) + if (!mounted) return null + return ( + + ) +} + +function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + + +async function expectFramesToStop(frameCounter: FrameCounter): Promise { + const deadline = Date.now() + 10_000 + let previous = frameCounter.getBlocking() + while (Date.now() < deadline) { + await sleep(750) + const current = frameCounter.getBlocking() + if (current === previous) return + previous = current + } + throw new Error('Frames kept arriving after the Camera unmounted.') +} + describe('VisionCamera - Camera View', () => { let backDevice: CameraDevice @@ -491,4 +553,88 @@ describe('VisionCamera - Camera View', () => { ) expect(sessionError).toBe(undefined) }) + + it('streams frames after each mount in an unmount -> mount -> unmount -> mount cycle', async () => { + const frameCounter = createSynchronizable(0) + let sessionError: Error | undefined + + // Start unmounted, like an app screen waiting for a button press. + const { rerender } = await render( + {}} + onError={(error) => { + sessionError = error + }} + />, + ) + expect(frameCounter.getBlocking()).toBe(0) + + // First mount: session starts and frames flow through the worklet. + const firstStarted = deferred() + await rerender( + { + sessionError = error + firstStarted.reject(error) + }} + />, + ) + await withTimeout( + firstStarted.promise, + 15_000, + 'Camera onStarted after first mount', + ) + const firstBaseline = frameCounter.getBlocking() + await waitUntil(() => frameCounter.getBlocking() >= firstBaseline + 3, { + timeout: 15_000, + }) + expect(sessionError).toBe(undefined) + + // Unmount: the session tears down and frame delivery stops. + await rerender( + {}} + onError={(error) => { + sessionError = error + }} + />, + ) + await expectFramesToStop(frameCounter) + expect(sessionError).toBe(undefined) + + // Second mount: a fresh session starts and frames flow again. + const secondStarted = deferred() + await rerender( + { + sessionError = error + secondStarted.reject(error) + }} + />, + ) + await withTimeout( + secondStarted.promise, + 15_000, + 'Camera onStarted after second mount', + ) + const secondBaseline = frameCounter.getBlocking() + await waitUntil(() => frameCounter.getBlocking() >= secondBaseline + 3, { + timeout: 15_000, + }) + expect(sessionError).toBe(undefined) + }) })