Don't register listener for SharedDocument, Add tests

This commit is contained in:
MrLetsplay 2024-05-18 15:19:26 +02:00
parent 4b0ec70f54
commit f82812577e
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
9 changed files with 182 additions and 16 deletions

View File

@ -1,8 +0,0 @@
package me.mrletsplay.shareclientcore;
public class ShareClient {
public static void main(String[] args) {
}
}

View File

@ -3,6 +3,8 @@ package me.mrletsplay.shareclientcore.connection.message;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
public record ChecksumMessage(int siteID, String documentPath, byte[] checksum) implements AddressableMessage { 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); 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 { public static ChecksumMessage deserialize(DataInputStream in) throws IOException {
try { try {
return new ChecksumMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt())); return new ChecksumMessage(in.readInt(), in.readUTF(), in.readNBytes(in.readInt()));

View File

@ -3,8 +3,12 @@ package me.mrletsplay.shareclientcore.connection.message;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; 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<Char> content) implements AddressableMessage {
@Override @Override
public MessageType getType() { public MessageType getType() {
@ -15,13 +19,26 @@ public record FullSyncMessage(int siteID, String documentPath, byte[] content) i
public void serialize(DataOutputStream out) throws IOException { public void serialize(DataOutputStream out) throws IOException {
out.writeInt(siteID); out.writeInt(siteID);
out.writeUTF(documentPath); 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 { public static FullSyncMessage deserialize(DataInputStream in) throws IOException {
try { 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<Char> 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) { }catch(IllegalArgumentException e) {
throw new IOException("Invalid content length", e); throw new IOException("Invalid content length", e);
} }

View File

@ -30,7 +30,7 @@ public interface Message extends SerializableObject {
case REQUEST_CHECKSUM -> m = RequestChecksumMessage.deserialize(dIn); case REQUEST_CHECKSUM -> m = RequestChecksumMessage.deserialize(dIn);
case FULL_SYNC -> m = FullSyncMessage.deserialize(dIn); case FULL_SYNC -> m = FullSyncMessage.deserialize(dIn);
case CHECKSUM -> m = ChecksumMessage.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; return m;

View File

@ -5,7 +5,15 @@ import java.util.List;
public class ArrayCharBag implements CharBag { public class ArrayCharBag implements CharBag {
private List<Char> chars = new ArrayList<>(); private List<Char> chars;
public ArrayCharBag() {
this.chars = new ArrayList<>();
}
public ArrayCharBag(List<Char> chars) {
this.chars = new ArrayList<>(chars);
}
@Override @Override
public int add(Char character) { public int add(Char character) {
@ -37,6 +45,11 @@ public class ArrayCharBag implements CharBag {
return chars.size(); return chars.size();
} }
@Override
public List<Char> toList() {
return new ArrayList<>(chars);
}
@Override @Override
public String toString() { public String toString() {
return chars.stream() return chars.stream()

View File

@ -21,6 +21,7 @@ public record Char(Identifier[] position, int lamport, char value) implements Se
result = prime * result + Objects.hash(lamport, value); result = prime * result + Objects.hash(lamport, value);
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj)

View File

@ -1,5 +1,7 @@
package me.mrletsplay.shareclientcore.document; package me.mrletsplay.shareclientcore.document;
import java.util.List;
public interface CharBag { public interface CharBag {
/** /**
@ -29,6 +31,12 @@ public interface CharBag {
*/ */
public int size(); public int size();
/**
* Collects the chars in this bag ordered by their position into a list
* @return
*/
public List<Char> toList();
/** /**
* Collects the chars in this bag ordered by their position into a string * Collects the chars in this bag ordered by their position into a string
* @return A string containing the chars * @return A string containing the chars

View File

@ -2,6 +2,7 @@ package me.mrletsplay.shareclientcore.document;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import me.mrletsplay.shareclientcore.connection.Change; import me.mrletsplay.shareclientcore.connection.Change;
@ -28,8 +29,6 @@ public class SharedDocument implements MessageListener {
this.site = connection.getSiteID(); this.site = connection.getSiteID();
this.listeners = new HashSet<>(); this.listeners = new HashSet<>();
connection.addListener(this);
charBag.add(Char.START_OF_DOCUMENT); charBag.add(Char.START_OF_DOCUMENT);
charBag.add(Char.END_OF_DOCUMENT); charBag.add(Char.END_OF_DOCUMENT);
if(initialContents != null && !initialContents.isEmpty()) { if(initialContents != null && !initialContents.isEmpty()) {
@ -135,6 +134,16 @@ public class SharedDocument implements MessageListener {
return charBag; return charBag;
} }
/**
* Collects the chars in this document ordered by their position into a list.<br>
* 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<Char> toList() {
List<Char> chars = charBag.toList();
return chars.subList(1, chars.size() - 1);
}
public String getContents() { public String getContents() {
String contents = charBag.toString(); String contents = charBag.toString();
return contents.substring(1, contents.length() - 1); return contents.substring(1, contents.length() - 1);

View File

@ -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<Char> 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");
}
}