* support/regression/generate-cases.py: implemented more flexible rule
[fw/sdcc] / support / regression / generate-cases.py
1 from HTMLgen import TemplateDocument
2 import sys, re, tempfile, os
3
4 """See InstanceGenerator for a description of this file"""
5
6 # Globals
7 # Directory that the generated files should be placed into
8 outdir = sys.argv[2]
9
10 # Start of the test function table definition
11 testfuntableheader = """
12 void
13 __runSuite(void)
14 {
15 """
16
17 # End of the test function table definition
18 testfuntablefooter = """}
19 """
20
21 # Code to generate the suite function
22 testfunsuite = """
23 code const char *
24 __getSuiteName(void)
25 {
26   return "{testcase}";
27 }
28 """ 
29
30 # Utility functions
31 def trim(a):
32     """Removes all white space from the start and the end of a string.
33     Like java.lang.String.trim"""
34     ret = chomp(re.sub(r'^\s+', '', a))
35     return ret
36     
37 def chomp(a):
38     """Removes all white space from the end of a string.
39     Like perl's chomp"""
40     return re.sub(r'\s+$', '', a)
41
42 def createdir(path):
43     """Creates a directory if it doesn't exist"""
44     if not os.path.isdir(path):
45         os.mkdir(path)
46
47 class InstanceGenerator:
48     """Test case iteration generator.
49     Takes the template given as the first argument, pulls out all the meta
50     iteration information, and generates an instance for each combination
51     of the names and types.
52
53     See doc/test_suite_spec.tex for more information on the template file
54     format."""
55
56     def __init__(self, inname):
57         self.inname = inname
58         # Initalise the replacements hash.
59         # Map of name to values.
60         self.replacements = { }
61         # Initalise the function list hash.
62         self.functions = []
63         # Emit the suite wrapper into a temporary file
64         self.tmpname = tempfile.mktemp()
65         (self.dirname, self.filename) = os.path.split(self.inname)
66         (self.basename, self.ext) = os.path.splitext (self.filename)
67
68     def permute(self, basepath, keys, trans = {}):
69         """Permutes across all of the names.  For each value, recursivly creates
70         a mangled form of the name, this value, and all the combinations of
71         the remaining values.  At the tail of the recursion when one full
72         combination is built, generates an instance of the test case from
73         the template."""
74         if len(keys) == 0:
75             # End of the recursion.
76             # Set the runtime substitutions.
77             trans['testcase'] = re.sub(r'\\', r'\\\\', basepath)
78             # Create the instance from the template
79             T = TemplateDocument(self.tmpname)
80             T.substitutions = trans
81             T.write(basepath + self.ext)
82         else:
83             # Pull off this key, then recursivly iterate through the rest.
84             key = keys[0]
85             for part in self.replacements[key]:
86                 trans[key] = part
87                 # Turn a empty string into something decent for a filename
88                 if not part:
89                     part = 'none'
90                 # Remove any bad characters from the filename.
91                 part = re.sub(r'\s+', r'_', part)
92                 # The slice operator (keys[1:]) creates a copy of the list missing the
93                 # first element.
94                 # Can't use '-' as a seperator due to the mcs51 assembler.
95                 self.permute(basepath + '_' + key + '_' + part, keys[1:], trans) 
96
97     def writetemplate(self):
98         """Given a template file and a temporary name writes out a verbatim copy
99         of the source file and adds the suite table and functions."""
100         fout = open(self.tmpname, 'w')
101
102         for line in self.lines:
103             fout.write(line)
104
105         # Emmit the suite table
106         fout.write(testfuntableheader)
107
108         n = 0;
109         for fun in self.functions:
110             # Turn the function definition into a pointer
111             fun = re.sub(r'\(\w+\)', '', fun)
112             fout.write("  __prints(\"Running " + fun + "\\n\");\n");
113             fout.write('  ' + fun + "();\n")
114             n += 1;
115
116         fout.write(testfuntablefooter)
117         fout.write("\nconst int __numCases = " + str(n) + ";\n")
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'^\W*test\w*\W*\(\W*void\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 def main():
178     # Check and parse the command line arguments
179     if len(sys.argv) < 3:
180         print "usage: generate-cases.py template.c outdir"
181         sys.exit(-1)
182         
183     # Input name is the first arg.
184
185     s = InstanceGenerator(sys.argv[1])
186     s.generate()
187
188 if __name__ == '__main__':
189     main()