1 /*
2  * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the <organization> nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "CppUTest/TestHarness.h"
29 #include "CppUTest/CommandLineArguments.h"
30 #include "CppUTest/PlatformSpecificFunctions.h"
31 
CommandLineArguments(int ac,const char * const * av)32 CommandLineArguments::CommandLineArguments(int ac, const char *const *av) :
33     ac_(ac), av_(av), needHelp_(false), verbose_(false), veryVerbose_(false), color_(false), runTestsAsSeperateProcess_(false), listTestGroupNames_(false), listTestGroupAndCaseNames_(false), runIgnored_(false), reversing_(false), shuffling_(false), shufflingPreSeeded_(false), repeat_(1), shuffleSeed_(0), groupFilters_(NULLPTR), nameFilters_(NULLPTR), outputType_(OUTPUT_ECLIPSE)
34 {
35 }
36 
~CommandLineArguments()37 CommandLineArguments::~CommandLineArguments()
38 {
39     while(groupFilters_) {
40         TestFilter* current = groupFilters_;
41         groupFilters_ = groupFilters_->getNext();
42         delete current;
43     }
44     while(nameFilters_) {
45         TestFilter* current = nameFilters_;
46         nameFilters_ = nameFilters_->getNext();
47         delete current;
48     }
49 }
50 
parse(TestPlugin * plugin)51 bool CommandLineArguments::parse(TestPlugin* plugin)
52 {
53     bool correctParameters = true;
54     for (int i = 1; i < ac_; i++) {
55         SimpleString argument = av_[i];
56 
57         if (argument == "-h") {
58             needHelp_ = true;
59             correctParameters = false;
60         }
61         else if (argument == "-v") verbose_ = true;
62         else if (argument == "-vv") veryVerbose_ = true;
63         else if (argument == "-c") color_ = true;
64         else if (argument == "-p") runTestsAsSeperateProcess_ = true;
65         else if (argument == "-b") reversing_ = true;
66         else if (argument == "-lg") listTestGroupNames_ = true;
67         else if (argument == "-ln") listTestGroupAndCaseNames_ = true;
68         else if (argument == "-ri") runIgnored_ = true;
69         else if (argument.startsWith("-r")) setRepeatCount(ac_, av_, i);
70         else if (argument.startsWith("-g")) addGroupFilter(ac_, av_, i);
71         else if (argument.startsWith("-t")) correctParameters = addGroupDotNameFilter(ac_, av_, i);
72         else if (argument.startsWith("-sg")) addStrictGroupFilter(ac_, av_, i);
73         else if (argument.startsWith("-xg")) addExcludeGroupFilter(ac_, av_, i);
74         else if (argument.startsWith("-xsg")) addExcludeStrictGroupFilter(ac_, av_, i);
75         else if (argument.startsWith("-n")) addNameFilter(ac_, av_, i);
76         else if (argument.startsWith("-sn")) addStrictNameFilter(ac_, av_, i);
77         else if (argument.startsWith("-xn")) addExcludeNameFilter(ac_, av_, i);
78         else if (argument.startsWith("-xsn")) addExcludeStrictNameFilter(ac_, av_, i);
79         else if (argument.startsWith("-s")) correctParameters = setShuffle(ac_, av_, i);
80         else if (argument.startsWith("TEST(")) addTestToRunBasedOnVerboseOutput(ac_, av_, i, "TEST(");
81         else if (argument.startsWith("IGNORE_TEST(")) addTestToRunBasedOnVerboseOutput(ac_, av_, i, "IGNORE_TEST(");
82         else if (argument.startsWith("-o")) correctParameters = setOutputType(ac_, av_, i);
83         else if (argument.startsWith("-p")) correctParameters = plugin->parseAllArguments(ac_, av_, i);
84         else if (argument.startsWith("-k")) setPackageName(ac_, av_, i);
85         else correctParameters = false;
86 
87         if (correctParameters == false) {
88             return false;
89         }
90     }
91     return true;
92 }
93 
usage() const94 const char* CommandLineArguments::usage() const
95 {
96     return "use -h for more extensive help\nusage [-h] [-v] [-vv] [-c] [-p] [-lg] [-ln] [-ri] [-r#]\n"
97                                            "      [-g|sg|xg|xsg groupName]... [-n|sn|xn|xsn testName]... [-t groupName.testName]...\n"
98                                            "      [-b] [-s [randomizerSeed>0]] [\"TEST(groupName, testName)\"]... [-o{normal, junit, teamcity}] [-k packageName]\n";
99 }
100 
help() const101 const char* CommandLineArguments::help() const
102 {
103     return
104       "Thanks for using CppUTest.\n"
105       "\n"
106       "Options that do not run tests but query:\n"
107       "  -h                 - this wonderful help screen. Joy!\n"
108       "  -lg                - print a list of group names, separated by spaces\n"
109       "  -ln                - print a list of test names in the form of group.name, separated by spaces\n"
110       "\n"
111       "Options that change the output format:\n"
112       "  -c                - colorize output, print green if OK, or red if failed\n"
113       "  -v                - verbose, print each test name as it runs\n"
114       "  -vv               - very verbose, print internal information during test run\n"
115       "\n"
116       "Options that change the output location:\n"
117       "  -oteamcity       - output to xml files (as the name suggests, for TeamCity)\n"
118       "  -ojunit          - output to JUnit ant plugin style xml files (for CI systems)\n"
119       "  -k package name  - Add a package name in JUnit output (for classification in CI systems)\n"
120       "\n"
121       "\n"
122       "Options that control which tests are run:\n"
123       "  -g group         - only run test whose group contains the substring group\n"
124       "  -n name          - only run test whose name contains the substring name\n"
125       "  -t group.name    - only run test whose name contains the substring group and name\n"
126       "  -sg group        - only run test whose group exactly matches the string group\n"
127       "  -sn name         - only run test whose name exactly matches the string name\n"
128       "  -xg group        - exclude tests whose group contains the substring group (v3.8)\n"
129       "  -xn name         - exclude tests whose name contains the substring name (v3.8)\n"
130       "  TEST(group,name) - only run test whose group and name matches the strings group and name.\n"
131       "                     This can be used to copy-paste output from the -v option on the command line.\n"
132       "\n"
133       "Options that control how the tests are run:\n"
134       "  -p               - run tests in a separate process.\n"
135       "  -b               - run the tests backwards, reversing the normal way\n"
136       "  -s [seed]        - shuffle tests randomly. Seed is optional\n"
137       "  -r#              - repeat the tests some number (#) of times, or twice if # is not specified.\n";
138 }
139 
needHelp() const140 bool CommandLineArguments::needHelp() const
141 {
142     return needHelp_;
143 }
144 
isVerbose() const145 bool CommandLineArguments::isVerbose() const
146 {
147     return verbose_;
148 }
149 
isVeryVerbose() const150 bool CommandLineArguments::isVeryVerbose() const
151 {
152     return veryVerbose_;
153 }
154 
isColor() const155 bool CommandLineArguments::isColor() const
156 {
157     return color_;
158 }
159 
isListingTestGroupNames() const160 bool CommandLineArguments::isListingTestGroupNames() const
161 {
162     return listTestGroupNames_;
163 }
164 
isListingTestGroupAndCaseNames() const165 bool CommandLineArguments::isListingTestGroupAndCaseNames() const
166 {
167     return listTestGroupAndCaseNames_;
168 }
169 
isRunIgnored() const170 bool CommandLineArguments::isRunIgnored() const
171 {
172     return runIgnored_;
173 }
174 
runTestsInSeperateProcess() const175 bool CommandLineArguments::runTestsInSeperateProcess() const
176 {
177     return runTestsAsSeperateProcess_;
178 }
179 
180 
getRepeatCount() const181 size_t CommandLineArguments::getRepeatCount() const
182 {
183     return repeat_;
184 }
185 
isReversing() const186 bool CommandLineArguments::isReversing() const
187 {
188     return reversing_;
189 }
190 
isShuffling() const191 bool CommandLineArguments::isShuffling() const
192 {
193     return shuffling_;
194 }
195 
getShuffleSeed() const196 size_t CommandLineArguments::getShuffleSeed() const
197 {
198     return shuffleSeed_;
199 }
200 
getGroupFilters() const201 const TestFilter* CommandLineArguments::getGroupFilters() const
202 {
203     return groupFilters_;
204 }
205 
getNameFilters() const206 const TestFilter* CommandLineArguments::getNameFilters() const
207 {
208     return nameFilters_;
209 }
210 
setRepeatCount(int ac,const char * const * av,int & i)211 void CommandLineArguments::setRepeatCount(int ac, const char *const *av, int& i)
212 {
213     repeat_ = 0;
214 
215     SimpleString repeatParameter(av[i]);
216     if (repeatParameter.size() > 2) repeat_ = (size_t) (SimpleString::AtoI(av[i] + 2));
217     else if (i + 1 < ac) {
218         repeat_ = (size_t) (SimpleString::AtoI(av[i + 1]));
219         if (repeat_ != 0) i++;
220     }
221 
222     if (0 == repeat_) repeat_ = 2;
223 
224 }
225 
setShuffle(int ac,const char * const * av,int & i)226 bool CommandLineArguments::setShuffle(int ac, const char * const *av, int& i)
227 {
228     shuffling_ = true;
229     shuffleSeed_ = (unsigned int)GetPlatformSpecificTimeInMillis();
230     if (shuffleSeed_ == 0) shuffleSeed_++;
231 
232     SimpleString shuffleParameter = av[i];
233     if (shuffleParameter.size() > 2) {
234         shufflingPreSeeded_ = true;
235         shuffleSeed_ = SimpleString::AtoU(av[i] + 2);
236     } else if (i + 1 < ac) {
237         unsigned int parsedParameter = SimpleString::AtoU(av[i + 1]);
238         if (parsedParameter != 0)
239         {
240             shufflingPreSeeded_ = true;
241             shuffleSeed_ = parsedParameter;
242             i++;
243         }
244     }
245     return (shuffleSeed_ != 0);
246 }
247 
getParameterField(int ac,const char * const * av,int & i,const SimpleString & parameterName)248 SimpleString CommandLineArguments::getParameterField(int ac, const char * const *av, int& i, const SimpleString& parameterName)
249 {
250     size_t parameterLength = parameterName.size();
251     SimpleString parameter(av[i]);
252     if (parameter.size() >  parameterLength) return av[i] + parameterLength;
253     else if (i + 1 < ac) return av[++i];
254     return "";
255 }
256 
addGroupFilter(int ac,const char * const * av,int & i)257 void CommandLineArguments::addGroupFilter(int ac, const char *const *av, int& i)
258 {
259     TestFilter* groupFilter = new TestFilter(getParameterField(ac, av, i, "-g"));
260     groupFilters_ = groupFilter->add(groupFilters_);
261 }
262 
addGroupDotNameFilter(int ac,const char * const * av,int & i)263 bool CommandLineArguments::addGroupDotNameFilter(int ac, const char *const *av, int& i)
264 {
265     SimpleString groupDotName = getParameterField(ac, av, i, "-t");
266     SimpleStringCollection collection;
267     groupDotName.split(".", collection);
268 
269     if (collection.size() != 2) return false;
270 
271     groupFilters_ = (new TestFilter(collection[0].subString(0, collection[0].size()-1)))->add(groupFilters_);
272     nameFilters_ = (new TestFilter(collection[1]))->add(nameFilters_);
273     return true;
274 }
275 
addStrictGroupFilter(int ac,const char * const * av,int & i)276 void CommandLineArguments::addStrictGroupFilter(int ac, const char *const *av, int& i)
277 {
278     TestFilter* groupFilter = new TestFilter(getParameterField(ac, av, i, "-sg"));
279     groupFilter->strictMatching();
280     groupFilters_ = groupFilter->add(groupFilters_);
281 }
282 
addExcludeGroupFilter(int ac,const char * const * av,int & i)283 void CommandLineArguments::addExcludeGroupFilter(int ac, const char *const *av, int& i)
284 {
285     TestFilter* groupFilter = new TestFilter(getParameterField(ac, av, i, "-xg"));
286     groupFilter->invertMatching();
287     groupFilters_ = groupFilter->add(groupFilters_);
288 }
289 
addExcludeStrictGroupFilter(int ac,const char * const * av,int & i)290 void CommandLineArguments::addExcludeStrictGroupFilter(int ac, const char *const *av, int& i)
291 {
292     TestFilter* groupFilter = new TestFilter(getParameterField(ac, av, i, "-xsg"));
293     groupFilter->strictMatching();
294     groupFilter->invertMatching();
295     groupFilters_ = groupFilter->add(groupFilters_);
296 }
297 
addNameFilter(int ac,const char * const * av,int & i)298 void CommandLineArguments::addNameFilter(int ac, const char *const *av, int& i)
299 {
300     TestFilter* nameFilter = new TestFilter(getParameterField(ac, av, i, "-n"));
301     nameFilters_ = nameFilter->add(nameFilters_);
302 }
303 
addStrictNameFilter(int ac,const char * const * av,int & index)304 void CommandLineArguments::addStrictNameFilter(int ac, const char *const *av, int& index)
305 {
306     TestFilter* nameFilter = new TestFilter(getParameterField(ac, av, index, "-sn"));
307     nameFilter->strictMatching();
308     nameFilters_= nameFilter->add(nameFilters_);
309 }
310 
addExcludeNameFilter(int ac,const char * const * av,int & index)311 void CommandLineArguments::addExcludeNameFilter(int ac, const char *const *av, int& index)
312 {
313     TestFilter* nameFilter = new TestFilter(getParameterField(ac, av, index, "-xn"));
314     nameFilter->invertMatching();
315     nameFilters_= nameFilter->add(nameFilters_);
316 }
317 
addExcludeStrictNameFilter(int ac,const char * const * av,int & index)318 void CommandLineArguments::addExcludeStrictNameFilter(int ac, const char *const *av, int& index)
319 {
320     TestFilter* nameFilter = new TestFilter(getParameterField(ac, av, index, "-xsn"));
321     nameFilter->invertMatching();
322     nameFilter->strictMatching();
323     nameFilters_= nameFilter->add(nameFilters_);
324 }
325 
addTestToRunBasedOnVerboseOutput(int ac,const char * const * av,int & index,const char * parameterName)326 void CommandLineArguments::addTestToRunBasedOnVerboseOutput(int ac, const char *const *av, int& index, const char* parameterName)
327 {
328     SimpleString wholename = getParameterField(ac, av, index, parameterName);
329     SimpleString testname = wholename.subStringFromTill(',', ')');
330     testname = testname.subString(2);
331     TestFilter* namefilter = new TestFilter(testname);
332     TestFilter* groupfilter = new TestFilter(wholename.subStringFromTill(wholename.at(0), ','));
333     namefilter->strictMatching();
334     groupfilter->strictMatching();
335     groupFilters_ = groupfilter->add(groupFilters_);
336     nameFilters_ = namefilter->add(nameFilters_);
337 }
338 
setPackageName(int ac,const char * const * av,int & i)339 void CommandLineArguments::setPackageName(int ac, const char *const *av, int& i)
340 {
341     SimpleString packageName = getParameterField(ac, av, i, "-k");
342     if (packageName.size() == 0) return;
343 
344     packageName_ = packageName;
345 }
346 
setOutputType(int ac,const char * const * av,int & i)347 bool CommandLineArguments::setOutputType(int ac, const char *const *av, int& i)
348 {
349     SimpleString outputType = getParameterField(ac, av, i, "-o");
350     if (outputType.size() == 0) return false;
351 
352     if (outputType == "normal" || outputType == "eclipse") {
353         outputType_ = OUTPUT_ECLIPSE;
354         return true;
355     }
356     if (outputType == "junit") {
357         outputType_ = OUTPUT_JUNIT;
358         return true;
359     }
360     if (outputType == "teamcity") {
361         outputType_ = OUTPUT_TEAMCITY;
362         return true;
363     }
364 
365     return false;
366 }
367 
isEclipseOutput() const368 bool CommandLineArguments::isEclipseOutput() const
369 {
370     return outputType_ == OUTPUT_ECLIPSE;
371 }
372 
isJUnitOutput() const373 bool CommandLineArguments::isJUnitOutput() const
374 {
375     return outputType_ == OUTPUT_JUNIT;
376 }
377 
isTeamCityOutput() const378 bool CommandLineArguments::isTeamCityOutput() const
379 {
380     return outputType_ == OUTPUT_TEAMCITY;
381 }
382 
getPackageName() const383 const SimpleString& CommandLineArguments::getPackageName() const
384 {
385     return packageName_;
386 }
387 
388