Merge commit '880c3ae75fd920c5ecdab65733e69f331e8e8782' as 'solution/H05'

This commit is contained in:
Oshgnacknak 2025-01-11 16:41:10 +01:00
commit c1fb4994fb
63 changed files with 2875 additions and 0 deletions

View file

@ -0,0 +1,12 @@
# Editor configuration, see https://editorconfig.org
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[{*.yml,*.json}]
indent_size = 2

88
solution/H05/.gitignore vendored Normal file
View file

@ -0,0 +1,88 @@
### Intellij ###
.idea/
*.iws
/out/
*.iml
.idea_modules/
atlassian-ide-plugin.xml
### VS-Code ###
.vscode/
.VSCodeCounter/
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
.externalToolBuilders/
*.launch
.factorypath
.recommenders/
.apt_generated/
.project
.classpath
### Linux ###
*~
.fuse_hidden*
.directory
.Trash-*
.nfs*
### macOS ###
.DS_Store
.AppleDouble
.LSOverride
Icon
._*
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
*.stackdump
[Dd]esktop.ini
$RECYCLE.BIN/
*.lnk
### Gradle ###
.gradle
/build/
out/
gradle-app.setting
!gradle-wrapper.jar
.gradletasknamecache
*.hprof
screenshots/
jagr.conf

4
solution/H05/README.md Normal file
View file

@ -0,0 +1,4 @@
# Musterlösung zu Hausübung 05
Beachten Sie die Hinweise zum Herunterladen, Importieren, Bearbeitern, Exportieren und Hochladen in unserem
[Studierenden-Guide](https://wiki.tudalgo.org/)

View file

@ -0,0 +1,37 @@
plugins {
alias(libs.plugins.algomate)
alias(libs.plugins.style)
}
version = file("version").readLines().first()
exercise {
assignmentId.set("h05")
}
submission {
// ACHTUNG!
// Setzen Sie im folgenden Bereich Ihre TU-ID (NICHT Ihre Matrikelnummer!), Ihren Nachnamen und Ihren Vornamen
// in Anführungszeichen (z.B. "ab12cdef" für Ihre TU-ID) ein!
// BEISPIEL:
// studentId = "ab12cdef"
// firstName = "sol_first"
// lastName = "sol_last"
studentId = "ab12cdef"
firstName = "sol_first"
lastName = "sol_last"
// Optionally require own tests for mainBuildSubmission task. Default is false
requireTests = false
}
jagr {
graders {
val graderPublic by getting
val graderPrivate by creating {
parent(graderPublic)
graderName.set("FOP-2425-H05-Private")
rubricProviderName.set("h05.H05_RubricProvider")
}
}
}

View file

@ -0,0 +1,13 @@
[versions]
algoutils = "0.9.1-SNAPSHOT"
[libraries]
annotations = "org.jetbrains:annotations:24.1.0"
algoutils-student = { module = "org.tudalgo:algoutils-student", version.ref = "algoutils" }
algoutils-tutor = { module = "org.tudalgo:algoutils-tutor", version.ref = "algoutils" }
junit-core = { module = "org.junit.jupiter:junit-jupiter", version = "5.10.2" }
asm-tree = { module = "org.ow2.asm:asm-tree", version = "9.6"}
[plugins]
algomate = { id = "org.tudalgo.algomate", version = "0.7.1" }
style = { id = "org.sourcegrade.style", version = "3.0.0" }

Binary file not shown.

View file

@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

245
solution/H05/gradlew vendored Executable file
View file

@ -0,0 +1,245 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
solution/H05/gradlew.bat vendored Normal file
View file

@ -0,0 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1,11 @@
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenLocal()
maven("https://s01.oss.sonatype.org/content/repositories/snapshots")
maven("https://jitpack.io")
mavenCentral()
}
}
rootProject.name = "H05-Root"

View file

@ -0,0 +1,101 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Set;
import static h05.TestUtils.assertStringEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class AirspaceTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testScanAirspace_Empty() {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
Airspace airspace = Airspace.get();
TestUtils.clearAirspace();
Delegation.disable(MethodHeader.of(Airspace.class, "scanAirspace"));
call(airspace::scanAirspace, emptyContext(), result -> "An exception occurred while invoking scanAirspace()");
assertStringEquals("Scanning...\nAirspace is empty", outputStream.toString(), emptyContext(),
result -> "scanAirspace() printed the wrong message to System.out");
} finally {
System.setOut(oldOut);
}
}
@Test
public void testScanAirspace_CargoPlane() {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
String aircraftRegistration = "D-FLOP";
CargoPlane cargoPlaneInstance = new CargoPlane(aircraftRegistration, 0, FuelType.JetA, 1000);
Context context = contextBuilder()
.add("plane in airspace", cargoPlaneInstance)
.build();
Airspace airspace = Airspace.get();
TestUtils.clearAirspace();
Delegation.disable(MethodHeader.of(Airspace.class, "register", Flying.class));
call(() -> airspace.register(cargoPlaneInstance), context, result ->
"An exception occurred while invoking register(Flying)");
call(airspace::scanAirspace, emptyContext(), result -> "An exception occurred while invoking scanAirspace()");
assertStringEquals("Scanning...\n%s is flying in airspace".formatted(aircraftRegistration),
outputStream.toString(),
context,
result -> "scanAirspace() printed the wrong message to System.out");
} finally {
System.setOut(oldOut);
}
}
@Test
public void testScanAirspace_PassengerPlane() {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
String aircraftRegistration = "D-FLOP";
int passengerCount = 100;
PassengerPlane passengerPlaneInstance = new PassengerPlane(aircraftRegistration, 0, FuelType.JetA, 1000, 5);
passengerPlaneInstance.board(passengerCount);
Context context = contextBuilder()
.add("plane in airspace", passengerPlaneInstance)
.build();
Airspace airspace = Airspace.get();
TestUtils.clearAirspace();
Delegation.disable(MethodHeader.of(Airspace.class, "register", Flying.class));
call(() -> airspace.register(passengerPlaneInstance), context, result ->
"An exception occurred while invoking register(Flying)");
call(airspace::scanAirspace, emptyContext(), result -> "An exception occurred while invoking scanAirspace()");
assertStringEquals("Scanning...\n%s is flying in airspace (%d PAX)".formatted(aircraftRegistration, passengerCount),
outputStream.toString(),
context,
result -> "scanAirspace() printed the wrong message to System.out");
} finally {
System.setOut(oldOut);
}
}
}

View file

@ -0,0 +1,151 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import static h05.TestUtils.assertDoubleEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.assertEquals;
@TestForSubmission
public class CargoPlaneTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(CargoPlane.class);
assertTrue(originalClassHeader.getInterfaceTypes().contains(CarriesCargo.class), emptyContext(),
result -> "Class CargoPlane does not implement interface CarriesCargo");
}
@Test
public void testConstructor() {
String aircraftRegistration = "D-DFOP";
int baseWeight = 100;
FuelType fuelType = FuelType.JetA;
double fuelCapacity = 50;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("baseWeight", baseWeight)
.add("fuelType", fuelType)
.add("fuelCapacity", fuelCapacity)
.build();
Delegation.disable(MethodHeader.of(CargoPlane.class, String.class, int.class, FuelType.class, double.class));
CargoPlane cargoPlaneInstance = callObject(
() -> new CargoPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity),
context,
result -> "An exception occurred while invoking constructor of CargoPlane");
assertEquals(aircraftRegistration,
FieldHeader.of(Plane.class, "aircraftRegistration").getValue(cargoPlaneInstance),
context,
result -> "Field aircraftRegistration has incorrect value");
assertEquals(baseWeight,
FieldHeader.of(Plane.class, "baseWeight").getValue(cargoPlaneInstance),
context,
result -> "Field baseWeight has incorrect value");
assertEquals(fuelType,
FieldHeader.of(Plane.class, "fuelType").getValue(cargoPlaneInstance),
context,
result -> "Field fuelType has incorrect value");
assertDoubleEquals(fuelCapacity,
FieldHeader.of(Plane.class, "fuelCapacity").getValue(cargoPlaneInstance),
context,
result -> "Field fuelCapacity has incorrect value");
}
@Test
public void testMass() {
int getSumReturnValue = 123;
Substitution.enable(MethodHeader.of(CargoStack.class, "getSum"), invocation -> getSumReturnValue);
Delegation.disable(MethodHeader.of(CargoPlane.class, "mass"));
int baseWeight = 100;
CargoPlane cargoPlaneInstance = new CargoPlane("D-ABCD", baseWeight, FuelType.JetA, 1000);
Context context = contextBuilder()
.add("baseWeight", baseWeight)
.add("containers.getSum()", getSumReturnValue)
.build();
double returnValue = callObject(cargoPlaneInstance::mass, context, result ->
"An exception occurred while invoking mass()");
assertDoubleEquals(baseWeight + getSumReturnValue, returnValue, context, result ->
"Method mass() returned an incorrect value");
}
@Test
public void testLoadContainer() {
AtomicInteger lastPushedValue = new AtomicInteger(-1);
Substitution.enable(MethodHeader.of(CargoStack.class, "push", int.class), invocation -> {
lastPushedValue.set(invocation.getIntParameter(0));
return invocation.callOriginalMethod(true);
});
Delegation.disable(MethodHeader.of(CargoPlane.class, "loadContainer", int.class));
CargoPlane cargoPlaneInstance = new CargoPlane("D-ABCD", 100, FuelType.JetA, 1000);
for (int i = 0; i < 10; i++) {
Context context = contextBuilder()
.add("cargoWeight", i)
.build();
final int finalI = i;
call(() -> cargoPlaneInstance.loadContainer(finalI), context, result ->
"An exception occurred while invoking loadContainer(int)");
assertEquals(i, lastPushedValue.get(), context, result ->
"Method did not call containers.push(int) with the correct value");
}
}
@Test
public void testHasFreightLoaded() {
AtomicBoolean isEmpty = new AtomicBoolean();
Substitution.enable(MethodHeader.of(CargoStack.class, "empty"), invocation -> isEmpty.get());
Delegation.disable(MethodHeader.of(CargoPlane.class, "hasFreightLoaded"));
CargoPlane cargoPlaneInstance = new CargoPlane("D-ABCD", 100, FuelType.JetA, 1000);
for (boolean b : new boolean[] {true, false}) {
Context context = contextBuilder()
.add("containers.empty()", b)
.build();
isEmpty.set(b);
boolean returnValue = callObject(cargoPlaneInstance::hasFreightLoaded, context, result ->
"An exception occurred while invoking hasFreightLoaded()");
assertEquals(!isEmpty.get(), returnValue, context, result ->
"Method hasFreightLoaded() did not return the correct value");
}
}
@Test
public void testUnloadNextContainer() {
List<Integer> stackElements = List.of(1, 2, 3, 4, 5);
AtomicInteger stackIndex = new AtomicInteger(stackElements.size() - 1);
Substitution.enable(MethodHeader.of(CargoStack.class, "pop"), invocation -> stackElements.get(stackIndex.getAndDecrement()));
Delegation.disable(MethodHeader.of(CargoPlane.class, "unloadNextContainer"));
CargoPlane cargoPlaneInstance = new CargoPlane("D-ABCD", 100, FuelType.JetA, 1000);
for (int i = 0; i < stackElements.size(); i++) {
Context context = contextBuilder()
.add("containers.pop()", stackElements.get(stackIndex.get()))
.build();
int returnValue = callObject(cargoPlaneInstance::unloadNextContainer, context, result ->
"An exception occurred while invoking unloadNextContainer()");
assertEquals(stackElements.get(stackIndex.get() + 1), returnValue, context, result ->
"Method unloadNextContainer() did not return the correct value");
}
}
}

