§
    ¤~½j³8  ã                   ó>  — d Z ddlZddlZddlmZ  ee¦  «                             ¦   «         j        j        j        Zedz  dz  Z	e	dz  dz  dz  Z
e	dz  d	z  d
z  Ze	dz  d	z  dz  Ze	dz  dz  Ze	dz  Z G d„ dej        ¦  «        Zedk    r ej        ¦   «          dS dS )aR  
Unit tests for FreeExile Visceral Procedural Audio & Sound FX Engine (PoE2 spirit).
Verifies sfx_engine.js pure Web Audio API synthesis, multi-bus architecture,
visceral sound generation routines, gesture unlock, settings persistence,
native haptic synchronization, and complete integration in index.html, main.js, and combat_skills.js.
é    N)ÚPathÚclientÚwebappÚjsÚaudiozsfx_engine.jsÚenginezaudio_haptics.jszcombat_skills.jszmain.jsz
index.htmlc                   ó~   — e Zd Ze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S )ÚTestVisceralAudioEnginec                 ó’  — t          t          dd¬¦  «        5 }|                     ¦   «         | _        d d d ¦  «         n# 1 swxY w Y   t          t          dd¬¦  «        5 }|                     ¦   «         | _        d d d ¦  «         n# 1 swxY w Y   t          dz  }|                     ¦   «         r?|                     d¦  «        D ])}| xj        d| 	                    d¬¦  «        z   z  c_        Œ*t          t          dd¬¦  «        5 }|                     ¦   «         | _        d d d ¦  «         n# 1 swxY w Y   t          t          dd¬¦  «        5 }|                     ¦   «         | _        d d d ¦  «         n# 1 swxY w Y   t          t          dd¬¦  «        5 }|                     ¦   «         | _        d d d ¦  «         d S # 1 swxY w Y   d S )NÚrzutf-8)ÚencodingÚ	templatesz*.htmlú
)ÚopenÚSFX_ENGINE_JSÚreadÚsfx_codeÚ
INDEX_HTMLÚ	html_codeÚCLIENT_WEBAPPÚis_dirÚglobÚ	read_textÚMAIN_JSÚ	main_codeÚCOMBAT_SKILLS_JSÚcombat_codeÚAUDIO_HAPTICS_JSÚhaptics_code)ÚclsÚfÚtemplates_dirÚtpls       ú>C:\Projects\FreeExile\tests\unit\test_visceral_audio_engine.pyÚ
setUpClassz"TestVisceralAudioEngine.setUpClass   sÅ  € å•- ¨wÐ7Ñ7Ô7ð 	$¸1ØŸ6š6™8œ8ˆCŒLð	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$å•*˜c¨GÐ4Ñ4Ô4ð 	%¸ØŸFšF™HœHˆCŒMð	%ð 	%ð 	%ñ 	%ô 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%øøøð 	%ð 	%ð 	%ð 	%å%¨Ñ3ˆØ×ÒÑ!Ô!ð 	HØ$×)Ò)¨(Ñ3Ô3ð Hð H�Ø�” ¨¯ª¸w¨Ñ(GÔ(GÑ!GÑG�”�Ý•'˜3¨Ð1Ñ1Ô1ð 	%°QØŸFšF™HœHˆCŒMð	%ð 	%ð 	%ñ 	%ô 	%ð 	%ð 	%ð 	%ð 	%ð 	%ð 	%øøøð 	%ð 	%ð 	%ð 	%åÕ" C°'Ð:Ñ:Ô:ð 	'¸aØŸfšf™hœhˆCŒOð	'ð 	'ð 	'ñ 	'ô 	'ð 	'ð 	'ð 	'ð 	'ð 	'ð 	'øøøð 	'ð 	'ð 	'ð 	'åÕ" C°'Ð:Ñ:Ô:ð 	(¸aØ Ÿvšv™xœxˆCÔð	(ð 	(ð 	(ñ 	(ô 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(øøøð 	(ð 	(ð 	(ð 	(ð 	(ð 	(sW   ˜>¾AÁAÁ BÂB
ÂB
ÄD+Ä+D/Ä2D/ÅE3Å3E7Å:E7ÆF<Æ<G ÇG c                 óâ   — |                       t                               ¦   «         d¦  «         t          | j                             ¦   «         ¦  «        }|                      |dd|› d�¦  «         dS )zLsfx_engine.js must exist and adhere to file length standards (<= 500 lines).z3sfx_engine.js must exist in client/webapp/js/audio/iô  zsfx_engine.js line count (z) must be <= 500 linesN)Ú
assertTruer   ÚexistsÚlenr   Ú
splitlinesÚassertLessEqual)ÚselfÚ
line_counts     r$   Ú*test_sfx_engine_file_exists_and_line_countzBTestVisceralAudioEngine.test_sfx_engine_file_exists_and_line_count'   sh   € à�Š�×,Ò,Ñ.Ô.Ð0eÑfÔfÐfÝ˜œ×1Ò1Ñ3Ô3Ñ4Ô4ˆ
Ø×Ò˜Z¨Ð.mÈ:Ð.mÐ.mÐ.mÑnÔnÐnÐnÐnó    c           	      óì   — t           t          t          t          g}|D ]V}t	          j        ddt          |¦  «        gddd¬¦  «        }|                      |j        dd|j	        › d|j
        › �¦  «         ŒWd	S )
zDAll modified engine JS files must pass Node.js syntax parsing check.Únodez-cTF)Úcapture_outputÚtextÚcheckr   zNode syntax error in z: N)r   r   r   r   Ú
subprocessÚrunÚstrÚassertEqualÚ
returncodeÚnameÚstderr)r,   Úfiles_to_checkÚjs_fileÚress       r$   Ú)test_javascript_syntax_validity_with_nodezATestVisceralAudioEngine.test_javascript_syntax_validity_with_node-   s”   € å'­Õ2BÕDTÐUˆØ%ð 	fð 	fˆGÝ”.Ø˜�s 7™|œ|Ð,Ø#ØØð	ñ ô ˆCð ×Ò˜Sœ^¨QÐ0dÈÌÐ0dÐ0dÐX[ÔXbÐ0dÐ0dÑeÔeÐeÐeð	fð 	fr/   c                 óJ  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )zWSFXEngine must export as ES Module and expose window globals for browser compatibility.zexport class SFXEnginezexport const sfxEnginezexport default sfxEnginezwindow.SFXEngine = SFXEnginezwindow.sfxEngine = sfxEnginezwindow.FreeExileAudio = {N©ÚassertInr   ©r,   s    r$   Ú!test_es_module_and_window_exportsz9TestVisceralAudioEngine.test_es_module_and_window_exports9   s’   € à�ŠÐ.°´Ñ>Ô>Ð>Ø�ŠÐ.°´Ñ>Ô>Ð>Ø�ŠÐ0°$´-Ñ@Ô@Ð@Ø�ŠÐ4°d´mÑDÔDÐDØ�ŠÐ4°d´mÑDÔDÐDØ�ŠÐ1°4´=ÑAÔAÐAÐAÐAr/   c                 ól  — g d¢}|D ]%}|                       d|› �| j        d|› d�¦  «         Œ&|                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d	| j        ¦  «         d
S )zWAudio engine must define multi-bus routing: master, sfx, music, ambient, ui gain nodes.)Ú
masterGainÚsfxGainÚ	musicGainÚambientGainÚuiGainzthis.zBus z must be declared in SFXEnginez2this.masterGain.connect(this.audioCtx.destination)z%this.sfxGain.connect(this.masterGain)z'this.musicGain.connect(this.masterGain)z)this.ambientGain.connect(this.masterGain)z$this.uiGain.connect(this.masterGain)NrA   )r,   Ú	bus_nodesÚbuss      r$   Ú#test_multi_bus_architecture_definedz;TestVisceralAudioEngine.test_multi_bus_architecture_definedB   sÄ   € àSÐSÐSˆ	Øð 	dð 	dˆCØ�MŠM˜- #˜-˜-¨¬Ð8b¸sÐ8bÐ8bÐ8bÑcÔcÐcÐcà�ŠÐJÈDÌMÑZÔZÐZØ�ŠÐ=¸t¼}ÑMÔMÐMØ�ŠÐ?ÀÄÑOÔOÐOØ�ŠÐAÀ4Ä=ÑQÔQÐQØ�ŠÐ<¸d¼mÑLÔLÐLÐLÐLr/   c                 ó¨   — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )z\Engine must procedurally pre-compute white and brown noise buffers without network requests.zgenerateNoiseBuffers()zthis.whiteNoiseBufferzthis.brownNoiseBufferNrA   rC   s    r$   Ú(test_procedural_noise_buffers_generationz@TestVisceralAudioEngine.test_procedural_noise_buffers_generationN   sM   € à�ŠÐ.°´Ñ>Ô>Ð>Ø�ŠÐ-¨t¬}Ñ=Ô=Ð=Ø�ŠÐ-¨t¬}Ñ=Ô=Ð=Ð=Ð=r/   c                 ó^   — g d¢}|D ]%}|                       |› d�| j        d|› d�¦  «         Œ&dS )zHAll 13 required savage primal ARPG sound generation routines must exist.)ÚplayBladeSlashÚplayBoneCrunchÚplayFleshHitÚplayDodgeWhooshÚplayFlameBurstÚplayLightningCrackÚplayFrostShatterÚplayBossHeavySlamÚplayBossStaggerGongÚplayLootDropChimeÚplayLevelUpÚstartAmbientWildernessÚstopAmbientWildernessÚplayUIButtonú(zMethod z#() must be implemented in SFXEngineNrA   )r,   Úrequired_methodsÚmethods      r$   Ú5test_all_thirteen_visceral_sound_routines_implementedzMTestVisceralAudioEngine.test_all_thirteen_visceral_sound_routines_implementedT   s`   € ð
ð 
ð 
Ðð  'ð 	nð 	nˆFØ�MŠM˜V˜,˜,˜,¨¬Ð7lÀÐ7lÐ7lÐ7lÑmÔmÐmÐmð	nð 	nr/   c                 ób  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         d	D ]}|                       || j        ¦  «         Œ|                       d
| j        ¦  «         |                       d| j        ¦  «         dS )z^Verify visceral synthesis techniques: sub-bass sine, filter sweeps, gong partials, loot tiers.z
'bandpass'rR   z70, 30rS   rX   ÚtriggerScreenShakerY   rZ   )ÚcommonÚmagicÚrareÚuniqueÚdiviner[   r\   NrA   )r,   Útiers     r$   Ú!test_visceral_synthesis_acousticsz9TestVisceralAudioEngine.test_visceral_synthesis_acousticsi   s  € ð 	�Š�l D¤MÑ2Ô2Ð2à�ŠÐ&¨¬Ñ6Ô6Ð6Ø�Š�h ¤Ñ.Ô.Ð.à�Š�n d¤mÑ4Ô4Ð4à�ŠÐ)¨4¬=Ñ9Ô9Ð9Ø�ŠÐ*¨D¬MÑ:Ô:Ð:à�ŠÐ+¨T¬]Ñ;Ô;Ð;à�ŠÐ)¨4¬=Ñ9Ô9Ð9ØCð 	/ð 	/ˆDØ�MŠM˜$ ¤Ñ.Ô.Ð.Ð.à�Š�m T¤]Ñ3Ô3Ð3à�ŠÐ.°´Ñ>Ô>Ð>Ð>Ð>r/   c                 óÞ   — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )zNEngine must handle browser AudioContext unlocking on user interaction gesture.zunlockAudio()zresume()ÚunlockAudior\   N)rB   r   r   rC   s    r$   Ú!test_audio_context_gesture_unlockz9TestVisceralAudioEngine.test_audio_context_gesture_unlock€   sa   € à�Š�o t¤}Ñ5Ô5Ð5Ø�Š�j $¤-Ñ0Ô0Ð0à�Š�m T¤^Ñ4Ô4Ð4Ø�ŠÐ.°´Ñ?Ô?Ð?Ð?Ð?r/   c                 óì  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d	| j        ¦  «         d
S )zGSettings must persist in localStorage under 'freeexile_audio_settings'.Úfreeexile_audio_settingsÚloadSettingsÚsaveSettingsÚsetMasterVolumeÚsetSFXVolumeÚsetAmbientVolumeÚsetMusicVolumeÚsetUIVolumeÚ
toggleMuteNrA   rC   s    r$   Ú$test_volume_and_settings_persistencez<TestVisceralAudioEngine.test_volume_and_settings_persistenceˆ   sÒ   € à�ŠÐ0°$´-Ñ@Ô@Ð@Ø�Š�n d¤mÑ4Ô4Ð4Ø�Š�n d¤mÑ4Ô4Ð4Ø�ŠÐ'¨¬Ñ7Ô7Ð7Ø�Š�n d¤mÑ4Ô4Ð4Ø�ŠÐ(¨$¬-Ñ8Ô8Ð8Ø�ŠÐ&¨¬Ñ6Ô6Ð6Ø�Š�m T¤]Ñ3Ô3Ð3Ø�Š�l D¤MÑ2Ô2Ð2Ð2Ð2r/   c                 óÞ   — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )zVEngine must support iOS WebKit messageHandlers, AndroidHaptics, and navigator.vibrate.ÚtriggerHapticzwebkit.messageHandlers.hapticÚAndroidHapticsznavigator.vibrateNrA   rC   s    r$   Ú#test_native_haptics_synchronizationz;TestVisceralAudioEngine.test_native_haptics_synchronization”   sc   € à�Š�o t¤}Ñ5Ô5Ð5Ø�ŠÐ5°t´}ÑEÔEÐEØ�ŠÐ&¨¬Ñ6Ô6Ð6Ø�ŠÐ)¨4¬=Ñ9Ô9Ð9Ð9Ð9r/   c                 ó4  — |                       d| j        ¦  «         | j                             d¦  «        }| j                             d¦  «        }| j                             d¦  «        }|                      ||d¦  «         |                      ||d¦  «         dS )zIindex.html must import sfx_engine.js before combat_skills.js and main.js.zjs/audio/sfx_engine.jszjs/engine/combat_skills.jsz
js/main.jsz6sfx_engine.js must be imported before combat_skills.jsz-sfx_engine.js must be imported before main.jsN)rB   r   ÚindexÚ
assertLess)r,   Úsfx_idxÚ
combat_idxÚmain_idxs       r$   Ú"test_index_html_imports_sfx_enginez:TestVisceralAudioEngine.test_index_html_imports_sfx_engine›   sŽ   € à�ŠÐ.°´Ñ?Ô?Ð?Ø”.×&Ò&Ð'?Ñ@Ô@ˆØ”^×)Ò)Ð*FÑGÔGˆ
Ø”>×'Ò'¨Ñ5Ô5ˆØ�Š˜ Ð-eÑfÔfÐfØ�Š˜ Ð+ZÑ[Ô[Ð[Ð[Ð[r/   c                 ó¶  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         d	S )
z\Game Settings modal must include sliders for Master, SFX, Ambient, Mute button, and Haptics.zid="audio-master-slider"zid="audio-sfx-slider"zid="audio-ambient-slider"zid="audio-mute-btn"zid="audio-haptics-toggle"zid="btn-preview-slash"zid="btn-preview-slam"zid="btn-preview-loot"N)rB   r   rC   s    r$   Ú'test_game_settings_modal_audio_controlsz?TestVisceralAudioEngine.test_game_settings_modal_audio_controls¤   sÀ   € à�ŠÐ0°$´.ÑAÔAÐAØ�ŠÐ-¨t¬~Ñ>Ô>Ð>Ø�ŠÐ1°4´>ÑBÔBÐBØ�ŠÐ+¨T¬^Ñ<Ô<Ð<Ø�ŠÐ1°4´>ÑBÔBÐBØ�ŠÐ.°´Ñ?Ô?Ð?Ø�ŠÐ-¨t¬~Ñ>Ô>Ð>Ø�ŠÐ-¨t¬~Ñ>Ô>Ð>Ð>Ð>r/   c                 óJ  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )zVmain.js must bind slider input events, mute toggle, preview buttons, and UI click SFX.ÚinitAudioSettingsUIzaudio-master-sliderzaudio-sfx-sliderzaudio-ambient-sliderzaudio-mute-btnr^   N)rB   r   rC   s    r$   Ú/test_main_js_wires_audio_settings_and_ui_clickszGTestVisceralAudioEngine.test_main_js_wires_audio_settings_and_ui_clicks¯   s‘   € à�ŠÐ+¨T¬^Ñ<Ô<Ð<Ø�ŠÐ+¨T¬^Ñ<Ô<Ð<Ø�ŠÐ(¨$¬.Ñ9Ô9Ð9Ø�ŠÐ,¨d¬nÑ=Ô=Ð=Ø�ŠÐ&¨¬Ñ7Ô7Ð7Ø�Š�n d¤nÑ5Ô5Ð5Ð5Ð5r/   c                 óŽ  — |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         |                       d	| j        ¦  «         |                       d
| j        ¦  «         |                       d| j        ¦  «         |                       d| j        ¦  «         dS )z[combat_skills.js must invoke SFXEngine methods for skills, impacts, dodge, and boss events.zsfxEngine.playDodgeWhoosh()zsfxEngine.playFlameBurst()zsfxEngine.playLightningCrack()zsfxEngine.playFrostShatter()zsfxEngine.playBladeSlash()zsfxEngine.playBoneCrunch()zsfxEngine.playFleshHit()zsfxEngine.playBossHeavySlam()zsfxEngine.playBossStaggerGong()zsfxEngine.playLootDropChimez$window.bossHeavySlam = bossHeavySlamz window.bossStagger = bossStaggerN)rB   r   rC   s    r$   Útest_combat_skills_integrationz6TestVisceralAudioEngine.test_combat_skills_integration¸   s*  € ð 	�ŠÐ3°TÔ5EÑFÔFÐFà�ŠÐ2°DÔ4DÑEÔEÐEØ�ŠÐ6¸Ô8HÑIÔIÐIØ�ŠÐ4°dÔ6FÑGÔGÐGà�ŠÐ2°DÔ4DÑEÔEÐEØ�ŠÐ2°DÔ4DÑEÔEÐEØ�ŠÐ0°$Ô2BÑCÔCÐCà�ŠÐ5°tÔ7GÑHÔHÐHØ�ŠÐ7¸Ô9IÑJÔJÐJØ�ŠÐ3°TÔ5EÑFÔFÐFà�ŠÐ<¸dÔ>NÑOÔOÐOØ�ŠÐ8¸$Ô:JÑKÔKÐKÐKÐKr/   c                 óæ   — d}t          j        ddd|gt          t          ¦  «        ddd¬¦  «        }|                      |j        dd	|j        › �¦  «         |                      d
|j        ¦  «         dS )zZExecute SFXEngine in Node.js with Web Audio API mock to verify runtime function execution.a{  
        class AudioParamMock {
          constructor() { this.value = 1.0; }
          setValueAtTime(v, t) { this.value = v; }
          linearRampToValueAtTime(v, t) { this.value = v; }
          exponentialRampToValueAtTime(v, t) { this.value = v; }
        }
        class AudioNodeMock {
          constructor() {
            this.gain = new AudioParamMock();
            this.frequency = new AudioParamMock();
            this.Q = new AudioParamMock();
            this.pan = new AudioParamMock();
          }
          connect() {}
          start() {}
          stop() {}
        }
        class AudioBufferMock {
          constructor(channels, length, sampleRate) {
            this.data = new Float32Array(length);
          }
          getChannelData() { return this.data; }
        }
        global.window = {
          AudioContext: class {
            constructor() {
              this.state = 'running';
              this.currentTime = 0.05;
              this.sampleRate = 44100;
              this.destination = new AudioNodeMock();
            }
            createGain() { return new AudioNodeMock(); }
            createOscillator() { return new AudioNodeMock(); }
            createBiquadFilter() { return new AudioNodeMock(); }
            createBufferSource() { return new AudioNodeMock(); }
            createStereoPanner() { return new AudioNodeMock(); }
            createBuffer(ch, len, sr) { return new AudioBufferMock(ch, len, sr); }
            resume() { return Promise.resolve(); }
          }
        };
        global.document = {
          addEventListener() {},
          removeEventListener() {},
        };
        global.localStorage = {
          _store: {},
          getItem(k) { return this._store[k] || null; },
          setItem(k, v) { this._store[k] = String(v); }
        };
        try {
          Object.defineProperty(globalThis, 'navigator', {
            value: { vibrate() {} },
            configurable: true,
            writable: true
          });
        } catch (e) {
          if (globalThis.navigator) globalThis.navigator.vibrate = () => {};
        }

        import('./client/webapp/js/audio/sfx_engine.js').then(({ sfxEngine }) => {
          sfxEngine.init();
          sfxEngine.setMasterVolume(0.8);
          sfxEngine.setSFXVolume(0.7);
          sfxEngine.setAmbientVolume(0.5);

          // Test all visceral sound routines execute without error
          sfxEngine.playBladeSlash();
          sfxEngine.playBoneCrunch();
          sfxEngine.playFleshHit();
          sfxEngine.playDodgeWhoosh();
          sfxEngine.playFlameBurst();
          sfxEngine.playLightningCrack();
          sfxEngine.playFrostShatter();
          sfxEngine.playBossHeavySlam();
          sfxEngine.playBossStaggerGong();
          sfxEngine.playLootDropChime('rare');
          sfxEngine.playLootDropChime('divine');
          sfxEngine.playLevelUp();
          sfxEngine.startAmbientWilderness();
          sfxEngine.stopAmbientWilderness();
          sfxEngine.playUIButton();

          console.log('SFX_ENGINE_EXECUTION_SUCCESS');
        }).catch(err => {
          console.error(err);
          process.exit(1);
        });
        r1   z--input-type=modulez-eTF)Úcwdr2   r3   r4   r   zHeadless execution failed: ÚSFX_ENGINE_EXECUTION_SUCCESSN)	r5   r6   r7   ÚPROJECT_ROOTr8   r9   r;   rB   Ústdout)r,   Úmock_scriptr>   s      r$   Ú'test_headless_node_sfx_engine_executionz?TestVisceralAudioEngine.test_headless_node_sfx_engine_executionÌ   sˆ   € ðXˆõr ŒnØÐ*¨D°+Ð>Ý•LÑ!Ô!ØØØð
