o
    iq                     @  s  U d Z ddlm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
mZmZ ddlmZ ddlmZmZmZmZmZmZmZ eejjZed Ze  dkZd	ed
< eed dddZ e!e Z"W d   n1 srw   Y  ded< ded< ded< ded< eed dddZ e!e Z#e#d Z$e#d Z%e#d Z&e#d Z'W d   n1 sw   Y  dZ(dZ)e*dd e&+ D Z,dmd!d"Z-dmd#d$Z.dnd'd(Z/dod+d,Z0dpd.d/Z1dqd3d4Z2drd7d8Z3dsd;d<Z4dtdBdCZ5G dDdE dEej6Z7G dFdG dGZ8e8j9e8j:e8j;e8j<e8j=e8j>e8j?ge8j@gdHZAe8j9ge8j:e8j;e8j<e8j=e8j>e8j?geAdI  dHe8j9e8j:ge8j;e8j<e8j=e8j>e8j?geAdI  dHeAeAeAeAdJZBeG dKdL dLe
ZCdudOdPZDdvdSdTZEdvdUdVZFdvdWdXZGdvdYdZZHdvd[d\ZIdwd`daZJdwdbdcZKdxdkdlZLdS )yzBasic tests for Parts I & II    )annotationsN)Flagautounique)Path)AnyCallableDictListOptionalSequenceTypetestsdarwinzdict[str, Any]EXPECTED_RESULTSzexpected_results.jsonrzutf-8)encodingzdict[str, List[str]]EXTRA_CREDIT_PROGRAMS	List[str]REQUIRES_MATHLIBzDict[str, List[str]]DEPENDENCIESASSEMBLY_DEPENDENCIESztest_properties.jsonextra_credit_testsrequires_mathlibassembly_libslibsz_osx.sz_linux.sc                 c  s:    | ]}|D ]}|t  |t fD ]}t|jV  qqqd S N)
MAC_SUFFIXLINUX_SUFFIXr   name).0r   libplatform_specific_lib r#   `/home/gtoal/gtoal.com/languages/algol60/new-unicode-parser/lang/c/tests4/test_framework/basic.py	<genexpr>)   s    r%   returnstrc                   C  s   t rdS dS )Nos_xlinux)IS_OSXr#   r#   r#   r$   get_platform3      r+   c                   C  s   t rtS tS r   )r*   r   r   r#   r#   r#   r$   get_platform_suffix:   r,   r-   source_filer   c                 C  s4   | j drt| | j dtd  } t| tS )zkey to use in EXPECTED_RESULTS, REQUIRES_MATHLIB, EXTRA_CREDIT_PROGRAMS
    If this ends with _client.c, use corresponding lib as props key
    _clientN)stemendswithreplace_stemlenr'   relative_toTEST_DIR)r.   r#   r#   r$   get_props_keyA   s   r6   progboolc                 C  s   t | }|tv o
t S r   )r6   r   r*   )r7   keyr#   r#   r$   needs_mathlibJ   s   r:   
List[Path]c           	      C  sn   t | }g }|tv rt| D ]}|t  }t| }|| q|tv r5t| }|D ]}t| }|| q)|S )zNGet extra libraries this test program depends on (aside from lib/client pairs))r6   r   r-   r5   appendr   )	r7   	props_keyr   asm_depasm_filenameasm_pathlib_filenamesllib_pathr#   r#   r$   get_libsO   s   
rD   proc subprocess.CompletedProcess[str]Nonec                 C  s   | j r
t| j  dS dS )zcPrint out stderr of CompletedProcess if it's not empty. Intended to print assembler/linker warningsN)stderrprint)rE   r#   r#   r$   print_stderr`   s   rJ   source_filesoptionsc              
   C  s~   | d  d}ztjg d|  | d|g dddd}t| W n tjy3 } zt|j|d}~ww tj|gdddd	d
S )a7  Compile input files using 'gcc' command and run the resulting executable

    Args:
        source_files: list of input files - could be C, assembly, or object files
        options: command-line options

    Returns:
        a CompletedProecess object that captures the executable's return code and output
    r    )gccz-DSUPPRESS_WARNINGSz-oT)checktextcapture_outputNF      $@)rP   rQ   rR   timeout)with_suffix