View file

@ -0,0 +1,43 @@
package h05;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class CarriesCargoTest {
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(CarriesCargo.class);
assertTrue(Modifier.isInterface(originalClassHeader.modifiers()), emptyContext(), result ->
"CarriesCargo was not declared to be an interface");
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Interface CarriesCargo was not declared public");
}
@Test
public void testMethodHeaders() {
MethodHeader loadContainer = assertNotNull(getOriginalMethodHeader(CarriesCargo.class, "loadContainer", int.class),
emptyContext(), result -> "Could not find method 'loadContainer(int)'");
assertEquals(void.class, loadContainer.getReturnType(), emptyContext(), result ->
"Method loadContainer(int) does not have return type void");
MethodHeader hasFreightLoaded = assertNotNull(getOriginalMethodHeader(CarriesCargo.class, "hasFreightLoaded"),
emptyContext(), result -> "Could not find method 'hasFreightLoaded()'");
assertEquals(boolean.class, hasFreightLoaded.getReturnType(), emptyContext(), result ->
"Method hasFreightLoaded() does not have return type boolean");
MethodHeader unloadNextContainer = assertNotNull(getOriginalMethodHeader(CarriesCargo.class, "unloadNextContainer"),
emptyContext(), result -> "Could not find method 'unloadNextContainer()'");
assertEquals(int.class, unloadNextContainer.getReturnType(), emptyContext(), result ->
"Method unloadNextContainer() does not have return type int");
}
}

View file

@ -0,0 +1,49 @@
package h05;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class CarriesPassengersTest {
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(CarriesPassengers.class);
assertTrue(Modifier.isInterface(originalClassHeader.modifiers()), emptyContext(), result ->
"Class CarriesPassengers is not an interface");
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Interface CarriesPassengers was not declared public");
}
@Test
public void testMethodHeaders() {
MethodHeader board = assertNotNull(getOriginalMethodHeader(CarriesPassengers.class, "board", int.class),
emptyContext(), result -> "Could not find method 'board(int)'");
assertTrue(Modifier.isAbstract(board.modifiers()), emptyContext(), result ->
"Method 'board(int)' was not declared abstract");
assertEquals(void.class, board.getReturnType(), emptyContext(), result ->
"Method 'board(int)' does not have return type void");
MethodHeader disembark = assertNotNull(getOriginalMethodHeader(CarriesPassengers.class, "disembark"),
emptyContext(), result -> "Could not find method 'disembark()'");
assertTrue(Modifier.isAbstract(disembark.modifiers()), emptyContext(), result ->
"Method 'disembark()' was not declared abstract");
assertEquals(void.class, disembark.getReturnType(), emptyContext(), result ->
"Method 'disembark()' does not have return type void");
MethodHeader getPassengerCount = assertNotNull(getOriginalMethodHeader(CarriesPassengers.class, "getPassengerCount"),
emptyContext(), result -> "Could not find method 'getPassengerCount()'");
assertTrue(Modifier.isAbstract(getPassengerCount.modifiers()), emptyContext(), result ->
"Method 'getPassengerCount()' was not declared abstract");
assertEquals(int.class, getPassengerCount.getReturnType(), emptyContext(), result ->
"Method 'getPassengerCount()' does not have return type int");
}
}

View file

@ -0,0 +1,73 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class CombinedPlaneTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(CombinedPlane.class);
assertEquals(PassengerPlane.class, originalClassHeader.getSuperType(), emptyContext(), result ->
"Class CombinedPlane does not extend PassengerPlane");
assertTrue(originalClassHeader.getInterfaceTypes().contains(CarriesCargo.class), emptyContext(), result ->
"Class CombinedPlane does not implement CarriesCargo");
}
@Test
public void testConstructor() {
String aircraftRegistration = "D-FLOP";
int baseWeight = 500;
FuelType fuelType = FuelType.JetB;
double fuelCapacity = 200;
int crewCount = 5;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("baseWeight", baseWeight)
.add("fuelType", fuelType)
.add("fuelCapacity", fuelCapacity)
.add("crewCount", crewCount)
.build();
Delegation.disable(MethodHeader.of(CombinedPlane.class, String.class, int.class, FuelType.class, double.class, int.class));
CombinedPlane combinedPlaneInstance = callObject(
() -> new CombinedPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity, crewCount),
context,
result -> "An exception occurred while invoking constructor of CombinedPlane");
assertEquals(aircraftRegistration,
FieldHeader.of(Plane.class, "aircraftRegistration").getValue(combinedPlaneInstance),
context,
result -> "Field aircraftRegistration has incorrect value");
assertEquals(baseWeight,
FieldHeader.of(Plane.class, "baseWeight").getValue(combinedPlaneInstance),
context,
result -> "Field baseWeight has incorrect value");
assertEquals(fuelType,
FieldHeader.of(Plane.class, "fuelType").getValue(combinedPlaneInstance),
context,
result -> "Field fuelType has incorrect value");
assertEquals(fuelCapacity,
FieldHeader.of(Plane.class, "fuelCapacity").getValue(combinedPlaneInstance),
context,
result -> "Field fuelCapacity has incorrect value");
assertEquals(crewCount,
FieldHeader.of(PassengerPlane.class, "crewCount").getValue(combinedPlaneInstance),
context,
result -> "Field crewCount has incorrect value");
}
}

View file

@ -0,0 +1,37 @@
package h05;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.getOriginalClassHeader;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.getOriginalMethodHeader;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class FlyingTest {
@Test
public void testHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(Flying.class);
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(),
result -> "Class Flying is not public");
assertTrue(Modifier.isInterface(originalClassHeader.modifiers()), emptyContext(),
result -> "Class Flying is not an interface");
}
@Test
public void testMethods() {
MethodHeader getIdentifier = assertNotNull(getOriginalMethodHeader(Flying.class, "getIdentifier"),
emptyContext(), result -> "Could not find method 'getIdentifier()'");
assertTrue(Modifier.isAbstract(getIdentifier.modifiers()), emptyContext(),
result -> "Method 'getIdentifier()' is not abstract");
assertEquals(String.class, getIdentifier.getReturnType(), emptyContext(),
result -> "Method 'getIdentifier()' has incorrect return type");
}
}

View file

@ -0,0 +1,62 @@
package h05;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.EnumConstant;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.json.JsonParameterSet;
import org.tudalgo.algoutils.tutor.general.json.JsonParameterSetTest;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class FuelTypeTest {
@Test
public void testHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(FuelType.class);
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Class FuelType is not public");
assertEquals(Enum.class, originalClassHeader.getSuperType(), emptyContext(),
result -> "Class FuelType is not an enum");
}
@Test
public void testFields() {
FieldHeader consumptionMultiplicator = assertNotNull(getOriginalFieldHeader(FuelType.class, "consumptionMultiplicator"),
emptyContext(), result -> "Could not find field 'consumptionMultiplicator'");
assertTrue(Modifier.isFinal(consumptionMultiplicator.modifiers()), emptyContext(), result ->
"Field consumptionMultiplicator is not final");
assertEquals(double.class, consumptionMultiplicator.getType(), emptyContext(), result ->
"Field consumptionMultiplicator does not have the correct type");
}
@Test
public void testConstructor() {
// Enums have special constructors: Enum(<name>, <ordinal>[, other args...])
MethodHeader constructor = assertNotNull(getOriginalMethodHeader(FuelType.class, String.class, int.class, double.class),
emptyContext(), result -> "Could not find constructor 'FuelType(double)'");
}
@ParameterizedTest
@JsonParameterSetTest("FuelTypeConstants.json")
public void testEnumConstants(JsonParameterSet params) {
String enumConstantName = params.getString("enumConstantName");
double enumConstantValue = params.getDouble("enumConstantValue");
EnumConstant enumConstant = assertNotNull(getOriginalEnumConstant(FuelType.class, enumConstantName),
emptyContext(), result -> "Could not find enum constant '" + enumConstantName + "'");
assertEquals(1, enumConstant.values().length, emptyContext(), result ->
"Enum constant '" + enumConstantName + "' does not have the expected amount of arguments");
assertEquals(enumConstantValue, enumConstant.values()[0], emptyContext(), result ->
"Enum constant '" + enumConstantName + "' does not have the correct value");
}
}

View file

