tools/disable_tests/gtest.py
def disabler(full_test_name: str, source_file: str, new_cond: Condition) -> str:
  """Disable a GTest test within the given file.

  Args:
    test_name: The name of the test, in the form TestSuite.TestName
    lines: The existing file, split into lines. Note that each line ends with a
      newline character.
    new_cond: The additional conditions under which to disable the test. These
      will be merged with any existing conditions.

  Returns:
    The new contents to write into the file, with the test disabled.
  """

  lines = source_file.split('\n')

  test_name = full_test_name.split('.')[1]

  disabled = 'DISABLED_' + test_name
  maybe = 'MAYBE_' + test_name
  current_name = None
  src_range = None

  # Search backwards from the end of the file, looking for the given test.
  # TODO: We should do more to avoid false positives. These could be caused by
  # the test name being mentioned in a comment or string, or test names that are
  # substrings of the given test name.
  for i in range(len(lines) - 1, -1, -1):
    line = lines[i]

    # TODO: This will incorrectly match test names within comments.
    idents = find_identifiers(line)

    if maybe in idents:
      # If the test is already conditionally disabled, search backwards to find
      # the preprocessor conditional block that disables it, and parse out the
      # conditions.
      existing_cond, src_range = find_conditions(lines, i, test_name)
      current_name = maybe
      break
    if disabled in idents:
      existing_cond = conditions.ALWAYS
      current_name = disabled
      break
    if test_name in idents:
      existing_cond = conditions.NEVER
      current_name = test_name
      break
  else:
    raise Exception(f"Couldn't find test definition for {full_test_name}")

  test_name_index = i

  merged = conditions.merge(existing_cond, new_cond)

  # If it's not conditionally disabled, we don't need a pre-processor block to
  # conditionally define the name. We just change the name within the test macro
  # to its appropriate value, and delete any existing preprocessor block.
  if isinstance(merged, conditions.BaseCondition):
    if merged == conditions.ALWAYS:
      replacement_name = disabled
    elif merged == conditions.NEVER:
      replacement_name = test_name

    lines[test_name_index] = \
        lines[test_name_index].replace(current_name, replacement_name)
    modified_line = test_name_index

    if src_range:
      del lines[src_range[0]:src_range[1] + 1]
      modified_line -= src_range[1] - src_range[0] + 1

    return clang_format('\n'.join(lines), [modified_line])

  # => now conditionally disabled
  lines[test_name_index] = lines[test_name_index].replace(current_name, maybe)

  condition_impl = cc_format_condition(merged)

  condition_block = [
      f'#if {condition_impl}',
      f'#define {maybe} {disabled}',
      '#else',
      f'#define {maybe} {test_name}',
      '#endif',
  ]

  modified_lines = []

  if src_range:
    # Replace the existing condition, if there was one
    lines[src_range[0]:src_range[1] + 1] = condition_block
    modified_lines = list(
        range(src_range[0], src_range[0] + len(condition_block)))
  else:
    # If not, find where to add a new one.
    for i in range(test_name_index, -1, -1):
      if any(test_macro in lines[i] for test_macro in TEST_MACROS):
        break
    else:
      raise Exception("Couldn't find where to insert test conditions")
    lines[i:i] = condition_block
    modified_lines = list(range(i, i + len(condition_block)))

  # Insert includes.
  # First find the set of headers we need for the given condition.
  necessary_includes = {
      include
      for var in conditions.find_terminals(merged)
      if (include := var.gtest_info.header) is not None
  }

  # Then scan through the existing set of headers, finding which are already
  # included, and where to insert the #includes for those that aren't.
  to_insert: Dict[str, int] = {}
  last_include = None
  if len(necessary_includes) > 0:
    # Track the path of the previous include, so we can find where to insert it
    # alphabetically.
    prev_path = ''
    i = 0
    while i < len(lines):
      match = get_directive(lines, i)
      i += 1
      if match is None:
        continue

      name, args = match
      if name != 'include':
        continue

      last_include = i

      # Strip both forms of delimiter around the include path.
      path = args[0].strip('<>"')
      # If this include path exactly matches one we need, then it's already
      # included and we don't need to add it. We remove it from both
      # necessary_includes, to not consider it, and from to_insert, in case we
      # already found a place to insert it.
      try:
        necessary_includes.remove(path)
      except KeyError:
        pass
      to_insert.pop(path, None)

      for include in necessary_includes:
        # Optimistically assume the includes are in sorted order, and try to
        # find a spot where we can insert each one.
        if prev_path < include < path:
          to_insert[include] = i
          necessary_includes.remove(include)
          prev_path = include
          i -= 1
          break

  if last_include is None:
    # This should never really happen outside of our tests, as the file will
    # need to at least include the GTest headers.
    last_include = 0

  # Deal with any includes that we couldn't find a spot for by putting them at
  # the end of the list of includes.
  for include in necessary_includes:
    assert last_include is not None
    to_insert[include] = last_include

  # Finally, insert all the includes in the positions we decided on. Do so from
  # higher to lower indices, so we don't need to adjust later positions to
  # account for previously-inserted lines.
  for path, i in sorted(to_insert.items(), key=lambda x: x[1], reverse=True):
    lines.insert(i, f'#include "{path}"')

  for i in range(len(modified_lines)):
    modified_lines[i] += len(to_insert)

  return clang_format('\n'.join(lines), modified_lines)
