模拟acm测试系统
1. 前言
最近和朋友在做一些acm的训练,虽然算法功力比较薄弱,但对acm的测试系统产生
了一点兴趣,于是就尝试使用shell脚本做了一样小型的模拟程序。
运行环境主要是linux和mac。我的环境是mac。目前只支持c语言。
主要功能有:
- 第一次运行时,会初始化设置你的项目根目录,并询问是否将脚本加入到PATH
- 根据用户输入的题目命名,生成题目目录,包括源文件,测试数据文件,期望数据文件
- 可以生成题目目录的时候,选择是否在控制台输入测试和期望数据
- 根据测试数据文件运行源代码,并生成结果输出文件,和期望数据文件进行比对,输出比对结果
- 提供运行时间的测试,毫秒级别,不过需要安装python
- 启动使用gdb命令调试程序
- 一些简单的错误处理
优点:
因为acm题目要求标准的输入和输出,这样导致测试的时候不好进行大数据量的测试。
一般的做法是在源代码中加入读测试文件的代码,再在提交前再修改成标准输入这些代码,
或是使用重定向的宏,提交前再注释这些宏。总之觉得有点麻烦。
理想的状态是本地的代码直接原封不动的提交上去。这个脚本正是为了达到这个目的而进行实现的。
注意:
因为在mac环境中,c语言的编译器默认选择clang,如果没有安装,则选择gcc。
在测试时间的功能里,是调用python来完成的,mac下没有找到系统自带的好用的时间命令。
这次的脚本编写,也可以是看作是一次shell命令学习,挺有意思,不过工具不重要,算法的提高
才是重点啊~~
想试试的同学,复制代码,保存到名为acm_manage.sh的文件中,在赋上可执行权限,运行./acm_manage.sh
想试试的同学,复制代码,保存到名为acm_manage.sh的文件中,在赋上可执行权限,运行./acm_manage.sh
就可以根据提示信息立即体验。
欢迎留言讨论。
(4.8更新)
有一个致命错误:
有一个致命错误:
之前是使用
while read -r line
do
echo $line | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
done < $TEST_DATA_FILE_PATH
echo $line | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
done < $TEST_DATA_FILE_PATH
来逐行读取文件,并运行程序,这样表面上可以运行通过一些题目,不过一个
重要的问题是,它不能处理需要连续读取多行内容,再运行程序的题目。因为
它是每读取一行,就重新运行一次main函数。显然是错的。。。。
改成一种简单的方式,一次性读取所有内容,并运行main函数:
cat $TEST_DATA_FILE_PATH | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
(4.12更新)
新添加gdb自动从测试文件输入数据功能
原来是这样:
debug_code()
{
echo "Debug is start."
$DEBUG_TOOL $EXECUTABLE_FILE_PATH
echo "Debug is done."
}
debug_code()
{
echo "Debug is start."
$DEBUG_TOOL $EXECUTABLE_FILE_PATH
echo "Debug is done."
}
改成:
debug_code()
{
echo "Debug is start."
echo "start < $TEST_DATA_FILE_PATH" > $DEBUG_COMMAND_FILE_PATH
$DEBUG_TOOL -x $DEBUG_COMMAND_FILE_PATH $EXECUTABLE_FILE_PATH
rm $DEBUG_COMMAND_FILE_PATH
echo "Debug is done."
}
debug_code()
{
echo "Debug is start."
echo "start < $TEST_DATA_FILE_PATH" > $DEBUG_COMMAND_FILE_PATH
$DEBUG_TOOL -x $DEBUG_COMMAND_FILE_PATH $EXECUTABLE_FILE_PATH
rm $DEBUG_COMMAND_FILE_PATH
echo "Debug is done."
}
实现方法是,使用-x命令,从文件中读取gdb shell的命令,在这里使用start带参数的方式来完成
gdb的重定向输入。所以我就先创建一个这样的命令文件,调试结束的时候再删除。之前使用gdb -ex
尝试直接输入启动命令,好像不成功。目前不知道还有没有其它的方式。
(4.13更新)
去掉编译-O2优化选项,便于调试。改成:
OPTIMIZE_OPTION =
在调试时,需要重新编译代码,之前没有写。。。
select_compile_tool
compile_code
加入到最后while循环部分的,debug分支中去。
(4.13更新)
去掉编译-O2优化选项,便于调试。改成:
OPTIMIZE_OPTION =
在调试时,需要重新编译代码,之前没有写。。。
select_compile_tool
compile_code
加入到最后while循环部分的,debug分支中去。
2. 代码
#!/bin/bash
#################################################################################################
# Test Shell Script for acm program.
#
# Usage is:
# ./acm_manage.sh i questionName
# ./acm_manage.sh r questionName
# ./acm_manage.sh d questionName
# ./acm_manage.sh h
# ./acm_manage.sh help
#
# Main Function is:
# 1. init acm directory and create source, test, expect files for you.
# 2. run your code with your test data file, and check its correctness with your expect file.
# 3. debug your code with your test data file.
#
# Platform:
# Linux, Mac OSX, Unix
#
# Support Program Language:
# C
#
# Author:
# Hanks
#
# Version:
# 1.0
#################################################################################################
########################
# varibles define start
########################
# script name
SCRIPT_NAME = "acm_manage.sh"
# diretory for question
QUESTION_DIR =
# question name
QUESTION_NAME =
# file name prefix
TEST_FILE_PREFIX = "test_"
EXPECT_FILE_PREFIX = "expect_"
RESULT_FILE_PREFIX = "result_"
# file type
SOURCE_FILE_TYPE = ".c"
DATA_FILE_TYPE = ".txt"
# path for test data file
TEST_DATA_FILE_PATH =
# path for executable file
EXECUTABLE_FILE_PATH =
# path for result file
RESULT_FILE_PATH =
# path for source code file
SOURCE_CODE_PATH =
# path for expect data file
EXPECT_FILE_PATH =
# default executable file name
DEFAILT_EXECUTABLE_FILE_NAME = "a.out"
# gdb command temp file
DEFAULT_DEBUG_COMMAND_FILE_NAME="gdb_cmd_temp"
DEBUG_COMMAND_FILE_PATH=
# cost second time
RUN_START_TIME =
RUN_END_TIME =
TIME_COST =
# accept time is 3 millisecond
ACCEPT_TIME = 3000
# make format of each line of content from echo be corrent
IFS = "
"
# init compiler command, default is clang, if no, use gcc
COMPILE_COMMAND =
DEBUG_OPTION = "-g"
OPTIMIZE_OPTION =
DEBUG_TOOL = "gdb"
# acm workspace existed flag
IS_WORKSPACE_NOT_EXIST =false
IS_ACM_ROOT_NOT_EXIST =false
#config variable
SCRIPT_DIRECTORY =$(cd "$(dirname "$ 0 ")"; pwd)
ACM_ROOT_NAME =
ACM_ROOT =
CONFIG_FILE_NAME = "$SCRIPT_DIRECTORY/acm_manage.ini"
SET_PATH =
######################
# varibles define end
######################
#######################
# function define start
#######################
# add acm_manage.sh to your PATH environment varibale,
# so you can use this command defaultly.
set_script_to_path()
{
# ask user whether to add to path or not
loop =true
while $loop; do
read -n1 -p "Add this script to your PATH to run anywhere. [y/n]?" answer
case $answer in
Y | y)
echo
echo "Fine, continue."
SET_PATH =true
loop =false
;;
N | n)
echo
echo "Ok, I got it. Donot set to path."
SET_PATH =fasle
loop =false
return
;;
*)
echo "Error choice, please answer with y or n."
;;
esac
echo
done
PROFILE_PATH = ~ /.profile
BASH_PROFILE_PATH = ~ /.bash_profile
OS_TYPE = `uname `
ADD_PATH_COMMAND = "export PATH=\$PATH:$SCRIPT_DIRECTORY"
TARGET_PROFILE_PATH =
if [ $OS_TYPE = "Darwin" ]; then
echo "You are a Mac system user."
TARGET_PROFILE_PATH =$PROFILE_PATH
elif [ $OS_TYPE = "Linux" ]; then
echo "You are a Linux system user."
TARGET_PROFILE_PATH =$BASH_PROFILE_PATH
elif [ $OS_TYPE = "FreeBSD" ]; then
echo "You are a FreeBSD system user."
TARGET_PROFILE_PATH =$BASH_PROFILE_PATH
fi
echo "Start to add this script to your path."
echo "#Add acm_manage.sh command to your PATH" >> $TARGET_PROFILE_PATH
echo $ADD_PATH_COMMAND >> $TARGET_PROFILE_PATH
echo "Add path is done. You can check $TARGET_PROFILE_PATH."
echo "So you can run this script anywhere. Enjoy."
}
# print help info for user
print_help()
{
echo
echo "Usage: acm_manage.sh [i|r|d|h|help] questionName"
echo "Options: These are optional argument"
echo " i init acm directory, create directory, source, test file and expect file automatically for you."
echo " h|help show help info."
echo " r run your code with your test data. And diff output and expect file to check correctness "
echo " d start debug tool (like gdb, lldb) to debug your code."
echo
echo "When you first run this shell, you should "
echo "enter the directory the script is in "
echo "and run the script. Have fun."
echo
}
read_conf_info_from_file()
{
ACM_ROOT = ` cat $CONFIG_FILE_NAME | grep ACM_ROOT | awk -F "=" '{print $2}' `
SET_PATH = ` cat $CONFIG_FILE_NAME | grep SET_PATH | awk -F "=" '{print $2}' `
echo "ACM_ROOT is $ACM_ROOT"
echo "SET_PATH is $SET_PATH"
}
create_conf_file()
{
# create configure init file
echo "ACM_ROOT=$ACM_ROOT" >> $CONFIG_FILE_NAME
echo "SET_PATH=$SET_PATH" >> $CONFIG_FILE_NAME
echo
echo "Create config file $CONFIG_FILE_NAME in the current directory."
echo
}
set_acm_root()
{
echo "This is your first time and last time to see this message, just config some info. ^_^"
echo "Please input your acm root directory name, it will be created in your current directory:"
read ACM_ROOT_NAME
ACM_ROOT =$SCRIPT_DIRECTORY /$ACM_ROOT_NAME /
if [ ! -d $ACM_ROOT ]; then
mkdir $ACM_ROOT
echo "Create $ACM_ROOT directory for you."
fi
}
init_acm_root_directory()
{
if [ ! -f $CONFIG_FILE_NAME ]; then
# config file is not existed, create it
set_acm_root
set_script_to_path
create_conf_file
echo "Now you can see usage to rock acm."
print_help
echo
else
# read config info from file and init ACM_ROOT
read_conf_info_from_file
fi
}
# init files path
init_path()
{
echo
# build paths for files
QUESTION_DIR =$ACM_ROOT$QUESTION_NAME
#echo "questoin directory is $QUESTION_DIR"
SOURCE_CODE_PATH =$QUESTION_DIR /$QUESTION_NAME$SOURCE_FILE_TYPE
echo "Source file is $SOURCE_CODE_PATH"
TEST_DATA_FILE_PATH =$QUESTION_DIR /$TEST_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "test data is $TEST_DATA_FILE_PATH"
EXPECT_FILE_PATH =$QUESTION_DIR /$EXPECT_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "expect is $EXPECT_FILE_PATH"
EXECUTABLE_FILE_PATH =$QUESTION_DIR /$DEFAILT_EXECUTABLE_FILE_NAME
#echo "executable is $EXECUTABLE_FILE_PATH"
RESULT_FILE_PATH =$QUESTION_DIR /$RESULT_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "result is $RESULT_FILE_PATH"
DEBUG_COMMAND_FILE_PATH=$QUESTION_DIR/$DEFAULT_DEBUG_COMMAND_FILE_NAME
}
# init source code template, default is c language
init_source_code()
{
echo "#include <stdio.h>" >> $SOURCE_CODE_PATH
echo >> $SOURCE_CODE_PATH
echo "int main(int argc, char *args[]) {" >> $SOURCE_CODE_PATH
echo >> $SOURCE_CODE_PATH
echo " return 0;" >> $SOURCE_CODE_PATH
echo "}" >> $SOURCE_CODE_PATH
}
# init test and expect data file from user input
init_test_and_expect_data()
{
loop =true
while $loop; do
read -n1 -p "Do you want to input test and expect data now. [y/n]?" answer
case $answer in
Y | y)
echo "Input start:"
echo "Please pay attention to the command to end your input."
echo "or else you will need modify these file by yourself again."
echo
echo "Please input your test input data and Press Enter and then ctrl+D to end input:"
cat > $TEST_DATA_FILE_PATH
echo "Please input your expect output data and Press Enter and then ctrl+D to end input:"
cat > $EXPECT_FILE_PATH
echo "Input end."
loop =false
;;
N | n)
touch $TEST_DATA_FILE_PATH
touch $EXPECT_FILE_PATH
echo
echo "Create empty test and expect file. You need add test data by yourself."
loop =false
;;
*)
echo "Wrong input, please answer just by y or n."
;;
esac
done
}
# create acm directory and files for user
init_acm_workspace()
{
echo "Init acm workspace for you."
# if directory is not existed, create a new one
# or else, do nothing to protect the existed source files
if $IS_WORKSPACE_NOT_EXIST; then
# create acm diretory
mkdir $QUESTION_DIR
# init source and test data
init_source_code
init_test_and_expect_data
echo "Init is done, let's rock."
else
echo "Sorry, workspace is already exsited, you can start rock."
fi
# if directory existed, do nothing
return
}
# check workspace existed
workspace_exist_check()
{
if $IS_WORKSPACE_NOT_EXIST; then
echo "Workspace \"$QUESTION_NAME\" is not existed. Please use command \"$SCRIPT_NAME i $QUESTION_NAME\" to init workspace."
exit 1
fi
}
compile_code()
{
echo "Compile start."
$COMPILE_COMMAND $DEBUG_OPTION $SOURCE_CODE_PATH $OPTIMIZE_OPTION -o $EXECUTABLE_FILE_PATH
echo "Compile end."
}
run_code()
{
# delete result file firstly if existed, to avoid
# result confict with old one
echo
echo "Clear result file."
rm $RESULT_FILE_PATH > /dev /null
# create a new empty result file
touch $RESULT_FILE_PATH
# run code with test data, and redirect output to result file
echo
echo "Start to run code:"
# record start time, %N means nanoseconds
# second is 1
# millisecond is 0.001
# macrosecond is 0.000001
# nanosecond is 0.000000001
#RUN_START_TIME=`date +%s%N`
# get total millisecond from epoth
RUN_START_TIME =$(python -c 'import time; print int(round(time.time()*1000))')
# while read -r line
# do
# echo $line | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
# done < $TEST_DATA_FILE_PATH
cat $TEST_DATA_FILE_PATH | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
# record end time
RUN_END_TIME =$(python -c 'import time; print int(round(time.time()*1000))')
echo "Run is done."
}
print_run_time_cost()
{
echo
# cost in milliseconds
TIME_COST =$((RUN_END_TIME -RUN_START_TIME))
# cost in seconds
SECOND_TIME_COST = ` echo "$TIME_COST / 1000 " | bc -l `
echo "Run time cost is ${SECOND_TIME_COST:0:4} seconds."
}
print_output()
{
echo
echo "Output is:"
cat $RESULT_FILE_PATH
}
judge_result()
{
echo
echo "Diff result is:"
if diff "$RESULT_FILE_PATH" "$EXPECT_FILE_PATH"; then
if [ $TIME_COST -le $ACCEPT_TIME ]; then
echo "Accept. Congratulations"
else
echo "Time limit exceeded. Cost time $TIME_COST > Accept time $ACCEPT_TIME milliseconds"
fi
else
echo "Wrong answer. Try again."
fi
}
debug_code()
{
echo "Debug is start."
echo "start < $TEST_DATA_FILE_PATH" > $DEBUG_COMMAND_FILE_PATH
#$DEBUG_TOOL $EXECUTABLE_FILE_PATH
$DEBUG_TOOL -x $DEBUG_COMMAND_FILE_PATH $EXECUTABLE_FILE_PATH
rm $DEBUG_COMMAND_FILE_PATH
echo "Debug is done."
}
# shift command arguments for while loop process
skip_command()
{
shift 2
}
# detect clang installed, or else use gcc
select_compile_tool()
{
which clang > /dev /null 2 > & 1
if [ $ ? -eq 0 ]
then
COMPILE_COMMAND = "clang"
else
COMPILE_COMMAND = "gcc"
fi
echo "Use $COMPILE_COMMAND to compile source code."
}
#######################
# function define end
#######################
##################
# Main part start
##################
# if no right number of argument, show help and exit
# if use h or help command, show help and exit
if [ "$1" = "h" ]
then
print_help
exit 0
elif [ "$1" = "help" ]
then
print_help
exit 0
elif [ $ # -lt 2 ]
then
print_help
exit 1
fi
init_acm_root_directory
# get question name and init path for all files
QUESTION_NAME = "$2"
init_path
# check if workspace is existed.
if [ ! -d $QUESTION_DIR ]; then
IS_WORKSPACE_NOT_EXIST =true
fi
# option argument command implementation
while [ "$1" ]
do
echo
if [ "$1" = "i" ]; then
init_acm_workspace
elif [ "$1" = "r" ]
then
workspace_exist_check
echo "Run code with your test data and check correctness."
select_compile_tool
compile_code
run_code
print_output
print_run_time_cost
judge_result
elif [ "$1" = "d" ]
then
workspace_exist_check
select_compile_tool
compile_code
echo "Debug your code with debug tool, default is gdb"
debug_code
else
echo "$SCRIPT_NAME does not recognize option $1."
print_help
exit 1
fi
# skip command for loop argument process now
# maybe there are some extensions in the future
shift 2
done
##################
# Main part end
##################
#################################################################################################
# Test Shell Script for acm program.
#
# Usage is:
# ./acm_manage.sh i questionName
# ./acm_manage.sh r questionName
# ./acm_manage.sh d questionName
# ./acm_manage.sh h
# ./acm_manage.sh help
#
# Main Function is:
# 1. init acm directory and create source, test, expect files for you.
# 2. run your code with your test data file, and check its correctness with your expect file.
# 3. debug your code with your test data file.
#
# Platform:
# Linux, Mac OSX, Unix
#
# Support Program Language:
# C
#
# Author:
# Hanks
#
# Version:
# 1.0
#################################################################################################
########################
# varibles define start
########################
# script name
SCRIPT_NAME = "acm_manage.sh"
# diretory for question
QUESTION_DIR =
# question name
QUESTION_NAME =
# file name prefix
TEST_FILE_PREFIX = "test_"
EXPECT_FILE_PREFIX = "expect_"
RESULT_FILE_PREFIX = "result_"
# file type
SOURCE_FILE_TYPE = ".c"
DATA_FILE_TYPE = ".txt"
# path for test data file
TEST_DATA_FILE_PATH =
# path for executable file
EXECUTABLE_FILE_PATH =
# path for result file
RESULT_FILE_PATH =
# path for source code file
SOURCE_CODE_PATH =
# path for expect data file
EXPECT_FILE_PATH =
# default executable file name
DEFAILT_EXECUTABLE_FILE_NAME = "a.out"
# gdb command temp file
DEFAULT_DEBUG_COMMAND_FILE_NAME="gdb_cmd_temp"
DEBUG_COMMAND_FILE_PATH=
# cost second time
RUN_START_TIME =
RUN_END_TIME =
TIME_COST =
# accept time is 3 millisecond
ACCEPT_TIME = 3000
# make format of each line of content from echo be corrent
IFS = "
"
# init compiler command, default is clang, if no, use gcc
COMPILE_COMMAND =
DEBUG_OPTION = "-g"
OPTIMIZE_OPTION =
DEBUG_TOOL = "gdb"
# acm workspace existed flag
IS_WORKSPACE_NOT_EXIST =false
IS_ACM_ROOT_NOT_EXIST =false
#config variable
SCRIPT_DIRECTORY =$(cd "$(dirname "$ 0 ")"; pwd)
ACM_ROOT_NAME =
ACM_ROOT =
CONFIG_FILE_NAME = "$SCRIPT_DIRECTORY/acm_manage.ini"
SET_PATH =
######################
# varibles define end
######################
#######################
# function define start
#######################
# add acm_manage.sh to your PATH environment varibale,
# so you can use this command defaultly.
set_script_to_path()
{
# ask user whether to add to path or not
loop =true
while $loop; do
read -n1 -p "Add this script to your PATH to run anywhere. [y/n]?" answer
case $answer in
Y | y)
echo
echo "Fine, continue."
SET_PATH =true
loop =false
;;
N | n)
echo
echo "Ok, I got it. Donot set to path."
SET_PATH =fasle
loop =false
return
;;
*)
echo "Error choice, please answer with y or n."
;;
esac
echo
done
PROFILE_PATH = ~ /.profile
BASH_PROFILE_PATH = ~ /.bash_profile
OS_TYPE = `uname `
ADD_PATH_COMMAND = "export PATH=\$PATH:$SCRIPT_DIRECTORY"
TARGET_PROFILE_PATH =
if [ $OS_TYPE = "Darwin" ]; then
echo "You are a Mac system user."
TARGET_PROFILE_PATH =$PROFILE_PATH
elif [ $OS_TYPE = "Linux" ]; then
echo "You are a Linux system user."
TARGET_PROFILE_PATH =$BASH_PROFILE_PATH
elif [ $OS_TYPE = "FreeBSD" ]; then
echo "You are a FreeBSD system user."
TARGET_PROFILE_PATH =$BASH_PROFILE_PATH
fi
echo "Start to add this script to your path."
echo "#Add acm_manage.sh command to your PATH" >> $TARGET_PROFILE_PATH
echo $ADD_PATH_COMMAND >> $TARGET_PROFILE_PATH
echo "Add path is done. You can check $TARGET_PROFILE_PATH."
echo "So you can run this script anywhere. Enjoy."
}
# print help info for user
print_help()
{
echo
echo "Usage: acm_manage.sh [i|r|d|h|help] questionName"
echo "Options: These are optional argument"
echo " i init acm directory, create directory, source, test file and expect file automatically for you."
echo " h|help show help info."
echo " r run your code with your test data. And diff output and expect file to check correctness "
echo " d start debug tool (like gdb, lldb) to debug your code."
echo
echo "When you first run this shell, you should "
echo "enter the directory the script is in "
echo "and run the script. Have fun."
echo
}
read_conf_info_from_file()
{
ACM_ROOT = ` cat $CONFIG_FILE_NAME | grep ACM_ROOT | awk -F "=" '{print $2}' `
SET_PATH = ` cat $CONFIG_FILE_NAME | grep SET_PATH | awk -F "=" '{print $2}' `
echo "ACM_ROOT is $ACM_ROOT"
echo "SET_PATH is $SET_PATH"
}
create_conf_file()
{
# create configure init file
echo "ACM_ROOT=$ACM_ROOT" >> $CONFIG_FILE_NAME
echo "SET_PATH=$SET_PATH" >> $CONFIG_FILE_NAME
echo
echo "Create config file $CONFIG_FILE_NAME in the current directory."
echo
}
set_acm_root()
{
echo "This is your first time and last time to see this message, just config some info. ^_^"
echo "Please input your acm root directory name, it will be created in your current directory:"
read ACM_ROOT_NAME
ACM_ROOT =$SCRIPT_DIRECTORY /$ACM_ROOT_NAME /
if [ ! -d $ACM_ROOT ]; then
mkdir $ACM_ROOT
echo "Create $ACM_ROOT directory for you."
fi
}
init_acm_root_directory()
{
if [ ! -f $CONFIG_FILE_NAME ]; then
# config file is not existed, create it
set_acm_root
set_script_to_path
create_conf_file
echo "Now you can see usage to rock acm."
print_help
echo
else
# read config info from file and init ACM_ROOT
read_conf_info_from_file
fi
}
# init files path
init_path()
{
echo
# build paths for files
QUESTION_DIR =$ACM_ROOT$QUESTION_NAME
#echo "questoin directory is $QUESTION_DIR"
SOURCE_CODE_PATH =$QUESTION_DIR /$QUESTION_NAME$SOURCE_FILE_TYPE
echo "Source file is $SOURCE_CODE_PATH"
TEST_DATA_FILE_PATH =$QUESTION_DIR /$TEST_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "test data is $TEST_DATA_FILE_PATH"
EXPECT_FILE_PATH =$QUESTION_DIR /$EXPECT_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "expect is $EXPECT_FILE_PATH"
EXECUTABLE_FILE_PATH =$QUESTION_DIR /$DEFAILT_EXECUTABLE_FILE_NAME
#echo "executable is $EXECUTABLE_FILE_PATH"
RESULT_FILE_PATH =$QUESTION_DIR /$RESULT_FILE_PREFIX$QUESTION_NAME$DATA_FILE_TYPE
#echo "result is $RESULT_FILE_PATH"
DEBUG_COMMAND_FILE_PATH=$QUESTION_DIR/$DEFAULT_DEBUG_COMMAND_FILE_NAME
}
# init source code template, default is c language
init_source_code()
{
echo "#include <stdio.h>" >> $SOURCE_CODE_PATH
echo >> $SOURCE_CODE_PATH
echo "int main(int argc, char *args[]) {" >> $SOURCE_CODE_PATH
echo >> $SOURCE_CODE_PATH
echo " return 0;" >> $SOURCE_CODE_PATH
echo "}" >> $SOURCE_CODE_PATH
}
# init test and expect data file from user input
init_test_and_expect_data()
{
loop =true
while $loop; do
read -n1 -p "Do you want to input test and expect data now. [y/n]?" answer
case $answer in
Y | y)
echo "Input start:"
echo "Please pay attention to the command to end your input."
echo "or else you will need modify these file by yourself again."
echo
echo "Please input your test input data and Press Enter and then ctrl+D to end input:"
cat > $TEST_DATA_FILE_PATH
echo "Please input your expect output data and Press Enter and then ctrl+D to end input:"
cat > $EXPECT_FILE_PATH
echo "Input end."
loop =false
;;
N | n)
touch $TEST_DATA_FILE_PATH
touch $EXPECT_FILE_PATH
echo
echo "Create empty test and expect file. You need add test data by yourself."
loop =false
;;
*)
echo "Wrong input, please answer just by y or n."
;;
esac
done
}
# create acm directory and files for user
init_acm_workspace()
{
echo "Init acm workspace for you."
# if directory is not existed, create a new one
# or else, do nothing to protect the existed source files
if $IS_WORKSPACE_NOT_EXIST; then
# create acm diretory
mkdir $QUESTION_DIR
# init source and test data
init_source_code
init_test_and_expect_data
echo "Init is done, let's rock."
else
echo "Sorry, workspace is already exsited, you can start rock."
fi
# if directory existed, do nothing
return
}
# check workspace existed
workspace_exist_check()
{
if $IS_WORKSPACE_NOT_EXIST; then
echo "Workspace \"$QUESTION_NAME\" is not existed. Please use command \"$SCRIPT_NAME i $QUESTION_NAME\" to init workspace."
exit 1
fi
}
compile_code()
{
echo "Compile start."
$COMPILE_COMMAND $DEBUG_OPTION $SOURCE_CODE_PATH $OPTIMIZE_OPTION -o $EXECUTABLE_FILE_PATH
echo "Compile end."
}
run_code()
{
# delete result file firstly if existed, to avoid
# result confict with old one
echo
echo "Clear result file."
rm $RESULT_FILE_PATH > /dev /null
# create a new empty result file
touch $RESULT_FILE_PATH
# run code with test data, and redirect output to result file
echo
echo "Start to run code:"
# record start time, %N means nanoseconds
# second is 1
# millisecond is 0.001
# macrosecond is 0.000001
# nanosecond is 0.000000001
#RUN_START_TIME=`date +%s%N`
# get total millisecond from epoth
RUN_START_TIME =$(python -c 'import time; print int(round(time.time()*1000))')
# while read -r line
# do
# echo $line | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
# done < $TEST_DATA_FILE_PATH
cat $TEST_DATA_FILE_PATH | $EXECUTABLE_FILE_PATH >> $RESULT_FILE_PATH
# record end time
RUN_END_TIME =$(python -c 'import time; print int(round(time.time()*1000))')
echo "Run is done."
}
print_run_time_cost()
{
echo
# cost in milliseconds
TIME_COST =$((RUN_END_TIME -RUN_START_TIME))
# cost in seconds
SECOND_TIME_COST = ` echo "$TIME_COST / 1000 " | bc -l `
echo "Run time cost is ${SECOND_TIME_COST:0:4} seconds."
}
print_output()
{
echo
echo "Output is:"
cat $RESULT_FILE_PATH
}
judge_result()
{
echo
echo "Diff result is:"
if diff "$RESULT_FILE_PATH" "$EXPECT_FILE_PATH"; then
if [ $TIME_COST -le $ACCEPT_TIME ]; then
echo "Accept. Congratulations"
else
echo "Time limit exceeded. Cost time $TIME_COST > Accept time $ACCEPT_TIME milliseconds"
fi
else
echo "Wrong answer. Try again."
fi
}
debug_code()
{
echo "Debug is start."
echo "start < $TEST_DATA_FILE_PATH" > $DEBUG_COMMAND_FILE_PATH
#$DEBUG_TOOL $EXECUTABLE_FILE_PATH
$DEBUG_TOOL -x $DEBUG_COMMAND_FILE_PATH $EXECUTABLE_FILE_PATH
rm $DEBUG_COMMAND_FILE_PATH
echo "Debug is done."
}
# shift command arguments for while loop process
skip_command()
{
shift 2
}
# detect clang installed, or else use gcc
select_compile_tool()
{
which clang > /dev /null 2 > & 1
if [ $ ? -eq 0 ]
then
COMPILE_COMMAND = "clang"
else
COMPILE_COMMAND = "gcc"
fi
echo "Use $COMPILE_COMMAND to compile source code."
}
#######################
# function define end
#######################
##################
# Main part start
##################
# if no right number of argument, show help and exit
# if use h or help command, show help and exit
if [ "$1" = "h" ]
then
print_help
exit 0
elif [ "$1" = "help" ]
then
print_help
exit 0
elif [ $ # -lt 2 ]
then
print_help
exit 1
fi
init_acm_root_directory
# get question name and init path for all files
QUESTION_NAME = "$2"
init_path
# check if workspace is existed.
if [ ! -d $QUESTION_DIR ]; then
IS_WORKSPACE_NOT_EXIST =true
fi
# option argument command implementation
while [ "$1" ]
do
echo
if [ "$1" = "i" ]; then
init_acm_workspace
elif [ "$1" = "r" ]
then
workspace_exist_check
echo "Run code with your test data and check correctness."
select_compile_tool
compile_code
run_code
print_output
print_run_time_cost
judge_result
elif [ "$1" = "d" ]
then
workspace_exist_check
select_compile_tool
compile_code
echo "Debug your code with debug tool, default is gdb"
debug_code
else
echo "$SCRIPT_NAME does not recognize option $1."
print_help
exit 1
fi
# skip command for loop argument process now
# maybe there are some extensions in the future
shift 2
done
##################
# Main part end
##################