Skip to content

Commit

Permalink
Compile server: PR#88
Browse files Browse the repository at this point in the history
  • Loading branch information
Berygna committed Jun 24, 2024
1 parent 72ee07c commit c9f9658
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class ProblemJudgeService {
private final JudgeService judgeService;

@Transactional(readOnly = true)
public ProblemJudgeResponse judgeProblem(String language, Long algorithmProblemId, String code) {
public synchronized ProblemJudgeResponse judgeProblem(String language, Long algorithmProblemId, String code) {

CodeExecutor codeExecutor = judgeService.getCodeExecutor(language);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package gooroommoon.algofi_compile.db.domain.entity;

import jakarta.persistence.*;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Entity
@Table(name = "algorithmproblem")
public class AlgorithmProblem {
Expand All @@ -18,4 +20,11 @@ public class AlgorithmProblem {

private Integer recommend_time;

public AlgorithmProblem(Long id, String title, String level, String content, Integer recommend_time) {
this.id = id;
this.title = title;
this.level = level;
this.content = content;
this.recommend_time = recommend_time;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Entity
@Table(name = "testcase")
public class TestCase {
Expand All @@ -20,4 +22,11 @@ public class TestCase {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "algorithmproblem_id")
private AlgorithmProblem algorithmProblem;

public TestCase(Long id, String testInput, String testOutput, AlgorithmProblem algorithmProblem) {
this.id = id;
this.testInput = testInput;
this.testOutput = testOutput;
this.algorithmProblem = algorithmProblem;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,20 @@
import gooroommoon.algofi_compile.judge.exception.RequestException;
import gooroommoon.algofi_compile.judge.exception.ServerException;
import gooroommoon.algofi_compile.judge.service.JudgeResult;
import gooroommoon.algofi_compile.judge.service.JudgeService;
import gooroommoon.algofi_compile.judge.service.language.CodeExecutor;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.nio.file.Path;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/judge-input")
public class InputJudgeController {

private final JudgeService judgeService;
private final InputJudgeService inputJudgeService;
@PostMapping
public ResponseEntity<CodeExecutionResponse> judgeInput(@RequestBody CodeExecutionRequest request) {
CodeExecutor codeExecutor = judgeService.getCodeExecutor(request.getLanguage());

Path path = codeExecutor.makeFileFromCode(request.getCode());

Process process = judgeService.executeCode(codeExecutor, path);

StringBuilder output = insertInputAndGetOutput(request.getInput(), process, codeExecutor, path);

String result = output.toString();
String result = inputJudgeService.judgeInput(request.getLanguage(), request.getCode(), request.getInput());
CodeExecutionResponse response = generateResponse(request.getExpected(), result);
return ResponseEntity.ok(response);
}
Expand All @@ -54,15 +42,6 @@ public ResponseEntity<CodeExecutionResponse> handleCodeExecutionException(CodeEx
return new ResponseEntity<>(response, HttpStatus.OK);
}

private StringBuilder insertInputAndGetOutput(String input, Process process, CodeExecutor codeExecutor, Path path) {
try {
return judgeService.insertInputAndGetOutput(process, input);
} finally {
judgeService.destroy(process);
codeExecutor.deleteFile(path);
}
}

private CodeExecutionResponse generateResponse(String expected, String result) {
if (!isCorrect(expected, result)) {
throw new CodeExecutionException(JudgeResult.WRONG_ANSWER);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package gooroommoon.algofi_compile.input;

import gooroommoon.algofi_compile.judge.service.JudgeService;
import gooroommoon.algofi_compile.judge.service.language.CodeExecutor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.nio.file.Path;

@Service
@RequiredArgsConstructor
public class InputJudgeService {
private final JudgeService judgeService;

public synchronized String judgeInput(String language, String code, String input){
CodeExecutor codeExecutor = judgeService.getCodeExecutor(language);

Path path = codeExecutor.makeFileFromCode(code);

Process process = judgeService.executeCode(codeExecutor, path);

StringBuilder output = insertInputAndGetOutput(process, input, codeExecutor, path);

return output.toString();
}

private StringBuilder insertInputAndGetOutput(Process process, String input, CodeExecutor codeExecutor, Path path) {
try {
return judgeService.insertInputAndGetOutput(process, input);
} finally {
judgeService.destroy(process);
codeExecutor.deleteFile(path);
}
}
}
5 changes: 0 additions & 5 deletions algofi-compile/src/main/resources/application-dev.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
spring.datasource.url=jdbc:h2:tcp://localhost/~/test
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package gooroommoon.algofi_compile.db.presentation;

import gooroommoon.algofi_compile.db.application.ProblemJudgeService;
import gooroommoon.algofi_compile.db.domain.TestCaseRepository;
import gooroommoon.algofi_compile.db.domain.entity.AlgorithmProblem;
import gooroommoon.algofi_compile.db.domain.entity.TestCase;
import gooroommoon.algofi_compile.db.presentation.dto.ProblemJudgeRequest;
import gooroommoon.algofi_compile.db.presentation.dto.ProblemJudgeResponse;
import gooroommoon.algofi_compile.judge.exception.CodeExecutionException;
import gooroommoon.algofi_compile.judge.service.JudgeResult;
import gooroommoon.algofi_compile.judge.service.JudgeService;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

@SpringBootTest
class ProblemJudgeControllerTest {

@Mock
private TestCaseRepository testCaseRepository;

@Test
void success() {
//given
AlgorithmProblem algorithmProblem = new AlgorithmProblem(1L, "문자 찾기","1","한 개의 문자열을 입력받고,특정 문자를 입력받아 해당 특정문자가 입력받은 문자열에 몇 개 존재하는지 알아내는 프로그램을 작성하세요. 대소문자를 구분하지 않습니다.문자열의 길이는 100을 넘지 않습니다.",10);
TestCase testCase = new TestCase(1L, "computerprogramming r","3", algorithmProblem);
when(testCaseRepository.findAllByAlgorithmProblemId(any()))
.thenReturn(List.of(testCase));

JudgeService judgeService = new JudgeService();

ProblemJudgeService problemJudgeService = new ProblemJudgeService(testCaseRepository, judgeService);
ProblemJudgeController problemJudgeController = new ProblemJudgeController(problemJudgeService);

//when
ProblemJudgeRequest request = new ProblemJudgeRequest(1L, "java",
"""
import java.util.Scanner;
public class Main {
public int solution(String str, char c){
int answer = 0;
str = str.toUpperCase();
c = Character.toUpperCase(c);
for(char x : str.toCharArray()){
if(x == c) answer++;
}
return answer;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Main T = new Main();
String str = sc.next();
char c = sc.next().charAt(0);
System.out.println(T.solution(str, c));
}
}""");
ResponseEntity<ProblemJudgeResponse> response = problemJudgeController.judgeProblem(request);
assertThat(Objects.requireNonNull(response.getBody()).getMessage()).isEqualTo(JudgeResult.ACCEPTED.getMessage());
}

@Test
void concurrencyTest() throws InterruptedException {
//given
AlgorithmProblem algorithmProblem = new AlgorithmProblem(1L, "문자 찾기","1","한 개의 문자열을 입력받고,특정 문자를 입력받아 해당 특정문자가 입력받은 문자열에 몇 개 존재하는지 알아내는 프로그램을 작성하세요. 대소문자를 구분하지 않습니다.문자열의 길이는 100을 넘지 않습니다.",10);
TestCase testCase = new TestCase(1L, "computerprogramming r","3", algorithmProblem);
when(testCaseRepository.findAllByAlgorithmProblemId(any()))
.thenReturn(List.of(testCase));

JudgeService judgeService = new JudgeService();

ProblemJudgeService problemJudgeService = new ProblemJudgeService(testCaseRepository, judgeService);
ProblemJudgeController problemJudgeController = new ProblemJudgeController(problemJudgeService);

int threadNum = 2;
CountDownLatch doneSignal = new CountDownLatch(threadNum);
ExecutorService executorService = Executors.newFixedThreadPool(threadNum);

ProblemJudgeRequest request1 = new ProblemJudgeRequest(1L, "java",
"""
import java.util.Scanner;
public class Main {
public int solution(String str, char c){
int answer = 0;
str = str.toUpperCase();
c = Character.toUpperCase(c);
for(char x : str.toCharArray()){
if(x == c) answer++;
}
return answer;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Main T = new Main();
String str = sc.next();
char c = sc.next().charAt(0);
System.out.println(T.solution(str, c));
}
}""");

ProblemJudgeRequest request2 = new ProblemJudgeRequest(1L, "java",
"""
public class Main {
public static void main(String[] args) {
System.out.println();
}
}""");

AtomicBoolean success1 = new AtomicBoolean();
AtomicBoolean success2 = new AtomicBoolean();

//when

executorService.execute(() -> {
try {
problemJudgeController.judgeProblem(request1);
success1.set(true);
} catch (CodeExecutionException e) {
success1.set(false);
} finally {
doneSignal.countDown();
}
});

executorService.execute(() -> {
try {
problemJudgeController.judgeProblem(request2);
success2.set(true);
} catch (CodeExecutionException e) {
success2.set(false);
} finally {
doneSignal.countDown();
}
});

doneSignal.await();
executorService.shutdown();

//then
assertAll(
() -> assertThat(success1.get()).isTrue(),
() -> assertThat(success2.get()).isFalse()
);
}
}
Loading

0 comments on commit c9f9658

Please sign in to comment.