subprocessrunrJ   CalledProcessErrorRuntimeErrorrH   )rK   rL   exeresulterrr#   r#   r$   gcc_compile_and_runf   s    r]   pathnew_stemc                 C  s&   t jdkr
| |S | || jS )z;Return a new path with the stem changed and suffix the same)   	   )sysversion_info	with_stem	with_namerU   suffix)r^   r_   r#   r#   r$   r2      s   

r2   expected_retcodeintexpected_stdoutactualexe_namec                 C  s   d| g}| |j kr|d|  d|j  g7 }|j|kr>|dt| dt|j  tt| |j }|| |j	rL|d ||j	 d
|S )a  Build the error message for when a compiled test program behaves incorrectly
    Called when a unittest assert* message fails
    Args:
        expected_retcode: expected return code from EXPECTED_RESULTS
        expected_stdout: expected stdout from EXPECTED_RESULTS (often empty)
        actual: result from calling subprocess.run() on compiled test program
        exe_name: full path to compiled test program
    Returns:
        an error message
    zIncorrect behavior in z* Bad return code: expected z	 and got z* Bad stdout: expected z+* Expected no output to stderr, but found:

)
returncodestdoutr<   reprlistdifflibndiff
splitlinesextendrH   join)rg   ri   rj   rk   	msg_linesdiffr#   r#   r$   build_error_message   s"   




rx   c                   @  s   e Zd ZU dZdZded< ded< ded< ded	< d
ed< d-ddZ	d.d/ddZd0ddZd1ddZ	d0ddZ
d0ddZd0d d!Zd2d%d&Zd3d(d)Zd4d+d,ZdS )5TestChaptera]  Base per-chapter test class - should be subclassed, not instantiated directly.

    For each chapter under test, we construct a subclass of TestChapter and generate
    a test method for each C program in the corresponding directory. Each dynamically generated
    test calls one of the main test methods defined below:

    * compile_failure: compilation should fail)
    * compile_success: compilation should succeed up to some intermediate stage)
    * compile_and_run: compiling and running the test program should give the expected result)
    * compile_client_and_run: the test program consists of a client and library.
        compiling the client with our compiler and library with the system compiler,
        run the compiled program, and validate the result
    * compile_lib_and_run:
        like compile_client_and_run, but compile the *library* withour compiler
        and *client* with the system compiler
    * compile_with_helper_lib_and_run:
        like compile_client_and_run except the library is defined in test_properties.json and is not under test
        library should be in TEST_DIR/helper_libs/
    * compile_with_asm_lib_and_run:
        like compile_client_and_run except the library is an assembly file defined in test_properties.json, not a C file

    The other methods in TestChapter are all utilties called by the compile_* methods.
    Fr   test_dirccz	list[str]rL   r'   
exit_stage	list[int]error_codesr&   rG   c                 C  s,   dd | j dD }|D ]}|  qdS )zKDelete files produced during this test run (e.g. assembly and object files)c                 s  s0    | ]}|  s|jd vr|jtvr|V  qdS )).cz.hz.mdN)is_dirrf   r   ASSEMBLY_LIBSr    fr#   r#   r$   r%      s    

z'TestChapter.tearDown.<locals>.<genexpr>*N)rz   rglobunlink)selfgarbage_filesjunkr#   r#   r$   tearDown   s   

zTestChapter.tearDownNr.   cc_optOptional[str]rF   c                 C  s^   |du r| j durd| j  }| jg| j }|dur|| || tj|dddd}|S )a  Compile the test program (possibly up to some intermediate stage), but don't run it.

        Args:
            source_file: Absolute path to source file
            cc_opt (optional): Additional command-line options to pass to compiler
                (in addition to exit stage and anything specified in self.options).
                Used to compile without linking (for library tests);
                to link math library; and to compile to assembly (for optimization tests)

        Returns:
            A CompletedObject the captures the result of compilation (including an exit code
            indicating whether it succeeded and any error messages produced by the compiler)
        Nz--TF)rR   rP   rQ   )r|   r{   rL   r<   rV   rW   )r   r.   r   argsrE   r#   r#   r$   invoke_compiler   s   

zTestChapter.invoke_compilerc                 C  sL   |j }|j| d }| j| d| dd |j| }| |  dS )zMake sure the compiler under test didn't emit executable or assembly code.

        Used when compiling invalid test cases or testing intermediate stages.z.szFound assembly file z for invalid program!msgN)r0   parentassertFalseexists)r   r.   r0   assembly_fileexecutable_filer#   r#   r$   validate_no_output  s   