@ -0,0 +1,238 @@
package h05;
import org.sourcegrade.jagr.api.rubric.*;
import org.sourcegrade.jagr.api.testing.RubricConfiguration;
import org.tudalgo.algoutils.transform.SolutionMergingClassTransformer;
import org.tudalgo.algoutils.tutor.general.json.JsonParameterSet;
import static org.tudalgo.algoutils.tutor.general.jagr.RubricUtils.criterion;
@SuppressWarnings("unused")
public class H05_RubricProvider implements RubricProvider {
private static final Criterion H5_1_1 = Criterion.builder()
.shortDescription("H5.1.1 | Flying Interface")
.maxPoints(1)
.addChildCriteria(
criterion("Das Flying-Interface ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> FlyingTest.class.getDeclaredMethod("testHeader")),
JUnitTestRef.ofMethod(() -> FlyingTest.class.getDeclaredMethod("testMethods")))
)
.build();
private static final Criterion H5_1_2 = Criterion.builder()
.shortDescription("H5.1.2 | FuelType")
.maxPoints(2)
.addChildCriteria(
criterion("Das FuelType-Enum ist korrekt implementiert und enthält die richtigen Werte.",
JUnitTestRef.ofMethod(() -> FuelTypeTest.class.getDeclaredMethod("testHeader")),
JUnitTestRef.ofMethod(() -> FuelTypeTest.class.getDeclaredMethod("testFields")),
JUnitTestRef.ofMethod(() -> FuelTypeTest.class.getDeclaredMethod("testEnumConstants", JsonParameterSet.class))),
criterion("Der Konstruktor des FuelType-Enums ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> FuelTypeTest.class.getDeclaredMethod("testConstructor")))
)
.build();
private static final Criterion H5_1_3 = Criterion.builder()
.shortDescription("H5.1.3 | Flugzeug")
.maxPoints(6)
.addChildCriteria(
criterion("Das Interface ist korrekt in die Flugzeugklasse implementiert.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testHeader"))),
criterion("Die Flugzeugklasse hat die richtigen Attribute.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testFields"))),
criterion("Methode mass funktioniert korrekt.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testMass"))),
criterion("Methode refuel gibt die richtige Nachricht aus.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testRefuel"))),
criterion("Methode getFuelConsumptionPerKilometer ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testGetFuelConsumptionPerKilometer"))),
criterion("Die Methoden fly und takeOff sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testFly")),
JUnitTestRef.ofMethod(() -> PlaneTest.class.getDeclaredMethod("testTakeOff")))
)
.build();
private static final Criterion H5_1_4 = Criterion.builder()
.shortDescription("H5.1.4 | Wetterballon")
.maxPoints(2)
.addChildCriteria(
criterion("Die WeatherBalloon-Klasse ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> WeatherBalloonTest.class.getDeclaredMethod("testHeader"))),
criterion("Die Methoden der Klasse WeatherBalloon-Klasse sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> WeatherBalloonTest.class.getDeclaredMethod("testStart")),
JUnitTestRef.ofMethod(() -> WeatherBalloonTest.class.getDeclaredMethod("testPop")),
JUnitTestRef.ofMethod(() -> WeatherBalloonTest.class.getDeclaredMethod("testGetIdentifier")))
)
.build();
private static final Criterion H5_1 = Criterion.builder()
.shortDescription("H5.1 | Basis")
.addChildCriteria(H5_1_1, H5_1_2, H5_1_3, H5_1_4)
.build();
private static final Criterion H5_2_1 = Criterion.builder()
.shortDescription("H5.2.1 | CarriesPassengers Interface")
.maxPoints(4)
.addChildCriteria(
criterion("Das CarriesPassengers-Interface ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> CarriesPassengersTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> CarriesPassengersTest.class.getDeclaredMethod("testMethodHeaders"))),
criterion("Die PassengerPlane-Klasse implementiert das CarriesPassengers-Interface korrekt.",
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testClassHeader"))),
criterion("Die Methoden board, disembark und getPassengerCount sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testBoard")),
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testDisembark")),
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testGetPassengerCount"))),
criterion("Die Konstanten und der Konstruktor sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testConstants")),
JUnitTestRef.ofMethod(() -> PassengerPlaneTest.class.getDeclaredMethod("testConstructor")))
)
.build();
private static final Criterion H5_2_2 = Criterion.builder()
.shortDescription("H5.2.2 | CarriesCargo Interface")
.maxPoints(4)
.addChildCriteria(
criterion("Das CarriesCargo-Interface ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> CarriesCargoTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> CarriesCargoTest.class.getDeclaredMethod("testMethodHeaders"))),
criterion("Die CargoPlane-Klasse implementiert das CarriesCargo-Interface korrekt.",
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testClassHeader"))),
criterion("Die Masse-Methode und der Konstruktor sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testConstructor")),
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testMass"))),
criterion("Die Methoden loadContainer, hasFreightLoaded und unloadNextContainer sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testLoadContainer")),
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testHasFreightLoaded")),
JUnitTestRef.ofMethod(() -> CargoPlaneTest.class.getDeclaredMethod("testUnloadNextContainer")))
)
.build();
private static final Criterion H5_2_3 = Criterion.builder()
.shortDescription("H5.2.3 | Kombiniertes Flugzeug")
.maxPoints(2)
.addChildCriteria(
criterion("Die CombinedPlane-Klasse implementiert die Interfaces CarriesPassengers und CarriesCargo korrekt.",
JUnitTestRef.ofMethod(() -> CombinedPlaneTest.class.getDeclaredMethod("testClassHeader"))),
criterion("Die Klasse ist vollständig korrekt.",
JUnitTestRef.ofMethod(() -> CombinedPlaneTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> CombinedPlaneTest.class.getDeclaredMethod("testConstructor")))
)
.build();
private static final Criterion H5_2 = Criterion.builder()
.shortDescription("H5.2 | Basis")
.addChildCriteria(H5_2_1, H5_2_2, H5_2_3)
.build();
private static final Criterion H5_3_1 = Criterion.builder()
.shortDescription("H5.3.1 | Betankung")
.maxPoints(1)
.addChildCriteria(
criterion("Das Refuelling-Interface ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> RefuellingTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> RefuellingTest.class.getDeclaredMethod("testMethodHeaders")))
)
.build();
private static final Criterion H5_3_2 = Criterion.builder()
.shortDescription("H5.3.2 | Tank")
.maxPoints(2)
.addChildCriteria(
criterion("Die Tank-Klasse ist korrekt deklariert und enthält die richtigen Attribute.",
JUnitTestRef.ofMethod(() -> TankTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> TankTest.class.getDeclaredMethod("testFields"))),
criterion("Die Methode refuelPlane ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> TankTest.class.getDeclaredMethod("testRefuelPlane")),
JUnitTestRef.ofMethod(() -> TankTest.class.getDeclaredMethod("testRefuelPlane_ErrorMessage")))
)
.build();
private static final Criterion H5_3_3 = Criterion.builder()
.shortDescription("H5.3.3 | Tanker-Flugzeug")
.maxPoints(3)
.addChildCriteria(
criterion("Der Kopf der TankerPlane-Klasse sowie das Attribut availableAmount und der Konstruktor sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testFields")),
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testConstructor"))),
criterion("Die Methoden loadFuel und mass sind korrekt implementiert.",
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testLoadFuel")),
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testMass")),
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testRefuelPlane"))),
criterion("Die Methode refuelPlane ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> TankerPlaneTest.class.getDeclaredMethod("testRefuelPlane")))
)
.build();
private static final Criterion H5_3 = Criterion.builder()
.shortDescription("H5.3 | Tanken")
.addChildCriteria(H5_3_1, H5_3_2, H5_3_3)
.build();
private static final Criterion H5_4 = Criterion.builder()
.shortDescription("H5.4 | Start- und Landebahn")
.maxPoints(2)
.addChildCriteria(
criterion("Die Runway-Klasse ist korrekt deklariert und enthält die richtigen Attribute.",
JUnitTestRef.ofMethod(() -> RunwayTest.class.getDeclaredMethod("testClassHeader")),
JUnitTestRef.ofMethod(() -> RunwayTest.class.getDeclaredMethod("testFields"))),
criterion("Die Methode land ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> RunwayTest.class.getDeclaredMethod("testLand", boolean.class)))
)
.build();
private static final Criterion H5_5 = Criterion.builder()
.shortDescription("H5.5 | Luftraum")
.maxPoints(1)
.addChildCriteria(
criterion("Die Methode scanAirspace ist korrekt implementiert.",
JUnitTestRef.ofMethod(() -> AirspaceTest.class.getDeclaredMethod("testScanAirspace_Empty")),
JUnitTestRef.ofMethod(() -> AirspaceTest.class.getDeclaredMethod("testScanAirspace_CargoPlane")),
JUnitTestRef.ofMethod(() -> AirspaceTest.class.getDeclaredMethod("testScanAirspace_PassengerPlane")))
)
.build();
private static final Criterion H5_6 = Criterion.builder()
.shortDescription("H5.6 | Spielwiese")
.maxPoints(2)
.addChildCriteria(
criterion("Der Luftraum ist bis nach dem Start von \"GAG-67\" korrekt.",
JUnitTestRef.ofMethod(() -> MainTest.class.getDeclaredMethod("testMain_FirstScans", JsonParameterSet.class))),
criterion("Der Luftraum ist bei jedem Scan korrekt.",
JUnitTestRef.ofMethod(() -> MainTest.class.getDeclaredMethod("testMain_AllScans", JsonParameterSet.class)))
)
.build();
@Override
public Rubric getRubric() {
return Rubric.builder()
.title("H05 | FOP und Fort: Abheben im Flugzeug")
.addChildCriteria(H5_1, H5_2, H5_3, H5_4, H5_5, H5_6)
.build();
}
@Override
public void configure(RubricConfiguration configuration) {
configuration.addTransformer(() -> new SolutionMergingClassTransformer.Builder("h05")
.addSolutionClass("h05.Airspace")
.addSolutionClass("h05.CargoPlane")
.addSolutionClass("h05.CargoStack")
.addSolutionClass("h05.CarriesCargo")
.addSolutionClass("h05.CarriesPassengers")
.addSolutionClass("h05.CombinedPlane")
.addSolutionClass("h05.Flying")
.addSolutionClass("h05.FuelType")
.addSolutionClass("h05.Main")
.addSolutionClass("h05.PassengerPlane")
.addSolutionClass("h05.Plane")
.addSolutionClass("h05.Refuelling")
.addSolutionClass("h05.Runway")
.addSolutionClass("h05.Tank")
.addSolutionClass("h05.TankerPlane")
.addSolutionClass("h05.WeatherBalloon")
.setSimilarity(0.80)
.build());
}
}

View file

@ -0,0 +1,77 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import org.tudalgo.algoutils.tutor.general.json.JsonParameterSet;
import org.tudalgo.algoutils.tutor.general.json.JsonParameterSetTest;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class MainTest {
private List<Set<String>> airspaceScans;
@AfterEach
public void tearDown() {
resetAll();
}
public void setupEnvironment() {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
TestUtils.clearAirspace();
Delegation.disable(MethodHeader.of(Main.class, "main", String[].class));
call(() -> Main.main(new String[0]), emptyContext(), result -> "An exception occurred while invoking main method");
} finally {
System.setOut(oldOut);
}
airspaceScans = Arrays.stream(outputStream.toString().strip().split("Scanning\\.*\n"))
.map(s -> Set.of(s.split("\n")))
.toList();
}
@ParameterizedTest
@JsonParameterSetTest("MainTest_Airspace.json")
public void testMain_FirstScans(JsonParameterSet params) {
setupEnvironment();
if (params.getInt("invocation") <= 2) {
testMain(params);
}
}
@ParameterizedTest
@JsonParameterSetTest("MainTest_Airspace.json")
public void testMain_AllScans(JsonParameterSet params) {
setupEnvironment();
testMain(params);
}
private void testMain(JsonParameterSet params) {
int invocation = params.getInt("invocation");
List<String> expectedAirspaceState = params.get("airspaceState");
Set<String> actualAirspaceState = airspaceScans.get(invocation);
for (String airspaceMessage : expectedAirspaceState) {
Context context = contextBuilder().add("expected message", airspaceMessage).build();
assertTrue(actualAirspaceState.stream().anyMatch(s -> TestUtils.stringEquals(airspaceMessage, s)),
context,
result -> "Airspace does not contain aircraft %s or message is incorrect"
.formatted(airspaceMessage.split(" is ", 2)[0]));
}
}
}

View file

@ -0,0 +1,162 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class PassengerPlaneTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(PassengerPlane.class);
assertTrue(originalClassHeader.getInterfaceTypes().contains(CarriesPassengers.class), emptyContext(),
result -> "Class PassengerPlane does not implement interface CarriesPassengers");
}
@Test
public void testConstants() {
FieldHeader averagePeopleWeight = assertNotNull(getOriginalFieldHeader(PassengerPlane.class, "AVERAGE_PEOPLE_WEIGHT"),
emptyContext(), result -> "Could not find field 'AVERAGE_PEOPLE_WEIGHT'");
assertTrue(Modifier.isProtected(averagePeopleWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_PEOPLE_WEIGHT' was not declared protected");
assertTrue(Modifier.isStatic(averagePeopleWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_PEOPLE_WEIGHT' was not declared static");
assertTrue(Modifier.isFinal(averagePeopleWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_PEOPLE_WEIGHT' was not declared final");
assertEquals(char.class, averagePeopleWeight.getType(), emptyContext(), result ->
"Field 'AVERAGE_PEOPLE_WEIGHT' does not have type char");
assertEquals((char) 100, getOriginalStaticFieldValue(PassengerPlane.class, "AVERAGE_PEOPLE_WEIGHT"),
emptyContext(), result -> "Value of field 'AVERAGE_PEOPLE_WEIGHT' is incorrect");
FieldHeader averageLuggageWeight = assertNotNull(getOriginalFieldHeader(PassengerPlane.class, "AVERAGE_LUGGAGE_WEIGHT"),
emptyContext(), result -> "Could not find field 'AVERAGE_LUGGAGE_WEIGHT'");
assertTrue(Modifier.isProtected(averageLuggageWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_LUGGAGE_WEIGHT' was not declared protected");
assertTrue(Modifier.isStatic(averageLuggageWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_LUGGAGE_WEIGHT' was not declared static");
assertTrue(Modifier.isFinal(averageLuggageWeight.modifiers()), emptyContext(), result ->
"Field 'AVERAGE_LUGGAGE_WEIGHT' was not declared final");
assertEquals(char.class, averageLuggageWeight.getType(), emptyContext(), result ->
"Field 'AVERAGE_LUGGAGE_WEIGHT' does not have type char");
assertEquals((char) 15, getOriginalStaticFieldValue(PassengerPlane.class, "AVERAGE_LUGGAGE_WEIGHT"),
emptyContext(), result -> "Value of field 'AVERAGE_LUGGAGE_WEIGHT' is incorrect");
}
@Test
public void testConstructor() {
String aircraftRegistration = "D-DFOP";
int baseWeight = 100;
FuelType fuelType = FuelType.JetA;
double fuelCapacity = 50;
int crewCount = 2;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("baseWeight", baseWeight)
.add("fuelType", fuelType)
.add("fuelCapacity", fuelCapacity)
.add("crewCount", crewCount)
.build();
MethodHeader constructor = MethodHeader.of(PassengerPlane.class, String.class, int.class, FuelType.class, double.class, int.class);
Delegation.disable(constructor);
PassengerPlane passengerPlaneInstance = callObject(
() -> new PassengerPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity, crewCount),
context,
result -> "An exception occurred while invoking constructor of PassengerPlane");
assertEquals(aircraftRegistration,
FieldHeader.of(Plane.class, "aircraftRegistration").getValue(passengerPlaneInstance),
context,
result -> "Field aircraftRegistration (in class Plane) has incorrect value");
assertEquals(baseWeight,
FieldHeader.of(Plane.class, "baseWeight").getValue(passengerPlaneInstance),
context,
result -> "Field baseWeight (in class Plane) has incorrect value");
assertEquals(fuelType,
FieldHeader.of(Plane.class, "fuelType").getValue(passengerPlaneInstance),
context,
result -> "Field fuelType (in class Plane) has incorrect value");
assertEquals(fuelCapacity,
FieldHeader.of(Plane.class, "fuelCapacity").getValue(passengerPlaneInstance),
context,
result -> "Field fuelCapacity (in class Plane) has incorrect value");
assertEquals(crewCount,
FieldHeader.of(PassengerPlane.class, "crewCount").getValue(passengerPlaneInstance),
context,
result -> "Field crewCount has incorrect value");
}
@Test
public void testBoard() {
FieldHeader passengerCountField = FieldHeader.of(PassengerPlane.class, "passengerCount");
MethodHeader board = MethodHeader.of(PassengerPlane.class, "board", int.class);
PassengerPlane passengerPlaneInstance = new PassengerPlane("D-ABCD", 0, FuelType.JetA, 1000, 2);
Delegation.disable(board);
int totalPassengersExpected = 0;
for (int i = 0; i < 10; i++, totalPassengersExpected += i) {
Context context = contextBuilder()
.add("peopleCount", i)
.add("passengerCount (before call)", totalPassengersExpected)
.build();
final int finalI = i;
call(() -> passengerPlaneInstance.board(finalI), context, result ->
"An exception occurred while invoking board(int)");
assertEquals(totalPassengersExpected, passengerCountField.getValue(passengerPlaneInstance), context,
result -> "board(int) did not add the given amount to field passengerCount");
}
}
@Test
public void testDisembark() {
FieldHeader passengerCountField = FieldHeader.of(PassengerPlane.class, "passengerCount");
MethodHeader disembark = MethodHeader.of(PassengerPlane.class, "disembark");
PassengerPlane passengerPlaneInstance = new PassengerPlane("D-ABCD", 0, FuelType.JetA, 1000, 2);
Delegation.disable(disembark);
for (int i = 0; i < 10; i++) {
passengerCountField.setValue(passengerPlaneInstance, i);
Context context = contextBuilder()
.add("passengerCount (before call)", i)
.build();
call(passengerPlaneInstance::disembark, context, result ->
"An exception occurred while invoking disembark()");
assertEquals(0, passengerCountField.getValue(passengerPlaneInstance), context, result ->
"disembark() did not set field passengerCount to 0");
}
}
@Test
public void testGetPassengerCount() {
FieldHeader passengerCountField = FieldHeader.of(PassengerPlane.class, "passengerCount");
PassengerPlane passengerPlaneInstance = new PassengerPlane("D-ABCD", 0, FuelType.JetA, 1000, 2);
Delegation.disable(MethodHeader.of(PassengerPlane.class, "getPassengerCount"));
for (int i = 0; i < 10; i++) {
passengerCountField.setValue(passengerPlaneInstance, i);
Context context = contextBuilder()
.add("passengerCount", i)
.build();
int returnValue = callObject(passengerPlaneInstance::getPassengerCount, context, result ->
"An exception occurred while invoking getPassengerCount()");
assertEquals(i, returnValue, context, result ->
"getPassengerCount() did not return the correct value");
}
}
}

View file

@ -0,0 +1,240 @@
package h05;
import com.google.common.util.concurrent.AtomicDouble;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.Locale;
import java.util.Set;
import static h05.TestUtils.*;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class PlaneTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(Plane.class);
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Class Plane is not public");
assertTrue(Modifier.isAbstract(originalClassHeader.modifiers()), emptyContext(), result ->
"Class Plane is not abstract");
assertTrue(originalClassHeader.getInterfaceTypes().contains(Flying.class), emptyContext(), result ->
"Class Plane does not implement interface Flying");
}
@Test
public void testFields() {
FieldHeader aircraftRegistration = assertNotNull(getOriginalFieldHeader(Plane.class, "aircraftRegistration"),
emptyContext(), result -> "Could not find field 'aircraftRegistration'");
assertEquals(String.class, aircraftRegistration.getType(), emptyContext(), result ->
"Field 'aircraftRegistration' does not have correct type");
FieldHeader baseWeight = assertNotNull(getOriginalFieldHeader(Plane.class, "baseWeight"),
emptyContext(), result -> "Could not find field 'baseWeight'");
assertEquals(int.class, baseWeight.getType(), emptyContext(), result ->
"Field 'baseWeight' does not have correct type");
FieldHeader fuelType = assertNotNull(getOriginalFieldHeader(Plane.class, "fuelType"),
emptyContext(), result -> "Could not find field 'fuelType'");
assertEquals(FuelType.class, fuelType.getType(), emptyContext(), result ->
"Field 'fuelType' does not have correct type");
FieldHeader fuelCapacity = assertNotNull(getOriginalFieldHeader(Plane.class, "fuelCapacity"),
emptyContext(), result -> "Could not find field 'fuelCapacity'");
assertEquals(double.class, fuelCapacity.getType(), emptyContext(), result ->
"Field 'fuelCapacity' does not have correct type");
FieldHeader currentFuelLevel = assertNotNull(getOriginalFieldHeader(Plane.class, "currentFuelLevel"),
emptyContext(), result -> "Could not find field 'currentFuelLevel'");
assertEquals(double.class, currentFuelLevel.getType(), emptyContext(), result ->
"Field 'currentFuelLevel' does not have correct type");
}
@Test
public void testMass() {
MethodHeader mass = assertNotNull(getOriginalMethodHeader(Plane.class, "mass"),
emptyContext(), result -> "Could not find method 'mass()'");
assertTrue(Modifier.isProtected(mass.modifiers()), emptyContext(), result ->
"Method 'mass()' is not protected");
assertTrue(Modifier.isAbstract(mass.modifiers()), emptyContext(), result ->
"Method 'mass()' is not abstract");
assertEquals(double.class, mass.getReturnType(), emptyContext(), result ->
"Return type of method 'mass()' is incorrect");
}
@Test
public void testRefuel() {
Delegation.disable(MethodHeader.of(Plane.class, "refuel", double.class));
String aircraftRegistration = "D-ABCD";
double currentFuelLevel = 0;
double fuelCapacity = 1000;
Plane planeInstance = new Plane(aircraftRegistration, 0, FuelType.JetA, fuelCapacity) {
@Override
protected double mass() {
return 0;
}
};
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
PrintStream old = System.out;
System.setOut(printStream);
double amount = Double.MAX_VALUE;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("currentFuelLevel", currentFuelLevel)
.add("fuelCapacity", fuelCapacity)
.add("amount", amount)
.build();
try {
call(() -> planeInstance.refuel(amount), context, result -> "An exception occurred while invoking refuel");
} finally {
System.setOut(old);
}
assertStringEquals(
"The Tank of Plane %s has overflowed!".formatted(aircraftRegistration),
outputStream.toString(),
context,
result -> "The message printed by method refuel is incorrect"
);
}
@Test
public void testGetFuelConsumptionPerKilometer() {
MethodHeader getFuelConsumptionPerKilometer = assertNotNull(
getOriginalMethodHeader(Plane.class, "getFuelConsumptionPerKilometer"),
emptyContext(), result -> "Could not find method 'getFuelConsumptionPerKilometer()'");
assertTrue(Modifier.isProtected(getFuelConsumptionPerKilometer.modifiers()), emptyContext(),
result -> "Method 'getFuelConsumptionPerKilometer()' is not protected");
assertEquals(double.class, getFuelConsumptionPerKilometer.getReturnType(), emptyContext(),
result -> "Return type of 'getFuelConsumptionPerKilometer()' is incorrect");
FieldHeader fuelTypeField = FieldHeader.of(Plane.class, "fuelType");
AtomicDouble massValue = new AtomicDouble();
Plane planeInstance = new Plane("D-ABCD", 0, FuelType.JetA, 1000) {
@Override
protected double mass() {
return massValue.get();
}
};
Delegation.disable(MethodHeader.of(Plane.class, "getFuelConsumptionPerKilometer"));
for (FuelType fuelType : FuelType.values()) {
fuelTypeField.setValue(planeInstance, fuelType);
for (double mass = 10000; mass < 30000; mass += 5000) {
Context context = contextBuilder()
.add("fuelType", fuelType)
.add("mass", mass)
.build();
massValue.set(mass);
double expected = 1.1494e-4 * mass * fuelType.getConsumptionMultiplicator();
double actual = callObject(planeInstance::getFuelConsumptionPerKilometer, context, result ->
"An exception occurred while invoking 'getFuelConsumptionPerKilometer()'");
assertDoubleEquals(expected, actual, context, result ->
"Return value of method 'getFuelConsumptionPerKilometer()' is incorrect");
}
}
}
@Test
public void testFly() {
MethodHeader fly = assertNotNull(getOriginalMethodHeader(Plane.class, "fly", double.class),
emptyContext(), result -> "Could not find method 'fly(double)'");
assertTrue(Modifier.isPublic(fly.modifiers()), emptyContext(), result ->
"Method 'fly(double)' is not public");
assertEquals(void.class, fly.getReturnType(), emptyContext(), result ->
"Return type of method 'fly(double)' is incorrect");
TestUtils.clearAirspace();
PrintStream originalOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(outputStream);
System.setOut(printStream);
try {
String aircraftRegistration = "D-ABCD";
double currentFuelLevel = 5;
FieldHeader currentFuelLevelField = FieldHeader.of(Plane.class, "currentFuelLevel");
Plane planeInstance = new Plane(aircraftRegistration, 0, FuelType.JetA, 1000) {
@Override
protected double mass() {
return 0;
}
};
Delegation.disable(MethodHeader.of(Plane.class, "fly", double.class));
Substitution.enable(MethodHeader.of(Plane.class, "getFuelConsumptionPerKilometer"), invocation -> 1d);
currentFuelLevelField.setValue(planeInstance, currentFuelLevel);
for (double distance : new double[] {1_000_000, 1}) {
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("currentFuelLevel", currentFuelLevel)
.add("getFuelConsumptionPerKilometer() return value", 1)
.add("distance", distance)
.build();
outputStream.reset();
call(() -> planeInstance.fly(distance), context, result -> "An exception occurred while invoking method fly");
if (distance > currentFuelLevel) {
assertStringEquals(
String.format((Locale) null, "Plane %s does not have enough fuel to fly %.1f km.", aircraftRegistration, distance),
outputStream.toString().trim(),
context,
result -> "Method fly did not print the correct string"
);
assertEquals(currentFuelLevel, currentFuelLevelField.getValue(planeInstance), context, result ->
"Plane does not have enough fuel to fly but currentFuelLevel was modified");
} else {
assertStringEquals(
String.format((Locale) null, "Plane %s flew %.1f km and has %.1f liters of fuel left.", aircraftRegistration, distance, currentFuelLevel - 1),
outputStream.toString().trim(),
context,
result -> "Method fly did not print the correct string"
);
assertEquals(currentFuelLevel - 1, currentFuelLevelField.getValue(planeInstance), context, result ->
"Plane had enough fuel to fly but currentFuelLevel was not set to the correct value");
}
}
} finally {
System.setOut(originalOut);
}
}
@Test
public void testTakeOff() {
Airspace airspace = Airspace.get();
Plane planeInstance = new Plane("D-ABCD", 0, FuelType.JetA, 1000) {
@Override
protected double mass() {
return 0;
}
};
Delegation.disable(MethodHeader.of(Plane.class, "takeOff"));
TestUtils.clearAirspace();
call(planeInstance::takeOff, emptyContext(), result ->
"An exception occurred while invoking method takeOff");
Set<?> flyingInAirspace = airspace.getFlyingInAirspace();
assertEquals(1, flyingInAirspace.size(), emptyContext(), result ->
"Number of planes in airspace differs from expected value");
assertSame(planeInstance, flyingInAirspace.iterator().next(), emptyContext(), result ->
"Calling Plane (using 'this') was not added to airspace");
}
}

View file

@ -0,0 +1,31 @@
package h05;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import java.lang.reflect.Modifier;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class RefuellingTest {
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(Refuelling.class);
assertTrue(Modifier.isInterface(originalClassHeader.modifiers()), emptyContext(), result ->
"Class Refuelling is not an interface");
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Interface Refuelling is not declared public");
}
@Test
public void testMethodHeaders() {
MethodHeader refuelPlane = getOriginalMethodHeader(Refuelling.class, "refuelPlane", Plane.class);
assertEquals(void.class, refuelPlane.getReturnType(), emptyContext(), result ->
"Method refuelPlane(Plane) does not have correct return type");
}
}

View file

@ -0,0 +1,101 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.concurrent.atomic.AtomicBoolean;
import static h05.TestUtils.assertStringEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class RunwayTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(Runway.class);
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"Class Runway is not declared public");
}
@Test
public void testFields() {
FieldHeader runwayLength = assertNotNull(getOriginalFieldHeader(Runway.class, "runwayLength"), emptyContext(),
result -> "Could not find field 'runwayLength'");
assertTrue(Modifier.isPrivate(runwayLength.modifiers()), emptyContext(), result ->
"Field 'runwayLength' is not declared private");
assertTrue(Modifier.isFinal(runwayLength.modifiers()), emptyContext(), result ->
"Field 'runwayLength' is not declared final");
assertEquals(int.class, runwayLength.getType(), emptyContext(), result ->
"Type of field 'runwayLength' is incorrect");
}
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void testLand(boolean canLand) {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
int runwayLength = canLand ? 3000 : 0;
Runway runwayInstance = new Runway(runwayLength);
String aircraftRegistration = "D-DFOP";
FuelType fuelType = FuelType.JetA;
double planeMass = 1000;
AtomicBoolean calledLand = new AtomicBoolean(false);
Plane planeInstance = new Plane(aircraftRegistration, 0, fuelType, 1000) {
@Override
protected double mass() {
return planeMass;
}
@Override
public void land() {
calledLand.set(true);
}
};
Context context = contextBuilder()
.add("runwayLength", runwayLength)
.add("plane.getIdentifier()", aircraftRegistration)
.add("plane.mass()", planeMass)
.build();
outputStream.reset();
Delegation.disable(MethodHeader.of(Runway.class, "land", Plane.class));
call(() -> runwayInstance.land(planeInstance), context, result ->
"An exception occurred while invoking land(Plane)");
if (canLand) {
assertTrue(calledLand.get(), context, result -> "Method did not call land(Plane) but was supposed to");
assertStringEquals("Plane %s has landed successfully.".formatted(aircraftRegistration),
outputStream.toString(),
context,
result -> "Method printed wrong message to System.out");
} else {
assertFalse(calledLand.get(), context, result -> "Method called land(Plane) when it was not supposed to");
assertStringEquals("Plane %s could not land. The runway is too short.".formatted(aircraftRegistration),
outputStream.toString(),
context,
result -> "Method printed wrong message to System.out");
}
} finally {
System.setOut(oldOut);
}
}
}

View file

@ -0,0 +1,103 @@
package h05;
import com.google.common.util.concurrent.AtomicDouble;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import static h05.TestUtils.assertDoubleEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class TankTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(Tank.class);
assertTrue(originalClassHeader.getInterfaceTypes().contains(Refuelling.class), emptyContext(), result ->
"Class Tank does not implement interface Refuelling");
}
@Test
public void testFields() {
FieldHeader fuelType = getOriginalFieldHeader(Tank.class, "fuelType");
assertEquals(FuelType.class, fuelType.getType(), emptyContext(), result ->
"Field 'fuelType' does not have type FuelType");
}
@Test
public void testRefuelPlane() {
FuelType fuelType = FuelType.JetA;
double fuelCapacity = 200;
double initialFuelLevel = 42;
AtomicDouble refuelAmount = new AtomicDouble(-1);
Tank tankInstance = new Tank(fuelType);
Plane planeInstance = new Plane("D-ABCD", 0, fuelType, fuelCapacity) {
@Override
protected double mass() {
return 0;
}
@Override
public void refuel(double amount) {
refuelAmount.set(amount);
}
};
FieldHeader.of(Plane.class, "currentFuelLevel").setValue(planeInstance, initialFuelLevel);
Context context = contextBuilder()
.add("plane.fuelType", fuelType)
.add("plane.fuelCapacity", fuelCapacity)
.add("plane.currentFuelLevel", initialFuelLevel)
.build();
Delegation.disable(MethodHeader.of(Tank.class, "refuelPlane", Plane.class));
call(() -> tankInstance.refuelPlane(planeInstance), context, result ->
"An exception occurred while invoking refuelPlane(Plane)");
assertDoubleEquals(fuelCapacity - initialFuelLevel, refuelAmount.get(), context, result ->
"refuelPlane(Plane) did not pass the correct value to plane.refuel(double)");
}
@Test
public void testRefuelPlane_ErrorMessage() {
PrintStream oldOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.setOut(new PrintStream(outputStream));
try {
FuelType tankFuelType = FuelType.JetA;
FuelType planeFuelType = FuelType.Biokerosin;
Tank tankInstance = new Tank(tankFuelType);
Plane planeInstance = new Plane("D-ABCD", 0, planeFuelType, 1000) {
@Override
protected double mass() {
return 0;
}
};
Context context = contextBuilder()
.add("Tank FuelType", tankFuelType)
.add("Plane FuelType", planeFuelType)
.build();
outputStream.reset();
Delegation.disable(MethodHeader.of(Tank.class, "refuelPlane", Plane.class));
call(() -> tankInstance.refuelPlane(planeInstance), context, result ->
"An exception occurred while invoking refuelPlane(Plane)");
assertTrue(outputStream.size() > 0, context, result -> "Nothing was written to System.out");
} finally {
System.setOut(oldOut);
}
}
}

