Skip to content

Commit 407ed05

Browse files
committed
Implement custom testcase.
1 parent 6c4e08b commit 407ed05

File tree

7 files changed

+167
-12
lines changed

7 files changed

+167
-12
lines changed

data/languages/cpp/problemtest.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
namespace {
2424

25+
constexpr static char kAnyValue[] = "*";
26+
2527
bool openFileForWriting(const std::string &filename, std::ofstream &out) {
2628
std::ofstream outfile(filename, std::ios::out | std::ios::trunc);
2729
if (!outfile.is_open()) {
@@ -134,15 +136,26 @@ bool ProblemTest::runTest(
134136
constexpr size_t expected_testcase_size =
135137
Binder::func_arg_size_v + 1;
136138
if (expected_testcase_size != lines.size()) {
137-
std::cerr << "Incorrect number of parameters specified in the"
138-
<< " testcase file. Must specify one line per "
139-
<< "parameter + one line for expected output. "
140-
<< "Found: " << lines.size() << " Expected: "
141-
<< expected_testcase_size << "." << std::endl;
139+
std::stringstream ss;
140+
ss << "Incorrect number of parameters specified in the"
141+
<< " testcase file. Must specify one line per "
142+
<< "parameter + one line for expected output. "
143+
<< "Found: " << lines.size() << " Expected: "
144+
<< expected_testcase_size << ".";
145+
std::cerr << ss.str() << std::endl;
142146
success = false;
147+
test["reason"] = ss.str();
143148
} else {
144149
string expected_str;
145150
std::swap(expected_str, lines.back());
151+
152+
if (expected_str == kAnyValue) {
153+
test["status"] = "Skipped";
154+
test["actual"] =
155+
std::move(Binder::solve(solution, std::move(lines)));
156+
return true;
157+
}
158+
146159
expected = std::move(parse<Binder::return_type>(expected_str));
147160
ret = std::move(Binder::solve(solution, std::move(lines)));
148161
success = Comparator::compare(ret, expected, true);

src/schema/results_validation_schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@
5353
},
5454
"then": {
5555
"required": ["actual", "expected", "reason", "testcase_file"]
56+
},
57+
"else": {
58+
"if": {
59+
"properties": {
60+
"status": { "const": "Skipped" }
61+
}
62+
},
63+
"then": {
64+
"required": ["actual"]
65+
}
5666
}
5767
},
5868
"tests_schema": {

src/ui/directory-manager.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ global.problemBuildsDir = null;
1010

1111
LANGUAGES_DIR_NAME = "languages";
1212
PROBLEMS_DIR_NAME = "problems";
13+
TESTCASES_DIR_NAME = "testcases";
1314
RESULTS_VALIDATION_SCHEMA_FILENAME = "results_validation_schema.json";
1415

1516
function getPathsFile() {
@@ -149,9 +150,21 @@ class DirectoryManager {
149150
getResultsSchemaJson() {
150151
return global.resultsSchemaJson;
151152
}
153+
154+
getCustomTestcaseName() {
155+
return "_custom_testcase";
156+
}
157+
158+
getCustomTestcaseFilename(problemName) {
159+
return path.join(problemBuildsDir,
160+
PROBLEMS_DIR_NAME,
161+
problemName,
162+
TESTCASES_DIR_NAME,
163+
this.getCustomTestcaseName() + '.test');
164+
}
152165
}
153166

154167
module.exports = {
155168
DirectoryManager,
156-
getPathsFile
169+
getPathsFile,
157170
};

src/ui/index.html

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<select id="problem-select">
1616
</select>
1717
<button id="run-button">▶ Run</button>
18+
<button id="custom-testcase-button">▶ Custom Testcase</button>
1819
<button id="save-button">Save</button>
1920
</div>
2021
<div class="panel" id="root-panel">
@@ -52,7 +53,19 @@
5253
<div class="tab" id="tab-compilation-button">Compilation</div>
5354
</div>
5455
<div id="tab-testcase" class="tab-content bottom-right-content content active">
55-
<div class="testcase-content" id="testcase-content tab-content content">
56+
<!-- Testcase Container-->
57+
<div id="tab-testcase-container">
58+
<h5 class="testcase-container-title">Input:</h5>
59+
<textarea id="input-container" class="testcase-container-text"></textarea>
60+
<h5 class="testcase-container-title">Output:</h5>
61+
<textarea readonly id="testcase-output" class="testcase-container-text">
62+
</textarea>
63+
<h5 class="testcase-container-title">Expected:</h5>
64+
<textarea readonly id="expected-output" class="testcase-container-text">
65+
</textarea>
66+
<h5 class="testcase-container-title">Stdout:</h5>
67+
<textarea readonly id="testcase-stdout" class="testcase-container-text"></textarea>
68+
<!-- Testcase Container END -->
5669
</div>
5770
</div>
5871
<div id="tab-test-results" class="tab-test-results tab-content tab-bottom-right content">

src/ui/index.js

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ function parseBuildError(stdout) {
5050
// Error running the command: cmake --build
5151
const regex = /cmake --build[\s\S]*?cmake --build/;
5252
const match = stdout.match(regex);
53+
if (!match || match.length === 0) {
54+
return stdout;
55+
}
5356
const buildError = match[0].split('\n').slice(1, -1).join('\n');
5457

5558
return buildError;
@@ -124,7 +127,7 @@ function setTestResults(results) {
124127
document.getElementById('tab-test-results-button').click();
125128
}
126129

127-
function run() {
130+
function run(callback, testcase = 'All') {
128131
saveSolution('cpp', editor.getValue());
129132
const pathsFile = DirectoryManager.getPathsFile();
130133
if (!file.existsSync(pathsFile)) {
@@ -139,8 +142,11 @@ function run() {
139142
`--problem_builds_dir ${problemBuildsDir} ` +
140143
`--language cpp ` +
141144
`--problem ${activeProblem} ` +
145+
`--testcase ${testcase} ` +
142146
`--verbose`;
143147

148+
console.log("Running command: " + command);
149+
144150
var resultsFilename;
145151
exec(command, (error, stdout, stderr) => {
146152
if (error) {
@@ -165,10 +171,54 @@ function run() {
165171
const results = file.readFileSync(resultsFilename, 'utf8');
166172
console.log(results);
167173
const resultsJson = JSON.parse(results);
168-
setTestResults(resultsJson);
174+
callback(resultsJson);
169175
});
170176
}
171177

178+
function setCustomTestcaseResults(results) {
179+
if (!validateResults(results)) {
180+
return;
181+
}
182+
183+
if (results.tests.length !== 1) {
184+
console.error("Expected 1 custom test results, got " +
185+
results.tests.length);
186+
return;
187+
}
188+
189+
if (results.tests[0].status !== "Skipped") {
190+
console.error("Expected custom test status to be skipped, got " +
191+
results.tests[0].status);
192+
}
193+
194+
document.getElementById('testcase-stdout').textContent = results.stdout;
195+
document.getElementById('testcase-output').textContent =
196+
results.tests[0].actual;
197+
}
198+
199+
function runCustomTestcase() {
200+
console.log("Running custom testcase for " + activeProblem);
201+
202+
const input = document.getElementById('input-container').value + "\n*";
203+
const customTestcaseFilename =
204+
directoryManager.getCustomTestcaseFilename(activeProblem);
205+
if (!file.existsSync(path.dirname(customTestcaseFilename))) {
206+
console.log('The directory does not exist. Directory: ' + path.dirname(customTestcaseFilename));
207+
return;
208+
}
209+
210+
file.writeFileSync(customTestcaseFilename, input);
211+
if (!file.existsSync(customTestcaseFilename)) {
212+
throw new Error(`Failed to write custom testcase to ` +
213+
`${customTestcaseFilename}`);
214+
}
215+
216+
console.log('Custom testcase written to ' + customTestcaseFilename);
217+
console.log('Testcase input: ' + input);
218+
219+
run(setCustomTestcaseResults, directoryManager.getCustomTestcaseName());
220+
}
221+
172222
function setDescription(problemName) {
173223
var element =
174224
document.querySelector('.markdown-content#description-content');
@@ -229,13 +279,26 @@ function initializeSaveCommand() {
229279
function initializeRunCommand() {
230280
ipcRenderer.on('run-command', () => {
231281
console.log('Received run command');
232-
run();
282+
run(setTestResults);
233283
});
234284

235285
document.getElementById('run-button')
236286
.addEventListener('click', function() {
237287
console.log('Run button clicked');
238-
run();
288+
run(setTestResults);
289+
});
290+
}
291+
292+
function initializeCustomTestcaseCommand() {
293+
ipcRenderer.on('custom-testcase-command', () => {
294+
console.log('Received custom testcase command');
295+
runCustomTestcase();
296+
});
297+
298+
document.getElementById('custom-testcase-button')
299+
.addEventListener('click', function() {
300+
console.log('Custom testcase button clicked');
301+
runCustomTestcase();
239302
});
240303
}
241304

@@ -247,6 +310,7 @@ document.addEventListener('DOMContentLoaded', (event) => {
247310
initializeProblemsCombo(problemNames);
248311
initializeSaveCommand();
249312
initializeRunCommand();
313+
initializeCustomTestcaseCommand();
250314

251315
amdRequire(['vs/editor/editor.main'], function() {
252316
monaco.editor.setTheme('vs-dark');

src/ui/main.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,24 @@ function registerRunCommand() {
7373
globalShortcut.isRegistered('CommandOrControl+R'))
7474
}
7575

76+
function registerCustomTestcaseCommand() {
77+
const ret = globalShortcut.register('CommandOrControl+T', () => {
78+
console.log('CommandOrControl+T is pressed')
79+
win.webContents.send('custom-testcase-command')
80+
})
81+
82+
if (!ret) {
83+
console.log('Registration failed!')
84+
}
85+
86+
console.log("CommandOrControl+T registered: " +
87+
globalShortcut.isRegistered('CommandOrControl+T'))
88+
}
89+
7690
function registerCommands() {
7791
registerSaveCommand();
7892
registerRunCommand();
93+
registerCustomTestcaseCommand();
7994
}
8095

8196
app.whenReady().then(() => {

src/ui/styles.css

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ html, body {
193193
height: 100%;
194194
}
195195

196-
#test-results-content, #user-solution-content, .tab-test-results, #bottom-right-panel, #root-panel, #tab-compilation, #tab-testcase, #compilation-content {
196+
#test-results-content, #user-solution-content, .tab-test-results, #bottom-right-panel, #root-panel, #tab-compilation, #compilation-content {
197197
height: 100%;
198198
overflow: auto;
199199
}
@@ -216,4 +216,31 @@ html, body {
216216
.gutter.gutter-vertical {
217217
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
218218
cursor: row-resize;
219+
}
220+
221+
#tab-testcase {
222+
height: 100%;
223+
}
224+
225+
#tab-testcase-container {
226+
height: 100%;
227+
overflow-y: visible;
228+
overflow-x: hidden;
229+
display: flex;
230+
flex-direction: column;
231+
}
232+
233+
.testcase-container-text {
234+
background-color: var(--grey-very-dark);
235+
color: var(--content-font-color);
236+
font-family: var(--content-font-family);
237+
overflow: hidden;
238+
padding: 3px;
239+
margin-right: 20px;
240+
height: 60px;
241+
width: 400px;
242+
}
243+
244+
.testcase-container-title {
245+
margin: 10px 3px 3px 3px;
219246
}

0 commit comments

Comments
 (0)