§
    %Çjx€  ã                   ó  — d Z ddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZmZmZ  ee¦  «                             ¦   «         j        Zej        Z ee¦  «        ej        vr$ej                             d ee¦  «        ¦  «          ee¦  «        ej        vr$ej                             d ee¦  «        ¦  «         ddlmZmZmZmZmZmZmZmZm Z m!Z!  G d„ dej"        ¦  «        Z#e$dk    r ej%        ¦   «          dS dS )	z•
Tier 2: Boundary & Corner Cases Test Suite (F1 - F13)
Contains >= 5 distinct, rigorous boundary/edge-case tests per feature (Total: 65 test cases).
é    N)ÚPath)ÚDictÚListÚOptionalÚTupleÚAny)
ÚFILMMAKER_DIRÚPROMPTS_DIRÚ
ASSETS_DIRÚ	BIBLE_DIRÚPIPELINE_DIRÚEXPORTS_DIRÚWEB_REVIEW_DIRÚfind_male_crying_violationsÚcreate_synthetic_wavÚcreate_synthetic_video_mp4c                   ó˜  — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z	d„ Z
d	„ Zd
„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z d„ Z!d „ Z"d!„ Z#d"„ Z$d#„ Z%d$„ Z&d%„ Z'd&„ Z(d'„ Z)d(„ Z*d)„ Z+d*„ Z,d+„ Z-d,„ Z.d-„ Z/d.„ Z0d/„ Z1d0„ Z2d1„ Z3d2„ Z4d3„ Z5d4„ Z6d5„ Z7d6„ Z8d7„ Z9d8„ Z:d9„ Z;d:„ Z<d;„ Z=d<„ Z>d=„ Z?d>„ Z@d?„ ZAd@„ ZBdA„ ZCdB„ ZDdCS )DÚTestTier2BoundarieszY
    Tier 2 tests push each subsystem against adverse, extreme, and edge conditions.
    c                 ó°  — d}t          j        d|¦  «        }g }t          dt          |¦  «        d¦  «        D ]y}||                              ¦   «         }|dz   t          |¦  «        k     r||dz                                 ¦   «         nd}t          |¦  «        dk    r|                     |¦  «         Œz|                      t          |¦  «        dd¦  «         dS )	zGF1.B1: Screenplay parser detects and rejects scenes with empty content.ur   #### Cáº¢NH 01: NGOáº I. Báº¦U TRá»œI - NGÃ€Y

#### Cáº¢NH 02: Ná»˜I. PHÃ’NG KHÃ�CH - NGÃ€Y
Ná»™i dung cáº£nh 2...u#   (####\s*Cáº¢NH\s*\d+[^:\n]*:[^\n]*)é   é   Ú r   zMust detect empty scene contentN)ÚreÚsplitÚrangeÚlenÚstripÚappendÚassertEqual)ÚselfÚmalformed_scriptÚscenesÚempty_scenesÚiÚheaderÚcontents          ú4C:\Projects\KieuStory\tests\test_tier2_boundaries.pyÚ-test_f1_b01_empty_scene_description_detectionzATestTier2Boundaries.test_f1_b01_empty_scene_description_detection&   sÖ   € ð SÐÝ”Ð@ÐBRÑSÔSˆØˆÝ�q�#˜f™+œ+ qÑ)Ô)ð 	,ð 	,ˆAØ˜A”Y—_’_Ñ&Ô&ˆFØ/0°1©uµs¸6±{´{Ò/BÐ/B�f˜Q ™U”m×)Ò)Ñ+Ô+Ð+ÈˆGÝ�7‰|Œ|˜qÒ Ð Ø×#Ò# FÑ+Ô+Ð+øØ×Ò�˜\Ñ*Ô*¨AÐ/PÑQÔQÐQÐQÐQó    c                 ó  — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )	zGF1.B2: Validator rejects individual shots declared with duration > 15s.ÚdurÚreturnc                 ó"   — d| cxk     odk    nc S )Nr   g      .@© ©r+   s    r'   Úvalidate_shot_durationzhTestTier2Boundaries.test_f1_b02_oversized_single_shot_duration_rejection.<locals>.validate_shot_duration4   s   € Ø�s�?�?’?�?˜d’?�?�?�?Ð"r)   g      $@g      >@ç        g      ÀN©ÚfloatÚboolÚ
assertTrueÚassertFalse)r    r0   s     r'   Ú4test_f1_b02_oversized_single_shot_duration_rejectionzHTestTier2Boundaries.test_f1_b02_oversized_single_shot_duration_rejection2   s¨   € ð	#­ð 	#µ$ð 	#ð 	#ð 	#ð 	#à�ŠÐ.Ð.¨tÑ4Ô4Ñ5Ô5Ð5Ø×ÒÐ/Ð/°Ñ5Ô5Ñ6Ô6Ð6Ø×ÒÐ/Ð/°Ñ4Ô4Ñ5Ô5Ð5Ø×ÒÐ/Ð/°Ñ5Ô5Ñ6Ô6Ð6Ð6Ð6r)   c                 óÈ   — g d¢}t          j        dt           j        ¦  «        }|D ];}|                      t	          |                     |¦  «        ¦  «        d|› �¦  «         Œ<dS )zKF1.B3: Act parser tolerates case variance, missing spaces, and punctuation.)u   Há»’I 1: KHá»žI Ä�áº¦Uu   Há»“i 1 - Khá»Ÿi Ä‘áº§uu   há»“i 1u   Há»’I  01 :u
   ## Há»’I 1u   há»“i\s*0?1\bzPattern failed on variation: N©r   ÚcompileÚ
IGNORECASEr5   r4   Úsearch)r    Ú
variationsÚpatternÚvars       r'   Ú3test_f1_b03_malformed_act_heading_parser_resiliencezGTestTier2Boundaries.test_f1_b03_malformed_act_heading_parser_resilience;   ss   € àrÐrÐrˆ
Ý”*Ð-­r¬}Ñ=Ô=ˆØð 	^ð 	^ˆCØ�OŠO�D §¢°Ñ!4Ô!4Ñ5Ô5Ð7\ÐWZÐ7\Ð7\Ñ]Ô]Ð]Ð]ð	^ð 	^r)   c                 óÞ   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )zDF1.B4: Shot parser rejects non-positive shot index (shot00, shot-1).Úshot_idr,   c                 ó–   — t          j        d| t           j        ¦  «        }|d uo%t          |                     d¦  «        ¦  «        dk    S )Nz	shot(\d+)r   r   )r   r<   r;   ÚintÚgroup)rB   Úms     r'   Úis_valid_shot_indexzeTestTier2Boundaries.test_f1_b04_negative_or_zero_shot_indexing_rejection.<locals>.is_valid_shot_indexD   s;   € Ý”	˜,¨µ´Ñ?Ô?ˆAØ˜D�=Ð8¥S¨¯ª°©¬¡_¤_°qÒ%8Ð8r)   Úep01_scene01_shot01Úep01_scene01_shot00Úep01_scene01_shotN©Ústrr4   r5   r6   )r    rG   s     r'   Ú4test_f1_b04_negative_or_zero_shot_indexing_rejectionzHTestTier2Boundaries.test_f1_b04_negative_or_zero_shot_indexing_rejectionB   s�   € ð	9­ð 	9µð 	9ð 	9ð 	9ð 	9ð 	�ŠÐ+Ð+Ð,AÑBÔBÑCÔCÐCØ×ÒÐ,Ð,Ð-BÑCÔCÑDÔDÐDØ×ÒÐ,Ð,Ð-@ÑAÔAÑBÔBÐBÐBÐBr)   c                 óÀ   — ddl m}  |¦   «         }d„ |                     di ¦  «        D ¦   «         }t          |¦  «        dz  }|                      |dd|› d�¦  «         d	S )
z[F1.B5: Calculated EP01 total runtime does not exceed maximum ceiling of 35 minutes (2100s).r   )Úload_muse_prompts_datac                 ó>   — g | ]}d |                      ¦   «         v ¯|‘ŒS )Úep01©Úlower)Ú.0Úks     r'   ú
<listcomp>zQTestTier2Boundaries.test_f1_b05_maximum_runtime_ceiling_guard.<locals>.<listcomp>O   s/   € Ð\Ð\Ð\˜AÈÐRS×RYÒRYÑR[ÔR[ÐH[ÐH[�aÐH[ÐH[ÐH[r)   Úmotion_promptsé
   i4  zEP01 runtime zs must not exceed 2100s (35m)N)ÚtestsrO   Úgetr   ÚassertLessEqual)r    rO   Ú	muse_dataÚ
ep01_shotsÚ	total_secs        r'   Ú)test_f1_b05_maximum_runtime_ceiling_guardz=TestTier2Boundaries.test_f1_b05_maximum_runtime_ceiling_guardK   s~   € à0Ð0Ð0Ð0Ð0Ð0Ø*Ð*Ñ,Ô,ˆ	Ø\Ð\ §¢Ð/?ÀÑ!DÔ!DÐ\Ñ\Ô\ˆ
Ý˜
‘O”O bÑ(ˆ	Ø×Ò˜Y¨Ð.f¸iÐ.fÐ.fÐ.fÑgÔgÐgÐgÐgr)   c                 ó¢   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )zCF2.B1: Validator detects and flags premature weeping in sub-beat A.Úbeat_a_textr,   c                 ó\   — t          t          j        d| t          j        ¦  «        ¦  «        S )Nu(   (khÃ³c|rÆ¡i lá»‡|nÆ°á»›c máº¯t|chÃ¢u sa)©r4   r   r<   r;   )ra   s    r'   Úcheck_beat_a_tearszWTestTier2Boundaries.test_f2_b01_premature_weeping_detection.<locals>.check_beat_a_tearsX   s#   € Ý�œ	Ð"MÈ{Õ\^Ô\iÑjÔjÑkÔkÐkr)   u'   Kiá»�u Ä‘i dáº¡o vÃ  rÆ¡i nÆ°á»›c máº¯tuD   Kiá»�u tháº£ bá»™ dá»�c ngá»�n tiá»ƒu khÃª ngáº¯m cáº£nh thanh thanhNrK   )r    rd   s     r'   Ú'test_f2_b01_premature_weeping_detectionz;TestTier2Boundaries.test_f2_b01_premature_weeping_detectionV   sr   € ð	l­Cð 	lµDð 	lð 	lð 	lð 	lð 	�ŠÐ*Ð*Ð+TÑUÔUÑVÔVÐVØ×ÒÐ+Ð+Ð,rÑsÔsÑtÔtÐtÐtÐtr)   c                 ó
  — dt           dt          t           t          f         fd„}d} ||¦  «        }|                      |d         ¦  «         |                      |d         ¦  «         |                      |d         ¦  «         dS )	z7F2.B2: Validator detects missing sub-beats in Scene 05.Ú
