# Copyright 2013 The Flutter 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/compiled_action.gni")
import("//third_party/dart/build/dart/dart_action.gni")
import("$flutter_root/common/config.gni")

is_aot_test =
    flutter_runtime_mode == "profile" || flutter_runtime_mode == "release"

# Creates a translation unit that defines the flutter::testing::GetFixturesPath
# method that tests can use to locate their fixtures.
#
# Arguments
#     assets_dir (required): The assets directory
template("fixtures_location") {
  testonly = true

  assert(defined(invoker.assets_dir), "The assets directory.")

  location_path = rebase_path(invoker.assets_dir)
  location_source = "namespace flutter {namespace testing {const char* GetFixturesPath() {return \"$location_path\";}}}"
  location_source_path = "$target_gen_dir/fixtures_location$target_name.cc"

  write_file(location_source_path, location_source)

  source_set(target_name) {
    public = []
    sources = [ location_source_path ]
  }
}

# Generates the Dart kernel snapshot.
#
# Arguments
#     dart_main (required): The Main Dart file.
#
#     dart_kernel (required): The path to the ouput kernel snapshot in the out
#                             directory.
template("dart_snapshot_kernel") {
  testonly = true

  assert(defined(invoker.dart_main), "The Dart Main file must be specified")
  assert(defined(invoker.dart_kernel),
         "The Dart Kernel file location must be specified")

  dart_action(target_name) {
    testonly = true

    deps = [
      # This generates the Frontend server snapshot used in this Dart action as
      # well as the patched SDK.
      "//third_party/dart/utils/kernel-service:frontend_server",
    ]

    if (is_aot_test) {
      deps += [ "$flutter_root/lib/snapshot:strong_platform" ]
    }

    script = "$root_out_dir/frontend_server.dart.snapshot"

    inputs = [ invoker.dart_main ]

    outputs = [ invoker.dart_kernel ]

    snapshot_depfile = "$target_gen_dir/snapshot_$target_name.depfile.d"
    depfile = snapshot_depfile

    args = [
      "--sdk-root",
      rebase_path("$root_out_dir/flutter_patched_sdk"),
      "--target",
      "flutter",
      "--output-dill",
      rebase_path(invoker.dart_kernel, root_out_dir),
      "--depfile",
      rebase_path(snapshot_depfile),
    ]

    if (flutter_runtime_mode == "release") {
      args += [ "-Ddart.vm.product=true" ]
    }

    if (is_aot_test) {
      args += [
        "--aot",

        # type flow analysis
        "--tfa",
      ]
    }

    args += [ rebase_path(invoker.dart_main) ]
  }
}

# Generates an AOT snapshot from a kernel snapshot.
#
# Arguments:
#
#     dart_kernel (required): The path to the kernel snapshot.
template("dart_snapshot_aot") {
  testonly = true

  assert(defined(invoker.dart_kernel),
         "The Dart Kernel file location must be specified")

  compiled_action(target_name) {
    testonly = true

    tool = "//third_party/dart/runtime/bin:gen_snapshot"

    inputs = [ invoker.dart_kernel ]

    vm_snapshot_data = "$target_gen_dir/assets/vm_snapshot_data"
    vm_snapshot_instr = "$target_gen_dir/assets/vm_snapshot_instr"
    isolate_snapshot_data = "$target_gen_dir/assets/isolate_snapshot_data"
    isolate_snapshot_instr = "$target_gen_dir/assets/isolate_snapshot_instr"

    outputs = [
      vm_snapshot_data,
      vm_snapshot_instr,
      isolate_snapshot_data,
      isolate_snapshot_instr,
    ]

    args = [
      "--causal_async_stacks",
      "--deterministic",
      "--snapshot_kind=app-aot-blobs",
      "--vm_snapshot_data=" + rebase_path(vm_snapshot_data),
      "--vm_snapshot_instructions=" + rebase_path(vm_snapshot_instr),
      "--isolate_snapshot_data=" + rebase_path(isolate_snapshot_data),
      "--isolate_snapshot_instructions=" + rebase_path(isolate_snapshot_instr),
      rebase_path(invoker.dart_kernel),
    ]

    forward_variables_from(invoker, [ "deps" ])
  }
}

