
    bi3N                       d Z ddlmZ ddlZddlZddlmZ ddlZ	ddl
Z
ddlmZmZ  ee      j                         j                   d   Zedz  dz  d	z  Zd
 Z e       Zej*                  Zej,                  Zej.                  Zej0                  Zej2                  Zej4                  Zej6                  Zej8                  Zej:                  Zej<                  Zej>                  ZdZ dZ!dZ"d"dZ#	 	 	 	 	 	 	 	 d#dZ$	 	 d$	 	 	 	 	 	 	 d%dZ%	 	 	 d&	 	 	 	 	 	 	 	 	 d'dZ&d(d)dZ'd*dZ(d*dZ)d*dZ*d*dZ+d*dZ,d*dZ-d*dZ.d*dZ/d*dZ0d*dZ1d*dZ2d*dZ3d*dZ4d*dZ5d*d Z6d*d!Z7y)+uH  IDS Phase 0.5 Lite Evaluator 회귀 테스트.

7+ 시나리오 (task-2446 Fix 4):
    1. L1 Contrast 정상 PASS / 그라데이션(분포 측정) PASS / 글리프 명도 단조 FAIL
    2. L2 Margin: 침범 0건 PASS / 침범 5건 FAIL
    3. L3 Hierarchy: 정상 비율 PASS / 헤딩=본문 동일 크기 FAIL (head/sub ratio<1.3)
    4. L4 Color Token: 4색 이내 PASS / off-token WARN / AI 퍼플 FAIL
    5. L5 Typography: Pretendard PASS / 시스템 fallback FAIL
    6. JSON Schema invalid input → SchemaValidationError
    7. mappingVersion 미일치 → SSotMismatchError
    )annotationsN)Path)Image	ImageDraw   scriptsidszlite_evaluator.pyc                 (   t         j                  j                  dt              } | | j                  t        dt               t         j                  j                  |       }|t        j                  d<   | j                  j                  |       |S )N_ids_lite_evaluatorz Cannot load lite_evaluator from )
	importlibutilspec_from_file_location
_LITE_PATHloaderRuntimeErrormodule_from_specsysmodulesexec_module)specmods     X/home/jay/workspace/.worktrees/task-2446-design/tests/scripts/ids/test_lite_evaluator.py_load_lite_moduler      sx    >>11zD |t{{*=j\JKK
..
)
)$
/C),CKK%&KKC J    i8  H   c                    t        |       ddt        t        t        dt        t        t        t        dddddd	d
dddg dddddddddg dddddddddg ddd ddddgd!	S )"u%   SSOT 적합 LayoutMeta 기본 구조.theme-fa-fintechzinsurance-fawidthheighttopbottomleftright         )baselinecolumnsguttersolid#F8F4EE)typedata	MainTitleheadline)x         r2   `   z#0B1E3F
Pretendard  333333?namerolebboxfontSizefill
fontFamily
fontWeight
lineHeightSubTitlesubhead)r2   ih  iX  P   @   z#1F1F1Fi  ?ActionButtoncta)r2   ip  i@  rD   ,   #FFFFFF)	
image_paththemePresettargetPersonamappingVersioncanvassafeAreagrid
background
components)strSSOT_MAPPING_VERSIONCANVAS_WCANVAS_HSAFE_PX)rK   s    r   
_base_metarY   A   s     *o)'.$9#wRYZ2<&	: $",!*!!	 #!+!*!!	 '+!*!!	+
) )r   c                
   d |D        \  }}}}t        fddD              }t        dt        |dz              }t        dt        |dz              }	t        dt        ||dz  z              }
t        |
      D ]O  }||	z   t        ||d|	z  z
  z  t        d	|
d	z
        z        z   }| j	                  |||	z   ||z   ||z   |	z
  g|
       Q ||dz  z   }| j	                  ||	z   |||z   |	z
  |t        d	|dz        z   g|
       y)um   bbox 내에 글리프-유사 패턴(가는 막대 strokes)을 그려 contrast 측정 가능 픽셀을 생성.c              3  2   K   | ]  }t        |        y wNint.0vs     r   	<genexpr>z#_draw_text_block.<locals>.<genexpr>w   s     'Q#a&'   c              3  `   K   | ]%  }t        j                  d       ||dz    d       ' yw)#      N)r^   lstrip)r`   ifill_hexs     r   rb   z#_draw_text_block.<locals>.<genexpr>x   s,     Q!S-a!a%8"=Qs   +.)r   rf      rf   g~jt?rk   g333333?r      r>   N)tuplemaxr^   range	rectangle)drawr<   rj   xywhfill_rgbstroke_wpadcolsri   cxcys     `           r   _draw_text_blockr}   p   s!    ($'JAq!QQyQQH1c!e)n%H
