struct / union in initializer, RFE #901.
[sdcc.git] / sdcc / support / regression / cases / generate-cases.py
blob2fb924b930d17203bee545650c804926af21f740
1 from HTMLgen import TemplateDocument
2 import sys, re, tempfile, os
4 """See InstanceGenerator for a description of this file"""
6 # Globals
7 # Directory that the generated files should be placed into
8 outdir = sys.argv[2]
10 # Start of the test function table definition
11 testfuntableheader = """
12 void
13 __runSuite(void)
15 """
17 # End of the test function table definition
18 testfuntablefooter = """}
19 """
21 # Code to generate the suite function
22 testfunsuite = """
23 __code const char *
24 __getSuiteName(void)
26 return "{testcase}";
28 """
30 # Utility functions
31 def createdir(path):
32 """Creates a directory if it doesn't exist"""
33 if not os.path.isdir(path):
34 os.mkdir(path)
36 class InstanceGenerator:
37 """Test case iteration generator.
38 Takes the template given as the first argument, pulls out all the meta
39 iteration information, and generates an instance for each combination
40 of the names and types.
42 See doc/test_suite_spec.tex for more information on the template file
43 format."""
45 def __init__(self, inname):
46 self.inname = inname
47 # Initalise the replacements hash.
48 # Map of name to values.
49 self.replacements = []
50 # Initalise the function list hash.
51 self.functions = []
52 # Emit the suite wrapper into a temporary file
53 self.tmpname = tempfile.mktemp()
54 (self.dirname, self.filename) = os.path.split(self.inname)
55 (self.basename, self.ext) = os.path.splitext (self.filename)
56 if self.ext == ".in":
57 (self.basename, self.ext) = os.path.splitext (self.filename[:-3])
59 def permute(self, basename, repl, trans = {}):
60 """Permutes across all of the names. For each value, recursively creates
61 a mangled form of the name, this value, and all the combinations of
62 the remaining values. At the tail of the recursion when one full
63 combination is built, generates an instance of the test case from
64 the template."""
65 basepath = os.path.join(outdir, basename)
66 if len(repl) == 0:
67 # End of the recursion.
68 # Set the runtime substitutions.
69 trans['testcase'] = re.sub(r'\\', r'\\\\', basename)
70 # Create the instance from the template
71 T = TemplateDocument(self.tmpname)
72 T.substitutions = trans
73 T.write(basepath + self.ext)
74 else:
75 # Pull off this key, then recursively iterate through the rest.
76 key = repl[0][0]
77 for part in repl[0][1]:
78 trans[key] = part
79 # Turn a empty string into something decent for a filename
80 if not part:
81 part = 'none'
82 # Remove any bad characters from the filename.
83 part = re.sub(r'\s+', r'_', part)
84 # The slice operator (_[1:]) creates a copy of the list missing the
85 # first element.
86 # Can't use '-' as a seperator due to the mcs51 assembler.
87 self.permute(basename + '_' + key + '_' + part, repl[1:], trans)
89 def writetemplate(self):
90 """Given a template file and a temporary name writes out a verbatim copy
91 of the source file and adds the suite table and functions."""
92 if sys.version_info[0]<3:
93 fout = open(self.tmpname, 'w')
94 else:
95 fout = open(self.tmpname, 'w', encoding="latin-1")
97 for line in self.lines:
98 fout.write(line)
100 # Emmit the suite table
101 fout.write(testfuntableheader)
103 n = 0;
104 for fun in self.functions:
105 # Turn the function definition into a function call
106 fout.write(" __prints(\"Running " + fun + "\\n\");\n");
107 fout.write(' ' + fun + "();\n")
108 n += 1;
110 fout.write(testfuntablefooter)
111 fout.write("\nconst int __numCases = " + str(n) + ";\n")
112 fout.write(testfunsuite);
114 fout.close()
115 return n
117 def readfile(self):
118 """Read in all of the input file."""
119 if sys.version_info[0]<3:
120 fin = open(self.inname)
121 else:
122 fin = open(self.inname, encoding="latin-1")
123 self.lines = fin.readlines()
124 fin.close()
126 def parse(self):
127 # Start off in the header.
128 inheader = 1;
130 # Iterate over the source file and pull out the meta data.
131 for line in self.lines:
132 line = line.strip()
134 # If we are still in the header, see if this is a substitution line
135 if inheader:
136 # A substitution line has a ':' in it
137 if re.search(r':', line) != None:
138 # Split out the name from the values
139 (name, rawvalues) = re.split(r':', line)
140 # Split the values at the commas
141 values = re.split(r',', rawvalues)
143 # Trim the name
144 name = name.strip()
145 # Trim all the values
146 values = [value.strip() for value in values]
148 self.replacements.append((name, values))
149 elif re.search(r'\*/', line) != None:
150 # Hit the end of the comments
151 inheader = 0;
152 else:
153 # Do nothing.
154 None
155 else:
156 # Pull out any test function names
157 m = re.match(r'^(?:\W*void\W+)?\W*(test\w*)\W*\(\W*void\W*\)', line)
158 if m != None:
159 self.functions.append(m.group(1))
161 def generate(self):
162 """Main function. Generates all of the instances."""
163 self.readfile()
164 self.parse()
165 if self.writetemplate() == 0:
166 sys.stderr.write("Empty function list in " + self.inname + "!\n")
168 # Create the output directory if it doesn't exist
169 createdir(outdir)
171 # Generate
172 self.permute(self.basename, self.replacements)
174 # Remove the temporary file
175 os.remove(self.tmpname)
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)
183 # Input name is the first arg.
185 s = InstanceGenerator(sys.argv[1])
186 s.generate()
188 if __name__ == '__main__':
189 main()