From f82812577e409e41891d33b78f5dd6f49e7b6e1c Mon Sep 17 00:00:00 2001 From: MrLetsplay Date: Sat, 18 May 2024 15:19:26 +0200 Subject: [PATCH] Don't register listener for SharedDocument, Add tests --- .../shareclientcore/ShareClient.java | 8 -- .../connection/message/ChecksumMessage.java | 24 +++++ .../connection/message/FullSyncMessage.java | 25 ++++- .../connection/message/Message.java | 2 +- .../document/ArrayCharBag.java | 15 ++- .../shareclientcore/document/Char.java | 1 + .../shareclientcore/document/CharBag.java | 8 ++ .../document/SharedDocument.java | 13 ++- .../shareclientcore/MessageTest.java | 102 ++++++++++++++++++ 9 files changed, 182 insertions(+), 16 deletions(-) delete mode 100644 src/main/java/me/mrletsplay/shareclientcore/ShareClient.java create mode 100644 src/test/java/me/mrletsplay/shareclientcore/MessageTest.java diff --git a/src/main/java/me/mrletsplay/shareclientcore/ShareClient.java b/src/main/java/me/mrletsplay/shareclientcore/ShareClient.java deleted file mode 100644 index 163bf6d..0000000 --- a/src/main/java/me/mrletsplay/shareclientcore/ShareClient.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.mrletsplay.shareclientcore; - -public class ShareClient { - - public static void main(String[] args) { - } - -} diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChecksumMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChecksumMessage.java index fa246af..1612ef4 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChecksumMessage.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/ChecksumMessage.java @@ -3,6 +3,8 @@ package me.mrletsplay.shareclientcore.connection.message; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; public record ChecksumMessage(int siteID, String documentPath, byte[] checksum) implements AddressableMessage { @@ -19,6 +21,28 @@ public record ChecksumMessage(int siteID, String documentPath, byte[] checksum) out.write(checksum); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(checksum); + result = prime * result + Objects.hash(documentPath, siteID); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ChecksumMessage other = (ChecksumMessage) obj; + return Arrays.equals(checksum, other.checksum) && Objects.equals(documentPath, other.documentPath) + && siteID == other.siteID; + } + public static ChecksumMessage deserialize(DataInputStream in) throws IOException { try { return new ChecksumMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt())); diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/FullSyncMessage.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/FullSyncMessage.java index 6829992..6b6e07d 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/message/FullSyncMessage.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/FullSyncMessage.java @@ -3,8 +3,12 @@ package me.mrletsplay.shareclientcore.connection.message; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; -public record FullSyncMessage(int siteID, String documentPath, byte[] content) implements AddressableMessage { +import me.mrletsplay.shareclientcore.document.Char; + +public record FullSyncMessage(int siteID, String documentPath, List content) implements AddressableMessage { @Override public MessageType getType() { @@ -15,13 +19,26 @@ public record FullSyncMessage(int siteID, String documentPath, byte[] content) i public void serialize(DataOutputStream out) throws IOException { out.writeInt(siteID); out.writeUTF(documentPath); - out.writeInt(content.length); - out.write(content); + + out.writeInt(content.size()); + for(Char c : content) { + c.serialize(out); + } } public static FullSyncMessage deserialize(DataInputStream in) throws IOException { try { - return new FullSyncMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt())); + int siteID = in.readInt(); + String documentPath = in.readUTF(); + + int contentSize = in.readInt(); + List content = new ArrayList<>(contentSize); + for(int i = 0; i < contentSize; i++) { + content.add(Char.deserialize(in)); + System.out.println(content.get(content.size() - 1)); + } + + return new FullSyncMessage(siteID, documentPath, content); }catch(IllegalArgumentException e) { throw new IOException("Invalid content length", e); } diff --git a/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java b/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java index 9abb969..bdf1f30 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java +++ b/src/main/java/me/mrletsplay/shareclientcore/connection/message/Message.java @@ -30,7 +30,7 @@ public interface Message extends SerializableObject { case REQUEST_CHECKSUM -> m = RequestChecksumMessage.deserialize(dIn); case FULL_SYNC -> m = FullSyncMessage.deserialize(dIn); case CHECKSUM -> m = ChecksumMessage.deserialize(dIn); - default -> m = new BasicMessage(type); + default -> m = new BasicMessage(type);// TODO: implement missing message types and delete BasicMessage } return m; diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/ArrayCharBag.java b/src/main/java/me/mrletsplay/shareclientcore/document/ArrayCharBag.java index 9b5aee9..a8ccf84 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/ArrayCharBag.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/ArrayCharBag.java @@ -5,7 +5,15 @@ import java.util.List; public class ArrayCharBag implements CharBag { - private List chars = new ArrayList<>(); + private List chars; + + public ArrayCharBag() { + this.chars = new ArrayList<>(); + } + + public ArrayCharBag(List chars) { + this.chars = new ArrayList<>(chars); + } @Override public int add(Char character) { @@ -37,6 +45,11 @@ public class ArrayCharBag implements CharBag { return chars.size(); } + @Override + public List toList() { + return new ArrayList<>(chars); + } + @Override public String toString() { return chars.stream() diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/Char.java b/src/main/java/me/mrletsplay/shareclientcore/document/Char.java index 48314c0..b17fe01 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/Char.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/Char.java @@ -21,6 +21,7 @@ public record Char(Identifier[] position, int lamport, char value) implements Se result = prime * result + Objects.hash(lamport, value); return result; } + @Override public boolean equals(Object obj) { if (this == obj) diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/CharBag.java b/src/main/java/me/mrletsplay/shareclientcore/document/CharBag.java index 642f925..59c9a5c 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/CharBag.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/CharBag.java @@ -1,5 +1,7 @@ package me.mrletsplay.shareclientcore.document; +import java.util.List; + public interface CharBag { /** @@ -29,6 +31,12 @@ public interface CharBag { */ public int size(); + /** + * Collects the chars in this bag ordered by their position into a list + * @return + */ + public List toList(); + /** * Collects the chars in this bag ordered by their position into a string * @return A string containing the chars diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java index a30b603..8513086 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java @@ -2,6 +2,7 @@ package me.mrletsplay.shareclientcore.document; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import me.mrletsplay.shareclientcore.connection.Change; @@ -28,8 +29,6 @@ public class SharedDocument implements MessageListener { this.site = connection.getSiteID(); this.listeners = new HashSet<>(); - connection.addListener(this); - charBag.add(Char.START_OF_DOCUMENT); charBag.add(Char.END_OF_DOCUMENT); if(initialContents != null && !initialContents.isEmpty()) { @@ -135,6 +134,16 @@ public class SharedDocument implements MessageListener { return charBag; } + /** + * Collects the chars in this document ordered by their position into a list.
+ * This differs from {@link CharBag#toList()} in that this method does not include the {@link Char#START_OF_DOCUMENT} and {@link Char#END_OF_DOCUMENT} chars. + * @return + */ + public List toList() { + List chars = charBag.toList(); + return chars.subList(1, chars.size() - 1); + } + public String getContents() { String contents = charBag.toString(); return contents.substring(1, contents.length() - 1); diff --git a/src/test/java/me/mrletsplay/shareclientcore/MessageTest.java b/src/test/java/me/mrletsplay/shareclientcore/MessageTest.java new file mode 100644 index 0000000..497d493 --- /dev/null +++ b/src/test/java/me/mrletsplay/shareclientcore/MessageTest.java @@ -0,0 +1,102 @@ +package me.mrletsplay.shareclientcore; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import me.mrletsplay.shareclientcore.connection.Change; +import me.mrletsplay.shareclientcore.connection.ChangeType; +import me.mrletsplay.shareclientcore.connection.message.ChangeMessage; +import me.mrletsplay.shareclientcore.connection.message.ChecksumMessage; +import me.mrletsplay.shareclientcore.connection.message.ClientHelloMessage; +import me.mrletsplay.shareclientcore.connection.message.FullSyncMessage; +import me.mrletsplay.shareclientcore.connection.message.Message; +import me.mrletsplay.shareclientcore.connection.message.PeerJoinMessage; +import me.mrletsplay.shareclientcore.connection.message.PeerLeaveMessage; +import me.mrletsplay.shareclientcore.connection.message.RequestChecksumMessage; +import me.mrletsplay.shareclientcore.connection.message.RequestFullSyncMessage; +import me.mrletsplay.shareclientcore.connection.message.ServerHelloMessage; +import me.mrletsplay.shareclientcore.document.Char; +import me.mrletsplay.shareclientcore.document.Identifier; + +public class MessageTest { + + private static byte[] serialize(Message m) throws IOException { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DataOutputStream dOut = new DataOutputStream(bOut); + dOut.writeUTF(m.getType().name()); + m.serialize(dOut); + return bOut.toByteArray(); + } + + private static Message deserialize(byte[] bytes) throws IOException { + return Message.deserialize(ByteBuffer.wrap(bytes)); + } + + @Test + public void testChangeMessage() throws IOException { + Change change = new Change("Project:src/test.txt", ChangeType.ADD, new Char(new Identifier[] {new Identifier(0, 1), new Identifier(1, 3)}, 42, 'e')); + ChangeMessage m = new ChangeMessage(change); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testClientHelloMessage() throws IOException { + ClientHelloMessage m = new ClientHelloMessage("User123", "abcdefg"); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testPeerJoinMessage() throws IOException { + PeerJoinMessage m = new PeerJoinMessage("User234", 2); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testPeerLeaveMessage() throws IOException { + PeerLeaveMessage m = new PeerLeaveMessage(2); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testServerHelloMessage() throws IOException { + ServerHelloMessage m = new ServerHelloMessage(42, 3); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testChecksumMessage() throws IOException { + ChecksumMessage m = new ChecksumMessage(2, "Project:src/test.txt", new byte[] {1, 2, 3, 4, 5, 6}); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testFullSyncMessage() throws IOException { + List chars = Arrays.asList( + new Char(new Identifier[] {new Identifier(0, 1), new Identifier(1, 3)}, 42, 'e'), + new Char(new Identifier[] {new Identifier(0, 1), new Identifier(1, 3), new Identifier(1, 4)}, 333, 'f') + ); + FullSyncMessage m = new FullSyncMessage(2, "Project:src/test.txt", chars); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testRequestChecksumMessage() throws IOException { + RequestChecksumMessage m = new RequestChecksumMessage(2, "Project:src/test.txt"); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + + @Test + public void testRequestFullSyncMessage() throws IOException { + RequestFullSyncMessage m = new RequestFullSyncMessage(2, "Project:src/test.txt"); + assertEquals(deserialize(serialize(m)), m, "Deserialized message must equal message"); + } + +}