aQX
Cq#a8a<()*D4[ QWs1AG,s1dQh/??@@AGR(]AECK@xPQ 
Q!VBNN	
S"a!eck2Ax1}(=#=>X  r   c                    t        j                  dt        t        f|      }t	        j
                  |      }|r-|D ](  }|j                  dd      st        ||d   |d          * |j                  |        y)u,   단색 배경 + 텍스트 블록 PNG 합성.RGBisTextTr<   r>   N	r   newrV   rW   r   Drawgetr}   save)pathbg_hexrS   imgrr   cs         r   _render_solid_bg_imager      sj     ))EHh/
8C>>#D 	=AuuXt$ qy!F)<	= HHTNr   c           	        t        j                  t        t        dft         j                        }t        j
                  t        |dd d      t        |dd d      t        |dd d      g      }t        j
                  t        |dd d      t        |dd d      t        |dd d      g      }t        t              D ]M  }|t        dt        dz
        z  }|d|z
  z  ||z  z   j                  t         j                        ||ddddf<   O t        j                  |d      }	t        j                  |	      }
|r-|D ](  }|j                  d	d
      st        |
|d   |d          * |	j!                  |        y)uT   수직 그라데이션 배경 + 텍스트 PNG 합성. L1 분포 측정 시나리오.r   )dtyperl   rg         Nr   r   Tr<   r>   )npzerosrW   rV   uint8arrayr^   rp   ro   astyper   	fromarrayr   r   r   r}   r   )r   	start_hexend_hexrS   arrsert   tr   rr   r   s               r   _render_gradient_bg_imager      sS    ((Hh*"((
;C
#i!nb)3y1~r+BC	RSTUXZD[\]A
#galB'WQq\2)>GAaLRT@UVWA8_ >Ax!|$$QUa!e+33BHH=Aq!G> //#u
%C>>#D 	=AuuXt$ qy!F)<	= HHTNr   c                    t        j                  dt        t        fd      }t	        j
                  |      }|r-|D ](  }|j                  dd      st        ||d   |d          * |j                  |        y)u'   AI 퍼플(hue 280, sat 0.7) 배경 PNG.r   )   +      r   Tr<   r>   Nr   )r   rS   r   rr   r   s        r   _render_purple_bg_imager      sj     ))EHh/
@C>>#D 	=AuuXt$ qy!F)<	= HHTNr   c                   | dz  }t        |      }t        |d|d          t        j                  |      j	                  d      }t        j                  |      }t        d |d   D              }d |d   D        \  }}}}	|j                  ||||z   ||	z   gd	
       t        ||d   |d          |j                  |       t        j                  t        j                  |      j	                  d            }
t        |
|      }|j                  dv sJ d|j                          t        d |j                   d   D              }|d   dk\  sJ |       y)ua   정상 케이스: 어두운 텍스트(#0B1E3F) vs 밝은 배경(#F8F4EE) → p5 ≥ 4.5 → PASS.zl1_normal.pngr-   rS   r   rS   r   c              3  2   K   | ]  }|d    dk(  s|  ywr;   rH   N r`   r   s     r   rb   z/test_l1_contrast_normal_pass.<locals>.<genexpr>        CQ&	U0BqC   c              3  2   K   | ]  }t        |        y wr\   r]   r_   s     r   rb   z/test_l1_contrast_normal_pass.<locals>.<genexpr>        .Q#a&.rc   r<         ?   rm   r>      PASSWARNu   L1 정상 케이스 FAIL: c              3  2   K   | ]  }|d    dk(  s|  yw)r;   r1   Nr   )r`   ds     r   rb   z/test_l1_contrast_normal_pass.<locals>.<genexpr>   s       ai:6Mr   per_componentp5g      @N)rY   r   r   openconvertr   r   nextrq   r}   r   r   asarrayevaluate_l1_contrastverdictreasondetails)tmp_pathimg_pathmetar   rr   rH   rs   rt   ru   rv   r   resultheadline_details                r   test_l1_contrast_normal_passr      sH    /)HhD8I$|BTU
**X

&
&u
-C>>#D
C$|,C
CC.#f+.JAq!QNNAq!a%Q'lN;T3v;F4HHX
**UZZ)11%8
9C!#t,F>>--[1KFMM?/[[- >>/2 O 4 C'88'r   c           	        | dz  }t        |      }ddg dddddd	d
g|d<   t        |dd|d          t        j                  t	        j
                  |      j                  d            }t        ||d   d         }t        |      dkD  sJ dt        |              t        |      t        |      dz     }|dkD  sJ d| d       y)u   그라데이션 배경에서 글리프 픽셀별 contrast 분포가 발생해야 한다.

    p5(어두운 끝)와 p95(밝은 끝)가 달라야 분포 측정이 작동.
    zl1_gradient.pngHeadliner1   )r2   d   r4   r4   r5   #000000r6   r7   r8   r9   rS   rJ   )r   r   rS   r   r   r   u   glyph 픽셀 부족:    g      ?zp5=u    너무 낮음N)
