Lock.pm 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. # ABSTRACT: Pick high priority cases for execution and lock them via the test results mechanism.
  2. # PODNAME: TestRail::Utils::Lock
  3. package TestRail::Utils::Lock;
  4. use strict;
  5. use warnings;
  6. use Carp qw{confess cluck};
  7. use Types::Standard qw( slurpy ClassName Object Str Int Bool HashRef ArrayRef Maybe Optional);
  8. use Type::Params qw( compile );
  9. use TestRail::API;
  10. use TestRail::Utils;
  11. =head1 DESCRIPTION
  12. Lock a test case via usage of the test result field.
  13. Has a hard limit of looking for 250 results, which is the only weakness of this locking approach.
  14. If you have other test runners that result in such tremendous numbers of lock collisions,
  15. it will result in 'hard-locked' cases, where manual intervention will be required to free the case.
  16. However in that case, one would assume you could afford to write a reaper script to detect and
  17. correct this condition, or consider altering your run strategy to reduce the probability of lock collisions.
  18. =head2 pickAndLockTest(options,[handle])
  19. Pick and lock a test case in a TestRail Run, and return it if successful, confess() on failure.
  20. testrail-lock's primary routine.
  21. =over 4
  22. =item HASHREF C<OPTIONS> - valid keys/values correspond to the longnames of arguments taken by L<testrail-lock>.
  23. =item TestRail::API C<HANDLE> - Instance of TestRail::API, in the case where the caller already has a valid object.
  24. There are two special keys, 'mock' and 'simulate_race_condition' in the HASHREF that are used for testing.
  25. =back
  26. =cut
  27. sub pickAndLockTest {
  28. my ($opts, $tr) = @_;
  29. if ($opts->{mock}) {
  30. require Test::LWP::UserAgent::TestRailMock; #LazyLoad
  31. $opts->{browser} = $Test::LWP::UserAgent::TestRailMock::mockObject;
  32. $opts->{debug} = 1;
  33. }
  34. $tr //= TestRail::API->new($opts->{apiurl},$opts->{user},$opts->{password},$opts->{'encoding'},$opts->{'debug'});
  35. $tr->{'browser'} = $opts->{'browser'} if $opts->{'browser'};
  36. $tr->{'debug'} = 0;
  37. my ($project,$plan,$run) = TestRail::Utils::getRunInformation($tr,$opts);
  38. my $status_ids;
  39. # Process statuses
  40. @$status_ids = $tr->statusNamesToIds($opts->{'lockname'},'untested','retest');
  41. my ($lock_status_id,$untested_id,$retest_id) = @$status_ids;
  42. my $cases = $tr->getTests($run->{'id'});
  43. my @statuses_to_check_for = ($untested_id,$retest_id);
  44. @statuses_to_check_for = ($lock_status_id) if $opts->{'simulate_race_condition'}; #Unit test stuff
  45. # Limit to only non-locked and open cases
  46. @$cases = grep { my $tstatus = $_->{'status_id'}; scalar(grep { $tstatus eq $_ } @statuses_to_check_for) } @$cases;
  47. @$cases = sort { $a->{'priority_id'} <=> $b->{'priority_id'} } @$cases; #Sort by priority
  48. my $test = shift @$cases;
  49. confess "No outstanding cases in the provided run.\n" if !$test;
  50. my $title;
  51. foreach my $test (@$cases) {
  52. $title = lockTest($test,$lock_status_id,$tr);
  53. last if $title;
  54. }
  55. confess "Failed to lock case!" if !$title;
  56. return $title;
  57. }
  58. =head2 lockTest(test,lock_status_id,handle)
  59. Lock the specified test.
  60. =over 4
  61. =item HASHREF C<TEST> - Test object returned by getTest, or a similar method.
  62. =item INTEGER C<LOCK_STATUS_ID> - Status used to denote locking of test
  63. =item TestRail::API C<HANDLE> - Instance of TestRail::API
  64. =back
  65. Returns undef in the event a lock could not occur, and warns on lock collisions.
  66. =cut
  67. sub lockTest {
  68. state $check = compile(HashRef, Int, Object);
  69. my ($test,$lock_status_id,$handle) = $check->(@_);
  70. my $res = $tr->createTestResults(
  71. $test->{id},
  72. $lock_status_id,
  73. "Test Locked by $opts->{hostname}.\n
  74. If this result is preceded immediately by another lock statement like this, please disregard it;
  75. a lock collision occurred."
  76. );
  77. #If we've got more than 100 lock conflicts, we have big-time problems
  78. my $results = $tr->getTestResults($test->{id},100);
  79. #Remember, we're returned results from newest to oldest...
  80. my $next_one = 0;
  81. foreach my $result (@$results) {
  82. unless ($result->{'status_id'} == $lock_status_id) {
  83. #Clearly no lock conflict going on here if next_one is true
  84. last if $next_one;
  85. #Otherwise just skip it until we get to the test we locked
  86. next;
  87. }
  88. if ($result->{id} == $res->{'id'}) {
  89. $next_one = 1;
  90. next;
  91. }
  92. if ($next_one) {
  93. #If we got this far, a lock conflict occurred. Try the next one.
  94. warn "Lock conflict detected. Try again...\n";
  95. return undef;
  96. }
  97. }
  98. return $test->{'title'} if $next_one;
  99. return undef;
  100. }
  101. 1;