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