preCICE v3.1.1
Loading...
Searching...
No Matches
createTest.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3import argparse
4import collections
5import os
6import pathlib
7import re
8import shutil
9
10
12 detect = ["CHANGELOG.md", "CMakeLists.txt", "LICENSE", "src", "tests", "cmake"]
13 return all(map(lambda c: os.path.exists(os.path.join(dir, c)), detect))
14
15
17 search_depth = 10
18 current = pathlib.Path(os.path.curdir).absolute()
19 candidates = [current] + list(current.parents)[:search_depth]
20 for dir in candidates:
21 if is_precice_root(dir):
22 return dir
23 raise BaseException("Unable to find the root directory of precice")
24
25
26def locateTemplate(filepath):
27 assert filepath is not None
28 base = os.path.splitext(filepath)[0]
29 return (base + ".cpp", base + ".xml")
30
31
32ABBREVIATIONS = ["MPI", "QN", "RBF", "NN", "NP"]
33
34
35def dirToSuite(dir):
36 """
37 Takes a kebab-case-directory and transforms it to a CamelCaseDirectory.
38 Abbreviations defined above will be all upper case.
39 """
40
41 def toSuite(s):
42 upper = s.upper()
43 if upper in ABBREVIATIONS:
44 return upper
45 else:
46 return s.capitalize()
47
48 return "".join(map(toSuite, dir.split("-")))
49
50
52 assert arg
53 if " " in arg:
54 raise argparse.ArgumentTypeError(
55 'The given suite name "{}" cannot contain spaces.'.format(arg)
56 )
57 if "." in arg:
58 raise argparse.ArgumentTypeError(
59 'The given suite name "{}" cannot contain the file extensions.'.format(arg)
60 )
61 if re.search(r"[^a-z-]", arg) is not None:
62 raise argparse.ArgumentTypeError(
63 'The given suite dir "{}" must be dashed-lower-case.'.format(arg)
64 )
65 if re.search(r"^[a-z]", arg) is None:
66 raise argparse.ArgumentTypeError(
67 'The given suite dir "{}" must start with a lowercase letter'.format(arg)
68 )
69 if re.search(r"[a-z]$", arg) is None:
70 raise argparse.ArgumentTypeError(
71 'The given suite dir "{}" must end with a lowercase letter'.format(arg)
72 )
73 return arg
74
75
77 assert arg
78 if " " in arg:
79 raise argparse.ArgumentTypeError(
80 'The given test name "{}" cannot contain spaces.'.format(arg)
81 )
82 if "." in arg:
83 raise argparse.ArgumentTypeError(
84 'The given test name "{}" cannot contain the file extensions.'.format(arg)
85 )
86 if re.search(r"[^a-zA-Z0-9]", arg) is not None:
87 raise argparse.ArgumentTypeError(
88 'The given test name "{}" must use CamelCase.'.format(arg)
89 )
90 return arg
91
92
93def testarg(arg):
94 """
95 Checks the given test argument and computes:
96 - the location as pathlib.PurePath
97 - the suites as a list of suite names
98 - the name of the test
99 """
100 parts = pathlib.PurePath(arg).parts
101 dirs, name = parts[:-1], parts[-1]
102 checkTestName(name)
103 [checkTestSuite(d) for d in dirs]
104
105 # If the given path is inside the tests dir, then use the relative path
106 full = pathlib.Path(arg).absolute()
107 tests = find_precice_root().joinpath("tests")
108 try:
109 parts = full.relative_to(tests).parts
110 dirs, name = parts[:-1], parts[-1]
111 except ValueError:
112 pass
113
114 location = tests.joinpath(*dirs)
115 if location.exists() and not location.is_dir():
116 raise argparse.ArgumentTypeError(
117 'The given test location "{}" exists, but is not a directory.'.format(
118 location
119 )
120 )
121
122 suites = [dirToSuite(dir) for dir in dirs]
123 return collections.namedtuple("TestSetup", "location suites name")(
124 location, suites, name
125 )
126
127
128PRECICE_TEST_BODY = """{
129 PRECICE_TEST(TODO);
130
131 // Implement your test here.
132 BOOST_TEST(false);
133 precice::Participant interface(context.name, context.config(), context.rank, context.size);
134
135 std::vector<VertexID> vertexIDs;
136
137 if (context.isNamed(TODO)) {
138 auto meshID = interface.getMeshID(TODO);
139 auto dataID = interface.getDataID(TODO, meshID);
140
141 std::vector<double> coords;
142
143 interface.setMeshVertices(meshID, TODO, coords.data(), vertexIDs.data());
144 } else {
145 }
146}
147"""
148
149
150def generateTestSource(name, suite, filepath):
151 if os.path.exists(filepath):
152 raise BaseException('The test source at "{}" already exists.'.format(filepath))
153
154 includes = ["<precice/precice.hpp>", "<vector>", '"testing/Testing.hpp"']
155 suites = ["Integration"] + suite
156 space = [""]
157 lines = ["#ifndef PRECICE_NO_MPI"]
158 lines += space
159 lines += ["#include " + inc for inc in includes if inc[0] != "<"]
160 lines += space
161 lines += ["#include " + inc for inc in includes if inc[0] == "<"]
162 lines += space
163 lines += ["BOOST_AUTO_TEST_SUITE({})".format(s) for s in suites]
164 lines += ["BOOST_AUTO_TEST_CASE({})".format(name), PRECICE_TEST_BODY]
165 lines += ["BOOST_AUTO_TEST_SUITE_END() // " + s for s in reversed(suites)]
166 lines += space
167 lines += ["#endif // PRECICE_NO_MPI"]
168
169 with open(filepath, "w") as f:
170 f.writelines([line + "\n" for line in lines])
171
172
173def generateTestSourceFromExisting(name, suite, filepath, existingSource):
174 if os.path.exists(filepath):
175 raise BaseException('The test source at "{}" already exists.'.format(filepath))
176 if not os.path.exists(existingSource):
177 raise BaseException(
178 'The template source at "{}" does not exist.'.format(existingSource)
179 )
180
181 suites = ["Integration"] + suite
182
183 templateContent = ["BOOST_AUTO_TEST_SUITE({})".format(s) for s in suites]
184 templateContent += [
185 "BOOST_AUTO_TEST_CASE({})".format(name),
186 "/* Test body goes here */",
187 ]
188 templateContent += ["// ORIGINAL START"]
189
190 with open(existingSource) as f:
191 templateContent += [line.rstrip("\n") for line in f.readlines()]
192
193 templateContent += ["// ORIGINAL END"]
194 templateContent += ["BOOST_AUTO_TEST_SUITE_END() // " + s for s in reversed(suites)]
195
196 with open(filepath, "w") as f:
197 f.writelines([line + "\n" for line in templateContent])
198
199
200def generateTestConfig(name, suite, filepath):
201 if os.path.exists(filepath):
202 print('The test config at "{}" already exists.'.format(filepath))
203 else:
204 open(filepath, "w").close()
205
206
207def main():
208 parser = argparse.ArgumentParser(
209 description="preCICE integration test creation tool."
210 )
211 parser.add_argument(
212 "test",
213 metavar="[test-suite/]TestCase",
214 type=testarg,
215 help="The path to the test, the last component being the test name. "
216 "If executed within tests/, then the test will be created relative to the local directory. "
217 "Otherwise, the path will be assumed to be relative to the tests directory.",
218 )
219 parser.add_argument(
220 "-n", "--dry-run", action="store_true", help="print actions only"
221 )
222 parser.add_argument(
223 "-t",
224 "--template",
225 metavar="[test-suite/]TestCase[.cpp|.xml]",
226 type=str,
227 default=None,
228 help="Test to use the given cpp and xml as a template to create a new test. Adds a comment to the top of the test to help changes.",
229 )
230 args = parser.parse_args()
231
232 print("Create directory {}".format(args.test.location))
233 if not args.dry_run:
234 os.makedirs(args.test.location, exist_ok=True)
235
236 source = args.test.name + ".cpp"
237 config = args.test.name + ".xml"
238 sourcePath = args.test.location.joinpath(source)
239 configPath = args.test.location.joinpath(config)
240
241 if not args.template:
242 # Generate from scratch
243 print("Create test source {}".format(source))
244 if not args.dry_run:
245 generateTestSource(args.test.name, args.test.suites, sourcePath)
246
247 print("Create test config {}".format(config))
248 if not args.dry_run:
249 generateTestConfig(args.test.name, args.test.suites, configPath)
250 else:
251 # Generate from existing
252 existingSource, existingConfig = locateTemplate(args.template)
253
254 print("Create test source {} from {}".format(source, existingSource))
255 if not args.dry_run:
257 args.test.name, args.test.suites, sourcePath, existingSource
258 )
259
260 print("Copy test config {} from {}".format(config, existingConfig))
261 if not args.dry_run:
262 shutil.copyfile(existingConfig, configPath)
263
264 print("Remember to run tools/building/updateSourceFiles.py or make sourcesIndex")
265
266
267if __name__ == "__main__":
268 main()
generateTestSource(name, suite, filepath)
dirToSuite(dir)
Definition createTest.py:35
checkTestSuite(arg)
Definition createTest.py:51
is_precice_root(dir)
Definition createTest.py:11
checkTestName(arg)
Definition createTest.py:76
testarg(arg)
Definition createTest.py:93
locateTemplate(filepath)
Definition createTest.py:26
generateTestSourceFromExisting(name, suite, filepath, existingSource)
find_precice_root()
Definition createTest.py:16
generateTestConfig(name, suite, filepath)