Skip to content

Commit bc1ad22

Browse files
committed
fix go runner
1 parent fc98e66 commit bc1ad22

File tree

1 file changed

+211
-79
lines changed

1 file changed

+211
-79
lines changed

tests/runners/csharp_runner.sh

Lines changed: 211 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,13 @@ path.write_text(text)
127127
"
128128

129129
# Generate test harness (Program.cs)
130+
local harness_status=0
131+
set +e
130132
ALGO_SLUG="${algo_name##*/}" \
131133
TEST_DATA_JSON="$test_data" \
132134
CS_SOURCE_PATH="$cs_files" \
133135
CSHARP_PROGRAM_PATH="$project_dir/Program.cs" \
134-
python3 - <<'PY' || {
136+
python3 - <<'PY'
135137
import json
136138
import os
137139
import re
@@ -259,32 +261,61 @@ if cs_method_name is None:
259261
260262
qualified_class_name = class_name if not namespace_name else f"{namespace_name}.{class_name}"
261263
262-
common_block = """\
263-
if (result is string s) {
264-
Console.WriteLine(s);
265-
} else if (result is System.Collections.IEnumerable enumerable && result is not string) {
266-
Console.WriteLine(JoinEnumerable(enumerable));
267-
} else {
268-
Console.WriteLine(FormatValue(result));
269-
}
270-
"""
264+
preferred_name_literals = ", ".join(json.dumps(name) for name in preferred_method_names)
265+
266+
267+
def is_flat_list(value):
268+
return isinstance(value, list) and all(not isinstance(item, (list, dict)) for item in value)
269+
270+
271+
shape = None
272+
if inputs == ["tree_values", "depth", "is_maximizing"]:
273+
shape = "minimax"
274+
elif inputs == ["tree_as_array"]:
275+
shape = "nullable_array"
276+
elif len(sample_inputs) == 1 and is_flat_list(sample_inputs[0]):
277+
shape = "array"
278+
elif len(sample_inputs) == 2 and is_flat_list(sample_inputs[0]) and not isinstance(sample_inputs[1], (list, dict)):
279+
shape = "array_and_scalar"
280+
elif len(sample_inputs) == 1 and not isinstance(sample_inputs[0], (list, dict)):
281+
shape = "scalar"
282+
elif len(sample_inputs) == 2 and all(not isinstance(value, (list, dict)) for value in sample_inputs):
283+
shape = "two_scalars"
284+
else:
285+
raise SystemExit(3)
286+
287+
common_block = [
288+
" if (method.ReturnType == typeof(void)) {",
289+
" result = argsToInvoke[0];",
290+
" }",
291+
" Console.WriteLine(FormatResult(result));",
292+
]
271293
272294
harness_lines = [
273295
"using System;",
274296
"using System.Linq;",
275297
"using System.Reflection;",
276298
"using System.Collections;",
299+
"using System.Collections.Generic;",
277300
"",
278301
source,
279302
"",
280303
"class TestHarnessProgram {",
281-
" static string FormatValue(object value) {",
304+
' static string FormatScalar(object value) {',
282305
' if (value == null) return "null";',
283306
' if (value is bool boolValue) return boolValue ? "true" : "false";',
307+
' if (value is string stringValue) return stringValue;',
308+
' if (value is IEnumerable enumerable && value is not string) {',
309+
' return "[" + string.Join(", ", enumerable.Cast<object>().Select(FormatScalar)) + "]";',
310+
" }",
284311
' return value.ToString() ?? "";',
285312
" }",
286313
" static string JoinEnumerable(IEnumerable values) {",
287-
' return string.Join(" ", values.Cast<object>().Select(FormatValue));',
314+
' return string.Join(" ", values.Cast<object>().Select(FormatScalar));',
315+
" }",
316+
" static string FormatResult(object value) {",
317+
' if (value is IEnumerable enumerable && value is not string) return JoinEnumerable(enumerable);',
318+
" return FormatScalar(value);",
288319
" }",
289320
" static int[] ParseIntArray(string line) {",
290321
" if (string.IsNullOrWhiteSpace(line)) return Array.Empty<int>();",
@@ -296,16 +327,98 @@ harness_lines = [
296327
' .Select(token => token.Equals("null", StringComparison.OrdinalIgnoreCase) || token.Equals("none", StringComparison.OrdinalIgnoreCase) ? (int?)null : int.Parse(token))',
297328
" .ToArray();",
298329
" }",
330+
" static string NormalizeName(string value) {",
331+
' return new string((value ?? "").Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());',
332+
" }",
333+
" static bool IsSimpleScalarType(Type type) {",
334+
" return type == typeof(string)",
335+
" || type == typeof(bool)",
336+
" || type == typeof(int)",
337+
" || type == typeof(long)",
338+
" || type == typeof(uint)",
339+
" || type == typeof(ulong)",
340+
" || type == typeof(float)",
341+
" || type == typeof(double)",
342+
" || type == typeof(decimal);",
343+
" }",
344+
" static object ParseScalar(Type type, string raw) {",
345+
' raw = (raw ?? "").Trim();',
346+
" if (type == typeof(string)) return raw;",
347+
" if (type == typeof(bool)) return bool.Parse(string.IsNullOrEmpty(raw) ? \"false\" : raw);",
348+
" if (type == typeof(int)) return int.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
349+
" if (type == typeof(long)) return long.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
350+
" if (type == typeof(uint)) return uint.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
351+
" if (type == typeof(ulong)) return ulong.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
352+
" if (type == typeof(float)) return float.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
353+
" if (type == typeof(double)) return double.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
354+
" if (type == typeof(decimal)) return decimal.Parse(string.IsNullOrEmpty(raw) ? \"0\" : raw);",
355+
' throw new InvalidOperationException("Unsupported scalar parameter type");',
356+
" }",
357+
" static bool MatchesGenericCollection(Type type, Type elementType) {",
358+
" if (!type.IsGenericType) return false;",
359+
" Type generic = type.GetGenericTypeDefinition();",
360+
" Type[] args = type.GetGenericArguments();",
361+
" if (args.Length != 1 || args[0] != elementType) return false;",
362+
" return generic == typeof(IEnumerable<>)",
363+
" || generic == typeof(ICollection<>)",
364+
" || generic == typeof(IList<>)",
365+
" || generic == typeof(IReadOnlyCollection<>)",
366+
" || generic == typeof(IReadOnlyList<>)",
367+
" || generic == typeof(List<>);",
368+
" }",
369+
" static bool IsFlatIntEnumerableType(Type type) {",
370+
" return type == typeof(int[]) || MatchesGenericCollection(type, typeof(int));",
371+
" }",
372+
" static bool IsFlatNullableIntEnumerableType(Type type) {",
373+
" return type == typeof(int?[]) || MatchesGenericCollection(type, typeof(int?));",
374+
" }",
375+
" static object BuildFlatIntArg(Type type, int[] values) {",
376+
" if (type == typeof(int[])) return values;",
377+
" if (MatchesGenericCollection(type, typeof(int))) return new List<int>(values);",
378+
' throw new InvalidOperationException("Unsupported integer sequence parameter type");',
379+
" }",
380+
" static object BuildNullableIntArg(Type type, int?[] values) {",
381+
" if (type == typeof(int?[])) return values;",
382+
" if (MatchesGenericCollection(type, typeof(int?))) return new List<int?>(values);",
383+
' throw new InvalidOperationException("Unsupported nullable integer sequence parameter type");',
384+
" }",
385+
" static int ScoreMethod(MethodInfo method, string[] preferredNames) {",
386+
" int score = 0;",
387+
" if (method.IsPublic) score += 100;",
388+
" string methodName = method.Name;",
389+
" string normalizedMethodName = NormalizeName(methodName);",
390+
" for (int i = 0; i < preferredNames.Length; i++) {",
391+
" string preferred = preferredNames[i];",
392+
" string normalizedPreferred = NormalizeName(preferred);",
393+
" int bonus = preferredNames.Length - i;",
394+
" if (methodName == preferred) score += 1000 + bonus;",
395+
" else if (normalizedMethodName == normalizedPreferred) score += 800 + bonus;",
396+
" else if (!string.IsNullOrEmpty(normalizedPreferred) && normalizedMethodName.StartsWith(normalizedPreferred)) score += 600 + bonus;",
397+
" else if (!string.IsNullOrEmpty(normalizedPreferred) && normalizedMethodName.Contains(normalizedPreferred)) score += 400 + bonus;",
398+
" }",
399+
" return score;",
400+
" }",
401+
" static MethodInfo ChooseMethod(Type targetType, string[] preferredNames, Func<MethodInfo, bool> isCompatible) {",
402+
" return targetType",
403+
" .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)",
404+
' .Where(candidate => candidate.Name != "Main")',
405+
" .Where(isCompatible)",
406+
" .OrderByDescending(candidate => ScoreMethod(candidate, preferredNames))",
407+
" .FirstOrDefault();",
408+
" }",
299409
" static void Main(string[] args) {",
300410
f" Type targetType = typeof({qualified_class_name});",
301-
" MethodInfo method = targetType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)",
302-
f' .FirstOrDefault(candidate => candidate.Name == "{cs_method_name}");',
303-
' if (method == null) throw new InvalidOperationException("Method not found");',
411+
f" string[] preferredNames = new[] {{ {preferred_name_literals} }};",
304412
]
305413
306-
if inputs == ["tree_values", "depth", "is_maximizing"]:
414+
if shape == "minimax":
307415
harness_lines.extend(
308416
[
417+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
418+
" int paramCount = candidate.GetParameters().Length;",
419+
" return paramCount == 3 || paramCount == 5 || paramCount == 7;",
420+
" });",
421+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
309422
' int[] scores = ParseIntArray(Console.ReadLine() ?? "");',
310423
' int depth = int.Parse((Console.ReadLine() ?? "0").Trim());',
311424
' bool isMaximizing = bool.Parse((Console.ReadLine() ?? "false").Trim());',
@@ -318,98 +431,111 @@ if inputs == ["tree_values", "depth", "is_maximizing"]:
318431
" } else if (paramCount == 7) {",
319432
" result = method.Invoke(null, new object[] { 0, 0, isMaximizing, scores, depth, int.MinValue, int.MaxValue });",
320433
" } else {",
321-
' throw new InvalidOperationException("Unsupported minimax signature");',
434+
' Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__");',
435+
" return;",
322436
" }",
323-
" Console.WriteLine(FormatValue(result));",
437+
" Console.WriteLine(FormatResult(result));",
324438
]
325439
)
326-
elif inputs == ["tree_as_array"]:
327-
harness_lines.extend(
328-
[
329-
' int?[] arr = ParseNullableIntArray(Console.ReadLine() ?? "");',
330-
" object result = method.Invoke(null, new object[] { arr });",
331-
]
332-
)
333-
harness_lines.extend(common_block.rstrip().splitlines())
334-
elif (
335-
(output == "array_of_integers" and inputs == ["array_of_integers"])
336-
or (
337-
len(sample_inputs) == 1
338-
and isinstance(sample_inputs[0], list)
339-
and isinstance(sample_expected, list)
340-
)
341-
):
440+
elif shape == "nullable_array":
342441
harness_lines.extend(
343442
[
344-
' int[] arr = ParseIntArray(Console.ReadLine() ?? "");',
345-
" object result = method.Invoke(null, new object[] { arr });",
443+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
444+
" ParameterInfo[] parameters = candidate.GetParameters();",
445+
" return parameters.Length == 1 && IsFlatNullableIntEnumerableType(parameters[0].ParameterType);",
446+
" });",
447+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
448+
' int?[] values = ParseNullableIntArray(Console.ReadLine() ?? "");',
449+
" object[] argsToInvoke = new object[] { BuildNullableIntArg(method.GetParameters()[0].ParameterType, values) };",
450+
" object result = method.Invoke(null, argsToInvoke);",
346451
]
347452
)
348-
harness_lines.extend(common_block.rstrip().splitlines())
349-
elif (
350-
(output == "integer_index" and len(inputs) == 2)
351-
or (
352-
len(sample_inputs) == 2
353-
and isinstance(sample_inputs[0], list)
354-
and not isinstance(sample_inputs[1], list)
355-
and not isinstance(sample_expected, list)
356-
)
357-
):
453+
harness_lines.extend(common_block)
454+
elif shape == "array":
358455
harness_lines.extend(
359456
[
360-
' int[] arr = ParseIntArray(Console.ReadLine() ?? "");',
361-
' int target = int.Parse((Console.ReadLine() ?? "0").Trim());',
362-
" object result = method.Invoke(null, new object[] { arr, target });",
363-
" Console.WriteLine(FormatValue(result));",
457+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
458+
" ParameterInfo[] parameters = candidate.GetParameters();",
459+
" return parameters.Length == 1 && IsFlatIntEnumerableType(parameters[0].ParameterType);",
460+
" });",
461+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
462+
' int[] values = ParseIntArray(Console.ReadLine() ?? "");',
463+
" object[] argsToInvoke = new object[] { BuildFlatIntArg(method.GetParameters()[0].ParameterType, values) };",
464+
" object result = method.Invoke(null, argsToInvoke);",
364465
]
365466
)
366-
elif (
367-
(output == "integer" and inputs == ["integer"])
368-
or (
369-
len(sample_inputs) == 1
370-
and not isinstance(sample_inputs[0], list)
371-
and not isinstance(sample_expected, list)
372-
)
373-
):
467+
harness_lines.extend(common_block)
468+
elif shape == "array_and_scalar":
374469
harness_lines.extend(
375470
[
376-
' int x = int.Parse((Console.ReadLine() ?? "0").Trim());',
377-
" object result = method.Invoke(null, new object[] { x });",
378-
" Console.WriteLine(FormatValue(result));",
471+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
472+
" ParameterInfo[] parameters = candidate.GetParameters();",
473+
" return parameters.Length == 2",
474+
" && IsFlatIntEnumerableType(parameters[0].ParameterType)",
475+
" && IsSimpleScalarType(parameters[1].ParameterType);",
476+
" });",
477+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
478+
' int[] values = ParseIntArray(Console.ReadLine() ?? "");',
479+
' string rawScalar = Console.ReadLine() ?? "";',
480+
" ParameterInfo[] parameters = method.GetParameters();",
481+
" object[] argsToInvoke = new object[] {",
482+
" BuildFlatIntArg(parameters[0].ParameterType, values),",
483+
" ParseScalar(parameters[1].ParameterType, rawScalar),",
484+
" };",
485+
" object result = method.Invoke(null, argsToInvoke);",
379486
]
380487
)
381-
elif (
382-
(output == "integer" and inputs == ["integer", "integer"])
383-
or (
384-
len(sample_inputs) == 2
385-
and all(not isinstance(value, list) for value in sample_inputs)
386-
and not isinstance(sample_expected, list)
387-
)
388-
):
488+
harness_lines.extend(common_block)
489+
elif shape == "scalar":
389490
harness_lines.extend(
390491
[
391-
' int a = int.Parse((Console.ReadLine() ?? "0").Trim());',
392-
' int b = int.Parse((Console.ReadLine() ?? "0").Trim());',
393-
" object result = method.Invoke(null, new object[] { a, b });",
394-
" Console.WriteLine(FormatValue(result));",
492+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
493+
" ParameterInfo[] parameters = candidate.GetParameters();",
494+
" return parameters.Length == 1 && IsSimpleScalarType(parameters[0].ParameterType);",
495+
" });",
496+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
497+
' string rawValue = Console.ReadLine() ?? "";',
498+
" ParameterInfo parameter = method.GetParameters()[0];",
499+
" object result = method.Invoke(null, new object[] { ParseScalar(parameter.ParameterType, rawValue) });",
500+
" Console.WriteLine(FormatResult(result));",
395501
]
396502
)
397-
else:
503+
elif shape == "two_scalars":
398504
harness_lines.extend(
399505
[
400-
' int[] arr = ParseIntArray(Console.ReadLine() ?? "");',
401-
" object result = method.Invoke(null, new object[] { arr });",
506+
" MethodInfo method = ChooseMethod(targetType, preferredNames, candidate => {",
507+
" ParameterInfo[] parameters = candidate.GetParameters();",
508+
" return parameters.Length == 2",
509+
" && IsSimpleScalarType(parameters[0].ParameterType)",
510+
" && IsSimpleScalarType(parameters[1].ParameterType);",
511+
" });",
512+
' if (method == null) { Console.WriteLine("__SKIP_UNSUPPORTED_CSHARP__"); return; }',
513+
' string rawA = Console.ReadLine() ?? "";',
514+
' string rawB = Console.ReadLine() ?? "";',
515+
" ParameterInfo[] parameters = method.GetParameters();",
516+
" object result = method.Invoke(null, new object[] {",
517+
" ParseScalar(parameters[0].ParameterType, rawA),",
518+
" ParseScalar(parameters[1].ParameterType, rawB),",
519+
" });",
520+
" Console.WriteLine(FormatResult(result));",
402521
]
403522
)
404-
harness_lines.extend(common_block.rstrip().splitlines())
405523
406524
harness_lines.extend([" }", "}"])
407525
Path(os.environ["CSHARP_PROGRAM_PATH"]).write_text("\n".join(harness_lines) + "\n")
408526
PY
527+
harness_status=$?
528+
set -e
529+
if [ "$harness_status" -ne 0 ]; then
530+
if [ "$harness_status" -eq 3 ]; then
531+
SKIPPED=$((SKIPPED + 1))
532+
echo "[SKIP] $algo_name: Unsupported C# signature for automated testing"
533+
return
534+
fi
409535
FAILED=$((FAILED + 1))
410536
ERRORS="$ERRORS\n x $algo_name: Failed to generate test harness"
411537
return
412-
}
538+
fi
413539

414540
# Build
415541
if ! dotnet build "$project_dir" -c Release -o "$project_dir/bin" >"$TEMP_DIR/compile_err.txt" 2>&1; then
@@ -509,6 +635,12 @@ else:
509635
actual=$(echo "$actual" | tr -s ' ' | sed 's/^ *//;s/ *$//')
510636
expected_str=$(echo "$expected_str" | tr -s ' ' | sed 's/^ *//;s/ *$//')
511637

638+
if [ "$actual" = "__SKIP_UNSUPPORTED_CSHARP__" ]; then
639+
SKIPPED=$((SKIPPED + 1))
640+
echo "[SKIP] $algo_name: Unsupported C# signature for automated testing"
641+
return
642+
fi
643+
512644
if [ "$actual" = "$expected_str" ]; then
513645
PASSED=$((PASSED + 1))
514646
echo "[PASS] $algo_name - $case_name: $input_str -> $expected_str"
@@ -544,7 +676,7 @@ echo "C# Test Results"
544676
echo "============================================================"
545677
echo " Passed: $PASSED"
546678
echo " Failed: $FAILED"
547-
echo " Skipped: $SKIPPED (no C# implementation)"
679+
echo " Skipped: $SKIPPED"
548680
echo " Total: $TOTAL"
549681

550682
if [ -n "$ERRORS" ]; then

0 commit comments

Comments
 (0)