1 #include <ostream>
2 #include <iostream>
3 #include <fstream>
4 #include <sstream>
5 #include <stdio.h>
6 #include <utility>
7 #include <vector>
8 #include <iomanip>
9 #include <boost/algorithm/string.hpp>
10 #include <cmath>
11 #include <cstdlib>
12
13 using std::endl;
14 using std::pair;
15 using std::string;
16 using std::stringstream;
17 using std::vector;
18
19 #ifdef STAN_TEST_ROW_VECTORS
20 int ROW_VECTORS = 1;
21 #else
22 int ROW_VECTORS = 0;
23 #endif
24
push_args(vector<string> & args,const string & type)25 void push_args(vector<string>& args, const string& type) {
26 if (type.compare("varmat") == 0) {
27 args.push_back("var");
28 args.push_back("std::vector<var>");
29 args.push_back(
30 "stan::math::var_value<Eigen::Matrix<double, Eigen::Dynamic, 1>>");
31 if (ROW_VECTORS == 1)
32 args.push_back(
33 "stan::math::var_value<Eigen::Matrix<double, 1, Eigen::Dynamic>>");
34 } else {
35 args.push_back(type);
36 args.push_back("std::vector<" + type + ">");
37 args.push_back("Eigen::Matrix<" + type + ", Eigen::Dynamic, 1>");
38 if (ROW_VECTORS == 1)
39 args.push_back("Eigen::Matrix<" + type + ", 1, Eigen::Dynamic>");
40 }
41 }
42
lookup_argument(const string & argument,const int & ind)43 vector<string> lookup_argument(const string& argument, const int& ind) {
44 using boost::iequals;
45 vector<string> args;
46 if (iequals(argument, "int")) {
47 args.push_back("int");
48 } else if (iequals(argument, "ints")) {
49 push_args(args, "int");
50 } else if (iequals(argument, "double")) {
51 args.push_back("double");
52 args.push_back("var");
53 } else if (iequals(argument, "doubles")) {
54 push_args(args, "double");
55 if (ind == 1) {
56 push_args(args, "var");
57 } else if (ind == 2) {
58 push_args(args, "fvar<double>");
59 } else if (ind == 3) {
60 push_args(args, "fvar<var>");
61 } else if (ind == 4) {
62 push_args(args, "fvar<fvar<double> >");
63 } else if (ind == 5) {
64 push_args(args, "fvar<fvar<var> >");
65 } else if (ind == 6) {
66 push_args(args, "varmat");
67 }
68 }
69 return args;
70 }
71
operator <<(std::ostream & o,pair<string,string> & p)72 std::ostream& operator<<(std::ostream& o, pair<string, string>& p) {
73 o << "<" << p.first << ", " << p.second << ">" << endl;
74 return o;
75 }
76
77 template <class T>
operator <<(std::ostream & o,vector<T> & vec)78 std::ostream& operator<<(std::ostream& o, vector<T>& vec) {
79 o << "vector size: " << vec.size() << endl;
80 for (size_t n = 0; n < vec.size(); n++) {
81 o << " \'" << vec[n] << "\'" << endl;
82 }
83 return o;
84 }
85
write_includes(vector<std::ostream * > & outs,const string & include)86 void write_includes(vector<std::ostream*>& outs, const string& include) {
87 for (size_t n = 0; n < outs.size(); n++) {
88 std::ostream* out = outs[n];
89 *out << "#include <gtest/gtest.h>" << endl;
90 *out << "#include <tuple>" << endl;
91 *out << "#include <test/prob/test_fixture_distr.hpp>" << endl;
92 *out << "#include <test/prob/test_fixture_cdf.hpp>" << endl;
93 *out << "#include <test/prob/test_fixture_cdf_log.hpp>" << endl;
94 *out << "#include <test/prob/test_fixture_ccdf_log.hpp>" << endl;
95 *out << "#include <" << include << ">" << endl;
96 *out << endl;
97 }
98 }
99
tokenize_arguments(const string & arguments)100 vector<string> tokenize_arguments(const string& arguments) {
101 vector<string> tokens;
102 string delimiters = ", ";
103 string args_only_string = arguments.substr(arguments.find(":") + 1);
104 boost::algorithm::trim(args_only_string);
105 boost::algorithm::split(tokens, args_only_string,
106 boost::is_any_of(delimiters),
107 boost::token_compress_on);
108 return tokens;
109 }
110
size(const vector<vector<string>> & sequences)111 size_t size(const vector<vector<string> >& sequences) {
112 if (sequences.size() == 0)
113 return 0;
114 size_t N = 1;
115 for (size_t n = 0; n < sequences.size(); n++)
116 N *= sequences[n].size();
117 return N;
118 }
119
is_argument_list(const string & line)120 bool is_argument_list(const string& line) {
121 size_t comment = line.find("// ");
122 if (comment == string::npos)
123 return false;
124 size_t keyword = line.find("Arguments:", comment + 1);
125 if (keyword == string::npos)
126 return false;
127 return true;
128 }
129
read_file(const string & in_name)130 string read_file(const string& in_name) {
131 std::ifstream in(in_name.c_str());
132
133 string file;
134 in.seekg(0, std::ios::end);
135 file.resize(in.tellg());
136 in.seekg(0, std::ios::beg);
137 in.read(&file[0], file.size());
138 in.close();
139
140 return file;
141 }
142
read_arguments_from_file(const string & file)143 string read_arguments_from_file(const string& file) {
144 string arguments;
145 std::istringstream in(file);
146 if (!in.eof()) {
147 std::getline(in, arguments);
148 while (in.good() && !is_argument_list(arguments)) {
149 std::getline(in, arguments);
150 }
151 }
152 if (!is_argument_list(arguments))
153 arguments = "";
154 return arguments;
155 }
156
read_test_name_from_file(const string & file)157 pair<string, string> read_test_name_from_file(const string& file) {
158 pair<string, string> name;
159
160 size_t pos = 0;
161 string class_keyword = "class ";
162 string public_keyword = "public ";
163 pos = file.find(class_keyword, pos);
164 if (pos < file.size()) {
165 pos += class_keyword.size();
166 size_t pos2 = file.find(":", pos);
167 string test_name = file.substr(pos, pos2 - pos);
168 pos = file.find(public_keyword, pos) + public_keyword.size();
169 pos2 = file.find("{", pos);
170 string fixture_name = file.substr(pos, pos2 - pos);
171 pos = file.find("};", pos) + 2;
172 boost::algorithm::trim(test_name);
173 boost::algorithm::trim(fixture_name);
174
175 if (fixture_name.find("Test") != string::npos) {
176 fixture_name += "Fixture";
177 name = pair<string, string>(test_name, fixture_name);
178 }
179 }
180 return name;
181 }
182
build_argument_sequence(const string & arguments,const int & ind)183 vector<vector<string> > build_argument_sequence(const string& arguments,
184 const int& ind) {
185 vector<string> argument_list = tokenize_arguments(arguments);
186 vector<vector<string> > argument_sequence;
187 for (size_t n = 0; n < argument_list.size(); n++)
188 argument_sequence.push_back(lookup_argument(argument_list[n], ind));
189 return argument_sequence;
190 }
191
check_all_double(string base,string arg)192 bool check_all_double(string base, string arg) {
193 string arguments = base + arg;
194 bool result = true;
195 bool temp;
196
197 vector<string> tokens;
198 string delimiters = ", ";
199 boost::algorithm::trim(arguments);
200 boost::algorithm::split(tokens, arguments, boost::is_any_of(delimiters),
201 boost::token_compress_on);
202
203 for (size_t i = 0; i < tokens.size(); i++) {
204 if (tokens[i] == "1" || tokens[i] == "Eigen::Dynamic>" || tokens[i] == "1>"
205 || tokens[i] == "Eigen::Dynamic")
206 result = result && true;
207 else {
208 temp = (tokens[i] == "double") || (tokens[i] == "std::vector<double>")
209 || (tokens[i] == "Eigen::Matrix<double") || (tokens[i] == "int")
210 || (tokens[i] == "std::vector<int>")
211 || (tokens[i] == "Eigen::Matrix<int");
212 result = result && temp;
213 }
214 }
215 return result;
216 }
217
num_doubles(string arguments)218 int num_doubles(string arguments) {
219 vector<string> tokens;
220 string delimiters = ", ";
221 boost::algorithm::trim(arguments);
222 boost::algorithm::split(tokens, arguments, boost::is_any_of(delimiters),
223 boost::token_compress_on);
224
225 int num = 0;
226 for (size_t i = 0; i < tokens.size(); i++) {
227 if (tokens[i] == "Doubles")
228 ++num;
229 }
230 return num;
231 }
num_ints(string arguments)232 int num_ints(string arguments) {
233 vector<string> tokens;
234 string delimiters = ", ";
235 boost::algorithm::trim(arguments);
236 boost::algorithm::split(tokens, arguments, boost::is_any_of(delimiters),
237 boost::token_compress_on);
238
239 int num = 0;
240 for (size_t i = 0; i < tokens.size(); i++) {
241 if (tokens[i] == "Ints")
242 ++num;
243 }
244 return num;
245 }
246
write_types_typedef(vector<std::ostream * > & outs,string base,size_t & N,vector<vector<string>> argument_sequence,const size_t depth,const int & index,const int & N_TESTS)247 void write_types_typedef(vector<std::ostream*>& outs, string base, size_t& N,
248 vector<vector<string> > argument_sequence,
249 const size_t depth, const int& index,
250 const int& N_TESTS) {
251 vector<string> args = argument_sequence.front();
252 argument_sequence.erase(argument_sequence.begin());
253 if (argument_sequence.size() > 0) {
254 for (size_t n = 0; n < args.size(); n++)
255 write_types_typedef(outs, base + args[n] + ", ", N, argument_sequence,
256 depth, index, N_TESTS);
257 } else {
258 string extra_args;
259 for (size_t n = depth; n < 6; n++) {
260 extra_args += ", empty";
261 }
262 for (size_t n = 0; n < args.size(); n++) {
263 std::ostream* out = outs[int(N / N_TESTS)];
264 if (index == 1) {
265 *out << "typedef std::tuple<" << base << args[n] << extra_args;
266 if (extra_args.size() == 0)
267 *out << " ";
268 *out << "> type_v_" << N << ";" << endl;
269 N++;
270 } else {
271 if (check_all_double(base, args[n]) == false) {
272 *out << "typedef std::tuple<" << base << args[n] << extra_args;
273 if (extra_args.size() == 0)
274 *out << " ";
275 else if (index == 2)
276 *out << "> type_fd_" << N << ";" << endl;
277 else if (index == 3)
278 *out << "> type_fv_" << N << ";" << endl;
279 else if (index == 4)
280 *out << "> type_ffd_" << N << ";" << endl;
281 else if (index == 5)
282 *out << "> type_ffv_" << N << ";" << endl;
283 else if (index == 6)
284 *out << "> type_vv_" << N << ";" << endl;
285 N++;
286 }
287 }
288 }
289 }
290 }
291
write_types(vector<std::ostream * > & outs,const vector<vector<string>> & argument_sequence,const int & index,const int & N_TESTS)292 size_t write_types(vector<std::ostream*>& outs,
293 const vector<vector<string> >& argument_sequence,
294 const int& index, const int& N_TESTS) {
295 size_t N = 0;
296 write_types_typedef(outs, "", N, argument_sequence, argument_sequence.size(),
297 index, N_TESTS);
298 for (size_t n = 0; n < outs.size(); n++)
299 *outs[n] << endl;
300 return N;
301 }
302
write_test(vector<std::ostream * > & outs,const string & test_name,const string & fixture_name,const size_t N,const int & index,const int & N_TESTS)303 void write_test(vector<std::ostream*>& outs, const string& test_name,
304 const string& fixture_name, const size_t N, const int& index,
305 const int& N_TESTS) {
306 for (size_t n = 0; n < N; n++) {
307 std::ostream* out = outs[int(n / N_TESTS)];
308 if (index == 1)
309 *out << "typedef std::tuple<" << test_name << ", type_v_" << n << "> "
310 << test_name << "_v_" << n << ";" << endl;
311 else if (index == 2)
312 *out << "typedef std::tuple<" << test_name << ", type_fd_" << n << "> "
313 << test_name << "_fd_" << n << ";" << endl;
314 else if (index == 3)
315 *out << "typedef std::tuple<" << test_name << ", type_fv_" << n << "> "
316 << test_name << "_fv_" << n << ";" << endl;
317 else if (index == 4)
318 *out << "typedef std::tuple<" << test_name << ", type_ffd_" << n << "> "
319 << test_name << "_ffd_" << n << ";" << endl;
320 else if (index == 5)
321 *out << "typedef std::tuple<" << test_name << ", type_ffv_" << n << "> "
322 << test_name << "_ffv_" << n << ";" << endl;
323 else if (index == 6)
324 *out << "typedef std::tuple<" << test_name << ", type_vv_" << n << "> "
325 << test_name << "_vv_" << n << ";" << endl;
326 }
327 for (size_t i = 0; i < outs.size(); i++) {
328 *outs[i] << endl;
329 }
330 for (size_t n = 0; n < N; n++) {
331 std::ostream* out = outs[int(n / N_TESTS)];
332 if (index == 1)
333 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_v_" << n
334 << ", " << fixture_name << ", " << test_name << "_v_" << n << ");"
335 << endl;
336 else if (index == 2)
337 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_fd_" << n
338 << ", " << fixture_name << ", " << test_name << "_fd_" << n << ");"
339 << endl;
340 else if (index == 3)
341 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_fv_" << n
342 << ", " << fixture_name << ", " << test_name << "_fv_" << n << ");"
343 << endl;
344 else if (index == 4)
345 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_ffd_" << n
346 << ", " << fixture_name << ", " << test_name << "_ffd_" << n << ");"
347 << endl;
348 else if (index == 5)
349 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_ffv_" << n
350 << ", " << fixture_name << ", " << test_name << "_ffv_" << n << ");"
351 << endl;
352 else if (index == 6)
353 *out << "INSTANTIATE_TYPED_TEST_SUITE_P(" << test_name << "_vv_" << n
354 << ", " << fixture_name << ", " << test_name << "_vv_" << n << ");"
355 << endl;
356 }
357 for (size_t i = 0; i < outs.size(); i++) {
358 *outs[i] << endl;
359 }
360 }
361
write_test_cases(vector<std::ostream * > & outs,const string & file,const vector<vector<string>> & argument_sequence,const int & index,const int & N_TESTS)362 void write_test_cases(vector<std::ostream*>& outs, const string& file,
363 const vector<vector<string> >& argument_sequence,
364 const int& index, const int& N_TESTS) {
365 pair<string, string> name = read_test_name_from_file(file);
366 string test_name = name.first;
367 string fixture_name = name.second;
368
369 size_t num_tests = write_types(outs, argument_sequence, index, N_TESTS);
370 write_test(outs, test_name, fixture_name, num_tests, index, N_TESTS);
371 }
372
create_files(const int & argc,const char * argv[],const int & index,const int & start,const int & N_TESTS)373 int create_files(const int& argc, const char* argv[], const int& index,
374 const int& start, const int& N_TESTS) {
375 if (argc != 3)
376 return -1;
377 string in_suffix = "_test.hpp";
378
379 string in_name = argv[1];
380
381 size_t last_in_suffix
382 = in_name.find_last_of(in_suffix) + 1 - in_suffix.length();
383 string out_name_base = in_name.substr(0, last_in_suffix);
384
385 string file = read_file(in_name);
386
387 string arguments = read_arguments_from_file(file);
388 vector<vector<string> > argument_sequence
389 = build_argument_sequence(arguments, index);
390
391 int num_tests;
392 if (index == 1)
393 num_tests = size(argument_sequence);
394 else
395 num_tests = size(argument_sequence)
396 - std::pow(3 + ROW_VECTORS, num_ints(arguments))
397 * std::pow(3 + ROW_VECTORS, num_doubles(arguments));
398
399 vector<std::ostream*> outs;
400 const double BATCHES = N_TESTS > 0 ? num_tests / N_TESTS : -N_TESTS;
401 for (int n = start + 1; n < start + 1 + BATCHES + 1; n++) {
402 stringstream out_name;
403 out_name << out_name_base;
404 out_name << "_" << std::setw(5) << std::setfill('0') << n;
405 if (index == 1)
406 out_name << "_generated_v_test.cpp";
407 else if (index == 2)
408 out_name << "_generated_fd_test.cpp";
409 else if (index == 3)
410 out_name << "_generated_fv_test.cpp";
411 else if (index == 4)
412 out_name << "_generated_ffd_test.cpp";
413 else if (index == 5)
414 out_name << "_generated_ffv_test.cpp";
415 else if (index == 6)
416 out_name << "_generated_vv_test.cpp";
417 std::string tmp(out_name.str());
418 outs.push_back(new std::ofstream(tmp.c_str()));
419 }
420
421 write_includes(outs, in_name);
422 if (N_TESTS > 0)
423 write_test_cases(outs, file, argument_sequence, index, N_TESTS);
424 else if (num_tests > 0)
425 write_test_cases(outs, file, argument_sequence, index,
426 ceil(num_tests / BATCHES));
427
428 for (size_t n = 0; n < outs.size(); n++) {
429 static_cast<std::ofstream*>(outs[n])->close();
430 delete (outs[n]);
431 }
432 outs.clear();
433 return start + BATCHES;
434 }
435
436 /**
437 * Generate test cases.
438 *
439 * @param argc Number of arguments
440 * @param argv Arguments. Should contain one argument with a filename and
441 * the number of tests per file or if non-positive, the number of files - 1
442 *
443 * @return 0 for success, negative number otherwise.
444 */
main(int argc,const char * argv[])445 int main(int argc, const char* argv[]) {
446 int N_TESTS = atoi(argv[2]);
447
448 create_files(argc, argv, 1, -1, N_TESTS); // create var tests
449 create_files(argc, argv, 5, -1, N_TESTS); // create ffv tests
450 create_files(argc, argv, 6, -1, N_TESTS); // create varmat tests
451 #ifdef STAN_PROB_TEST_ALL
452 create_files(argc, argv, 2, -1, N_TESTS); // create fd tests
453 create_files(argc, argv, 3, -1, N_TESTS); // create fv tests
454 create_files(argc, argv, 4, -1, N_TESTS); // create ffd tests
455 #endif
456
457 return 0;
458 }
459