scene_textr,   c           	      ó  — t          t          j        d| t          j        ¦  «        ¦  «        t          t          j        d| t          j        ¦  «        ¦  «        t          t          j        d| t          j        ¦  «        ¦  «        dœS )Nu"   (tiá»ƒu khÃª|suá»‘i|bÆ°á»›c dáº§n)u    (sÃ¨ sÃ¨|náº¥m má»“|váº¯ng tanh)u-   (báº¡c má»‡nh|dáº¥u hÃ i|dáº¥u giÃ y|in rÃªu))Úbeat_aÚbeat_bÚbeat_crc   )rg   s    r'   Úaudit_scene05_sub_beatszZTestTier2Boundaries.test_f2_b02_missing_sub_beat_flagging.<locals>.audit_scene05_sub_beats`   sm   € å�rœyÐ)NÐPZÕ\^Ô\iÑjÔjÑkÔkÝ�rœyÐ)LÈjÕZ\ÔZgÑhÔhÑiÔiÝ�rœyÐ)YÐ[eÕgiÔgtÑuÔuÑvÔvðð ð r)   uA   Kiá»�u Ä‘i dáº¡o bÃªn suá»‘i rá»“i há»�i náº¥m má»“ lÃ  cá»§a ai.ri   rj   rk   N)rL   r   r4   r5   r6   )r    rl   Úincomplete_sceneÚaudits       r'   Ú%test_f2_b02_missing_sub_beat_flaggingz9TestTier2Boundaries.test_f2_b02_missing_sub_beat_flagging^   s�   € ð	µð 	½½SÅ$¸Y¼ð 	ð 	ð 	ð 	ð _ÐØ'Ð'Ð(8Ñ9Ô9ˆØ�Š˜˜hœÑ(Ô(Ð(Ø�Š˜˜hœÑ(Ô(Ð(Ø×Ò˜˜xœÑ)Ô)Ð)Ð)Ð)r)   c                 ó    — g d¢g d¢g d¢dœ}|                      ¦   «         D ],\  }}|                      t          |¦  «        d|› d�¦  «         Œ-dS )zBF2.B3: Each sub-beat in Scene 05 must contain >= 4 distinct shots.)Úshot01Úshot02Úshot03Úshot04)Úshot05Úshot06Úshot07Úshot08)Úshot09Úshot10Úshot11Úshot12)zC05-AzC05-BzC05-Cé   z must have at least 4 shotsN)ÚitemsÚassertGreaterEqualr   )r    Úsub_beats_shotsÚbeatÚshotss       r'   Ú+test_f2_b03_sub_beat_shot_partition_minimumz?TestTier2Boundaries.test_f2_b03_sub_beat_shot_partition_minimuml   s„   € ð >Ð=Ð=Ø=Ð=Ð=Ø=Ð=Ð=ð
ð 
ˆð
 +×0Ò0Ñ2Ô2ð 	Yð 	Y‰KˆD�%Ø×#Ò#¥C¨¡J¤J°°dÐ3WÐ3WÐ3WÑXÔXÐXÐXð	Yð 	Yr)   c                 óª   — dt           dt          fd„}d}d}|                       ||¦  «        ¦  «         |                       ||¦  «        ¦  «         dS )zWF2.B4: Sequence validator flags inverted dramatic order (tears before mound encounter).Útextr,   c                 ó^  — t          j        d| t           j        ¦  «        }t          j        d| t           j        ¦  «        }t          j        d| t           j        ¦  «        }|r|r|sdS |                     ¦   «         |                     ¦   «         cxk     o|                     ¦   «         k     nc S )Nu   tiá»ƒu khÃªu   sÃ¨ sÃ¨u
   dáº¥u hÃ iF)r   r<   r;   Ústart)r…   Úm_aÚm_bÚm_cs       r'   Úvalidate_sub_beat_orderzaTestTier2Boundaries.test_f2_b04_inversion_of_sub_beats_rejection.<locals>.validate_sub_beat_orderx   s–   € Ý”)˜N¨Dµ"´-Ñ@Ô@ˆCÝ”)˜J¨­b¬mÑ<Ô<ˆCÝ”)˜M¨4µ´Ñ?Ô?ˆCØð ˜Cð  Cð Ø�uØ—9’9‘;”; §¢¡¤Ð:Ð:Ò:Ð:¨s¯yªy©{¬{Ò:Ð:Ð:Ð:Ð:r)   uG   Ä�i bÃªn tiá»ƒu khÃª tháº¥y náº¥m má»“ sÃ¨ sÃ¨ rá»“i tháº¥y dáº¥u hÃ i.uG   Tháº¥y dáº¥u hÃ i rá»“i Ä‘i bÃªn tiá»ƒu khÃª tháº¥y náº¥m má»“ sÃ¨ sÃ¨.NrK   )r    r‹   Úvalid_sampleÚinverted_samples       r'   Ú,test_f2_b04_inversion_of_sub_beats_rejectionz@TestTier2Boundaries.test_f2_b04_inversion_of_sub_beats_rejectionv   su   € ð	;­#ð 	;µ$ð 	;ð 	;ð 	;ð 	;ð aˆØcˆØ�ŠÐ/Ð/°Ñ=Ô=Ñ>Ô>Ð>Ø×ÒÐ0Ð0°ÑAÔAÑBÔBÐBÐBÐBr)   c                 ó²   — dt           dt          t                    fd„}d} ||¦  «        }|                      d|¦  «         |                      d|¦  «         dS )uT   F2.B5: Beat B must contain dialogue exchanges between ThÃºy Kiá»�u and VÆ°Æ¡ng Quan.r…   r,   c                 ón   — g }d| v r|                      d¦  «         d| v r|                      d¦  «         |S )Nõ   THÃšY KIá»€Uõ   VÆ¯Æ NG QUAN)r   )r…   Úspeakerss     r'   Úextract_dialogue_speakersz_TestTier2Boundaries.test_f2_b05_empty_dialogue_in_sub_beat_b.<locals>.extract_dialogue_speakers†   sF   € ØˆHØ Ð%Ð%Ø—’ Ñ/Ô/Ð/Ø Ð%Ð%Ø—’ Ñ/Ô/Ð/ØˆOr)   uQ   **THÃšY KIá»€U**: Sao váº¯ng tanh?
**VÆ¯Æ NG QUAN**: Ä�Ã¢y lÃ  má»™ Ä�áº¡m TiÃªn.r‘   r’   N)rL   r   ÚassertIn)r    r”   Úsampler“   s       r'   Ú(test_f2_b05_empty_dialogue_in_sub_beat_bz<TestTier2Boundaries.test_f2_b05_empty_dialogue_in_sub_beat_b„   sn   € ð	­Cð 	µD½´Ið 	ð 	ð 	ð 	ð fˆØ,Ð,¨VÑ4Ô4ˆØ�Š�n hÑ/Ô/Ð/Ø�Š�n hÑ/Ô/Ð/Ð/Ð/r)   c                 óp   — d}t          |¦  «        }|                      t          |¦  «        dd¦  «         dS )zHF3.B1: Anti-Male-Tears detector does NOT flag female characters weeping.u©   
        **THÃšY KIá»€U**
        *(Quá»³ sá»¥p xuá»‘ng bÃªn náº¥m Ä‘áº¥t, giá»�t nÆ°á»›c máº¯t lÄƒn dÃ i trÃªn mÃ¡)*
        Ä�au Ä‘á»›n thay pháº­n Ä‘Ã n bÃ !
        r   z4Female weeping must NOT trigger male tears violationN©r   r   r   )r    Úfemale_sampleÚ
