Commit a9eacf10 by Michael Schmid

renamed namespace and added a few tests

parent 4391562c
...@@ -16,6 +16,12 @@ ...@@ -16,6 +16,12 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.sourceforge.cobertura</groupId>
<artifactId>cobertura</artifactId>
<version>2.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId> <artifactId>commons-math3</artifactId>
<version>3.2</version> <version>3.2</version>
...@@ -56,6 +62,17 @@ ...@@ -56,6 +62,17 @@
</dependency> </dependency>
</dependencies> </dependencies>
<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.7</version>
</plugin>
</plugins>
</reporting>
<build> <build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins> <plugins>
......
...@@ -11,7 +11,8 @@ import mvd.jester.priority.RateMonotonic; ...@@ -11,7 +11,8 @@ import mvd.jester.priority.RateMonotonic;
*/ */
public class App { public class App {
public static void main(String[] args) { public static void main(String[] args) {
SystemSetup.Builder builder = new SystemSetup.Builder().setNumberOfProcessors(8); for (int p = 4; p <= 16; p *= 2) {
SystemSetup.Builder builder = new SystemSetup.Builder().setNumberOfProcessors(p);
TestEnvironment te = new TestEnvironment(builder, 40000); TestEnvironment te = new TestEnvironment(builder, 40000);
te.registerSchedulingAlgorithm(new RateMonotonic()); te.registerSchedulingAlgorithm(new RateMonotonic());
...@@ -21,20 +22,7 @@ public class App { ...@@ -21,20 +22,7 @@ public class App {
te.registerTest(mvd.jester.tests.MaiaBertogna.class); te.registerTest(mvd.jester.tests.MaiaBertogna.class);
te.registerTest(mvd.jester.tests.ChwaLee.class); te.registerTest(mvd.jester.tests.ChwaLee.class);
te.registerSimulator(mvd.jester.simulator.MaiaBertogna.class);
te.registerSimulator(mvd.jester.simulator.SchmidMottok.class);
te.runExperiments(); te.runExperiments();
}
// SystemSetup setup = SystemSetup.readFromFile(
// "/home/mike/Promotion/projects/eclipse/jester/results/test_this.txt", 16);
// SchmidMottok sm = new SchmidMottok(setup);
// MaiaBertogna mb = new MaiaBertogna(setup);
// RateMonotonic rm = new RateMonotonic();
// sm.runSchedulabilityCheck(rm);
// mb.runSchedulabilityCheck(rm);
} }
} }
...@@ -39,8 +39,8 @@ public class ResultCollector<T extends TypeInterface> ...@@ -39,8 +39,8 @@ public class ResultCollector<T extends TypeInterface>
} }
public void addResult(SchedulingInfo schedulingInfo) { public boolean addResult(SchedulingInfo schedulingInfo) {
this.schedulingInfos.add(schedulingInfo); return this.schedulingInfos.add(schedulingInfo);
} }
public Set<SchedulingInfo> getResults() { public Set<SchedulingInfo> getResults() {
......
...@@ -4,13 +4,17 @@ import java.io.IOException; ...@@ -4,13 +4,17 @@ import java.io.IOException;
import java.time.LocalTime; import java.time.LocalTime;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ObjectArrays; import com.google.common.collect.ObjectArrays;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable; import com.google.common.collect.TreeBasedTable;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter; import org.apache.commons.csv.CSVPrinter;
import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.simulator.AbstractSimulator; import mvd.jester.simulator.AbstractSimulator;
import mvd.jester.tests.AbstractTest; import mvd.jester.tests.AbstractTest;
import mvd.jester.utils.Logger; import mvd.jester.utils.Logger;
...@@ -33,14 +37,55 @@ public class ResultLogger { ...@@ -33,14 +37,55 @@ public class ResultLogger {
} }
public void logTests(Set<ResultCollector<AbstractTest>> results) { public void logTests(Set<ResultCollector<AbstractTest>> results) {
if (!results.isEmpty()) {
logFeasibility(results, "test"); logFeasibility(results, "test");
logFeasibilityRatio(results, "test");
logTaskRatio(results, "test"); logTaskRatio(results, "test");
logFeasibilityLevel(results, "test");
}
} }
public void logSimulations(Set<ResultCollector<AbstractSimulator>> results) { public void logSimulations(Set<ResultCollector<AbstractSimulator>> results) {
if (!results.isEmpty()) {
logFeasibility(results, "sim"); logFeasibility(results, "sim");
logTaskRatio(results, "sim"); logTaskRatio(results, "sim");
} }
}
public <T extends TypeInterface> void logFeasibilityLevel(Set<ResultCollector<T>> results,
String type) {
LocalTime date = LocalTime.now();
Logger log = new Logger("./results/feasibility_level_" + type + "_" + numberOfProcessors
+ "_" + date.getHour() + ":" + date.getMinute() + ".txt");
Table<Long, ResultCollector<T>, Double> resultTable = TreeBasedTable.create();
Set<ResultCollector<T>> resultCollectors = new TreeSet<>();
for (long util = 0; util <= numberOfProcessors * 10; util += numberOfProcessors / 4) {
for (ResultCollector<T> rc : results) {
resultCollectors.add(rc);
final long local_util = util;
Supplier<Stream<SchedulingInfo>> schedulingResults = () -> rc.getResults().stream()
.filter(r -> Math.round(r.getUtilization() * 10 / (numberOfProcessors / 4))
* (numberOfProcessors / 4) == local_util);
if (schedulingResults.get().filter(r -> !r.checkTasksetFeasible()).count() > 0) {
double feasibleTasksets = (double) schedulingResults.get()
.filter(r -> r.checkLevelFail(Level.HIGH)).count()
/ schedulingResults.get().filter(r -> !r.checkTasksetFeasible())
.count();
resultTable.put(util, rc, feasibleTasksets);
} else {
resultTable.put(util, rc, 0.);
}
}
}
logData(log, resultTable, resultCollectors, "Utilization");
}
public <T extends TypeInterface> void logFeasibility(Set<ResultCollector<T>> results, public <T extends TypeInterface> void logFeasibility(Set<ResultCollector<T>> results,
String type) { String type) {
...@@ -51,23 +96,49 @@ public class ResultLogger { ...@@ -51,23 +96,49 @@ public class ResultLogger {
Table<Long, ResultCollector<T>, Long> resultTable = TreeBasedTable.create(); Table<Long, ResultCollector<T>, Long> resultTable = TreeBasedTable.create();
Set<ResultCollector<T>> resultCollectors = new TreeSet<>(); Set<ResultCollector<T>> resultCollectors = new TreeSet<>();
for (ResultCollector<T> r : results) { for (long util = 0; util <= numberOfProcessors * 10; util += numberOfProcessors / 4) {
System.out.println(r.getName() + ": "
+ r.getResults().stream().filter(p -> p.checkTasksetFeasible()).count());
}
for (long util = 0; util < numberOfProcessors * 10; util++) {
for (ResultCollector<T> rc : results) { for (ResultCollector<T> rc : results) {
resultCollectors.add(rc); resultCollectors.add(rc);
final long local_util = util; final long local_util = util;
long feasibleTasksets = rc.getResults().stream() long feasibleTasksets = rc.getResults().stream()
.filter(r -> Math.round(r.getUtilization() * 10) == local_util) .filter(r -> Math.round(r.getUtilization() * 10 / (numberOfProcessors / 4))
* (numberOfProcessors / 4) == local_util)
.filter(r -> r.checkTasksetFeasible()).count(); .filter(r -> r.checkTasksetFeasible()).count();
resultTable.put(util, rc, feasibleTasksets); resultTable.put(util, rc, feasibleTasksets);
} }
} }
logData(log, resultTable, resultCollectors); logData(log, resultTable, resultCollectors, "Utilization");
}
public <T extends TypeInterface> void logFeasibilityRatio(Set<ResultCollector<T>> results,
String type) {
LocalTime date = LocalTime.now();
Logger log = new Logger("./results/feasibility_ratio_" + type + "_" + numberOfProcessors
+ "_" + date.getHour() + ":" + date.getMinute() + ".txt");
Table<Long, ResultCollector<T>, Double> resultTable = TreeBasedTable.create();
Set<ResultCollector<T>> resultCollectors = new TreeSet<>();
for (long util = 0; util <= numberOfProcessors * 10; util += numberOfProcessors / 4) {
for (ResultCollector<T> rc : results) {
resultCollectors.add(rc);
final long local_util = util;
Supplier<Stream<SchedulingInfo>> schedulingResults = () -> rc.getResults().stream()
.filter(r -> Math.round(r.getUtilization() * 10 / (numberOfProcessors / 4))
* (numberOfProcessors / 4) == local_util);
if (schedulingResults.get().count() > 0) {
double feasibleTasksets =
(double) schedulingResults.get().filter(r -> r.checkTasksetFeasible())
.count() / schedulingResults.get().count();
resultTable.put(util, rc, feasibleTasksets);
} else {
resultTable.put(util, rc, 1.);
}
}
}
logData(log, resultTable, resultCollectors, "Utilization");
} }
public <T extends TypeInterface> void logTaskRatio(Set<ResultCollector<T>> results, public <T extends TypeInterface> void logTaskRatio(Set<ResultCollector<T>> results,
...@@ -79,28 +150,28 @@ public class ResultLogger { ...@@ -79,28 +150,28 @@ public class ResultLogger {
Table<Long, ResultCollector<T>, Long> resultTable = TreeBasedTable.create(); Table<Long, ResultCollector<T>, Long> resultTable = TreeBasedTable.create();
Set<ResultCollector<T>> resultCollectors = new TreeSet<>(); Set<ResultCollector<T>> resultCollectors = new TreeSet<>();
for (long ratio = 0; ratio < 10; ratio += 1) { for (long ratio = 0; ratio <= 10; ratio += 1) {
for (ResultCollector<T> rc : results) { for (ResultCollector<T> rc : results) {
resultCollectors.add(rc); resultCollectors.add(rc);
final long local_ratio = ratio; final long local_ratio = ratio;
long feasibleTasksets = rc.getResults().stream() long feasibleTasksets = rc.getResults().stream()
.filter(r -> Math.round(r.getParallelTaskRatio() * 10) == local_ratio) .filter(r -> Math.ceil(r.getParallelTaskRatio() * 10) == local_ratio)
.filter(r -> r.checkTasksetFeasible()).count(); .filter(r -> r.checkTasksetFeasible()).count();
resultTable.put(ratio, rc, feasibleTasksets); resultTable.put(ratio, rc, feasibleTasksets);
} }
} }
logData(log, resultTable, resultCollectors); logData(log, resultTable, resultCollectors, "TaskRatio");
} }
private <T extends TypeInterface> void logData(Logger log, private <T extends TypeInterface> void logData(Logger log,
Table<Long, ResultCollector<T>, Long> resultTable, Table<Long, ResultCollector<T>, ? extends Number> resultTable,
Set<ResultCollector<T>> resultCollectors) { Set<ResultCollector<T>> resultCollectors, String xDataName) {
final Appendable out = new StringBuilder(); final Appendable out = new StringBuilder();
try { try {
String[] resultCollectorNames = resultCollectors.stream() String[] resultCollectorNames = resultCollectors.stream()
.map(ResultCollector<T>::getName).toArray(String[]::new); .map(ResultCollector<T>::getName).toArray(String[]::new);
String[] header = ObjectArrays.concat("Utilization", resultCollectorNames); String[] header = ObjectArrays.concat(xDataName, resultCollectorNames);
final CSVPrinter printer = CSVFormat.DEFAULT.withHeader(header).print(out); final CSVPrinter printer = CSVFormat.DEFAULT.withHeader(header).print(out);
printer.printRecords(resultTable.rowMap().entrySet().stream() printer.printRecords(resultTable.rowMap().entrySet().stream()
...@@ -114,110 +185,4 @@ public class ResultLogger { ...@@ -114,110 +185,4 @@ public class ResultLogger {
log.log(out); log.log(out);
log.finalize(); log.finalize();
} }
// public <T extends TypeInterface> void logFeasibility(
// Table<Long, ResultCollector<T>, Set<SchedulingInfo>> results, String type) {
// LocalTime date = LocalTime.now();
// Logger log = new Logger("./results/feasibility_" + type + "_" + numberOfProcessors + "_"
// + date.getHour() + ":" + date.getMinute() + ".txt");
// String firstLine = new String("Utilization");
// if (!results.isEmpty()) {
// for (ResultCollector<T> pair : results.columnKeySet()) {
// firstLine = firstLine + "\t" + pair.getName();
// }
// log.log(firstLine);
// for (Long util : totalNumberOfTasksets.keySet()) {
// String line = String.valueOf((double) util / 10);
// for (ResultCollector<T> pair : results.columnKeySet()) {
// long feasibleTasksets = results.get(util, pair).stream()
// .filter(s -> s.checkTasksetFeasible()).count();
// line += "\t" + feasibleTasksets;
// }
// log.log(line);
// }
// }
// log.finalize();
// }
// public <T extends TypeInterface> void logFailedTardiness(
// Table<Long, ResultCollector<T>, Set<SchedulingInfo>> results, String type) {
// LocalTime date = LocalTime.now();
// Logger log = new Logger("./results/failed_tardiness_" + type + "_" + numberOfProcessors
// + "_" + date.getHour() + ":" + date.getMinute() + ".txt");
// String firstLine = new String("Utilization");
// if (!results.isEmpty()) {
// for (ResultCollector<T> pair : results.columnKeySet()) {
// firstLine = firstLine + "\t" + pair.getName();
// }
// log.log(firstLine);
// for (Long kv : totalNumberOfTasksets.keySet()) {
// String line = String.valueOf((double) kv / 10);
// for (ResultCollector<T> pair : results.columnKeySet()) {
// Set<SchedulingInfo> simulationInfos = results.get(kv, pair);
// List<Long> values = new ArrayList<>();
// for (SchedulingInfo s : simulationInfos) {
// Optional<TerminationInfo> failedTerminationInfo =
// s.getFailedTerminationInfo();
// if (failedTerminationInfo.isPresent()) {
// values.add(failedTerminationInfo.get().getLateness());
// }
// }
// double meanTardiness = 0;
// if (!values.isEmpty()) {
// meanTardiness = Stats.meanOf(values.iterator());
// }
// line += "\t" + meanTardiness;
// }
// log.log(line);
// }
// }
// log.finalize();
// }
// public <T extends TypeInterface> void logTardinessStatistics(
// Table<Long, ResultCollector<T>, Set<SchedulingInfo>> results, String type) {
// LocalTime date = LocalTime.now();
// Logger log = new Logger("./results/statistics_tardiness_" + type + "_" + numberOfProcessors
// + "_" + date.getHour() + ":" + date.getMinute() + ".txt");
// String firstLine = new String("Utilization");
// if (!results.isEmpty()) {
// for (ResultCollector<T> pair : results.columnKeySet()) {
// firstLine = firstLine + "\t" + pair.getName();
// }
// log.log(firstLine);
// for (Long kv : totalNumberOfTasksets.keySet()) {
// String line = String.valueOf((double) kv / 10);
// for (ResultCollector<T> pair : results.columnKeySet()) {
// Set<SchedulingInfo> simulationInfos = results.get(kv, pair);
// DescriptiveStatistics stats = new DescriptiveStatistics();
// for (SchedulingInfo s : simulationInfos) {
// for (TerminationInfo t : s.getTerminationInfos()) {
// stats.addValue(t.getLateness());
// }
// }
// line += "\t" + stats.getMean();
// }
// log.log(line);
// }
// }
// log.finalize();
// }
} }
...@@ -3,9 +3,10 @@ package mvd.jester.info; ...@@ -3,9 +3,10 @@ package mvd.jester.info;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import mvd.jester.info.TerminationInfo.Level;
/** /**
* DeadlineMissInfo * SchedulingInfo
*/ */
public class SchedulingInfo { public class SchedulingInfo {
...@@ -44,13 +45,18 @@ public class SchedulingInfo { ...@@ -44,13 +45,18 @@ public class SchedulingInfo {
terminationInfos.stream().filter(t -> t.getLateness() > 0).findFirst(); terminationInfos.stream().filter(t -> t.getLateness() > 0).findFirst();
} }
public boolean checkLevelFail(Level level) {
return terminationInfos.stream()
.anyMatch(t -> t.getLateness() > 0 && t.getTaskLevel() == level);
}
public boolean checkTasksetFeasible() { public boolean checkTasksetFeasible() {
// return terminationInfos.isEmpty(); // return terminationInfos.isEmpty();
return !terminationInfos.stream().anyMatch(t -> t.getLateness() > 0); return !terminationInfos.stream().anyMatch(t -> t.getLateness() > 0);
} }
public void addTerminationInfo(TerminationInfo terminationInfo) { public boolean addTerminationInfo(TerminationInfo terminationInfo) {
terminationInfos.add(terminationInfo); return terminationInfos.add(terminationInfo);
} }
/** /**
......
...@@ -9,14 +9,31 @@ public class TerminationInfo { ...@@ -9,14 +9,31 @@ public class TerminationInfo {
private final long deadline; private final long deadline;
private final long responseTime; private final long responseTime;
private final long lateness; private final long lateness;
private final Level taskLevel;
public TerminationInfo(long releaseTime, long deadline, long responseTime) { public TerminationInfo(long releaseTime, long deadline, long responseTime) {
this.releaseTime = releaseTime; this.releaseTime = releaseTime;
this.deadline = deadline; this.deadline = deadline;
this.responseTime = responseTime; this.responseTime = responseTime;
this.lateness = responseTime - deadline; this.lateness = responseTime - deadline;
this.taskLevel = Level.LOW;
} }
public TerminationInfo(long releaseTime, long deadline, long responseTime, Level taskLevel) {
this.releaseTime = releaseTime;
this.deadline = deadline;
this.responseTime = responseTime;
this.lateness = responseTime - deadline;
this.taskLevel = taskLevel;
}
public TerminationInfo(long deadline, long responseTime, Level taskLevel) {
this.releaseTime = 0;
this.deadline = deadline;
this.responseTime = responseTime;
this.lateness = responseTime - deadline;
this.taskLevel = taskLevel;
}
/** /**
* @return the deadline * @return the deadline
...@@ -45,4 +62,15 @@ public class TerminationInfo { ...@@ -45,4 +62,15 @@ public class TerminationInfo {
public long getResponseTime() { public long getResponseTime() {
return responseTime; return responseTime;
} }
/**
* @return the taskLevel
*/
public Level getTaskLevel() {
return taskLevel;
}
public enum Level {
HIGH, LOW
}
} }
...@@ -180,8 +180,8 @@ public class SystemSetup { ...@@ -180,8 +180,8 @@ public class SystemSetup {
systemSetup.tasks = generateTaskSet(); systemSetup.tasks = generateTaskSet();
} }
public void addTask(SystemSetup systemSetup) { public boolean addTask(SystemSetup systemSetup) {
systemSetup.tasks.add(generateTask()); return systemSetup.tasks.add(generateTask());
} }
public Builder setNumberOfProcessors(long numberOfProcessors) { public Builder setNumberOfProcessors(long numberOfProcessors) {
......
...@@ -5,8 +5,8 @@ import java.util.HashSet; ...@@ -5,8 +5,8 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.AbstractSimulator; import mvd.jester.simulator.AbstractSimulator;
import mvd.jester.simulator.MaiaBertogna; import mvd.jester.simulator.ParallelSynchronous;
import mvd.jester.simulator.SchmidMottok; import mvd.jester.simulator.DynamicForkJoin;
import mvd.jester.simulator.internals.TaskContextInterface; import mvd.jester.simulator.internals.TaskContextInterface;
import mvd.jester.tests.AbstractTest; import mvd.jester.tests.AbstractTest;
import mvd.jester.tests.ChwaLee; import mvd.jester.tests.ChwaLee;
...@@ -19,7 +19,7 @@ public class EarliestDeadlineFirst implements PriorityManager { ...@@ -19,7 +19,7 @@ public class EarliestDeadlineFirst implements PriorityManager {
final static Set<Class<? extends AbstractTest>> abstractTests = final static Set<Class<? extends AbstractTest>> abstractTests =
new HashSet<>(Arrays.asList(ChwaLee.class)); new HashSet<>(Arrays.asList(ChwaLee.class));
final static Set<Class<? extends AbstractSimulator>> abstractSimulators = final static Set<Class<? extends AbstractSimulator>> abstractSimulators =
new HashSet<>(Arrays.asList(MaiaBertogna.class, SchmidMottok.class)); new HashSet<>(Arrays.asList(ParallelSynchronous.class, DynamicForkJoin.class));
/** /**
* Compare the priority of two tasks according to the Rate Monotonic policy * Compare the priority of two tasks according to the Rate Monotonic policy
......
...@@ -13,16 +13,16 @@ public class RateMonotonic implements PriorityManager { ...@@ -13,16 +13,16 @@ public class RateMonotonic implements PriorityManager {
final static Set<Class<? extends AbstractTest>> abstractTests = new HashSet<>(Arrays final static Set<Class<? extends AbstractTest>> abstractTests = new HashSet<>(Arrays
.asList(mvd.jester.tests.MaiaBertogna.class, mvd.jester.tests.SchmidMottok.class)); .asList(mvd.jester.tests.MaiaBertogna.class, mvd.jester.tests.SchmidMottok.class));
final static Set<Class<? extends AbstractSimulator>> abstractSimulators = final static Set<Class<? extends AbstractSimulator>> abstractSimulators =
new HashSet<>(Arrays.asList(mvd.jester.simulator.MaiaBertogna.class, new HashSet<>(Arrays.asList(mvd.jester.simulator.ParallelSynchronous.class,
mvd.jester.simulator.SchmidMottok.class)); mvd.jester.simulator.DynamicForkJoin.class));
/** /**
* Compare the priority of two tasks according to the Rate Monotonic policy * Compare the priority of two tasks according to the Rate Monotonic policy
* *
* @param t1 The first task * @param t1 The first task
* @param t2 The second task * @param t2 The second task
* @return 0 if both tasks have the same priority, positive number if the first task has a * @return 0 if both tasks have the same priority, negative number if the first task has a
* higher priority, negative number if the second task has a higher priority * higher priority, positive number if the second task has a higher priority
*/ */
@Override @Override
public int compare(Task t1, Task t2) { public int compare(Task t1, Task t2) {
......
...@@ -25,7 +25,6 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter ...@@ -25,7 +25,6 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter
protected final SystemSetup systemSetup; protected final SystemSetup systemSetup;
protected final Set<ProcessorContext> processors; protected final Set<ProcessorContext> processors;
protected TreeMultiset<TaskContextInterface> readyTasks; protected TreeMultiset<TaskContextInterface> readyTasks;
protected long hyperPeriod;
AbstractSimulator(SystemSetup systemSetup) { AbstractSimulator(SystemSetup systemSetup) {
this.systemSetup = systemSetup; this.systemSetup = systemSetup;
...@@ -34,8 +33,6 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter ...@@ -34,8 +33,6 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter
for (int i = 0; i < systemSetup.getNumberOfProcessors(); ++i) { for (int i = 0; i < systemSetup.getNumberOfProcessors(); ++i) {
processors.add(new ProcessorContext(i)); processors.add(new ProcessorContext(i));
} }
this.hyperPeriod = getHyperPeriod();
} }
...@@ -45,7 +42,7 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter ...@@ -45,7 +42,7 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter
public SchedulingInfo runSimulation(PriorityManager priorityManager) { public SchedulingInfo runSimulation(PriorityManager priorityManager) {
SchedulingInfo schedulingInfo = new SchedulingInfo(systemSetup.getParallelTaskRatio(), SchedulingInfo schedulingInfo = new SchedulingInfo(systemSetup.getParallelTaskRatio(),
systemSetup.getUtilization()); systemSetup.getUtilization());
init(priorityManager); long hyperPeriod = init(priorityManager);
for (int t = 0; t < hyperPeriod; ++t) { for (int t = 0; t < hyperPeriod; ++t) {
if (!releaseTasks(t)) { if (!releaseTasks(t)) {
throw new RuntimeException("Could not release a task. This should not happen!"); throw new RuntimeException("Could not release a task. This should not happen!");
...@@ -66,10 +63,12 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter ...@@ -66,10 +63,12 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter
Optional<TaskContextInterface> optionalTc = p.updateExecution(t); Optional<TaskContextInterface> optionalTc = p.updateExecution(t);
if (optionalTc.isPresent()) { if (optionalTc.isPresent()) {
TaskContextInterface tc = optionalTc.get(); TaskContextInterface tc = optionalTc.get();
if (t >= tc.getDeadline()) {
TerminationInfo terminationInfo = TerminationInfo terminationInfo =
new TerminationInfo(tc.getReleaseTime(), tc.getDeadline(), t); new TerminationInfo(tc.getReleaseTime(), tc.getDeadline(), t);
schedulingInfo.addTerminationInfo(terminationInfo); schedulingInfo.addTerminationInfo(terminationInfo);
if (t >= tc.getDeadline()) {
EventPrinter.print("Time " + t + ": Task " + tc + " failed its deadline!"); EventPrinter.print("Time " + t + ": Task " + tc + " failed its deadline!");
schedulingInfo.setFailedTerminationInfo(terminationInfo); schedulingInfo.setFailedTerminationInfo(terminationInfo);
return schedulingInfo; return schedulingInfo;
...@@ -83,12 +82,12 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter ...@@ -83,12 +82,12 @@ public abstract class AbstractSimulator implements SimulatorInterface, TypeInter
return schedulingInfo; return schedulingInfo;
} }
private void init(PriorityManager priorityManager) { private long init(PriorityManager priorityManager) {
this.readyTasks = TreeMultiset.create((t1, t2) -> priorityManager.compare(t1, t2)); this.readyTasks = TreeMultiset.create((t1, t2) -> priorityManager.compare(t1, t2));
for (ProcessorContext p : processors) { for (ProcessorContext p : processors) {
p.setJob(null); p.setJob(null);
} }
this.hyperPeriod = getHyperPeriod(); return getHyperPeriod();
} }
private Set<ProcessorContext> sortProcessors(Set<ProcessorContext> processors) { private Set<ProcessorContext> sortProcessors(Set<ProcessorContext> processors) {
......
...@@ -3,14 +3,14 @@ package mvd.jester.simulator; ...@@ -3,14 +3,14 @@ package mvd.jester.simulator;
import mvd.jester.model.SystemSetup; import mvd.jester.model.SystemSetup;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.internals.schmidmottok.TaskContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskContext;
/** /**
* SchmidMottok * SchmidMottok
*/ */
public class SchmidMottok extends AbstractSimulator { public class DynamicForkJoin extends AbstractSimulator {
public SchmidMottok(SystemSetup systemSetup) { public DynamicForkJoin(SystemSetup systemSetup) {
super(systemSetup); super(systemSetup);
} }
......
...@@ -2,14 +2,14 @@ package mvd.jester.simulator; ...@@ -2,14 +2,14 @@ package mvd.jester.simulator;
import mvd.jester.model.SystemSetup; import mvd.jester.model.SystemSetup;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
/** /**
* MaiaBertogna * MaiaBertogna
*/ */
public class MaiaBertogna extends AbstractSimulator { public class ParallelSynchronous extends AbstractSimulator {
public MaiaBertogna(SystemSetup systemSetup) { public ParallelSynchronous(SystemSetup systemSetup) {
super(systemSetup); super(systemSetup);
} }
......
package mvd.jester.simulator.internals.schmidmottok; package mvd.jester.simulator.internals.dynamicforkjoin;
import java.util.Optional; import java.util.Optional;
import mvd.jester.simulator.EventPrinter; import mvd.jester.simulator.EventPrinter;
......
package mvd.jester.simulator.internals.schmidmottok; package mvd.jester.simulator.internals.dynamicforkjoin;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
......
package mvd.jester.simulator.internals.schmidmottok; package mvd.jester.simulator.internals.dynamicforkjoin;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Optional; import java.util.Optional;
......
package mvd.jester.simulator.internals.schmidmottok; package mvd.jester.simulator.internals.dynamicforkjoin;
import java.util.Optional; import java.util.Optional;
......
package mvd.jester.simulator.internals.maiabertogna; package mvd.jester.simulator.internals.parallelsynchronous;
import java.util.Optional; import java.util.Optional;
import mvd.jester.simulator.EventPrinter; import mvd.jester.simulator.EventPrinter;
......
package mvd.jester.simulator.internals.maiabertogna; package mvd.jester.simulator.internals.parallelsynchronous;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional; import java.util.Optional;
......
package mvd.jester.simulator.internals.maiabertogna; package mvd.jester.simulator.internals.parallelsynchronous;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Optional; import java.util.Optional;
...@@ -20,7 +20,6 @@ public class TaskContext implements TaskContextInterface { ...@@ -20,7 +20,6 @@ public class TaskContext implements TaskContextInterface {
private int currentSegment; private int currentSegment;
private int segmentCounter; private int segmentCounter;
public TaskContext(Task task, long releaseTime) { public TaskContext(Task task, long releaseTime) {
this.task = task; this.task = task;
this.segments = new ArrayList<>(); this.segments = new ArrayList<>();
......
...@@ -2,7 +2,6 @@ package mvd.jester.tests; ...@@ -2,7 +2,6 @@ package mvd.jester.tests;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import mvd.jester.TypeInterface; import mvd.jester.TypeInterface;
import mvd.jester.info.TerminationInfo; import mvd.jester.info.TerminationInfo;
import mvd.jester.model.SystemSetup; import mvd.jester.model.SystemSetup;
...@@ -15,12 +14,10 @@ public abstract class AbstractTest implements TestInterface, TypeInterface { ...@@ -15,12 +14,10 @@ public abstract class AbstractTest implements TestInterface, TypeInterface {
protected final Map<Task, TerminationInfo> responseTimes; protected final Map<Task, TerminationInfo> responseTimes;
protected final SystemSetup systemSetup; protected final SystemSetup systemSetup;
protected Set<Task> tasks;
public AbstractTest(SystemSetup systemSetup) { public AbstractTest(SystemSetup systemSetup) {
this.systemSetup = systemSetup; this.systemSetup = systemSetup;
this.responseTimes = new HashMap<>(); this.responseTimes = new HashMap<>();
this.tasks = systemSetup.getTasks();
} }
} }
...@@ -5,9 +5,11 @@ import java.util.ArrayList; ...@@ -5,9 +5,11 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import com.google.common.math.LongMath; import com.google.common.math.LongMath;
import mvd.jester.info.SchedulingInfo; import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo; import mvd.jester.info.TerminationInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.model.SortedTaskSet; import mvd.jester.model.SortedTaskSet;
import mvd.jester.model.SystemSetup; import mvd.jester.model.SystemSetup;
...@@ -25,19 +27,20 @@ public class ChwaLee extends AbstractTest { ...@@ -25,19 +27,20 @@ public class ChwaLee extends AbstractTest {
@Override @Override
public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) { public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) {
tasks = new SortedTaskSet(priorityManager); SortedTaskSet tasks = new SortedTaskSet(priorityManager);
tasks.addAll(systemSetup.getTasks()); tasks.addAll(systemSetup.getTasks());
responseTimes.clear(); responseTimes.clear();
for (Task t : tasks) { for (Task t : tasks) {
long responseTime = calculateResponseTime(t); Level taskLevel = tasks.headSet(t).size() <= tasks.size() / 2 ? Level.HIGH : Level.LOW;
responseTimes.put(t, new TerminationInfo(0, t.getDeadline(), responseTime)); long responseTime = calculateResponseTime(tasks, t);
responseTimes.put(t, new TerminationInfo(t.getDeadline(), responseTime, taskLevel));
} }
return new SchedulingInfo(new HashSet<>(responseTimes.values()), return new SchedulingInfo(new HashSet<>(responseTimes.values()),
systemSetup.getParallelTaskRatio(), systemSetup.getUtilization()); systemSetup.getParallelTaskRatio(), systemSetup.getUtilization());
} }
private long calculateResponseTime(Task task) { private long calculateResponseTime(Set<Task> tasks, Task task) {
long minimumWcet = getMinimumWcet(task); long minimumWcet = getMinimumWcet(task);
long deadline = task.getDeadline(); long deadline = task.getDeadline();
long numberOfProcessors = systemSetup.getNumberOfProcessors(); long numberOfProcessors = systemSetup.getNumberOfProcessors();
......
...@@ -2,9 +2,11 @@ package mvd.jester.tests; ...@@ -2,9 +2,11 @@ package mvd.jester.tests;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import com.google.common.math.LongMath; import com.google.common.math.LongMath;
import mvd.jester.info.SchedulingInfo; import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo; import mvd.jester.info.TerminationInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.model.SortedTaskSet; import mvd.jester.model.SortedTaskSet;
import mvd.jester.model.Task; import mvd.jester.model.Task;
...@@ -22,12 +24,13 @@ public class MaiaBertogna extends AbstractTest { ...@@ -22,12 +24,13 @@ public class MaiaBertogna extends AbstractTest {
@Override @Override
public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) { public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) {
tasks = new SortedTaskSet(priorityManager); SortedTaskSet tasks = new SortedTaskSet(priorityManager);
tasks.addAll(systemSetup.getTasks()); tasks.addAll(systemSetup.getTasks());
responseTimes.clear(); responseTimes.clear();
for (Task t : tasks) { for (Task t : tasks) {
long responseTime = calculateResponseTime(t); Level taskLevel = tasks.headSet(t).size() <= tasks.size() / 2 ? Level.HIGH : Level.LOW;
responseTimes.put(t, new TerminationInfo(0, t.getDeadline(), responseTime)); long responseTime = calculateResponseTime(tasks, t);
responseTimes.put(t, new TerminationInfo(t.getDeadline(), responseTime, taskLevel));
} }
return new SchedulingInfo(new HashSet<>(responseTimes.values()), return new SchedulingInfo(new HashSet<>(responseTimes.values()),
...@@ -39,7 +42,7 @@ public class MaiaBertogna extends AbstractTest { ...@@ -39,7 +42,7 @@ public class MaiaBertogna extends AbstractTest {
return "MaiaBertogna"; return "MaiaBertogna";
} }
private long calculateResponseTime(Task task) { private long calculateResponseTime(Set<Task> tasks, Task task) {
long minimumWcet = getMinimumWcet(task); long minimumWcet = getMinimumWcet(task);
long responseTime = minimumWcet; long responseTime = minimumWcet;
long previousResponseTime = 0; long previousResponseTime = 0;
......
...@@ -2,9 +2,11 @@ package mvd.jester.tests; ...@@ -2,9 +2,11 @@ package mvd.jester.tests;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import com.google.common.math.LongMath; import com.google.common.math.LongMath;
import mvd.jester.info.SchedulingInfo; import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo; import mvd.jester.info.TerminationInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.model.SortedTaskSet; import mvd.jester.model.SortedTaskSet;
import mvd.jester.model.Task; import mvd.jester.model.Task;
...@@ -22,12 +24,13 @@ public class SchmidMottok extends AbstractTest { ...@@ -22,12 +24,13 @@ public class SchmidMottok extends AbstractTest {
@Override @Override
public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) { public SchedulingInfo runSchedulabilityCheck(PriorityManager priorityManager) {
tasks = new SortedTaskSet(priorityManager); SortedTaskSet tasks = new SortedTaskSet(priorityManager);
tasks.addAll(systemSetup.getTasks()); tasks.addAll(systemSetup.getTasks());
responseTimes.clear(); responseTimes.clear();
for (Task t : tasks) { for (Task t : tasks) {
long responseTime = calculateResponseTime(t); Level taskLevel = tasks.headSet(t).size() <= tasks.size() / 2 ? Level.HIGH : Level.LOW;
responseTimes.put(t, new TerminationInfo(0, t.getDeadline(), responseTime)); long responseTime = calculateResponseTime(tasks, t);
responseTimes.put(t, new TerminationInfo(t.getDeadline(), responseTime, taskLevel));
} }
return new SchedulingInfo(new HashSet<>(responseTimes.values()), return new SchedulingInfo(new HashSet<>(responseTimes.values()),
...@@ -39,7 +42,7 @@ public class SchmidMottok extends AbstractTest { ...@@ -39,7 +42,7 @@ public class SchmidMottok extends AbstractTest {
return "SchmidMottok"; return "SchmidMottok";
} }
private long calculateResponseTime(Task task) { private long calculateResponseTime(Set<Task> tasks, Task task) {
long minimumWcet = getMinimumWcet(task); long minimumWcet = getMinimumWcet(task);
long responseTime = minimumWcet; long responseTime = minimumWcet;
long previousResponseTime = 0; long previousResponseTime = 0;
......
...@@ -31,10 +31,10 @@ public class Logger { ...@@ -31,10 +31,10 @@ public class Logger {
public void finalize() { public void finalize() {
try { try {
if (printWriter != null) { if (printWriter != null) {
printWriter.close(); // Will close bw and fw too printWriter.close();
} }
if (bufferedWriter != null) { if (bufferedWriter != null) {
bufferedWriter.close(); // Will close fw too bufferedWriter.close();
} }
if (fileWriter != null) { if (fileWriter != null) {
fileWriter.close(); fileWriter.close();
......
package mvd.jester;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import mvd.jester.info.SchedulingInfo;
import mvd.jester.priority.PriorityManager;
/**
* TestResultCollector
*/
public class TestResultCollector {
@Test
@DisplayName("Test if getters and setters work correctly.")
void testGettersAndSetters() {
final PriorityManager pm = mock(PriorityManager.class);
final String pmName = "PriorityManagerName";
when(pm.getName()).thenReturn(pmName);
final TypeInterface ti = mock(TypeInterface.class);
final String tiName = "TypeInterfaceName";
when(ti.getName()).thenReturn(tiName);
final ResultCollector<TypeInterface> rc1 = new ResultCollector<>(pm, ti);
final ResultCollector<TypeInterface> rc2 = new ResultCollector<>(pm, ti);
assertTrue(rc1.getPriorityManager() == pm);
assertTrue(rc1.getAbstractValue() == ti);
assertTrue(rc1.getName().equals(tiName + "_" + pmName));
assertTrue(rc1.compareTo(rc2) == 0);
}
@Test
@DisplayName("Check if adding SchedulingInfos works correctly.")
void testSchedulingInfos() {
final TypeInterface ti = mock(TypeInterface.class);
final PriorityManager pm = mock(PriorityManager.class);
final ResultCollector<TypeInterface> rc = new ResultCollector<>(pm, ti);
final Set<SchedulingInfo> schedulingInfos = new HashSet<>();
long numberOfSchedulingInfos = ThreadLocalRandom.current().nextLong(5, 10);
for (int i = 0; i < numberOfSchedulingInfos; ++i) {
SchedulingInfo si = mock(SchedulingInfo.class);
schedulingInfos.add(si);
assertTrue(rc.addResult(si));
assertFalse(rc.addResult(si));
}
assertTrue(rc.getResults().size() == numberOfSchedulingInfos);
assertTrue(rc.getResults().equals(schedulingInfos));
assertTrue(rc.addResult(null));
}
}
package mvd.jester.info;
import static org.junit.Assert.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import mvd.jester.info.TerminationInfo.Level;
/**
* TestTerminationInfo
*/
public class TestSchedulingInfo {
@Test
@DisplayName("Check if SchedulingInfos parameters are calculated correctly.")
public void testSchedulingInfo() {
for (int run = 0; run < 1000; ++run) {
Set<TerminationInfo> terminationInfos = new HashSet<>();
int numberOfTerminationInfos = ThreadLocalRandom.current().nextInt(5, 10);
boolean feasibile = true;
boolean levelFailed = false;
for (int i = 0; i < numberOfTerminationInfos; ++i) {
long deadline = ThreadLocalRandom.current().nextLong(50, 100);
long responseTime = ThreadLocalRandom.current().nextLong(50, 100);
Level taskLevel =
ThreadLocalRandom.current().nextLong(0, 100) < 50 ? Level.LOW : Level.HIGH;
terminationInfos.add(new TerminationInfo(deadline, responseTime, taskLevel));
if (deadline < responseTime) {
feasibile = false;
if (taskLevel == Level.HIGH) {
levelFailed = true;
}
}
}
SchedulingInfo schedulingInfo =
new SchedulingInfo(terminationInfos, 2, numberOfTerminationInfos);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == levelFailed);
assertTrue(schedulingInfo.checkTasksetFeasible() == feasibile);
}
}
@Test
@DisplayName("Check Getters and Setters.")
public void testGettersAndSetters() {
double taskRatio = 0.23;
double utilization = 0.49;
SchedulingInfo si = new SchedulingInfo(taskRatio, utilization);
Set<TerminationInfo> terminationInfos = new HashSet<>();
long numberOfTerminationInfos = ThreadLocalRandom.current().nextLong(5, 10);
for (int i = 0; i < numberOfTerminationInfos; ++i) {
TerminationInfo ti = mock(TerminationInfo.class);
terminationInfos.add(ti);
assertTrue(si.addTerminationInfo(ti));
assertFalse(si.addTerminationInfo(ti));
}
assertTrue(si.getParallelTaskRatio() == taskRatio);
assertTrue(si.getUtilization() == utilization);
assertTrue(si.getTerminationInfos().size() == numberOfTerminationInfos);
assertTrue(si.getTerminationInfos().equals(terminationInfos));
assertTrue(si.addTerminationInfo(null));
assertFalse(si.getFailedTerminationInfo().isPresent());
TerminationInfo ti = mock(TerminationInfo.class);
si.setFailedTerminationInfo(ti);
assertTrue(si.getFailedTerminationInfo().isPresent());
assertTrue(si.getFailedTerminationInfo().get().equals(ti));
}
}
package mvd.jester.info;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import mvd.jester.info.TerminationInfo.Level;
/**
* TestTerminationInfo
*/
public class TestTerminationInfo {
@Test
@DisplayName("Check Getters and Setters")
public void testGettersAndSetters() {
long releaseTime = 20;
long deadline = 40;
long responseTime = 30;
Level taskLevel = Level.LOW;
TerminationInfo ti = new TerminationInfo(releaseTime, deadline, responseTime);
TerminationInfo til = new TerminationInfo(deadline, responseTime, taskLevel);
TerminationInfo tirl = new TerminationInfo(releaseTime, deadline, responseTime, taskLevel);
assertTrue(ti.getDeadline() == deadline);
assertTrue(til.getDeadline() == deadline);
assertTrue(tirl.getDeadline() == deadline);
assertTrue(ti.getResponseTime() == responseTime);
assertTrue(til.getResponseTime() == responseTime);
assertTrue(tirl.getResponseTime() == responseTime);
assertTrue(ti.getReleaseTime() == releaseTime);
assertTrue(tirl.getReleaseTime() == releaseTime);
assertTrue(til.getTaskLevel() == taskLevel);
assertTrue(tirl.getTaskLevel() == taskLevel);
assertTrue(ti.getLateness() == responseTime - deadline);
assertTrue(til.getLateness() == responseTime - deadline);
assertTrue(tirl.getLateness() == responseTime - deadline);
}
}
package mvd.jester.model; package mvd.jester.model;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import org.junit.Rule;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.rules.TemporaryFolder;
public class TestSystemSetup { public class TestSystemSetup {
@Rule
public TemporaryFolder tf = new TemporaryFolder();
private static final int NUMBER_OF_SETS = 1000; private static final int NUMBER_OF_RUNS = 1000;
@Test @Test
@DisplayName("Check if randomly generated task parameters lie within the specified ranges.") @DisplayName("Check if randomly generated task parameters lie within the specified ranges.")
public void testRandomTaskSetGeneration() { public void testRandomTaskSetGeneration() {
for (int i = 0; i < NUMBER_OF_SETS; ++i) { for (int i = 0; i < NUMBER_OF_RUNS; ++i) {
SystemSetup taskSet = new SystemSetup.Builder().setNumberOfSegments(1, 7) long numberOfProcessors = ThreadLocalRandom.current().nextLong(2, 8);
.setNumberOfJobs(2, 10).setPeriods(100, 1000, 1000).build(); SystemSetup.Builder systemSetupBuilder = new SystemSetup.Builder()//
.setNumberOfSegments(1, 7)//
.setNumberOfJobs(2, 10)//
.setPeriods(100, 1000, 1000)//
.setNumberOfProcessors(numberOfProcessors);
for (Task t : taskSet.getTasks()) { SystemSetup systemSetup = systemSetupBuilder.build();
assertTrue(systemSetup.getNumberOfProcessors() == numberOfProcessors);
for (Task t : systemSetup.getTasks()) {
assertTrue(t.getPeriod() >= 100); assertTrue(t.getPeriod() >= 100);
assertTrue(t.getPeriod() <= 1000); assertTrue(t.getPeriod() <= 1000);
assertTrue(t.getDeadline() == t.getPeriod()); assertTrue(t.getDeadline() == t.getPeriod());
...@@ -35,8 +52,39 @@ public class TestSystemSetup { ...@@ -35,8 +52,39 @@ public class TestSystemSetup {
assertTrue(t.getSegments().size() >= 1); assertTrue(t.getSegments().size() >= 1);
assertTrue(t.getSegments().size() <= 7); assertTrue(t.getSegments().size() <= 7);
}
assertTrue(systemSetupBuilder.addTask(systemSetup));
Set<Task> tasks = systemSetup.getTasks();
systemSetupBuilder.rebuild(systemSetup);
assertFalse(tasks == systemSetup.getTasks());
} }
} }
@Test
@DisplayName("Check Getters and Setters.")
void testGettersAndSetters() {
@SuppressWarnings("unchecked")
Set<Task> t1 = mock(Set.class);
@SuppressWarnings("unchecked")
Set<Task> t2 = mock(Set.class);
SystemSetup systemSetup = new SystemSetup(t1, 2);
systemSetup.setTasks(t2);
assertTrue(systemSetup.getTasks() == t2);
} }
// @Test
// @DisplayName("Check if parser works correclty.")
// void testParser() throws IOException {
// SystemSetup systemSetup = new SystemSetup.Builder().setNumberOfSegments(1, 7)
// .setNumberOfJobs(2, 10).setPeriods(100, 1000, 1000).build();
// systemSetup.writeToFile(null);
// systemSetup.writeToFile(tf.getRoot().getAbsolutePath());
// }
} }
package mvd.jester.priority; package mvd.jester.priority;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.HashSet; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.model.SystemSetup;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.DynamicForkJoin;
import mvd.jester.simulator.ParallelSynchronous;
import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
import mvd.jester.tests.ChwaLee;
import mvd.jester.tests.MaiaBertogna;
import mvd.jester.tests.SchmidMottok;
/** /**
* TestEarliestDeadlineFirst * TestEarliestDeadlineFirst
...@@ -16,13 +24,43 @@ public class TestEarliestDeadlineFirst { ...@@ -16,13 +24,43 @@ public class TestEarliestDeadlineFirst {
@DisplayName("Test if priority manager returns the correct priority.") @DisplayName("Test if priority manager returns the correct priority.")
public void testPriority() { public void testPriority() {
EarliestDeadlineFirst edf = new EarliestDeadlineFirst(); EarliestDeadlineFirst edf = new EarliestDeadlineFirst();
Task t1 = new Task(100, new HashSet<>());
Task t2 = new Task(200, new HashSet<>());
TaskContext tc1 = new TaskContext(t1, 0); Task t1 = mock(Task.class);
TaskContext tc2 = new TaskContext(t2, 0); Task t2 = mock(Task.class);
when(t1.getDeadline()).thenReturn((long) 100);
when(t2.getDeadline()).thenReturn((long) 200);
TaskContext tc1 = mock(TaskContext.class);
TaskContext tc2 = mock(TaskContext.class);
when(tc1.getDeadline()).thenReturn((long) 100);
when(tc2.getDeadline()).thenReturn((long) 200);
assertTrue(edf.compare(t1, t2) < 0); assertTrue(edf.compare(t1, t2) < 0);
assertTrue(edf.compare(tc1, tc2) < 0); assertTrue(edf.compare(tc1, tc2) < 0);
assertTrue(edf.compare(t1, t1) == 0);
assertTrue(edf.compare(tc1, tc1) == 0);
assertTrue(edf.compare(t2, t1) > 0);
assertTrue(edf.compare(tc2, tc1) > 0);
} }
@Test
@DisplayName("Check Getters, Tests and Simulators.")
void testGettersTestsAndSimulators() {
EarliestDeadlineFirst edf = new EarliestDeadlineFirst();
assertTrue(edf.hasTest(ChwaLee.class));
assertFalse(edf.hasTest(MaiaBertogna.class));
assertFalse(edf.hasTest(SchmidMottok.class));
assertTrue(edf.hasSimulator(ParallelSynchronous.class));
assertTrue(edf.hasSimulator(DynamicForkJoin.class));
assertTrue(edf.hasTest(new ChwaLee(mock(SystemSetup.class))));
assertFalse(edf.hasTest(new SchmidMottok(mock(SystemSetup.class))));
assertFalse(edf.hasTest(new MaiaBertogna(mock(SystemSetup.class))));
assertTrue(edf.hasSimulator(new ParallelSynchronous(mock(SystemSetup.class))));
assertTrue(edf.hasSimulator(new DynamicForkJoin(mock(SystemSetup.class))));
assertTrue(edf.getName().equals("EDF"));
}
} }
package mvd.jester.priority; package mvd.jester.priority;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashSet; import java.util.HashSet;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.model.SystemSetup;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.DynamicForkJoin;
import mvd.jester.simulator.ParallelSynchronous;
import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
import mvd.jester.tests.ChwaLee;
import mvd.jester.tests.MaiaBertogna;
import mvd.jester.tests.SchmidMottok;
/** /**
* TestRateMonotonic * TestRateMonotonic
...@@ -17,13 +26,41 @@ public class TestRateMonotonic { ...@@ -17,13 +26,41 @@ public class TestRateMonotonic {
@DisplayName("Test if priority manager returns the correct priority.") @DisplayName("Test if priority manager returns the correct priority.")
public void testPriority() { public void testPriority() {
RateMonotonic rm = new RateMonotonic(); RateMonotonic rm = new RateMonotonic();
Task t1 = new Task(100, new HashSet<Segment>()); Task t1 = mock(Task.class);
Task t2 = new Task(200, new HashSet<Segment>()); Task t2 = mock(Task.class);
when(t1.getPeriod()).thenReturn((long) 100);
when(t2.getPeriod()).thenReturn((long) 200);
TaskContext tc1 = new TaskContext(t1, 0); TaskContext tc1 = mock(TaskContext.class);
TaskContext tc2 = new TaskContext(t2, 0); TaskContext tc2 = mock(TaskContext.class);
when(tc1.getTask()).thenReturn(t1);
when(tc2.getTask()).thenReturn(t2);
assertTrue(rm.compare(t1, t2) < 0); assertTrue(rm.compare(t1, t2) < 0);
assertTrue(rm.compare(tc1, tc2) < 0); assertTrue(rm.compare(tc1, tc2) < 0);
assertTrue(rm.compare(t1, t1) == 0);
assertTrue(rm.compare(tc1, tc1) == 0);
assertTrue(rm.compare(t2, t1) > 0);
assertTrue(rm.compare(tc2, tc1) > 0);
}
@Test
@DisplayName("Check Tests and Simulators.")
void testTestsAndSimulators() {
RateMonotonic rm = new RateMonotonic();
assertFalse(rm.hasTest(ChwaLee.class));
assertTrue(rm.hasTest(MaiaBertogna.class));
assertTrue(rm.hasTest(SchmidMottok.class));
assertTrue(rm.hasSimulator(ParallelSynchronous.class));
assertTrue(rm.hasSimulator(DynamicForkJoin.class));
assertFalse(rm.hasTest(new ChwaLee(mock(SystemSetup.class))));
assertTrue(rm.hasTest(new SchmidMottok(mock(SystemSetup.class))));
assertTrue(rm.hasTest(new MaiaBertogna(mock(SystemSetup.class))));
assertTrue(rm.hasSimulator(new ParallelSynchronous(mock(SystemSetup.class))));
assertTrue(rm.hasSimulator(new DynamicForkJoin(mock(SystemSetup.class))));
assertTrue(rm.getName().equals("RM"));
} }
} }
...@@ -17,8 +17,8 @@ import org.junit.jupiter.api.Test; ...@@ -17,8 +17,8 @@ import org.junit.jupiter.api.Test;
import mvd.jester.model.Task; import mvd.jester.model.Task;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.TaskContextInterface; import mvd.jester.simulator.internals.TaskContextInterface;
import mvd.jester.simulator.internals.maiabertogna.JobContext; import mvd.jester.simulator.internals.parallelsynchronous.JobContext;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
/** /**
* TestProcessorContext * TestProcessorContext
......
package mvd.jester.simulator.schmidmottok; package mvd.jester.simulator.dynamicforkjoin;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
...@@ -14,10 +14,10 @@ import org.junit.jupiter.api.DisplayName; ...@@ -14,10 +14,10 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.schmidmottok.JobContext; import mvd.jester.simulator.internals.dynamicforkjoin.JobContext;
import mvd.jester.simulator.internals.schmidmottok.SegmentContext; import mvd.jester.simulator.internals.dynamicforkjoin.SegmentContext;
import mvd.jester.simulator.internals.schmidmottok.TaskContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskContext;
import mvd.jester.simulator.internals.schmidmottok.TaskletContext;; import mvd.jester.simulator.internals.dynamicforkjoin.TaskletContext;;
/** /**
* TestJobContext * TestJobContext
......
package mvd.jester.simulator.schmidmottok; package mvd.jester.simulator.dynamicforkjoin;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
...@@ -11,10 +11,10 @@ import org.junit.jupiter.api.Test; ...@@ -11,10 +11,10 @@ import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.simulator.internals.JobContextInterface; import mvd.jester.simulator.internals.JobContextInterface;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.schmidmottok.JobContext; import mvd.jester.simulator.internals.dynamicforkjoin.JobContext;
import mvd.jester.simulator.internals.schmidmottok.SegmentContext; import mvd.jester.simulator.internals.dynamicforkjoin.SegmentContext;
import mvd.jester.simulator.internals.schmidmottok.TaskContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskContext;
import mvd.jester.simulator.internals.schmidmottok.TaskletContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskletContext;
/** /**
* TestSegmentContext * TestSegmentContext
......
package mvd.jester.simulator.schmidmottok; package mvd.jester.simulator.dynamicforkjoin;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
...@@ -14,7 +14,7 @@ import mvd.jester.model.Task; ...@@ -14,7 +14,7 @@ import mvd.jester.model.Task;
import mvd.jester.simulator.internals.JobContextInterface; import mvd.jester.simulator.internals.JobContextInterface;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.TaskContextInterface; import mvd.jester.simulator.internals.TaskContextInterface;
import mvd.jester.simulator.internals.schmidmottok.TaskContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskContext;
/** /**
* TestTaskContext * TestTaskContext
......
package mvd.jester.simulator.schmidmottok; package mvd.jester.simulator.dynamicforkjoin;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
...@@ -11,10 +11,10 @@ import java.util.concurrent.ThreadLocalRandom; ...@@ -11,10 +11,10 @@ import java.util.concurrent.ThreadLocalRandom;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.simulator.internals.schmidmottok.JobContext; import mvd.jester.simulator.internals.dynamicforkjoin.JobContext;
import mvd.jester.simulator.internals.schmidmottok.SegmentContext; import mvd.jester.simulator.internals.dynamicforkjoin.SegmentContext;
import mvd.jester.simulator.internals.schmidmottok.TaskContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskContext;
import mvd.jester.simulator.internals.schmidmottok.TaskletContext; import mvd.jester.simulator.internals.dynamicforkjoin.TaskletContext;
/** /**
* TestTaskletContext * TestTaskletContext
......
package mvd.jester.simulator.maiabertogna; package mvd.jester.simulator.parallelsynchronous;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
...@@ -15,9 +15,9 @@ import org.junit.jupiter.api.Test; ...@@ -15,9 +15,9 @@ import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.TaskContextInterface; import mvd.jester.simulator.internals.TaskContextInterface;
import mvd.jester.simulator.internals.maiabertogna.JobContext; import mvd.jester.simulator.internals.parallelsynchronous.JobContext;
import mvd.jester.simulator.internals.maiabertogna.SegmentContext; import mvd.jester.simulator.internals.parallelsynchronous.SegmentContext;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
/** /**
* TestJobContext * TestJobContext
......
package mvd.jester.simulator.maiabertogna; package mvd.jester.simulator.parallelsynchronous;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
...@@ -10,8 +10,8 @@ import org.junit.jupiter.api.DisplayName; ...@@ -10,8 +10,8 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.model.Segment; import mvd.jester.model.Segment;
import mvd.jester.simulator.internals.JobContextInterface; import mvd.jester.simulator.internals.JobContextInterface;
import mvd.jester.simulator.internals.maiabertogna.SegmentContext; import mvd.jester.simulator.internals.parallelsynchronous.SegmentContext;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
/** /**
......
package mvd.jester.simulator.maiabertogna; package mvd.jester.simulator.parallelsynchronous;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
...@@ -14,7 +14,7 @@ import mvd.jester.model.Task; ...@@ -14,7 +14,7 @@ import mvd.jester.model.Task;
import mvd.jester.simulator.internals.JobContextInterface; import mvd.jester.simulator.internals.JobContextInterface;
import mvd.jester.simulator.internals.ProcessorContext; import mvd.jester.simulator.internals.ProcessorContext;
import mvd.jester.simulator.internals.TaskContextInterface; import mvd.jester.simulator.internals.TaskContextInterface;
import mvd.jester.simulator.internals.maiabertogna.TaskContext; import mvd.jester.simulator.internals.parallelsynchronous.TaskContext;
/** /**
* TestTaskContext * TestTaskContext
......
package mvd.jester.tests; package mvd.jester.tests;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.model.SystemSetup; import mvd.jester.model.SystemSetup;
import mvd.jester.priority.RateMonotonic;
/** /**
* TestMaiaBertogna * TestMaiaBertogna
...@@ -12,7 +16,41 @@ public class TestMaiaBertogna { ...@@ -12,7 +16,41 @@ public class TestMaiaBertogna {
@Test @Test
@DisplayName("Check if the schedulability check returns the correct values.") @DisplayName("Check if the schedulability check returns the correct values.")
public void testRunSchedulabilityCheck() { public void testRunSchedulabilityCheck() {
SystemSetup systemSetup = SystemSetup.readFromFile("/resources/Taskset1.txt", 0); {
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 4);
MaiaBertogna mb = new MaiaBertogna(systemSetup); MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == true);
assertTrue(schedulingInfo.checkTasksetFeasible() == false);
assertTrue(schedulingInfo.getFailedTerminationInfo().isPresent());
}
{
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 8);
MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == true);
assertTrue(schedulingInfo.checkTasksetFeasible() == false);
assertTrue(schedulingInfo.getFailedTerminationInfo().isPresent());
}
{
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 16);
MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == false);
assertTrue(schedulingInfo.checkTasksetFeasible() == true);
assertTrue(!schedulingInfo.getFailedTerminationInfo().isPresent());
}
} }
} }
package mvd.jester.tests;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import mvd.jester.info.SchedulingInfo;
import mvd.jester.info.TerminationInfo.Level;
import mvd.jester.model.SystemSetup;
import mvd.jester.priority.RateMonotonic;
/**
* TestSchmidMottok
*/
public class TestSchmidMottok {
@Test
@DisplayName("Check if the schedulability check returns the correct values.")
public void testRunSchedulabilityCheck() {
{
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 4);
MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == true);
assertTrue(schedulingInfo.checkTasksetFeasible() == false);
assertTrue(schedulingInfo.getFailedTerminationInfo().isPresent());
}
{
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 8);
MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == true);
assertTrue(schedulingInfo.checkTasksetFeasible() == false);
assertTrue(schedulingInfo.getFailedTerminationInfo().isPresent());
}
{
SystemSetup systemSetup =
SystemSetup.readFromFile("src/test/resources/Taskset1.txt", 16);
MaiaBertogna mb = new MaiaBertogna(systemSetup);
SchedulingInfo schedulingInfo = mb.runSchedulabilityCheck(new RateMonotonic());
assertTrue(schedulingInfo.getTerminationInfos().size() == 4);
assertTrue(schedulingInfo.checkLevelFail(Level.HIGH) == false);
assertTrue(schedulingInfo.checkTasksetFeasible() == true);
assertTrue(!schedulingInfo.getFailedTerminationInfo().isPresent());
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment