<commit_before>#
# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"

import os
import re
import string
import StringIO
import sys
from types import *
import unittest

import TestCmd

sys.stdout = StringIO.StringIO()

if sys.platform == 'win32':
    existing_lib = "msvcrt"
else:
    existing_lib = "m"

class SConfTestCase(unittest.TestCase):

    def setUp(self):
        # we always want to start with a clean directory
        self.save_cwd = os.getcwd()
        self.test = TestCmd.TestCmd(workdir = '') 
        os.chdir(self.test.workpath(''))

    def tearDown(self):
        self.test.cleanup()
        import SCons.SConsign
        SCons.SConsign.Reset()
        os.chdir(self.save_cwd)

    def _resetSConfState(self):
        # Ok, this is tricky, and i do not know, if everything is sane.
        # We try to reset scons' state (including all global variables)
        import SCons.SConsign
        SCons.SConsign.write() # simulate normal scons-finish
        for n in sys.modules.keys():
            if string.split(n, '.')[0] == 'SCons' and n[:12] != 'SCons.compat':
                m = sys.modules[n]
                if type(m) is ModuleType:
                    # if this is really a scons module, clear its namespace
                    del sys.modules[n]
                    m.__dict__.clear()
        # we only use SCons.Environment and SCons.SConf for these tests.
        import SCons.Environment
        import SCons.SConf
        self.Environment = SCons.Environment
        self.SConf = SCons.SConf
        # and we need a new environment, cause references may point to
        # old modules (well, at least this is safe ...)
        self.scons_env = self.Environment.Environment()
        self.scons_env.AppendENVPath('PATH', os.environ['PATH'])

        # we want to do some autodetection here
        # this stuff works with
        #    - cygwin on Windows (using cmd.exe, not bash)
        #    - posix
        #    - msvc on Windows (hopefully)
        if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
            not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
            not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
            raise Exception, "This test needs an installed compiler!"
        if self.scons_env['CXX'] == 'g++':
            global existing_lib
            existing_lib = 'm'

        if sys.platform in ['cygwin', 'win32']:
             # On Windows, SCons.Platform.win32 redefines the builtin
             # file() and open() functions to close the file handles.
             # This interferes with the unittest.py infrastructure in
             # some way.  Just sidestep the issue by restoring the
             # original builtin functions whenever we have to reset
             # all of our global state.

             import __builtin__
             import SCons.Platform.win32

             __builtin__.file = SCons.Platform.win32._builtin_file
             __builtin__.open = SCons.Platform.win32._builtin_open

    def _baseTryXXX(self, TryFunc):
        # TryCompile and TryLink are much the same, so we can test them
        # in one method, we pass the function as a string ('TryCompile',
        # 'TryLink'), so we are aware of reloading modules.
        
        def checks(self, sconf, TryFuncString):
            TryFunc = self.SConf.SConfBase.__dict__[TryFuncString]
            res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" )
            res2 = TryFunc( sconf,
                            '#include "no_std_header.h"\nint main() {return 0; }\n',
                            '.c' )
            return (res1,res2)

        # 1. test initial behaviour (check ok / failed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks( self, sconf, TryFunc )
            assert res[0] and not res[1], res
        finally:
            sconf.Finish()
            
        # 2.1 test the error caching mechanism (no dependencies have changed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks( self, sconf, TryFunc )
            assert res[0] and not res[1], res
        finally:
            sconf.Finish()
        # we should have exactly one one error cached 
        log = self.test.read( self.test.workpath('config.log') )
        expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) 
        firstOcc = expr.match( log )
        assert firstOcc != None, log
        secondOcc = expr.match( log, firstOcc.end(0) )
        assert secondOcc == None, log

        # 2.2 test the error caching mechanism (dependencies have changed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        no_std_header_h = self.test.workpath('config.tests', 'no_std_header.h')
        test_h = self.test.write( no_std_header_h,
                                  "/* we are changing a dependency now */\n" );
        try:
            res = checks( self, sconf, TryFunc )
            log = self.test.read( self.test.workpath('config.log') )
            assert res[0] and res[1], res
        finally:
            sconf.Finish()

    def test_TryBuild(self):
        """Test SConf.TryBuild
        """
        # 1 test that we can try a builder that returns a list of nodes
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        class MyBuilder:
            def __init__(self):
                self.prefix = ''
                self.suffix = ''
            def __call__(self, env, target, source):
                class MyNode:
                    def __init__(self, name):
                        self.name = name
                        self.state = None
                        self.waiting_parents = set()
                        self.side_effects = []
                        self.builder = None
                        self.prerequisites = []
                    def disambiguate(self):
                        return self
                    def has_builder(self):
                        return 1
                    def add_pre_action(self, *actions):
                        pass
                    def add_post_action(self, *actions):
                        pass
                    def children(self):
                        return []
                    def get_state(self):
                        return self.state
                    def set_state(self, state):
                        self.state = state
                    def alter_targets(self):
                        return [], None
                    def depends_on(self, nodes):
                        return None
                    def postprocess(self):
                        pass
                    def clear(self):
                        pass
                    def is_up_to_date(self):
                        return None
                    def prepare(self):
                        pass
                    def retrieve_from_cache(self):
                        return 0
                    def build(self, **kw):
                        return
                    def built(self):
                        pass
                    def get_stored_info(self):
                        pass
                    def do_not_store_info(self):
                        pass
                    def get_executor(self):
                        class Executor:
                            pass
                        e = Executor()
                        e.targets = [self]
                        return e
                return [MyNode('n1'), MyNode('n2')]
        try:
            self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
            sconf.TryBuild(self.scons_env.SConfActionBuilder)
        finally:
            sconf.Finish()

    def test_TryCompile(self):
        """Test SConf.TryCompile
        """
        self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
        
    def test_TryLink(self):
        """Test SConf.TryLink
        """
        self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) 

    def test_TryRun(self):
        """Test SConf.TryRun
        """
        def checks(sconf):
            prog = """
#include <stdio.h>
int main() {
  printf( "Hello" );
  return 0;
}
"""
            res1 = sconf.TryRun( prog, ".c" ) 
            res2 = sconf.TryRun( "not a c program\n", ".c" )
            return (res1, res2)
        
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks(sconf)
            assert res[0][0] and res[0][1] == "Hello", res
            assert not res[1][0] and res[1][1] == "", res
        finally:
            sconf.Finish()
        log = self.test.read( self.test.workpath('config.log') )

        # test the caching mechanism
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks(sconf)
            assert res[0][0] and res[0][1] == "Hello", res
            assert not res[1][0] and res[1][1] == "", res
        finally:
            sconf.Finish()
        # we should have exactly one error cached 
        log = self.test.read( self.test.workpath('config.log') )
        expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
        firstOcc = expr.match( log )
        assert firstOcc != None, log
        secondOcc = expr.match( log, firstOcc.end(0) )
        assert secondOcc == None, log


    def test_TryAction(self):
        """Test SConf.TryAction
        """
        def actionOK(target, source, env):
            open(str(target[0]), "w").write( "RUN OK\n" )
            return None
        def actionFAIL(target, source, env):
            return 1
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                  conf_dir=self.test.workpath('config.tests'),
                                  log_file=self.test.workpath('config.log'))
        try:
            (ret, output) = sconf.TryAction(action=actionOK)
            assert ret and output == "RUN OK" + os.linesep, (ret, output)
            (ret, output) = sconf.TryAction(action=actionFAIL)
            assert not ret and output == "", (ret, output)
        finally:
            sconf.Finish()


    def test_CheckHeader(self):<commit_msg>Basic test for CheckCC.<commit_after>#
# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"

import os
import re
import string
import StringIO
import sys
from types import *
import unittest

import TestCmd

sys.stdout = StringIO.StringIO()

if sys.platform == 'win32':
    existing_lib = "msvcrt"
else:
    existing_lib = "m"

class SConfTestCase(unittest.TestCase):

    def setUp(self):
        # we always want to start with a clean directory
        self.save_cwd = os.getcwd()
        self.test = TestCmd.TestCmd(workdir = '') 
        os.chdir(self.test.workpath(''))

    def tearDown(self):
        self.test.cleanup()
        import SCons.SConsign
        SCons.SConsign.Reset()
        os.chdir(self.save_cwd)

    def _resetSConfState(self):
        # Ok, this is tricky, and i do not know, if everything is sane.
        # We try to reset scons' state (including all global variables)
        import SCons.SConsign
        SCons.SConsign.write() # simulate normal scons-finish
        for n in sys.modules.keys():
            if string.split(n, '.')[0] == 'SCons' and n[:12] != 'SCons.compat':
                m = sys.modules[n]
                if type(m) is ModuleType:
                    # if this is really a scons module, clear its namespace
                    del sys.modules[n]
                    m.__dict__.clear()
        # we only use SCons.Environment and SCons.SConf for these tests.
        import SCons.Environment
        import SCons.SConf
        self.Environment = SCons.Environment
        self.SConf = SCons.SConf
        # and we need a new environment, cause references may point to
        # old modules (well, at least this is safe ...)
        self.scons_env = self.Environment.Environment()
        self.scons_env.AppendENVPath('PATH', os.environ['PATH'])

        # we want to do some autodetection here
        # this stuff works with
        #    - cygwin on Windows (using cmd.exe, not bash)
        #    - posix
        #    - msvc on Windows (hopefully)
        if (not self.scons_env.Detect( self.scons_env.subst('$CXX') ) or
            not self.scons_env.Detect( self.scons_env.subst('$CC') ) or
            not self.scons_env.Detect( self.scons_env.subst('$LINK') )):
            raise Exception, "This test needs an installed compiler!"
        if self.scons_env['CXX'] == 'g++':
            global existing_lib
            existing_lib = 'm'

        if sys.platform in ['cygwin', 'win32']:
             # On Windows, SCons.Platform.win32 redefines the builtin
             # file() and open() functions to close the file handles.
             # This interferes with the unittest.py infrastructure in
             # some way.  Just sidestep the issue by restoring the
             # original builtin functions whenever we have to reset
             # all of our global state.

             import __builtin__
             import SCons.Platform.win32

             __builtin__.file = SCons.Platform.win32._builtin_file
             __builtin__.open = SCons.Platform.win32._builtin_open

    def _baseTryXXX(self, TryFunc):
        # TryCompile and TryLink are much the same, so we can test them
        # in one method, we pass the function as a string ('TryCompile',
        # 'TryLink'), so we are aware of reloading modules.
        
        def checks(self, sconf, TryFuncString):
            TryFunc = self.SConf.SConfBase.__dict__[TryFuncString]
            res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" )
            res2 = TryFunc( sconf,
                            '#include "no_std_header.h"\nint main() {return 0; }\n',
                            '.c' )
            return (res1,res2)

        # 1. test initial behaviour (check ok / failed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks( self, sconf, TryFunc )
            assert res[0] and not res[1], res
        finally:
            sconf.Finish()
            
        # 2.1 test the error caching mechanism (no dependencies have changed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks( self, sconf, TryFunc )
            assert res[0] and not res[1], res
        finally:
            sconf.Finish()
        # we should have exactly one one error cached 
        log = self.test.read( self.test.workpath('config.log') )
        expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) 
        firstOcc = expr.match( log )
        assert firstOcc != None, log
        secondOcc = expr.match( log, firstOcc.end(0) )
        assert secondOcc == None, log

        # 2.2 test the error caching mechanism (dependencies have changed)
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        no_std_header_h = self.test.workpath('config.tests', 'no_std_header.h')
        test_h = self.test.write( no_std_header_h,
                                  "/* we are changing a dependency now */\n" );
        try:
            res = checks( self, sconf, TryFunc )
            log = self.test.read( self.test.workpath('config.log') )
            assert res[0] and res[1], res
        finally:
            sconf.Finish()

    def test_TryBuild(self):
        """Test SConf.TryBuild
        """
        # 1 test that we can try a builder that returns a list of nodes
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        class MyBuilder:
            def __init__(self):
                self.prefix = ''
                self.suffix = ''
            def __call__(self, env, target, source):
                class MyNode:
                    def __init__(self, name):
                        self.name = name
                        self.state = None
                        self.waiting_parents = set()
                        self.side_effects = []
                        self.builder = None
                        self.prerequisites = []
                    def disambiguate(self):
                        return self
                    def has_builder(self):
                        return 1
                    def add_pre_action(self, *actions):
                        pass
                    def add_post_action(self, *actions):
                        pass
                    def children(self):
                        return []
                    def get_state(self):
                        return self.state
                    def set_state(self, state):
                        self.state = state
                    def alter_targets(self):
                        return [], None
                    def depends_on(self, nodes):
                        return None
                    def postprocess(self):
                        pass
                    def clear(self):
                        pass
                    def is_up_to_date(self):
                        return None
                    def prepare(self):
                        pass
                    def retrieve_from_cache(self):
                        return 0
                    def build(self, **kw):
                        return
                    def built(self):
                        pass
                    def get_stored_info(self):
                        pass
                    def do_not_store_info(self):
                        pass
                    def get_executor(self):
                        class Executor:
                            pass
                        e = Executor()
                        e.targets = [self]
                        return e
                return [MyNode('n1'), MyNode('n2')]
        try:
            self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
            sconf.TryBuild(self.scons_env.SConfActionBuilder)
        finally:
            sconf.Finish()

    def test_TryCompile(self):
        """Test SConf.TryCompile
        """
        self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile )
        
    def test_TryLink(self):
        """Test SConf.TryLink
        """
        self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) 

    def test_TryRun(self):
        """Test SConf.TryRun
        """
        def checks(sconf):
            prog = """
#include <stdio.h>
int main() {
  printf( "Hello" );
  return 0;
}
"""
            res1 = sconf.TryRun( prog, ".c" ) 
            res2 = sconf.TryRun( "not a c program\n", ".c" )
            return (res1, res2)
        
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks(sconf)
            assert res[0][0] and res[0][1] == "Hello", res
            assert not res[1][0] and res[1][1] == "", res
        finally:
            sconf.Finish()
        log = self.test.read( self.test.workpath('config.log') )

        # test the caching mechanism
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            res = checks(sconf)
            assert res[0][0] and res[0][1] == "Hello", res
            assert not res[1][0] and res[1][1] == "", res
        finally:
            sconf.Finish()
        # we should have exactly one error cached 
        log = self.test.read( self.test.workpath('config.log') )
        expr = re.compile( ".*failed in a previous run and all", re.DOTALL )
        firstOcc = expr.match( log )
        assert firstOcc != None, log
        secondOcc = expr.match( log, firstOcc.end(0) )
        assert secondOcc == None, log


    def test_TryAction(self):
        """Test SConf.TryAction
        """
        def actionOK(target, source, env):
            open(str(target[0]), "w").write( "RUN OK\n" )
            return None
        def actionFAIL(target, source, env):
            return 1
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                  conf_dir=self.test.workpath('config.tests'),
                                  log_file=self.test.workpath('config.log'))
        try:
            (ret, output) = sconf.TryAction(action=actionOK)
            assert ret and output == "RUN OK" + os.linesep, (ret, output)
            (ret, output) = sconf.TryAction(action=actionFAIL)
            assert not ret and output == "", (ret, output)
        finally:
            sconf.Finish()

    def _test_check_compilers(self, comp, func, name):
        """This is the implementation for CheckCC and CheckCXX tests."""
        from copy import deepcopy

        # Check that Check* works
        r = func()
        assert r, "could not find %s ?" % comp

        # Check that Check* does fail if comp is not available in env
        oldcomp = deepcopy(self.scons_env[comp])
        del self.scons_env[comp]
        r = func()
        assert not r, "%s worked wo comp ?" % name

        # Check that Check* does fail if comp is set but empty
        self.scons_env[comp] = ''
        r = func()
        assert not r, "%s worked with comp = '' ?" % name

        # Check that Check* does fail if comp is set to buggy executable
        self.scons_env[comp] = 'thiscccompilerdoesnotexist'
        r = func()
        assert not r, "%s worked with comp = thiscompilerdoesnotexist ?" % name

        # Check that Check* does fail if CFLAGS is buggy
        self.scons_env[comp] = oldcomp
        self.scons_env['%sFLAGS' % comp] = 'qwertyuiop'
        r = func()
        assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % name

    def test_CheckCC(self):
        """Test SConf.CheckCC()
        """
        self._resetSConfState()
        sconf = self.SConf.SConf(self.scons_env,
                                 conf_dir=self.test.workpath('config.tests'),
                                 log_file=self.test.workpath('config.log'))
        try:
            self._test_check_compilers('CC', sconf.CheckCC, 'CheckCC')
        finally:
            sconf.Finish()


    def test_CheckHeader(self):
{'fraction_non_alphanumeric': 0.09652714589020321, 'fraction_numerical': 0.00470124355474674, 'mean_word_length': 1.6059079233353093, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7224476', 'n_tokens_mistral': 7370, 'n_tokens_neox': 7071, 'n_words': 2030}