package space.anyi.codesandbox; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import space.anyi.codesandbox.docker.DockerClientFactory; import space.anyi.serve.entity.contest.Submission; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; /** * @fileName: DockerCodeSandBoxTest * @projectName: serve * @package: space.anyi.codesandbox * @author: yangyi * @date:14/10/2026 6:00 pm * @description: Docker 代码沙箱测试,需本机 Docker daemon 运行中,判题镜像缺失时自动拉取 */ class DockerCodeSandBoxTest { private static DockerCodeSandBox dockerCodeSandBox; @BeforeAll static void setUp() { dockerCodeSandBox = new DockerCodeSandBox(new DockerClientFactory().dockerClient()); } @Test void testAcAndMultiLineOutput() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " java.util.Scanner in = new java.util.Scanner(System.in);\n" + " int a = in.nextInt();\n" + " int b = in.nextInt();\n" + " System.out.println(a + b);\n" + " }\n" + "}"; List testCases = List.of(new TestCase(1L, "1 2", "3"), new TestCase(2L, "2 2", "4")); CodeContext codeContext = new CodeContext(1L, code, "java", 2000L, 256 * 1024L, testCases); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_AC, codeResponse.status()); assertEquals(2, codeResponse.testCaseResponses().size()); } @Test void testCompileError() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " int x = \"类型不匹配\";\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(2L, code, "java", 2000L, 256 * 1024L, List.of(new TestCase(1L, "", ""))); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_CE, codeResponse.status()); } @Test void testRuntimeError() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " int x = 1 / 0;\n" + " System.out.println(x);\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(3L, code, "java", 2000L, 256 * 1024L, List.of(new TestCase(1L, "", ""))); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_RE, codeResponse.status()); } @Test void testWrongAnswer() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " System.out.println(9);\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(4L, code, "java", 2000L, 256 * 1024L, List.of(new TestCase(1L, "", "3"))); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_WA, codeResponse.status()); } @Test void testTrailingWhitespaceIgnored() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"3 \\t\");\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(5L, code, "java", 2000L, 256 * 1024L, List.of(new TestCase(1L, "", "3"))); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_AC, codeResponse.status()); } @Test void testMultiByteUtf8Output() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"你好,世界!\");\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(7L, code, "java", 2000L, 256 * 1024L, List.of(new TestCase(1L, "", "你好,世界!"))); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); assertEquals(Submission.STATUS_AC, codeResponse.status()); } @Test void testInfiniteLoopTle() { String code = "public class Main {\n" + " public static void main(String[] args) {\n" + " while (true) {}\n" + " }\n" + "}"; CodeContext codeContext = new CodeContext(6L, code, "java", 1000L, 256 * 1024L, List.of(new TestCase(1L, "", ""))); long start = System.currentTimeMillis(); CodeResponse codeResponse = dockerCodeSandBox.process(codeContext); long elapsed = System.currentTimeMillis() - start; assertEquals(Submission.STATUS_TLE, codeResponse.status()); assertEquals(1, codeResponse.testCaseResponses().size()); // 超时应及时生效,允许容器创建/清理的开销 assertEquals(true, elapsed < 15_000, "TLE 超时未生效,实际耗时:" + elapsed + "ms"); } }