Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions tests/browser/compiler-explorer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,83 @@ const noJava =
: "javac/java are not available";

describe("toolchain harnesses", { concurrency: true }, () => {
for (const value of ["INFINITY", "-INFINITY", "NAN"]) {
it(
`C double harness preserves all cases when returning ${value}`,
{ skip: noCc },
async () => {
const dir = mkdtempSync(join(tmpdir(), "codetrial-c-double-"));
try {
const source = join(dir, "growth.c");
const binary = join(dir, "growth");
writeFileSync(
source,
generateHarness(
"c",
judges["powx-n"],
`double growthOverPeriods(double x, int n) {
if (n < 0) return ${value};
double result = 1.0;
for (int i = 0; i < n; i++) result *= x;
return result;
}`,
),
);
await run("cc", ["-std=c17", source, "-o", binary]);
const parsed = parseCompilerResults((await run(binary)).stdout);
assert.equal(parsed.setupError, undefined);
assert.deepEqual(
parsed.results.map((result) => result.actual),
[1024, 9.261000000000001, null, 1, null, null],
);
} finally {
rmSync(dir, { recursive: true, force: true });
}
},
);
}

it(
"C double array harness preserves finite elements beside non-finite values",
{ skip: noCc },
async () => {
const dir = mkdtempSync(join(tmpdir(), "codetrial-c-double-array-"));
try {
const source = join(dir, "array.c");
const binary = join(dir, "array");
const spec = {
kind: "function",
entry: "numbers",
paramNames: [],
paramTypes: [],
returnType: "double[]",
cases: [{ input: [], expected: [1.25, null, null, null, 2.5] }],
};
writeFileSync(
source,
generateHarness(
"c",
spec,
`double* numbers(int* returnSize) {
static double values[] = {1.25, INFINITY, -INFINITY, NAN, 2.5};
*returnSize = 5;
return values;
}`,
),
);
await run("cc", ["-std=c17", source, "-o", binary]);
const parsed = parseCompilerResults((await run(binary)).stdout);
assert.equal(parsed.setupError, undefined);
assert.deepEqual(
parsed.results.map((result) => result.actual),
[[1.25, null, null, null, 2.5]],
);
} finally {
rmSync(dir, { recursive: true, force: true });
}
},
);

it(
"C Two Sum harness can produce passing Compiler Explorer JSON",
{ skip: noCc },
Expand Down
5 changes: 3 additions & 2 deletions web/compiler-explorer.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ static struct Node* randomList(int values[][2], int size) { originalRandomNodesS
static struct Node* graphNode(int** adjacency, int size, int* colSize) { originalGraphNodesSize = size; if (!size) return NULL; struct Node** nodes = calloc(size, sizeof(*nodes)); for (int i = 0; i < size; i++) { nodes[i] = calloc(1, sizeof(*nodes[i])); nodes[i]->val = i + 1; nodes[i]->numNeighbors = colSize[i]; nodes[i]->neighbors = calloc(colSize[i], sizeof(*nodes[i]->neighbors)); originalGraphNodes[i] = nodes[i]; } for (int i = 0; i < size; i++) for (int j = 0; j < colSize[i]; j++) nodes[i]->neighbors[j] = nodes[adjacency[i][j] - 1]; struct Node* first = nodes[0]; free(nodes); return first; }
static struct Node* nextTree(int* values, bool* nulls, int size) { if (!size || nulls[0]) return NULL; struct Node** nodes = calloc(size, sizeof(*nodes)); for (int i = 0; i < size; i++) if (!nulls[i]) { nodes[i] = calloc(1, sizeof(*nodes[i])); nodes[i]->val = values[i]; } for (int i = 0, child = 1; i < size && child < size; i++) { if (!nodes[i]) continue; if (child < size) nodes[i]->left = nodes[child++]; if (child < size) nodes[i]->right = nodes[child++]; } struct Node* root = nodes[0]; free(nodes); return root; }
static void json_int_array(FILE* out, const int* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fprintf(out, "%d", values[i]); } fputc(']', out); }
static void json_double_array(FILE* out, const double* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fprintf(out, "%.17g", values[i]); } fputc(']', out); }
static void json_double(FILE* out, double value) { if (isfinite(value)) fprintf(out, "%.17g", value); else fputs("null", out); }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Python runner still has the bug this fixes for C. In web/runners.js the per-case json.dumps(actual) check only catches TypeError/ValueError, and the default allow_nan=True raises neither, so float("inf") reaches the final json.dumps({"results": results}) as bare Infinity and the worker's JSON.parse rejects the whole run. This can be a follow-up: map non-finite floats in actual to None before appending, and add the same powx-n case for Python.

static void json_double_array(FILE* out, const double* values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_double(out, values[i]); } fputc(']', out); }
static void json_string_array(FILE* out, char** values, int size) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_string(out, values[i]); } fputc(']', out); }
static void json_int_matrix(FILE* out, int** values, int size, int* colSize) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); json_int_array(out, values[i], colSize[i]); } fputc(']', out); }
static void json_char_matrix(FILE* out, char** values, int size, int* colSize) { fputc('[', out); for (int i = 0; i < size; i++) { if (i) fputc(',', out); fputc('[', out); for (int j = 0; j < colSize[i]; j++) { if (j) fputc(',', out); char item[2] = { values[i][j], 0 }; json_string(out, item); } fputc(']', out); } fputc(']', out); }
Expand Down Expand Up @@ -485,7 +486,7 @@ function cPrintActual(spec) {
if (spec.outputType === "quadTree") return `json_quad_tree(stdout, ${name});`;
if (type === "boolean") return `printf(${name} ? "true" : "false");`;
if (type === "integer") return `printf("%d", ${name});`;
if (type === "double") return `printf("%.17g", ${name});`;
if (type === "double") return `json_double(stdout, ${name});`;
if (type === "string") return `json_string(stdout, ${name});`;
if (type === "integer[]")
return `json_int_array(stdout, ${name}, ${Number.isInteger(spec.outputParam) ? `${name}Size` : "returnSize"});`;
Expand Down