rY   r   r   r   r   r   r   _glyph_pixel_contrastslensorted)r   r   r   r   	contrastsr   s         r   &test_l1_contrast_gradient_distributionr      s    
 ++HhD (&		
D %	 **UZZ)11%8
9C&sD,>q,ABIy>CI#8Y8H!II 
		3y>R/	0B8-s2$n--8r   c                v   | dz  }t        |      }|d   D ]  }d|d<   	 t        |d|d          t        j                  t	        j
                  |      j                  d            }t        ||      }|j                  dk(  sJ d	|j                          t        d
 |j                  xs dfD              sJ y)uM   배경(#888) vs 텍스트(#999) 같이 contrast 부족 → p5 < 4.5 → FAIL.z
l1_low.pngrS   z#999999r>   z#888888r   r   FAILu   contrast 부족인데 PASS: c              3  @   K   | ]  }d |j                         v   yw)contrastN)lowerr`   rs     r   rb   z=test_l1_contrast_low_glyph_brightness_fail.<locals>.<genexpr>  s     F1zQWWY&Fs    N)rY   r   r   r   r   r   r   r   r   r   anyr   r   r   r   r   r   r   s         r   *test_l1_contrast_low_glyph_brightness_failr      s     ,&HhD, &	8I$|BTU
**UZZ)11%8
9C!#t,F>>V#T'CFNNCS%TT#F1D"0EFFFFr   c                    | dz  }t        |      }t        ||d          t        |      }|j                  dk(  sJ |j                         |j                  d   dk(  sJ |j                  d   du sJ y	)
u1   모든 텍스트가 safe-area(72px) 안 → PASS.zl2_clean.pngrS   rS   r   violation_countr   ssot_alignedTN)rY   r   evaluate_l2_marginr   r   r   r   r   r   s       r   test_l2_margin_safe_area_cleanr     s{     .(HhD8\0BC%F>>V#3V^^3#>>+,111>>.)T111r   c                0   | dz  }t        |      }t        d      D cg c]  }d| ddd|dz  z   ddgd	d
dddd c}|d<   t        ||d          t        |      }|j                  dk(  sJ |j
                         |j
                  d   dk(  sJ yc c}w )u/   5개 컴포넌트가 safe-area 침범 → FAIL.zl2_violate.pngr   Badbody
   r   r3   <   rI   r   r6     ffffff?r9   rS   r   r   r   N)rY   rp   r   r   r   r   )r   r   r   ri   r   s        r   test_l2_margin_violations_failr     s     **HhD q  !Ia"fc2.&		
