From 1dce5f71e36e70c0bd169f4f987606334b720b07 Mon Sep 17 00:00:00 2001 From: rainboy Date: Wed, 24 Apr 2019 20:38:23 +0800 Subject: [PATCH 1/5] judger nodejs binding initialize :tada: --- .gitignore | 1 + bindings/NodeJS/1.cpp | 18 +++ bindings/NodeJS/binding.gyp | 36 +++++ bindings/NodeJS/judger.cc | 292 +++++++++++++++++++++++++++++++++++ bindings/NodeJS/package.json | 11 ++ 5 files changed, 358 insertions(+) create mode 100644 bindings/NodeJS/1.cpp create mode 100644 bindings/NodeJS/binding.gyp create mode 100644 bindings/NodeJS/judger.cc create mode 100644 bindings/NodeJS/package.json diff --git a/.gitignore b/.gitignore index 8358b00..0cca208 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ build/ __pycache__ *.pyc cmake-build-debug/ +.ycm_extra_conf.py diff --git a/bindings/NodeJS/1.cpp b/bindings/NodeJS/1.cpp new file mode 100644 index 0000000..e48b1fc --- /dev/null +++ b/bindings/NodeJS/1.cpp @@ -0,0 +1,18 @@ +/*----------------- +* author: Rainboy +* email: rainboylvx@qq.com +* time: 2019年 04月 24日 星期三 18:22:46 CST +* problem: online judge-_id +*----------------*/ +#include +#include +#include +#include +using namespace std; + + + +int main(){ + string a("je"); + return 0; +} diff --git a/bindings/NodeJS/binding.gyp b/bindings/NodeJS/binding.gyp new file mode 100644 index 0000000..46b92f7 --- /dev/null +++ b/bindings/NodeJS/binding.gyp @@ -0,0 +1,36 @@ +{ + "targets": [ + { + "target_name": "judgerlib", + "type": "static_library", + "sources": [ + "../../src/child.c", + "../../src/killer.c", + "../../src/logger.c", + "../../src/runner.c", + "../../src/rules/c_cpp.c", + "../../src/rules/c_cpp_file_io.c", + "../../src/rules/general.c" + ], + 'include_dirs':[ + "../../src", + "../../src/rules" + ] + }, + { + "target_name": "judger", + "sources": [ "judger.cc"], + 'include_dirs':[ + "../../src", + "../../src/rules" + ], + "libraries":[ + "-L/usr/lib","-lpthread","-lseccomp" + ], + + "dependencies": [ + "judgerlib" + ] + } + ] +} diff --git a/bindings/NodeJS/judger.cc b/bindings/NodeJS/judger.cc new file mode 100644 index 0000000..5451a94 --- /dev/null +++ b/bindings/NodeJS/judger.cc @@ -0,0 +1,292 @@ +#include "node.h" +#include +#include +#include +#include "../../src/runner.h" + +namespace demo { + + using v8::FunctionCallbackInfo; + using v8::Isolate; + using v8::Local; + using v8::Int32; + using v8::Array; + using v8::Integer; + using v8::Uint32; + using v8::Number; + using v8::Object; + using v8::String; + using v8::Value; + using v8::MaybeLocal; + using v8::Exception; + using v8::Context; + + /* 转成数字 */ + long ToNumber(Isolate * isolate,Local &args,char * key,long default_val){ + Local context= isolate->GetCurrentContext(); + Local val = args->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); + if(val->IsNullOrUndefined()) + return default_val; + else if( val->IsNumber()){ + Local num = val->ToInteger(context).ToLocalChecked(); + return num->Value(); + } + else { + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument must be Object!"))); + } + } + + char * _ToCharPTR(Local &val,Local &context,char * str_content){ + String::Utf8Value str_val( val->ToString(context).ToLocalChecked()); + strcpy(str_content,*str_val); + return str_content; + } + + /* 转成字符串 */ + char * ToCStr(Isolate * isolate,Local &args,char * key,char *default_val,char *str_content){ + + Local context= isolate->GetCurrentContext(); + Local val= args->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); + if(val->IsNullOrUndefined()) + return default_val; + else if(val->IsString()){ + _ToCharPTR(val,context,str_content); + return str_content; + } + else { + char ret[100]; + sprintf(ret,"typeof %s must be String!",key); + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,ret))); + } + } + + + /** + * 参数: + * { + * max_cpu_time + * max_real_time + * max_memory + * memory_limit_check_only ?? + * max_stack + * max_process + * max_output_size + * exe_path + * input_path + * output_path + * error_path + * args:[] + * env:[] + * } + * 返回值 + * { + * cpu_time + * real_time + * memory + * signal + * exit_code + * result + * error: + * } + */ + + void Method(const FunctionCallbackInfo& args) + { + Isolate* isolate = args.GetIsolate(); + Local context= isolate->GetCurrentContext(); + + if( !args[0]->IsObject()){ + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument must be Object!"))); + } + + Local argument = args[0]->ToObject(); + + /* 参数 */ + struct config _config; + + + /* max_cpu_time */ + _config.max_cpu_time = ToNumber(isolate,argument,(char *)"max_cpu_time",UNLIMITED); + + /* max_real_time */ + _config.max_real_time = ToNumber(isolate,argument,(char *)"max_real_time",UNLIMITED); + + /* max_memory */ + _config.max_memory = ToNumber(isolate,argument,(char *)"max_memory",UNLIMITED); + + /* memory_limit_check_only */ + _config.memory_limit_check_only = + ToNumber(isolate,argument,(char *)"memory_limit_check_only",0); + + if(_config.memory_limit_check_only) + _config.memory_limit_check_only = 1; + + + /* max_stack */ + _config.max_stack = + ToNumber(isolate,argument,(char *)"max_stack",16*1024*1024); //默认16mb + + /* max_process_number */ + _config.max_process_number = + ToNumber(isolate,argument,(char *)"max_process_number",UNLIMITED); + + /* max_output_size */ + _config.max_output_size = + ToNumber(isolate,argument,(char *)"max_output_size",UNLIMITED); + + /* input_path */ + char input_path[255]; + _config.input_path = ToCStr(isolate,argument,(char *)"input_path",(char *)"/dev/stdin",input_path); + + /* output_path */ + char output_path[255]; + _config.output_path= ToCStr(isolate,argument,(char *)"output_path",(char *)"/dev/stdout",output_path); + + /* error_path */ + char error_path[255]; + _config.error_path= ToCStr(isolate,argument,(char *)"error_path",(char *)"/dev/stderr",error_path); + + /* exe_path */ + char exe_path[255]; + _config.exe_path = ToCStr(isolate,argument,(char *)"exe_path",(char *)"/bin/ls",exe_path); + + /* args */ + _config.args[0] = _config.exe_path; + + char _args[100][255]; + Local margs= argument->Get(context,String::NewFromUtf8(isolate,"args")).ToLocalChecked(); + if( margs->IsNullOrUndefined()){ + _config.args[1] = NULL; + } + else if( margs->IsArray()){ + Local args = margs.As(); + int len = args->Length(); + int i; + for(i=0;i in = args->Get(i); + if(in->IsString()){ + _ToCharPTR(in,context,_args[i]); + _config.args[i+1] = _args[i]; + } + else + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"the content of args must be string!"))); + } + _config.args[len+1] = NULL; + } + else { //not array + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument.args must be Array!"))); + } + + char _env[100][255]; + Local menv= argument->Get(context,String::NewFromUtf8(isolate,"env")).ToLocalChecked(); + if( menv->IsNullOrUndefined()){ + _config.env[0] = NULL; + } + else if( margs->IsArray()){ + Local env = menv.As(); + int len = env->Length(); + int i; + for(i=0;i in = env->Get(i); + if(in->IsString()){ + _ToCharPTR(in,context,_env[i]); + _config.env[i] = _env[i]; + } + else + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"the content of env must be string!"))); + } + _config.env[len] = NULL; + } + else { //not array + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument.env must be Array!"))); + } + + /* log_path */ + char log_path[255]; + _config.log_path= ToCStr(isolate,argument,(char *)"log_path",(char *)"judger.log",log_path); + + /* seccomp_rule_name */ + char seccomp_rule_name[255]; + _config.seccomp_rule_name = ToCStr(isolate,argument,(char *)"seccomp_rule_name",NULL,seccomp_rule_name); + + /* uid */ + _config.uid = + ToNumber(isolate,argument,(char *)"uid",65534); + /* gid */ + _config.gid = + ToNumber(isolate,argument,(char *)"gid",65534); + + + +/* + * printf("max_cpu_time %d\n",_config.max_cpu_time); + * printf("max_real_time %d\n",_config.max_real_time); + * printf("max_memory %ld\n",_config.max_memory); + * printf("max_stack %ld\n",_config.max_stack); + * printf("max_process_number %d\n",_config.max_process_number); + * printf("max_output_size %ld\n",_config.max_output_size); + * printf("%s\n",_config.input_path); + * printf("%s\n",_config.output_path); + * printf("%s\n",_config.error_path); + * + * int idx = 0; + * while( _config.args[idx]!= NULL){ + * printf("args[%d] = %s\n",idx,_config.args[idx]); + * idx++; + * } + * + * idx = 0; + * while( _config.env[idx]!= NULL){ + * printf("env[%d] = %s\n",idx,_config.env[idx]); + * idx++; + * } + * + * printf("log_path %s\n",_config.log_path); + * printf("seccomp_rule_name %s\n",_config.seccomp_rule_name); + * printf("uid %d\n",_config.uid); + * printf("gid %d\n",_config.gid); + */ + + struct result _result = {0, 0, 0, 0, 0, 0, 0}; + run(&_config, &_result); + + /* + *printf("{\n" + * " \"cpu_time\": %d,\n" + * " \"real_time\": %d,\n" + * " \"memory\": %ld,\n" + * " \"signal\": %d,\n" + * " \"exit_code\": %d,\n" + * " \"error\": %d,\n" + * " \"result\": %d\n" + * "}", + * _result.cpu_time, + * _result.real_time, + * _result.memory, + * _result.signal, + * _result.exit_code, + * _result.error, + * _result.result); + */ + + /* 返回值 */ + Local result = Object::New(isolate); + result->Set(String::NewFromUtf8(isolate,"cpu_time"),Integer::New(isolate,_result.cpu_time)); + result->Set(String::NewFromUtf8(isolate,"real_time"),Integer::New(isolate,_result.real_time)); + result->Set(String::NewFromUtf8(isolate,"memory"),Integer::New(isolate,_result.memory)); + result->Set(String::NewFromUtf8(isolate,"signal"),Integer::New(isolate,_result.signal)); + result->Set(String::NewFromUtf8(isolate,"exit_code"),Integer::New(isolate,_result.exit_code)); + result->Set(String::NewFromUtf8(isolate,"result"),Integer::New(isolate,_result.result)); + result->Set(String::NewFromUtf8(isolate,"error"),Integer::New(isolate,_result.error)); + + args.GetReturnValue().Set(result); + } + + void init(Local exports) + { + NODE_SET_METHOD(exports, "run", Method); + } + + NODE_MODULE(addon, init) + +} diff --git a/bindings/NodeJS/package.json b/bindings/NodeJS/package.json new file mode 100644 index 0000000..1a7017f --- /dev/null +++ b/bindings/NodeJS/package.json @@ -0,0 +1,11 @@ +{ + "name": "judger", + "version": "1.0.0", + "description": "judger for online system", + "main": "./build/Release/judger.node", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "virusdefender,rainboy", + "license": "GPL" +} From d54b84ab588c1711cd4af3c8c4b5951bdd895a53 Mon Sep 17 00:00:00 2001 From: rainboy Date: Fri, 26 Apr 2019 10:50:59 +0800 Subject: [PATCH 2/5] use popen to run libjudger.so :sparkles: --- .gitignore | 1 + bindings/NodeJS/1.cpp | 18 --- bindings/NodeJS/binding.gyp | 30 +---- bindings/NodeJS/judger.cc | 233 +++++++++++++++-------------------- bindings/NodeJS/package.json | 1 + 5 files changed, 104 insertions(+), 179 deletions(-) delete mode 100644 bindings/NodeJS/1.cpp diff --git a/.gitignore b/.gitignore index 0cca208..e1c1505 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ __pycache__ *.pyc cmake-build-debug/ .ycm_extra_conf.py +node_modules/ diff --git a/bindings/NodeJS/1.cpp b/bindings/NodeJS/1.cpp deleted file mode 100644 index e48b1fc..0000000 --- a/bindings/NodeJS/1.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/*----------------- -* author: Rainboy -* email: rainboylvx@qq.com -* time: 2019年 04月 24日 星期三 18:22:46 CST -* problem: online judge-_id -*----------------*/ -#include -#include -#include -#include -using namespace std; - - - -int main(){ - string a("je"); - return 0; -} diff --git a/bindings/NodeJS/binding.gyp b/bindings/NodeJS/binding.gyp index 46b92f7..99abf0e 100644 --- a/bindings/NodeJS/binding.gyp +++ b/bindings/NodeJS/binding.gyp @@ -1,36 +1,8 @@ { "targets": [ - { - "target_name": "judgerlib", - "type": "static_library", - "sources": [ - "../../src/child.c", - "../../src/killer.c", - "../../src/logger.c", - "../../src/runner.c", - "../../src/rules/c_cpp.c", - "../../src/rules/c_cpp_file_io.c", - "../../src/rules/general.c" - ], - 'include_dirs':[ - "../../src", - "../../src/rules" - ] - }, { "target_name": "judger", - "sources": [ "judger.cc"], - 'include_dirs':[ - "../../src", - "../../src/rules" - ], - "libraries":[ - "-L/usr/lib","-lpthread","-lseccomp" - ], - - "dependencies": [ - "judgerlib" - ] + "sources": [ "judger.cc"] } ] } diff --git a/bindings/NodeJS/judger.cc b/bindings/NodeJS/judger.cc index 5451a94..0bfcb63 100644 --- a/bindings/NodeJS/judger.cc +++ b/bindings/NodeJS/judger.cc @@ -1,13 +1,17 @@ #include "node.h" -#include -#include -#include -#include "../../src/runner.h" +#include +#include +#include +#include +#include +#include + namespace demo { using v8::FunctionCallbackInfo; using v8::Isolate; + using v8::JSON; using v8::Local; using v8::Int32; using v8::Array; @@ -22,14 +26,18 @@ namespace demo { using v8::Context; /* 转成数字 */ - long ToNumber(Isolate * isolate,Local &args,char * key,long default_val){ + void ToNumber(Isolate * isolate,Local &args,char * key,std::string &str){ Local context= isolate->GetCurrentContext(); - Local val = args->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); + Local val = args->ToObject()->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); if(val->IsNullOrUndefined()) - return default_val; + return ; else if( val->IsNumber()){ Local num = val->ToInteger(context).ToLocalChecked(); - return num->Value(); + str+=" --"; + str+=std::string(key); + str+="="; + str+=std::to_string(num->Value()); + return ; } else { isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument must be Object!"))); @@ -37,21 +45,30 @@ namespace demo { } char * _ToCharPTR(Local &val,Local &context,char * str_content){ - String::Utf8Value str_val( val->ToString(context).ToLocalChecked()); - strcpy(str_content,*str_val); - return str_content; + String::Utf8Value str_val( val->ToString(context).ToLocalChecked()); + strcpy(str_content,*str_val); + return str_content; } /* 转成字符串 */ - char * ToCStr(Isolate * isolate,Local &args,char * key,char *default_val,char *str_content){ + void ToCStr(Isolate * isolate,Local &args,char * key,std::string &str){ + char str_content[255]={0}; Local context= isolate->GetCurrentContext(); - Local val= args->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); + Local val; + if(args->IsObject()) + val = args->ToObject()->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); + else + val = args; if(val->IsNullOrUndefined()) - return default_val; + return; else if(val->IsString()){ _ToCharPTR(val,context,str_content); - return str_content; + str+=" --"; + str+=std::string(key); + str+="="; + str+=std::string(str_content); + return ; } else { char ret[100]; @@ -95,68 +112,55 @@ namespace demo { Isolate* isolate = args.GetIsolate(); Local context= isolate->GetCurrentContext(); + std::string _args = "/usr/lib/judger/libjudger.so"; + if( !args[0]->IsObject()){ isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument must be Object!"))); } - Local argument = args[0]->ToObject(); - - /* 参数 */ - struct config _config; + Local argument = args[0]; /* max_cpu_time */ - _config.max_cpu_time = ToNumber(isolate,argument,(char *)"max_cpu_time",UNLIMITED); + ToNumber(isolate,argument,(char *)"max_cpu_time",_args); /* max_real_time */ - _config.max_real_time = ToNumber(isolate,argument,(char *)"max_real_time",UNLIMITED); + ToNumber(isolate,argument,(char *)"max_real_time",_args); /* max_memory */ - _config.max_memory = ToNumber(isolate,argument,(char *)"max_memory",UNLIMITED); - - /* memory_limit_check_only */ - _config.memory_limit_check_only = - ToNumber(isolate,argument,(char *)"memory_limit_check_only",0); + ToNumber(isolate,argument,(char *)"max_memory",_args); + + /* memory_limit_check_only */ + ToNumber(isolate,argument,(char *)"memory_limit_check_only",_args); - if(_config.memory_limit_check_only) - _config.memory_limit_check_only = 1; /* max_stack */ - _config.max_stack = - ToNumber(isolate,argument,(char *)"max_stack",16*1024*1024); //默认16mb + ToNumber(isolate,argument,(char *)"max_stack",_args); //默认16mb /* max_process_number */ - _config.max_process_number = - ToNumber(isolate,argument,(char *)"max_process_number",UNLIMITED); + ToNumber(isolate,argument,(char *)"max_process_number",_args); /* max_output_size */ - _config.max_output_size = - ToNumber(isolate,argument,(char *)"max_output_size",UNLIMITED); + ToNumber(isolate,argument,(char *)"max_output_size",_args); /* input_path */ - char input_path[255]; - _config.input_path = ToCStr(isolate,argument,(char *)"input_path",(char *)"/dev/stdin",input_path); + ToCStr(isolate,argument,(char *)"input_path",_args); /* output_path */ - char output_path[255]; - _config.output_path= ToCStr(isolate,argument,(char *)"output_path",(char *)"/dev/stdout",output_path); + ToCStr(isolate,argument,(char *)"output_path",_args); /* error_path */ - char error_path[255]; - _config.error_path= ToCStr(isolate,argument,(char *)"error_path",(char *)"/dev/stderr",error_path); + ToCStr(isolate,argument,(char *)"error_path",_args); /* exe_path */ - char exe_path[255]; - _config.exe_path = ToCStr(isolate,argument,(char *)"exe_path",(char *)"/bin/ls",exe_path); + ToCStr(isolate,argument,(char *)"exe_path",_args); /* args */ - _config.args[0] = _config.exe_path; - char _args[100][255]; - Local margs= argument->Get(context,String::NewFromUtf8(isolate,"args")).ToLocalChecked(); + Local margs= argument->ToObject()->Get(context,String::NewFromUtf8(isolate,"args")).ToLocalChecked(); if( margs->IsNullOrUndefined()){ - _config.args[1] = NULL; + NULL; } else if( margs->IsArray()){ Local args = margs.As(); @@ -164,23 +168,16 @@ namespace demo { int i; for(i=0;i in = args->Get(i); - if(in->IsString()){ - _ToCharPTR(in,context,_args[i]); - _config.args[i+1] = _args[i]; - } - else - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"the content of args must be string!"))); + ToCStr(isolate,in,(char *)"args",_args); } - _config.args[len+1] = NULL; } else { //not array - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument.args must be Array!"))); + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"args must be a list"))); } - char _env[100][255]; - Local menv= argument->Get(context,String::NewFromUtf8(isolate,"env")).ToLocalChecked(); + Local menv= argument->ToObject()->Get(context,String::NewFromUtf8(isolate,"env")).ToLocalChecked(); if( menv->IsNullOrUndefined()){ - _config.env[0] = NULL; + NULL; } else if( margs->IsArray()){ Local env = menv.As(); @@ -188,103 +185,75 @@ namespace demo { int i; for(i=0;i in = env->Get(i); - if(in->IsString()){ - _ToCharPTR(in,context,_env[i]); - _config.env[i] = _env[i]; - } - else - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"the content of env must be string!"))); + ToCStr(isolate,in,(char *)"env",_args); } - _config.env[len] = NULL; } else { //not array - isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument.env must be Array!"))); + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"env must be a list"))); } /* log_path */ - char log_path[255]; - _config.log_path= ToCStr(isolate,argument,(char *)"log_path",(char *)"judger.log",log_path); + ToCStr(isolate,argument,(char *)"log_path",_args); /* seccomp_rule_name */ char seccomp_rule_name[255]; - _config.seccomp_rule_name = ToCStr(isolate,argument,(char *)"seccomp_rule_name",NULL,seccomp_rule_name); + ToCStr(isolate,argument,(char *)"seccomp_rule_name",_args); /* uid */ - _config.uid = - ToNumber(isolate,argument,(char *)"uid",65534); + ToNumber(isolate,argument,(char *)"uid",_args); /* gid */ - _config.gid = - ToNumber(isolate,argument,(char *)"gid",65534); + ToNumber(isolate,argument,(char *)"gid",_args); + char buf[255]; + FILE *_result_f; + std::string result; + if(( _result_f = popen(_args.c_str(),"r"))==NULL) + isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"run /usr/lib/judger/libjudger.so failed!"))); + while(fgets(buf,sizeof(buf),_result_f)){ + result += std::string(buf); + } + pclose(_result_f); -/* - * printf("max_cpu_time %d\n",_config.max_cpu_time); - * printf("max_real_time %d\n",_config.max_real_time); - * printf("max_memory %ld\n",_config.max_memory); - * printf("max_stack %ld\n",_config.max_stack); - * printf("max_process_number %d\n",_config.max_process_number); - * printf("max_output_size %ld\n",_config.max_output_size); - * printf("%s\n",_config.input_path); - * printf("%s\n",_config.output_path); - * printf("%s\n",_config.error_path); - * - * int idx = 0; - * while( _config.args[idx]!= NULL){ - * printf("args[%d] = %s\n",idx,_config.args[idx]); - * idx++; - * } - * - * idx = 0; - * while( _config.env[idx]!= NULL){ - * printf("env[%d] = %s\n",idx,_config.env[idx]); - * idx++; - * } - * - * printf("log_path %s\n",_config.log_path); - * printf("seccomp_rule_name %s\n",_config.seccomp_rule_name); - * printf("uid %d\n",_config.uid); - * printf("gid %d\n",_config.gid); - */ + const char *pr = result.c_str(); + int len = strlen(pr); + for(len--;len>=0;len--){ + if( pr[len] == '{') + break; + } - struct result _result = {0, 0, 0, 0, 0, 0, 0}; - run(&_config, &_result); + printf("%*.*s",len,len,pr); //输出 程序的输出 - /* - *printf("{\n" - * " \"cpu_time\": %d,\n" - * " \"real_time\": %d,\n" - * " \"memory\": %ld,\n" - * " \"signal\": %d,\n" - * " \"exit_code\": %d,\n" - * " \"error\": %d,\n" - * " \"result\": %d\n" - * "}", - * _result.cpu_time, - * _result.real_time, - * _result.memory, - * _result.signal, - * _result.exit_code, - * _result.error, - * _result.result); - */ - - /* 返回值 */ - Local result = Object::New(isolate); - result->Set(String::NewFromUtf8(isolate,"cpu_time"),Integer::New(isolate,_result.cpu_time)); - result->Set(String::NewFromUtf8(isolate,"real_time"),Integer::New(isolate,_result.real_time)); - result->Set(String::NewFromUtf8(isolate,"memory"),Integer::New(isolate,_result.memory)); - result->Set(String::NewFromUtf8(isolate,"signal"),Integer::New(isolate,_result.signal)); - result->Set(String::NewFromUtf8(isolate,"exit_code"),Integer::New(isolate,_result.exit_code)); - result->Set(String::NewFromUtf8(isolate,"result"),Integer::New(isolate,_result.result)); - result->Set(String::NewFromUtf8(isolate,"error"),Integer::New(isolate,_result.error)); - - args.GetReturnValue().Set(result); + MaybeLocal mres = JSON::Parse(isolate,String::NewFromUtf8(isolate,pr+len)); + args.GetReturnValue().Set(mres.ToLocalChecked()); } void init(Local exports) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); NODE_SET_METHOD(exports, "run", Method); + exports->Set( String::NewFromUtf8(isolate,"UNLIMITED"),Integer::New(isolate, -1)); + exports->Set(String::NewFromUtf8(isolate,"VERSION"),Integer::New(isolate, 0x020101)); + + exports->Set(String::NewFromUtf8(isolate,"RESULT_SUCCESS"),Integer::New(isolate, 0)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_WRONG_ANSWER"),Integer::New(isolate, -1)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_CPU_TIME_LIMIT_EXCEEDED"),Integer::New(isolate, 1)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_REAL_TIME_LIMIT_EXCEEDED"),Integer::New(isolate, 2)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_MEMORY_LIMIT_EXCEEDED"),Integer::New(isolate, 3)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_RUNTIME_ERROR"),Integer::New(isolate, 4)); + exports->Set(String::NewFromUtf8(isolate,"RESULT_SYSTEM_ERROR"),Integer::New(isolate, 5)); + + exports->Set(String::NewFromUtf8(isolate,"ERROR_INVALID_CONFIG"),Integer::New(isolate, -1)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_FORK_FAILED"),Integer::New(isolate, -2)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_PTHREAD_FAILED"),Integer::New(isolate, -3)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_WAIT_FAILED"),Integer::New(isolate, -4)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_ROOT_REQUIRED"),Integer::New(isolate, -5)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_LOAD_SECCOMP_FAILED"),Integer::New(isolate, -6)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_SETRLIMIT_FAILED"),Integer::New(isolate, -7)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_DUP2_FAILED"),Integer::New(isolate, -8)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_SETUID_FAILED"),Integer::New(isolate, -9)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_EXECVE_FAILED"),Integer::New(isolate, -10)); + exports->Set(String::NewFromUtf8(isolate,"ERROR_SPJ_ERROR"),Integer::New(isolate, -11)); } NODE_MODULE(addon, init) diff --git a/bindings/NodeJS/package.json b/bindings/NodeJS/package.json index 1a7017f..1e99a7a 100644 --- a/bindings/NodeJS/package.json +++ b/bindings/NodeJS/package.json @@ -4,6 +4,7 @@ "description": "judger for online system", "main": "./build/Release/judger.node", "scripts": { + "build":"node-gyp rebuild", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "virusdefender,rainboy", From d6a31515062e03dbe0161755e6b72a230c99dfc1 Mon Sep 17 00:00:00 2001 From: rainboy Date: Fri, 26 Apr 2019 15:09:43 +0800 Subject: [PATCH 3/5] upgrade code format :zap: --- bindings/NodeJS/binding.gyp | 3 +- bindings/NodeJS/judger.cc | 85 ++++++++++++------------------------ bindings/NodeJS/package.json | 24 +++++----- 3 files changed, 45 insertions(+), 67 deletions(-) diff --git a/bindings/NodeJS/binding.gyp b/bindings/NodeJS/binding.gyp index 99abf0e..63d2da1 100644 --- a/bindings/NodeJS/binding.gyp +++ b/bindings/NodeJS/binding.gyp @@ -2,7 +2,8 @@ "targets": [ { "target_name": "judger", - "sources": [ "judger.cc"] + 'cflags_cc!': [ '-std=c++11','-O2' ], + "sources": [ "judger.cc"], } ] } diff --git a/bindings/NodeJS/judger.cc b/bindings/NodeJS/judger.cc index 0bfcb63..a386e1b 100644 --- a/bindings/NodeJS/judger.cc +++ b/bindings/NodeJS/judger.cc @@ -26,21 +26,22 @@ namespace demo { using v8::Context; /* 转成数字 */ - void ToNumber(Isolate * isolate,Local &args,char * key,std::string &str){ + bool ToNumber(Isolate * isolate,Local &args,char * key,std::string &str){ Local context= isolate->GetCurrentContext(); Local val = args->ToObject()->Get(context,String::NewFromUtf8(isolate,key)).ToLocalChecked(); if(val->IsNullOrUndefined()) - return ; + return true; else if( val->IsNumber()){ Local num = val->ToInteger(context).ToLocalChecked(); str+=" --"; str+=std::string(key); str+="="; str+=std::to_string(num->Value()); - return ; + return true; } else { isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"typeof argument must be Object!"))); + return false; } } @@ -51,7 +52,7 @@ namespace demo { } /* 转成字符串 */ - void ToCStr(Isolate * isolate,Local &args,char * key,std::string &str){ + bool ToCStr(Isolate * isolate,Local &args,char * key,std::string &str){ char str_content[255]={0}; Local context= isolate->GetCurrentContext(); @@ -61,19 +62,20 @@ namespace demo { else val = args; if(val->IsNullOrUndefined()) - return; + return true; else if(val->IsString()){ _ToCharPTR(val,context,str_content); str+=" --"; str+=std::string(key); str+="="; str+=std::string(str_content); - return ; + return true; } else { char ret[100]; sprintf(ret,"typeof %s must be String!",key); isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,ret))); + return false; } } @@ -120,47 +122,23 @@ namespace demo { Local argument = args[0]; + std::string int_vars[] = { "max_cpu_time","max_real_time","max_memory","memory_limit_check_only","max_stack","max_process_number","max_output_size","uid","gid"}; + std::string str_vars[] = { "input_path","output_path","error_path","exe_path","log_path","seccomp_rule_name"}; - /* max_cpu_time */ - ToNumber(isolate,argument,(char *)"max_cpu_time",_args); + for( auto var :int_vars){ + if( ! ToNumber(isolate,argument,(char *)var.c_str(),_args)) + return; + } - /* max_real_time */ - ToNumber(isolate,argument,(char *)"max_real_time",_args); - - /* max_memory */ - ToNumber(isolate,argument,(char *)"max_memory",_args); - - /* memory_limit_check_only */ - ToNumber(isolate,argument,(char *)"memory_limit_check_only",_args); - - - - /* max_stack */ - ToNumber(isolate,argument,(char *)"max_stack",_args); //默认16mb - - /* max_process_number */ - ToNumber(isolate,argument,(char *)"max_process_number",_args); - - /* max_output_size */ - ToNumber(isolate,argument,(char *)"max_output_size",_args); - - /* input_path */ - ToCStr(isolate,argument,(char *)"input_path",_args); - - /* output_path */ - ToCStr(isolate,argument,(char *)"output_path",_args); - - /* error_path */ - ToCStr(isolate,argument,(char *)"error_path",_args); - - /* exe_path */ - ToCStr(isolate,argument,(char *)"exe_path",_args); + for( auto var : str_vars){ + if( !ToCStr(isolate,argument,(char *)var.c_str(),_args)) + return; + } /* args */ - Local margs= argument->ToObject()->Get(context,String::NewFromUtf8(isolate,"args")).ToLocalChecked(); if( margs->IsNullOrUndefined()){ - NULL; + ; } else if( margs->IsArray()){ Local args = margs.As(); @@ -168,16 +146,18 @@ namespace demo { int i; for(i=0;i in = args->Get(i); - ToCStr(isolate,in,(char *)"args",_args); + if( !ToCStr(isolate,in,(char *)"args",_args)) + return; } } else { //not array isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"args must be a list"))); + return; } Local menv= argument->ToObject()->Get(context,String::NewFromUtf8(isolate,"env")).ToLocalChecked(); if( menv->IsNullOrUndefined()){ - NULL; + ; } else if( margs->IsArray()){ Local env = menv.As(); @@ -185,24 +165,16 @@ namespace demo { int i; for(i=0;i in = env->Get(i); - ToCStr(isolate,in,(char *)"env",_args); + if(!ToCStr(isolate,in,(char *)"env",_args)) + return; + } } else { //not array isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate,"env must be a list"))); + return; } - /* log_path */ - ToCStr(isolate,argument,(char *)"log_path",_args); - - /* seccomp_rule_name */ - char seccomp_rule_name[255]; - ToCStr(isolate,argument,(char *)"seccomp_rule_name",_args); - - /* uid */ - ToNumber(isolate,argument,(char *)"uid",_args); - /* gid */ - ToNumber(isolate,argument,(char *)"gid",_args); char buf[255]; @@ -222,7 +194,8 @@ namespace demo { break; } - printf("%*.*s",len,len,pr); //输出 程序的输出 + if(len) + printf("%*.*s",len,len,pr); //输出 程序的输出 MaybeLocal mres = JSON::Parse(isolate,String::NewFromUtf8(isolate,pr+len)); args.GetReturnValue().Set(mres.ToLocalChecked()); diff --git a/bindings/NodeJS/package.json b/bindings/NodeJS/package.json index 1e99a7a..6390aaf 100644 --- a/bindings/NodeJS/package.json +++ b/bindings/NodeJS/package.json @@ -1,12 +1,16 @@ { - "name": "judger", - "version": "1.0.0", - "description": "judger for online system", - "main": "./build/Release/judger.node", - "scripts": { - "build":"node-gyp rebuild", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "virusdefender,rainboy", - "license": "GPL" + "name": "judger", + "version": "1.0.0", + "description": "judger for online system", + "main": "./build/Release/judger.node", + "scripts": { + "build": "node-gyp rebuild", + "test": "mocha -t 5000 ../../tests/Nodejs_and_core" + }, + "author": "virusdefender,rainboy", + "license": "GPL", + "devDependencies": { + "mocha": "^6.1.4", + "node-gyp": "^4.0.0" + } } From 0aec31f8eace31b158ef5bb4a3ab9117520f036c Mon Sep 17 00:00:00 2001 From: rainboy Date: Fri, 26 Apr 2019 15:28:35 +0800 Subject: [PATCH 4/5] add nodejs test 1 :white_check_mark: --- bindings/NodeJS/package.json | 2 +- tests/Nodejs_and_core/base.js | 71 +++++++ tests/Nodejs_and_core/test_integration.js | 245 ++++++++++++++++++++++ 3 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 tests/Nodejs_and_core/base.js create mode 100644 tests/Nodejs_and_core/test_integration.js diff --git a/bindings/NodeJS/package.json b/bindings/NodeJS/package.json index 6390aaf..65bb06f 100644 --- a/bindings/NodeJS/package.json +++ b/bindings/NodeJS/package.json @@ -5,7 +5,7 @@ "main": "./build/Release/judger.node", "scripts": { "build": "node-gyp rebuild", - "test": "mocha -t 5000 ../../tests/Nodejs_and_core" + "test": "sudo ./node_modules/.bin/mocha -t 5000 ../../tests/Nodejs_and_core" }, "author": "virusdefender,rainboy", "license": "GPL", diff --git a/tests/Nodejs_and_core/base.js b/tests/Nodejs_and_core/base.js new file mode 100644 index 0000000..e8a94f0 --- /dev/null +++ b/tests/Nodejs_and_core/base.js @@ -0,0 +1,71 @@ +const fs = require("fs") +const path = require("path") +const execSync = require("child_process").execSync + +var out_base_path = '/tmp/judge' +var compile_base_path = "" +var compile_c = function(name){ + out_path =path.join(out_base_path,'main') + execSync(`gcc -o ${out_path} ${path.join(this.compile_base_path,name)}`) + return out_path +} + +var compile_cpp = function(name){ + out_path =path.join(out_base_path,'main') + execSync(`g++ -o ${out_path} ${path.join(this.compile_base_path,name)}`) + return out_path +} + +var make_input = content =>{ + fs.writeFileSync('/tmp/judge/input',content,{'encoding':'utf-8'}) + return '/tmp/judge/input' +} + +var read_input = read_path => { + return fs.readFileSync(read_path,{'encoding':'utf-8'}) +} + +var output_path = ()=>{ + return '/tmp/judge/output' +} + +var error_path = ()=>{ + return '/tmp/judge/error' +} + +var mkdir = _path => { + execSync(`mkdir -p ${_path}`) +} + +var rmdir = _path => { + execSync(`rm -r ${_path}`) +} + +var baseconfig = ()=>{ + return { + max_cpu_time:1000, + max_real_time:8000, + max_memory:128*1024*1024, + max_stack:128*1024*1024, + max_process_number:8, + max_output_size:128*1024*1024, + exe_path:"/dev/null", + input_path:"/dev/null", + output_path:"/dev/null", + error_path:"/dev/null", + args:[], + env:[], + log_path:"/dev/null", + seccomp_rule_name:null, + gid:0, + uid:0, + info:false, + debug:false, + } +} + +module.exports = { + compile_c,compile_cpp,make_input,read_input,mkdir,rmdir,baseconfig,compile_base_path, + output_path, + error_path +} diff --git a/tests/Nodejs_and_core/test_integration.js b/tests/Nodejs_and_core/test_integration.js new file mode 100644 index 0000000..2a78dff --- /dev/null +++ b/tests/Nodejs_and_core/test_integration.js @@ -0,0 +1,245 @@ +const assert = require("assert") +const path = require("path") +var judger = require("../../bindings/NodeJS") +var base = require('./base.js') +var signal = require("os").constants.signals + + +var compile_base_path = base.compile_base_path = path.join(__dirname,"../test_src","integration") + + + +describe('#常用测试',function(){ + + + before( ()=>{ + //mkdir /tmp/judge + base.mkdir('/tmp/judge') + }) + after( ()=>{ + base.rmdir('/tmp/judge') + }) + + it('args must be a list 1',()=>{ + assert.throws(()=>judger.run({args:{},output_path:"/dev/null"}),/args must be a list$/) + }) + + it('args must be a list 2',()=>{ + assert.throws(()=>judger.run({args:"1234",output_path:"/dev/null"}),/args must be a list$/) + }) + + it('test_env_must_be_list',()=>{ + assert.throws(()=>judger.run({env:"1234",output_path:"/dev/null"}),/env must be a list$/) + }) + it('test_seccomp_rule_can_be_none',()=>{ + let ret = judger.run({ output_path:"/dev/null",exe_path:"/bin/ls",args:["/"],env:["a=b"] }) + assert.strictEqual(ret.result,0) + }) + + it('test normal.c',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("normal.c") + config["input_path"] = base.make_input("judger_test") + config["output_path"] = config["error_path"] = base.output_path() + let ret = judger.run(config) + let output = "judger_test\nHello world" + let content = base.read_input( base.output_path()) + assert.strictEqual(content,output) + }) + + it('test math.c' ,()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("math.c") + config["output_path"] = config["error_path"] = base.output_path() + let ret = judger.run(config) + assert.strictEqual(ret.result,judger.RESULT_SUCCESS) + let content = base.read_input( base.output_path()) + assert.strictEqual(content,"abs 1024") + }) + + it('test_args',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("args.c") + config["args"] = ["test", "hehe", "000"] + config["output_path"] = config["error_path"] = base.output_path() + + let result = judger.run(config) + let output = "argv[0]: /tmp/judge/main\nargv[1]: test\nargv[2]: hehe\nargv[3]: 000\n" + assert.strictEqual(result.result, judger.RESULT_SUCCESS) + assert.strictEqual(output, base.read_input(config["output_path"])) + + }) + + it('test env',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("env.c") + config["output_path"] = config["error_path"] = base.output_path() + config["env"] = ["env=judger_test","test=judger"] + let result = judger.run(config) + let output = "judger_test\njudger\n" + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + assert.strictEqual(output, base.read_input(config["output_path"])) + + }) + + it('test real time',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("sleep.c") + config["max_real_time"] = 3 + let result = judger.run(config) + //assert.strictEqual(result["result"], judger.RESULT_REAL_TIME_LIMIT_EXCEEDED) + assert.strictEqual(result["signal"], signal.SIGKILL) + assert.ok(result["real_time"] >= config["max_real_time"]) + + }) + + it('test cpu time',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("while1.c") + let result= judger.run(config) + + assert.strictEqual(result["result"], judger.RESULT_CPU_TIME_LIMIT_EXCEEDED) + assert.strictEqual(result["signal"], signal.SIGKILL) + assert.ok(result["cpu_time"] >= config["max_cpu_time"]) + + }) + + it('test memory1',()=>{ + let config = base.baseconfig() + config["max_memory"] = 64 * 1024 * 1024 + config["exe_path"] = base.compile_c("memory1.c") + let result= judger.run(config) + //# malloc succeeded + assert.ok(result["memory"] > 80 * 1024 * 1024) + assert.strictEqual(result["result"], judger.RESULT_MEMORY_LIMIT_EXCEEDED) + }) + + it('test memory2',()=>{ + let config = base.baseconfig() + config["max_memory"] = 64 * 1024 * 1024 + config["exe_path"] = base.compile_c("memory2.c") + let result= judger.run(config) + //# malloc failed, return 1 + assert.strictEqual(result["exit_code"], 1) + //# malloc failed, so it should use a little memory + assert.ok(result["memory"] < 12 * 1024 * 1024) + assert.strictEqual(result["result"], judger.RESULT_RUNTIME_ERROR) + }) + + it('test memory3',()=>{ + let config = base.baseconfig() + config["max_memory"] = 512 * 1024 * 1024 + config["exe_path"] = base.compile_c("memory3.c") + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + assert.ok(result["memory"] >= 102400000 * 4) + }) + it('test re1',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("re1.c") + let result= judger.run(config) + //# re1.c return 25 + assert.strictEqual(result["exit_code"], 25) + + }) + it('test re2',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("re2.c") + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_RUNTIME_ERROR) + assert.strictEqual(result["signal"], signal.SIGSEGV) + }) + it('test child proc cpu time limit',()=>{ + let config = base.baseconfig() + + config["exe_path"] = base.compile_c("child_proc_cpu_time_limit.c") + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_CPU_TIME_LIMIT_EXCEEDED) + + }) + it('test child proc real time limit',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("child_proc_real_time_limit.c") + config["max_real_time"] = 3 + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_REAL_TIME_LIMIT_EXCEEDED) + assert.strictEqual(result["signal"], signal.SIGKILL) + }) + it('test stdout and stderr',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("stdout_stderr.c") + config["output_path"] = config["error_path"] = base.output_path() + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + let output = "stderr\n+++++++++++++++\n--------------\nstdout\n" + assert.strictEqual(output, base.read_input(config["output_path"])) + }) + it('test uid and gid',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("uid_gid.c") + config["output_path"] = config["error_path"] = base.output_path() + config["uid"] = 65534 + config["gid"] = 65534 + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + output = "uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)\nuid 65534\ngid 65534\n" + assert.strictEqual(output, base.read_input(config["output_path"])) + + }) + it('test gcc random',()=>{ + let config = base.baseconfig() + config["max_memory"] = judger.UNLIMITED + config["exe_path"] = "/usr/bin/gcc" + config["args"] = [path.join(compile_base_path,'gcc_random.c'), + "-o", '/tmp/judge/gcc_random'] + let result= judger.run(config) + assert.ok(result["real_time"] >= 2000) + + }) + it('test cpp meta',()=>{ + let config = base.baseconfig() + config["exe_path"] = "/usr/bin/g++" + config["max_memory"] = 1024 * 1024 * 1024 + config["args"] = [path.join(compile_base_path,'cpp_meta.cpp'), + "-o",'/tmp/judge/cpp_meta'] + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_CPU_TIME_LIMIT_EXCEEDED) + assert.ok(result["cpu_time"] > 1500) + assert.ok(result["real_time"] >= 2000) + }) + it('test output size',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_c("output_size.c") + config["output_path"] = base.output_path() + config["max_output_size"] = 1000 * 10 + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_RUNTIME_ERROR) + assert.ok(base.read_input("/tmp/judge/fsize_test").length <= 1000*10) + }) + it('test stack size',()=>{ + let config = base.baseconfig() + config["max_memory"] = 256 * 1024 * 1024 + config["exe_path"] = base.compile_c("stack.c") + config["max_stack"] = 16 * 1024 * 1024 + config["output_path"] = config["error_path"] = base.output_path() + + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_RUNTIME_ERROR) + + config["max_stack"] = 128 * 1024 * 1024 + result= judger.run(config).result + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + assert.strictEqual("big stack", base.read_input(config["output_path"])) + + }) + it('test writev',()=>{ + let config = base.baseconfig() + config["exe_path"] = base.compile_cpp("writev.cpp") + config["seccomp_rule_name"] = "c_cpp" + config["input_path"] = base.make_input("111" * 10000 + "\n") + config["output_path"] = config["error_path"] = base.output_path() + + let result= judger.run(config) + assert.strictEqual(result["result"], judger.RESULT_SUCCESS) + }) +}) From 831d2fb7299a55e0588523c55b345a8ca2af3640 Mon Sep 17 00:00:00 2001 From: rainboy Date: Fri, 26 Apr 2019 15:41:52 +0800 Subject: [PATCH 5/5] add readme :memo: --- bindings/NodeJS/readme.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 bindings/NodeJS/readme.md diff --git a/bindings/NodeJS/readme.md b/bindings/NodeJS/readme.md new file mode 100644 index 0000000..43a80ad --- /dev/null +++ b/bindings/NodeJS/readme.md @@ -0,0 +1,15 @@ +## 安装 + +先安装`judger`,然后 + +``` +npm install +npm run build +npm run test +``` + +## 已知bug + +#### bug_1 + +`freopen("data.out","w",stdout)`,不能产生"data.out",这个应该是judger代码的锅,:cry: