~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to tests/lib/drizzle_test_run/dtr_test_management.py

  • Committer: patrick crews
  • Date: 2011-01-26 21:35:28 UTC
  • mfrom: (2088.9.25 project_steve_austin)
  • mto: This revision was merged to the branch mainline in revision 2121.
  • Revision ID: gleebix@gmail.com-20110126213528-rounf54hyridrak4
Re-merge with trunk to fix make-distcheck

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python
 
2
# -*- mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
 
3
# vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
 
4
#
 
5
# Copyright (C) 2010 Patrick Crews
 
6
#
 
7
""" dtr_test_management:
 
8
    code related to the gathering / analysis / management of 
 
9
    the test cases
 
10
    ie - collecting the list of tests in each suite, then
 
11
    gathering additional, relevant information for the test-runner's dtr
 
12
    mode. (traditional diff-based testing)
 
13
 
 
14
"""
 
15
 
 
16
# imports
 
17
import os
 
18
import re
 
19
import sys
 
20
 
 
21
import lib.test_mgmt.test_management as test_management
 
22
 
 
23
 
 
24
    
 
25
class testCase:
 
26
    """Holds info on a per .test file basis
 
27
       Attributes contain information necessary to execute / validate
 
28
       the test file when it is executed.
 
29
 
 
30
    """
 
31
    def __init__(self, system_manager, test_case=None, test_name=None, suite_name=None
 
32
                 , suite_path=None, test_server_options=[], test_path=None, result_path=None
 
33
                 , comment=None, master_sh=None
 
34
                 , disable=0, innodb_test=1 
 
35
                 , need_debug=0, debug=0):
 
36
        self.system_manager = system_manager
 
37
        self.logging = self.system_manager.logging
 
38
        self.skip_keys = ['system_manager']
 
39
        self.testcase = test_case
 
40
        self.testname = test_name
 
41
        self.suitename = suite_name
 
42
        self.suitepath = suite_path
 
43
        self.fullname = "%s.%s" %(suite_name, test_name)
 
44
        self.testpath = test_path
 
45
        self.resultpath = result_path
 
46
 
 
47
        self.skip_flag = 0
 
48
        self.timezone = "GMT-3"
 
49
        self.component_id = "drizzled"
 
50
        self.slave_count = 0
 
51
        self.master_count = 1
 
52
        self.server_options = test_server_options
 
53
        self.comment = comment
 
54
        self.master_sh = master_sh
 
55
        self.disable = disable
 
56
        self.innodb_test  = innodb_test
 
57
        self.need_debug = need_debug
 
58
        if debug:
 
59
            self.system_manager.logging.debug_class(self)
 
60
 
 
61
    def should_run(self):
 
62
        if self.skip_flag or self.disable:
 
63
            return 0
 
64
        else:
 
65
            return 1
 
66
 
 
67
 
 
68
        
 
69
        
 
70
          
 
71
class testManager(test_management.testManager):
 
72
    """Deals with scanning test directories, gathering test cases, and 
 
73
       collecting per-test information (opt files, etc) for use by the
 
74
       test-runner
 
75
 
 
76
    """
 
77
 
 
78
    def process_suite(self,suite_dir):
 
79
        """Process a test suite.
 
80
           This includes searching for tests in test_list and only
 
81
           working with the named tests (all tests in suite is the default)
 
82
           Further processing includes reading the disabled.def file
 
83
           to know which tests to skip, processing the suite.opt file,
 
84
           and processing the individual test cases for data relevant
 
85
           to the rest of the test-runner
 
86
        
 
87
        """
 
88
 
 
89
        if self.verbose:
 
90
                self.system_manager.logging.verbose("Processing suite: %s" %(suite_dir))
 
91
 
 
92
        # Generate our test and result files:
 
93
        testdir = os.path.join(suite_dir, 't')
 
94
        resultdir = os.path.join(suite_dir, 'r')
 
95
 
 
96
        # Do basic checks to make sure this is worth further work
 
97
        self.check_suite(suite_dir, testdir, resultdir)      
 
98
 
 
99
        # Get suite-level options
 
100
        suite_options = []
 
101
        suite_options = self.process_suite_options(suite_dir) 
 
102
 
 
103
        # Get the 'name' of the suite.  This can require some processing
 
