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; import java.util.regex.Pattern; @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.extractConfig( new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.DTA_TESTCONFIGREGEX)); String image=""; if(config==null) { config = RegexUtil.extractConfig( new FileInputStream(Paths.get(workDirectory.toAbsolutePath().toString(), "config.txt").toFile()), Pattern.compile(RegexUtil.TESTCONFIGREGEX)); if(config==null) { throw new RuntimeException("couldn't find repo config for unittest image extraction"); } image=config.group(4); } else { image=config.group(5); } // 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( image, 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 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; } }