zoukankan      html  css  js  c++  java
  • 模拟acm测试系统

    模拟acm测试系统

    模拟acm测试系统

    1. 前言

    最近和朋友在做一些acm的训练,虽然算法功力比较薄弱,但对acm的测试系统产生
    了一点兴趣,于是就尝试使用shell脚本做了一样小型的模拟程序。
     
    运行环境主要是linux和mac。我的环境是mac。目前只支持c语言。
     
    主要功能有:
    1. 第一次运行时,会初始化设置你的项目根目录,并询问是否将脚本加入到PATH
    2. 根据用户输入的题目命名,生成题目目录,包括源文件,测试数据文件,期望数据文件
    3. 可以生成题目目录的时候,选择是否在控制台输入测试和期望数据
    4. 根据测试数据文件运行源代码,并生成结果输出文件,和期望数据文件进行比对,输出比对结果
    5. 提供运行时间的测试,毫秒级别,不过需要安装python
    6. 启动使用gdb命令调试程序
    7. 一些简单的错误处理

    优点:

    因为acm题目要求标准的输入和输出,这样导致测试的时候不好进行大数据量的测试。
    一般的做法是在源代码中加入读测试文件的代码,再在提交前再修改成标准输入这些代码,
    或是使用重定向的宏,提交前再注释这些宏。总之觉得有点麻烦。
    理想的状态是本地的代码直接原封不动的提交上去。这个脚本正是为了达到这个目的而进行实现的。

    注意:

    因为在mac环境中,c语言的编译器默认选择clang,如果没有安装,则选择gcc。
    在测试时间的功能里,是调用python来完成的,mac下没有找到系统自带的好用的时间命令。
    这次的脚本编写,也可以是看作是一次shell命令学习,挺有意思,不过工具不重要,算法的提高
    才是重点啊~~

    想试试的同学,复制代码,保存到名为acm_manage.sh的文件中,在赋上可执行权限,运行./acm_manage.sh
    就可以根据提示信息立即体验。
     
    欢迎留言讨论。
     

    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"

    # 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="-O2"
    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 -"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"
    }

    # 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 -"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 -'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
        # record end time
        RUN_END_TIME=$(python -'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."
        $DEBUG_TOOL $EXECUTABLE_FILE_PATH
    }

    # 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
            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
    ##################





     
     
     
     
    作者:btchenguang
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
     
    分类: algorithm
    标签: algorithm
  • 相关阅读:
    怎样在ASP.NET中使用VB.NET上传大文件
    准备开始研读petShop
    Documentum常见问题9—导航树的一些常见设置
    [转]CMMI 入门 基本术语:约束、假设、依赖、风险、承诺
    MyEclipse 8.6反编译插件安装
    Documentum常见问题7—为客户化Type类型Import时添加自定义属性
    【自省篇】程序员的十大技术烦恼
    Documentum常见问题10修改特定Type的显示图标
    打发时间的题目,每天做一点
    Documentum常见问题8—限制导航树上的文件夹个数
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3003219.html
Copyright © 2011-2022 走看看