1/*
2 * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17#include "ojproblemcasesrunner.h"
18#include "../utils.h"
19#include "../settings.h"
20#include "../systemconsts.h"
21#include "../widgets/ojproblemsetmodel.h"
22#include <QElapsedTimer>
23#include <QProcess>
24
25
26OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir,
27 const QVector<POJProblemCase>& problemCases, QObject *parent):
28 Runner(filename,arguments,workDir,parent),
29 mExecTimeout(-1)
30{
31 mProblemCases = problemCases;
32 mBufferSize = 8192;
33 mOutputRefreshTime = 1000;
34 setWaitForFinishTime(100);
35}
36
37OJProblemCasesRunner::OJProblemCasesRunner(const QString& filename, const QString& arguments, const QString& workDir,
38 POJProblemCase problemCase, QObject *parent):
39 Runner(filename,arguments,workDir,parent),
40 mExecTimeout(-1)
41{
42 mProblemCases.append(problemCase);
43 mBufferSize = 8192;
44 mOutputRefreshTime = 1000;
45 setWaitForFinishTime(100);
46}
47
48void OJProblemCasesRunner::runCase(int index,POJProblemCase problemCase)
49{
50 emit caseStarted(problemCase->getId(),index, mProblemCases.count());
51 auto action = finally([this,&index, &problemCase]{
52 emit caseFinished(problemCase->getId(), index, mProblemCases.count());
53 });
54 QProcess process;
55 bool errorOccurred = false;
56 QByteArray readed;
57 QByteArray buffer;
58 QByteArray output;
59 int noOutputTime = 0;
60 QElapsedTimer elapsedTimer;
61 bool execTimeouted = false;
62 process.setProgram(mFilename);
63 process.setArguments(splitProcessCommand(mArguments));
64 process.setWorkingDirectory(mWorkDir);
65 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
66 QString path = env.value("PATH");
67 QStringList pathAdded;
68 bool writeChannelClosed = false;
69 if (pSettings->compilerSets().defaultSet()) {
70 foreach(const QString& dir, pSettings->compilerSets().defaultSet()->binDirs()) {
71 pathAdded.append(dir);
72 }
73 }
74 pathAdded.append(pSettings->dirs().appDir());
75 if (!path.isEmpty()) {
76 path= pathAdded.join(PATH_SEPARATOR) + PATH_SEPARATOR + path;
77 } else {
78 path = pathAdded.join(PATH_SEPARATOR);
79 }
80 env.insert("PATH",path);
81 process.setProcessEnvironment(env);
82 process.setProcessChannelMode(QProcess::MergedChannels);
83 process.connect(
84 &process, &QProcess::errorOccurred,
85 [&](){
86 errorOccurred= true;
87 });
88 problemCase->output.clear();
89 process.start();
90 process.waitForStarted(5000);
91 if (process.state()==QProcess::Running) {
92 if (fileExists(problemCase->inputFileName))
93 process.write(readFileToByteArray(problemCase->inputFileName));
94 else
95 process.write(problemCase->input.toUtf8());
96 process.waitForFinished(0);
97 }
98
99 elapsedTimer.start();
100 while (true) {
101 if (process.bytesToWrite()==0 && !writeChannelClosed) {
102 writeChannelClosed = true;
103 process.closeWriteChannel();
104 }
105 process.waitForFinished(mWaitForFinishTime);
106 if (process.state()!=QProcess::Running) {
107 break;
108 }
109 if (mExecTimeout>0) {
110 int msec = elapsedTimer.elapsed();
111 if (msec>mExecTimeout) {
112 execTimeouted=true;
113 }
114 }
115 if (mStop || execTimeouted) {
116 process.terminate();
117 process.kill();
118 break;
119 }
120 if (errorOccurred)
121 break;
122 readed = process.read(mBufferSize);
123 buffer += readed;
124 if (buffer.length()>=mBufferSize || noOutputTime > mOutputRefreshTime) {
125 if (!buffer.isEmpty()) {
126 emit newOutputGetted(problemCase->getId(),QString::fromLocal8Bit(buffer));
127 output.append(buffer);
128 buffer.clear();
129 }
130 noOutputTime = 0;
131 } else {
132 noOutputTime += mWaitForFinishTime;
133 }
134 }
135 problemCase->runningTime=elapsedTimer.elapsed();
136 if (execTimeouted) {
137 problemCase->output = tr("Case Timeout");
138 emit resetOutput(problemCase->getId(), problemCase->output);
139 } else {
140 if (process.state() == QProcess::ProcessState::NotRunning)
141 buffer += process.readAll();
142 emit newOutputGetted(problemCase->getId(),QString::fromLocal8Bit(buffer));
143 output.append(buffer);
144 problemCase->output = QString::fromLocal8Bit(output);
145 if (errorOccurred) {
146 //qDebug()<<"process error:"<<process.error();
147 switch (process.error()) {
148 case QProcess::FailedToStart:
149 emit runErrorOccurred(tr("The runner process '%1' failed to start.").arg(mFilename));
150 break;
151 // case QProcess::Crashed:
152 // if (!mStop)
153 // emit runErrorOccurred(tr("The runner process crashed after starting successfully."));
154 // break;
155 case QProcess::Timedout:
156 emit runErrorOccurred(tr("The last waitFor...() function timed out."));
157 break;
158 case QProcess::WriteError:
159 emit runErrorOccurred(tr("An error occurred when attempting to write to the runner process."));
160 break;
161 case QProcess::ReadError:
162 emit runErrorOccurred(tr("An error occurred when attempting to read from the runner process."));
163 break;
164 default:
165 break;
166 }
167 }
168 }
169}
170
171void OJProblemCasesRunner::run()
172{
173 emit started();
174 auto action = finally([this]{
175 emit terminated();
176 });
177 for (int i=0; i < mProblemCases.size(); i++) {
178 if (mStop)
179 break;
180 POJProblemCase problemCase = mProblemCases[i];
181 runCase(i,problemCase);
182 }
183}
184
185int OJProblemCasesRunner::execTimeout() const
186{
187 return mExecTimeout;
188}
189
190void OJProblemCasesRunner::setExecTimeout(int newExecTimeout)
191{
192 mExecTimeout = newExecTimeout;
193}
194
195int OJProblemCasesRunner::waitForFinishTime() const
196{
197 return mWaitForFinishTime;
198}
199
200void OJProblemCasesRunner::setWaitForFinishTime(int newWaitForFinishTime)
201{
202 mWaitForFinishTime = newWaitForFinishTime;
203}
204
205int OJProblemCasesRunner::outputRefreshTime() const
206{
207 return mOutputRefreshTime;
208}
209
210void OJProblemCasesRunner::setOutputRefreshTime(int newOutputRefreshTime)
211{
212 mOutputRefreshTime = newOutputRefreshTime;
213}
214
215int OJProblemCasesRunner::bufferSize() const
216{
217 return mBufferSize;
218}
219
220void OJProblemCasesRunner::setBufferSize(int newBufferSize)
221{
222 mBufferSize = newBufferSize;
223}
224
225
226