View file

@ -0,0 +1,189 @@
package h05;
import com.google.common.util.concurrent.AtomicDouble;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.FieldHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import static h05.TestUtils.assertDoubleEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.assertEquals;
@TestForSubmission
public class TankerPlaneTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testClassHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(TankerPlane.class);
assertEquals(Plane.class, originalClassHeader.getSuperType(), emptyContext(), result ->
"TankerPlane does not extend Plane");
assertTrue(originalClassHeader.getInterfaceTypes().contains(Refuelling.class), emptyContext(), result ->
"TankerPlane does not implement Refuelling");
}
@Test
public void testFields() {
FieldHeader availableAmount = getOriginalFieldHeader(TankerPlane.class, "availableAmount");
assertTrue(Modifier.isPrivate(availableAmount.modifiers()), emptyContext(), result ->
"Field 'availableAmount' was not declared private");
assertTrue(Modifier.isFinal(availableAmount.modifiers()), emptyContext(), result ->
"Field 'availableAmount' was not declared final");
assertEquals(double[].class, availableAmount.getType(), emptyContext(), result ->
"Type of field 'availableAmount' is incorrect");
String aircraftRegistration = "D-FLOP";
int baseWeight = 500;
FuelType fuelType = FuelType.JetB;
double fuelCapacity = 200;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("baseWeight", baseWeight)
.add("fuelType", fuelType)
.add("fuelCapacity", fuelCapacity)
.build();
Delegation.disable(MethodHeader.of(TankerPlane.class, String.class, int.class, FuelType.class, double.class));
TankerPlane tankerPlaneInstance = callObject(
() -> new TankerPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity),
context,
result -> "An exception occurred while invoking constructor of TankerPlane");
double[] availableAmountValue = FieldHeader.of(TankerPlane.class, "availableAmount").getValue(tankerPlaneInstance);
assertNotNull(availableAmount, context, result -> "Field 'availableAmount' is null");
assertEquals(FuelType.values().length, availableAmountValue.length, context,
result -> "Field 'availableAmount' does not have correct length");
}
@Test
public void testConstructor() {
String aircraftRegistration = "D-FLOP";
int baseWeight = 500;
FuelType fuelType = FuelType.JetB;
double fuelCapacity = 200;
Context context = contextBuilder()
.add("aircraftRegistration", aircraftRegistration)
.add("baseWeight", baseWeight)
.add("fuelType", fuelType)
.add("fuelCapacity", fuelCapacity)
.build();
Delegation.disable(MethodHeader.of(TankerPlane.class, String.class, int.class, FuelType.class, double.class));
TankerPlane tankerPlaneInstance = callObject(
() -> new TankerPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity),
context,
result -> "An exception occurred while invoking constructor of TankerPlane");
assertEquals(aircraftRegistration,
FieldHeader.of(Plane.class, "aircraftRegistration").getValue(tankerPlaneInstance),
context,
result -> "Field aircraftRegistration has incorrect value");
assertEquals(baseWeight,
FieldHeader.of(Plane.class, "baseWeight").getValue(tankerPlaneInstance),
context,
result -> "Field baseWeight has incorrect value");
assertEquals(fuelType,
FieldHeader.of(Plane.class, "fuelType").getValue(tankerPlaneInstance),
context,
result -> "Field fuelType has incorrect value");
assertEquals(fuelCapacity,
FieldHeader.of(Plane.class, "fuelCapacity").getValue(tankerPlaneInstance),
context,
result -> "Field fuelCapacity has incorrect value");
}
@Test
public void testLoadFuel() {
double[] availableAmount = new double[FuelType.values().length];
Arrays.fill(availableAmount, 42);
TankerPlane tankerPlaneInstance = new TankerPlane("D-ABCD", 0, FuelType.JetA, 1000);
FieldHeader.of(TankerPlane.class, "availableAmount").setValue(tankerPlaneInstance, availableAmount);
Delegation.disable(MethodHeader.of(TankerPlane.class, "loadFuel", FuelType.class, double.class));
for (FuelType fuelType : FuelType.values()) {
double amount = 1295;
Context context = contextBuilder()
.add("availableAmount", availableAmount)
.add("fuelType", fuelType)
.add("amount", amount)
.build();
call(() -> tankerPlaneInstance.loadFuel(fuelType, amount), context, result ->
"An exception occurred while invoking loadFuel(FuelType, double): " + result.cause());
assertDoubleEquals(1337, availableAmount[fuelType.ordinal()], context, result ->
"loadFuel(FuelType, double) did not update the fuel amount for fuel type" + fuelType);
}
}
@Test
public void testMass() {
String aircraftRegistration = "D-FLOP";
int baseWeight = 500;
FuelType fuelType = FuelType.JetA;
double fuelCapacity = 200;
TankerPlane tankerPlaneInstance = new TankerPlane(aircraftRegistration, baseWeight, fuelType, fuelCapacity);
double totalFuelAmount = 0;
double[] availableAmount = new double[FuelType.values().length];
for (int i = 0; i < availableAmount.length; i++) {
totalFuelAmount += availableAmount[i] = (i + 1) * 100;
}
FieldHeader.of(TankerPlane.class, "availableAmount").setValue(tankerPlaneInstance, availableAmount);
Context context = contextBuilder()
.add("baseWeight", baseWeight)
.add("availableAmount", availableAmount)
.build();
Delegation.disable(MethodHeader.of(TankerPlane.class, "mass"));
double returnValue = callObject(tankerPlaneInstance::mass, context, result ->
"An exception occurred while invoking mass()");
assertDoubleEquals(baseWeight + totalFuelAmount, returnValue, context, result ->
"mass() did not return the correct value");
}
@Test
public void testRefuelPlane() throws ReflectiveOperationException {
TankerPlane tankerPlaneInstance = new TankerPlane("D-ABCD", 0, FuelType.JetA, 1000);
double[] availableAmount = new double[FuelType.values().length];
Arrays.fill(availableAmount, 200);
TankerPlane.class.getDeclaredField("availableAmount").set(tankerPlaneInstance, availableAmount);
FuelType fuelType = FuelType.JetA;
double fuelCapacity = 200;
double initialFuelLevel = 100;
AtomicDouble refuelAmount = new AtomicDouble(-1);
Plane planeInstance = new Plane("D-ABCD", 0, fuelType, fuelCapacity) {
@Override
protected double mass() {
return 0;
}
@Override
public void refuel(double amount) {
refuelAmount.set(amount);
}
};
FieldHeader.of(Plane.class, "currentFuelLevel").setValue(planeInstance, initialFuelLevel);
Context context = contextBuilder()
.add("plane.fuelType", fuelType)
.add("plane.fuelCapacity", fuelCapacity)
.add("plane.currentFuelLevel", initialFuelLevel)
.build();
Delegation.disable(MethodHeader.of(TankerPlane.class, "refuelPlane", Plane.class));
call(() -> tankerPlaneInstance.refuelPlane(planeInstance), context, result ->
"An exception occurred while invoking refuelPlane(Plane)");
assertDoubleEquals(fuelCapacity - initialFuelLevel, refuelAmount.get(), context, result ->
"refuelPlane(Plane) did not pass the correct value to plane.refuel(double)");
}
}

