Utils.pm 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. # ABSTRACT: Utilities for the testrail command line functions, and their main loops.
  2. # PODNAME: TestRail::Utils
  3. package TestRail::Utils;
  4. use strict;
  5. use warnings;
  6. use Carp qw{confess cluck};
  7. use Pod::Perldoc 3.10;
  8. use IO::Interactive::Tiny ();
  9. use Term::ANSIColor 2.01 qw(colorstrip);
  10. use Scalar::Util qw{blessed};
  11. =head1 SCRIPT HELPER FUNCTIONS
  12. =head2 help
  13. Print the perldoc for $0 and exit.
  14. =cut
  15. sub help {
  16. @ARGV = ($0);
  17. Pod::Perldoc->run();
  18. exit 0;
  19. }
  20. =head2 userInput
  21. Wait for user input and return it.
  22. =cut
  23. sub userInput {
  24. local $| = 1;
  25. my $rt = <STDIN>;
  26. chomp $rt;
  27. return $rt;
  28. }
  29. =head2 interrogateUser($options,@keys)
  30. Wait for specified keys via userInput, and put them into $options HASHREF, if they are not already defined.
  31. Returns modified $options HASHREF.
  32. Dies if the user provides no value.
  33. =cut
  34. sub interrogateUser {
  35. my ($options,@keys) = @_;
  36. foreach my $key (@keys) {
  37. if (!$options->{$key}) {
  38. print "Type the $key for your testLink install below:\n";
  39. $options->{$key} = TestRail::Utils::userInput();
  40. die "$key cannot be blank!" unless $options->{$key};
  41. }
  42. }
  43. return $options;
  44. }
  45. =head2 parseConfig(homedir)
  46. Parse .testrailrc in the provided home directory.
  47. Returns:
  48. ARRAY - (apiurl,password,user)
  49. =cut
  50. sub parseConfig {
  51. my ($homedir,$login_only) = @_;
  52. my $results = {};
  53. my $arr =[];
  54. open(my $fh, '<', $homedir . '/.testrailrc') or return (undef,undef,undef);#couldn't open!
  55. while (<$fh>) {
  56. chomp;
  57. @$arr = split(/=/,$_);
  58. if (scalar(@$arr) != 2) {
  59. warn("Could not parse $_ in '$homedir/.testrailrc'!\n");
  60. next;
  61. }
  62. $results->{lc($arr->[0])} = $arr->[1];
  63. }
  64. close($fh);
  65. return ($results->{'apiurl'},$results->{'password'},$results->{'user'}) if $login_only;
  66. return $results;
  67. }
  68. =head2 getFilenameFromTapLine($line)
  69. Analyze TAP output by prove and look for filename boundaries (no other way to figure out what file is run).
  70. Long story short: don't end 'unknown' TAP lines with any number of dots if you don't want it interpreted as a test name.
  71. Apparently this is the TAP way of specifying the file that's run...which is highly inadequate.
  72. Inputs:
  73. STRING LINE - some line of TAP
  74. Returns:
  75. STRING filename of the test that output the TAP.
  76. =cut
  77. sub getFilenameFromTapLine {
  78. my $orig = shift;
  79. $orig =~ s/ *$//g; # Strip all trailing whitespace
  80. #Special case
  81. my ($is_skipall) = $orig =~ /(.*)\.+ skipped:/;
  82. return $is_skipall if $is_skipall;
  83. my @process_split = split(/ /,$orig);
  84. return 0 unless scalar(@process_split);
  85. my $dotty = pop @process_split; #remove the ........ (may repeat a number of times)
  86. return 0 if $dotty =~ /\d/; #Apparently looking for literal dots returns numbers too. who knew?
  87. chomp $dotty;
  88. my $line = join(' ',@process_split);
  89. #IF it ends in a bunch of dots
  90. #AND it isn't an ok/not ok
  91. #AND it isn't a comment
  92. #AND it isn't blank
  93. #THEN it's a test name
  94. return $line if ($dotty =~ /^\.+$/ && !($line =~ /^ok|not ok/) && !($line =~ /^# /) && $line);
  95. return 0;
  96. }
  97. =head2 TAP2TestFiles(file)
  98. Returns ARRAY of TAP output for the various test files therein.
  99. file is optional, will read TAP from STDIN if not passed.
  100. =cut
  101. sub TAP2TestFiles {
  102. my $file = shift;
  103. my ($fh,$fcontents,@files);
  104. if ($file) {
  105. open($fh,'<',$file);
  106. while (<$fh>) {
  107. $_ = colorstrip($_); #strip prove brain damage
  108. if (getFilenameFromTapLine($_)) {
  109. push(@files,$fcontents) if $fcontents;
  110. $fcontents = '';
  111. }
  112. $fcontents .= $_;
  113. }
  114. close($fh);
  115. push(@files,$fcontents) if $fcontents;
  116. } else {
  117. #Just read STDIN, print help if no file was passed
  118. die "ERROR: no file passed, and no data piped in! See --help for usage.\n" if IO::Interactive::Tiny::is_interactive();
  119. while (<>) {
  120. $_ = colorstrip($_); #strip prove brain damage
  121. if (getFilenameFromTapLine($_)) {
  122. push(@files,$fcontents) if $fcontents;
  123. $fcontents = '';
  124. }
  125. $fcontents .= $_;
  126. }
  127. push(@files,$fcontents) if $fcontents;
  128. }
  129. return @files;
  130. }
  131. =head2 getRunInformation
  132. Return the relevant project definition, plan, run and milestone definition HASHREFs for the provided options.
  133. Dies in the event the project/plan/run could not be found.
  134. =cut
  135. sub getRunInformation {
  136. my ($tr,$opts) = @_;
  137. confess("First argument must be instance of TestRail::API") unless blessed($tr) eq 'TestRail::API';
  138. my $project = $tr->getProjectByName($opts->{'project'});
  139. confess "No such project '$opts->{project}'.\n" if !$project;
  140. my ($run,$plan);
  141. if ($opts->{'plan'}) {
  142. $plan = $tr->getPlanByName($project->{'id'},$opts->{'plan'});
  143. confess "No such plan '$opts->{plan}'!\n" if !$plan;
  144. $run = $tr->getChildRunByName($plan,$opts->{'run'}, $opts->{'configs'});
  145. } else {
  146. $run = $tr->getRunByName($project->{'id'},$opts->{'run'});
  147. }
  148. confess "No such run '$opts->{run}' matching the provided configs (if any).\n" if !$run;
  149. #If the run/plan has a milestone set, then return it too
  150. my $milestone;
  151. my $mid = $plan ? $plan->{'milestone_id'} : $run->{'milestone_id'};
  152. if ($mid) {
  153. $milestone = $tr->getMilestoneByID($mid);
  154. confess "Could not fetch run milestone!" unless $milestone; #hope this doesn't happen
  155. }
  156. return ($project, $plan, $run, $milestone);
  157. }
  158. =head2 getHandle(opts)
  159. Convenience method for binaries and testing.
  160. Returns a new TestRail::API when passed an options hash such as is built by most of the binaries,
  161. or returned by parseConfig.
  162. Has a special 'mock' hash key that can only be used by those testing this distribution.
  163. =cut
  164. sub getHandle {
  165. my $opts = shift;
  166. $opts->{'debug'} = 1 if ($opts->{'mock'});
  167. my $tr = TestRail::API->new($opts->{apiurl},$opts->{user},$opts->{password},$opts->{'encoding'},$opts->{'debug'});
  168. if ($opts->{'mock'}) {
  169. use lib 't/lib'; #Unit tests will always run from the main dir during make test
  170. require Test::LWP::UserAgent::TestRailMock;
  171. $opts->{'browser'} = $Test::LWP::UserAgent::TestRailMock::mockObject;
  172. $opts->{'debug'} = 0;
  173. }
  174. return $tr;
  175. }
  176. 1;
  177. __END__
  178. =head1 SPECIAL THANKS
  179. Thanks to cPanel Inc, for graciously funding the creation of this module.