Imported the regression suite
[fw/sdcc] / support / regression / generate-cases.py
1 from HTMLgen import TemplateDocument
2 import sys, re, tempfile, os
3
4 # Globals
5 # Directory that the generated files should be placed into
6 outdir = 'cases'
7
8 # Start of the test function table definition
9 testfuntableheader = """
10 static void (*const _tests[])(void) = {
11 """
12
13
14 # End of the test function table definition
15 testfuntablefooter = """\tNULL
16 };
17 """
18
19 # Code to generate the suite function
20 testfunsuite = """
21 void **
22 suite(void)
23 {
24     return (void **)_tests;
25 }
26
27 const char *
28 getSuiteName(void)
29 {
30     return "{testcase}";
31 }
32 """ 
33
34 # Utility functions
35 def trim(a):
36     """Removes all white space from the start and the end of a string.
37     Like java.lang.String.trim"""
38     ret = chomp(re.sub(r'^\s+', '', a))
39     return ret
40     
41 def chomp(a):
42     """Removes all white space from the end of a string.
43     Like perl's chomp"""
44     return re.sub(r'\s+$', '', a)
45
46 def createdir(path):
47     """Creates a directory if it doesn't exist"""
48     if not os.path.isdir(path):
49         os.mkdir(path)
50
51 class InstanceGenerator:
52     """Test case iteration generator.
53     Takes the template given as the first argument, pulls out all the meta
54     iteration information, and generates an instance for each combination
55     of the names and types.
56
57     See doc/test_suite_spec.tex for more information on the template file
58     format."""
59
60     def __init__(self, inname):
61         self.inname = inname
62         # Initalise the replacements hash.
63         # Map of name to values.
64         self.replacements = { }
65         # Initalise the function list hash.
66         self.functions = []
67         # Emit the suite wrapper into a temporary file
68         self.tmpname = tempfile.mktemp()
69         (self.basename, self.ext) = re.split(r'\.', self.inname)
70         self.ext = '.' + self.ext
71
72     def permute(self, basepath, keys, trans = {}):
73         """Permutes across all of the names.  For each value, recursivly creates
74         a mangled form of the name, this value, and all the combinations of
75         the remaining values.  At the tail of the recursion when one full
76         combination is built, generates an instance of the test case from
77         the template."""
78         if len(keys) == 0:
79             # End of the recursion.
80             # Set the runtime substitutions.
81             trans['testcase'] = basepath
82             # Create the instance from the template
83             T = TemplateDocument(self.tmpname)
84             T.substitutions = trans
85             T.write(basepath + self.ext)
86         else:
87             # Pull off this key, then recursivly iterate through the rest.
88             key = keys[0]
89             for part in self.replacements[key]:
90                 trans[key] = part
91                 # Turn a empty string into something decent for a filename
92                 if not part:
93                     part = 'none'
94                 # Remove any bad characters from the filename.
95                 part = re.sub(r'\s+', r'_', part)
96                 # The slice operator (keys[1:]) creates a copy of the list missing the
97                 # first element.
98                 # Can't use '-' as a seperator due to the mcs51 assembler.
99                 self.permute(basepath + '_' + key + '_' + part, keys[1:], trans) 
100
101     def writetemplate(self):
102         """Given a template file and a temporary name writes out a verbatim copy
103         of the source file and adds the suite table and functions."""
104         fout = open(self.tmpname, 'w')
105
106         for line in self.lines:
107             fout.write(line)
108
109         # Emmit the suite table
110         fout.write(testfuntableheader)
111
112         for fun in self.functions:
113             # Turn the function definition into a pointer
114             fun = re.sub(r'\(\w+\)', '', fun)
115             fout.write("\t" + fun + ",\n")
116
117         fout.write(testfuntablefooter)
118         fout.write(testfunsuite);
119         
120         fout.close()
121
122     def readfile(self):
123         """Read in all of the input file."""
124         fin = open(self.inname)
125         self.lines = fin.readlines()
126         fin.close()
127
128     def parse(self):
129         # Start off in the header.
130         inheader = 1;
131
132         # Iterate over the source file and pull out the meta data.
133         for line in self.lines:
134             line = trim(line)
135
136             # If we are still in the header, see if this is a substitution line
137             if inheader:
138                 # A substitution line has a ':' in it
139                 if re.search(r':', line) != None:
140                     # Split out the name from the values
141                     (name, rawvalues) = re.split(r':', line)
142                     # Split the values at the commas
143                     values = re.split(r',', rawvalues)
144                     
145                     # Trim the name
146                     name = trim(name)
147                     # Trim all the values
148                     values = map(trim, values)
149                     
150                     self.replacements[name] = values
151                 elif re.search(r'\*/', line) != None:
152                     # Hit the end of the comments
153                     inheader = 0;
154                 else:
155                     # Do nothing.
156                     None
157             else:
158                 # Pull out any test function names
159                 if re.search(r'^test\w+\(\w+\)', line) != None:
160                     self.functions.append(line)
161
162     def generate(self):
163         """Main function.  Generates all of the instances."""
164         self.readfile()
165         self.parse()
166         self.writetemplate()
167
168         # Create the output directory if it doesn't exist
169         createdir(outdir)
170
171         # Generate
172         self.permute(os.path.join(outdir, self.basename), self.replacements.keys())
173
174         # Remove the temporary file
175         os.remove(self.tmpname)
176
177 # Check and parse the command line arguments
178 if len(sys.argv) < 2:
179     # PENDING: How to throw an error?
180     print "usage: generate-cases.py template.c"
181
182 # Input name is the first arg.
183
184 s = InstanceGenerator(sys.argv[1])
185 s.generate()