Utils.pm 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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 Scalar::Util qw{blessed};
  9. use File::Find;
  10. use Cwd qw{abs_path};
  11. use File::Basename qw{basename};
  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. exit 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 getRunInformation
  99. Return the relevant project definition, plan and run definition HASHREFs for the provided options.
  100. Practically all the binaries need this information, so it has been subroutined out.
  101. Dies in the event the project/plan/run could not be found.
  102. =cut
  103. sub getRunInformation {
  104. my ($tr,$opts) = @_;
  105. confess("First argument must be instance of TestRail::API") unless blessed($tr) eq 'TestRail::API';
  106. my $project = $tr->getProjectByName($opts->{'project'});
  107. confess "No such project '$opts->{project}'.\n" if !$project;
  108. my ($run,$plan);
  109. if ($opts->{'plan'}) {
  110. $plan = $tr->getPlanByName($project->{'id'},$opts->{'plan'});
  111. confess "No such plan '$opts->{plan}'!\n" if !$plan;
  112. $run = $tr->getChildRunByName($plan,$opts->{'run'}, $opts->{'configs'});
  113. } else {
  114. $run = $tr->getRunByName($project->{'id'},$opts->{'run'});
  115. }
  116. confess "No such run '$opts->{run}' matching the provided configs (if any).\n" if !$run;
  117. return ($project,$plan,$run);
  118. }
  119. =head2 findTests(opts,case1,...,caseN)
  120. Given an ARRAY of tests, find tests meeting your criteria (or not) in the specified directory.
  121. =over 4
  122. =item HASHREF C<OPTS> - Options for finding tests:
  123. =over 4
  124. =item STRING C<MATCH> - Only return tests which exist in the path provided. Mutually exclusive with no-match.
  125. =item STRING C<NO-MATCH> - Only return tests which aren't in the path provided (orphan tests). Mutually exclusive with match.
  126. =item BOOL C<NO-RECURSE> - Do not do a recursive scan for files.
  127. =item BOOL C<NAMES-ONLY> - Only return the names of the tests rather than the entire test objects.
  128. =back
  129. =item ARRAY C<CASES> - Array of cases to translate to pathnames based on above options.
  130. =back
  131. Returns tests found that meet the criteria laid out in the options.
  132. Provides absolute path to tests if match is passed; this is the 'full_title' key if names-only is false/undef.
  133. Dies if mutually exclusive options are passed.
  134. =cut
  135. sub findTests {
  136. my ($opts,@cases) = @_;
  137. confess "Error! match and no-match options are mutually exclusive.\n" if ($opts->{'match'} && $opts->{'no-match'});
  138. my @tests = @cases;
  139. my (@realtests);
  140. if ($opts->{'match'} || $opts->{'no-match'}) {
  141. my $dir = $opts->{'match'} ? $opts->{'match'} : $opts->{'no-match'};
  142. if (!$opts->{'no-recurse'}) {
  143. File::Find::find( sub { push(@realtests,$File::Find::name) if -f }, $dir );
  144. @tests = grep {my $real = $_->{'title'}; grep { $real eq basename($_) } @realtests} @cases; #XXX if you have dups in your tree, be-ware
  145. } else {
  146. #Handle special windows case -- glob doesn't prepend abspath
  147. @realtests = glob("$dir/*");
  148. @tests = map {
  149. $_->{'title'} = "$dir/".$_->{'title'} if( $^O eq 'MSWin32' );
  150. $_
  151. } grep {my $fname = $_->{'title'}; grep { basename($_) eq $fname} @realtests } @cases;
  152. }
  153. @tests = map {{'title' => $_}} grep {my $otest = basename($_); scalar(grep {basename($_->{'title'}) eq $otest} @tests) == 0} @realtests if $opts->{'no-match'}; #invert the list in this case.
  154. }
  155. @tests = map { abs_path($_->{'title'}) } @tests if $opts->{'match'} && $opts->{'names-only'};
  156. @tests = map { $_->{'full_title'} = abs_path($_->{'title'}); $_ } @tests if $opts->{'match'} && !$opts->{'names-only'};
  157. @tests = map { $_->{'title'} } @tests if !$opts->{'match'} && $opts->{'names-only'};
  158. return @tests;
  159. }
  160. 1;
  161. __END__
  162. =head1 SPECIAL THANKS
  163. Thanks to cPanel Inc, for graciously funding the creation of this module.