violationss      r'   Ú9test_f3_b01_female_tears_preserved_without_false_positivezMTestTier2Boundaries.test_f3_b01_female_tears_preserved_without_false_positive•   s=   € ðˆõ
 1°Ñ?Ô?ˆ
Ø×Ò�˜Z™œ¨!Ð-cÑdÔdÐdÐdÐdr)   c                 óp   — d}t          |¦  «        }|                      t          |¦  «        dd¦  «         dS )zMF3.B2: Poetic/narrator verses on tears do not flag male character violations.u    
        **NGÆ¯á»œI DáºªN CHUYá»†N (V.O.)**
        Tráº£i qua má»™t cuá»™c bá»ƒ dÃ¢u, nhá»¯ng Ä‘iá»�u trÃ´ng tháº¥y mÃ  Ä‘au Ä‘á»›n lÃ²ng rÆ¡i lá»‡...
        r   z>Narrator V.O. verses must not be attributed to male charactersNr™   )r    Únarration_sampler›   s      r'   Ú>test_f3_b02_figurative_tear_metaphors_in_narration_delineationzRTestTier2Boundaries.test_f3_b02_figurative_tear_metaphors_in_narration_delineationŸ   s?   € ðÐõ 1Ð1AÑBÔBˆ
Ø×Ò�˜Z™œ¨!Ð-mÑnÔnÐnÐnÐnr)   c                 óp   — d}t          |¦  «        }|                      t          |¦  «        dd¦  «         dS )z^F3.B3: Anti-Male-Tears validator detects uppercase, lowercase, and mixed-case crying keywords.uE   **KIM TRá»ŒNG**: *(KHÃ“C Ná»¨C Ná»ž)*
**VÆ¯Æ NG QUAN**: *(RÆ¡i Lá»‡)*r   z%Must detect crying regardless of caseNr™   ©r    r–   r›   s      r'   Ú3test_f3_b03_case_insensitive_tear_keywords_coveragezGTestTier2Boundaries.test_f3_b03_case_insensitive_tear_keywords_coverage¨   s9   € àYˆÝ0°Ñ8Ô8ˆ
Ø×Ò�˜Z™œ¨!Ð-TÑUÔUÐUÐUÐUr)   c                 ón   ‡— dŠt          ˆfd„dD ¦   «         ¦  «        }|                      |d¦  «         dS )zLF3.B4: Detector catches crying keywords immediately adjacent to punctuation.u9   KIM TRá»ŒNG: (nÆ°á»›c_máº¯t? KhÃ´ng, rÆ¡i.lá»‡! khÃ³c...)c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S ©NrR   )rT   Úkwr–   s     €r'   ú	<genexpr>z]TestTier2Boundaries.test_f3_b04_punctuation_adjacent_crying_cues_detection.<locals>.<genexpr>²   s0   øè è € ÐXÐX°�r˜VŸ\š\™^œ^Ð+ÐXÐXÐXÐXÐXÐXr)   )u   khÃ³cu	   rÆ¡i.lá»‡u   nÆ°á»›cz1Must detect keywords with surrounding punctuationN)Úanyr5   )r    Úkw_foundr–   s     @r'   Ú6test_f3_b04_punctuation_adjacent_crying_cues_detectionzJTestTier2Boundaries.test_f3_b04_punctuation_adjacent_crying_cues_detection®   sF   ø€ àLˆåÐXÐXÐXÐXÐ6WÐXÑXÔXÑXÔXˆØ�Š˜Ð"UÑVÔVÐVÐVÐVr)   c                 óp   — d}t          |¦  «        }|                      t          |¦  «        dd¦  «         dS )z?F3.B5: Action beat validator flags mixed cues (stoic + crying).uN   **Tá»ª Háº¢I**: *(CÆ¡ máº·t Ä‘anh láº¡i nhÆ°ng ngáº¥n lá»‡ trong khÃ³e máº¯t)*r   z,Must flag even when mixed with stoic phrasesNr™   r¡   s      r'   Ú-test_f3_b05_composite_male_actions_validationzATestTier2Boundaries.test_f3_b05_composite_male_actions_validationµ   s9   € àaˆÝ0°Ñ8Ô8ˆ
Ø×Ò�˜Z™œ¨!Ð-[Ñ\Ô\Ð\Ð\Ð\r)   c                 ó  — t           dz  }|                      |                     ¦   «         ¦  «         |                     d¬¦  «        }t	          j        d|¦  «        }|                      t          |¦  «        dd¦  «         dS )zCF4.B1: Project Bible registers 32 characters in CHARACTER_BIBLE.md.úCHARACTER_BIBLE.mdúutf-8©Úencodingz###\s*\d+[\.\:]\s*([^\n]+)é   z*Character bible must enumerate full rosterN)r   r5   ÚexistsÚ	read_textr   Úfindallr   r   )r    Ú
bible_pathr&   Úfound_charss       r'   Ú1test_f4_b01_bible_character_coverage_completenesszETestTier2Boundaries.test_f4_b01_bible_character_coverage_completeness¾   sz   € åÐ!5Ñ5ˆ
Ø�Š˜
×)Ò)Ñ+Ô+Ñ,Ô,Ð,Ø×&Ò&°Ð&Ñ8Ô8ˆå”jÐ!>ÀÑHÔHˆØ×Ò¥ KÑ 0Ô 0°"Ð6bÑcÔcÐcÐcÐcr)   c                 ó¢   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )zGF4.B2: Validator raises error if bible text has zero acting guidelines.r…   r,   c                 ó\   — t          t          j        d| t          j        ¦  «        ¦  «        S )Nu@   (acting|diá»…n xuáº¥t|quy chuáº©n biá»ƒu cáº£m|micro-expression)rc   ©r…   s    r'   Úcheck_acting_guidelines_presentzpTestTier2Boundaries.test_f4_b02_missing_acting_directive_validator_flag.<locals>.check_acting_guidelines_presentÉ   s$   € Ý�œ	Ð"eÐgkÕmoÔmzÑ{Ô{Ñ|Ô|Ð|r)   u4   VÄƒn báº£n chá»‰ mÃ´ táº£ bá»‘i cáº£nh vÃ  vÅ© khÃ­.u/   Quy chuáº©n diá»…n xuáº¥t vÃ  micro-expression.N©rL   r4   r6   r5   )r    r¼   s     r'   Ú3test_f4_b02_missing_acting_directive_validator_flagzGTestTier2Boundaries.test_f4_b02_missing_acting_directive_validator_flagÇ   sp   € ð	}µ#ð 	}½$ð 	}ð 	}ð 	}ð 	}à×ÒÐ8Ð8Ð9oÑpÔpÑqÔqÐqØ�ŠÐ7Ð7Ð8iÑjÔjÑkÔkÐkÐkÐkr)   c                 óâ   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}|                     ¦   «         }|                      t          |¦  «        dd¦  «         dS )zGF4.B3: Acting directives section must contain >= 100 substantive words.r®   r¯   r°   r   iô  z.Bible must contain extensive directive contentN)r   r³   r´   r   r   r   )r    r¶   r&   Úwordss       r'   Ú-test_f4_b03_empty_directive_content_detectionzATestTier2Boundaries.test_f4_b03_empty_directive_content_detectionÎ   sl   € åÐ!5Ñ5ˆ
Ø<F×<MÒ<MÑ<OÔ<OÐW�*×&Ò&°Ð&Ñ8Ô8Ð8ÐUWˆØ—’‘”ˆØ×Ò¥ E¡
¤
¨CÐ1aÑbÔbÐbÐbÐbr)   c                 óD  ‡— t           dz  }t           dz  }|                     ¦   «         r|                     d¬¦  «        nd|                     ¦   «         r|                     d¬¦  «        ndz   Št          ˆfd„dD ¦   «         ¦  «        }|                      |d¦  «         d	S )
zBF4.B4: Bible enumerates prohibited melodramatic theatrical habits.zMASTER_BIBLE.mdr®   r¯   r°   r   c              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r¥   rR   )rT   ÚwÚcombineds     €r'   r§   zRTestTier2Boundaries.test_f4_b04_prohibited_acting_keywords_list.<locals>.<genexpr>Û   s2   øè è € ÐnÐn¸˜q H§N¢NÑ$4Ô$4Ð4ÐnÐnÐnÐnÐnÐnr)   )u   trÃ¡nhu   khÃ´ngu   cáº¥mu   tuyá»‡t Ä‘á»‘iz/Bible must delineate prohibited acting patternsN)r   r³   r´   r¨   r5   )r    Úmaster_bibleÚ
char_bibleÚhas_prohibitionsrÅ   s       @r'   Ú+test_f4_b04_prohibited_acting_keywords_listz?TestTier2Boundaries.test_f4_b04_prohibited_acting_keywords_listÕ   s·   ø€ å Ð#4Ñ4ˆÝÐ!5Ñ5ˆ
Ø@L×@SÒ@SÑ@UÔ@UÐ]�L×*Ò*°GÐ*Ñ<Ô<Ð<Ð[]Ø>H×>OÒ>OÑ>QÔ>QÐY�J×(Ò(°'Ð(Ñ:Ô:Ð:ÐWYñ[ˆåÐnÐnÐnÐnÐ>mÐnÑnÔnÑnÔnÐØ�ŠÐ(Ð*[Ñ\Ô\Ð\Ð\Ð\r)   c                 óÎ   — t           dz  }|                     ¦   «         r|                     d¬¦  «        nd}|                      d|d¦  «         |                      d|d¦  «         d	S )
ug   F4.B5: Bible contrasts young male (Kim Trá»�ng 18yo) vs elder male (VÆ°Æ¡ng Ã”ng 55yo) acting demeanor.r®   r¯   r°   r   Ú18u,   Bible must address 18yo Kim Trá»�ng demeanorÚ55u-   Bible must address 55yo VÆ°Æ¡ng Ã”ng demeanorN)r   r³   r´   r•   )r    r¶   r&   s      r'   Ú1test_f4_b05_age_appropriate_acting_rules_contrastzETestTier2Boundaries.test_f4_b05_age_appropriate_acting_rules_contrastÞ   sn   € åÐ!5Ñ5ˆ
Ø<F×<MÒ<MÑ<OÔ<OÐW�*×&Ò&°Ð&Ñ8Ô8Ð8ÐUWˆØ�Š�d˜GÐ%SÑTÔTÐTØ�Š�d˜GÐ%TÑUÔUÐUÐUÐUr)   c                 óV  — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         d	S )
zQF5.B1: Validator rejects resolutions that do not meet 720p sweet spot (1280x720).Úres_strr,   c                 ó
   — | dv S )N)Ú1280x720Ú720x1280r.   )rÏ   s    r'   Úvalidate_start_frame_resz^TestTier2Boundaries.test_f5_b01_invalid_resolution_rejection.<locals>.validate_start_frame_resê   s   € ØÐ6Ð6Ð6r)   rÑ   rÒ   Ú	1920x1080Ú640x480ÚinvalidNrK   )r    rÓ   s     r'   Ú(test_f5_b01_invalid_resolution_rejectionz<TestTier2Boundaries.test_f5_b01_invalid_resolution_rejectionè   sÆ   € ð	7­cð 	7µdð 	7ð 	7ð 	7ð 	7à�ŠÐ0Ð0°Ñ<Ô<Ñ=Ô=Ð=Ø�ŠÐ0Ð0°Ñ<Ô<Ñ=Ô=Ð=Ø×ÒÐ1Ð1°+Ñ>Ô>Ñ?Ô?Ð?Ø×ÒÐ1Ð1°)Ñ<Ô<Ñ=Ô=Ð=Ø×ÒÐ1Ð1°)Ñ<Ô<Ñ=Ô=Ð=Ð=Ð=r)   c                 óð   — dt           dt          fd„}|                       |dddœ¦  «        ¦  «         |                       |dddœ¦  «        ¦  «         |                       |dddœ¦  «        ¦  «         dS )	zWF5.B2: Validator detects prompts specifying character but missing environmental anchor.Úprompt_dictr,   c                 óî   — |                       dd¦  «        }|                       dd¦  «        }t          |                     ¦   «         ¦  «        dk    o$t          |                     ¦   «         ¦  «        dk    S )NÚcharacter_anchorr   Úenvironment_anchorr   )rZ   r   r   )rÙ   ÚcharÚenvs      r'   Úhas_both_anchorsz^TestTier2Boundaries.test_f5_b02_missing_environment_anchor_detection.<locals>.has_both_anchorsô   s_   € Ø—?’?Ð#5°rÑ:Ô:ˆDØ—/’/Ð"6¸Ñ;Ô;ˆCÝ�t—z’z‘|”|Ñ$Ô$ qÒ(ÐA­S°·²±´Ñ-=Ô-=ÀÒ-AÐAr)   u   ThÃºy Kiá»�uu   Suá»‘i nhá»�)rÛ   rÜ   r   N)r   r4   r5   r6   )r    rß   s     r'   Ú0test_f5_b02_missing_environment_anchor_detectionzDTestTier2Boundaries.test_f5_b02_missing_environment_anchor_detectionò   s«   € ð	B­$ð 	Bµ4ð 	Bð 	Bð 	Bð 	Bð 	�ŠÐ(Ð(¸nÐdrÐ)sÐ)sÑtÔtÑuÔuÐuØ×ÒÐ)Ð)¸~ÐegÐ*hÐ*hÑiÔiÑjÔjÐjØ×ÒÐ)Ð)¸rÐYgÐ*hÐ*hÑiÔiÑjÔjÐjÐjÐjr)   c                 óN   — d\  }}||z  }d}|                       ||d¬¦  «         dS )z9F5.B3: Verify 1280x720 matches exactly 16:9 aspect ratio.)i   iÐ  gÇqÇqü?é   )ÚplacesN)ÚassertAlmostEqual)r    rÄ   ÚhÚratioÚexpecteds        r'   Ú)test_f5_b03_aspect_ratio_consistency_16_9z=TestTier2Boundaries.test_f5_b03_aspect_ratio_consistency_16_9ü   s:   € à‰ˆˆ1Ø�A‘ˆØˆØ×Ò˜u h°qÐÑ9Ô9Ð9Ð9Ð9r)   c                 ó†   — ddl m}  |di d¬¦  «        }|                      |du pt          |t          ¦  «        ¦  «         dS )zOF5.B4: Production runner handles nonexistent explicit start frame path cleanly.r   ©Úresolve_start_framerH   znonexistent_frame_12345.png©Úexplicit_frameN©Úproduction_orchestratorrë   r5   Ú