zTestChapter.validate_no_outputrj   c                 C  s   t |}t| }|d }|dd}|jd }| j||jt||||d | j||jt||||d | j|j	t||||d dS )aH  Validate that the running compiled executable gave the expected result.

        Compare return code and stdout to values in EXPECTED_RESULTS.

        Args:
            source_file: Absolute path of the source file for a test program
            actual: result of compiling this source file with self.cc and running it
        return_codern   rM   r   r   N)
r6   r   getr   assertEqualrm   rx   rn   r   rH   )r   r.   rj   r9   expectedrg   ri   rZ   r#   r#   r$   validate_runs(  s&   

zTestChapter.validate_runsc                 C  s   | j tjd| d | |}|  W d   n1 sw   Y  | jrDddd | jD }| j|j| jd|j d| d	d | 	| dS )
a  Test that compiling an invalid program returns a non-zero exit code

        Use this when compilation of the test program should fail at or before the stage under test.
        E.g. if type_error.c contains a type error,
        when we use the --stage validate option, test_type_error will call compile_failure
        but when we use the --stage parse option, test_type_error will call compile_success (below)
        zDidn't catch error in r   Nz, c                 s      | ]}t |V  qd S r   )r'   )r    er#   r#   r$   r%   [      z.TestChapter.compile_failure.<locals>.<genexpr>z"Compilation failed with exit code z0, but expected one of the following exit codes: .)
assertRaisesrV   rX   r   check_returncoder~   ru   assertInrm   r   )r   r.   r[   exit_code_listr#   r#   r$   compile_failureJ  s   	

zTestChapter.compile_failurec                 C  s8   |  |}| j|jdd| d|j d | | dS )a  Test that compiling a valid program returns exit code of 0.

        Use this when compilation of the program should succeed up until the stage under test.
        This is only used when testing an intermediate stage; when testing the whole compiler,
        use compile_and_run instead.r   compilation of  failed with error:
r   N)r   r   rm   rH   r   )r   r.   r[   r#   r#   r$   compile_successd  s   
zTestChapter.compile_successc                 C  s   t |}|r| || dS t|rd}nd}| j||d}| j|jdd| d|j d t| |d}t	j
|gd	d
d
dd}| || dS )z>Compile a valid test program, run it, and validate the resultsN-lmr   r   r   r   r   rM   FTrS   )rP   rR   rQ   rT   )rD   library_test_helperr:   r   r   rm   rH   rJ   rU   rV   rW   r   )r   r.   
extra_libsr   compile_resultrZ   r[   r#   r#   r$   compile_and_runu  s&   
zTestChapter.compile_and_runfile_under_testother_filesr;   c           	      C  s   |j dkr(| j|dd}| j|jdd| d|j d t| |d}|}n|}|d}|g| }g }t|sCtd	d
 |D rH|	d t
||}| || dS )a}  Compile one file in a multi-file program and validate the results.

        Compile file_under_test with compiler under test and other_files with 'gcc' command.
        Link 'em together, run the resulting executable, make validate the results.

        Args:
            file_under_test: Absolute path of one file in a multi-file program.
                Usually a C file we want to compile with self.cc, but sometimes
                (in optimization tests) an assembly file that we've alerady
                compiled with self.cc and inspected
            other_files: Absolute paths to other files in the multi-file program
        r   z-cr   r   r   r   r   z.oc                 s  r   r   )r:   r   r#   r#   r$   r%     r   z2TestChapter.library_test_helper.<locals>.<genexpr>r   N)rf   r   r   rm   rH   rJ   rU   r:   anyr<   r]   r   )	r   r   r   compilation_resultcompiled_file_under_testvalidation_keyrK   rL   r[   r#   r#   r$   r     s$   





