testrail-report 11 KB

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