Disable a GTest test within the given file.

  Args:
    test_name: The name of the test, in the form TestSuite.TestName
    lines: The existing file, split into lines. Note that each line ends with a
      newline character.
    new_cond: The additional conditions under which to disable the test. These
      will be merged with any existing conditions.

  Returns:
    The new contents to write into the file, with the test disabled.
  
Disable a GTest test within the given file.
Disable a GTest test within the given file .
def disabler(full_test_name: str, source_file: str, new_cond: Condition) -> str:
  lines = source_file.split('\n')
  test_name = full_test_name.split('.')[1]
  disabled = 'DISABLED_' + test_name
  maybe = 'MAYBE_' + test_name
  current_name = None
  src_range = None
  for i in range(len(lines) - 1, -1, -1):
    line = lines[i]
    idents = find_identifiers(line)
    if maybe in idents:
      existing_cond, src_range = find_conditions(lines, i, test_name)
      current_name = maybe
      break
    if disabled in idents:
      existing_cond = conditions.ALWAYS
      current_name = disabled
      break
    if test_name in idents:
      existing_cond = conditions.NEVER
      current_name = test_name
      break
  else:
    raise Exception(f"Couldn't find test definition for {full_test_name}")
  test_name_index = i
  merged = conditions.merge(existing_cond, new_cond)
  if isinstance(merged, conditions.BaseCondition):
    if merged == conditions.ALWAYS:
      replacement_name = disabled
    elif merged == conditions.NEVER:
      replacement_name = test_name
    lines[test_name_index] = \
        lines[test_name_index].replace(current_name, replacement_name)
    modified_line = test_name_index
    if src_range:
      del lines[src_range[0]:src_range[1] + 1]
      modified_line -= src_range[1] - src_range[0] + 1
    return clang_format('\n'.join(lines), [modified_line])
  lines[test_name_index] = lines[test_name_index].replace(current_name, maybe)
  condition_impl = cc_format_condition(merged)
  condition_block = [
      f'#if {condition_impl}',
      f'#define {maybe} {disabled}',
      '#else',
      f'#define {maybe} {test_name}',
      '#endif',
  ]
  modified_lines = []
  if src_range:
    lines[src_range[0]:src_range[1] + 1] = condition_block
    modified_lines = list(
        range(src_range[0], src_range[0] + len(condition_block)))
  else:
    for i in range(test_name_index, -1, -1):
      if any(test_macro in lines[i] for test_macro in TEST_MACROS):
        break
    else:
      raise Exception("Couldn't find where to insert test conditions")
    lines[i:i] = condition_block
    modified_lines = list(range(i, i + len(condition_block)))
  necessary_includes = {
      include
      for var in conditions.find_terminals(merged)
      if (include := var.gtest_info.header) is not None
  }
  to_insert: Dict[str, int] = {}
  last_include = None
  if len(necessary_includes) > 0:
    prev_path = ''
    i = 0
    while i < len(lines):
      match = get_directive(lines, i)
      i += 1
      if match is None:
        continue
      name, args = match
      if name != 'include':
        continue
      last_include = i
      path = args[0].strip('<>"')
      try:
        necessary_includes.remove(path)
      except KeyError:
        pass
      to_insert.pop(path, None)
      for include in necessary_includes:
        if prev_path < include < path:
          to_insert[include] = i
          necessary_includes.remove(include)
          prev_path = include
          i -= 1
          break
  if last_include is None:
    last_include = 0
  for include in necessary_includes:
    assert last_include is not None
    to_insert[include] = last_include
  for path, i in sorted(to_insert.items(), key=lambda x: x[1], reverse=True):
    lines.insert(i, f'#include "{path}"')
  for i in range(len(modified_lines)):
    modified_lines[i] += len(to_insert)
  return clang_format('\n'.join(lines), modified_lines)
