diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java index 048576f..8aa6b33 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/SharedDocument.java @@ -56,7 +56,7 @@ public class SharedDocument implements MessageListener { Identifier[] newPos = Util.generatePositionBetween(charBefore.position(), charAfter.position(), site); lamport++; Char ch = new Char(newPos, lamport, bytes[i]); - charBag.add(ch); + if(charBag.add(ch) == -1) throw new IllegalStateException("Couldn't insert newly created char"); changes[i] = new Change(path, ChangeType.ADD, ch); charBefore = ch; } @@ -110,7 +110,7 @@ public class SharedDocument implements MessageListener { // TODO: more efficient implementation (e.g. range delete in CharBag) Char toRemove = charBag.get(index + 1); changes[n] = new Change(path, ChangeType.REMOVE, toRemove); - charBag.remove(toRemove); + if(charBag.remove(toRemove) == -1) throw new IllegalStateException("Couldn't remove existing char"); } @@ -195,7 +195,6 @@ public class SharedDocument implements MessageListener { Change c = change.change(); if(!c.documentPath().equals(path)) return; - System.out.println("Change: " + c + " | " + Arrays.toString(c.character().position())); switch(c.type()) { case ADD -> remoteInsert(c.character()); case REMOVE -> remoteDelete(c.character()); diff --git a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java index 462c89a..3257f31 100644 --- a/src/main/java/me/mrletsplay/shareclientcore/document/Util.java +++ b/src/main/java/me/mrletsplay/shareclientcore/document/Util.java @@ -1,6 +1,7 @@ package me.mrletsplay.shareclientcore.document; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class Util { @@ -44,6 +45,10 @@ public class Util { return newPosition.toArray(Identifier[]::new); } + System.err.println("Got invalid state"); + System.err.println(c1 + "/" + c2); + System.err.println(Arrays.toString(before)); + System.err.println(Arrays.toString(after)); throw new RuntimeException("Invalid site order"); } diff --git a/src/test/java/me/mrletsplay/shareclientcore/SharingTest.java b/src/test/java/me/mrletsplay/shareclientcore/SharingTest.java index a2cd7e8..e5cb6ee 100644 --- a/src/test/java/me/mrletsplay/shareclientcore/SharingTest.java +++ b/src/test/java/me/mrletsplay/shareclientcore/SharingTest.java @@ -2,6 +2,8 @@ package me.mrletsplay.shareclientcore; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Random; + import org.junit.jupiter.api.Test; import me.mrletsplay.shareclientcore.connection.DummyConnection; @@ -32,6 +34,69 @@ public class SharingTest { assertEquals("This is a test!Hello World!", sharedA.getContentsAsString()); assertEquals("This is a test!Hello World!", sharedB.getContentsAsString()); + + sharedA.localDelete(10, 11); + + assertEquals("This is a World!", sharedA.getContentsAsString()); + assertEquals("This is a World!", sharedB.getContentsAsString()); + + sharedB.localDelete(7, 8); + + assertEquals("This is!", sharedA.getContentsAsString()); + assertEquals("This is!", sharedB.getContentsAsString()); + } + + @Test + public void testSharedDocument2() { + DummyConnection a = new DummyConnection(0); + DummyConnection b = new DummyConnection(1); + + a.setSendMessageHandler(m -> b.receive(m)); + b.setSendMessageHandler(m -> a.receive(m)); + + SharedDocument sharedA = new SharedDocument(a, "doc"); + SharedDocument sharedB = new SharedDocument(b, "doc"); + + a.addListener(sharedA); + b.addListener(sharedB); + + String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890"; + + Random r = new Random(0); + for(int i = 0; i < 1000; i++) { + switch(r.nextInt(sharedA.getContents().length == 0 ? 2 : 4)) { + case 0: { + // Insert A + char[] insert = new char[r.nextInt(16)]; + for(int j = 0; j < insert.length; j++) insert[j] = chars.charAt(r.nextInt(chars.length())); + sharedA.localInsert(r.nextInt(sharedA.getContents().length + 1), String.valueOf(insert)); + } + case 1: { + // Insert B + char[] insert = new char[r.nextInt(16)]; + for(int j = 0; j < insert.length; j++) insert[j] = chars.charAt(r.nextInt(chars.length())); + sharedB.localInsert(r.nextInt(sharedB.getContents().length + 1), String.valueOf(insert)); + } + case 2: { + // Delete A + int len = sharedA.getContents().length; + int idx = r.nextInt(len); + int n = r.nextInt(len - idx); + sharedA.localDelete(idx, n); + } + case 3: { + // Delete B + int len = sharedB.getContents().length; + int idx = r.nextInt(len); + int n = r.nextInt(len - idx + 1); + sharedB.localDelete(idx, n); + } + } + + System.out.println("A: " + sharedA.getContentsAsString()); + System.out.println("B: " + sharedB.getContentsAsString()); + assertEquals(sharedA.getContentsAsString(), sharedB.getContentsAsString()); + } } }