D 8\0BC%F>>V#3V^^3#>>+,111#s   Bc                    | dz  }t        |      }t        ||d          t        |      }|j                  dk(  sJ |j                         |j                  d   dk\  sJ y)u>   정상 비율: head=96, sub=64, head/sub=1.5 ≥ 1.3 → PASS.zl3_pass.pngrS   r   r   head_sub_ratiorF   N)rY   r   evaluate_l3_hierarchyr   r   r   s       r   test_l3_hierarchy_normal_passr   :  sd     -'HhD8\0BC"4(F>>V#3V^^3#>>*+s222r   c                f   | dz  }t        |      }d|d   d   d<   d|d   d   d<   |d   j                  ddg d	d
ddddd       t        ||d          t        |      }|j                  dk(  sJ |j
                         t        d |j                  xs dj                  d      D              sJ y)u   heading=subhead 동일 크기 → ratio<1.3 → FAIL.

    또한 absolute_min=40 위반(fontSize=20) 추가하여 FAIL 확정.
    zl3_fail.pngrE   rS   r   r=   rl   TooSmallcaption)r2   r7   r   r   r   r   r6   r   r   r9   r   r   c              3  $   K   | ]  }d |v  
 yw)absolute_minNr   r   s     r   rb   z6test_l3_hierarchy_head_eq_body_fail.<locals>.<genexpr>_  s     Mq~"Ms   r   ;N)	rY   appendr   r   r   r   r   r   splitr   s       r   #test_l3_hierarchy_head_eq_body_failr   E  s    
 -'HhD(*Dq*%(*Dq*%'&		
 8\0BC"4(F>>V#3V^^3#MV]]-@b,G,G,LMMMMr   c                X   | dz  }t        |      }t        ||d          t        j                  t	        j
                  |      j                  d            }t        ||      }|j                  dv sJ |j                         |j                  d   dk  sJ |j                  d   d	k  sJ y
)uE   단순 단색 배경 + 텍스트 → 색상 카운트 ≤ 4 → PASS.zl4_pass.pngrS   r   r   r   color_countrk   ai_purple_ratio皙?N)
rY   r   r   r   r   r   r   evaluate_l4_color_tokenr   r   r   r   r   r   r   s        r    test_l4_color_token_palette_passr   g  s     -'HhD8\0BC
**UZZ)11%8
9C$S$/F>>--=v~~=->>-(A--->>+,t333r   c                   | dz  }t        |      }|d   D ]  }|d   dk(  sd|d<    n t        |d|d          t        j                  t	        j
                  |      j                  d	            }t        ||      }|j                  d
   dk  sJ |j                  dk(  sJ d|j                          d|j                  d   v sJ y)uQ   themePreset 팔레트에 없는 형광 핑크 텍스트 → WARN (회귀 차단).z
l4_off.pngrS   r;   r1   z#FF00AAr>   z#F0F0F0r   r   r   rk   r   u    off-token이 PASS로 통과됨: off_token_fillsN)
rY   r   r   r   r   r   r   r   r   r   r   s         r   "test_l4_color_token_off_token_warnr   t  s     ,&HhD, V9
"!AfI 8I$|BTU
**UZZ)11%8
9C$S$/F>>-(A--->>V#X'GGW%XX#'89999r   c                Z   | dz  }t        |      }t        ||d          t        j                  t	        j
                  |      j                  d            }t        ||      }|j                  dk(  sJ |j                         |j                  d   dkD  sJ d|j                  xs d	v sJ y
)uJ   AI 퍼플 배경(hue 271, sat>0.5) 캔버스 100% → 10% 초과 → FAIL.zl4_purple.pngrS   r   r   r   r   r   u	   AI 퍼플r   N)rY   r   r   r   r   r   r   r   r   r   r   r   s        r   "test_l4_color_token_ai_purple_failr     s     /)HhDHl1CD
**UZZ)11%8
9C$S$/F>>V#3V^^3#>>+,t3336==.B///r   c                    | dz  }t        |      }d|d   d   d<   t        ||d          t        |      }|j                  dk(  sJ |j                         d|j                  d	   v sJ y
)u$   Pretendard + 정상 weight → PASS.zl5_pass.pngNoto Sans KRrS   rl   r?   r   r   r6   families_seenN)rY   r   evaluate_l5_typographyr   r   r   s       r   "test_l5_typography_pretendard_passr    sw     -'HhD*8Dq,'8\0BC#D)F>>V#3V^^3#6>>/::::r   c                   | dz  }t        |      }d|d   d   d<   d|d   d   d<   t        ||d          t        |      }|j                  d	k(  sJ |j                         t        d
 |j                  d   D              sJ y)u2   시스템 fallback(굴림체 등 banned) → FAIL.zl5_fail.pngu	   굴림체rS   r   r?   u	   바탕체rl   r   r   c              3  *   K   | ]  }d |d   v   yw)bannedr.   Nr   r_   s     r   rb   z:test_l5_typography_system_fallback_fail.<locals>.<genexpr>  s     Rx1V9$Rs   family_violationsN)rY   r   r  r   r   r   r   s       r   'test_l5_typography_system_fallback_failr
    s     -'HhD*5Dq,'*5Dq,'8\0BC#D)F>>V#3V^^3#Rfnn=P.QRRRRr   c                L   | dz  }t        |       t        |      dt        t        t        dt
        t
        t
        t
        dddiddg d	d
