1#!/usr/bin/env python2
2
3import os
4import sys
5import time
6from glob import glob
7
8from TestCommon import *
9
10startTime = time.time()
11
12
13class RunError(Exception):
14    """Test Run Error.
15
16    Raised by Test.run() if the process exits with a non-zero status,
17    which indicates an error.
18    """
19
20
21class Test(object):
22
23
24    ## Init ##
25
26    def __init__(self):
27        pass
28
29
30    ## Customization ##
31
32    def modelNames(self):
33        return self._modelNames
34
35
36    ## Testing ##
37
38    def main(self, args=sys.argv):
39        # The tests are listed explicitly rather than scanning for them (via glob)
40        # in order to perform them in a certain order (simplest to most complex)
41        self.readArgs(args)
42        results = []
43        for self._modelName in self.modelNames():
44            print '*** %s ***\n' % self._modelName
45            if not self._modelName.endswith('.mkmodel'):
46                self._modelName += '.mkmodel'
47            didFail = None
48            try:
49                if self.canRun():
50                    # support multiple config files for testing
51                    configFilenames = glob(os.path.join(
52                        self._modelName, 'Settings*.config'))
53                    if configFilenames:
54                        configFilenames = [os.path.basename(p) for p in configFilenames]
55                    else:
56                        configFilenames = ['Settings.config']
57                    for configFilename in configFilenames:
58                        self.runCompletePath(configFilename)
59                else:
60                    didFail = '       skipped'
61            except RunError:
62                didFail = '*** FAILED ***'
63            results.append((self._modelName, didFail))
64
65        self.printInfo()
66        self.printResults(results)
67
68        # print duration for curiosity's sake
69        print
70        duration = time.time() - startTime
71        print '%.0f seconds' % (duration)
72
73    def readArgs(self, args):
74        if len(args) > 1:
75            self._modelNames = args[1:]
76        else:
77            self._modelNames = '''
78                MKBasic MKNone MKString MKDateTime MKEnums MKDefaultMinMax
79                MKTypeValueChecking MKInheritance MKInheritanceAbstract
80                MKList MKObjRef MKObjRefReuse MKDelete MKDeleteMark
81                MKMultipleStores MKMultipleThreads
82                MKModelInh1 MKModelInh2 MKModelInh3
83                MKExcel
84            '''.split()
85
86    def canRun(self):
87        path = os.path.join(self._modelName, 'CanRun.py')
88        if os.path.exists(path):
89            file = open(path)
90            names = {}
91            exec file in names
92            assert 'CanRun' in names, 'expecting a CanRun() function'
93            return names['CanRun']()
94        else:
95            return True
96
97    def runCompletePath(self, configFilename='Settings.config'):
98        self._configFilename = configFilename
99        self.testDesign()
100        self.testEmpty()
101        self.insertSamples()
102        self.testSamples()
103        rmdir(workDir)
104        print '\n'
105
106    def testEmpty(self):
107        """Run all TestEmpty*.py files in the model, in alphabetical order."""
108        names = glob(os.path.join(self._modelName, 'TestEmpty*.py'))
109        if names:
110            for name in sorted(names):
111                self.createDatabase()
112                self.testRun(os.path.basename(name), deleteData=False)
113        else:
114            self.createDatabase()
115
116    def testSamples(self):
117        self.testRun('TestSamples.py', deleteData=False)
118
119    def testRun(self, pyFile, deleteData):
120        if os.path.exists(os.path.join(self._modelName, pyFile)):
121            print '%s:' % pyFile
122            deleteData = 'yes' if deleteData else 'no'
123            self.run('python TestRun.py %s %s %s delete=%s' % (
124                self._modelName, self._configFilename, pyFile, deleteData))
125        else:
126            print 'NO %s TO TEST.' % pyFile
127
128    def testDesign(self):
129        self.run('python TestDesign.py %s %s' % (
130            self._modelName, self._configFilename))
131
132    def executeFile(self, filename):
133        filename = os.path.normpath(filename)
134        if os.path.exists(filename):
135            if '%s' in sqlCommand:
136                cmd = sqlCommand % filename
137            else:
138                cmd = '%s < %s' % (sqlCommand, filename)
139            self.run(cmd)
140
141    def createDatabase(self):
142        self.executeFile(workDir + '/GeneratedSQL/Create.sql')
143
144    def insertSamples(self):
145        self.createDatabase()
146        self.executeFile(workDir + '/GeneratedSQL/InsertSamples.sql')
147
148    def printInfo(self):
149        print
150        print 'SYSTEM INFO'
151        print '-----------'
152        print 'sys.version =', sys.version
153        print 'sys.platform =', sys.platform
154        print 'os.name =', os.name
155        if hasattr(sys, 'getwindowsversion'):
156            print 'sys.getwindowsversion() =', sys.getwindowsversion()
157        print 'os.getcwd() =', os.getcwd()
158        print 'dbName =', dbName
159        if sqlVersionCommand:
160            self.run(sqlVersionCommand)
161
162        # Since Test.py runs things via os.system() it won't actually have
163        # the DB API module loaded. But that's really desireable so its
164        # version number can be printed out, so import the store:
165        objStoreName = dbName + 'ObjectStore'
166        values = {}
167        exec 'import MiddleKit.Run.' + objStoreName in values
168
169        out = sys.stdout
170        out.write('modules with versions:\n')
171        modules = sorted((m for m in sys.modules.values()
172            if m is not None and m.__name__ != 'sys'),
173            key=lambda m: m.__name__)
174        for mod in modules:
175            ver = getattr(mod, 'version', None)
176            verInfo = getattr(mod, 'version_info', None)
177            if ver or verInfo:
178                out.write('    %s' % mod.__name__)
179                if verInfo:
180                    out.write(', %s' % (verInfo,))
181                if ver:
182                    out.write(', %r' % ver)
183                out.write(', %s' % getattr(mod, '__file__', '(built-in)'))
184                out.write('\n')
185
186    def printResults(self, results):
187        """Summarize the results of each test."""
188        print
189        print 'RESULTS'
190        print '-------'
191        for name, outcome in results:
192            if not outcome:
193                outcome = '     succeeded'
194            print outcome, name
195
196
197    ## Self utility ##
198
199    def run(self, cmd):
200        """Self utility method to run a system command.
201
202        If the command has a non-zero exit status, raises RunError.
203        Otherwise, returns 0.
204
205        Note that on Windows ME, os.system() always returns 0 even if the
206        program was a Python program that exited via sys.exit(1) or an
207        uncaught exception. On Windows XP Pro SP 1, this problem does not
208        occur. Windows ME has plenty of other problems as well; avoid it.
209        """
210        print '<cmd>', cmd
211        sys.stdout.flush()
212        sys.stderr.flush()
213        returnCode = os.system(cmd)
214        sys.stdout.flush()
215        sys.stderr.flush()
216
217        if returnCode:
218            raise RunError(returnCode)
219
220        # print '>> RETURN CODE =', returnCode
221        return returnCode
222
223
224if __name__ == '__main__':
225    Test().main()
226