1"""
2A very crude emulator of dejagnu, just enough to integrate the libbfi
3unittests into the pyobjc ones.
4"""
5import os
6import re
7import sys
8import signal
9from fnmatch import fnmatch
10import unittest
11from distutils.util import get_platform
12from distutils.sysconfig import get_config_var
13
14gDgCommands=re.compile(r'''
15        (?:{\s*(dg-do)\s*run\s*({[^}]*})?\s*})
16        |
17        (?:{\s*(dg-output)\s*"([^"]*)"\s*})
18        ''',
19            re.VERBOSE|re.MULTILINE)
20
21def signame(code):
22    for nm in dir(signal):
23        if nm.startswith('SIG') and nm[3] != '_' \
24                and getattr(signal, nm) == code:
25            return nm
26    return code
27
28def exitCode2Description(code):
29    """
30    Convert the exit code as returned by os.popen().close() to a string
31    """
32    if os.WIFEXITED(code):
33        return 'exited with status %s'%(os.WEXITSTATUS(code),)
34
35    elif os.WIFSIGNALED(code):
36        sig = os.WTERMSIG(code)
37        return 'crashed with signal %s [%s]'%(signame(sig), sig)
38
39    else:
40        return 'exit code %s'%(code,)
41
42def platform_matches(matchstr):
43    # This is a hack
44    if sys.byteorder == 'little':
45        platform = 'i386-apple-darwin'
46    else:
47        platform = 'powerpc-apple-darwin'
48
49    return fnmatch(platform, matchstr)
50
51def parseDG(fdata):
52    result = []
53    for  item in gDgCommands.findall(fdata):
54        if item[0] == 'dg-do':
55            result.append(('run', item[1]))
56        elif item[2] == 'dg-output':
57            result.append(('expect', item[3].decode('string_escape')))
58    return result
59
60
61class DgTestCase (unittest.TestCase):
62    def __init__(self, filename):
63        unittest.TestCase.__init__(self)
64        self.filename = filename
65
66    def runTest(self):
67        script = parseDG(open(self.filename).read())
68        output = []
69
70        for command, data in script:
71            if command == 'run':
72                action = 'run'
73                action_data = data
74            if command == 'expect':
75                output.append(data)
76        output = ''.join(output)
77        output = output.replace('\\', '')
78
79        d = action_data.split()
80        if d and d[1] == 'target':
81            for item in d[2:]:
82                if platform_matches(item):
83                    break
84
85            else:
86                # Test shouldn't be run on this platform
87                return
88
89        # NOTE: We're ignoring the xfail data for now, none of the
90        # testcases are supposed to fail on darwin.
91
92        self.compileTestCase()
93        data = self.runTestCase()
94
95        if output != '':
96            self.assertEquals(data.rstrip(), output.rstrip())
97        os.unlink('/tmp/test.bin')
98
99
100    def shortDescription(self):
101        fn = os.path.basename(self.filename)[:-2]
102        dn = os.path.basename(os.path.dirname(self.filename))
103        return "dejagnu.%s.%s"%(dn, fn)
104
105    def compileTestCase(self):
106        libdir = os.path.join('build', 'temp.%s-%d.%d'%(get_platform(), sys.version_info[0], sys.version_info[1]), 'libffi-src')
107        libffiobjects = self.object_files(libdir)
108
109        if self.filename.endswith('.m'):
110            extra_link = '-framework Foundation'
111        else:
112            extra_link = ''
113
114        commandline='MACOSX_DEPLPOYMENT_TARGET=%s cc %s -g -DMACOSX -Ilibffi-src/include -Ilibffi-src/powerpc -o /tmp/test.bin %s %s %s 2>&1'%(
115                get_config_var('MACOSX_DEPLOYMENT_TARGET'),
116                get_config_var('CFLAGS'), self.filename, ' '.join(libffiobjects),
117		extra_link)
118
119        fp = os.popen(commandline)
120        data = fp.read()
121        xit = fp.close()
122        if xit != None:
123            self.fail("Compile failed[%s]:\n%s"%(xit, data))
124
125
126    def runTestCase(self):
127        os.environ['DYLD_BIND_AT_LAUNCH'] = '1'
128        fp = os.popen('/tmp/test.bin', 'r')
129        del os.environ['DYLD_BIND_AT_LAUNCH']
130        data = fp.read()
131        xit = fp.close()
132        if xit != None:
133            self.fail("Running failed (%s)"%(exitCode2Description(xit),))
134        return data
135
136
137    def object_files(self, basedir):
138        result = []
139        for dirpath, dirnames, filenames in os.walk(basedir):
140            for fn in filenames:
141                if fn.endswith('.o'):
142                    result.append(os.path.join(dirpath, fn))
143        return result
144
145
146def testSuiteForDirectory(dirname):
147    tests = []
148    for fn in os.listdir(dirname):
149        if not fn.endswith('.c') and not fn.endswith('.m'): continue
150        tst = DgTestCase(os.path.join(dirname, fn))
151        if alltests and tst.shortDescription() not in alltests:
152            continue
153        tests.append(tst)
154
155    return unittest.TestSuite(tests)
156
157
158alltests = []
159if __name__ == "__main__":
160    alltests = sys.argv[1:]
161    runner = unittest.TextTestRunner(verbosity=2)
162    runner.run(testSuiteForDirectory('libffi-src/tests/testsuite/libffi.call'))
163