API
 
Loading...
Searching...
No Matches
generateEntropyTests.py
Go to the documentation of this file.
1#!/bin/env python3
2
3import os
4import sys
5import pathlib
6import getopt
7import random
8from generateTemplatedCatch2Tests import *
9import json
10
11'''
12command line options:
13 -n number of flatlog types to include
14 -e entropy number. how many flatlogs will be created and checked
15Future add:
16 -s random seed val
17 config for specifying log types to use
18'''
19# check jinja2 is installed. install it if not
20try:
21 import jinja2
22except ModuleNotFoundError:
23 print("module 'Jinja2' is not installed. Installing Jinja2...")
24 subprocess.check_call([sys.executable, "-m", "pip", "install", 'Jinja2'])
25 import jinja2
26
27
28def usage():
29 print("Usage: python3 ./generateEntropyTests.py\n" +
30 "-n <number of types>\n" +
31 "-e <entropy>\n" +
32 "-s <random seed>\n" +
33 "-f <flatlog types to use>")
34 exit(0)
35
36def main():
37 # default entropy, number of flatlog types, and random seed
38 seed = 1
39 entropy = 1
40 nTypes = 2
41 desiredTypes = []
42
43 # path to .hpp files
44 typesFolderPath = "./../types"
45 typesFolderPath = os.path.abspath(
46 os.path.join(os.path.dirname(__file__), typesFolderPath)
47 )
48
49 # path to gen tests
50 genTestsFolderPath = "./generated_tests"
51 genTestsFolderPath = os.path.abspath(
52 os.path.join(os.path.dirname(__file__), genTestsFolderPath)
53 )
54
55 # get list of flatlogs for which there are generated_tests
56 allTypes = os.listdir(genTestsFolderPath)
57 if (nTypes > len(allTypes)):
58 print(f"Error: n cannot be larger than amount of types in generated_tests. Retry with n < {len(allTypes)}.")
59 exit(0)
60 allTypes.sort()
61
62 # get opt
63 try:
64 opts, args = getopt.getopt(sys.argv[1:], "n:e:s:f:")
65 except getopt.GetoptError:
66 usage()
67
68 for opt, arg in opts:
69 if opt in ["-e"]:
70 if not arg.isdigit():
71 print(f"Error: entropy {arg} provided is not an integer.")
72 exit(0)
73 entropy = int(arg)
74 elif opt in ["-n"]:
75 if not arg.isdigit():
76 print(f"Error: number {arg} provided is not an integer.")
77 exit(0)
78 nTypes = int(arg)
79 elif opt in ["-s"]:
80 if not arg.isdigit():
81 print(f"Error: random seed {arg} provided is not an integer.")
82 exit(0)
83 # use random seed if provided with -s
84 seed = int(arg)
85 elif opt in ["-f"]:
86 if arg.strip() != "":
87 desiredTypes = [f"{x.strip()}_generated_tests.cpp" for x in arg.split(",")]
88
89 # check generated test exists for desired flatlog types
90 for dType in desiredTypes:
91 if dType not in allTypes:
92 print(f"Error: there is not a generated test type for requested type '{dType[:dType.index("_generated_tests.cpp")]}'")
93 exit(0)
94
95
96 random.seed(seed)
97
98 # load template
99 env = jinja2.Environment(
100 loader = jinja2.FileSystemLoader(searchpath="./")
101 )
102 env.trim_blocks = True
103 env.lstrip_blocks = True
104 testTemplate = env.get_template("entropyTestTemplate.jinja2")
105
106
107
108 # use required types
109 if nTypes < len(desiredTypes):
110 print(f"Error: n={nTypes} is less than desired flatlog types: {desiredTypes}. Please select n >= number of desired types.")
111 exit(0)
112
113 # check desired types are in allTypes and add them
114 testTypes = []
115 for dType in desiredTypes:
116 assert(dType in allTypes) # sanity check, validated before this
117 testTypes.append(dType)
118 allTypes.remove(dType)
119
120 # randomly select remaining nTypes
121 for _ in range(nTypes - len(desiredTypes)):
122
123 randomIdx = random.randint(0, len(allTypes) - 1)
124 testTypes.append(allTypes[randomIdx])
125
126 del allTypes[randomIdx]
127
128
129 # construct info dictionary for each type
130 baseTypesDict = dict()
131 typesInfoList = []
132 for type in testTypes:
133
134 # check valid type to generate info for
135 if "_generated_tests.cpp" not in type:
136 continue
137 typeName = type[:type.index("_generated_tests.cpp")]
138 typePath = os.path.join(typesFolderPath, f"{typeName}.hpp")
139
140 # make dictionary with info for template
141 info = makeTestInfoDict(typePath, baseTypesDict)
142
143 if info is None:
144 # check if this is an inherited type
145 for baseType, inheritedTypes in baseTypesDict.items():
146 for inheritedType in inheritedTypes:
147 if inheritedType in type:
148 info = makeInheritedTypeInfoDict(typesFolderPath, baseType, inheritedType)
149
150 assert(info is not None)
151 typesInfoList.append(info)
152
153 # make big string of const fields & big string of asserts in tandem
154 objCount = 0
155 objectCtors = []
156 testVariables = []
157 catchAsserts = []
158 totalFieldCount = 0
159 for _ in range(entropy):
160
161 for type in typesInfoList:
162
163 objName = f"{type["classVarName"]}_{objCount}"
164 initObjStr = f"{type["className"]}_0 {objName} = {type["className"]}_0("
165
166 for field in type["messageTypes"][0]:
167
168 # make test variable
169 testValName = f"{type["classVarName"]}{field["name"].capitalize()}_{str(totalFieldCount).rjust(8, '0')}"
170
171 initObjStr += f"(char *) {testValName}, " if "char *" in field["type"] else f"{testValName}, "
172 if field == type["messageTypes"][0][-1]:
173 initObjStr = initObjStr[:-2] + ");" # remove comma if last member of class
174
175 constVarStr = f"const {field["type"]} {testValName} = {makeTestVal(field)};"
176 testVariables.append(constVarStr)
177
178 # make catch 2 assertion
179 if "char *" in field["type"]:
180 catchAssertStr = f"REQUIRE(strcmp({objName}.m_{field["name"]}, {testValName}) == 0);"
181 else:
182 catchAssertStr = f"REQUIRE({objName}.m_{field["name"]} == {testValName});"
183 catchAsserts.append(catchAssertStr)
184
185 totalFieldCount += 1
186
187 catchAsserts.append(f"REQUIRE({objName}.m_verify);")
188 initObjStr = initObjStr[:-2] if (len(type["messageTypes"][0]) != 0) else initObjStr
189 initObjStr += ");"
190 objectCtors.append(initObjStr)
191 objCount += 1
192
193 jinjaDict = dict()
194 jinjaDict["types"] = typesInfoList
195 jinjaDict["seedOpt"] = seed
196 jinjaDict["dTypesOpt"] = desiredTypes
197 jinjaDict["nTypesOpt"] = nTypes
198 jinjaDict["entropyOpt"] = entropy
199 jinjaDict["objectCtors"] = objectCtors
200 jinjaDict["catchAsserts"] = catchAsserts
201 jinjaDict["testVariables"] = testVariables
202
203
204 # print(json.dumps(jinjaDict, indent=4))
205
206 # render
207 renderedTest = testTemplate.render(jinjaDict)
208
209 # generated tests output path
210 outFolderPath = "./gen_entropy_tests/"
211 outFolderPath = os.path.abspath(
212 os.path.join(os.path.dirname(__file__), outFolderPath)
213 )
214
215 # make directory if it doesn't exist
216 pathlib.Path(outFolderPath).mkdir(exist_ok=True)
217
218 # write out file
219 outFilename = f"generated_test_e{entropy}_n{nTypes}.cpp"
220 outPath = os.path.abspath(
221 os.path.join(outFolderPath, outFilename)
222 )
223 with open(outPath,"w") as outfile:
224 print(renderedTest,file=outfile)
225
226
227
228
229if (__name__ == "__main__"):
230 main()
231
dict makeInheritedTypeInfoDict(str typesFolderPath, str baseName, str logName)
dict makeTestInfoDict(str hppFname, dict baseTypesDict)