go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/analysis/frontend/ui/src/hooks/use_fetch_exonerated_test_variant_branches.ts (about)

     1  // Copyright 2024 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  import { useQuery } from 'react-query';
    16  
    17  import { getClustersService, getTestVariantsService } from '@/services/services';
    18  import { prpcRetrier } from '@/tools/prpc_retrier';
    19  import {
    20    QueryTestVariantStabilityRequest,
    21    QueryTestVariantStabilityRequest_TestVariantPosition,
    22    TestStabilityCriteria,
    23    TestVariantStabilityAnalysis,
    24    TestVariantStabilityAnalysis_FailureRate,
    25    TestVariantStabilityAnalysis_FlakeRate,
    26  } from '@/proto/go.chromium.org/luci/analysis/proto/v1/test_variants.pb';
    27  import { GitilesCommit, SourceRef } from '@/proto/go.chromium.org/luci/analysis/proto/v1/sources.pb';
    28  import { Variant } from '@/proto/go.chromium.org/luci/analysis/proto/v1/common.pb';
    29  import { ClusterExoneratedTestVariantBranch } from '@/proto/go.chromium.org/luci/analysis/proto/v1/clusters.pb';
    30  
    31  const useFetchExoneratedTestVariantBranches = (project: string, clusterAlgorithm: string, clusterId: string) => {
    32    return useQuery(['exoneratedTestVariantBranches', project, clusterAlgorithm, clusterId], async () => {
    33      const clusterService = getClustersService();
    34      const clusterResponse = await clusterService.queryExoneratedTestVariantBranches({
    35        parent: `projects/${project}/clusters/${clusterAlgorithm}/${clusterId}/exoneratedTestVariantBranches`,
    36      });
    37      const clusterExoneratedTestVariantBranches = clusterResponse.testVariantBranches;
    38      if (clusterExoneratedTestVariantBranches.length === 0) {
    39        // The criteria is irrelevant as there are no branches to display.
    40        // Return a filler criteria.
    41        const emptyCriteria : TestStabilityCriteria = {
    42          failureRate: {
    43            failureThreshold: 0,
    44            consecutiveFailureThreshold: 0,
    45          },
    46          flakeRate: {
    47            flakeRateThreshold: 0.0,
    48            flakeThreshold: 0,
    49            minWindow: 0,
    50          },
    51        };
    52        const result: ExoneratedTestVariantBranches = {
    53          testVariantBranches: [],
    54          criteria: emptyCriteria,
    55        };
    56        return result;
    57      }
    58      const tvRequest: QueryTestVariantStabilityRequest = {
    59        project: project,
    60        testVariants: clusterExoneratedTestVariantBranches.map((tvb) => {
    61          let gitilesCommit: GitilesCommit | undefined;
    62          if (tvb.sourceRef?.gitiles) {
    63            gitilesCommit = {
    64              host: tvb.sourceRef.gitiles.host,
    65              ref: tvb.sourceRef.gitiles.ref,
    66              project: tvb.sourceRef.gitiles.project,
    67              // Query for the most recent commit position.
    68              commitHash: 'ff'.repeat(20),
    69              position: '999999999999',
    70            };
    71          }
    72          return QueryTestVariantStabilityRequest_TestVariantPosition.create({
    73            testId: tvb.testId,
    74            variant: tvb.variant,
    75            sources: {
    76              gitilesCommit: gitilesCommit,
    77            },
    78          });
    79        }),
    80      };
    81      const tvService = getTestVariantsService();
    82      const tvResponse = await tvService.queryStability(tvRequest);
    83  
    84      const exoneratedTVBs = tvResponse.testVariants?.map((analyzedTV, i) => {
    85        // QueryFailureRate returns test variants in the same order
    86        // that they are requested.
    87        const exoneratedTV = clusterExoneratedTestVariantBranches[i];
    88        return testVariantBranchFromAnalysis(exoneratedTV, analyzedTV);
    89      }) || [];
    90  
    91      const result: ExoneratedTestVariantBranches = {
    92        testVariantBranches: exoneratedTVBs,
    93        // Criteria will always be set if the RPC succeeds.
    94        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    95        criteria: tvResponse.criteria!,
    96      };
    97      return result;
    98    }, {
    99      retry: prpcRetrier,
   100    });
   101  };
   102  
   103  export default useFetchExoneratedTestVariantBranches;
   104  
   105  function testVariantBranchFromAnalysis(ctvb: ClusterExoneratedTestVariantBranch, analysis: TestVariantStabilityAnalysis): ExoneratedTestVariantBranch {
   106    return {
   107      testId: ctvb.testId,
   108      variant: ctvb.variant,
   109      // Source ref will always be set.
   110      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   111      sourceRef: ctvb.sourceRef!,
   112      // Last exoneration will always be set.
   113      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   114      lastExoneration: ctvb.lastExoneration!,
   115      criticalFailuresExonerated: ctvb.criticalFailuresExonerated,
   116      // It is a guarantee of the RPC that both failure rate and flake rate will be set.
   117      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   118      failureRate: analysis.failureRate!,
   119      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
   120      flakeRate: analysis.flakeRate!,
   121    };
   122  }
   123  
   124  export interface ExoneratedTestVariantBranches {
   125    testVariantBranches: ExoneratedTestVariantBranch[];
   126    criteria: TestStabilityCriteria;
   127  }
   128  
   129  export interface ExoneratedTestVariantBranch {
   130    testId: string;
   131    variant: Variant | undefined;
   132    sourceRef: SourceRef;
   133    // RFC 3339 encoded date/time of the last exoneration.
   134    lastExoneration: string;
   135    // The number of critical failures exonerated in the last week.
   136    criticalFailuresExonerated: number;
   137  
   138    failureRate: TestVariantStabilityAnalysis_FailureRate;
   139    flakeRate: TestVariantStabilityAnalysis_FlakeRate;
   140  }