View file

@ -0,0 +1,46 @@
package h05;
import org.tudalgo.algoutils.tutor.general.assertions.Assertions2;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import org.tudalgo.algoutils.tutor.general.assertions.PreCommentSupplier;
import org.tudalgo.algoutils.tutor.general.assertions.ResultOfObject;
import org.tudalgo.algoutils.tutor.general.assertions.expected.ExpectedObject;
import org.tudalgo.algoutils.tutor.general.assertions.expected.ExpectedObjects;
import java.util.Set;
public final class TestUtils {
private TestUtils() {}
public static void clearAirspace() {
Airspace airspace = Airspace.get();
Set.copyOf(airspace.getFlyingInAirspace()).forEach(airspace::deregister);
}
public static void assertStringEquals(String expected,
String actual,
Context context,
PreCommentSupplier<? super ResultOfObject<String>> preCommentSupplier) {
Assertions2.<String>testOfObjectBuilder()
.expected(ExpectedObjects.equalTo(expected.replaceAll("[^A-Za-z0-9-()]", "")))
.build()
.run(actual != null ? actual.replaceAll("[^A-Za-z0-9-()]", "") : null)
.check(context, preCommentSupplier);
}
public static boolean stringEquals(String expected, String actual) {
return expected.replaceAll("[^A-Za-z0-9-()]", "").equals(actual.replaceAll("[^A-Za-z0-9-()]", ""));
}
public static void assertDoubleEquals(double expected,
double actual,
Context context,
PreCommentSupplier<? super ResultOfObject<Double>> preCommentSupplier) {
Assertions2.<Double>testOfObjectBuilder()
.expected(ExpectedObject.of(expected, a -> a > expected - 0.001 && a < expected + 0.001))
.build()
.run(actual)
.check(context, preCommentSupplier);
}
}