ñ 
ô 
ˆð 	×Ò˜œ¨Ð,VÈ#Ì*Ð,VÐ,VÑWÔWÐWØ�ŠÐ4°c´jÑAÔAÐAÐAÐAr/   N)Ú__name__Ú
__module__Ú__qualname__Úclassmethodr%   r.   r?   rD   rM   rO   rb   rk   rn   ry   r}   r„   r†   r‰   r‹   r’   © r/   r$   r
   r
      s0  € € € € € Øð(ð (ñ „[ð(ð oð oð oð
fð 
fð 
fðBð Bð Bð
Mð 
Mð 
Mð>ð >ð >ðnð nð nð*?ð ?ð ?ð.@ð @ð @ð
3ð 
3ð 
3ð:ð :ð :ð\ð \ð \ð	?ð 	?ð 	?ð6ð 6ð 6ðLð Lð Lð(cBð cBð cBð cBð cBr/   r
   Ú__main__)Ú__doc__r5   ÚunittestÚpathlibr   Ú__file__ÚresolveÚparentr�   r   r   r   r   r   r   ÚTestCaser
   r“   Úmainr—   r/   r$   ú<module>r¡      s&  ððð ð Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜xÑ'¨(Ñ2€Ø Ñ$ wÑ.°Ñ@€Ø  4Ñ'¨(Ñ2Ð5GÑGÐ Ø  4Ñ'¨(Ñ2Ð5GÑGÐ Ø
˜$Ñ
 Ñ
*€Ø˜\Ñ)€
ðZBð ZBð ZBð ZBð ZB˜hÔ/ñ ZBô ZBð ZBðz ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr/   