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