104
        # But the name is useful for reporting and whatnot
 
105
        suite_name = self.get_suite_name(suite_dir)
 
106
        
 
107
        # Get the contents of the testdir and filter it accordingly
 
108
        # This applies do-test / skip-test filters and any specific
 
109
        # test case names        
 
110
        testlist = self.testlist_filter(os.listdir(testdir))
 
111
        # sort our list if no individual tests were specified
 
112
        if not self.desired_tests:
 
113
            testlist.sort()
 
114
                       
 
115
        # gather deeper information on tests we are interested in
 
116
        if testlist:
 
117
            # We have tests we want to process, we gather additional information
 
118
            # that is useful at the suite level.  This includes disabled tests          
 
119
            # Gather disabled test list.  
 
120
            # This is used in process_test_file()
 
121
            disabled_tests = {}
 
122
            disabled_tests = self.process_disabled_test_file(testdir)                        
 
123
            for test_case in testlist:
 
124
                self.add_test(self.process_test_file(suite_dir,
 
125
                                              suite_name, suite_options
 
126
                                              , disabled_tests, testdir 
 
127
                                              , resultdir, test_case))
 
128
 
 
129
    def process_test_file(self, suite_dir, suite_name, suite_options 
 
130
                          , disabled_tests, testdir 
 
131
                          , resultdir, test_case):
 
132
        """ We generate / find / store all the relevant information per-test.
 
133
            This information will be used when actually executing the test
 
134
            We store the data in a testCase object 
 
135
 
 
136
        """
 
137
        
 
138
        test_server_options = suite_options
 
139
        test_name = test_case.replace('.test','')
 
140
        if self.verbose:
 
141
            self.system_manager.logging.verbose("Processing test: %s.%s" %(suite_name,test_name))
 
142
 
 
143
        
 
144
        # Fix this , create a testCase with gather_test_data() passed
 
145
        # as the argument
 
146
        # Ensure we pass everything we need and use it all
 
147
        (  test_path
 
148
         , result_file_name
 
149
         , result_path
 
150
         , comment
 
151
         , master_sh
 
152
         , test_server_options
 
153
         , disable
 
154
         , innodb_test
 
155
         , need_debug) = self.gather_test_data(test_case, test_name,
 
156
                                 suite_name, test_server_options,testdir, 
 
157
                                 resultdir, disabled_tests)  
 
158
        test_case = testCase(self.system_manager, test_case, test_name, suite_name, 
 
159
                             suite_dir, test_server_options,test_path, result_path,
 
160
                             debug=self.debug)      
 
161
        return test_case
 
162
 
 
163
 
 
164
########################################################################
 
165
# utility functions
 
166
#
 
167
# Stuff that helps us out and simplifies our main functions
 
168
# But isn't that important unless you need to dig deep
 
169
########################################################################
 
170
 
 
171
    def gather_test_data(self, test_case, test_name, suite_name,
 
172
                         test_server_options, testdir, resultdir, disabled_tests):
 
173
        """ We gather all of the data needed to produce a testCase for
 
174
            a given test
 
175
 
 
176
        """
 
177
        """
 
178
        (self, test_case=None, test_name=None, suite_name=None
 
179
                 , test_server_options=[], test_path=None, result_path=None
 
180
                 , comment=None, master_sh=None, disable=0, innodb_test=0 
 
181
                 , need_debug=0, debug=0):
 
182
        """
 
183
        test_path = os.path.join(testdir,test_case)
 
184
        result_file_name = test_name+'.result'       
 
185
        result_path = self.find_result_path(resultdir, result_file_name)
 
186
        comment = None
 
187
        master_sh = None
 
188
        master_opt_path = test_path.replace('.test','-master.opt')
 
189
        test_server_options = test_server_options + self.process_opt_file(
 
190
                                                           master_opt_path)
 
191
        (disable, comment) = self.check_if_disabled(disabled_tests, test_name)
 
192
        innodb_test = 0
 
193
        need_debug = 0
 
194
        return (test_path, result_file_name, result_path, comment, master_sh,
 
195
                test_server_options, disable, innodb_test, need_debug)
 
196
 
 
197
    def check_suite(self, suite_dir, testdir, resultdir):
 