isinstancerL   ©r    rë   Úress      r'   Ú1test_f5_b04_nonexistent_start_frame_path_handlingzETestTier2Boundaries.test_f5_b04_nonexistent_start_frame_path_handling  sX   € à?Ð?Ð?Ð?Ð?Ð?Ø!Ð!Ð"7¸ÐLiÐjÑjÔjˆØ�Š˜˜t˜Ð;¥z°#µsÑ';Ô';Ñ<Ô<Ð<Ð<Ð<r)   c                 óð   — dt           dt          t                    fd„}|                      t           |d¦  «        ¦  «        d¦  «         |                      t           |d¦  «        ¦  «        d¦  «         dS )	zPF5.B5: Scanner flags prompts with ambiguous motion risking reverse optical flow.Úpromptr,   c                 ó(   ‡ — g d¢}ˆ fd„|D ¦   «         S )N)u   bay lÃ¹iu   Ä‘i lÃ¹iÚmoonwalku   bay ngÆ°á»£cc                 ó@   •— g | ]}|‰                      ¦   «         v ¯|‘ŒS r.   rR   )rT   rÄ   rõ   s     €r'   rV   z{TestTier2Boundaries.test_f5_b05_forbidden_motion_phrases_scanner.<locals>.scan_dangerous_motion_phrases.<locals>.<listcomp>  s+   ø€ Ð@Ð@Ð@˜!¨A°·²±´Ð,?Ð,?�AÐ,?Ð,?Ð,?r)   r.   )rõ   Ú	forbiddens   ` r'   Úscan_dangerous_motion_phraseszgTestTier2Boundaries.test_f5_b05_forbidden_motion_phrases_scanner.<locals>.scan_dangerous_motion_phrases  s(   ø€ ØLÐLÐLˆIØ@Ð@Ð@Ð@˜yÐ@Ñ@Ô@Ð@r)   u   Ä�Ã n Ã©n bay ngÆ°á»£c chiá»�ur   u)   Ä�Ã n Ã©n bay lÆ°á»›t vá»� phÃ­a trÆ°á»›cr   N)rL   r   r   r   )r    rú   s     r'   Ú,test_f5_b05_forbidden_motion_phrases_scannerz@TestTier2Boundaries.test_f5_b05_forbidden_motion_phrases_scanner	  sŽ   € ð	Aµ#ð 	A½$½s¼)ð 	Að 	Að 	Að 	Að 	×Ò�Ð:Ð:Ð;[Ñ\Ô\Ñ]Ô]Ð_`ÑaÔaÐaØ×Ò�Ð:Ð:Ð;fÑgÔgÑhÔhÐjkÑlÔlÐlÐlÐlr)   c                 ó¢   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )zUF6.B1: Pure instrumental music shots without dialogue are exempt from lip-sync guard.Úaudio_promptr,   c                 ó:   ‡ — t          ˆ fd„dD ¦   «         ¦  «        S )Nc              3   óD   •K  — | ]}|‰                      ¦   «         v V — Œd S r¥   rR   )rT   rÄ   rý   s     €r'   r§   zpTestTier2Boundaries.test_f6_b01_pure_instrumental_shots_exemption.<locals>.requires_lip_guard.<locals>.<genexpr>  s4   øè è € Ð^Ð^°Q�q˜L×.Ò.Ñ0Ô0Ð0Ð^Ð^Ð^Ð^Ð^Ð^r)   )u   tá»« xau   ngoÃ i khungzv.o.)r¨   )rý   s   `r'   Úrequires_lip_guardz]TestTier2Boundaries.test_f6_b01_pure_instrumental_shots_exemption.<locals>.requires_lip_guard  s'   ø€ ÝÐ^Ð^Ð^Ð^Ð:]Ð^Ñ^Ô^Ñ^Ô^Ð^r)   u.   Tiáº¿ng Ä‘Ã n tá»³ bÃ  Ä‘á»™c táº¥u rÃ©o ráº¯tu-   Tiáº¿ng Kiá»�u gá»�i tá»« xa: ChÃ ng Kim Æ¡i!Nr½   )r    r   s     r'   Ú-test_f6_b01_pure_instrumental_shots_exemptionzATestTier2Boundaries.test_f6_b01_pure_instrumental_shots_exemption  sp   € ð	_­Sð 	_µTð 	_ð 	_ð 	_ð 	_à×ÒÐ+Ð+Ð,\Ñ]Ô]Ñ^Ô^Ð^Ø�ŠÐ*Ð*Ð+ZÑ[Ô[Ñ\Ô\Ð\Ð\Ð\r)   c                 ó`   — ddddœ}|d         |d         k    }|                       |¦  «         dS )zOF6.B2: Handler distinguishes between on-screen speaker and off-screen listener.u   Kim Trá»�ngu   ThÃºy Kiá»�u (off-screen)u!   Tiáº¿ng Kiá»�u gá»�i ngoÃ i khung)Úon_screen_characterÚspeakerÚaudior  r  N)r5   )r    Úsample_shotÚshould_lock_lipss      r'   Ú*test_f6_b02_mixed_on_off_dialogue_handlingz>TestTier2Boundaries.test_f6_b02_mixed_on_off_dialogue_handling  sJ   € ð $1Ø2Ø8ð
ð 
ˆð (Ð(=Ô>À+ÈiÔBXÒXÐØ�ŠÐ(Ñ)Ô)Ð)Ð)Ð)r)   c                 ó²   — d}t          j        dt           j        ¦  «        }|                      t	          |                     |¦  «        ¦  «        ¦  «         dS )zHF6.B3: Lip-guard regex matches despite arbitrary whitespace or newlines.uU      [KHÃ“A   KHáº¨U   HÃŒNH  Báº®T   BUá»˜C]: 
  Ä�Ã´i mÃ´i khÃ©p cháº·t tá»± nhiÃªn  u   khÃ©p\s*cháº·t|lips\s*closedNr9   )r    Úclauser>   s      r'   Ú0test_f6_b03_whitespace_padded_lip_guard_matchingzDTestTier2Boundaries.test_f6_b03_whitespace_padded_lip_guard_matching&  sG   € àiˆÝ”*Ð<½b¼mÑLÔLˆØ�Š�˜WŸ^š^¨FÑ3Ô3Ñ4Ô4Ñ5Ô5Ð5Ð5Ð5r)   c                 óN   — dddœ}d|d         v }|                       |¦  «         dS )zBF6.B4: On-screen speaker shots should NOT have closed lips clause.u=   ThÃºy Kiá»�u máº¥p mÃ¡y mÃ´i Ä‘á»�c cÃ¢u thÆ¡ tuyá»‡t má»‡nh.Ú	on_screen)Úmotion_promptÚtypeu   khÃ©p cháº·tr  N)r6   )r    Úon_screen_shotÚhas_closed_lipss      r'   Ú/test_f6_b04_on_screen_speaking_not_over_guardedzCTestTier2Boundaries.test_f6_b04_on_screen_speaking_not_over_guarded,  sB   € ð ]Øð
ð 
ˆð )¨N¸?Ô,KÐKˆØ×Ò˜Ñ)Ô)Ð)Ð)Ð)r)   c                 ó¢   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )z9F6.B5: Validator flags partially truncated guard clauses.r…   r,   c                 ó   — d| v od| v pd| v S )Nu   khÃ©pu   cháº·tu   mÃ´ir.   r»   s    r'   Úis_full_guardzWTestTier2Boundaries.test_f6_b05_truncated_guard_clause_detection.<locals>.is_full_guard7  s   € Ø˜d�?ÐK¨°DÐ(8Ð(J¸FÀd¸NÐKr)   u'   [KHÃ“A KHáº¨U HÃŒNH]: MÃ´i khÃ©p cháº·tu   [KHÃ“A KHáº¨U HÃŒNH]:NrK   )r    r  s     r'   Ú,test_f6_b05_truncated_guard_clause_detectionz@TestTier2Boundaries.test_f6_b05_truncated_guard_clause_detection5  sl   € ð	L¥ð 	L­ð 	Lð 	Lð 	Lð 	Là�Š˜˜Ð&OÑPÔPÑQÔQÐQØ×Ò˜˜Ð'>Ñ?Ô?Ñ@Ô@Ð@Ð@Ð@r)   c                 ó  — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )	zDF7.B1: Validator flags shot IDs with leading or trailing whitespace.rB   r,   c                 ó:   — | |                       ¦   «         k    od| vS )Nú )r   )rB   s    r'   Úis_clean_shot_idzZTestTier2Boundaries.test_f7_b01_whitespace_in_shot_ids_rejection.<locals>.is_clean_shot_idA  s   € Ø˜gŸmšm™oœoÒ-ÐD°#¸WÐ2DÐDr)   rH   z ep01_scene01_shot01zep01_scene01_shot01 zep01_scene01 shot01NrK   )r    r  s     r'   Ú,test_f7_b01_whitespace_in_shot_ids_rejectionz@TestTier2Boundaries.test_f7_b01_whitespace_in_shot_ids_rejection?  s²   € ð	E¥cð 	E­dð 	Eð 	Eð 	Eð 	Eà�ŠÐ(Ð(Ð)>Ñ?Ô?Ñ@Ô@Ð@Ø×ÒÐ)Ð)Ð*@ÑAÔAÑBÔBÐBØ×ÒÐ)Ð)Ð*@ÑAÔAÑBÔBÐBØ×ÒÐ)Ð)Ð*?Ñ@Ô@ÑAÔAÐAÐAÐAr)   c                 ó¸   ‡— g d¢Št          ˆfd„‰D ¦   «         ¦  «        }|                      t          |¦  «        d¦  «         |                      d|¦  «         dS )z;F7.B2: Validator detects duplicate shot IDs in prompt list.)rH   Úep01_scene01_shot02rH   c                 óF   •— g | ]}‰                      |¦  «        d k    ¯|‘ŒS )r   )Úcount)rT   ÚxÚ	shot_lists     €r'   rV   zOTestTier2Boundaries.test_f7_b02_duplicate_shot_id_detection.<locals>.<listcomp>K  s/   ø€ ÐIÐIÐI °)·/²/À!Ñ2DÔ2DÀqÒ2HÐ2H˜!Ð2HÐ2HÐ2Hr)   r   rH   N)Úsetr   r   r•   )r    Ú
duplicatesr!  s     @r'   Ú'test_f7_b02_duplicate_shot_id_detectionz;TestTier2Boundaries.test_f7_b02_duplicate_shot_id_detectionH  sg   ø€ àYÐYÐYˆ	ÝÐIÐIÐIÐI YÐIÑIÔIÑJÔJˆ
Ø×Ò�˜Z™œ¨!Ñ,Ô,Ð,Ø�ŠÐ+¨ZÑ8Ô8Ð8Ð8Ð8r)   c                 óŠ   — d}	 t          j        |¦  «         |                      d¦  «         dS # t           j        $ r Y dS w xY w)zIF7.B3: JSON loader handles malformed JSON string without unhandled crash.z'{"motion_prompts": {"shot01": "test",}}zShould raise JSONDecodeErrorN)ÚjsonÚloadsÚfailÚJSONDecodeError)r    Ú	corrupteds     r'   Ú,test_f7_b03_corrupted_json_syntax_resiliencez@TestTier2Boundaries.test_f7_b03_corrupted_json_syntax_resilienceO  sZ   € à=ˆ	ð	ÝŒJ�yÑ!Ô!Ð!Ø�IŠIÐ4Ñ5Ô5Ð5Ð5Ð5øÝÔ#ð 	ð 	ð 	ØˆDˆDð	øøøs   „)/ ¯AÁAc                 ó˜   — dddœddidœ}dt           dt          t                   fd	„} ||¦  «        }|                      |d
g¦  «         dS )z;F7.B4: Validator catches single prompt missing audio guard.Úoku'   Tuyá»‡t Ä‘á»‘i khÃ´ng sinh nháº¡c ná»�n)ÚmotionÚaudio_guardr.  zmissing guard)rq   rr   Úp_dictr,   c                 ó>   — d„ |                       ¦   «         D ¦   «         S )Nc                 ól   — g | ]1\  }}d t          j        |d¬¦  «                             ¦   «         v¯/|‘Œ2S )u'   tuyá»‡t Ä‘á»‘i khÃ´ng sinh nháº¡c ná»�nF)Úensure_ascii)r&  ÚdumpsrS   )rT   rU   Úvs      r'   rV   zfTestTier2Boundaries.test_f7_b04_missing_audio_guard_flagging.<locals>.check_guards.<locals>.<listcomp>_  s~   € ð  Pð  Pð  P™$˜!˜QÐ4]ÕeiÔeoÐpqð  AFð  fGñ  fGô  fG÷  fMò  fMñ  fOô  fOð  5Oð  5O�Að  5Oð  5Oð  5Or)   )r~   )r0  s    r'   Úcheck_guardszRTestTier2Boundaries.test_f7_b04_missing_audio_guard_flagging.<locals>.check_guards^  s0   € ð Pð  P &§,¢,¡.¤.ð  Pñ  Pô  Pð  Pr)   rr   N)r   r   rL   r   )r    Úpromptsr6  Úmissings       r'   Ú(test_f7_b04_missing_audio_guard_flaggingz<TestTier2Boundaries.test_f7_b04_missing_audio_guard_flaggingX  s}   € ð "&Ð6_Ð`Ð`Ø Ð1ð
ð 
ˆð	P¥ð 	P­$­s¬)ð 	Pð 	Pð 	Pð 	Pà�,˜wÑ'Ô'ˆØ×Ò˜ 8 *Ñ-Ô-Ð-Ð-Ð-r)   c                 óP   — d}|                       t          |¦  «        d¦  «         dS )zIF7.B5: Prompt validator flags prompts exceeding 2000 character API limit.ÁÄ	  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiÐ  N)ÚassertGreaterr   )r    Úoversized_prompts     r'   Ú4test_f7_b05_extremely_long_prompt_truncation_warningzHTestTier2Boundaries.test_f7_b05_extremely_long_prompt_truncation_warningc  s-   € à%ÐØ×Ò�3Ð/Ñ0Ô0°$Ñ7Ô7Ð7Ð7Ð7r)   c                 ó|   — ddl m}  |¦   «         }|                     g d¦  «        }|                      |¦  «         dS )zEF8.B1: stitch_with_audio_crossfade returns False on empty video list.r   ©ÚAudioContinuityEnginez
output.mp4N)Úaudio_continuity_enginerA  Ústitch_with_audio_crossfader6   )r    rA  ÚengineÚresults       r'   Ú+test_f8_b01_zero_length_input_list_handlingz?TestTier2Boundaries.test_f8_b01_zero_length_input_list_handlingk  sQ   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ×3Ò3°B¸ÑEÔEˆØ×Ò˜Ñ Ô Ð Ð Ð r)   c                 óŒ   — dg}g }t          |¦  «        dk    r|                     d¦  «         |                      d|¦  «         dS )zSF8.B2: Filter string generator handles single file without acrossfade filter crash.z	file1.mp4r   z[0:a]anull[a_faded]N)r   r   r•   )r    Úvideo_pathsÚfilter_partss      r'   Ú-test_f8_b02_single_file_crossfade_passthroughzATestTier2Boundaries.test_f8_b02_single_file_crossfade_passthroughr  sQ   € à"�mˆØˆÝˆ{ÑÔ˜qÒ Ð Ø×ÒÐ 5Ñ6Ô6Ð6Ø�ŠÐ+¨\Ñ:Ô:Ð:Ð:Ð:r)   c                 óÚ   — ddl m}  |¦   «         }|                     d¦  «        }|                      |d         ¦  «         |                      |                     dd¦  «        d¦  «         dS )zGF8.B3: inspect_shot_audio returns has_audio=False for nonexistent path.r   r@  znonexistent_path_xyz.mp4Ú	has_audioÚmax_volume_dbç     ÀXÀN)rB  rA  Úinspect_shot_audior6   r   rZ   )r    rA  rD  Úinfos       r'   Ú%test_f8_b03_nonexistent_file_handlingz9TestTier2Boundaries.test_f8_b03_nonexistent_file_handlingz  sy   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ×(Ò(Ð)CÑDÔDˆØ×Ò˜˜kÔ*Ñ+Ô+Ð+Ø×Ò˜Ÿš /°5Ñ9Ô9¸5ÑAÔAÐAÐAÐAr)   c                 ól   — ddl m}  |¦   «         }dddddœ}|                      |d         d¦  «         dS )	zXF8.B4: inspect_shot_audio assigns default volume -99.0 dB when file has 0 audio streams.r   r@  z
silent.mp4FrN  )ÚfilerL  rM  Úmean_volume_dbrT  N)rB  rA  r   )r    rA  rD  Úreps       r'   Ú&test_f8_b04_zero_audio_stream_handlingz:TestTier2Boundaries.test_f8_b04_zero_audio_stream_handling‚  sV   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆà#°%È%ÐchÐiÐiˆØ×Ò˜Ð-Ô.°Ñ6Ô6Ð6Ð6Ð6r)   c                 ó  — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )	z1F8.B5: Validator rejects crossfade duration <= 0.r+   r,   c                 ó   — | dk    S )Nr1   r.   r/   s    r'   Úvalidate_crossfadezbTestTier2Boundaries.test_f8_b05_negative_crossfade_duration_validation.<locals>.validate_crossfadeŒ  s   € Ø˜’9Ðr)   g      ð?g      à?r1   ç      ð¿Nr2   )r    rY  s     r'   Ú2test_f8_b05_negative_crossfade_duration_validationzFTestTier2Boundaries.test_f8_b05_negative_crossfade_duration_validationŠ  s¦   € ð	¥Eð 	­dð 	ð 	ð 	ð 	à�ŠÐ*Ð*¨3Ñ/Ô/Ñ0Ô0Ð0Ø�ŠÐ*Ð*¨3Ñ/Ô/Ñ0Ô0Ð0Ø×ÒÐ+Ð+¨CÑ0Ô0Ñ1Ô1Ð1Ø×ÒÐ+Ð+¨DÑ1Ô1Ñ2Ô2Ð2Ð2Ð2r)   c                 ó~   — ddl m}  |¦   «         }|                     ddd¦  «        }|                      |¦  «         dS )zHF9.B1: layer_bgm_with_ducking returns False if input files do not exist.r   r@  z	none1.mp4z	none2.mp3úout.mp4N)rB  rA  Úlayer_bgm_with_duckingr6   ©r    rA  rD  rò   s       r'   Ú.test_f9_b01_missing_ambience_fallback_gracefulzBTestTier2Boundaries.test_f9_b01_missing_ambience_fallback_graceful–  sS   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ×+Ò+¨K¸ÀiÑPÔPˆØ×Ò˜ÑÔÐÐÐr)   c                 óê  — t          j        dd¬¦  «        5 }|j        }ddd¦  «         n# 1 swxY w Y   	 ddlm}  |¦   «         }|                     |¦  «        }|                      |                     dd¦  «        ¦  «         t          j	         
                    |¦  «        rt          j        |¦  «         dS dS # t          j	         
                    |¦  «        rt          j        |¦  «         w w xY w)z;F9.B2: Audio inspector flags 0-byte audio file as unusable.z.wavF©ÚsuffixÚdeleteNr   r@  rL  )ÚtempfileÚNamedTemporaryFileÚnamerB  rA  rO  r6   rZ   ÚosÚpathr³   Úunlink)r    ÚtfÚtf_pathrA  rD  rP  s         r'   Ú*test_f9_b02_corrupted_audio_file_detectionz>TestTier2Boundaries.test_f9_b02_corrupted_audio_file_detection�  s2  € åÔ(°¸uÐEÑEÔEð 	ÈØ”gˆGð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð	#àEÐEÐEÐEÐEÐEØ*Ð*Ñ,Ô,ˆFØ×,Ò,¨WÑ5Ô5ˆDØ×Ò˜TŸXšX k°5Ñ9Ô9Ñ:Ô:Ð:åŒw�~Š~˜gÑ&Ô&ð #Ý”	˜'Ñ"Ô"Ð"Ð"Ð"ð#ð #ø�rŒw�~Š~˜gÑ&Ô&ð #Ý”	˜'Ñ"Ô"Ð"Ð"ð#øøøs   —+«/²/·AB< Â<6C2c                 óÞ   — dt           dt          fd„}|                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         |                       |d¦  «        ¦  «         dS )z=F9.B3: Ducking level is bounded between -24.0 dB and -6.0 dB.Úduck_dbr,   c                 ó"   — d| cxk    odk    nc S )Ng      8Àg      Àr.   )ro  s    r'   Úis_valid_duck_levelz[TestTier2Boundaries.test_f9_b03_ambience_volume_ducking_bounds.<locals>.is_valid_duck_level­  s$   € Ø˜GÐ+Ð+Ò+Ð+ tÒ+Ð+Ð+Ð+Ð+r)   g      ,Àg      IÀr1   Nr2   )r    rq  s     r'   Ú*test_f9_b03_ambience_volume_ducking_boundsz>TestTier2Boundaries.test_f9_b03_ambience_volume_ducking_bounds«  sˆ   € ð	,­ð 	,µ4ð 	,ð 	,ð 	,ð 	,à�ŠÐ+Ð+¨EÑ2Ô2Ñ3Ô3Ð3Ø×ÒÐ,Ð,¨UÑ3Ô3Ñ4Ô4Ð4Ø×ÒÐ,Ð,¨SÑ1Ô1Ñ2Ô2Ð2Ð2Ð2r)   c                 ó¦   — ddl }ddlm} |                     |j        ¦  «        }|                      d|¦  «         |                      d|¦  «         dS )zVF9.B4: FFmpeg command uses -stream_loop -1 for continuous looping of background track.r   Nr@  z-stream_loopz-1)ÚinspectrB  rA  Ú	getsourcer^  r•   ©r    rt  rA  Úsrcs       r'   Ú.test_f9_b04_looping_behavior_on_short_ambiencezBTestTier2Boundaries.test_f9_b04_looping_behavior_on_short_ambience³  sb   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô LÑMÔMˆØ�Š�n cÑ*Ô*Ð*Ø�Š�d˜CÑ Ô Ð Ð Ð r)   c                 óH   — dddœ}|                       |d         d¦  «         dS )zCF9.B5: Audio inspector checks stereo channel count (channels == 2).r   i€»  )ÚchannelsÚsample_raterz  N©r   )r    Úmock_streams     r'   Ú1test_f9_b05_stereo_channel_consistency_validationzETestTier2Boundaries.test_f9_b05_stereo_channel_consistency_validation»  s0   € à#$°UÐ;Ð;ˆØ×Ò˜ ZÔ0°!Ñ4Ô4Ð4Ð4Ð4r)   c                 óP   — ddl m}  |¦   «         }|                      |¦  «         dS )zRF10.B1: Loudnorm filter string includes standard target -14 even for quiet inputs.r   r@  N)rB  rA  ÚassertIsNotNone)r    rA  rD  s      r'   Ú+test_f10_b01_extreme_quiet_input_gain_limitz?TestTier2Boundaries.test_f10_b01_extreme_quiet_input_gain_limitÃ  s<   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ×Ò˜VÑ$Ô$Ð$Ð$Ð$r)   c                 ó8   — d}|                       |dd¦  «         dS )z<F10.B2: True peak limit ensures no clipping above -1.0 dBTP.rZ  r1   z,True peak must be strictly below 0 dBFS/dBTPN)Ú
assertLess)r    Útp_limits     r'   Ú+test_f10_b02_extreme_loud_input_attenuationz?TestTier2Boundaries.test_f10_b02_extreme_loud_input_attenuationÉ  s$   € àˆØ�Š˜ #Ð'UÑVÔVÐVÐVÐVr)   c                 óH   — dddœ}|                       |d         d¦  «         dS )z7F10.B3: Inspector returns -99.0 dB for silent material.rN  )rM  rT  rT  Nr|  )r    rU  s     r'   Ú(test_f10_b03_pure_silence_input_handlingz<TestTier2Boundaries.test_f10_b03_pure_silence_input_handlingÎ  s1   € à %¸Ð?Ð?ˆØ×Ò˜Ð-Ô.°Ñ6Ô6Ð6Ð6Ð6r)   c                 ó|   — ddl m}  |¦   «         }|                     dd¦  «        }|                      |¦  «         dS )zKF10.B4: normalize_loudness handles non-existent or truncated inputs safely.r   r@  znonexistent_short.mp4r]  N)rB  rA  Únormalize_loudnessr6   r_  s       r'   Ú/test_f10_b04_short_duration_material_resiliencezCTestTier2Boundaries.test_f10_b04_short_duration_material_resilienceÓ  sR   € àAÐAÐAÐAÐAÐAØ&Ð&Ñ(Ô(ˆØ×'Ò'Ð(?ÀÑKÔKˆØ×Ò˜ÑÔÐÐÐr)   c                 óz   — ddl }ddlm} |                     |j        ¦  «        }|                      d|¦  «         dS )zAF10.B5: Output encoding forces -ar 48000 for standard resampling.r   Nr@  Ú48000)rt  rB  rA  ru  r‰  r•   rv  s       r'   Ú0test_f10_b05_non_standard_sample_rate_resamplingzDTestTier2Boundaries.test_f10_b05_non_standard_sample_rate_resamplingÚ  sN   € àˆˆˆØAÐAÐAÐAÐAÐAØ×ÒÐ 5Ô HÑIÔIˆØ�Š�g˜sÑ#Ô#Ð#Ð#Ð#r)   c                 óÚ   — ddl m}m} ddlm}  ||¦  «        }|d         }	 d|d<   |                     dddi¬	¦  «        }|                      |j        d
¦  «         ||d<   dS # ||d<   w xY w)zMF11.B1: /api/generate returns 409 Conflict when status is already generating.r   )ÚappÚ	gen_state©Ú
TestClientÚstatusÚ
generatingz/api/generaterõ   Útest)r&  i™  N)Úserverr�  r�  Ústarlette.testclientr’  Úpostr   Ústatus_code)r    r�  r�  r’  ÚclientÚprev_statusrò   s          r'   Ú-test_f11_b01_concurrent_generate_conflict_409zATestTier2Boundaries.test_f11_b01_concurrent_generate_conflict_409ä  sª   € à)Ð)Ð)Ð)Ð)Ð)Ð)Ð)Ø3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ Ô)ˆð	.Ø".ˆI�hÑØ—+’+˜o°X¸vÐ4F�+ÑGÔGˆCØ×Ò˜Sœ_¨cÑ2Ô2Ð2à"-ˆI�hÑÐÐø +ˆI�hÑÐ-Ð-Ð-Ð-s   £9A# Á#A*c                 ó”   — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        d¦  «         dS )zIF11.B2: /api/episode/{filename} rejects path traversal attempts with 404.r   ©r�  r‘  z+/api/episode/../../windows/system32/cmd.exei”  N)r–  r�  r—  r’  rZ   r   r™  ©r    r�  r’  rš  rò   s        r'   Ú/test_f11_b02_invalid_episode_path_traversal_404zCTestTier2Boundaries.test_f11_b02_invalid_episode_path_traversal_404ñ  sc   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�jŠjÐFÑGÔGˆØ×Ò˜œ¨#Ñ.Ô.Ð.Ð.Ð.r)   c                 óz   — ddl m} ddlm} 	  |d¬¦  «         dS # |$ r |                      d¦  «         Y dS w xY w)z7F11.B3: Request schema validates prompt field presence.r   )ÚGenerateRequest)ÚValidationErrorzvalid prompt)rõ   z-Valid prompt should not raise ValidationErrorN)r–  r¢  Úpydanticr£  r(  )r    r¢  r£  s      r'   Ú/test_f11_b03_empty_prompt_generation_validationzCTestTier2Boundaries.test_f11_b03_empty_prompt_generation_validationù  s€   € à*Ð*Ð*Ð*Ð*Ð*Ø,Ð,Ð,Ð,Ð,Ð,ð	GØˆO >Ð2Ñ2Ô2Ð2Ð2Ð2øØð 	Gð 	Gð 	GØ�IŠIÐEÑFÔFÐFÐFÐFÐFð	Gøøøs   Ž œ:¹:c                 óL   — ddl }|                      |j        dk    d¦  «         dS )z@F11.B4: Verify build_library_data cache interval is 3.0 seconds.r   NT)r–  r   Ú_cache_time)r    r–  s     r'   Ú.test_f11_b04_library_cache_invalidation_windowzBTestTier2Boundaries.test_f11_b04_library_cache_invalidation_window  s/   € àˆˆˆØ×Ò˜Ô+¨qÒ0°$Ñ7Ô7Ð7Ð7Ð7r)   c                 óð   — ddl m} ddlm}  ||¦  «        }|                     d¦  «        }|                      |j        d¦  «         |                      |                     ¦   «         d         g ¦  «         dS )zOF11.B5: Querying nonexistent category returns empty list rather than 500 error.r   rž  r‘  z$/api/videos?category=nonexistent_xyzéÈ   ÚvideosN)r–  r�  r—  r’  rZ   r   r™  r&  rŸ  s        r'   Ú-test_f11_b05_nonexistent_video_category_queryzATestTier2Boundaries.test_f11_b05_nonexistent_video_category_query  s†   € àÐÐÐÐÐØ3Ð3Ð3Ð3Ð3Ð3Ø�˜C‘”ˆØ�jŠjÐ?Ñ@Ô@ˆØ×Ò˜œ¨#Ñ.Ô.Ð.Ø×Ò˜Ÿš™œ HÔ-¨rÑ2Ô2Ð2Ð2Ð2r)   c                 óR   — ddl m}  |d¦  «        }|                      |¦  «         dS )zAF12.B1: find_rendered_video returns None for nonexistent shot ID.r   ©Úfind_rendered_videoÚshot_nonexistent_999999N)rï   r¯  ÚassertIsNone)r    r¯  r5  s      r'   Ú%test_f12_b01_invalid_shot_id_handlingz9TestTier2Boundaries.test_f12_b01_invalid_shot_id_handling  s?   € à?Ð?Ð?Ð?Ð?Ð?ØÐÐ 9Ñ:Ô:ˆØ×Ò˜!ÑÔÐÐÐr)   c                 ó†   — ddl m}  |di d¬¦  «        }|                      |du pt          |t          ¦  «        ¦  «         dS )zJF12.B2: resolve_start_frame does not crash when explicit frame is missing.r   rê   rH   znonexistent.pngrì   Nrî   rñ   s      r'   Ú2test_f12_b02_missing_input_frame_graceful_fallbackzFTestTier2Boundaries.test_f12_b02_missing_input_frame_graceful_fallback  sX   € à?Ð?Ð?Ð?Ð?Ð?Ø!Ð!Ð"7¸ÐL]Ð^Ñ^Ô^ˆØ�Š˜˜t˜Ð;¥z°#µsÑ';Ô';Ñ<Ô<Ð<Ð<Ð<r)   c                 ó‚   — ddl m}  |di ¦  «        }|                      |du pt          |t          ¦  «        ¦  «         dS )z<F12.B3: Shot 01 of scene does not attempt to lookup Shot 00.r   rê   Úep01_scene02_shot01Nrî   rñ   s      r'   Ú2test_f12_b03_first_shot_of_scene_tail_frame_lookupzFTestTier2Boundaries.test_f12_b03_first_shot_of_scene_tail_frame_lookup  sR   € à?Ð?Ð?Ð?Ð?Ð?à!Ð!Ð"7¸Ñ<Ô<ˆØ�Š˜˜t˜Ð;¥z°#µsÑ';Ô';Ñ<Ô<Ð<Ð<Ð<r)   c                 ón   — ddl m}  |d¦  «        }|                      t          |¦  «        d¦  «         dS )zAF12.B4: get_shots_for_scene with empty string returns empty list.r   )Úget_shots_for_scener   N)rï   r¹  r   r   )r    r¹  r‚   s      r'   Ú!test_f12_b04_empty_scene_id_queryz5TestTier2Boundaries.test_f12_b04_empty_scene_id_query&  sF   € à?Ð?Ð?Ð?Ð?Ð?Ø#Ð# BÑ'Ô'ˆØ×Ò�˜U™œ QÑ'Ô'Ð'Ð'Ð'r)   c                 ó¼   — ddl }|                     t          j        t	          t
          dz  ¦  «        dgdddd¬¦  «        }|                      |j        d	¦  «         dS )
z2F12.B5: CLI exits with code 2 on unknown argument.r   Nzproduction_orchestrator.pyz--invalid-flag-xyzTr¯   Úreplace)Úcapture_outputr…   r±   Úerrorsr   )Ú
subprocessÚrunÚsysÚ
executablerL   r   r   Ú
returncode)r    r¿  Úprocs      r'   Ú/test_f12_b05_unsupported_cli_command_error_codezCTestTier2Boundaries.test_f12_b05_unsupported_cli_command_error_code,  sj   € àÐÐÐØ�~Š~ÝŒ^�S¥Ð0LÑ!LÑMÔMÐOcÐdØ d°WÀYð ñ 
ô 
ˆð 	×Ò˜œ¨!Ñ,Ô,Ð,Ð,Ð,r)   c                 ó¢  — ddl m} t          j        dd¬¦  «        5 }t	          |j        ¦  «        }ddd¦  «         n# 1 swxY w Y   	 |                      |                     ¦   «         j        d¦  «         | 	                    ¦   «         r| 
                    ¦   «          dS dS # | 	                    ¦   «         r| 
                    ¦   «          w w xY w)zBF13.B1: find_rendered_video filters out 0-byte or corrupted files.r   r®  z.mp4Frb  N)rï   r¯  re  rf  r   rg  r   ÚstatÚst_sizer³   rj  )r    r¯  rk  rl  s       r'   Ú&test_f13_b01_zero_byte_video_detectionz:TestTier2Boundaries.test_f13_b01_zero_byte_video_detection8  s  € à?Ð?Ð?Ð?Ð?Ð?ÝÔ(°¸uÐEÑEÔEð 	$ÈÝ˜2œ7‘m”mˆGð	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$ð	!à×Ò˜WŸ\š\™^œ^Ô3°QÑ7Ô7Ð7à�~Š~ÑÔð !Ø—’Ñ Ô Ð Ð Ð ð!ð !øˆw�~Š~ÑÔð !Ø—’Ñ Ô Ð Ð ð!øøøs   �>¾AÁAÁ
-B# Â#+Cc                 ó6   — d}|                       d|¦  «         dS )zLF13.B2: Concat filter handles scale normalization for differing resolutions.zM[0:v]scale=1280:720[v0];[1:v]scale=1280:720[v1];[v0][v1]concat=n=2:v=1:a=0[v]zscale=1280:720N©r•   ©r    Ú
cmd_filters     r'   Ú2test_f13_b02_mismatched_resolution_inputs_handlingzFTestTier2Boundaries.test_f13_b02_mismatched_resolution_inputs_handlingD  s"   € àdˆ
Ø�ŠÐ&¨
Ñ3Ô3Ð3Ð3Ð3r)   c                 ó6   — d}|                       d|¦  «         dS )zBF13.B3: Audio filter enforces 2 channels via aformat or aresample.z1aformat=channel_layouts=stereo:sample_rates=48000ÚstereoNrË  rÌ  s     r'   Ú2test_f13_b03_audio_channel_count_mismatch_handlingzFTestTier2Boundaries.test_f13_b03_audio_channel_count_mismatch_handlingI  s!   € àHˆ
Ø�Š�h 
Ñ+Ô+Ð+Ð+Ð+r)   c                 ó|   — dddigi}t          d„ |d         D ¦   «         ¦  «        }|                      |¦  «         dS )zPF13.B4: Validator detects if video file is missing either video or audio stream.ÚstreamsÚ
codec_typeÚvideoc              3   óH   K  — | ]}|                      d ¦  «        dk    V — ŒdS )rÔ  r  N)rZ   )rT   Úss     r'   r§   zTTestTier2Boundaries.test_f13_b04_truncated_video_stream_detection.<locals>.<genexpr>Q  s3   è è € ÐVÐV¸1˜Ÿš˜lÑ+Ô+¨wÒ6ÐVÐVÐVÐVÐVÐVr)   N)r¨   r6   )r    Ú
mock_proberL  s      r'   Ú-test_f13_b04_truncated_video_stream_detectionzATestTier2Boundaries.test_f13_b04_truncated_video_stream_detectionN  sO   € à <°Ð"9Ð!:Ð;ˆ
ÝÐVÐVÀ
È9Ô@UÐVÑVÔVÑVÔVˆ	Ø×Ò˜Ñ#Ô#Ð#Ð#Ð#r)   c                 óp   — ddl }ddlm} |                     |¦  «        }|                      d|¦  «         dS )zYF13.B5: FFmpeg invocation includes -y flag to safely overwrite or handle existing target.r   N)Úconcat_scene_shotsz"-y")rt  rï   rÛ  ru  r•   )r    rt  rÛ  rw  s       r'   Ú-test_f13_b05_export_path_collision_protectionzATestTier2Boundaries.test_f13_b05_export_path_collision_protectionT  sK   € àˆˆˆØ>Ð>Ð>Ð>Ð>Ð>Ø×ÒÐ 2Ñ3Ô3ˆØ�Š�f˜cÑ"Ô"Ð"Ð"Ð"r)   N)EÚ__name__Ú
__module__Ú__qualname__Ú__doc__r(   r7   r@   rM   r_   re   ro   rƒ   rŽ   r—   rœ   rŸ   r¢   rª   r¬   r¸   r¾   rÁ   rÉ   rÍ   r×   rà   rè   ró   rû   r  r  r  r  r  r  r$  r+  r9  r>  rF  rJ  rQ  rV  r[  r`  rm  rr  rx  r~  r�  r…  r‡  rŠ  r�  rœ  r   r¥  r¨  r¬  r²  r´  r·  rº  rÅ  rÉ  rÎ  rÑ  rÙ  rÜ  r.   r)   r'   r   r      s8  € € € € € ðð ð
Rð 
Rð 
Rð7ð 7ð 7ð^ð ^ð ^ðCð Cð Cðhð hð hðuð uð uð*ð *ð *ðYð Yð YðCð Cð Cð0ð 0ð 0ð"eð eð eðoð oð oðVð Vð VðWð Wð Wð]ð ]ð ]ðdð dð dðlð lð lðcð cð cð]ð ]ð ]ðVð Vð Vð>ð >ð >ðkð kð kð:ð :ð :ð=ð =ð =ðmð mð mð]ð ]ð ]ð	*ð 	*ð 	*ð6ð 6ð 6ð*ð *ð *ðAð Að AðBð Bð Bð9ð 9ð 9ðð ð ð	.ð 	.ð 	.ð8ð 8ð 8ð!ð !ð !ð;ð ;ð ;ðBð Bð Bð7ð 7ð 7ð3ð 3ð 3ðð ð ð#ð #ð #ð3ð 3ð 3ð!ð !ð !ð5ð 5ð 5ð%ð %ð %ðWð Wð Wð
7ð 7ð 7ð
ð ð ð$ð $ð $ð.ð .ð .ð/ð /ð /ðGð Gð Gð8ð 8ð 8ð
3ð 3ð 3ðð ð ð=ð =ð =ð=ð =ð =ð(ð (ð (ð-ð -ð -ð
!ð 
!ð 
!ð4ð 4ð 4ð
,ð ,ð ,ð
$ð $ð $ð#ð #ð #ð #ð #r)   r   Ú__main__)&rà  rh  rÁ  r   r&  re  ÚunittestÚpathlibr   Útypingr   r   r   r   r   Ú__file__ÚresolveÚparentÚ	TESTS_DIRÚBASE_DIRrL   ri  ÚinsertrY   r	   r
   r   r   r   r   r   r   r   r   ÚTestCaser   rÝ  Úmainr.   r)   r'   ú<module>rí     sã  ððð ð
 
€	€	€	Ø 
€
€
€
Ø 	€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ð Ð Ø 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3ð ˆD�‰NŒN×"Ò"Ñ$Ô$Ô+€	ØÔ€Ø€3€x�=„=˜œÐ Ð Ø„H‡O‚O�A�s�s˜8‘}”}Ñ%Ô%Ð%Ø€3€y�>„>˜œÐ!Ð!Ø„H‡O‚O�A�s�s˜9‘~”~Ñ&Ô&Ð&ðð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð{#ð {#ð {#ð {#ð {#˜(Ô+ñ {#ô {#ð {#ð| ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr)   