#!/usr/bin/env python
#
# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Deploys and runs a test package on a Fuchsia target."""

import argparse
import json
import logging
import os
import socket
import subprocess
import sys
import tempfile
import time

from common_args import AddCommonArgs, ConfigureLogging, GetDeploymentTargetForArgs
from net_test_server import SetupTestServer
from run_package import RunPackage, RunPackageArgs

DEFAULT_TEST_SERVER_CONCURRENCY = 4

TEST_RESULT_PATH = '/data/test_summary.json'
TEST_FILTER_PATH = '/data/test_filter.txt'

def main():
  parser = argparse.ArgumentParser()
  AddCommonArgs(parser)

  parser.add_argument('--gtest_filter',
                      help='GTest filter to use in place of any default.')
  parser.add_argument('--gtest_repeat',
                      help='GTest repeat value to use. This also disables the '
                           'test launcher timeout.')
  parser.add_argument('--test-launcher-retry-limit',
                      help='Number of times that test suite will retry failing '
                           'tests. This is multiplicative with --gtest_repeat.')
  parser.add_argument(
      '--test-launcher-shard-index',
      type=int,
      default=os.environ.get('GTEST_SHARD_INDEX'),
      help='Index of this instance amongst swarming shards.')
  parser.add_argument(
      '--test-launcher-total-shards',
      type=int,
      default=os.environ.get('GTEST_TOTAL_SHARDS'),
      help='Total number of swarming shards of this suite.')
  parser.add_argument('--gtest_break_on_failure', action='store_true',
                      default=False,
                      help='Should GTest break on failure; useful with '
                           '--gtest_repeat.')
  parser.add_argument('--single-process-tests', action='store_true',
                      default=False,
                      help='Runs the tests and the launcher in the same '
                           'process. Useful for debugging.')
  parser.add_argument('--test-launcher-batch-limit',
                      type=int,
                      help='Sets the limit of test batch to run in a single '
                      'process.')
  # --test-launcher-filter-file is specified relative to --output-directory,
  # so specifying type=os.path.* will break it.
  parser.add_argument('--test-launcher-filter-file',
                      default=None,
                      help='Override default filter file passed to target test '
                      'process. Set an empty path to disable filtering.')
  parser.add_argument('--test-launcher-jobs',
                      type=int,
                      help='Sets the number of parallel test jobs.')
  parser.add_argument('--test-launcher-summary-output',
                      help='Where the test launcher will output its json.')
  parser.add_argument('--enable-test-server', action='store_true',
                      default=False,
                      help='Enable Chrome test server spawner.')
  parser.add_argument('--test-launcher-bot-mode', action='store_true',
                      default=False,
                      help='Informs the TestLauncher to that it should enable '
                      'special allowances for running on a test bot.')
  parser.add_argument('--child-arg', action='append',
                      help='Arguments for the test process.')
  parser.add_argument('child_args', nargs='*',
                      help='Arguments for the test process.')
  args = parser.parse_args()

  # Flag output_directory is required for tests launched with this script.
  if not args.output_directory:
    raise ValueError("output-directory must be specified.")

  ConfigureLogging(args)

  child_args = []
  if args.test_launcher_shard_index != None:
    child_args.append(
        '--test-launcher-shard-index=%d' % args.test_launcher_shard_index)
  if args.test_launcher_total_shards != None:
    child_args.append(
        '--test-launcher-total-shards=%d' % args.test_launcher_total_shards)
  if args.single_process_tests:
    child_args.append('--single-process-tests')
  if args.test_launcher_bot_mode:
    child_args.append('--test-launcher-bot-mode')
  if args.test_launcher_batch_limit:
    child_args.append('--test-launcher-batch-limit=%d' %
                       args.test_launcher_batch_limit)

  # Only set --test-launcher-jobs if the caller specifies it, in general.
  # If the caller enables the test-server then we need to launch the right
  # number of instances to match the maximum number of parallel test jobs, so
  # in that case we set --test-launcher-jobs based on the number of CPU cores
  # specified for the emulator to use.
  test_concurrency = None
  if args.test_launcher_jobs:
    test_concurrency = args.test_launcher_jobs
  elif args.enable_test_server:
    if args.device == 'device':
      test_concurrency = DEFAULT_TEST_SERVER_CONCURRENCY
    else:
      test_concurrency = args.qemu_cpu_cores
  if test_concurrency:
    child_args.append('--test-launcher-jobs=%d' % test_concurrency)

  if args.gtest_filter:
    child_args.append('--gtest_filter=' + args.gtest_filter)
  if args.gtest_repeat:
    child_args.append('--gtest_repeat=' + args.gtest_repeat)
    child_args.append('--test-launcher-timeout=-1')
  if args.test_launcher_retry_limit:
    child_args.append(
        '--test-launcher-retry-limit=' + args.test_launcher_retry_limit)
  if args.gtest_break_on_failure:
    child_args.append('--gtest_break_on_failure')
  if args.test_launcher_summary_output:
    child_args.append('--test-launcher-summary-output=' + TEST_RESULT_PATH)

  if args.child_arg:
    child_args.extend(args.child_arg)
  if args.child_args:
    child_args.extend(args.child_args)

  with GetDeploymentTargetForArgs(args) as target:
    target.Start()

    if args.test_launcher_filter_file:
      target.PutFile(args.test_launcher_filter_file, TEST_FILTER_PATH,
                     for_package=args.package_name)
      child_args.append('--test-launcher-filter-file=' + TEST_FILTER_PATH)

    test_server = None
    if args.enable_test_server:
      assert test_concurrency
      test_server = SetupTestServer(target, test_concurrency,
                                    args.package_name)

    run_package_args = RunPackageArgs.FromCommonArgs(args)
    returncode = RunPackage(
        args.output_directory, target, args.package, args.package_name,
        child_args, run_package_args)

    if test_server:
      test_server.Stop()

    if args.test_launcher_summary_output:
      target.GetFile(TEST_RESULT_PATH, args.test_launcher_summary_output,
                     for_package=args.package_name)

    return returncode


if __name__ == '__main__':
  sys.exit(main())
