From 3e22af0918ba3b81d210ddbc561b776e6773cc42 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Wed, 4 Oct 2023 17:26:20 +0200 Subject: [PATCH 01/22] Update Transaction.cs The state vector is optional. --- YDotNet/Document/Transactions/Transaction.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/YDotNet/Document/Transactions/Transaction.cs b/YDotNet/Document/Transactions/Transaction.cs index a68e97ba..6e4f55ba 100644 --- a/YDotNet/Document/Transactions/Transaction.cs +++ b/YDotNet/Document/Transactions/Transaction.cs @@ -119,15 +119,15 @@ public byte[] StateVectorV1() /// /// /// - /// The state vector to be used as base for comparison and generation of the difference. + /// The optional state vector to be used as base for comparison and generation of the difference. /// /// /// The lib0 v1 encoded state difference between the of this and the /// remote . /// - public byte[] StateDiffV1(byte[] stateVector) + public byte[] StateDiffV1(byte[]? stateVector) { - var handle = TransactionChannel.StateDiffV1(Handle, stateVector, (uint) stateVector.Length, out var length); + var handle = TransactionChannel.StateDiffV1(Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); var data = MemoryReader.ReadBytes(handle, length); BinaryChannel.Destroy(handle, length); @@ -152,15 +152,15 @@ public byte[] StateDiffV1(byte[] stateVector) /// /// /// - /// The state vector to be used as base for comparison and generation of the difference. + /// The optional state vector to be used as base for comparison and generation of the difference. /// /// /// The lib0 v2 encoded state difference between the of this and the /// remote . /// - public byte[] StateDiffV2(byte[] stateVector) + public byte[] StateDiffV2(byte[]? stateVector) { - var handle = TransactionChannel.StateDiffV2(Handle, stateVector, (uint) stateVector.Length, out var length); + var handle = TransactionChannel.StateDiffV2(Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); var data = MemoryReader.ReadBytes(handle, length); BinaryChannel.Destroy(handle, length); From 9092c8255b5145c3a16a25004ee61c3daf421c18 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 5 Oct 2023 08:26:04 +0200 Subject: [PATCH 02/22] Use 32 bit client id for now yjs uses 32 bit client ids. If a client is exceeds the 32 range it cannot be decoded on the client anymore: There is also an open discussion about that. https://github.com/y-crdt/y-crdt/issues/209 --- YDotNet/Document/Options/DocOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YDotNet/Document/Options/DocOptions.cs b/YDotNet/Document/Options/DocOptions.cs index 36b68008..eec5f19c 100644 --- a/YDotNet/Document/Options/DocOptions.cs +++ b/YDotNet/Document/Options/DocOptions.cs @@ -10,7 +10,7 @@ public class DocOptions /// internal static DocOptions Default => new() { - Id = (ulong) Random.Shared.NextInt64(), + Id = (ulong) Random.Shared.Next(), ShouldLoad = true, Encoding = DocEncoding.Utf16 }; From 09140e6cde295d20fdee484f6b98664cc672e2b3 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 22:57:03 -0300 Subject: [PATCH 03/22] refactor(transaction): simplify Transaction.StateDiffV1() --- YDotNet/Document/Transactions/Transaction.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/YDotNet/Document/Transactions/Transaction.cs b/YDotNet/Document/Transactions/Transaction.cs index 6e4f55ba..8092e946 100644 --- a/YDotNet/Document/Transactions/Transaction.cs +++ b/YDotNet/Document/Transactions/Transaction.cs @@ -127,7 +127,8 @@ public byte[] StateVectorV1() /// public byte[] StateDiffV1(byte[]? stateVector) { - var handle = TransactionChannel.StateDiffV1(Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); + var stateVectorLength = stateVector?.Length ?? 0; + var handle = TransactionChannel.StateDiffV1(Handle, stateVector, (uint) stateVectorLength, out var length); var data = MemoryReader.ReadBytes(handle, length); BinaryChannel.Destroy(handle, length); @@ -160,7 +161,8 @@ public byte[] StateDiffV1(byte[]? stateVector) /// public byte[] StateDiffV2(byte[]? stateVector) { - var handle = TransactionChannel.StateDiffV2(Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); + var handle = TransactionChannel.StateDiffV2( + Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); var data = MemoryReader.ReadBytes(handle, length); BinaryChannel.Destroy(handle, length); From 44426ea61987ce5e6f22b2c84d2141052de16bb9 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 22:57:19 -0300 Subject: [PATCH 04/22] refactor(transaction): make stateVector param optional in TransactionChannel.StateDiffV1() --- YDotNet/Native/Transaction/TransactionChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YDotNet/Native/Transaction/TransactionChannel.cs b/YDotNet/Native/Transaction/TransactionChannel.cs index 2dc2f0e7..92184e17 100644 --- a/YDotNet/Native/Transaction/TransactionChannel.cs +++ b/YDotNet/Native/Transaction/TransactionChannel.cs @@ -28,7 +28,7 @@ internal static class TransactionChannel EntryPoint = "ytransaction_state_diff_v1")] public static extern nint StateDiffV1( nint transaction, - byte[] stateVector, + byte[]? stateVector, uint stateVectorLength, out uint length); From 147e0b012ce676b4bd2eaeb9acaeca003c4e9ba9 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 22:58:03 -0300 Subject: [PATCH 05/22] refactor(transaction): simplify Transaction.StateDiffV2() --- YDotNet/Document/Transactions/Transaction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/YDotNet/Document/Transactions/Transaction.cs b/YDotNet/Document/Transactions/Transaction.cs index 8092e946..c073c6af 100644 --- a/YDotNet/Document/Transactions/Transaction.cs +++ b/YDotNet/Document/Transactions/Transaction.cs @@ -161,8 +161,8 @@ public byte[] StateDiffV1(byte[]? stateVector) /// public byte[] StateDiffV2(byte[]? stateVector) { - var handle = TransactionChannel.StateDiffV2( - Handle, stateVector, (uint) (stateVector != null ? stateVector.Length : 0), out var length); + var stateVectorLength = stateVector?.Length ?? 0; + var handle = TransactionChannel.StateDiffV2(Handle, stateVector, (uint) stateVectorLength, out var length); var data = MemoryReader.ReadBytes(handle, length); BinaryChannel.Destroy(handle, length); From 6e394883c678384e521fc52c9d256cec2dfdcb61 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 22:58:09 -0300 Subject: [PATCH 06/22] refactor(transaction): make stateVector param optional in TransactionChannel.StateDiffV2() --- YDotNet/Native/Transaction/TransactionChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YDotNet/Native/Transaction/TransactionChannel.cs b/YDotNet/Native/Transaction/TransactionChannel.cs index 92184e17..72d5b158 100644 --- a/YDotNet/Native/Transaction/TransactionChannel.cs +++ b/YDotNet/Native/Transaction/TransactionChannel.cs @@ -38,7 +38,7 @@ public static extern nint StateDiffV1( EntryPoint = "ytransaction_state_diff_v2")] public static extern nint StateDiffV2( nint transaction, - byte[] stateVector, + byte[]? stateVector, uint stateVectorLength, out uint length); From 8c9fb7abd140fea98c78a1269d2d701fbe2490a7 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 23:01:13 -0300 Subject: [PATCH 07/22] test(transaction): add tests for using StateDiffV1() with null param --- .../Transactions/StateDiffV1Tests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs index dfb29da8..dd0e2ca8 100644 --- a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs +++ b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs @@ -5,6 +5,20 @@ namespace YDotNet.Tests.Unit.Transactions; public class StateDiffV1Tests { + [Test] + public void Null() + { + // Arrange + var senderDoc = ArrangeSenderDoc(); + + // Act + var stateDiff = senderDoc.ReadTransaction().StateDiffV1(stateVector: null); + + // Assert + Assert.That(stateDiff, Is.Not.Null); + Assert.That(stateDiff, Has.Length.EqualTo(26)); + } + [Test] public void ReadOnly() { From 025dcb25f9a8dc120a4a4ef4fa2678d9f5d823c0 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Tue, 10 Oct 2023 23:02:24 -0300 Subject: [PATCH 08/22] test(transaction): add tests for using StateDiffV2() with null param --- .../Transactions/StateDiffV2Tests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs index 8876eb02..eb1415cd 100644 --- a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs +++ b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs @@ -5,6 +5,20 @@ namespace YDotNet.Tests.Unit.Transactions; public class StateDiffV2Tests { + [Test] + public void Null() + { + // Arrange + var senderDoc = ArrangeSenderDoc(); + + // Act + var stateDiff = senderDoc.ReadTransaction().StateDiffV2(stateVector: null); + + // Assert + Assert.That(stateDiff, Is.Not.Null); + Assert.That(stateDiff, Has.Length.InRange(from: 37, to: 38)); + } + [Test] public void ReadOnly() { From 519466c049c516440da4e9ed2644a5ba30496fc2 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Fri, 13 Oct 2023 00:54:25 +0000 Subject: [PATCH 09/22] Revert "Use 32-bit number when generating random client IDs" --- YDotNet/Document/Options/DocOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YDotNet/Document/Options/DocOptions.cs b/YDotNet/Document/Options/DocOptions.cs index eec5f19c..36b68008 100644 --- a/YDotNet/Document/Options/DocOptions.cs +++ b/YDotNet/Document/Options/DocOptions.cs @@ -10,7 +10,7 @@ public class DocOptions /// internal static DocOptions Default => new() { - Id = (ulong) Random.Shared.Next(), + Id = (ulong) Random.Shared.NextInt64(), ShouldLoad = true, Encoding = DocEncoding.Utf16 }; From 58b1726f9e3415d517abfdd669e2c97b0f0232f6 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Thu, 12 Oct 2023 22:01:13 -0300 Subject: [PATCH 10/22] test(transaction): fix unit tests for Transaction.StateDiff*() and Doc.ObserveUpdates*() --- .../Document/{UpdatesV1Tests.cs => ObserveUpdatesV1Tests.cs} | 2 +- .../Document/{UpdatesV2Tests.cs => ObserveUpdatesV2Tests.cs} | 0 Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs | 2 +- Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename Tests/YDotNet.Tests.Unit/Document/{UpdatesV1Tests.cs => ObserveUpdatesV1Tests.cs} (95%) rename Tests/YDotNet.Tests.Unit/Document/{UpdatesV2Tests.cs => ObserveUpdatesV2Tests.cs} (100%) diff --git a/Tests/YDotNet.Tests.Unit/Document/UpdatesV1Tests.cs b/Tests/YDotNet.Tests.Unit/Document/ObserveUpdatesV1Tests.cs similarity index 95% rename from Tests/YDotNet.Tests.Unit/Document/UpdatesV1Tests.cs rename to Tests/YDotNet.Tests.Unit/Document/ObserveUpdatesV1Tests.cs index 3b3bd0a9..00ed0818 100644 --- a/Tests/YDotNet.Tests.Unit/Document/UpdatesV1Tests.cs +++ b/Tests/YDotNet.Tests.Unit/Document/ObserveUpdatesV1Tests.cs @@ -41,7 +41,7 @@ public void TriggersWhenTransactionIsCommittedUntilUnobserve() // Assert Assert.That(called, Is.EqualTo(expected: 2)); Assert.That(data, Is.Not.Null); - Assert.That(data, Has.Length.InRange(from: 25, to: 31)); + Assert.That(data, Has.Length.InRange(from: 23, to: 31)); // Act data = null; diff --git a/Tests/YDotNet.Tests.Unit/Document/UpdatesV2Tests.cs b/Tests/YDotNet.Tests.Unit/Document/ObserveUpdatesV2Tests.cs similarity index 100% rename from Tests/YDotNet.Tests.Unit/Document/UpdatesV2Tests.cs rename to Tests/YDotNet.Tests.Unit/Document/ObserveUpdatesV2Tests.cs diff --git a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs index dd0e2ca8..d1afe8b2 100644 --- a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs +++ b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV1Tests.cs @@ -16,7 +16,7 @@ public void Null() // Assert Assert.That(stateDiff, Is.Not.Null); - Assert.That(stateDiff, Has.Length.EqualTo(26)); + Assert.That(stateDiff, Has.Length.InRange(from: 22, to: 26)); } [Test] diff --git a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs index eb1415cd..ef0ef949 100644 --- a/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs +++ b/Tests/YDotNet.Tests.Unit/Transactions/StateDiffV2Tests.cs @@ -16,7 +16,7 @@ public void Null() // Assert Assert.That(stateDiff, Is.Not.Null); - Assert.That(stateDiff, Has.Length.InRange(from: 37, to: 38)); + Assert.That(stateDiff, Has.Length.InRange(from: 32, to: 38)); } [Test] From 4bb78c80070af7400e38f672562848a03b873694 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 10:42:22 -0300 Subject: [PATCH 11/22] ci(tests): add GitHub workflow file to build binaries based on SebastianStehle's version --- .github/workflows/build-binaries.yml | 130 +++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 .github/workflows/build-binaries.yml diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml new file mode 100644 index 00000000..7d0db768 --- /dev/null +++ b/.github/workflows/build-binaries.yml @@ -0,0 +1,130 @@ +name: Build binaries + +on: + push: + tags: + - 'binaries' + branches: + - '50-implement-automated-tests-pipeline' + +env: + YRS_REPO: https://github.com/LSViana/y-crdt + YRS_BRANCH: main + CARGO_TERM_COLOR: always + +jobs: + # Based on https://www.rohanjain.in/cargo-cross/ + build-native-binaries: + runs-on: ${{matrix.os}} + strategy: + matrix: + include: + # Windows + - build: win-x64 + os: windows-latest + rust: stable + target: x86_64-pc-windows-msvc + linker: mingw-w64 + cross: false + + # Linux + - build: linux-x64 + os: ubuntu-latest + rust: stable + target: x86_64-unknown-linux-gnu + cross: false + + - build: linux-x64-musl + os: ubuntu-latest + rust: stable + target: x86_64-unknown-linux-musl + cross: false + + - build: linux-armv7 + os: ubuntu-latest + rust: stable + target: armv7-unknown-linux-gnueabihf + linker: gcc-arm-linux-gnueabihf + cross: true + + - build: linux-armv7-musl + os: ubuntu-latest + rust: stable + target: armv7-unknown-linux-musleabihf + linker: gcc-arm-linux-gnueabihf + cross: true + + - build: linux-arm64 + os: ubuntu-latest + rust: stable + target: aarch64-unknown-linux-gnu + linker: gcc-aarch64-linux-gnu + cross: true + + - build: linux-arm64-musl + os: ubuntu-latest + rust: stable + target: aarch64-unknown-linux-musl + linker: gcc-aarch64-linux-gnu + cross: true + + # macOS + - build: macos + os: macos-latest + rust: stable + target: x86_64-apple-darwin + cross: false + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Cross + if: matrix.cross + uses: taiki-e/install-action@v2 + with: + tool: cross + + - name: Install musl tools + run: sudo apt install -y musl musl-dev musl-tools + if: endsWith(matrix.build, '-musl') + + - name: Install Linker + if: matrix.cross + run: | + sudo apt update + sudo apt install ${{ matrix.linker }} + cat .cargo/config.github >> .cargo/config + + - name: Install Rust + run: | + rustup install ${{ matrix.rust }} + rustup target add ${{ matrix.target }} + rustup show + + - name: Clone Yrs + run: | + git clone ${YRS_REPO} --branch ${YRS_BRANCH} --single-branch yrs + shell: bash + + - name: Build (Cargo) + if: "!matrix.cross" + run: | + cd yrs + RUSTFLAGS="-C target-feature=-crt-static" cargo build --release --target ${{ matrix.target }} + shell: bash + + - name: Build (Cross) + if: matrix.cross + run: | + cd yrs + RUSTFLAGS="-C target-feature=-crt-static" cross build --release --target ${{ matrix.target }} + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.build }} + path: | + yrs/target/${{ matrix.target }}/release/*yrs.dll + yrs/target/${{ matrix.target }}/release/*yrs.so + yrs/target/${{ matrix.target }}/release/*yrs.dylib From cde167d4791d37b7e177e50ca3ad488fca7e1dab Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 10:50:55 -0300 Subject: [PATCH 12/22] ci(tests): add GitHub workflow file to run unit tests --- .github/workflows/build.yml | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..7a530682 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,58 @@ +name: Build + +on: + push: + branches: + - 'main' + - '50-implement-automated-tests-pipeline' + +jobs: + test: + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + + matrix: + include: + # Windows + - build: win-x64 + os: windows-latest + + # Linux + - build: linux-x64 + os: ubuntu-latest + + - build: linux-x64-musl + os: ubuntu-latest + + # macOS + - build: macos + os: macos-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Download artifacts + uses: dawidd6/action-download-artifact@v2 + with: + path: ./output + workflow: build-binaries.yml + workflow_conclusion: success + name: ${{ matrix.build }} + name_is_regexp: true + + - name: Copy binaries + run: | + cp output/${{ matrix.build }}/*.* YDotNet + + - name: Build tests + run: | + cd Tests/YDotNet.Tests.Unit + dotnet build + + - name: Run tests + run: | + dotnet test + env: + RUST_BACKTRACE: 1 From 58c2d9da0b04ce1f692c87b6c36a6f6e7468272f Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 10:53:17 -0300 Subject: [PATCH 13/22] ci(tests): add Cargo configuration file for running GitHub actions --- .cargo/config.github | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .cargo/config.github diff --git a/.cargo/config.github b/.cargo/config.github new file mode 100644 index 00000000..91e208bc --- /dev/null +++ b/.cargo/config.github @@ -0,0 +1,11 @@ +[target.aarch64-unknown-linux-gnu] +linker = "aarch64-linux-gnu-gcc" + +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-gnu-gcc" + +[target.armv7-unknown-linux-gnueabihf] +linker = "arm-linux-gnueabihf-gcc" + +[target.armv7-unknown-linux-musleabihf] +linker = "arm-linux-musleabihf-gcc" From dd614bcbcca8ed3a631a89c9177e8c6cedbd32d9 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:10:41 -0300 Subject: [PATCH 14/22] ci(tests): include `libyrs.so` in the list of files to be copied during the build phase --- YDotNet/YDotNet.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/YDotNet/YDotNet.csproj b/YDotNet/YDotNet.csproj index 2b78e0f7..97c19b45 100644 --- a/YDotNet/YDotNet.csproj +++ b/YDotNet/YDotNet.csproj @@ -25,6 +25,9 @@ Always + + Always + From dda602663a91b12d41d4c1ed52da7fabd4fe144c Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:12:12 -0300 Subject: [PATCH 15/22] ci(tests): ignore tests that are failing due to issues on yffi --- Tests/YDotNet.Tests.Unit/UndoManagers/RedoTests.cs | 1 + Tests/YDotNet.Tests.Unit/UndoManagers/UndoTests.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Tests/YDotNet.Tests.Unit/UndoManagers/RedoTests.cs b/Tests/YDotNet.Tests.Unit/UndoManagers/RedoTests.cs index 11b1ecd5..69ab52f5 100644 --- a/Tests/YDotNet.Tests.Unit/UndoManagers/RedoTests.cs +++ b/Tests/YDotNet.Tests.Unit/UndoManagers/RedoTests.cs @@ -8,6 +8,7 @@ namespace YDotNet.Tests.Unit.UndoManagers; public class RedoTests { [Test] + [Ignore("Waiting for fix on yffi.")] public void ReturnsFalseWhenNoChangesApplied() { // Arrange diff --git a/Tests/YDotNet.Tests.Unit/UndoManagers/UndoTests.cs b/Tests/YDotNet.Tests.Unit/UndoManagers/UndoTests.cs index a7bbc23c..8a8a9801 100644 --- a/Tests/YDotNet.Tests.Unit/UndoManagers/UndoTests.cs +++ b/Tests/YDotNet.Tests.Unit/UndoManagers/UndoTests.cs @@ -8,6 +8,7 @@ namespace YDotNet.Tests.Unit.UndoManagers; public class UndoTests { [Test] + [Ignore("Waiting for fix on yffi.")] public void ReturnsFalseWhenNoChangesApplied() { // Arrange @@ -82,6 +83,7 @@ public void UndoAddingAndUpdatingAndRemovingContentOnText() } [Test] + [Ignore("Waiting for fix on yffi.")] public void UndoAddingAndUpdatingAndRemovingContentOnArray() { // Arrange From e0fa3799cb1755d14fd272ad1b70fbdf643bf4a5 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:12:38 -0300 Subject: [PATCH 16/22] ci(tests): remove development branch from the triggers of `build-binaries` workflow --- .github/workflows/build-binaries.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 7d0db768..69195cd4 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -4,8 +4,6 @@ on: push: tags: - 'binaries' - branches: - - '50-implement-automated-tests-pipeline' env: YRS_REPO: https://github.com/LSViana/y-crdt From 92fbbe9c16604cae611bb0c87519bf6d1ac52ecb Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:23:31 -0300 Subject: [PATCH 17/22] ci(tests): simplify the `ChannelSettings.NativeLib` definition --- YDotNet/Native/ChannelSettings.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/YDotNet/Native/ChannelSettings.cs b/YDotNet/Native/ChannelSettings.cs index 2dc2eebf..b138e7c9 100644 --- a/YDotNet/Native/ChannelSettings.cs +++ b/YDotNet/Native/ChannelSettings.cs @@ -2,9 +2,8 @@ namespace YDotNet.Native; internal static class ChannelSettings { -#if WINDOWS - public const string NativeLib = "yrs.dll"; -#else - public const string NativeLib = "libyrs.dylib"; -#endif + // The file extension doesn't need to be defined here because the `DllImport` attribute adds it automatically. + // + // More information: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/native-library-loading + public const string NativeLib = "yrs"; } From 2d79a7758a81107a70382ce5ef849095e635cc01 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:28:04 -0300 Subject: [PATCH 18/22] ci(tests): remove development branch from the triggers of `build` workflow --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a530682..a97f800a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,6 @@ on: push: branches: - 'main' - - '50-implement-automated-tests-pipeline' jobs: test: From deac54d5a460288e43c25de0f714793361a45ab0 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 11:37:30 -0300 Subject: [PATCH 19/22] fix(map): use pointer to Input cell instead of providing the value directly to fix tests on macOS --- YDotNet/Document/Types/Maps/Map.cs | 4 +++- YDotNet/Native/Types/Maps/MapChannel.cs | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/YDotNet/Document/Types/Maps/Map.cs b/YDotNet/Document/Types/Maps/Map.cs index 0b8dcc4b..644d0173 100644 --- a/YDotNet/Document/Types/Maps/Map.cs +++ b/YDotNet/Document/Types/Maps/Map.cs @@ -35,10 +35,12 @@ internal Map(nint handle) public void Insert(Transaction transaction, string key, Input input) { var keyHandle = MemoryWriter.WriteUtf8String(key); + var valueHandle = MemoryWriter.WriteStruct(input.InputNative); - MapChannel.Insert(Handle, transaction.Handle, keyHandle, input.InputNative); + MapChannel.Insert(Handle, transaction.Handle, keyHandle, valueHandle); MemoryWriter.Release(keyHandle); + MemoryWriter.Release(valueHandle); } /// diff --git a/YDotNet/Native/Types/Maps/MapChannel.cs b/YDotNet/Native/Types/Maps/MapChannel.cs index 8bbde4f9..244c8cbc 100644 --- a/YDotNet/Native/Types/Maps/MapChannel.cs +++ b/YDotNet/Native/Types/Maps/MapChannel.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using YDotNet.Native.Cells.Inputs; namespace YDotNet.Native.Types.Maps; @@ -8,7 +7,7 @@ internal static class MapChannel public delegate void ObserveCallback(nint state, nint eventHandle); [DllImport(ChannelSettings.NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ymap_insert")] - public static extern void Insert(nint map, nint transaction, nint key, InputNative inputNative); + public static extern void Insert(nint map, nint transaction, nint key, nint value); [DllImport(ChannelSettings.NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ymap_get")] public static extern nint Get(nint map, nint transaction, nint key); From 4d48b03b0c38cc0674b0c8cb62d165e8b670db34 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 12:07:21 -0300 Subject: [PATCH 20/22] feat(output): introduce the OutputType enum --- YDotNet/Document/Cells/OutputType.cs | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 YDotNet/Document/Cells/OutputType.cs diff --git a/YDotNet/Document/Cells/OutputType.cs b/YDotNet/Document/Cells/OutputType.cs new file mode 100644 index 00000000..86dc6d2b --- /dev/null +++ b/YDotNet/Document/Cells/OutputType.cs @@ -0,0 +1,89 @@ +namespace YDotNet.Document.Cells; + +/// +/// Represents the type of value stored inside of an cell. +/// +/// +/// This can be used as a safety check to only read the correct type of value from the cell. +/// +// TODO [LSViana] If this enum is used in `Input` later, rename it `CellType` and update the documentation. +public enum OutputType +{ + /// + /// Represents a cell with a value. + /// + Boolean = -8, + + /// + /// Represents a cell with a value. + /// + Double = -7, + + /// + /// Represents a cell with a value. + /// + Long = -6, + + /// + /// Represents a cell with a value. + /// + String = -5, + + /// + /// Represents a cell with a array value. + /// + Bytes = -4, + + /// + /// Represents a cell with an array of value. + /// + Collection = -3, + + /// + /// Represents a cell with a dictionary of value. + /// + Object = -2, + + /// + /// Represents a cell with the null value. + /// + Null = -1, + + /// + /// Represents a cell with the undefined value. + /// + Undefined = 0, + + /// + /// Represents a cell with an value. + /// + Array = 1, + + /// + /// Represents a cell with an value. + /// + Map = 2, + + /// + /// Represents a cell with an value. + /// + Text = 3, + + /// + /// Represents a cell with an value. + /// + XmlElement = 4, + + /// + /// Represents a cell with an value. + /// + XmlText = 5, + + // The following constant is commented because it's not exposed by `Input` or `Output`. + // XmlFragment = 6, + + /// + /// Represents a cell with an value. + /// + Doc = 7 +} From e71a1895efd1382340c94f86832f8e737e67233d Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 12:07:54 -0300 Subject: [PATCH 21/22] feat(output): introduce the Output.Type property --- YDotNet/Document/Cells/Output.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/YDotNet/Document/Cells/Output.cs b/YDotNet/Document/Cells/Output.cs index b538b303..58342f4b 100644 --- a/YDotNet/Document/Cells/Output.cs +++ b/YDotNet/Document/Cells/Output.cs @@ -33,6 +33,11 @@ internal Output(nint handle, bool disposable = false) OutputNative = handle == nint.Zero ? null : Marshal.PtrToStructure(handle); } + /// + /// Gets the value that represents the value stored in this cell. + /// + public OutputType Type => (OutputType) OutputNative.Value.Tag; + /// /// Gets the or null if this output cell contains a different type stored. /// From 5465b1b787a8f3b692116893e16f9b36b96c4e61 Mon Sep 17 00:00:00 2001 From: Lucas Viana Date: Sat, 14 Oct 2023 12:08:58 -0300 Subject: [PATCH 22/22] test(output): update tests for Map.Get() to check for Output.Type --- Tests/YDotNet.Tests.Unit/Maps/GetTests.cs | 195 +++++++++++++--------- 1 file changed, 117 insertions(+), 78 deletions(-) diff --git a/Tests/YDotNet.Tests.Unit/Maps/GetTests.cs b/Tests/YDotNet.Tests.Unit/Maps/GetTests.cs index 848d728f..37f84118 100644 --- a/Tests/YDotNet.Tests.Unit/Maps/GetTests.cs +++ b/Tests/YDotNet.Tests.Unit/Maps/GetTests.cs @@ -18,12 +18,14 @@ public void GetBoolean() ); // Act - var value1 = map.Get(transaction, "star-⭐").Boolean; - var value2 = map.Get(transaction, "moon-🌕").Boolean; + var value1 = map.Get(transaction, "star-⭐"); + var value2 = map.Get(transaction, "moon-🌕"); // Assert - Assert.That(value1, Is.True); - Assert.That(value2, Is.False); + Assert.That(value1.Boolean, Is.True); + Assert.That(value2.Boolean, Is.False); + Assert.That(value1.Type, Is.EqualTo(OutputType.Boolean)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -36,12 +38,14 @@ public void GetDouble() ); // Act - var value1 = map.Get(transaction, "value1").Double; - var value2 = map.Get(transaction, "value2").Double; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); // Assert - Assert.That(value1, Is.EqualTo(expected: 24.69)); - Assert.That(value2, Is.EqualTo(expected: -4.20)); + Assert.That(value1.Double, Is.EqualTo(expected: 24.69)); + Assert.That(value2.Double, Is.EqualTo(expected: -4.20)); + Assert.That(value1.Type, Is.EqualTo(OutputType.Double)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Double)); } [Test] @@ -54,12 +58,14 @@ public void GetLong() ); // Act - var value1 = map.Get(transaction, "value1").Long; - var value2 = map.Get(transaction, "value2").Long; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); // Assert - Assert.That(value1, Is.EqualTo(expected: 2469L)); - Assert.That(value2, Is.EqualTo(expected: -420L)); + Assert.That(value1.Long, Is.EqualTo(expected: 2469L)); + Assert.That(value2.Long, Is.EqualTo(expected: -420L)); + Assert.That(value1.Type, Is.EqualTo(OutputType.Long)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Long)); } [Test] @@ -72,12 +78,14 @@ public void GetString() ); // Act - var value1 = map.Get(transaction, "name").String; - var value2 = map.Get(transaction, "earth-🌍").String; + var value1 = map.Get(transaction, "name"); + var value2 = map.Get(transaction, "earth-🌍"); // Assert - Assert.That(value1, Is.EqualTo("Lucas")); - Assert.That(value2, Is.EqualTo("globe-🌐")); + Assert.That(value1.String, Is.EqualTo("Lucas")); + Assert.That(value2.String, Is.EqualTo("globe-🌐")); + Assert.That(value1.Type, Is.EqualTo(OutputType.String)); + Assert.That(value2.Type, Is.EqualTo(OutputType.String)); } [Test] @@ -90,12 +98,14 @@ public void GetBytes() ); // Act - var value1 = map.Get(transaction, "value1").Bytes; - var value2 = map.Get(transaction, "value2").Bytes; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); // Assert - Assert.That(value1, Is.EqualTo(new byte[] { 2, 4, 6, 9 })); - Assert.That(value2, Is.Null); + Assert.That(value1.Bytes, Is.EqualTo(new byte[] { 2, 4, 6, 9 })); + Assert.That(value2.Bytes, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Bytes)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -113,15 +123,18 @@ public void GetCollection() ); // Act - var value1 = map.Get(transaction, "value1").Collection; - var value2 = map.Get(transaction, "value2").Collection; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1Collection = value1.Collection; // Assert - //Assert.That(value1, Is.EqualTo(new byte[] { 2, 4, 6, 9 })); - Assert.That(value1.Length, Is.EqualTo(expected: 2)); - Assert.That(value1[0].Long, Is.EqualTo(expected: 2469)); - Assert.That(value1[1].Long, Is.EqualTo(expected: -420L)); - Assert.That(value2, Is.Null); + Assert.That(value1Collection, Is.Not.Null); + Assert.That(value1Collection.Length, Is.EqualTo(expected: 2)); + Assert.That(value1Collection[0].Long, Is.EqualTo(expected: 2469)); + Assert.That(value1Collection[1].Long, Is.EqualTo(expected: -420L)); + Assert.That(value2.Collection, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Collection)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -139,14 +152,18 @@ public void GetObject() ); // Act - var value1 = map.Get(transaction, "value1").Object; - var value2 = map.Get(transaction, "value2").Object; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1Object = value1.Object; // Assert - Assert.That(value1.Keys.Count, Is.EqualTo(expected: 2)); - Assert.That(value1["star-⭐"].Long, Is.EqualTo(expected: 2469)); - Assert.That(value1["moon-🌕"].Long, Is.EqualTo(expected: -420L)); - Assert.That(value2, Is.Null); + Assert.That(value1Object, Is.Not.Null); + Assert.That(value1Object.Count, Is.EqualTo(expected: 2)); + Assert.That(value1Object["star-⭐"].Long, Is.EqualTo(expected: 2469)); + Assert.That(value1Object["moon-🌕"].Long, Is.EqualTo(expected: -420L)); + Assert.That(value2.Object, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Object)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -160,14 +177,17 @@ public void GetNull() ); // Act - var value1 = map.Get(transaction, "value1").Null; - var value2 = map.Get(transaction, "value2").Null; - var value3 = map.Get(transaction, "value3").Null; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value3 = map.Get(transaction, "value3"); // Assert - Assert.That(value1, Is.True); - Assert.That(value2, Is.False); - Assert.That(value3, Is.False); + Assert.That(value1.Null, Is.True); + Assert.That(value2.Null, Is.False); + Assert.That(value3.Null, Is.False); + Assert.That(value1.Type, Is.EqualTo(OutputType.Null)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Undefined)); + Assert.That(value3.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -181,14 +201,17 @@ public void GetUndefined() ); // Act - var value1 = map.Get(transaction, "value1").Undefined; - var value2 = map.Get(transaction, "value2").Undefined; - var value3 = map.Get(transaction, "value3").Undefined; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value3 = map.Get(transaction, "value3"); // Assert - Assert.That(value1, Is.True); - Assert.That(value2, Is.False); - Assert.That(value3, Is.False); + Assert.That(value1.Undefined, Is.True); + Assert.That(value2.Undefined, Is.False); + Assert.That(value3.Undefined, Is.False); + Assert.That(value1.Type, Is.EqualTo(OutputType.Undefined)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Null)); + Assert.That(value3.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -201,13 +224,14 @@ public void GetText() ); // Act - var value1 = map.Get(transaction, "value1").Text; - var value2 = map.Get(transaction, "value2").Text; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); // Assert - Assert.That(value1, Is.Not.Null); - Assert.That(value1.String(transaction), Is.EqualTo("Lucas")); - Assert.That(value2, Is.Null); + Assert.That(value1.Text.String(transaction), Is.EqualTo("Lucas")); + Assert.That(value2.Text, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Text)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -225,13 +249,16 @@ public void GetArray() ); // Act - var value1 = map.Get(transaction, "value1").Array; - var value2 = map.Get(transaction, "value2").Array; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1Array = value1.Array; // Assert - Assert.That(value1, Is.Not.Null); - Assert.That(value1.Length, Is.EqualTo(expected: 2)); - Assert.That(value2, Is.Null); + Assert.That(value1Array, Is.Not.Null); + Assert.That(value1Array.Length, Is.EqualTo(expected: 2)); + Assert.That(value2.Array, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Array)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Null)); } [Test] @@ -249,15 +276,18 @@ public void GetMap() ); // Act - var value1 = map.Get(transaction, "value1").Map; - var value2 = map.Get(transaction, "value2").Map; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1Map = value1.Map; // Assert - Assert.That(value1, Is.Not.Null); - Assert.That(value1.Length(transaction), Is.EqualTo(expected: 2)); - Assert.That(value1.Get(transaction, "value1-1").Long, Is.EqualTo(expected: 2469L)); - Assert.That(value1.Get(transaction, "value1-2").Long, Is.EqualTo(expected: -420L)); - Assert.That(value2, Is.Null); + Assert.That(value1Map, Is.Not.Null); + Assert.That(value1Map.Length(transaction), Is.EqualTo(expected: 2)); + Assert.That(value1Map.Get(transaction, "value1-1").Long, Is.EqualTo(expected: 2469L)); + Assert.That(value1Map.Get(transaction, "value1-2").Long, Is.EqualTo(expected: -420L)); + Assert.That(value2.Map, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Map)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Boolean)); } [Test] @@ -270,13 +300,16 @@ public void GetXmlElement() ); // Act - var value1 = map.Get(transaction, "value1").XmlElement; - var value2 = map.Get(transaction, "value2").XmlElement; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1XmlElement = value1.XmlElement; // Assert - Assert.That(value1, Is.Not.Null); - Assert.That(value1.Tag, Is.EqualTo("person")); - Assert.That(value2, Is.Null); + Assert.That(value1XmlElement, Is.Not.Null); + Assert.That(value1XmlElement.Tag, Is.EqualTo("person")); + Assert.That(value2.XmlElement, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.XmlElement)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Null)); } [Test] @@ -289,13 +322,16 @@ public void GetXmlText() ); // Act - var value1 = map.Get(transaction, "value1").XmlText; - var value2 = map.Get(transaction, "value2").XmlText; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); + var value1XmlText = value1.XmlText; // Assert - Assert.That(value1, Is.Not.Null); - Assert.That(value1.Length(transaction), Is.EqualTo(expected: 5)); - Assert.That(value2, Is.Null); + Assert.That(value1XmlText, Is.Not.Null); + Assert.That(value1XmlText.Length(transaction), Is.EqualTo(expected: 5)); + Assert.That(value2.XmlText, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.XmlText)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Null)); } [Test] @@ -306,10 +342,11 @@ public void GetDoc() var (map, transaction) = ArrangeDoc(("sub-doc", Input.Doc(subDoc))); // Act - var subDocFromMap = map.Get(transaction, "sub-doc").Doc; + var value1 = map.Get(transaction, "sub-doc"); // Assert - Assert.That(subDoc.Id, Is.EqualTo(subDocFromMap.Id)); + Assert.That(subDoc.Id, Is.EqualTo(value1.Doc.Id)); + Assert.That(value1.Type, Is.EqualTo(OutputType.Doc)); } [Test] @@ -322,12 +359,14 @@ public void GetWrongTypeOnExistingKeyReturnsNull() ); // Act - var value1 = map.Get(transaction, "value1").Double; - var value2 = map.Get(transaction, "value2").Long; + var value1 = map.Get(transaction, "value1"); + var value2 = map.Get(transaction, "value2"); // Assert - Assert.That(value1, Is.Null); - Assert.That(value2, Is.Null); + Assert.That(value1.Double, Is.Null); + Assert.That(value2.Long, Is.Null); + Assert.That(value1.Type, Is.EqualTo(OutputType.Long)); + Assert.That(value2.Type, Is.EqualTo(OutputType.Double)); } [Test]