def disabler ( full_test_name : str , source_file : str , new_cond : Condition ) -> str : lines = source_file . split ( '\n' ) test_name = full_test_name . split ( '.' ) [ 1 ] disabled = 'DISABLED_' + test_name maybe = 'MAYBE_' + test_name current_name = None src_range = None for i in range ( len ( lines ) - 1 , - 1 , - 1 ) : line = lines [ i ] idents = find_identifiers ( line ) if maybe in idents : existing_cond , src_range = find_conditions ( lines , i , test_name ) current_name = maybe break if disabled in idents : existing_cond = conditions . ALWAYS current_name = disabled break if test_name in idents : existing_cond = conditions . NEVER current_name = test_name break else : raise Exception ( f"Couldn't find test definition for {full_test_name}" ) test_name_index = i merged = conditions . merge ( existing_cond , new_cond ) if isinstance ( merged , conditions . BaseCondition ) : if merged == conditions . ALWAYS : replacement_name = disabled elif merged == conditions . NEVER : replacement_name = test_name lines [ test_name_index ] = lines [ test_name_index ] . replace ( current_name , replacement_name ) modified_line = test_name_index if src_range : del lines [ src_range [ 0 ] : src_range [ 1 ] + 1 ] modified_line -= src_range [ 1 ] - src_range [ 0 ] + 1 return clang_format ( '\n' . join ( lines ) , [ modified_line ] ) lines [ test_name_index ] = lines [ test_name_index ] . replace ( current_name , maybe ) condition_impl = cc_format_condition ( merged ) condition_block = [ f'#if {condition_impl}' , f'#define {maybe} {disabled}' , '#else' , f'#define {maybe} {test_name}' , '#endif' , ] modified_lines = [ ] if src_range : lines [ src_range [ 0 ] : src_range [ 1 ] + 1 ] = condition_block modified_lines = list ( range ( src_range [ 0 ] , src_range [ 0 ] + len ( condition_block ) ) ) else : for i in range ( test_name_index , - 1 , - 1 ) : if any ( test_macro in lines [ i ] for test_macro in TEST_MACROS ) : break else : raise Exception ( "Couldn't find where to insert test conditions" ) lines [ i : i ] = condition_block modified_lines = list ( range ( i , i + len ( condition_block ) ) ) necessary_includes = { include for var in conditions . find_terminals ( merged ) if ( include := var . gtest_info . header ) is not None } to_insert : Dict [ str , int ] = { } last_include = None if len ( necessary_includes ) > 0 : prev_path = '' i = 0 while i < len ( lines ) : match = get_directive ( lines , i ) i += 1 if match is None : continue name , args = match if name != 'include' : continue last_include = i path = args [ 0 ] . strip ( '<>"' ) try : necessary_includes . remove ( path ) except KeyError : pass to_insert . pop ( path , None ) for include in necessary_includes : if prev_path < include < path : to_insert [ include ] = i necessary_includes . remove ( include ) prev_path = include i -= 1 break if last_include is None : last_include = 0 for include in necessary_includes : assert last_include is not None to_insert [ include ] = last_include for path , i in sorted ( to_insert . items ( ) , key = lambda x : x [ 1 ] , reverse = True ) : lines . insert ( i , f'#include "{path}"' ) for i in range ( len ( modified_lines ) ) : modified_lines [ i ] += len ( to_insert ) return clang_format ( '\n' . join ( lines ) , modified_lines )
Disable a GTest test within the given file.
Disable a GTest test within the given file .
"""Disable a GTest test within the given file.

  Args:
    test_name: The name of the test, in the form TestSuite.TestName
    lines: The existing file, split into lines. Note that each line ends with a
      newline character.
    new_cond: The additional conditions under which to disable the test. These
      will be merged with any existing conditions.

  Returns:
    The new contents to write into the file, with the test disabled.
  """ # Search backwards from the end of the file, looking for the given test. # TODO: We should do more to avoid false positives. These could be caused by # the test name being mentioned in a comment or string, or test names that are # substrings of the given test name. # TODO: This will incorrectly match test names within comments. # If the test is already conditionally disabled, search backwards to find # the preprocessor conditional block that disables it, and parse out the # conditions. # If it's not conditionally disabled, we don't need a pre-processor block to # conditionally define the name. We just change the name within the test macro # to its appropriate value, and delete any existing preprocessor block. # => now conditionally disabled # Replace the existing condition, if there was one # If not, find where to add a new one. # Insert includes. # First find the set of headers we need for the given condition. # Then scan through the existing set of headers, finding which are already # included, and where to insert the #includes for those that aren't. # Track the path of the previous include, so we can find where to insert it # alphabetically. # Strip both forms of delimiter around the include path. # If this include path exactly matches one we need, then it's already # included and we don't need to add it. We remove it from both # necessary_includes, to not consider it, and from to_insert, in case we # already found a place to insert it. # Optimistically assume the includes are in sorted order, and try to # find a spot where we can insert each one. # This should never really happen outside of our tests, as the file will # need to at least include the GTest headers. # Deal with any includes that we couldn't find a spot for by putting them at # the end of the list of includes. # Finally, insert all the includes in the positions we decided on. Do so from # higher to lower indices, so we don't need to adjust later positions to # account for previously-inserted lines.
The new contents to write into the file, with the test disabled.
The new contents to write into the file with the test disabled .
full_test_name
str
source_file
str
new_cond
Condition
The additional conditions under which to disable the test. These
will be merged with any existing conditions.
The additional conditions under which to disable the test . These will be merged with any existing conditions .
test_name
The name of the test, in the form TestSuite.TestName
The name of the test in the form TestSuite . TestName
lines
The existing file, split into lines. Note that each line ends with a
newline character.
The existing file split into lines . Note that each line ends with a newline character .