ddgd}t        j                  t              5 }t        |       ddd       dt        j                        v sJ y# 1 sw Y   #xY w)uA   필수 필드 누락 → SchemaValidationError → 진입 차단.zschema_fail.pngr   r   r!   r)   r&   Xr1   )r   r   r3   r   r5   r   )r:   r;   r<   r=   r>   )rK   rL   rN   rO   rP   rQ   rS   NrM   )r   rT   rU   rV   rW   rX   pytestraisesSchemaValidationErrorevaluatevalue)r   r   bad_metaexcs       r   &test_schema_invalid_input_blocks_entryr    s     ++H8$(m).$9#wRYZQ "+!
H$ 
,	- c#))n,,, s   ,BB#c                    | dz  }t        |       t        |      }d|d<   t        j                  t              5  t        |       ddd       y# 1 sw Y   yxY w)u;   themePreset enum 위반(theme-A) → SchemaValidationError.zschema_preset.pngztheme-ArL   N)r   rY   r  r  r  r  )r   r   r  s      r   test_schema_invalid_preset_enumr    sU     --H8$(#H'H]	,	-   s   AAc                ,   | dz  }t        |      }t        ||d          d|d<   t        j                  t              5 }t        |       ddd       dt        j                        v sJ t        t        |j                        v sJ y# 1 sw Y   @xY w)uL   mappingVersion이 SSOT_MAPPING_VERSION(v1.0)이 아니면 SSotMismatchError.zmv_fail.pngrS   r   zv9.9rN   N)	rY   r   r  r  SSotMismatchErrorr  rT   r  rU   )r   r   r   r  s       r   *test_mapping_version_mismatch_blocks_entryr    s     -'HhD8\0BC#D		(	) SS^###3syy>111 s   B

Bc                d   | dz  }t        |      }d|d   d   d<   t        j                  dt        t        fd      }t        j                  |      }t        d |d   D              }d	 |d
   D        \  }}}}	|j                  ||||z   ||	z   gd       |d   D ]  }
t        ||
d
   |
d           |j                  |       t        |      }t        |t              sJ t        |j                        dk(  sJ |j                   dv sJ |j"                         |j$                  d   t&        k(  sJ y)u4   정상 LayoutMeta → evaluate() 종합 결과 PASS.ze2e_pass.pngr  rS   rl   r?   r   )         c              3  2   K   | ]  }|d    dk(  s|  ywr   r   r   s     r   rb   z0test_evaluate_end_to_end_pass.<locals>.<genexpr>  r   r   c              3  2   K   | ]  }t        |        y wr\   r]   r_   s     r   rb   z0test_evaluate_end_to_end_pass.<locals>.<genexpr>   r   rc   r<   r   rm   r>   r   r   rN   N)rY   r   r   rV   rW   r   r   r   rq   r}   r   r  
isinstance
EvalResultr   itemsoverallfail_reasonslayout_meta_summaryrU   )r   r   r   r   rr   rH   rs   rt   ru   rv   r   r   s               r   test_evaluate_end_to_end_passr&    s:    .(HhD*8Dq,'
))EHh/
AC>>#D
C$|,C
CC.#f+.JAq!QNNAq!a%Q'lN;, 5qy!F)45HHXd^Ffj)))v||!!!>>--Bv/B/BB-%%&67;OOOOr   )rK   r   returndict)rr   zImageDraw.ImageDrawr<   zlist[float]rj   rT   r'  None)r-   N)r   r   r   rT   rS   list[dict] | Noner'  r)  )rJ   z#1A1A1AN)
r   r   r   rT   r   rT   rS   r*  r'  r)  r\   )r   r   rS   r*  r'  r)  )r   r   r'  r)  )8__doc__
__future__r   importlib.utilr   r   pathlibr   numpyr   r  PILr   r   __file__resolveparents
_REPO_ROOTr   r   _liter!  r  r  rU   r  r   r   r   r   r  r   rV   rW   rX   rY   r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r
  r  r  r  r&  r   r   r   <module>r6     s  
 #  
     
 (^##%--a0
)#e+.AA
	 	
33 ++ 11 >>11 -- 33 77 55 55  
,^

  
	2 $(
 " 
	$ $(	
  "	
 
.	"90!.HG*	22<3ND
4:&
0$
;
S$-8 
2$Pr   