go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/milo/ui/src/test_verdict/tools/variant_utils/variant_utils.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 { groupBy } from 'lodash-es';
    16  
    17  import { Variant } from '@/proto/go.chromium.org/luci/resultdb/proto/v1/common.pb';
    18  
    19  /**
    20   * A list of variant keys that have special meanings. Ordered by their
    21   * significance.
    22   */
    23  const SPECIAL_VARIANT_KEYS = ['builder', 'test_suite'];
    24  
    25  /**
    26   * Returns the order of the variant key.
    27   */
    28  function getVariantKeyOrder(key: string) {
    29    const i = SPECIAL_VARIANT_KEYS.indexOf(key);
    30    if (i === -1) {
    31      return SPECIAL_VARIANT_KEYS.length;
    32    }
    33    return i;
    34  }
    35  
    36  /**
    37   * Given a list of variants, return a list of variant keys that can uniquely
    38   * identify each unique variant in the list.
    39   */
    40  export function getCriticalVariantKeys(variants: readonly Variant[]): string[] {
    41    // Get all the keys.
    42    const keys = new Set<string>();
    43    for (const variant of variants) {
    44      for (const key of Object.keys(variant.def)) {
    45        keys.add(key);
    46      }
    47    }
    48  
    49    // Sort the variant keys. We prefer keys in SPECIAL_VARIANT_KEYS over others.
    50    const orderedKeys = [...keys.values()].sort((key1, key2) => {
    51      const priorityDiff = getVariantKeyOrder(key1) - getVariantKeyOrder(key2);
    52      if (priorityDiff !== 0) {
    53        return priorityDiff;
    54      }
    55      return key1.localeCompare(key2);
    56    });
    57  
    58    // Find all the critical keys.
    59    const criticalKeys: string[] = [];
    60    let variantGroups = [variants];
    61    for (const key of orderedKeys) {
    62      const newGroups: typeof variantGroups = [];
    63      for (const group of variantGroups) {
    64        newGroups.push(...Object.values(groupBy(group, (g) => g.def[key])));
    65      }
    66  
    67      // Group by this key split the groups into more groups. Add this key to the
    68      // critical key list.
    69      if (newGroups.length !== variantGroups.length) {
    70        criticalKeys.push(key);
    71        variantGroups = newGroups;
    72      }
    73    }
    74  
    75    // Add at least one key to the critical key list.
    76    if (criticalKeys.length === 0 && orderedKeys.length !== 0) {
    77      criticalKeys.push(orderedKeys[0]);
    78    }
    79  
    80    return criticalKeys;
    81  }