View file

@ -0,0 +1,80 @@
package h05;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.sourcegrade.jagr.api.rubric.TestForSubmission;
import org.tudalgo.algoutils.transform.util.headers.ClassHeader;
import org.tudalgo.algoutils.transform.util.headers.MethodHeader;
import org.tudalgo.algoutils.tutor.general.assertions.Context;
import java.lang.reflect.Modifier;
import java.util.Set;
import static h05.TestUtils.assertStringEquals;
import static org.tudalgo.algoutils.transform.SubmissionExecutionHandler.*;
import static org.tudalgo.algoutils.tutor.general.assertions.Assertions2.*;
@TestForSubmission
public class WeatherBalloonTest {
@AfterEach
public void tearDown() {
resetAll();
}
@Test
public void testHeader() {
ClassHeader originalClassHeader = getOriginalClassHeader(WeatherBalloon.class);
assertTrue(Modifier.isPublic(originalClassHeader.modifiers()), emptyContext(), result ->
"WeatherBalloon is not public");
assertTrue(originalClassHeader.getInterfaceTypes().contains(Flying.class), emptyContext(), result ->
"WeatherBalloon does not implement interface Flying");
}
@Test
public void testStart() {
Airspace airspace = Airspace.get();
TestUtils.clearAirspace();
WeatherBalloon weatherBalloonInstance = new WeatherBalloon(0);
Delegation.disable(MethodHeader.of(WeatherBalloon.class, "start"));
call(weatherBalloonInstance::start, emptyContext(), result ->
"An exception occurred while invoking method start");
Set<Flying> flyingInAirspace = airspace.getFlyingInAirspace();
assertEquals(1, flyingInAirspace.size(), emptyContext(), result ->
"Number of aircraft in airspace differs from expected value");
assertSame(weatherBalloonInstance, flyingInAirspace.iterator().next(), emptyContext(), result ->
"Calling WeatherBalloon (using 'this') was not added to airspace");
}
@Test
public void testPop() {
Airspace airspace = Airspace.get();
TestUtils.clearAirspace();
WeatherBalloon weatherBalloonInstance = new WeatherBalloon(0);
weatherBalloonInstance.start();
Delegation.disable(MethodHeader.of(WeatherBalloon.class, "pop"));
call(weatherBalloonInstance::pop, emptyContext(), result ->
"An exception occurred while invoking method pop");
Set<?> flyingInAirspace = airspace.getFlyingInAirspace();
assertEquals(0, flyingInAirspace.size(), emptyContext(), result ->
"Number of aircraft in airspace differs from expected value");
}
@Test
public void testGetIdentifier() {
int balloonNumber = 12345;
Context context = contextBuilder()
.add("balloonNumber", balloonNumber)
.build();
WeatherBalloon weatherBalloonInstance = new WeatherBalloon(balloonNumber);
// TODO: Implementation and exercise sheet differ
Delegation.disable(MethodHeader.of(WeatherBalloon.class, "getIdentifier"));
assertStringEquals("WeatherBalloon " + balloonNumber, weatherBalloonInstance.getIdentifier(), context,
result -> "Identifier returned by getIdentifier is incorrect");
}
}

