diff --git a/src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java b/src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java index cffa370..065855e 100644 --- a/src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java +++ b/src/main/java/net/berack/upo/valpre/NetBuilderInteractive.java @@ -1,20 +1,47 @@ package net.berack.upo.valpre; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Scanner; import java.util.function.Function; import net.berack.upo.valpre.rand.Distribution; import net.berack.upo.valpre.sim.Net; import net.berack.upo.valpre.sim.ServerNode; +/** + * Interactive net builder. This class allows the user to build a net by adding + * nodes and connections. The user can also save the net to a file. + */ public class NetBuilderInteractive { private final Net net = new Net(); + private final PrintStream out; + private final Scanner scanner; + + /** + * Create a new interactive net builder. Uses System.in and System.out. + */ + public NetBuilderInteractive() { + this(System.out, System.in); + } + + /** + * Create a new interactive net builder. + * + * @param out the output stream + * @param in the input stream + */ + public NetBuilderInteractive(PrintStream out, InputStream in) { + this.out = out; + this.scanner = new Scanner(in); + } /** * Run the interactive net builder. * * @param args the arguments */ - public void run() { + public Net run() { while (true) { try { var choice = choose("Choose the next step to do:", @@ -32,9 +59,12 @@ public class NetBuilderInteractive { var targetNode = this.net.getNode(target); this.net.addConnection(sourceNode, targetNode, weight); } - case 3 -> this.printNodes(); + case 3 -> this.out.println(this.net); case 4 -> this.net.save(ask("Enter the filename: ")); - case 5 -> System.exit(0); + default -> { + this.scanner.close(); + return this.net; + } } } catch (Exception e) { e.printStackTrace(); @@ -42,34 +72,13 @@ public class NetBuilderInteractive { } } - /** - * Print the nodes in the net. - */ - private void printNodes() { - var builder = new StringBuilder(); - builder.append("Nodes:\n"); - for (var i = 0; i < this.net.size(); i++) { - var name = this.net.getNode(i).name; - builder.append(name).append(" -> "); - - for (var connection : this.net.getChildren(i)) { - var child = this.net.getNode(connection.index); - builder.append(child.name).append("(").append(connection.weight).append("), "); - } - - builder.delete(builder.length() - 2, builder.length()); - builder.append("\n"); - } - System.out.print(builder.toString()); - } - /** * Build a node. * * @return the node */ private ServerNode buildNode() { - var choice = choose("Choose the type of node to create:", "Source", "Queue"); + var choice = choose("Choose the type of node to create:", "Source", "Queue", "Queue with unavailable time"); var name = ask("Node name: "); var distribution = askDistribution("Service distribution"); @@ -81,6 +90,10 @@ public class NetBuilderInteractive { yield ServerNode.Builder.sourceLimited(name, limit, distribution); } case 2 -> { + var servers = ask("Number of servers: ", Integer::parseInt, 1); + yield ServerNode.Builder.queue(name, servers, distribution, null); + } + case 3 -> { var servers = ask("Number of servers: ", Integer::parseInt, 1); var unavailable = askDistribution("Unavailable distribution"); yield ServerNode.Builder.queue(name, servers, distribution, unavailable); @@ -94,7 +107,7 @@ public class NetBuilderInteractive { * * @return the distribution */ - public static Distribution askDistribution(String ask) { + private Distribution askDistribution(String ask) { var choice = choose(ask + ":", "Exponential", "Uniform", "Erlang", "UnavailableTime", "Normal", "NormalBoxMuller", "None"); @@ -138,7 +151,7 @@ public class NetBuilderInteractive { * @param ask the question to ask * @return the answer */ - private static String ask(String ask) { + private String ask(String ask) { return ask(ask, Function.identity(), ""); } @@ -150,13 +163,13 @@ public class NetBuilderInteractive { * @param defaultValue the default value * @return the answer */ - private static T ask(String ask, Function parser, T defaultValue) { - System.out.print(ask); + private T ask(String ask, Function parser, T defaultValue) { + this.out.print(ask); try { - var line = System.console().readLine(); + var line = this.scanner.nextLine(); return parser.apply(line); } catch (Exception e) { - System.out.println("Invalid input: " + e.getMessage()); + this.out.println("Invalid input: " + e.getMessage()); return defaultValue; } } @@ -168,7 +181,7 @@ public class NetBuilderInteractive { * @param options the options to choose from * @return the choice */ - private static int choose(String ask, String... options) { + private int choose(String ask, String... options) { var builder = new StringBuilder(); builder.append(ask).append("\n"); for (int i = 0; i < options.length; i++) { diff --git a/src/test/java/net/berack/upo/valpre/sim/TestInteractions.java b/src/test/java/net/berack/upo/valpre/sim/TestInteractions.java index 7c5a67e..7f776a8 100644 --- a/src/test/java/net/berack/upo/valpre/sim/TestInteractions.java +++ b/src/test/java/net/berack/upo/valpre/sim/TestInteractions.java @@ -2,8 +2,14 @@ package net.berack.upo.valpre.sim; import static org.junit.Assert.assertEquals; +import java.io.ByteArrayInputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + import org.junit.Test; +import net.berack.upo.valpre.NetBuilderInteractive; import net.berack.upo.valpre.rand.Distribution; public class TestInteractions { @@ -90,4 +96,66 @@ public class TestInteractions { + other + " -\n", net.toString()); } + + @Test(timeout = 1000) + public void netBuilderInteractive() { + var out = new PrintStream(OutputStream.nullOutputStream()); + + var inputs = List.of("5"); + var bytes = String.join("\n", inputs).getBytes(); + var in = new ByteArrayInputStream(bytes); + var net = new NetBuilderInteractive(out, in).run(); + assertEquals("", net.toString()); + + inputs = List.of("1", "1", "Source", "1", "1.0", "10000", "5"); + bytes = String.join("\n", inputs).getBytes(); + in = new ByteArrayInputStream("1\n1\nSource\n1\n1.0\n1000\n5\n".getBytes()); + net = new NetBuilderInteractive(out, in).run(); + assertEquals("Source[servers:1, queue:100, spawn:1000, Exponential(1.0)] -\n", net.toString()); + + inputs = List.of("1", "1", "Source", "1", "2.0", "500", + "1", "2", "Queue", "5", "3.2", "0.6", "1", + "5"); + bytes = String.join("\n", inputs).getBytes(); + in = new ByteArrayInputStream(bytes); + net = new NetBuilderInteractive(out, in).run(); + assertEquals("Source[servers:1, queue:100, spawn:500, Exponential(2.0)] -\n" + + "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString()); + + inputs = List.of("1", "1", "Source", "1", "2.0", "500", + "1", "2", "Queue", "5", "3.2", "0.6", "1", + "2", "Source", "Queue", "1.0", + "5"); + bytes = String.join("\n", inputs).getBytes(); + in = new ByteArrayInputStream(bytes); + net = new NetBuilderInteractive(out, in).run(); + assertEquals("Source[servers:1, queue:100, spawn:500, Exponential(2.0)] -> Queue(1.0)\n" + + "Queue[servers:1, queue:100, spawn:0, Normal(3.2, 0.6)] -\n", net.toString()); + } + + /* + * An interaction example is like this: + * 1. Add a node + * - Choose the type of node to create: + * - 1. Source + * - 2. Queue + * - 3. Queue with unavailable time + * - Node name: Name + * - Arrivals limit (0 for Int.Max) / Number of servers: 1 + * - Choose the type of service distribution: + * - - 1. Exponential + * - - 2. Uniform + * - - 3. Erlang + * - - 4. UnavailableTime + * - - 5. Normal + * - - 6. NormalBoxMuller + * - - 7. None + * 2. Add a connection + * - Enter the source node: Source + * - Enter the target node: Queue + * - Enter the weight: 1.0 + * 3. Print Nodes + * 4. Save the net + * 5. Exit + */ }