zTestChapter.library_test_helperclient_pathc                 C  s,   t ||jdtd  }| ||g dS )z>Multi-file program test where our compiler compiles the clientNr/   )r2   r0   r3   r   )r   r   rC   r#   r#   r$   compile_client_and_run  s   z"TestChapter.compile_client_and_runrC   c                 C  s"   t ||jd }| ||g dS )z?Multi-file program test where our compiler compiles the libraryr/   N)r2   r0   r   )r   rC   r   r#   r#   r$   compile_lib_and_run  s   zTestChapter.compile_lib_and_run)r&   rG   r   )r.   r   r   r   r&   rF   )r.   r   r&   rG   )r.   r   rj   rF   r&   rG   )r   r   r   r;   r&   rG   )r   r   r&   rG   )rC   r   r&   rG   )__name__
__module____qualname____doc__longMessage__annotations__r   r   r   r   r   r   r   r   r   r   r#   r#   r#   r$   ry      s&   
 



"


$
.ry   c                   @  s0   e Zd ZdZdZdZdZdZdZdZ	dZ
d	Zd
S )TestDirsz-Subdirectory names within each test directoryinvalid_lexinvalid_parseinvalid_semanticsinvalid_declarationsinvalid_typesinvalid_labelsinvalid_struct_tagsvalidN)r   r   r   r   INVALID_LEXINVALID_PARSEINVALID_SEMANTICSINVALID_DECLARATIONSINVALID_TYPESINVALID_LABELSINVALID_STRUCT_TAGSVALIDr#   r#   r#   r$   r     s    r   )invalidr   r   )lexparsevalidatetackycodegenrW   c                   @  sZ   e Zd ZdZe Ze Ze Ze Ze Z	e Z
e ZdZeeB eB eB e	B e
B eB ZdS )ExtraCreditzEAn ExtraCredit flag represents a set of enabled extra-credit featuresr   N)r   r   r   r   r   BITWISECOMPOUND	INCREMENTGOTOSWITCHNANUNIONNONEALLr#   r#   r#   r$   r     s     r   source_progextra_credit_flagsc                   s>   d| j vrdS t| }dd t| D }t fdd|D S )a^  Based on our current extra credit settings, should we include this test program?

    Args:
        source_prog: Absolute path to a C test program
        extra_credit_flags: extra credit features to test (specified on the command line)

    Returns:
        true if we should _exclude_ this program from test run, false if we should include it.
    extra_creditFc                 s  s    | ]
}t t| V  qd S r   )r   r'   upper)r    featurer#   r#   r$   r%   <  s    
z(excluded_extra_credit.<locals>.<genexpr>c                 3  s    | ]}| vV  qd S r   r#   r   r   r#   r$   r%   B  r   )partsr6   r   r   )r   r   r9   features_requiredr#   r   r$   excluded_extra_credit)  s   
r   programCallable[[TestChapter], None]c                      d fdd}|S )	z4Generate a test method for an invalid source programr   ry   r&   rG   c                      |    d S r   )r   r   r   r#   r$   test_invalidH     z'make_invalid_test.<locals>.test_invalidNr   ry   r&   rG   r#   )r   r   r#   r   r$   make_invalid_testE     r   c                   r   )	zGenerate one test method to compile a valid program.

    Only used when testing intermediate stages. Use make_test_run when testing
    the whole compilerr   ry   r&   rG   c                   r   r   )r   r   r   r#   r$   
test_validT  r   z#make_test_valid.<locals>.test_validNr   r#   )r   r   r#   r   r$   make_test_validN  s   r   c                   r   )	zGenerate one test method to compile and run a valid single-file program
    (the program may depend on additional source or assembly files that are not under test)
    r   ry   r&   rG   c                   r   r   )r   r   r   r#   r$   test_run_  r   zmake_test_run.<locals>.test_runNr   r#   )r   r   r#   r   r$   make_test_runZ  s   r   c                   r   )	z9Generate one test method for client in multi-file programr   ry   r&   rG   c                   r   r   )r   r   r   r#   r$   test_clienth  r   z%make_test_client.<locals>.test_clientNr   r#   )r   r   r#   r   r$   make_test_cliente  r   r   c                   r   )	z:Generate one test method for library in multi-file programr   ry   r&   rG   c                   r   r   )r   r   r   r#   r$   test_libq  r   zmake_test_lib.<locals>.test_libNr   r#   )r   r   r#   r   r$   make_test_libn  r   r   rz   stage/list[tuple[str, Callable[[TestChapter], None]]]c           
      C  sn   g }t | d D ],}| | }|dD ] }t||rq|| d}d| }t|}	|||	f qq|S )a  Generate one test method for each invalid test program in test_dir.

    We use extra_credit_flags and stage to discover invalid test cases within test_dir.

    Args:
        test_dir: Absolute path to the test directory for a specific chapter
                  (e.g. /path/to/write-a-c-compiler-tests/chapter_1/)
        stage: only compile programs through this stage. this dictates which programs
               are considered invalid (e.g. if stage is "parse" programs with type errors
               are valid, because we stop before typechecking)
        extra_credit_flags: extra credit features to test (specified on the command line)

    Returns:
        A list of (name, test method) tuples, intended to be included on a dynamically generated
        subclass of TestChapter
    r   *.crM   test_)DIRECTORIES_BY_STAGEr   r   r4   rU   r   r<   )
