package blackout; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance.Lifecycle; import unsw.blackout.BlackoutController; import unsw.blackout.FileTransferException; import unsw.utils.Angle; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static unsw.utils.MathsHelper.RADIUS_OF_JUPITER; @TestInstance(value = Lifecycle.PER_CLASS) public class Task2Tests { @Test public void testDownloadSpeed() { BlackoutController controller = new BlackoutController(); // The expected download speed should be (download speed / num transferring files), with the remainder ignored. controller.createSatellite("Satellite1", "ShrinkingSatellite", 1000 + RADIUS_OF_JUPITER, Angle.fromDegrees(320)); controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(310)); controller.createDevice("DeviceB", "LaptopDevice", Angle.fromDegrees(310)); String contents = "a relatively long string"; controller.addFileToDevice("DeviceA", "file1", contents); controller.addFileToDevice("DeviceB", "file2", contents); assertDoesNotThrow(() -> controller.sendFile("file1", "DeviceA", "Satellite1")); assertDoesNotThrow(() -> controller.sendFile("file2", "DeviceB", "Satellite1")); // shrinking satellite is 15 bytes per tick, so should be 15/2 = 7. controller.simulate(); assertEquals(7, controller.getInfo("Satellite1").getFiles().get("file1").getData().length()); assertEquals(7, controller.getInfo("Satellite1").getFiles().get("file2").getData().length()); // again, should be 14 controller.simulate(); assertEquals(14, controller.getInfo("Satellite1").getFiles().get("file1").getData().length()); assertEquals(14, controller.getInfo("Satellite1").getFiles().get("file2").getData().length()); } @Test public void testDeleteFiles() { BlackoutController controller = new BlackoutController(); // Files that are being transferred should be deleted if they are in transmission, and kept if they are not. controller.createSatellite("Satellite1", "StandardSatellite", 1000 + RADIUS_OF_JUPITER, Angle.fromDegrees(310)); controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(310)); // Upload a file to the satellite, should be there after 1 tick. controller.addFileToDevice("DeviceA", "file1", "1"); assertDoesNotThrow(() -> controller.sendFile("file1", "DeviceA", "Satellite1")); controller.simulate(); assert(controller.getInfo("Satellite1").getFiles().size() == 1); // Upload another file, which will not transmit in time. controller.addFileToDevice("DeviceA", "file2", "this string will take a fair amount of ticks to transmit"); assertDoesNotThrow(() -> controller.sendFile("file2", "DeviceA", "Satellite1")); controller.simulate(); assert(controller.getInfo("Satellite1").getFiles().size() == 2); for (int i = 0; i < 50; ++i) { controller.simulate(); } // At this point we should be occluded and out of range, // while the file was in transmission - zero files should be present. assert(controller.getInfo("Satellite1").getFiles().size() == 1); } @Test public void testTransientDeleteFile() { BlackoutController controller = new BlackoutController(); // Files should NOT be deleted after occlusion or running out of distance. controller.createSatellite("Satellite1", "ElephantSatellite", 1000 + RADIUS_OF_JUPITER, Angle.fromDegrees(330)); controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(310)); // Upload a file which will not transmit in time. controller.addFileToDevice("DeviceA", "file1", "this string will take a fair amount of ticks to transmit but just to be sure"); assertDoesNotThrow(() -> controller.sendFile("file1", "DeviceA", "Satellite1")); controller.simulate(); assert(controller.getInfo("Satellite1").getFiles().size() == 1); assert(controller.getInfo("Satellite1").getFiles().get("file1").hasTransferCompleted() == false); // The file should NOT be deleted after this period. // In addition the file should have transferred fully by this time. for (int i = 0; i < 180; ++i) { controller.simulate(); } assert(controller.getInfo("Satellite1").getFiles().size() == 1); assert(controller.getInfo("Satellite1").getFiles().get("file1").hasTransferCompleted() == true); } @Test public void testRelayAccess() { BlackoutController controller = new BlackoutController(); // We create two devices on one side of the planet, they should not be able to communicate. controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(180)); controller.createDevice("DeviceB", "LaptopDevice", Angle.fromDegrees(0)); assert(controller.communicableEntitiesInRange("DeviceA").size() == 0); assert(controller.communicableEntitiesInRange("DeviceB").size() == 0); // We create a chain of relays that should connect the two devices. int OFFSET = 15_000; int NUM_SATELLITES = 5; for (int i = 0; i < NUM_SATELLITES; ++i) { controller.createSatellite("Satellite" + i, "RelaySatellite", OFFSET + RADIUS_OF_JUPITER, Angle.fromDegrees(180 - i * (180 / NUM_SATELLITES))); } // We should be able to communicate with all relays, plus the other device. assert(controller.communicableEntitiesInRange("DeviceA").size() == NUM_SATELLITES + 1); assert(controller.communicableEntitiesInRange("DeviceB").size() == NUM_SATELLITES + 1); } @Test public void testCloudStorageDevice() { BlackoutController controller = new BlackoutController(); // The size of the string in cloudstoragedevice should be the same in the satellite // but larger after transfer into the laptop device. (Files are only decompressed // inside devices (minus cloudstoragedevice). controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(180)); controller.createDevice("DeviceB", "CloudStorageDevice", Angle.fromDegrees(180)); controller.createSatellite("Satellite1", "ShrinkingSatellite", 10000 + RADIUS_OF_JUPITER, Angle.fromDegrees(180)); // Compression isn't guaranteed, but if we repeat digits we can improve our chances. // Send the file to the satellite, it should be the same size as in the cloudstoragedevice. controller.addFileToDevice("DeviceB", "file", "biiiiiiiiiiiiiiiiiiiiiiiig"); assertDoesNotThrow(() -> controller.sendFile("file", "DeviceB", "Satellite1")); controller.simulate(); // should arrive in two ticks controller.simulate(); assert(controller.getInfo("Satellite1").getFiles().get("file").getFileSize() == controller.getInfo("DeviceB").getFiles().get("file").getFileSize()); assertDoesNotThrow(() -> controller.sendFile("file", "Satellite1", "DeviceA")); controller.simulate(); // TODO make < assert(controller.getInfo("Satellite1").getFiles().get("file").getFileSize() <= controller.getInfo("DeviceA").getFiles().get("file").getFileSize()); } @Test public void testSendFileExceptions() { BlackoutController controller = new BlackoutController(); controller.createDevice("DeviceA", "LaptopDevice", Angle.fromDegrees(180)); controller.createDevice("DeviceB", "LaptopDevice", Angle.fromDegrees(180)); controller.createSatellite("Satellite1", "ShrinkingSatellite", 15000 + RADIUS_OF_JUPITER, Angle.fromDegrees(180)); // VirtualFileNotFoundException if the file doesn't exist on fromID assertThrows(FileTransferException.VirtualFileNotFoundException.class, () -> controller.sendFile("?", "DeviceA", "Satellite1")); // ... or it's a partial file. controller.addFileToDevice("DeviceA", "file", "contents"); assertDoesNotThrow(() -> controller.sendFile("file", "DeviceA", "Satellite1")); assertThrows(FileTransferException.VirtualFileNotFoundException.class, () -> controller.sendFile("file", "Satellite1", "DeviceB")); controller.simulate(); // make the file arrive assert(controller.getInfo("Satellite1").getFiles().get("file").hasTransferCompleted()); // VirtualFileAlreadyExistsException if the file already exists on targetId. assertThrows(FileTransferException.VirtualFileAlreadyExistsException.class, () -> controller.sendFile("file", "Satellite1", "DeviceA")); // or if it's currently downloading controller.addFileToDevice("DeviceA", "file1", "contents"); assertDoesNotThrow(() -> controller.sendFile("file1", "DeviceA", "Satellite1")); controller.simulate(); assert(controller.getInfo("Satellite1").getFiles().get("file1").hasTransferCompleted()); assertDoesNotThrow(() -> controller.sendFile("file1", "Satellite1", "DeviceB")); assert(controller.getInfo("DeviceB").getFiles().get("file1").hasTransferCompleted() == false); assertThrows(FileTransferException.VirtualFileAlreadyExistsException.class, () -> controller.sendFile("file1", "Satellite1", "DeviceB")); controller.simulate(); // send all files // At this point our satellite has 0 files currently downloading and 2 files stored. // VirtualFileNoBandwidthException if we are sending too many files. // 15 bytes / tick = max of 15 files. assertDoesNotThrow(() -> { for (int i = 2; i < 15 + 2; ++i) { controller.addFileToDevice("DeviceA", "file" + i, "contents"); controller.sendFile("file" + i, "DeviceA", "Satellite1"); } }); controller.addFileToDevice("DeviceA", "too many files file", "contents"); assertThrows(FileTransferException.VirtualFileNoBandwidthException.class, () -> controller.sendFile("too many files file", "DeviceA", "Satellite1")); // VirtualFileNoStorageSpaceException should throw if the lack of room was due to a max file cap // max file cap of three for StandardSatellite, we have to be careful to avoid it's bandwidth limits controller.createSatellite("Satellite2", "StandardSatellite", 15000 + RADIUS_OF_JUPITER, Angle.fromDegrees(180)); assertDoesNotThrow(() -> { for (int i = 0; i < 3; ++i) { controller.addFileToDevice("DeviceA", "unique_file" + i, "1"); controller.sendFile("unique_file" + i, "DeviceA", "Satellite2"); controller.simulate(); } }); controller.addFileToDevice("DeviceA", "unique_file3", "contents"); assertThrows(FileTransferException.VirtualFileNoStorageSpaceException.class, () -> controller.sendFile("unique_file3", "DeviceA", "Satellite2")); // ... or if we exceed the storage byte limit controller.createSatellite("Satellite3", "StandardSatellite", 15000 + RADIUS_OF_JUPITER, Angle.fromDegrees(180)); controller.addFileToDevice("DeviceA", "big_file", "a very long string that definitely exceeds the max byte storage amount of satellite3"); assertThrows(FileTransferException.VirtualFileNoStorageSpaceException.class, () -> controller.sendFile("big_file", "DeviceA", "Satellite3")); } }