模拟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
就可以根据提示信息立即体验。
 
欢迎留言讨论。
 
(4.8更新)
有一个致命错误:
之前是使用
while read -r line
do
   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."
    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分支中去。

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





 
 
 
 
 
 
 
 
 
posted @ 2013-04-06 15:42 btchenguang 阅读(2832) 评论(1) 推荐(3) 编辑
摘要: SSH(Secure SHell)使用1. 什么是SSH?2. SSH是如何工作的?3. 为什么要采用第二种方法登录?4. 本机Win 7和Virtual box虚拟机ubuntu之间的实验4.1 进行ssh的密码口令连接4.2 进行ssh的密钥连接-在Win 7下制作密钥4.2.1 使用puttygen制作密钥4.2.2 修改ubuntu系统中openssh的配置4.2.3 拷贝公钥信息到ubuntu的特定文件中 4.2.4 使用putty进行密钥登录4.3 进行ssh的密钥连接-在Ubuntu下制作密钥4.3.1 在Linux下生成密钥4.3.2 把公钥写入authorized_keys文 阅读全文
posted @ 2012-09-28 22:05 btchenguang 阅读(25611) 评论(1) 推荐(6) 编辑
摘要: 通用视图1. 前言2. 使用通用视图安全问题的题外话3. 用于显示对象内容的通用视图4. 通用视图的几种扩展用法4.1 自定义结果集的模板名4.2 增加额外的context4.3 查看结果集的子集4.4 更灵活的结果集操作4.5 利用通用视图做额外工作通用视图1. 前言回想一下,在Django中view层起到的作用是相当于controller的角色,在view中实施的动作,一般是取得请求参数,再从model中得到数据,再通过数据创建模板,返回相应响应对象。但在一些比较通用的功能中,比如显示对象列表,显示某对象信息,如果反复写这么多流程的代码,也是一件浪费时间的事,在这里,Django同样给我们 阅读全文
posted @ 2012-09-23 10:46 btchenguang 阅读(8037) 评论(0) 推荐(2) 编辑
摘要: 3. Descriptor介绍3.1 Descriptor代码示例3.2 定义3.3 Descriptor Protocol(协议)3.4 Descriptor调用方法4. 基于Descriptor实现的功能4.1 property4.2 函数和方法,绑定与非绑定4.3 super5. 结尾3. Descriptor介绍3.1 Descriptor代码示例class RevealAccess(object):"""创建一个Descriptor类,用来打印出访问它的操作信息"""def __init__(self, initval=No 阅读全文
posted @ 2012-09-18 16:18 btchenguang 阅读(7150) 评论(3) 推荐(2) 编辑
摘要: python中基于descriptor的一些概念(上)1. 前言2. 新式类与经典类2.1 内置的object对象2.2 类的方法2.2.1 静态方法2.2.2 类方法2.3 新式类(new-style class)2.3.1 __init__方法2.3.2 __new__静态方法2.4. 新式类的实例2.4.1 Property2.4.2 __slots__属性2.4.3 __getattribute__方法2.4.4 实例的方法2.5 新的对象模型2.5.1 多继承2.5.2 MRO(Method Resolution Order, 方法解析顺序)2.5.3 协作式调用父类方法python 阅读全文
posted @ 2012-09-17 17:59 btchenguang 阅读(28938) 评论(2) 推荐(14) 编辑
摘要: Model进阶用法回顾访问外键访问多对多关系更改数据库结构当处理数据库结构改变时,需要注意到几点:增加字段首先在开发环境中:再到产品环境中:删除字段删除多对多字段删除modelManager管理器给管理器添加新的方法修改返回的QuerySetModel的方法执行自定义SQL语句Model进阶用法回顾Django中的model层主要和数据库进行交互,使用数据库API对数据库进行增删改查的操作。下面将介绍关于model层更深入的用法。下面是之前创建model的代码:from django.db import modelsclass Publisher(models.Model):name = mo 阅读全文
posted @ 2012-09-09 11:05 btchenguang 阅读(26907) 评论(2) 推荐(6) 编辑
摘要: Template加载机制扩展你的模板系统创建模板库实现自定义过滤器1. 创建register变量2. 定义过滤器函数3. 注册过滤器函数实现自定义tag了解模板编译过程 创建tag实战1. 定义Node节点类,实现render方法2. 创建Compilation函数3. 注册tag4. 运行复杂的实现自定义tag的其他几种方法1. 在Node类的render函数中设置context2. 实现块作用区域的tag3. 在块作用tag中保留context内容4. 快速创建简单tag的方法5. 创建Inclusion Tag创建自定义模板加载类Template加载机制一般来说,你在你的文件系统中存入模 阅读全文
posted @ 2012-09-05 18:45 btchenguang 阅读(27876) 评论(1) 推荐(9) 编辑
摘要: 前言需要扩展Python语言的理由:创建Python扩展的步骤1. 创建应用程序代码2. 利用样板来包装代码a. 包含python的头文件b. 为每个模块的每一个函数增加一个型如PyObject* Module_func()的包装函数c. 为每个模块增加一个型如PyMethodDef ModuleM... 阅读全文
posted @ 2012-09-04 19:06 btchenguang 阅读(49517) 评论(11) 推荐(7) 编辑
摘要: HTML代码自动转义(auto-escaping)当使用模板生成HTML代码时,如果变量内容是一些影响HTML结果的字符时,那就挺危险的。例如,模板内容如下:Hello {{ name }}当name的值为:<script>alert('hello')</script>渲染后的HTML结果就是:Hello <script>alert('hello')</script>以上的代码运行的结果就是会让浏览器弹出一个javascript的警告窗口。同理,如果name的值为<b>hanks,那么结果中Hello以 阅读全文
posted @ 2012-09-03 16:49 btchenguang 阅读(4273) 评论(2) 推荐(2) 编辑
摘要: 也许,你想要自定义和扩展模板引擎,下面会介绍一些关于如何去扩展模板系统的方法,了解一下模板系统的工作原理,同时也会介绍Django模板系统中的auto-escapint功能,这是一种安全机制。复习一下模板语言的用法{# 模板tag的用法 #}{% if done %}<strong>Over</strong>{% else %}<strong>wait</strong>{% endif %}{# 模板变量的用法 #}Now is {{ nowtime }}在views.py中使用模板的时候:1. 通过模板名,获得模板对象2. 创建context对 阅读全文
posted @ 2012-09-01 16:06 btchenguang 阅读(9936) 评论(0) 推荐(2) 编辑
点击右上角即可分享
微信分享提示