def main():
    parser = ArgumentParser(
        description='Run WRF-Hydro test suite locally',
        epilog='Example usage: python -B run_tests.py '
        '--config nwm_ana nwm_long_range '
        '--compiler gfort '
        '--candidate_dir '
        '/glade/scratch/jmills/wrf_hydro_nwm_public_origin '
        '--reference_dir '
        '/glade/scratch/jmills/wrf_hydro_nwm_public_upstream '
        '--domain_dir /glade/scratch/jmills/CONUS_V2 '
        '--scheduler '
        '--output_dir /glade/scratch/jmills/conus_v2_testing_gfort')

    parser.add_argument(
        "--config",
        required=True,
        nargs='+',
        help="<Required> The configuration(s) to test, "
        "must be one listed in trunk/NDHMS/hydro_namelist.json keys.")

    parser.add_argument('--compiler',
                        required=True,
                        help='<Required> compiler, options are intel or gfort')

    parser.add_argument('--output_dir',
                        required=True,
                        help='<Required> test output directory')

    parser.add_argument('--candidate_dir',
                        required=True,
                        help='<Required> candidate model directory')

    parser.add_argument('--reference_dir',
                        required=True,
                        help='<Required> reference model directory')

    parser.add_argument('--domain_dir',
                        required=False,
                        help='optional domain directory')

    parser.add_argument(
        "--domain_tag",
        required=False,
        help=
        "The release tag of the domain to retrieve, e.g. v5.0.1. or dev. If "
        "specified, a small test domain will be retrieved and placed in the "
        "specified output_dir and used for the testing domain")

    parser.add_argument(
        '--exe_cmd',
        default="'mpirun -np {0} ./wrf_hydro.exe'",
        required=False,
        help='The MPI-dependent model execution command. Default is best guess. '
        'The first/zeroth variable is set to the total number of cores (ncores). The '
        'wrf_hydro_py convention is that the exe is always named wrf_hydro.exe.'
    )

    parser.add_argument('--ncores',
                        default='2',
                        required=False,
                        help='Number of cores to use for testing')

    parser.add_argument(
        '--scheduler',
        required=False,
        action='store_true',
        help='Scheduler to use for testing, options are PBSCheyenne or do not '
        'specify for no scheduler')

    parser.add_argument(
        '--nnodes',
        default='6',
        required=False,
        help='Number of nodes to use for testing if running on scheduler')

    parser.add_argument('--account',
                        default='NRAL0017',
                        required=False,
                        action='store',
                        help='Account number to use if using a scheduler.')

    parser.add_argument('--walltime',
                        default='02:00:00',
                        required=False,
                        action='store',
                        help='Account number to use if using a scheduler.')

    parser.add_argument(
        '--queue',
        default='regular',
        required=False,
        action='store',
        help='Queue to use if running on NCAR Cheyenne, options are regular, '
        'premium, or shared')

    parser.add_argument('--print',
                        required=False,
                        action='store_true',
                        help='Print log to stdout instead of html')

    parser.add_argument('--pdb',
                        required=False,
                        action='store_true',
                        help='pdb (debug) in pytest')

    parser.add_argument('-x',
                        required=False,
                        action='store_true',
                        help='Exit pdb on first failure.')

    parser.add_argument(
        '--use_existing_test_dir',
        default=False,
        required=False,
        action='store_true',
        help='Use existing compiles and runs, only perform output comparisons.'
    )

    parser.add_argument('--xrcmp_n_cores',
                        default=0,
                        required=False,
                        help='Use xrcmp if > 0, and how many cores if so?')

    args = parser.parse_args()

    # Make all directories pathlib objects
    output_dir = pathlib.Path(args.output_dir)
    candidate_dir = pathlib.Path(args.candidate_dir)
    reference_dir = pathlib.Path(args.reference_dir)
    domain_dir = args.domain_dir

    if domain_dir is not None:
        domain_dir = pathlib.Path(domain_dir)

    # Get other args
    config_list = args.config

    compiler = args.compiler
    domain_tag = args.domain_tag
    exe_cmd = args.exe_cmd
    ncores = int(args.ncores)
    nnodes = int(args.nnodes)
    scheduler = args.scheduler
    account = args.account
    walltime = args.walltime
    queue = args.queue
    print_log = args.print
    pdb = args.pdb
    pdb_x = args.x
    use_existing_test_dir = args.use_existing_test_dir
    xrcmp_n_cores = args.xrcmp_n_cores

    # Make output dir if does not exist
    if not use_existing_test_dir:
        if output_dir.is_dir():
            raise (IsADirectoryError('Output directory ' + str(output_dir) +
                                     ' already exists'))
        else:
            output_dir.mkdir(parents=True)

    # Get the domain if asked for
    if domain_tag is not None:
        # Reset domain dir to be the downlaoded domain in the output dir
        domain_dir = output_dir.joinpath('example_case')

        if domain_tag == 'dev':
            file_id = '1xFYB--zm9f8bFHESzgP5X5i7sZryQzJe'
            download_file_from_google_drive(
                file_id, str(output_dir.joinpath('gdrive_testcase.tar.gz')))

            # untar the test case
            untar_cmd = 'tar -xf *testcase*.tar.gz'
            subprocess.run(untar_cmd, shell=True, cwd=str(output_dir))
        else:
            get_release_asset(download_dir=str(output_dir),
                              repo_name='NCAR/wrf_hydro_nwm_public',
                              tag=domain_tag,
                              asset_name='testcase')
            # untar the test case
            untar_cmd = 'tar -xf *testcase*.tar.gz'
            subprocess.run(untar_cmd, shell=True, cwd=str(output_dir))

    # Make copy paths
    candidate_copy = output_dir.joinpath(candidate_dir.name + '_can_pytest')
    reference_copy = output_dir.joinpath(reference_dir.name + '_ref_pytest')
    # copy directories to avoid polluting user source code directories
    if not candidate_copy.exists() or not use_existing_test_dir:
        shutil.copytree(str(candidate_dir), str(candidate_copy), symlinks=True)
    if not reference_copy.exists() or not use_existing_test_dir:
        shutil.copytree(str(reference_dir), str(reference_copy), symlinks=True)

    # run pytest for each supplied config
    has_failure = False
    print("\n\n---------------- Starting WRF-Hydro Testing ----------------")
    print("Testing the configs: " + ', '.join(config_list), flush=True)
    for config in config_list:
        extra_spaces = 29
        total_len = len(config) + extra_spaces
        print('\n\n' + ('#' * total_len))
        print('### TESTING:  ---  ' + config + '  ---  ###')
        print(('#' * total_len) + '\n', flush=True)

        test_result = run_tests(config=config,
                                compiler=compiler,
                                domain_dir=str(domain_dir),
                                candidate_dir=str(candidate_copy),
                                reference_dir=str(reference_copy),
                                output_dir=str(output_dir),
                                scheduler=scheduler,
                                exe_cmd=exe_cmd,
                                ncores=ncores,
                                nnodes=nnodes,
                                account=account,
                                walltime=walltime,
                                queue=queue,
                                print_log=print_log,
                                pdb=pdb,
                                pdb_x=pdb_x,
                                use_existing_test_dir=use_existing_test_dir,
                                xrcmp_n_cores=xrcmp_n_cores)

        if test_result.returncode != 0:
            has_failure = True

    # Exit with 1 if failure
    if has_failure:
        print('\n\n' '##################################')
        print('###  ---  TESTING FAILED  ---  ###')
        print('##################################\n\n', flush=True)
        exit(1)
    else:
        print('\n\n' '##################################')
        print('###  ---  TESTING PASSED  ---  ###')
        print('##################################\n\n', flush=True)
        exit(0)