198
        """Handle basic checks of the suite:
 
199
           does the suite exist?
 
200
           is there a /t dir?
 
201
           is there a /r dir?
 
202
           Error and die if not
 
203
 
 
204
        """
 
205
        # We expect suite to be a path name, no fuzzy searching
 
206
        if not os.path.exists(suite_dir):
 
207
            self.system_manager.logging.error("Suite: %s does not exist" %(suite_dir))
 
208
            sys.exit(1)
 
209
                
 
210
        # Ensure our test and result directories are present
 
211
        if not os.path.exists(testdir):
 
212
            self.system_manager.logging.error("Suite: %s does not have a 't' directory (expected location for test files)" %(suite_dir))
 
213
            sys.exit(1)
 
214
        if not os.path.exists(resultdir):
 
215
            self.system_manager.logging.error("Suite: %s does not have an 'r' directory (expected location for result files)" %(suite_dir))
 
216
            sys.exit(1)
 
217
 
 
218
    def get_suite_name(self, suite_dir):
 
219
        """ Get the 'name' of the suite
 
220
            This can either be the path basename or one directory up, as
 
221
            in the case of files in the drizzle/plugins directory
 
222
        
 
223
        """
 
224
 
 
225
        # We trim any trailing path delimiters as python returns
 
226
        # '' for basedir if the path ends that way
 
227
        # BEGIN horrible hack to accomodate bad location of main suite : /
 
228
        if suite_dir == self.testdir:
 
229
            return 'main'
 
230
        # END horrible hack : /
 
231
        if suite_dir.endswith('/'):
 
232
            suite_dir=suite_dir[:-1]
 
233
        suite_dir_root,suite_dir_basename = os.path.split(suite_dir)
 
234
        if suite_dir_basename == 'tests' or suite_dir_basename == 'drizzle-tests':
 
235
            suite_name = os.path.basename(suite_dir_root)
 
236
        else:
 
237
            suite_name = suite_dir_basename
 
238
        if self.debug:
 
239
            self.system_manager.logging.debug("Suite_name:  %s" %(suite_name))
 
240
        return suite_name
 
241
 
 
242
    def process_suite_options(self, suite_dir):
 
243
        """ Process the suite.opt and master.opt files
 
244
            that reside at the suite-level if they exist.
 
245
            Return a list of the options found
 
246
 
 
247
        """
 
248
        found_options = []
 
249
        opt_files = ['t/master.opt','t/suite.opt']
 
250
        for opt_file in opt_files:
 
251
            found_options = found_options + self.process_opt_file(os.path.join(suite_dir,opt_file))
 
252
        return found_options
 
253
 
 
254
    def process_disabled_test_file(self, testdir):
 
255
        """ Checks and processes the suite's disabled.def
 
256
            file.  This file must reside in the suite/t directory
 
257
            It must be in the format:
 
258
            test-name : comment (eg BugNNNN - bug on hold, test disabled)
 
259
            In reality a test should *never* be disabled.  EVER.
 
260
            However, we keep this as a bit of utility
 
261
 
 
262
        """
 
263
        disabled_tests = {}
 
264
        disabled_def_path = os.path.join(testdir,'disabled.def')
 
265
        if not os.path.exists(disabled_def_path):
 
266
            return disabled_tests
 
267
 
 
268
        try:
 
269
            disabled_test_file = open(disabled_def_path,'r')
 
270
        except IOError, e: 
 
271
            self.system_manager.logging.error("Problem opening disabled.def file: %s" %(disabled_def_path))
 
272
            sys.exit(1)
 
273
 
 
274
        if self.debug:
 
275
            self.system_manager.logging.debug("Processing disabled.def file: %s" %(disabled_def_path))
 
276
        disabled_bug_pattern = re.compile("[\S]+[\s]+:[\s]+[\S]")
 
277
        
 
278
        for line in disabled_test_file:
 
279
            line = line.strip()
 
280
            if not line.startswith('#'): # comment
 
281
                if re.match(disabled_test_pattern,line):
 
282
                    if self.debug:
 
283
                        self.system_manager.logging.debug("found disabled test - %s" %(line))
 
284
                    test_name, test_comment = line.split(':')
 
285
                    disabled_tests[test_name.strip()]=test_comment.strip() 
 
286
            
 
287
        disabled_test_file.close()
 
288
        return disabled_tests
 
