Squashed 'solution/H05/' content from commit db3524a

git-subtree-dir: solution/H05
git-subtree-split: db3524a6321cc840e329abb2b44bea58c7583330
This commit is contained in:
Oshgnacknak 2025-01-11 16:41:10 +01:00
commit 880c3ae75f
63 changed files with 2875 additions and 0 deletions

12
.editorconfig Normal file
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
.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
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/)

37
build.gradle.kts Normal file
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")
}
}
}

13
gradle/libs.versions.toml Normal file
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" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

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
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
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

11
settings.gradle.kts Normal file
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");
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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
version Normal file
View file

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