# Generates a kernel or AOT snapshot as necessary from the main Dart file.
# Other Dart dependencies referenced by that main Dart file will be tracked.
#
# Arguments:
#
#     dart_main (required): The path to the main Dart file.
template("dart_snapshot") {
  assert(defined(invoker.dart_main), "The main Dart file must be specified.")

  testonly = true

  dart_snapshot_kernel_target_name = "dart_snapshot_kernel_$target_name"
  dart_snapshot_kernel_path = "$target_gen_dir/assets/kernel_blob.bin"
  dart_snapshot_kernel(dart_snapshot_kernel_target_name) {
    dart_main = invoker.dart_main
    dart_kernel = dart_snapshot_kernel_path
  }

  snapshot_deps = [ ":$dart_snapshot_kernel_target_name" ]

  if (is_aot_test) {
    dart_snapshot_aot_target_name = "dart_snapshot_aot_$target_name"
    dart_snapshot_aot(dart_snapshot_aot_target_name) {
      dart_kernel = dart_snapshot_kernel_path
      deps = [ ":$dart_snapshot_kernel_target_name" ]
    }
    snapshot_deps += [ ":$dart_snapshot_aot_target_name" ]
  }

  group(target_name) {
    testonly = true

    deps = snapshot_deps
  }
}

# Copies a (potentially empty) list of fixtures to the fixtures directory for
# the unit test.
#
# Arguments:
#
#     fixtures (required): The list of fixtures to copy.
template("copy_fixtures") {
  testonly = true

  assert(defined(invoker.fixtures), "The test fixtures must be specified.")

  has_fixtures = false
  foreach(fixture, invoker.fixtures) {
    has_fixtures = true
  }

  if (has_fixtures) {
    copy(target_name) {
      sources = invoker.fixtures
      outputs = [ "$target_gen_dir/assets/{{source_file_part}}" ]
    }
  } else {
    group(target_name) {
      # The copy target cannot accept an empty list.
    }
  }
}

# Specifies the fixtures to copy to a location known by the specific unit test.
# Test executable can only depend on one such target. You can use either one of
# both arguments to expand this template. If you have none, then you'll see the
# unused invoker scope error. In such cases specify the fixtures using an empty
# array.
#
# Arguments:
#
#     fixtures (optional): The list of test fixtures. An empty list may be
#                          specified.
#
#     dart_main (optional): The path to the main Dart file. If specified, it is
#                           snapshotted.
template("test_fixtures") {
  # Even if no fixtures are present, the location of the fixtures directory
  # must always be known to tests.
  fixtures_location_target_name = "fixtures_location_$target_name"
  fixtures_location(fixtures_location_target_name) {
    assets_dir = "$target_gen_dir/assets"
  }
  test_deps = [ ":$fixtures_location_target_name" ]

  # If the fixtures are specified, copy them to the assets directory.
  if (defined(invoker.fixtures)) {
    copy_fixtures_target_name = "copy_fixtures_$target_name"
    copy_fixtures(copy_fixtures_target_name) {
      fixtures = invoker.fixtures
    }
    test_deps += [ ":$copy_fixtures_target_name" ]
  }

  # If a Dart file is specified, snapshot it and place it in the generated
  # assets directory.
  if (defined(invoker.dart_main)) {
    dart_snapshot_target_name = "dart_snapshot_$target_name"
    dart_snapshot(dart_snapshot_target_name) {
      dart_main = invoker.dart_main
    }
    test_deps += [ ":$dart_snapshot_target_name" ]
  }

  group(target_name) {
    testonly = true
    deps = test_deps
  }
}