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