Find.pm 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. # PODNAME: TestRail::Utils::Find
  2. # ABSTRACT: Find runs and tests according to user specifications.
  3. package TestRail::Utils::Find;
  4. use strict;
  5. use warnings;
  6. use Carp qw{confess cluck};
  7. use Scalar::Util qw{blessed};
  8. use File::Find;
  9. use Cwd qw{abs_path};
  10. use File::Basename qw{basename};
  11. use TestRail::Utils;
  12. =head1 DESCRIPTION
  13. =head1 FUNCTIONS
  14. =head2 findRuns
  15. Find runs based on the options HASHREF provided.
  16. See the documentation for L<testrail-runs>, as the long argument names there correspond to hash keys.
  17. The primary routine of testrail-runs.
  18. =over 4
  19. =item HASHREF C<OPTIONS> - flags acceptable by testrail-tests
  20. =item TestRail::API C<HANDLE> - TestRail::API object
  21. =back
  22. Returns ARRAYREF of run definition HASHREFs.
  23. =cut
  24. sub findRuns {
  25. my ($opts,$tr) = @_;
  26. confess("TestRail handle must be provided as argument 2") unless blessed($tr) eq 'TestRail::API';
  27. my ($status_ids);
  28. #Process statuses
  29. if ($opts->{'statuses'}) {
  30. @$status_ids = $tr->statusNamesToIds(@{$opts->{'statuses'}});
  31. }
  32. my $project = $tr->getProjectByName($opts->{'project'});
  33. confess("No such project '$opts->{project}'.\n") if !$project;
  34. my $pconfigs = [];
  35. $pconfigs = $tr->translateConfigNamesToIds($project->{'id'},$opts->{configs}) if $opts->{'configs'};
  36. my ($runs,$plans,$planRuns,$cruns,$found) = ([],[],[],[],0);
  37. $runs = $tr->getRuns($project->{'id'}) if (!$opts->{'configs'}); # If configs are passed, global runs are not in consideration.
  38. $plans = $tr->getPlans($project->{'id'});
  39. @$plans = map {$tr->getPlanByID($_->{'id'})} @$plans;
  40. foreach my $plan (@$plans) {
  41. $cruns = $tr->getChildRuns($plan);
  42. next if !$cruns;
  43. foreach my $run (@$cruns) {
  44. next if scalar(@$pconfigs) != scalar(@{$run->{'config_ids'}});
  45. #Compare run config IDs against desired, invalidate run if all conditions not satisfied
  46. $found = 0;
  47. foreach my $cid (@{$run->{'config_ids'}}) {
  48. $found++ if grep {$_ == $cid} @$pconfigs;
  49. }
  50. $run->{'created_on'} = $plan->{'created_on'};
  51. $run->{'milestone_id'} = $plan->{'milestone_id'};
  52. push(@$planRuns, $run) if $found == scalar(@{$run->{'config_ids'}});
  53. }
  54. }
  55. push(@$runs,@$planRuns);
  56. if ($opts->{'statuses'}) {
  57. @$runs = $tr->getRunSummary(@$runs);
  58. @$runs = grep { defined($_->{'run_status'}) } @$runs; #Filter stuff with no results
  59. foreach my $status (@{$opts->{'statuses'}}) {
  60. @$runs = grep { $_->{'run_status'}->{$status} } @$runs; #If it's positive, keep it. Otherwise forget it.
  61. }
  62. }
  63. #Sort FIFO/LIFO by milestone or creation date of run
  64. my $sortkey = 'created_on';
  65. if ($opts->{'milesort'}) {
  66. @$runs = map {
  67. my $run = $_;
  68. $run->{'milestone'} = $tr->getMilestoneByID($run->{'milestone_id'}) if $run->{'milestone_id'};
  69. my $milestone = $run->{'milestone'} ? $run->{'milestone'}->{'due_on'} : 0;
  70. $run->{'due_on'} = $milestone;
  71. $run
  72. } @$runs;
  73. $sortkey = 'due_on';
  74. }
  75. if ($opts->{'lifo'}) {
  76. @$runs = sort { $b->{$sortkey} <=> $a->{$sortkey} } @$runs;
  77. } else {
  78. @$runs = sort { $a->{$sortkey} <=> $b->{$sortkey} } @$runs;
  79. }
  80. return $runs;
  81. }
  82. =head2 getTests(opts,testrail)
  83. Get the tests specified by the options passed.
  84. =over 4
  85. =item HASHREF C<OPTS> - Options for getting the tests
  86. =over 4
  87. =item STRING C<PROJECT> - name of Project to look for tests in
  88. =item STRING C<RUN> - name of Run to get tests from
  89. =item STRING C<PLAN> (optional) - name of Plan to get run from
  90. =item ARRAYREF[STRING] C<CONFIGS> (optional) - names of configs run must satisfy, if part of a plan
  91. =item ARRAYREF[STRING] C<USERS> (optional) - names of users to filter cases by assignee
  92. =item ARRAYREF[STRING] C<STATUSES> (optional) - names of statuses to filter cases by
  93. =back
  94. =back
  95. Returns ARRAYREF of tests, and the run in which they belong.
  96. =cut
  97. sub getTests {
  98. my ($opts,$tr) = @_;
  99. confess("TestRail handle must be provided as argument 2") unless blessed($tr) eq 'TestRail::API';
  100. my (undef,undef,$run) = TestRail::Utils::getRunInformation($tr,$opts);
  101. my ($status_ids,$user_ids);
  102. #Process statuses
  103. @$status_ids = $tr->statusNamesToIds(@{$opts->{'statuses'}}) if $opts->{'statuses'};
  104. #Process assignedto ids
  105. @$user_ids = $tr->userNamesToIds(@{$opts->{'users'}}) if $opts->{'users'};
  106. my $cases = $tr->getTests($run->{'id'},$status_ids,$user_ids);
  107. return ($cases,$run);
  108. }
  109. =head2 findTests(opts,case1,...,caseN)
  110. Given an ARRAY of tests, find tests meeting your criteria (or not) in the specified directory.
  111. =over 4
  112. =item HASHREF C<OPTS> - Options for finding tests:
  113. =over 4
  114. =item STRING C<MATCH> - Only return tests which exist in the path provided. Mutually exclusive with no-match.
  115. =item STRING C<NO-MATCH> - Only return tests which aren't in the path provided (orphan tests). Mutually exclusive with match.
  116. =item BOOL C<NO-RECURSE> - Do not do a recursive scan for files.
  117. =item BOOL C<NAMES-ONLY> - Only return the names of the tests rather than the entire test objects.
  118. =item STRING C<EXTENSION> (optional) - Only return files ending with the provided text (e.g. .t, .test, .pl, .pm)
  119. =back
  120. =item ARRAY C<CASES> - Array of cases to translate to pathnames based on above options.
  121. =back
  122. Returns tests found that meet the criteria laid out in the options.
  123. Provides absolute path to tests if match is passed; this is the 'full_title' key if names-only is false/undef.
  124. Dies if mutually exclusive options are passed.
  125. =cut
  126. sub findTests {
  127. my ($opts,@cases) = @_;
  128. confess "Error! match and no-match options are mutually exclusive.\n" if ($opts->{'match'} && $opts->{'no-match'});
  129. my @tests = @cases;
  130. my (@realtests);
  131. my $ext = $opts->{'extension'} // '';
  132. if ($opts->{'match'} || $opts->{'no-match'}) {
  133. my @tmpArr = ();
  134. my $dir = $opts->{'match'} ? $opts->{'match'} : $opts->{'no-match'};
  135. if (!$opts->{'no-recurse'}) {
  136. File::Find::find( sub { push(@realtests,$File::Find::name) if -f && m/\Q$ext\E$/ }, $dir );
  137. } else {
  138. @realtests = glob("$dir/*$ext");
  139. }
  140. foreach my $case (@cases) {
  141. foreach my $path (@realtests) {
  142. next unless $case->{'title'} eq basename($path);
  143. $case->{'path'} = $path;
  144. push(@tmpArr, $case);
  145. last;
  146. }
  147. }
  148. @tests = @tmpArr; #XXX if you have dups in your tree, be-ware
  149. @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.
  150. }
  151. @tests = map { abs_path($_->{'path'}) } @tests if $opts->{'match'} && $opts->{'names-only'};
  152. @tests = map { $_->{'full_title'} = abs_path($_->{'path'}); $_ } @tests if $opts->{'match'} && !$opts->{'names-only'};
  153. @tests = map { $_->{'title'} } @tests if !$opts->{'match'} && $opts->{'names-only'};
  154. return @tests;
  155. }
  156. 1;
  157. __END__
  158. =head1 SPECIAL THANKS
  159. Thanks to cPanel Inc, for graciously funding the creation of this module.