#!/usr/bin/env python

# Copyright (c) 2018, Cornell University
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# Neither the name of Cornell University nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Tuan Ta

import os
import sys
import argparse
import subprocess

from multiprocessing import Pool

import clusterjob
from clusterjob import *

#-------------------------------------------------------------------------
# utility function to run a process
#-------------------------------------------------------------------------
def execute(cmd):
  try:
    return subprocess.check_output(cmd, shell=True)
  except subprocess.CalledProcessError, err:
    print "ERROR: " + err.output

#-------------------------------------------------------------------------
# Input options
#-------------------------------------------------------------------------

# general options
parser = argparse.ArgumentParser(description='run RISC-V assembly tests')
parser.add_argument('--max-tick',
                    help = 'maximum simulated tick',
                    type = int,
                    default = 10000000000)
parser.add_argument('--num-cpus',
                    help = 'number of CPUs\
                            (some tests require at least 4 CPUs)',
                    type = int,
                    default = 4)
parser.add_argument('--ruby',
                    help = 'Use Ruby memory?',
                    action = 'store_true')

args = parser.parse_args()

# convert all paths to absolute paths
test_dir = os.path.abspath('../../bin/riscv/')
test_summary_out = './test-summary.out'

#-------------------------------------------------------------------------
# gem5 variables
#-------------------------------------------------------------------------
gem5_dir = os.path.abspath('../../../../../')
gem5_bin = os.path.join(gem5_dir, 'build', 'RISCV', 'gem5.opt')
config = os.path.join(gem5_dir, 'configs', 'example', 'se.py')

# list of CPU models to be tested
cpu_models = ['AtomicSimpleCPU',
              'TimingSimpleCPU',
              'MinorCPU',
              'DerivO3CPU']

# get a list of test binaries in the given directory
tests = []
for line in execute("ls %s/*" % (test_dir)).splitlines():
  if line:
    tests.append(line.split('/')[-1])

# total number of tests to run
n_tests = len(tests) * len(cpu_models)

# make a list of jobs
job_cmds = []
job_names = []
for test in tests:
  for model in cpu_models:
    test_name = test + '-' + model
    job_names.append(test_name)
    job_cmds.append([gem5_bin,
                     config,
                     '-m', str(args.max_tick),
                     '--cpu-type', model,
                     '-n', str(args.num_cpus),
                     '-c', test_dir + '/' + test,
                     '--ruby' if args.ruby else '--caches',
                    ])

# execute all jobs
job_pool = Pool(processes = n_tests)
job_outputs = job_pool.map(subprocess.call, job_cmds)
job_pool.close()

# process job outputs
file = open(test_summary_out, "w")

job_outputs = zip(job_names, job_outputs)
for entry in job_outputs:
  # a negative return value indicates that the job was terminated
  # by a signal
  # a positive return value indicates that the job exited with a return
  # value
  if entry[1] < 0:
    file.write("%-50s failed - signal = %d\n" % (entry[0], -1 * entry[1]))
  elif entry[1] > 0:
    file.write("%-50s failed - status = %d\n" % (entry[0], entry[1]))
  else:
    file.write("%-50s passed\n" % (entry[0]))

file.close()
