testrail-report 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. #!/usr/bin/env perl
  2. # ABSTRACT: Upload your TAP results to TestRail after they've finished
  3. # PODNAME: testrail-report
  4. =head1 SYNOPSIS
  5. testrail-report [OPTIONS] tapfile
  6. prove -v sometest.t > results.tap && testrail-report [OPTIONS] results.tap
  7. prove -v sometest.t | testrail-report [OPTIONS]
  8. prove -PTestRail='apiurl=http://some.testlink.install/,user=someUser,password=somePassword,project=TestProject,run=TestRun,plan=TestPlan,configs=Config1:Config2:Config3,version=0.014' sometest.t
  9. =head1 DESCRIPTION
  10. testrail-report - report raw TAP results to a TestRail install
  11. =head2 PARAMETERS:
  12. =head3 MANDATORY PARAMETERS
  13. --project [someproject] : associate results (if any) with theprovided project name.
  14. --run [somerun] : associates results (if any) with the provided run name.
  15. IF none of these options are provided, you will be asked to type
  16. these in as needed, supposing you are not redirecting input
  17. (such as piping into this command).
  18. =head3 SEMI-OPTIONAL PARAMETERS
  19. --plan [someplan] : look for the provided run name within the provided plan.
  20. --config [someconfig] : filter run by the provided configuration.
  21. This option can be passed multiple times for detailed filtering.
  22. -e --encoding: Character encoding of arguments. Defaults to UTF-8.
  23. See L<Encode::Supported> for supported encodings.
  24. Test plans can have runs with the same name, but different configurations, which is understandably confusing.
  25. You can do the same outside of plans, and without configurations; but doing so is ill advised, and the only option from there is to use IDs.
  26. So, try not to do that if you want to use this tool, and want sanity in your Test management system.
  27. The way around this is to specify what plan and configuration you want to set results for.
  28. This should provide sufficient uniqueness to get any run using names.
  29. =head3 OPTIONAL PARAMETERS
  30. --spawn [testsuite_id] : Attempt to create a run based on the provided testsuite ID. Uses the name provided with --run.
  31. If plans/configurations are supplied, it will attempt to create it as a child of the provided plan, and with the supplied configurations.
  32. If the specified run already exists, the program will simply use the existing run, and disregard the supplied testsuite_id.
  33. If the specified plan does not exist, it too will be created for you.
  34. --section [section_name] : When spawning, restrict the cases used in the provided testsuite ID to these sections.
  35. Option may be passed multiple times to specify multiple sections.
  36. =head3 CONFIG OPTIONS
  37. In your $HOME (or the current directory, if your system has no concept of a home directory), put a file called .testrailrc with key=value
  38. syntax separated by newlines. Valid Keys are: apiurl,user,password
  39. =head3 CONFIG OVERRIDES
  40. These override the config, if present. If neither are used, you will be prompted.
  41. --apiurl [url] : full URL to get to TestRail index document
  42. --password [key] : Your TestRail Password or a valid API key (TestRail 4.2 and above).
  43. --user [name] : Your TestRail User Name.
  44. =head3 BEHAVIOR
  45. --case-ok : Whether to consider each OK to correspond to a test in TestRail
  46. --step-results [name] : 'System Name' of a 'step_results' type field to set for your tests.
  47. These options are mutually exclusive. If neither is set, the
  48. overall result of the test will be used as the pass/fail for the test.
  49. =head3 RESULT OPTIONS
  50. --version : String describing the version of the system under test.
  51. --autoclose : If there are no more tests in 'untested' or 'retest' status for the specified run/plan, close it.
  52. =head2 PROVE PLUGIN:
  53. passing -PTestRail='key=value,...' to prove will
  54. automatically upload your test results while the test is running if
  55. real-time results are desired.
  56. See L<App::Prove::Plugin::TestRail> for more information.
  57. =head2 REQUIREMENTS:
  58. Your TestRail install must have 3 custom statuses with the internal
  59. names 'skip', 'todo_pass', and 'todo_fail', to represent those
  60. states which TAP can have.
  61. Also, be sure your tests don't output non-TAP (unknown) lines ending in dots (.)
  62. This will cause the preceding characters to be interpreted as a test name, which may lead to unexpected results.
  63. =head2 TESTING OPTIONS:
  64. --mock don't do any real HTTP requests.
  65. =cut
  66. use strict;
  67. use warnings;
  68. use TestRail::Utils;
  69. use Getopt::Long;
  70. use Term::ANSIColor 2.01 qw(colorstrip);
  71. use Test::Rail::Parser;
  72. use IO::Interactive::Tiny ();
  73. use File::HomeDir qw{my_home};
  74. print "testrail-report\n----------------------\n";
  75. sub help {
  76. print "testrail-report - report raw TAP results to a TestRail install
  77. USAGE:
  78. testrail-report [OPTIONS] tapfile
  79. prove -v sometest.t > results.tap && testrail-report [OPTIONS] \\
  80. results.tap
  81. prove -v sometest.t | testrail-report [OPTIONS]
  82. prove -PTestRail='http://some.testlink.install/','someUser',\\
  83. 'somePassword' sometest.t
  84. PARAMETERS:
  85. [MANDATORY PARAMETERS]
  86. --project [someproject] : associate results (if any) with the
  87. provided project name.
  88. --run [somerun] : associates results (if any) with the provided run
  89. name.
  90. IF none of these options are provided, you will be asked to type
  91. these in as needed, supposing you are not redirecting input
  92. (such as piping into this command).
  93. [SEMI-OPTIONAL PARAMETERS]
  94. --plan [someplan] : look for the provided run name within
  95. the provided plan.
  96. --configs [someconfigs] : filter run by the provided configuration.
  97. This option can be passed multiple times for detailed filtering.
  98. -e --encoding: Character encoding of arguments. Defaults to UTF-8.
  99. See L<Encode::Supported> for supported encodings.
  100. Test plans can have runs with the same name, but different
  101. configurations, which is understandably confusing. You can do the
  102. same outside of plans, and without configurations; but doing so is
  103. ill-advised, and the only option from there is to use IDs. So, try
  104. not to do that if you want to use this tool, and want sanity in your
  105. Test Management System.
  106. The way around this is to specify what plan and configuration
  107. you want to set results for. This should provide sufficient
  108. uniqueness to get to any run using words.
  109. [OPTIONAL PARAMETERS]
  110. --spawn [testsuite_id] : Attempt to create a run based on the
  111. provided testsuite ID. Uses the name provided with --run.
  112. If plans/configurations are supplied, it will attempt to create
  113. it as a child of the provided plan, and with the supplied
  114. configurations.
  115. If the specified run already exists, the
  116. program will simply use the existing run,
  117. and disregard the supplied testsuite_id.
  118. If the specified plan does not exist, it will be created
  119. as well.
  120. --section [section_name] : When spawning, restrict the cases used
  121. in the provided testsuite ID to these sections.
  122. Option may be passed multiple times to specify multiple sections.
  123. [CONFIG OVERRIDES]
  124. In your \$HOME, (or the current directory, if your system has no
  125. concept of a home directory) put a file called .testrailrc with
  126. key=value syntax separated by newlines.
  127. Valid Keys are: apiurl,user,password
  128. [CONFIG OPTIONS] - These override the config, if present.
  129. If neither are used, you will be prompted.
  130. --apiurl [url] : full URL to get to TestRail index document
  131. --password [key] : Your TestRail Password, or a valid API key (TestRail 4.2 and above).
  132. --user [name] : Your TestRail User Name.
  133. [BEHAVIOR]
  134. --case-ok : Whether to consider each OK to correspond to
  135. a test in TestRail
  136. --step-results [name] : 'System Name' of a 'step_results' type field
  137. to set for your tests.
  138. These options are mutually exclusive. If neither is set, the
  139. overall result of the test will be used as the pass/fail for the test.
  140. [RESULT OPTIONS]
  141. --version : String describing the version of the system under test.
  142. --autoclose : If there are no more tests in 'untested' or 'retest'
  143. status for the specified run/plan, close it.
  144. PROVE PLUGIN:
  145. passing -PTestRail=apiurl,user,pass,project,run to prove will
  146. automatically upload your test results while the test is running if
  147. real-time results are desired.
  148. See App::Prove::Plugin::TestRail for more information.
  149. REQUIREMENTS:
  150. Your TestRail install must have 3 custom statuses with the internal
  151. names 'skip', 'todo_pass', and 'todo_fail', to represent those
  152. states which TAP can have.
  153. Also, be sure your tests don't output non-TAP (unknown) lines
  154. ending in dots (.)
  155. This will cause the preceding characters to be interpreted as
  156. a test name, which may lead to unexpected results.
  157. TESTING OPTIONS:
  158. --mock don't do any real HTTP requests.
  159. ";
  160. exit 0;
  161. }
  162. #Main loop------------
  163. my ($help,$apiurl,$user,$password,$project,$run,$case_per_ok,$step_results,$mock,$configs,$plan,$version,$spawn,$sections,$autoclose,$encoding);
  164. #parse switches
  165. GetOptions(
  166. 'run=s' => \$run,
  167. 'apiurl=s' => \$apiurl,
  168. 'password=s' => \$password,
  169. 'user=s' => \$user,
  170. 'project=s' => \$project,
  171. 'case-ok' => \$case_per_ok,
  172. 'step-results=s' => \$step_results,
  173. 'mock' => \$mock,
  174. 'config=s@' => \$configs,
  175. 'plan=s' => \$plan,
  176. 'version=s' => \$version,
  177. 'spawn=i' => \$spawn,
  178. 'section=s@' => \$sections,
  179. 'autoclose' => \$autoclose,
  180. 'e|encoding=s' => \$encoding,
  181. 'help' => \$help
  182. );
  183. if ($help) { help(); }
  184. #Parse config file if we are missing api url/key or user
  185. my $homedir = my_home() || '.';
  186. if (-e $homedir . '/.testrailrc' && (!$apiurl || !$password || !$user) ) {
  187. ($apiurl,$password,$user) = TestRail::Utils::parseConfig($homedir,1);
  188. }
  189. #If argument is passed use it instead of stdin
  190. my $file = $ARGV[0];
  191. die "No Such File $file" if ($file && !-e $file);
  192. my ($fh,$fcontents,@files);
  193. if ($file) {
  194. open($fh,'<',$file);
  195. while (<$fh>) {
  196. $_ = colorstrip($_); #strip prove brain damage
  197. if (TestRail::Utils::getFilenameFromTapLine($_)) {
  198. push(@files,$fcontents) if $fcontents;
  199. $fcontents = '';
  200. }
  201. $fcontents .= $_;
  202. }
  203. close($fh);
  204. push(@files,$fcontents) if $fcontents;
  205. } else {
  206. #Just read STDIN, print help if no file was passed
  207. if (IO::Interactive::Tiny::is_interactive()) {
  208. print "ERROR: no file passed, and no data piped in! See --help for usage.\n";
  209. exit(1);
  210. }
  211. if ( !$run || !$apiurl || !$password || !$user || !$project ) {
  212. print "ERROR: Interactive mode not allowed when piping input. See --help for options.\n";
  213. exit(1);
  214. }
  215. while (<>) {
  216. $_ = colorstrip($_); #strip prove brain damage
  217. if (TestRail::Utils::getFilenameFromTapLine($_)) {
  218. push(@files,$fcontents) if $fcontents;
  219. $fcontents = '';
  220. }
  221. $fcontents .= $_;
  222. }
  223. help() if !$fcontents; #Nothing passed to stdin!
  224. push(@files,$fcontents) if $fcontents;
  225. }
  226. #Interrogate user if they didn't provide info
  227. if (!$apiurl) {
  228. print "Type the API endpoint url for your testLink install below:\n";
  229. $apiurl = TestRail::Utils::userInput();
  230. }
  231. if (!$user) {
  232. print "Type your testLink user name below:\n";
  233. $user = TestRail::Utils::userInput();
  234. }
  235. if (!$password) {
  236. print "Type the password for your testLink user below:\n";
  237. $password = TestRail::Utils::userInput();
  238. }
  239. if (!$apiurl || !$password || !$user) {
  240. print "ERROR: api url, username and password cannot be blank.\n";
  241. exit 1;
  242. }
  243. #Interrogate user if they didn't provide info
  244. if (!$project) {
  245. print "Type the name of the project you are testing under:\n";
  246. $project = TestRail::Utils::userInput();
  247. }
  248. # Interrogate user if options were not passed
  249. if (!$run) {
  250. print "Type the name of the existing run you would like to run against:\n";
  251. $run = TestRail::Utils::userInput();
  252. }
  253. my $debug = 0;
  254. my $browser;
  255. if ($mock) {
  256. use Test::LWP::UserAgent::TestRailMock;
  257. $browser = $Test::LWP::UserAgent::TestRailMock::mockObject;
  258. $debug = 1;
  259. }
  260. my $result_options = undef;
  261. $result_options = {'version' => $version} if $version;
  262. my $tap;
  263. foreach my $phil (@files) {
  264. $tap = Test::Rail::Parser->new({
  265. 'tap' => $phil,
  266. 'apiurl' => $apiurl,
  267. 'user' => $user,
  268. 'pass' => $password,
  269. 'run' => $run,
  270. 'project' => $project,
  271. 'case_per_ok' => $case_per_ok,
  272. 'step_results' => $step_results,
  273. 'debug' => $debug,
  274. 'browser' => $browser,
  275. 'plan' => $plan,
  276. 'configs' => $configs,
  277. 'result_options' => $result_options,
  278. 'spawn' => $spawn,
  279. 'sections' => $sections,
  280. 'autoclose' => $autoclose,
  281. 'encoding' => $encoding,
  282. 'merge' => 1
  283. });
  284. $tap->run();
  285. }
  286. print "Done.\n";
  287. #all done
  288. 0;
  289. __END__
  290. =head1 SEE ALSO
  291. L<TestRail::API>
  292. L<App::Prove::Plugin::TestRail>
  293. L<TAP::Parser>
  294. L<File::HomeDir> for the finding of .testrailrc
  295. =head1 SPECIAL THANKS
  296. Thanks to cPanel Inc, for graciously funding the creation of this module.