Commit b294c18e authored by Lückemeyer's avatar Lückemeyer
Browse files

refactor & extend url listener

parent 420073ca
Pipeline #9062 passed with stage
# base image to build a JRE
FROM amazoncorretto:17.0.3-alpine as corretto-jdk
# required for strip-debug to work
RUN apk add --no-cache binutils
# Build small JRE image
RUN $JAVA_HOME/bin/jlink \
--verbose \
--add-modules ALL-MODULE-PATH \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /customjre
# main app image
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"
ENV SPRING_CONFIG_ADDITIONAL_LOCATION "file:/data/config/"
# copy JRE from the base image
COPY --from=corretto-jdk /customjre $JAVA_HOME
# Add app user
ARG AUSER=appuser
ARG AGID=137
ENV USER=$AUSER
ENV GID=$AGID
ARG BUILD_NUMBER=
RUN addgroup -g $GID -S docker
RUN adduser --no-create-home -u 1000 -G docker -D $USER
# Prepare environment.
# Create needed folders
RUN mkdir /data && \
mkdir /data/config && \
chown -R $USER /data
VOLUME /data
COPY --chown=1000:$GID target/dta-backend-$BUILD_NUMBER.jar app.jar
USER 1000:$GID
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
# base image to build a JRE
FROM amazoncorretto:17.0.3-alpine as corretto-jdk
# required for strip-debug to work
RUN apk add --no-cache binutils
# Build small JRE image
RUN $JAVA_HOME/bin/jlink \
--verbose \
--add-modules ALL-MODULE-PATH \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /customjre
# main app image
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"
ENV SPRING_CONFIG_ADDITIONAL_LOCATION "file:/data/config/"
# copy JRE from the base image
COPY --from=corretto-jdk /customjre $JAVA_HOME
# Add app user
ARG AUSER=appuser
ARG AGID=137
ENV USER=$AUSER
ENV GID=$AGID
ARG BUILD_NUMBER=
# Create docker group identical to host
RUN addgroup -g $GID -S docker
RUN adduser --no-create-home -u 1000 -G docker -D $USER
# Prepare environment.
# Create needed folders
RUN mkdir /data && \
mkdir /data/config && \
chown -R $USER /data
VOLUME /data
COPY --chown=1000:$GID target/dta-backend-$BUILD_NUMBER.jar app.jar
USER 1000:$GID
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
package de.hftstuttgart.dtabackend.rest.v1.task;
import de.hftstuttgart.dtabackend.utils.*;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tika.Tika;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import jakarta.servlet.annotation.MultipartConfig;
/**
* Rest controller for everything related to the TASK files
*/
@RestController
@RequestMapping("/v1/task")
@MultipartConfig
public class TaskUpload {
private static final Logger LOG = LogManager.getLogger(TaskUpload.class);
private final JGitUtil jGitUtil;
private final Path testTmpPath;
private final ExecuteTestUtil executeTestUtil;
public TaskUpload(
Environment env,
JGitUtil jGitUtil,
ExecuteTestUtil executeTestUtil
) {
this.jGitUtil = jGitUtil;
this.executeTestUtil = executeTestUtil;
// set path of temporary directory on host and inside our container
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
}
@RequestMapping(method = RequestMethod.POST)
public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef,
@RequestParam("assignmentId") String assignmentId
) throws IOException, InterruptedException {
LOG.info("submission for testing received");
LOG.debug("creating new temporary directory");
Path workDirectory = Files.createTempDirectory(testTmpPath, "dtt");
LOG.debug(String.format("working dir for test is: %s", workDirectory.toAbsolutePath().toString()));
// define paths for the test, the submission and where the result is to be expected afterwards
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src");
String mimeInfo = new Tika().detect(taskFileRef.getInputStream());
switch (mimeInfo) {
case "text/plain":
LOG.debug("textfile uploaded, searching for dtt config");
// find URI in config file
Matcher config = RegexUtil.findStudentConfig(taskFileRef.getInputStream());
LOG.debug("calling repo clone");
jGitUtil.cloneRepository(config, srcPath.toAbsolutePath().toString());
break;
case "application/zip":
LOG.debug("zip archive uploaded, extracting content as student submission");
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
break;
default:
String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo);
LOG.error(msg);
throw new RuntimeException(msg);
}
// run test
LOG.debug("calling test execution");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
if (mimeInfo.equals("text/plain")) {
LOG.info("check for provided Ticketsystem information");
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary);
}
taskFileRef.getInputStream().close();
LOG.info("submission tested successfully");
return resultSummary;
}
}
package de.hftstuttgart.dtabackend.rest.v1.task;
import de.hftstuttgart.dtabackend.utils.*;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tika.Tika;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import jakarta.servlet.annotation.MultipartConfig;
/**
* Rest controller for everything related to the TASK files
*/
@RestController
@RequestMapping("/v1/task/*")
@MultipartConfig
public class TaskUpload {
private static final Logger LOG = LogManager.getLogger(TaskUpload.class);
private final JGitUtil jGitUtil;
private final Path testTmpPath;
private final ExecuteTestUtil executeTestUtil;
public TaskUpload(
Environment env,
JGitUtil jGitUtil,
ExecuteTestUtil executeTestUtil
) {
this.jGitUtil = jGitUtil;
this.executeTestUtil = executeTestUtil;
// set path of temporary directory on host and inside our container
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
}
@RequestMapping(method = RequestMethod.POST)
public ResultSummary uploadAndTestFile(@RequestParam("taskFile") MultipartFile taskFileRef,
@RequestParam("assignmentId") String assignmentId
) throws IOException, InterruptedException {
LOG.info("submission for testing received");
LOG.debug("creating new temporary directory");
Path workDirectory = Files.createTempDirectory(testTmpPath, "dtt");
LOG.debug(String.format("working dir for test is: %s", workDirectory.toAbsolutePath().toString()));
// define paths for the test, the submission and where the result is to be expected afterwards
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "src");
String mimeInfo = new Tika().detect(taskFileRef.getInputStream());
switch (mimeInfo) {
case "text/plain":
LOG.debug("textfile uploaded, searching for dtt config");
// find URI in config file
Matcher config = RegexUtil.findStudentConfig(taskFileRef.getInputStream());
LOG.debug("calling repo clone");
jGitUtil.cloneRepository(config, srcPath.toAbsolutePath().toString());
break;
case "application/zip":
LOG.debug("zip archive uploaded, extracting content as student submission");
ArchiveUtil.extractProjectFromZip(taskFileRef.getInputStream(), srcPath.toAbsolutePath());
break;
default:
String msg = String.format("couldn't process uploaded file with mime type %s", mimeInfo);
LOG.error(msg);
throw new RuntimeException(msg);
}
// run test
LOG.debug("calling test execution");
ResultSummary resultSummary = executeTestUtil.runTests(assignmentId, workDirectory);
if (mimeInfo.equals("text/plain")) {
LOG.info("check for provided Ticketsystem information");
UnifiedTicketingUtil.reportResults(taskFileRef.getInputStream(), resultSummary);
}
taskFileRef.getInputStream().close();
LOG.info("submission tested successfully");
return resultSummary;
}
}
package de.hftstuttgart.dtabackend.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Path;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.hftstuttgart.dtabackend.models.Result;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import java.io.FileNotFoundException;
public class CompetencyAssessmentUtil {
private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class);
public static String TEST_COMPETENCY_MANIFEST_FILE_NAME="competency-tests.mft";
/* public static void main(String[] args) throws StreamReadException, DatabindException, MalformedURLException, IOException {
List<TestCompetencyProfile> testCompetencyProfiles=readTestCompetencyProfiles(Path.of(args[0]), args[1]);
sumTestCompetencyProfiles(testCompetencyProfiles);
ObjectMapper objectMapper = new ObjectMapper();
ResultSummary resultSummary = objectMapper.readValue(
new File(Path.of(args[0]).toFile(), args[2]).toURI().toURL(),
ResultSummary.class);
sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary);
}
*/
public static float[] sumTestCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles) {
float[] tcpTotalProfile=new float[TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(TestCompetencyProfile currentProfile: testCompetencyProfiles) {
tcpTotalProfile=TestCompetencyProfile.competencySum(tcpTotalProfile, currentProfile.competencyAssessments);
}
return tcpTotalProfile;
}
public static float[] sumSuccessfulCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary) {
float[] sumSuccessful=new float[TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(Result currentResult: resultSummary.results) {
if(currentResult.state==Result.State.SUCCESS.ordinal()) {
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=(currentResult.packageName!=null)?currentResult.packageName:"";
currentProfile.testClassName=(currentResult.className!=null)?currentResult.className:"";
currentProfile.testName=(currentResult.name!=null)?currentResult.name:"";
int testIndex=testCompetencyProfiles.indexOf(currentProfile);
if(testIndex!=-1) {
sumSuccessful=TestCompetencyProfile.competencySum(sumSuccessful, testCompetencyProfiles.get(testIndex).competencyAssessments);
}
}
}
return sumSuccessful;
}
public static List<TestCompetencyProfile> readTestCompetencyProfiles(Path testPath, String fileName) {
List<TestCompetencyProfile> testCompetencyProfiles=new ArrayList<TestCompetencyProfile>();
try {
BufferedReader testCompetencyManifest=new BufferedReader(new FileReader(new File(testPath.toFile(), fileName)));
String testEntry=testCompetencyManifest.readLine();
while(testEntry!=null)
{
String[] testEntyComponents=testEntry.split(TestCompetencyProfile.COMPETENCY_SEPARATOR);
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=testEntyComponents[0];
currentProfile.testClassName=testEntyComponents[1];
currentProfile.testName=testEntyComponents[2];
for(int competencyIndex=0; competencyIndex<TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex]=Float.valueOf(testEntyComponents[competencyIndex+3]);
}
testCompetencyProfiles.add(currentProfile);
testEntry=testCompetencyManifest.readLine();
}
testCompetencyManifest.close();
LOG.info("Added "+testCompetencyProfiles.size()+" test competency profiles from test competency manifest. Optional agent functionality enabled.");
} catch (FileNotFoundException e) {
LOG.info("Test competency manifest file for agent feedback not found. Skipping optional functionality.");
testCompetencyProfiles=null;
} catch (IOException e) {
LOG.info("Test competency manifest file for agent feedback unreadable. Skipping optional functionality.");
testCompetencyProfiles=null;
}
return testCompetencyProfiles;
}
}
package de.hftstuttgart.dtabackend.utils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.hftstuttgart.dtabackend.models.Result;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import java.io.FileNotFoundException;
public class CompetencyAssessmentUtil {
private static final Logger LOG = LogManager.getLogger(CompetencyAssessmentUtil.class);
public static String TEST_COMPETENCY_MANIFEST_FILE_NAME="competency-tests.mft";
public static void main(String[] args) throws StreamReadException, DatabindException, MalformedURLException, IOException {
ResultSummary summary=ExecuteTestUtil.generateResult(Path.of(args[0]), Path.of(args[1]));
System.out.println(summary.successfulTestCompetencyProfile);
}
public static float[] sumTestCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles) {
float[] tcpTotalProfile=new float[TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(TestCompetencyProfile currentProfile: testCompetencyProfiles) {
tcpTotalProfile=TestCompetencyProfile.competencySum(tcpTotalProfile, currentProfile.competencyAssessments);
}
return tcpTotalProfile;
}
public static float[] sumSuccessfulCompetencyProfiles(List<TestCompetencyProfile> testCompetencyProfiles, ResultSummary resultSummary) {
float[] sumSuccessful=new float[TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS];
for(Result currentResult: resultSummary.results) {
if(currentResult.state==Result.State.SUCCESS.ordinal()) {
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=(currentResult.packageName!=null)?currentResult.packageName:"";
currentProfile.testClassName=(currentResult.className!=null)?currentResult.className:"";
currentProfile.testName=(currentResult.name!=null)?currentResult.name:"";
int testIndex=testCompetencyProfiles.indexOf(currentProfile);
if(testIndex!=-1) {
sumSuccessful=TestCompetencyProfile.competencySum(sumSuccessful, testCompetencyProfiles.get(testIndex).competencyAssessments);
}
}
}
return sumSuccessful;
}
public static List<TestCompetencyProfile> readTestCompetencyProfiles(Path testPath, String fileName) {
List<TestCompetencyProfile> testCompetencyProfiles=new ArrayList<TestCompetencyProfile>();
try {
BufferedReader testCompetencyManifest=new BufferedReader(new FileReader(new File(testPath.toFile(), fileName)));
String testEntry=testCompetencyManifest.readLine();
while(testEntry!=null)
{
String[] testEntyComponents=testEntry.split(TestCompetencyProfile.COMPETENCY_SEPARATOR);
TestCompetencyProfile currentProfile=new TestCompetencyProfile();
currentProfile.testPackageName=testEntyComponents[0];
currentProfile.testClassName=testEntyComponents[1];
currentProfile.testName=testEntyComponents[2];
for(int competencyIndex=0; competencyIndex<TestCompetencyProfile.MAX_COMPETENCY_DIMENSIONS; competencyIndex++) {
currentProfile.competencyAssessments[competencyIndex]=Float.valueOf(testEntyComponents[competencyIndex+3]);
}
testCompetencyProfiles.add(currentProfile);
testEntry=testCompetencyManifest.readLine();
}
testCompetencyManifest.close();
LOG.info("Added "+testCompetencyProfiles.size()+" test competency profiles from test competency manifest. Optional agent functionality enabled.");
} catch (FileNotFoundException e) {
LOG.info("Test competency manifest file for agent feedback not found. Skipping optional functionality.");
testCompetencyProfiles=null;
} catch (IOException e) {
LOG.info("Test competency manifest file for agent feedback unreadable. Skipping optional functionality.");
testCompetencyProfiles=null;
}
return testCompetencyProfiles;
}
public static String packFloats(float[] array) {
return IntStream.range(0, array.length)
.mapToObj(i -> String.valueOf(array[i]))
.collect(Collectors.joining(";"));
}
}
package de.hftstuttgart.dtabackend.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Component
public class ExecuteTestUtil {
private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class);
private final JGitUtil jGitUtil;
private final DockerUtil dockerUtil;
private final String assignmentBasePath;
private final Path testTmpPathHost;
private final Path testTmpPath;
public ExecuteTestUtil(
Environment env,
JGitUtil jGitUtil,
DockerUtil dockerUtil
) {
this.jGitUtil = jGitUtil;
this.dockerUtil = dockerUtil;
// set base path for assignments to be stored
Path p = Paths.get(
env.getProperty("data.dir"),
env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString();
// set path of temporary directory on host and inside our container
this.testTmpPathHost = Paths.get(env.getProperty("host.tests.tmp.dir"));
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
}
public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException {
// define paths for the test, the submission and where the result is to be expected afterwards
Path testPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/test");
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/src");
Path resultPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/result");
// clone stored test to tmpdir
LOG.debug("copying pre-downloaded unitttest repo");
FileUtil.copyFolder(
Paths.get(
assignmentBasePath,
assignmentId
),
testPath
);
LOG.debug("copy test config");
Files.copy(
Paths.get(
assignmentBasePath,
assignmentId + ".txt"
),
Paths.get(
workDirectory.toAbsolutePath().toString(),
"config.txt"
)
);
Files.createDirectory(resultPath);
LOG.info("reading test config");
Matcher config = RegexUtil.findProfessorConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()));
// define the paths to mount as Binds from Host to the test-container
Path testPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
testPath.getName(testPath.getNameCount()-1).toString()
);
Path srcPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
srcPath.getName(srcPath.getNameCount()-1).toString()
);
Path resultPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
resultPath.getName(resultPath.getNameCount()-1).toString()
);
// start test-container with professor given image and bind mounts for test, submission and result
dockerUtil.runContainer(
config.group(4),
new Bind(testPathHost.toAbsolutePath().toString(), new Volume("/data/test")),
new Bind(srcPathHost.toAbsolutePath().toString(), new Volume("/data/src")),
new Bind(resultPathHost.toAbsolutePath().toString(), new Volume("/data/result"))
);
// define expected result file
File resultFile = Paths.get(resultPath.toAbsolutePath().toString(), "result.json").toFile();
// check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath()));
throw new RuntimeException("no resultfile found");
}
LOG.debug("parse results json");
ObjectMapper objectMapper = new ObjectMapper();
ResultSummary resultSummary = objectMapper.readValue(
resultFile.toURI().toURL(),
ResultSummary.class);
LOG.info("Checking for optional test competency profile information for paedagogical agent functionality...");
List<TestCompetencyProfile> testCompetencyProfiles=CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME);
if(testCompetencyProfiles!=null) {
LOG.info("Found optional test competency profiles, generating agent profile data...");
resultSummary.overallTestCompetencyProfile=packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles));
resultSummary.successfulTestCompetencyProfile=packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary));
}
return resultSummary;
}
private static String packFloats(float[] array) {
return IntStream.range(0, array.length)
.mapToObj(i -> String.valueOf(array[i]))
.collect(Collectors.joining(";"));
}
}
package de.hftstuttgart.dtabackend.utils;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Volume;
import de.hftstuttgart.dtabackend.models.ResultSummary;
import de.hftstuttgart.dtabackend.models.TestCompetencyProfile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.regex.Matcher;
@Component
public class ExecuteTestUtil {
private static final Logger LOG = LogManager.getLogger(ExecuteTestUtil.class);
private final JGitUtil jGitUtil;
private final DockerUtil dockerUtil;
private final String assignmentBasePath;
private final Path testTmpPathHost;
private final Path testTmpPath;
public ExecuteTestUtil(
Environment env,
JGitUtil jGitUtil,
DockerUtil dockerUtil
) {
this.jGitUtil = jGitUtil;
this.dockerUtil = dockerUtil;
// set base path for assignments to be stored
Path p = Paths.get(
env.getProperty("data.dir"),
env.getProperty("data.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString();
// set path of temporary directory on host and inside our container
this.testTmpPathHost = Paths.get(env.getProperty("host.tests.tmp.dir"));
this.testTmpPath = Paths.get(env.getProperty("tests.tmp.dir"));
}
public ResultSummary runTests(String assignmentId, Path workDirectory) throws IOException, InterruptedException {
// define paths for the test, the submission and where the result is to be expected afterwards
Path testPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/test");
Path srcPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/src");
Path resultPath = Paths.get(workDirectory.toAbsolutePath().toString(), "/result");
// clone stored test to tmpdir
LOG.debug("copying pre-downloaded unitttest repo");
FileUtil.copyFolder(
Paths.get(
assignmentBasePath,
assignmentId
),
testPath
);
LOG.debug("copy test config");
Files.copy(
Paths.get(
assignmentBasePath,
assignmentId + ".txt"
),
Paths.get(
workDirectory.toAbsolutePath().toString(),
"config.txt"
)
);
Files.createDirectory(resultPath);
LOG.info("reading test config");
Matcher config = RegexUtil.findProfessorConfig(
new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()));
// define the paths to mount as Binds from Host to the test-container
Path testPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
testPath.getName(testPath.getNameCount()-1).toString()
);
Path srcPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
srcPath.getName(srcPath.getNameCount()-1).toString()
);
Path resultPathHost = Paths.get(
testTmpPathHost.toAbsolutePath().toString(),
workDirectory.getName(workDirectory.getNameCount()-1).toString(),
resultPath.getName(resultPath.getNameCount()-1).toString()
);
// start test-container with professor given image and bind mounts for test, submission and result
dockerUtil.runContainer(
config.group(4),
new Bind(testPathHost.toAbsolutePath().toString(), new Volume("/data/test")),
new Bind(srcPathHost.toAbsolutePath().toString(), new Volume("/data/src")),
new Bind(resultPathHost.toAbsolutePath().toString(), new Volume("/data/result"))
);
ResultSummary resultSummary = generateResult(resultPath, testPathHost);
return resultSummary;
}
static ResultSummary generateResult(Path resultPath, Path testPathHost)
throws IOException, StreamReadException, DatabindException, MalformedURLException {
// define expected result file
File resultFile = Paths.get(resultPath.toAbsolutePath().toString(), "result.json").toFile();
// check if result file is there
if (!resultFile.exists() || !resultFile.isFile()) {
LOG.error(String.format("Could not find result file in %s", resultFile.getAbsolutePath()));
throw new RuntimeException("no resultfile found");
}
LOG.debug("parse results json");
ObjectMapper objectMapper = new ObjectMapper();
ResultSummary resultSummary = objectMapper.readValue(
resultFile.toURI().toURL(),
ResultSummary.class);
LOG.debug("result json returned time "+ resultSummary.timestamp + " with "+resultSummary.results.size()+ " test results.");
LOG.info("Checking for optional test competency profile information for paedagogical agent functionality...");
List<TestCompetencyProfile> testCompetencyProfiles=CompetencyAssessmentUtil.readTestCompetencyProfiles(testPathHost, CompetencyAssessmentUtil.TEST_COMPETENCY_MANIFEST_FILE_NAME);
if(testCompetencyProfiles!=null) {
LOG.info("Found optional test competency profiles, generating agent profile data...");
resultSummary.overallTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumTestCompetencyProfiles(testCompetencyProfiles));
resultSummary.successfulTestCompetencyProfile=CompetencyAssessmentUtil.packFloats(CompetencyAssessmentUtil.sumSuccessfulCompetencyProfiles(testCompetencyProfiles, resultSummary));
}
return resultSummary;
}
}
......@@ -7,7 +7,7 @@ spring.http.multipart.max-file-size=5Mb
###############################################
# Holds the uploaded Zip-Files
tests.tmp.dir=/tmp/dtt-tests
tests.tmp.dir=~/dta-tests
host.tests.tmp.dir=${tests.tmp.dir}
data.dir=/data
data.dir.test.folder.name=UnitTests
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment