Compare commits

..

2 Commits

Author SHA1 Message Date
11354a5a2f update 2024-08-17 14:26:23 +08:00
8dfd101ae9 linux done 2024-08-17 14:21:55 +08:00
3 changed files with 120 additions and 70 deletions

View File

@ -5,11 +5,11 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <functional> #include <functional>
#include <future>
#include <iostream> #include <iostream>
#include<json/json.h> #include<json/json.h>
#include <ostream> #include <ostream>
#include <queue> #include <queue>
#include <sstream>
#include <string> #include <string>
#include <thread> #include <thread>
@ -59,21 +59,42 @@ struct Test{
} }
}; };
#ifdef _WIN32 struct ProgramRet{
struct ProgramRet{ #ifdef _WIN32
time_point<steady_clock> start;
time_point<steady_clock> end;
};
void executeCommandWithRedirect(const std::string &command, const std::string &inputFile, const std::string &outputFile);
#else
struct ProgramRet{
time_point<system_clock> start; time_point<system_clock> start;
time_point<system_clock> end; time_point<system_clock> end;
}; #else
#endif time_point<system_clock> start;
time_point<system_clock> end;
#endif
path outPath;
unsigned int id;
friend std::ostream& operator<<(std::ostream&os,ProgramRet&ret){
os<<"ProgramRet { start: "<<ret.start<<" ,end: "<<ret.end<<"outPath: "<<ret.outPath<<" ,id: "<<ret.id<<" }";
return os;
}
};
template<typename T> template<typename T>
T getValueOrPanic(T value); T getValueOrPanic(T value);
int compareFiles(const std::string& path1, const std::string& path2);
struct Statu{
bool isTLE = false;
int cmp;
int id;
friend std::ostream& operator<<(std::ostream &os,Statu &s){
if(s.isTLE){
os<<"tests "<<s.id<<" TLE";
}else if(s.cmp==0){
os<<"tests "<<s.id<<" AC";
}else{
os<<"tests "<<s.id<<" WA line:"<<s.cmp;
}
return os;
}
};
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
LOG("Zengtudor OI test tools . loading files .. please add a json file in arg"); LOG("Zengtudor OI test tools . loading files .. please add a json file in arg");
@ -119,6 +140,12 @@ int main(int argc, char* argv[]) {
LOG("creating building dir") LOG("creating building dir")
std::filesystem::create_directory(buildDir); std::filesystem::create_directory(buildDir);
} }
LOG("getting run time out")
int runtimeout = 0;
if(jsonValue["runtimeout"]!=Json::nullValue){
runtimeout = jsonValue["runtimeout"].asInt();
printValue(runtimeout)
}
LOG("compiling") LOG("compiling")
string compileCommands; string compileCommands;
const path exePath = (buildDir/"main").string()+(isWindows==true?".exe":""); const path exePath = (buildDir/"main").string()+(isWindows==true?".exe":"");
@ -130,16 +157,19 @@ int main(int argc, char* argv[]) {
auto cpuNums = getCpuNums(); auto cpuNums = getCpuNums();
printValue(cpuNums) printValue(cpuNums)
if(jsonValue["tests"].size()==0)LOG("cannot find tests"); if(jsonValue["tests"].size()==0){
LOG("cannot find tests")
return 0;
}
LOG("parsing tests") LOG("parsing tests")
vector<Test> tests; vector<Test> tests(jsonValue["tests"].size()+1);
auto &jsonTests = jsonValue["tests"]; auto &jsonTests = jsonValue["tests"];
for(unsigned int i=1;i<=jsonTests.size();i++){ for(unsigned int i=1;i<=jsonTests.size();i++){
tests.push_back({ tests[i]={
.id = i, .id = i,
.in=getAbsolutePath(projectPath, getValueOrPanic(jsonTests[i-1]["in"]).asString()), .in=getAbsolutePath(projectPath, getValueOrPanic(jsonTests[i-1]["in"]).asString()),
.ans=getAbsolutePath(projectPath,getValueOrPanic(jsonTests[i-1]["ans"]).asString()), .ans=getAbsolutePath(projectPath,getValueOrPanic(jsonTests[i-1]["ans"]).asString()),
}); };
} }
queue<std::function<ProgramRet()>> tasks; queue<std::function<ProgramRet()>> tasks;
const auto testsPath = projectPath/"tests"; const auto testsPath = projectPath/"tests";
@ -149,8 +179,9 @@ int main(int argc, char* argv[]) {
} }
printValue(testsPath) printValue(testsPath)
const string exePathStr = getProtectPath(exePath); const string exePathStr = getProtectPath(exePath);
for (auto i : tests) { for (int j=1;j<=jsonTests.size();j++) {
tasks.push([i, &exePathStr, &testsPath]() -> ProgramRet { auto &i = tests[j];
tasks.push([i, &exePathStr, &testsPath]() -> ProgramRet {
string id_str = std::to_string(i.id); string id_str = std::to_string(i.id);
path outPath = testsPath / (id_str + ".out"); path outPath = testsPath / (id_str + ".out");
@ -162,85 +193,96 @@ int main(int argc, char* argv[]) {
// 执行命令 // 执行命令
auto start = high_resolution_clock::now(); auto start = high_resolution_clock::now();
#ifdef _WIN32 int result = system(commands.c_str());
executeCommandWithRedirect(commands, getProtectPath(i.in), getProtectPath(outPath));
#else
system(commands.c_str())
#endif
auto end = high_resolution_clock::now(); auto end = high_resolution_clock::now();
// AS_EM(result, 0,"error in exec commands") if (result != 0) {
cerr << "Error executing command: " << commands << endl;
exit(1);
}
return ProgramRet{ return ProgramRet{
.start = start, .start = start,
.end = end .end = end,
.outPath=outPath,
.id=i.id
}; };
}); });
} }
vector<std::future<ProgramRet>> futures;
while(tasks.size()>0){ while(tasks.size()>0){
auto i = tasks.front(); auto i = tasks.front();
tasks.pop(); tasks.pop();
thread newThread(i); futures.push_back(std::async(std::launch::async,i));
AS_EQ(newThread.joinable(),true);
newThread.join();
} }
vector<ProgramRet> results(jsonTests.size()+1);
for(auto &i:futures){
if(runtimeout>0){
if(i.wait_for(std::chrono::milliseconds(runtimeout))==std::future_status::ready){
auto ret = i.get();
results[ret.id]=ret;
}
}else{
i.wait();
auto ret = i.get();
results[ret.id]=ret;
}
}
LOG("get results done")
for(int i=1;i<=jsonTests.size();i++){
LOG(results[i])
}
vector<Statu> status(jsonTests.size()+1);
for(int i=1;i<=jsonTests.size();i++){
printValue(tests[i].ans)
status[i]={
.isTLE=(results[i].id==0?true:false),
.cmp=compareFiles(results[i].outPath,tests[i].ans),
.id=i
};
}
for(int i=1;i<=jsonTests.size();i++){
cout<<status[i]<<endl;
}
exit(0);
} }
#ifdef _WIN32 int compareFiles(const std::string& path1, const std::string& path2) {
std::ifstream file1(path1);
std::ifstream file2(path2);
void executeCommandWithRedirect(const std::string &command, const std::string &inputFile, const std::string &outputFile) { if (!file1.is_open()) {
// 创建输入输出文件的句柄 std::cerr << "无法打开文件: " << path1 << std::endl;
HANDLE hInput = CreateFile(inputFile.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); exit(1);
if (hInput == INVALID_HANDLE_VALUE) { }
throw std::runtime_error("Failed to open input file: " + inputFile); if (!file2.is_open()) {
std::cerr << "无法打开文件: " << path2 << std::endl;
exit(1);
} }
HANDLE hOutput = CreateFile(outputFile.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); std::string line1, line2;
if (hOutput == INVALID_HANDLE_VALUE) { int lineNumber = 1;
CloseHandle(hInput);
throw std::runtime_error("Failed to open output file: " + outputFile); while (std::getline(file1, line1) && std::getline(file2, line2)) {
if (line1 != line2) {
return lineNumber; // 返回第一个不同的行号
}
++lineNumber;
} }
STARTUPINFO si; // 如果一个文件还没读完
PROCESS_INFORMATION pi; if (std::getline(file1, line1) || std::getline(file2, line2)) {
ZeroMemory(&si, sizeof(si)); return lineNumber;
si.cb = sizeof(si);
si.dwFlags |= STARTF_USESTDHANDLES; // 使用标准句柄
si.hStdInput = hInput;
si.hStdOutput = hOutput;
si.hStdError = GetStdHandle(STD_ERROR_HANDLE); // 重定向错误输出
ZeroMemory(&pi, sizeof(pi));
// 将命令转换为 LPSTR 类型
LPSTR cmd = const_cast<LPSTR>(command.c_str());
// 创建进程
if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
CloseHandle(hInput);
CloseHandle(hOutput);
throw std::runtime_error("CreateProcess failed with error: " + std::to_string(GetLastError()));
} }
// 等待子进程结束 return 0; // 文件内容完全一致
WaitForSingleObject(pi.hProcess, INFINITE);
// 关闭句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(hInput);
CloseHandle(hOutput);
} }
#endif
string getProtectPath(const std::filesystem::path &p){ string getProtectPath(const std::filesystem::path &p){
return "\""+p.string()+"\""; return "\""+p.string()+"\"";
} }
template<typename T> template<typename T>
T getValueOrPanic(T value){ T getValueOrPanic(T value){
AS_NE(value, Json::nullValue) AS_NE(value, Json::nullValue)

View File

@ -1,8 +1,11 @@
#include<bits/stdc++.h> #include<bits/stdc++.h>
#include <chrono>
#include <thread>
using namespace std; using namespace std;
int main(){ int main(){
// this_thread::sleep_for(chrono::seconds(2));
string s; string s;
cin>>s; cin>>s;
cout<<s<<endl; cout<<s<<endl;

View File

@ -9,6 +9,11 @@
{ {
"in":"a.in", "in":"a.in",
"ans":"1.ans" "ans":"1.ans"
},
{
"in":"a.in",
"ans":"2.ans"
} }
] ],
"runtimeout":100
} }