Skip to content
GitLab
Explore
Projects
Groups
Snippets
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CoTA
cota-backend
Commits
b294c18e
Commit
b294c18e
authored
1 year ago
by
Lückemeyer
Browse files
Options
Download
Email Patches
Plain Diff
refactor & extend url listener
parent
420073ca
master
amg-dev-volumes
amg-svn+ssh
No related merge requests found
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
Dockerfile
+50
-48
Dockerfile
src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java
+97
-97
...a/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java
src/main/java/de/hftstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
+98
-90
...tstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
+145
-143
...ava/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
src/main/resources/application.properties
+1
-1
src/main/resources/application.properties
with
391 additions
and
379 deletions
+391
-379
Dockerfile
+
50
-
48
View file @
b294c18e
# 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"]
This diff is collapsed.
Click to expand it.
src/main/java/de/hftstuttgart/dtabackend/rest/v1/task/TaskUpload.java
+
97
-
97
View file @
b294c18e
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
;
}
}
This diff is collapsed.
Click to expand it.
src/main/java/de/hftstuttgart/dtabackend/utils/CompetencyAssessmentUtil.java
+
98
-
90
View file @
b294c18e
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
(
";"
));
}
}
This diff is collapsed.
Click to expand it.
src/main/java/de/hftstuttgart/dtabackend/utils/ExecuteTestUtil.java
+
145
-
143
View file @
b294c18e
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
;
}
}
This diff is collapsed.
Click to expand it.
src/main/resources/application.properties
+
1
-
1
View file @
b294c18e
...
...
@@ -7,7 +7,7 @@ spring.http.multipart.max-file-size=5Mb
###############################################
# Holds the uploaded Zip-Files
tests.tmp.dir
=
/tmp
/dt
t
-tests
tests.tmp.dir
=
~
/dt
a
-tests
host.tests.tmp.dir
=
${tests.tmp.dir}
data.dir
=
/data
data.dir.test.folder.name
=
UnitTests
...
...
This diff is collapsed.
Click to expand it.
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Explore
Projects
Groups
Snippets