| # Copyright 2018 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import("//build/testing/platforms.gni") |
| |
| # Extension identifying a test spec JSON file. |
| _test_spec_ext = "spec.json" |
| |
| # Extension identifying the runtime dependencies of a test. |
| test_data_ext = "spec.data" |
| |
| # Describes the target device environment in which a test should run. This |
| # specification is written in JSON to the build directory, to be aggregated |
| # at test-time. |
| # |
| # Parameters |
| # |
| # name (required) |
| # [string] The test's name. |
| # |
| # location (required) |
| # [string]: Unique reference to a test, e.g., a filesystem path or a |
| # fuchsia URI. |
| # |
| # output_dir (required for fuchsia; else is optional) |
| # [string]: Directory where the test spec will be written. |
| # |
| # environments (optional, default: QEMU for fuchsia; else a VM) |
| # [list of scopes] Device environments in which the test should run. |
| # |
| # Each scope in $environments contains: |
| # |
| # dimensions (required) |
| # [scope] Dimensions of bots to target. Valid dimensions are |
| # element-wise subsets of the test platform entries defined in |
| # //build/testing/platforms.gni. |
| # |
| # label (optional) |
| # [string] A key on which tests may be grouped. Tests with a given |
| # label will be run (1) together, and (2) only with support from the |
| # Infrastructure team. Labels are used as an escape hatch from the |
| # default testing pipeline for special tests or environments. |
| # |
| template("test_spec") { |
| # A canonical target name is the name of the test itself; however that name |
| # must be reserved for the actual test target in the expansion of other |
| # templates that give both a test and its spec. With that in mind, |
| # `test_name` is provided as a parameter, and the choice of an alternative |
| # target name does not ultimately matter. |
| not_needed([ "target_name" ]) |
| |
| assert(defined(invoker.name), "name must be defined.") |
| assert(defined(invoker.location), "location must be defined.") |
| forward_variables_from(invoker, |
| [ |
| "environments", |
| "output_dir", |
| ]) |
| |
| # Set default environments: QEMU for a fuchsia test, and VMs for linux and mac |
| # tests. |
| if (is_fuchsia) { |
| assert(defined(invoker.output_dir), "output_dir must be defined.") |
| envs_specified = defined(environments) |
| |
| if (!envs_specified) { |
| environments = [ |
| { |
| dimensions = { |
| device_type = "QEMU" |
| } |
| }, |
| ] |
| } |
| |
| if (current_cpu == "x64" && !envs_specified) { |
| # TODO(joshuaseaton): This is temporary until we are confident that all |
| # tests that need to specify hardware are doing so. |
| environments += [ |
| { |
| dimensions = { |
| device_type = "Intel NUC Kit NUC7i5DNHE" |
| } |
| }, |
| ] |
| } else if (current_cpu == "arm64" && !envs_specified) { |
| # TODO(IN-571): Remove after vim2s are ready to be targeted. |
| environments += [ |
| { |
| dimensions = { |
| device_type = "Khadas Vim2 Max" |
| } |
| label = "vim2" |
| }, |
| ] |
| } |
| } else if (is_linux || is_mac) { |
| if (defined(output_dir)) { |
| assert(output_dir == target_out_dir, |
| "tests specs must be written to target_out_dir") |
| } else { |
| output_dir = target_out_dir |
| } |
| |
| if (!defined(environments)) { |
| environments = [ |
| { |
| dimensions = { |
| if (is_linux) { |
| os = "Linux" # Used to target a Linux VM. |
| } else if (is_mac) { |
| os = "Mac" # Used to target a Mac VM. |
| } |
| } |
| |
| # TODO(IN-819) Use this label only for bring-up; delete afterward. |
| label = "host" |
| }, |
| ] |
| } |
| } else { |
| assert(false, "$current_os not supported") |
| } |
| |
| foreach(env, environments) { |
| empty_scope = { # Clear from previous iteration |
| } |
| assert(defined(env.dimensions) && env.dimensions != empty_scope, |
| "each environment must specify dimensions") |
| } |
| |
| # Call "expanding" the operation that takes a scope |
| # { |
| # x = a |
| # y = b |
| # z = c |
| # ... |
| # } |
| # and converts it to a list [{x=a}, {y=b}, {z=c},...]. |
| # |
| # Expand each scope of test platform dimensions and group them by architecture |
| # (i.e., cpu). The same thing is done below with each environment's dimensions |
| # scope to more easily compare. |
| target_platform_dims = [] |
| other_platform_dims = [] |
| foreach(platform, test_platforms) { |
| platform_dims = [] # Clear from previous iteration. |
| foreach(key, all_dimension_keys) { |
| platform_dims += [ |
| { |
| forward_variables_from(platform, [ key ]) |
| }, |
| ] |
| } |
| |
| # Empty scopes may have been introduced to platform_dims, corresponding to |
| # non-existent keys; |
| # Add and then subtract an empty scope to remove them. |
| empty_dim = { # Clear from previous iteration. |
| } |
| platform_dims += [ empty_dim ] |
| platform_dims -= [ empty_dim ] |
| |
| if (!defined(platform.cpu) || platform.cpu == current_cpu) { |
| target_platform_dims += [ platform_dims ] |
| } else { |
| other_platform_dims += [ platform_dims ] |
| } |
| } |
| |
| target_envs = [] |
| foreach(env, environments) { |
| dims = [] # Clear from previous iteration. |
| if (defined(env.dimensions)) { |
| foreach(key, all_dimension_keys) { |
| dims += [ |
| { |
| forward_variables_from(env.dimensions, [ key ]) |
| }, |
| ] |
| } |
| } |
| empty_dim = { # Clear from previous iteration. |
| } |
| dims += [ empty_dim ] |
| dims -= [ empty_dim ] |
| |
| # Check if the environment's dimensions match those of a platform of the |
| # target architecture; if a match, include the environment among the |
| # test spec's. |
| # Note that in GN "A is a subset of B" is equivalent to `A + B - B == []`. |
| match = false |
| foreach(platform_dims, target_platform_dims) { |
| if (dims + platform_dims - platform_dims == []) { |
| match = true |
| target_envs += [ env ] |
| } |
| } |
| |
| # If the environment's dimensions do not match a target architecture, ensure |
| # that they match those of a platform of another architecture. |
| if (!match) { |
| foreach(platform_dims, other_platform_dims) { |
| match = match || dims + platform_dims - platform_dims == [] |
| } |
| if (!match) { |
| print("Could not match environment specifications for '$target_name':") |
| print("$env") |
| assert( |
| match, |
| "Consult //build/testing/platforms.gni for all allowable specifications") |
| } |
| } |
| } |
| |
| test_spec = { |
| test = { |
| name = get_label_info(":${invoker.name}", "label_no_toolchain") |
| location = invoker.location |
| if (is_linux || is_mac) { |
| # Trim leading //. |
| location = rebase_path(location, root_build_dir) |
| } |
| os = current_os |
| cpu = current_cpu |
| } |
| environments = [] # Clear from above. |
| environments = target_envs |
| } |
| |
| foreach(env, target_envs) { |
| dims = [] # Clear from previous iteration. |
| dims = env.dimensions |
| |
| # TODO(IN-571): Delete this block once vim2s are ready to be targeted. |
| if (defined(dims.device_type) && dims.device_type == "Khadas Vim2 Max") { |
| assert(defined(env.label) && env.label == "vim2", |
| "vim2s may not yet be targeted.") |
| } |
| |
| # TODO(IN-819): Delete this block once Linux VMs and Macs are ready to be |
| # targeted. |
| if (defined(dims.os)) { |
| assert(defined(env.label) && env.label == "host", |
| "Linux VMs or Macs may not yet be targeted for tests.") |
| } |
| } |
| |
| # We take the basename just to make sure no other path components are given |
| # in the name, for which we have no guarantees. |
| test_name = get_path_info(invoker.name, "name") |
| write_file("${output_dir}/${test_name}.${_test_spec_ext}", test_spec, "json") |
| } |