910e62b5创建于 1月15日历史提交
# Copyright 2018 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import("//build/config/chrome_build.gni")
import("//build/config/chromeos/args.gni")
import("//build/config/dcheck_always_on.gni")
import("//build/config/gclient_args.gni")
import("//build/config/python.gni")
import("//build/util/generate_wrapper.gni")

assert(is_chromeos)
assert(is_chromeos_device)

# Determine the real paths for various items in the SDK, which may be used
# in the 'generate_runner_script' template below. We do so outside the template
# to confine exec_script to a single invocation.
if (cros_sdk_version != "") {
  # Ideally these should be maps, however, gn doesn't support map, so using a
  # list of list to simulate a map:
  # [key1, [value1, value2, ...]], [key2, [value1, value2, ...]], where
  # the keys are boards and values are symlinks or symlink targets, and the
  # mapping shouldn't be used for anything else.
  #
  # A sample usage is:
  # foreach(m, _symlink_targets_map) {
  #   if(m[0] == target_key) {
  #     target_value = m[1]
  #   }
  # }
  #
  _symlink_map = []
  _symlink_targets_map = []
  _potential_test_boards = [ cros_board ]

  foreach(b, _potential_test_boards) {
    _cache_path_prefix =
        "//build/cros_cache/chrome-sdk/symlinks/${b}+${cros_sdk_version}"

    _cros_is_vm = false
    foreach(b1, string_split(cros_boards_with_qemu_images, ":")) {
      if (b == b1) {
        _cros_is_vm = true
      }
    }

    _symlinks = []
    _symlinks = [
      # Tast harness & test data.
      rebase_path("${_cache_path_prefix}+autotest_server_package.tar.bz2"),

      # Binutils (and other toolchain tools) used to deploy Chrome to the device.
      rebase_path(
          "${_cache_path_prefix}+environment_chromeos-base_chromeos-chrome.tar.xz"),
      rebase_path("${_cache_path_prefix}+target_toolchain"),
    ]
    if (_cros_is_vm) {
      # VM-related tools.
      _symlinks +=
          [ rebase_path("${_cache_path_prefix}+chromiumos_test_image.tar.xz") ]
    }
    _symlink_map += [ [
          b,
          _symlinks,
        ] ]
  }

  _all_symlinks = []
  foreach(m, _symlink_map) {
    _all_symlinks += m[1]
  }
  _all_symlink_targets =
      exec_script("//build/get_symlink_targets.py", _all_symlinks, "list lines")
  _index = 0
  foreach(m, _symlink_map) {
    _symlink_targets = []
    foreach(_, m[1]) {
      _symlink_targets += [ _all_symlink_targets[_index] ]
      _index += 1
    }

    _symlink_targets_map += [ [
          m[0],
          _symlink_targets,
        ] ]
  }
}

template("generate_chromeos_sdk_deps") {
  forward_variables_from(invoker,
                         [
                           "deploy_chrome",
                           "is_tast",
                         ])
  if (!defined(deploy_chrome)) {
    deploy_chrome = false
  }
  if (!defined(is_tast)) {
    is_tast = false
  }

  _sdk_data = []
  assert(cros_sdk_version != "", "cros sdk version is not defined")
  foreach(b, _potential_test_boards) {
    _cros_is_vm = false
    foreach(b1, string_split(cros_boards_with_qemu_images, ":")) {
      if (b == b1) {
        _cros_is_vm = true
      }
    }

    # Determine the real paths for various items in the SDK, which may be used
    # in the 'generate_runner_script' template below.
    if (is_tast || _cros_is_vm || deploy_chrome) {
      _symlink_targets = []
      foreach(m, _symlink_targets_map) {
        if (b == m[0]) {
          _symlink_targets = []
          _symlink_targets = m[1]
        }
      }

      if (is_tast) {
        # Add tast sdk items.
        _sdk_data += [ _symlink_targets[0] ]
      }
      if (deploy_chrome) {
        # To deploy chrome to the VM, it needs to be stripped down to fit into
        # the VM. This is done by using binutils in the toolchain. So add the
        # toolchain to the data.
        _sdk_data += [
          _symlink_targets[1],
          _symlink_targets[2],
        ]
      }
      if (_cros_is_vm) {
        # Add vm sdk items.
        _sdk_data += [ _symlink_targets[3] ]
      }
    }
  }
  group(target_name) {
    data = _sdk_data
    data += [
      # Needed for various SDK components used below.
      "//build/cros_cache/chrome-sdk/misc/",
      "//build/cros_cache/chrome-sdk/symlinks/",
      "//build/cros_cache/common/",
      "//chrome/VERSION",

      # The LKGM file controls what version of the VM image to download. Add it
      # as data here so that changes to it will trigger analyze.
      "//chromeos/CHROMEOS_LKGM",
    ]
    if (cros_boards_with_qemu_images != "") {
      data += [ "//build/cros_cache/cipd/" ]
    }
  }
}

# Creates tast filter files for skylab tast tests.
# Args:
#   tast_attr_expr: Tast expression to determine tests to run. This creates the
#       initial set of tests that can be further filtered..
#   tast_tests: Names of tests to enable in tast. All other tests will be
#       disabled that are not listed.
#   tast_disabled_tests: Names of tests to disable in tast. All other tests that
#       match the tast expression will still run.
#   tast_control: gni file with collections of tests to be used for specific
#       filters (e.g. "//chromeos/tast_control.gni"). Any lists of strings in
#       this file will be used to generate additional tast expressions with
#       those strings expanded into tests to disable (i.e. as && !"name:test").
#       The name of those lists are then intended to be used to specify in
#       test_suites.pyl which collection to be used on specific test suites.
template("generate_skylab_tast_filter") {
  forward_variables_from(invoker,
                         [
                           "tast_attr_expr",
                           "tast_tests",
                           "tast_disabled_tests",
                           "tast_control",
                         ])

  if (defined(tast_disabled_tests)) {
    assert(defined(tast_attr_expr),
           "tast_attr_expr must be used when specifying tast_disabled_tests.")
  }
  _generated_filter = "$root_build_dir/bin/${target_name}.filter"
  _skylab_args = [
    "generate-filter",
    "--output",
    rebase_path(_generated_filter),
  ]
  if (defined(tast_control)) {
    _skylab_args += [
      "--tast-control",
      rebase_path(tast_control),
    ]
  }
  if (defined(tast_attr_expr)) {
    _skylab_args += [
      "--tast-expr",
      tast_attr_expr,
    ]
  }
  if (defined(tast_tests)) {
    foreach(_test, tast_tests) {
      _skylab_args += [
        "--enabled-tests",
        _test,
      ]
    }
  }
  if (defined(tast_disabled_tests)) {
    foreach(_test_name, tast_disabled_tests) {
      _skylab_args += [
        "--disabled-tests",
        _test_name,
      ]
    }
  }
  action(target_name) {
    testonly = true
    script = "//build/chromeos/generate_skylab_tast_filter.py"
    if (defined(tast_control)) {
      sources = [ tast_control ]
    }
    outputs = [ _generated_filter ]
    args = _skylab_args
    if (defined(invoker.data_deps)) {
      data_deps = invoker.data_deps
    }
    data = [ _generated_filter ]
    if (defined(invoker.data)) {
      data += invoker.data
    }
    if (defined(invoker.deps)) {
      deps = invoker.deps
    }
  }
}