View file

@ -0,0 +1,18 @@
[
{
"enumConstantName": "JetA",
"enumConstantValue": 1.0
},
{
"enumConstantName": "JetB",
"enumConstantValue": 1.2
},
{
"enumConstantName": "AvGas",
"enumConstantValue": 0.99
},
{
"enumConstantName": "Biokerosin",
"enumConstantValue": 1.02
}
]

View file

@ -0,0 +1,58 @@
[
{
"invocation": 0,
"airspaceState": [
""
]
},
{
"invocation": 1,
"airspaceState": [
"Airspace is empty"
]
},
{
"invocation": 2,
"airspaceState": [
"GAG-67 is flying in airspace (100 PAX)",
"WeatherBalloon 99 is flying in airspace"
]
},
{
"invocation": 3,
"airspaceState": [
"GAG-67 is flying in airspace (0 PAX)",
"WeatherBalloon 99 is flying in airspace"
]
},
{
"invocation": 4,
"airspaceState": [
"GAG-67 is flying in airspace (0 PAX)",
"WeatherBalloon 99 is flying in airspace",
"D-AFFF is flying in airspace"
]
},
{
"invocation": 5,
"airspaceState": [
"GAG-67 is flying in airspace (0 PAX)",
"WeatherBalloon 99 is flying in airspace",
"D-AFFF is flying in airspace",
"D-ABBB is flying in airspace (30 PAX)"
]
},
{
"invocation": 6,
"airspaceState": [
"GAG-67 is flying in airspace (0 PAX)",
"WeatherBalloon 99 is flying in airspace"
]
},
{
"invocation": 7,
"airspaceState": [
"GAG-67 is flying in airspace (0 PAX)"
]
}
]

View file

@ -0,0 +1,57 @@
package h05;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class Airspace {
private static Airspace INSTANCE = null;
public static Airspace get(){
if(INSTANCE == null){
INSTANCE = new Airspace();
}
return INSTANCE;
}
private final Set<Flying> flyingInAirspace = new HashSet<>();
private Airspace(){
}
Set<Flying> getFlyingInAirspace() {
return Collections.unmodifiableSet(flyingInAirspace);
}
void register(Flying flying){
flyingInAirspace.add(flying);
}
void deregister(Flying flying){
flyingInAirspace.remove(flying);
}
void scanAirspace(){
System.out.println("Scanning...");
if (flyingInAirspace.isEmpty()){
System.out.println("Airspace is empty");
return;
}
for (Flying flying : flyingInAirspace){
System.out.print(flying.getIdentifier() + " is flying in airspace");
if(flying instanceof PassengerPlane passengerPlane){
System.out.print(" (" + passengerPlane.getPassengerCount() + " PAX)");
}
System.out.println();
}
}
}

View file

@ -0,0 +1,34 @@
package h05;
public class CargoPlane extends Plane implements CarriesCargo{
/*
Stacks are a type of data structure that follows the Last In First Out (LIFO) principle.
Explanation will be found in Task description.
*/
private final CargoStack containers = new CargoStack();
public CargoPlane(String aircraftRegistration, int baseWeight, FuelType fuelType, double maxFuelLevel) {
super(aircraftRegistration, baseWeight, fuelType, maxFuelLevel);
}
@Override
public void loadContainer(int cargoWeight) {
containers.push(cargoWeight);
}
@Override
public boolean hasFreightLoaded() {
return !containers.empty();
}
@Override
public int unloadNextContainer() {
return containers.pop();
}
@Override
protected double mass() {
return baseWeight + containers.getSum();
}
}

View file

@ -0,0 +1,27 @@
package h05;
import java.util.Stack;
public class CargoStack {
private final Stack<Integer> stack = new Stack<>();
private int sum = 0;
public boolean empty(){
return stack.empty();
}
public void push(int value){
sum += value;
stack.push(value);
}
public int pop(){
int removedValue = stack.pop();
sum -= removedValue;
return removedValue;
}
public int getSum(){
return sum;
}
}