289
        
 
290
 
 
291
    def process_opt_file(self, opt_file_path):
 
292
        """ Process a test-run '.opt' file.
 
293
        These files contain test and suite-specific server options
 
294
        (ie what options the server needs to use for the test) 
 
295
 
 
296
        Returns a list of the options (we don't really validate...yet)
 
297
         
 
298
        NOTE:  test-run.pl allows for server *and* system options
 
299
        (eg timezone, slave_count, etc) in opt files.  We don't.
 
300
        None of our tests use this and we should probably avoid it
 
301
        We can introduce server and system .opt files or even better
 
302
        would be to use config files as we do with drizzle-automation
 
303
        This would allow us to specify options for several different
 
304
        things in a single file, but in a clean and standardized manner
 
305
 
 
306
        """
 
307
        found_options = []
 
308
        if not os.path.exists(opt_file_path):
 
309
            return found_options
 
310
 
 
311
        try:
 
312
            opt_file = open(opt_file_path,'r')
 
313
        except IOError, e: 
 
314
            self.system_manager.logging.error("Problem opening option file: %s" %(opt_file_path))
 
315
            sys.exit(1)
 
316
 
 
317
        if self.debug:
 
318
            self.system_manager.logging.debug("Processing opt file: %s" %(opt_file_path))
 
319
        for line in opt_file:
 
320
            options = line.split('--')
 
321
            if options:
 
322
                for option in options:
 
323
                    if option:
 
324
                        if 'restart' in option or '#' in option:
 
325
                            option = 'restart'
 
326
                        found_options.append('--%s' %(option.strip()))
 
327
        opt_file.close()
 
328
        return found_options
 
329
 
 
330
    def testlist_filter(self, testlist):
 
331
        """ Filter our list of testdir contents based on several 
 
332
            criteria.  This looks for user-specified test-cases
 
333
            and applies the do-test and skip-test filters
 
334
 
 
335
            Returns the list of tests that we want to execute
 
336
            for further processing
 
337
 
 
338
        """
 
339
 
 
340
        # We want only .test files
 
341
        # Possible TODO:  allow alternate test extensions
 
342
        testlist = [test_file for test_file in testlist if test_file.endswith('.test')]
 
343
         
 
344
        # Search for specific test names
 
345
        if self.desired_tests: # We have specific, named tests we want from the suite(s)
 
346
           tests_to_use = []
 
347
           for test in self.desired_tests:
 
348
               if test.endswith('.test'): 
 
349
                   pass
 
350
               else:
 
351
                   test = test+'.test'
 
352
               if test in testlist:
 
353
                   tests_to_use.append(test)
 
354
           testlist = tests_to_use
 
355
 
 
356
        # TODO:  Allow for regex?
 
357
        # Apply do-test filter
 
358
        if self.dotest:
 
359
            testlist = [test_file for test_file in testlist if test_file.startswith(self.dotest)]
 
360
        # Apply skip-test filter
 
361
        if self.skiptest:
 
362
            testlist = [test_file for test_file in testlist if not test_file.startswith(self.skiptest)]
 
363
        return testlist
 
364
 
 
365
    def find_result_path(self, result_dir, result_file_name):
 
366
        """ This is copied from test-run.pl dtr_cases.pl
 
367
            If we have an engine option passed in and the 
 
368
            path resultdir/engine/testname.result exists, that is 
 
369
            our .result file
 
370
        
 
371
            Need to check if we really need this - maybe PBXT?
 
372
 
 
373
        """
 
374
        result_path = os.path.join(result_dir,result_file_name)
 
375
        if self.engine:
 
376
            candidate_path = os.path.join(result_dir, self.engine, 
 
377
                                          result_file_name)
 
378
            if os.path.exists(candidate_path):
 
379
                result_path = candidate_path
 
380
        return result_path
 
381
 
 
382
    def check_if_disabled(self, disabled_tests, test_name):
 
383
        """ Scan the list of disabled tests if it exists to see 
 
384
            if the test is disabled.
 
385
        
 
386
        """
 
387
   
 
388
        if disabled_tests:
 
389
            if test_name in disabled_tests:
 
390
                if self.debug:
 
391
                    self.system_manager.logging.debug("%s says - I'm disabled" %(test_name))
 
392
                return (1, disabled_tests[test_name])
 
393
        return (0,None)