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