# Creates a script at $generated_script that can be used to launch a cros VM
# and optionally run a test within it.
# Args:
#   test_exe: Name of test binary located in the out dir. This will get copied
#       to the VM and executed there.
#   tast_attr_expr: Tast expression to pass to local_test_runner on the VM.
#   tast_tests: List of Tast tests to run on the VM. Note that when this is
#       specified, the target name used to invoke this template will be
#       designated as the "name" of this test and will primarly used for test
#       results tracking and displaying (eg: flakiness dashboard).
#   generated_script: Path to place the generated script.
#   deploy_chrome: If true, deploys a locally built chrome located in the root
#       build dir to the VM or DUT after launching it.
#   runtime_deps_file: Path to file listing runtime deps for the test. If set,
#       all files listed will be copied to the VM before testing.
#   skip_generating_board_args: By default, this template generates an '--board'
#       arg with corresponding '--flash' or '--use-vm' args for device and vm
#       respectively. This argument instructs the template to skip generating
#       them, and it's designed for use cases where one builds for one board
#       (e.g. amd64-generic), but tests on a different board (e.g. eve).
#   tast_vars: A list of "key=value" runtime variable pairs to pass to invoke
#   strip_chrome: If true, strips Chrome before deploying it for non-Tast tests.
#       the Tast tests. For more details, please see:
#       https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#Runtime-variables
template("generate_runner_script") {
  forward_variables_from(invoker,
                         [
                           "deploy_chrome",
                           "generated_script",
                           "runtime_deps_file",
                           "skip_generating_board_args",
                           "strip_chrome",
                           "tast_attr_expr",
                           "tast_tests",
                           "tast_vars",
                           "testonly",
                           "test_exe",
                         ])

  if (!defined(skip_generating_board_args)) {
    # --board should be assigned by the autotest wrapper on skylab builders
    skip_generating_board_args = is_skylab
  }

  if (skip_generating_board_args) {
    # cros_board is not needed, so setting it to empty to avoid being used
    # accidentally below.
    cros_board = ""
    not_needed([ cros_board ])
  }

  if (!defined(deploy_chrome)) {
    deploy_chrome = false
  }
  if (!defined(strip_chrome)) {
    strip_chrome = false
  }
  is_tast = defined(tast_attr_expr) || defined(tast_tests)
  assert(!(is_tast && defined(test_exe)),
         "Tast tests are invoked from binaries shipped with the VM image. " +
             "There should be no locally built binary needed.")
  assert(is_tast || !defined(tast_vars),
         "tast_vars is only support for Tast tests")

  if (is_tast) {
    not_needed([ "strip_chrome" ])
  }

  # If we're in the cros chrome-sdk (and not the raw ebuild), the test will
  # need some additional runtime data located in the SDK cache.
  if (cros_sdk_version != "") {
    assert(defined(generated_script),
           "Must specify where to place generated test launcher script via " +
               "'generated_script'")

    generate_chromeos_sdk_deps(target_name + "_cros_deps__helper") {
      is_tast = is_tast
      deploy_chrome = deploy_chrome
    }
  }

  generate_wrapper(target_name) {
    executable = "//build/chromeos/test_runner.py"
    wrapper_script = generated_script
    executable_args = []

    if (defined(runtime_deps_file)) {
      write_runtime_deps = runtime_deps_file
    }

    # Build executable_args for the three different test types: GTest, Tast,
    # and host-side commands (eg telemetry).
    if (defined(test_exe)) {
      executable_args += [
        "gtest",
        "--test-exe",
        test_exe,
      ]

      # This target is not a gtest unit test, but an integration test suite.
      # Similar to interactive ui tests, it would start a full browser and
      # then do testing.
      # See more at //docs/testing/chromeos_integration.
      if (test_exe == "chromeos_integration_tests") {
        # Run the test sudo helper.
        executable_args += [ "--run-test-sudo-helper" ]

        # It need to first stop the existing chromeos-chrome.
        executable_args += [ "--stop-ui" ]

        # Use the deployed dbus configs.
        executable_args += [ "--use-deployed-dbus-configs" ]

        # Make the tests match the browser's selinux tags so it gets the same
        # security context as the browser would.
        executable_args += [ "--set-selinux-label=chromeos_integration_tests=u:object_r:chrome_browser_exec:s0" ]
      }
      if (defined(runtime_deps_file)) {
        executable_args += [
          "--runtime-deps-path",
          rebase_path(runtime_deps_file, root_build_dir),
        ]
      }
    } else if (is_tast) {
      # When --tast-tests is specified, test_runner.py will call
      # local_test_runner on the VM to run the set of tests.
      executable_args += [
        "tast",
        "--suite-name",
        target_name,
      ]
      if (defined(tast_attr_expr)) {
        executable_args += [
          "--attr-expr",
          tast_attr_expr,
        ]
      } else {
        foreach(test, tast_tests) {
          executable_args += [
            "-t",
            test,
          ]
        }
      }
      if (defined(tast_vars)) {
        foreach(var, tast_vars) {
          executable_args += [
            "--tast-var",
            var,
          ]
        }
      }
      if (dcheck_always_on) {
        executable_args += [
          "--tast-extra-use-flags",
          "chrome_dcheck",
        ]
      }
    } else {
      executable_args += [ "host-cmd" ]
    }
    executable_args += [
      "--cros-cache",
      "build/cros_cache/",
      "--path-to-outdir",
      rebase_path(root_out_dir, "//"),
      "-v",
    ]

    if (!is_tast && strip_chrome) {
      executable_args += [ "--strip-chrome" ]
    }

    if (!skip_generating_board_args) {
      executable_args += [
        "--board",
        cros_board,
      ]

      _cros_is_vm = false
      foreach(b, string_split(cros_boards_with_qemu_images, ":")) {
        if (cros_board == b) {
          _cros_is_vm = true
        }
      }
      if (_cros_is_vm) {
        executable_args += [ "--use-vm" ]
      } else {
        executable_args += [ "--flash" ]
      }
    }

    # If we have public Chromium builds, use public Chromium OS images when
    # flashing the test device.
    if (!is_chrome_branded) {
      executable_args += [ "--public-image" ]
    }

    if (deploy_chrome && !defined(test_exe)) {
      executable_args += [ "--deploy-chrome" ]
    }

    # executable_args should be finished, now build the data and deps lists.
    deps = [ "//testing/buildbot/filters:chromeos_filters" ]
    if (defined(invoker.deps)) {
      deps += invoker.deps
    }
    data = [
      "//.vpython3",

      # We use android test-runner's results libs to construct gtest output
      # json.
      "//build/android/pylib/__init__.py",
      "//build/android/pylib/base/",
      "//build/android/pylib/results/",
      "//build/chromeos/",
      "//build/util/",
      "//third_party/chromite/",
    ]

    if (defined(invoker.data)) {
      data += invoker.data
    }

    data_deps = [ "//testing:test_scripts_shared" ]
    if (cros_sdk_version != "") {
      data_deps += [ ":" + target_name + "_cros_deps__helper" ]
    }
    if (defined(invoker.data_deps)) {
      data_deps += invoker.data_deps
    }
  }
}