def main():
    parser = ArgumentParser(
        description='Run WRF-Hydro test suite locally',
        epilog='Example usage: python -B run_tests.py '
        '--config nwm_ana nwm_long_range '
        '--compiler gfort '
        '--candidate_dir '
        '/glade/scratch/jmills/wrf_hydro_nwm_public_origin '
        '--reference_dir '
        '/glade/scratch/jmills/wrf_hydro_nwm_public_upstream '
        '--domain_dir /glade/scratch/jmills/CONUS_V2 '
        '--scheduler '
        '--output_dir /glade/scratch/jmills/conus_v2_testing_gfort')

    parser.add_argument(
        "--config",
        required=True,
        nargs='+',
        help="<Required> The configuration(s) to test, "
        "must be one listed in trunk/NDHMS/hydro_namelist.json keys.")

    parser.add_argument('--compiler',
                        required=True,
                        help='<Required> compiler, options are intel or gfort')

    parser.add_argument('--output_dir',
                        required=True,
                        help='<Required> test output directory')

    parser.add_argument('--candidate_dir',
                        required=True,
                        help='<Required> candidate model directory')

    parser.add_argument('--reference_dir',
                        required=True,
                        help='<Required> reference model directory')

    parser.add_argument('--domain_dir',
                        required=False,
                        help='optional domain directory')

    parser.add_argument(
        "--domain_tag",
        required=False,
        help=
        "The release tag of the domain to retrieve, e.g. v5.0.1. or dev. If "
        "specified, a small test domain will be retrieved and placed in the "
        "specified output_dir and used for the testing domain")

    parser.add_argument('--ncores',
                        default='2',
                        required=False,
                        help='Number of cores to use for testing')

    parser.add_argument(
        '--scheduler',
        required=False,
        action='store_true',
        help='Scheduler to use for testing, options are PBSCheyenne or do not '
        'specify for no scheduler')

    parser.add_argument(
        '--nnodes',
        default='6',
        required=False,
        help='Number of nodes to use for testing if running on scheduler')

    parser.add_argument('--account',
                        default='NRAL0017',
                        required=False,
                        action='store',
                        help='Account number to use if using a scheduler.')

    parser.add_argument('--walltime',
                        default='02:00:00',
                        required=False,
                        action='store',
                        help='Account number to use if using a scheduler.')

    parser.add_argument(
        '--queue',
        default='regular',
        required=False,
        action='store',
        help='Queue to use if running on NCAR Cheyenne, options are regular, '
        'premium, or shared')

    parser.add_argument('--print',
                        required=False,
                        action='store_true',
                        help='Print log to stdout instead of html')

    args = parser.parse_args()

    # Make all directories pathlib objects
    output_dir = pathlib.Path(args.output_dir)
    candidate_dir = pathlib.Path(args.candidate_dir)
    reference_dir = pathlib.Path(args.reference_dir)
    domain_dir = args.domain_dir

    if domain_dir is not None:
        domain_dir = pathlib.Path(domain_dir)

    # Get other args
    config_list = args.config

    compiler = args.compiler
    domain_tag = args.domain_tag
    ncores = int(args.ncores)
    nnodes = int(args.nnodes)
    scheduler = args.scheduler
    account = args.account
    walltime = args.walltime
    queue = args.queue
    print_log = args.print

    # Make output dir if does not exist
    if output_dir.is_dir():
        raise (IsADirectoryError('Output directory ' + str(output_dir) +
                                 ' already exists'))
    else:
        output_dir.mkdir(parents=True)

    # Get the domain if asked for
    if domain_tag is not None:
        # Reset domain dir to be the downlaoded domain in the output dir
        domain_dir = output_dir.joinpath('example_case')

        if domain_tag == 'dev':
            file_id = '1EHgWeM8k2-Y3jNMLri6C0u_fIUQIonO_'
            download_file_from_google_drive(
                file_id, str(output_dir.joinpath('gdrive_testcase.tar.gz')))

            # untar the test case
            untar_cmd = 'tar -xf *testcase*.tar.gz'
            subprocess.run(untar_cmd, shell=True, cwd=str(output_dir))
        else:
            get_release_asset(download_dir=str(output_dir),
                              repo_name='NCAR/wrf_hydro_nwm_public',
                              tag=domain_tag,
                              asset_name='testcase')
            # untar the test case
            untar_cmd = 'tar -xf *testcase*.tar.gz'
            subprocess.run(untar_cmd, shell=True, cwd=str(output_dir))

    # Make copy paths
    candidate_copy = output_dir.joinpath(candidate_dir.name + '_can_pytest')
    reference_copy = output_dir.joinpath(reference_dir.name + '_ref_pytest')

    # Remove if exist and make if not
    if candidate_copy.is_dir():
        shutil.rmtree(str(candidate_copy))
    if reference_copy.is_dir():
        shutil.rmtree(str(reference_copy))

    # copy directories to avoid polluting user source code directories
    shutil.copytree(str(candidate_dir), str(candidate_copy), symlinks=True)
    shutil.copytree(str(reference_dir), str(reference_copy), symlinks=True)

    # run pytest for each supplied config
    has_failure = False
    print("\n\n---------------- Starting WRF-Hydro Testing ----------------")
    print("Testing the configs: " + ', '.join(config_list), flush=True)
    for config in config_list:
        extra_spaces = 29
        total_len = len(config) + extra_spaces
        print('\n\n' + ('#' * total_len))
        print('### TESTING:  ---  ' + config + '  ---  ###')
        print(('#' * total_len) + '\n', flush=True)

        test_result = run_tests(config=config,
                                compiler=compiler,
                                domain_dir=str(domain_dir),
                                candidate_dir=str(candidate_copy),
                                reference_dir=str(reference_copy),
                                output_dir=str(output_dir),
                                scheduler=scheduler,
                                ncores=ncores,
                                nnodes=nnodes,
                                account=account,
                                walltime=walltime,
                                queue=queue,
                                print_log=print_log)

        if test_result.returncode != 0:
            has_failure = True

    # Exit with 1 if failure
    if has_failure:
        print('\n\n' '##################################')
        print('###  ---  TESTING FAILED  ---  ###')
        print('##################################\n\n', flush=True)
        exit(1)
    else:
        print('\n\n' '##################################')
        print('###  ---  TESTING PASSED  ---  ###')
        print('##################################\n\n', flush=True)
        exit(0)