View file

@ -0,0 +1,8 @@
package h05;
public interface CarriesCargo {
void loadContainer(int cargoWeight);
boolean hasFreightLoaded();
int unloadNextContainer();
}

View file

@ -0,0 +1,7 @@
package h05;
public interface CarriesPassengers {
void board(int peopleCount);
int getPassengerCount();
void disembark();
}

View file

@ -0,0 +1,32 @@
package h05;
public class CombinedPlane extends PassengerPlane implements CarriesCargo{
private final CargoStack containers = new CargoStack();
public CombinedPlane(String aircraftRegistration, int baseWeight, FuelType fuelType, double maxFuelLevel, int crew) {
super(aircraftRegistration, baseWeight, fuelType, maxFuelLevel, crew);
}
@Override
public void loadContainer(int cargoWeight) {
containers.push(cargoWeight);
}
@Override
public boolean hasFreightLoaded() {
return !containers.empty();
}
@Override
public int unloadNextContainer() {
return containers.pop();
}
@Override
protected double mass() {
return super.mass() + containers.getSum();
}
}

View file

@ -0,0 +1,5 @@
package h05;
public interface Flying {
String getIdentifier();
}

View file

@ -0,0 +1,14 @@
package h05;
public enum FuelType {
JetA(1.0), JetB(1.2), AvGas(0.99), Biokerosin (1.02);
private final double consumptionMultiplicator;
FuelType(double consumptionMultiplicator){
this.consumptionMultiplicator = consumptionMultiplicator;
}
public double getConsumptionMultiplicator() {
return consumptionMultiplicator;
}
}

View file

@ -0,0 +1,69 @@
package h05;
/**
* Main entry point in executing the program.
*/
public class Main {
/**
* Main entry point in executing the program.
*
* @param args program arguments, currently ignored
*/
public static void main(String[] args) {
Airspace airspace = Airspace.get();
airspace.scanAirspace();
/*
Die konkreten Zahlen sind noch nicht final und können sich ändern.
*/
Runway runway01 = new Runway(2000);
Runway runway02 = new Runway(4000);
WeatherBalloon weatherBalloon = new WeatherBalloon(99);
weatherBalloon.start();
Tank jetATank = new Tank(FuelType.JetA);
Tank jetBTank = new Tank(FuelType.JetB);
TankerPlane tankerPlane = new TankerPlane("D-ABCD", 10000, FuelType.JetA, 1000);
tankerPlane.loadFuel(FuelType.AvGas, 100000);
tankerPlane.loadFuel(FuelType.Biokerosin, 100000);
PassengerPlane passengerPlane = new PassengerPlane("GAG-67", 10000, FuelType.JetA, 1700, 5);
jetATank.refuelPlane(passengerPlane);
passengerPlane.board(100);
passengerPlane.takeOff();
airspace.scanAirspace();
CargoPlane cargoPlane = new CargoPlane("D-AFFF", 8000, FuelType.JetB, 1500);
cargoPlane.loadContainer(1000);
jetBTank.refuelPlane(cargoPlane);
passengerPlane.disembark();
airspace.scanAirspace();
cargoPlane.takeOff();
cargoPlane.fly(1000);
airspace.scanAirspace();
CombinedPlane combinedPlane = new CombinedPlane("D-ABBB", 9000, FuelType.AvGas, 10700, 5);
tankerPlane.refuelPlane(combinedPlane);
combinedPlane.board(30);
combinedPlane.loadContainer(400);
combinedPlane.takeOff();
combinedPlane.fly(3000);
airspace.scanAirspace();
runway01.land(combinedPlane);
runway02.land(cargoPlane);
airspace.scanAirspace();
weatherBalloon.pop();
airspace.scanAirspace();
}
}

View file

@ -0,0 +1,36 @@
package h05;
public class PassengerPlane extends Plane implements CarriesPassengers {
protected static final char AVERAGE_PEOPLE_WEIGHT = 100;
protected static final char AVERAGE_LUGGAGE_WEIGHT = 15;
private int passengerCount = 0;
private final int crewCount;
public PassengerPlane(String aircraftRegistration, int baseWeight, FuelType fuelType, double fuelCapacity, int crewCount) {
super(aircraftRegistration, baseWeight, fuelType, fuelCapacity);
this.crewCount = crewCount;
}
@Override
public void board(int passengerCount) {
this.passengerCount += passengerCount;
}
@Override
public void disembark() {
this.passengerCount = 0;
}
@Override
public int getPassengerCount() {
return passengerCount;
}
@Override
protected double mass() {
return baseWeight + (passengerCount + crewCount) * AVERAGE_PEOPLE_WEIGHT + passengerCount * AVERAGE_LUGGAGE_WEIGHT;
}
}

View file

@ -0,0 +1,77 @@
package h05;
public abstract class Plane implements Flying {
private static final double CONSUMPTION_PER_KM_KG = 1.1494e-4;
private final String aircraftRegistration;
protected final int baseWeight;
private double currentFuelLevel;
private final FuelType fuelType;
private final double fuelCapacity;
public Plane(String aircraftRegistration, int baseWeight, FuelType fuelType, double fuelCapacity){
this.aircraftRegistration = aircraftRegistration;
this.baseWeight = baseWeight;
this.fuelType = fuelType;
this.currentFuelLevel = 0;
this.fuelCapacity = fuelCapacity;
}
protected abstract double mass();
protected double getFuelConsumptionPerKilometer(){
return CONSUMPTION_PER_KM_KG * mass() * fuelType.getConsumptionMultiplicator();
}
public void takeOff(){
Airspace.get().register(this);
}
public void land(){
Airspace.get().deregister(this);
}
public void fly(double distance){
double neededFuel = distance * getFuelConsumptionPerKilometer();
// if the plane does not have enough fuel to fly the distance, it will not fly
if(neededFuel > currentFuelLevel){
System.out.println("Plane " + aircraftRegistration + " does not have enough fuel to fly " + distance + " km.");
return;
}
currentFuelLevel -= neededFuel;
System.out.println("Plane " + aircraftRegistration + " flew " + distance + " km and has " + currentFuelLevel + " liters of fuel left.");
}
public FuelType getFuelType() {
return fuelType;
}
public double getFuelCapacity() {
return fuelCapacity;
}
public double getCurrentFuelLevel() {
return currentFuelLevel;
}
public void refuel(double amount){
currentFuelLevel += amount;
if(currentFuelLevel > fuelCapacity){
currentFuelLevel = fuelCapacity;
System.out.println("The Tank of Plane " + aircraftRegistration + " has overflowed!");
}
}
@Override
public String getIdentifier(){
return aircraftRegistration;
}
}

View file

@ -0,0 +1,5 @@
package h05;
public interface Refuelling {
void refuelPlane(Plane plane);
}

View file

@ -0,0 +1,41 @@
package h05;
public class Runway {
private final int runwayLength;
/*
TODO: Man könnte sich noch überlegen, wie Flugzeugtypen, die hier landen können, festgesetzt werden
*/
public Runway(int runwayLength){
this.runwayLength = runwayLength;
}
public int getRunwayLength(){
return runwayLength;
}
/*
TODO: hier müsste man sich noch eine bessere Formel für die Landung überlegen
*/
public static double calculateLandingDistance(double mass){
return mass/40;
}
public boolean canLand(Plane plane){
return calculateLandingDistance(plane.mass()) <= runwayLength;
}
/*
TODO: Man könnte auch stattdessen die land() methode in plane erweitern und diese hier weglassen
*/
public void land(Plane plane){
if(canLand(plane)){
plane.land();
System.out.println("Plane " + plane.getIdentifier() + " has landed successfully.");
} else {
System.out.println("Plane " + plane.getIdentifier() + " could not land. The runway is too short.");
}
}
}

View file

@ -0,0 +1,21 @@
package h05;
public class Tank implements Refuelling{
private final FuelType fuelType;
public Tank(FuelType fuelType){
this.fuelType = fuelType;
}
@Override
public void refuelPlane(Plane plane) {
if(plane.getFuelType() != fuelType){
System.out.println("Incompatible fuel types, not refuelling");
return;
}
double missingFuel = plane.getFuelCapacity() - plane.getCurrentFuelLevel();
plane.refuel(missingFuel);
}
}

View file

@ -0,0 +1,35 @@
package h05;
public class TankerPlane extends Plane implements Refuelling{
private final double[] availableAmount = new double[FuelType.values().length];
public TankerPlane(String aircraftRegistration, int baseWeight, FuelType fuelType, double maxFuelLevel) {
super(aircraftRegistration, baseWeight, fuelType, maxFuelLevel);
}
public void loadFuel(FuelType fuelType, double amount){
availableAmount[fuelType.ordinal()] += amount;
}
@Override
protected double mass() {
double totalAmount = 0;
for (int i = 0; i < FuelType.values().length; i++){
totalAmount += availableAmount[i];
}
return baseWeight + totalAmount;
}
@Override
public void refuelPlane(Plane plane) {
double missingFuel = plane.getFuelCapacity() - plane.getCurrentFuelLevel();
double actualAmount = Math.min(availableAmount[plane.getFuelType().ordinal()], missingFuel);
availableAmount[plane.getFuelType().ordinal()] -= actualAmount;
plane.refuel(actualAmount);
}
}

View file

@ -0,0 +1,23 @@
package h05;
public class WeatherBalloon implements Flying{
private final int balloonNumber;
public WeatherBalloon(int balloonNumber){
this.balloonNumber = balloonNumber;
}
public void start(){
Airspace.get().register(this);
}
public void pop(){
Airspace.get().deregister(this);
}
@Override
public String getIdentifier() {
return "WeatherBalloon " + balloonNumber;
}
}

View file

@ -0,0 +1,16 @@
package h05;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* An example JUnit test class.
*/
public class ExampleJUnitTest {
@Test
public void testAddition() {
assertEquals(2, 1 + 1);
}
}

1
solution/H05/version Normal file
View file

@ -0,0 +1 @@
0.1.0-SNAPSHOT