# Handy template to define a generated_script test prepended with the
# cros_test_wrapper, which will automatically handle spinning up a CrOS VM
# and/or deploying the browser to the DUT prior to test execution.
template("cros_test_wrapper_script_test") {
  generate_wrapper("${target_name}") {
    forward_variables_from(invoker,
                           "*",
                           [
                             "args",
                             "data_deps",
                           ])
    executable = "$root_out_dir/bin/cros_test_wrapper"
    wrapper_script = "$root_out_dir/bin/run_${target_name}"
    testonly = true

    executable_args = []
    if (defined(invoker.args)) {
      executable_args += invoker.args
    }

    data_deps = [ "//chromeos:cros_test_wrapper" ]
    if (defined(invoker.data_deps)) {
      data_deps += invoker.data_deps
    }
  }
}

template("tast_test") {
  forward_variables_from(invoker, "*")

  # Default the expression to match any chrome-related test.
  if (!defined(tast_attr_expr) && !defined(tast_tests)) {
    # The following expression filters out all non-critical tests. See the link
    # below for more details:
    # https://chromium.googlesource.com/chromiumos/platform/tast/+/main/docs/test_attributes.md
    tast_attr_expr = "\"group:mainline\" && \"dep:chrome\""

    if (defined(enable_tast_informational_tests) &&
        enable_tast_informational_tests) {
      tast_attr_expr += " && informational"
    } else {
      tast_attr_expr += " && !informational"
    }
    if (!is_chrome_branded) {
      tast_attr_expr += " && !\"dep:chrome_internal\""
    }
  } else {
    assert(defined(tast_attr_expr) != defined(tast_tests),
           "Specify one of tast_tests or tast_attr_expr.")
  }

  _data_deps = [
    "//:chromiumos_preflight",  # Builds the browser.
    "//chromeos:cros_chrome_deploy",  # Adds additional browser run-time deps.

    # Tools used to symbolize Chrome crash dumps.
    # TODO(crbug.com/40160552): Remove these if/when all tests pick them up by
    # default.
    "//third_party/breakpad:dump_syms",
    "//third_party/breakpad:minidump_dump",
    "//third_party/breakpad:minidump_stackwalk",
  ]
  _data = [ "//components/crash/content/tools/generate_breakpad_symbols.py" ]

  if (is_skylab) {
    generate_skylab_tast_filter(target_name) {
      # chromite.deploy_chrome is needed for sideloading Chrome.
      data = _data + [ "//third_party/chromite/" ]
      data_deps = _data_deps

      # To disable a test on specific milestones, add it to the appropriate
      # collection in the following file
      tast_control = "//chromeos/tast_control.gni"
    }
  } else {
    # Append any disabled tests to the expression.
    if (defined(tast_disabled_tests)) {
      assert(defined(tast_attr_expr),
             "tast_attr_expr must be used when specifying tast_disabled_tests.")
      foreach(_test_name, tast_disabled_tests) {
        tast_attr_expr += " && !\"name:${_test_name}\""
      }
    }
    if (defined(tast_attr_expr)) {
      tast_attr_expr = "( " + tast_attr_expr + " )"
    }
    generate_runner_script(target_name) {
      testonly = true
      generated_script = "$root_build_dir/bin/run_${target_name}"
      runtime_deps_file = "$root_out_dir/${target_name}.runtime_deps"
      deploy_chrome = true
      data_deps = _data_deps
      data = _data
    }
  }
}