rz   r   r   r   invalid_subdirinvalid_test_dirr   r9   	test_nametest_methodr#   r#   r$   make_invalid_testsw  s   

r  c           
      C  s   g }t | d D ]J}| | }|dD ]>}t||rq|| d}d| }|dkrFd|jvr6t|}	n|jdrAt	|}	n	t
|}	nt|}	|||	f qq|S )a  Generate one test method for each valid test program in test_dir.

    We use stage and extra_credit_flags to discover valid test cases in test_dir. We also
    use stage to determine what sort of test to run (e.g. if stage is "run" we actually run the
    executable we compile; otherwise we just check that compilation succeeded).

    Args:
        test_dir: Absolute path to the test directory for a specific chapter
                  (e.g. /path/to/write-a-c-compiler-tests/chapter_1/)
        stage: only compile programs through this stage. this dictates which programs
               are considered valid (e.g. if stage is "parse" programs with type errors
               are valid, because we stop before typechecking)
        extra_credit_flags: extra credit features to test (specified on the command line)

    Returns:
        A list of (name, test method) tuples, intended to be included on a dynamically generated
        subclass of TestChapter
    r   r   rM   r   rW   	librariesclient)r   r   r   r4   rU   r   r   r0   r1   r   r   r   r<   )
rz   r   r   r   valid_subdirvalid_testdirr   r9   r  r  r#   r#   r$   make_valid_tests  s$   





r  compilerchapterSequence[str]skip_invalidr~   r}   Type[unittest.TestCase]c                C  s   t d|  }d| }|| ||dkrdn||d}	|s0t|||}
|
D ]\}}||	|< q't|||}|D ]\}}||	|< q8t|tf|	S )a  Construct the test class for a normal (non-optimization) chapter.

    Construct a subclass of TestChapter, generating a test method for each
    program in this chapter's test suite (possibly including some extra credit programs,
    depending on the extra_credit argument).

    Args:
        compiler: absolute path to compiler under test
        chapter: the chapter we're testing
        options: extra command-line options to pass through to compiler
        stage: only compile programs up through this stage
        extra_credit_flags: extra credit features to test, represented as a bit vector
        skip_invalid: true if we should skip invalid test programs
        error_codes: expected compiler exit codes when rejecting invalid programs
    chapter_ry   rW   N)rz   r{   rL   r|   r~   )r5   joinpathresolver  r  typery   )r	  r
  rL   r   r   r  r~   rz   testclass_nametestclass_attrsinvalid_testsr  test_clsvalid_testsr#   r#   r$   build_test_class  s    
	

r  )r&   r'   )r.   r   r&   r'   )r7   r   r&   r8   )r7   r   r&   r;   )rE   rF   r&   rG   )rK   r;   rL   r   r&   rF   )r^   r   r_   r'   r&   r   )
rg   rh   ri   r'   rj   rF   rk   r'   r&   r'   )r   r   r   r   r&   r8   )r   r   r&   r   )rz   r   r   r'   r   r   r&   r   )r	  r   r
  rh   rL   r  r   r'   r   r   r  r8   r~   r}   r&   r  )Mr   
__future__r   rq   jsonplatformrV   rb   unittestenumr   r   r   pathlibr   typingr   r   r	   r
   r   r   r   __file__r   ROOT_DIRr5   systemlowerr*   r   openr   loadr   	test_infor   r   r   r   r   r   setvaluesr   r+   r-   r6   r:   rD   rJ   r]   r2   rx   TestCasery   r   r   r   r   r   r   r   r   r   dirsr   r   r   r   r   r   r   r   r  r  r  r#   r#   r#   r$   <module>   s    $






	



%
	+  	



	


	
	
&3