-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit of Xledger.Collections and tests.
- Loading branch information
Showing
18 changed files
with
1,472 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Build .NET assemblies and test | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-dotnet@v4 | ||
with: | ||
dotnet-version: 8.0.x | ||
- run: dotnet restore | ||
- run: dotnet build --no-restore | ||
- run: dotnet test --no-build --verbosity normal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
name: Upload .NET package | ||
|
||
on: | ||
release: | ||
types: [created] | ||
|
||
jobs: | ||
release: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
packages: write | ||
contents: read | ||
env: | ||
version: ${{ github.event.release.tag_name }} | ||
steps: | ||
- name: Checkout source | ||
uses: actions/checkout@v4 | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v4 | ||
with: | ||
dotnet-version: 8.0.x | ||
- name: Build and pack | ||
run: | | ||
echo $version | ||
echo ${version:1} | ||
case $version in v*) true;; *) echo "Git tag must start with a 'v'"; false;; esac | ||
export version=${version:1} | ||
echo $version | ||
dotnet build /p:VersionPrefix=$version --configuration Release Xledger.Sql | ||
dotnet pack /p:VersionPrefix=$version --configuration Release Xledger.Sql | ||
# https://learn.microsoft.com/en-us/nuget/quickstart/create-and-publish-a-package-using-the-dotnet-cli | ||
- name: Push package to nuget | ||
run: dotnet nuget push Xledger.Sql/bin/Release/*.nupkg --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json | ||
# run: dotnet nuget push Xledger.Sql/bin/Release/*.nupkg --api-key "$NUGET_API_KEY" --source https://apiint.nugettest.org/v3/index.json | ||
env: | ||
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// This file is used by Code Analysis to maintain SuppressMessage | ||
// attributes that are applied to this project. | ||
// Project-level suppressions either have no target or are given | ||
// a specific target and scoped to a namespace, type, member, etc. | ||
|
||
using System.Diagnostics.CodeAnalysis; | ||
|
||
[assembly: SuppressMessage("Assertions", "xUnit2013:Do not use equality check to check for collection size.", Justification = "This is testing the entire collection interface.", Scope = "namespaceanddescendants", Target = "~N:Xledger.Collections.Test")] | ||
[assembly: SuppressMessage("Assertions", "xUnit2017:Do not use Contains() to check if a value exists in a collection", Justification = "This is testing the entire collection interface.", Scope = "namespaceanddescendants", Target = "~N:Xledger.Collections.Test")] | ||
[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Justification = "Polyfill.>", Scope = "namespace", Target = "~N:System.Runtime.CompilerServices")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
#if NETFRAMEWORK | ||
namespace System.Runtime.CompilerServices; | ||
/// <summary> | ||
/// Allows compiling <c>init</c> properties for .NET Framework. | ||
/// https://developercommunity.visualstudio.com/t/error-cs0518-predefined-type-systemruntimecompiler/1244809 | ||
/// https://stackoverflow.com/questions/64749385/predefined-type-system-runtime-compilerservices-isexternalinit-is-not-defined | ||
/// https://stackoverflow.com/questions/62648189/testing-c-sharp-9-0-in-vs2019-cs0518-isexternalinit-is-not-defined-or-imported | ||
/// </summary> | ||
[ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)] | ||
static class IsExternalInit { } | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
namespace Xledger.Collections.Test; | ||
|
||
public class TestImmArray { | ||
[Fact] | ||
public void TestEmpty() { | ||
var imm = ImmArray<string>.Empty; | ||
Assert.Empty(imm); | ||
Assert.Equal(0, imm.Count); | ||
Assert.False(imm.GetEnumerator().MoveNext()); | ||
Assert.Equal(imm.GetHashCode(), new ImmArray<string>().GetHashCode()); | ||
Assert.Equal(imm, new ImmArray<string>()); | ||
Assert.Equal(imm, imm); | ||
Assert.Equal(new ImmArray<string>(), imm); | ||
Assert.Equal("[]", imm.ToString()); | ||
|
||
Assert.False(imm.Equals((object)null)); | ||
Assert.False(imm.Equals((ImmArray<string>)null)); | ||
} | ||
|
||
[Fact] | ||
public void TestSingle() { | ||
var imm1 = new ImmArray<int>([1]); | ||
Assert.NotEmpty(imm1); | ||
Assert.Equal(1, imm1.Count); | ||
Assert.Equal(imm1, imm1); | ||
Assert.True(imm1.GetEnumerator().MoveNext()); | ||
Assert.Equal("[1]", imm1.ToString()); | ||
|
||
var imm2 = new ImmArray<int>([1]); | ||
Assert.Equal(imm1.GetHashCode(), imm2.GetHashCode()); | ||
Assert.Equal(imm1, imm2); | ||
Assert.Equal(imm2, imm1); | ||
} | ||
|
||
[Fact] | ||
public void TestCopy() { | ||
int[] arr = [1, 2, 3, 4, 5, 6]; | ||
var lst = new List<int>(arr); | ||
var imm = arr.ToImmArray(); | ||
|
||
int[] target1 = new int[100]; | ||
lst.CopyTo(target1); | ||
lst.CopyTo(target1, 12); | ||
lst.CopyTo(2, target1, 30, 3); | ||
|
||
int[] target2 = new int[100]; | ||
imm.CopyTo(target2); | ||
imm.CopyTo(target2, 12); | ||
imm.CopyTo(2, target2, 30, 3); | ||
|
||
Assert.Equal(target1, target2); | ||
|
||
target1 = lst.ToArray(); | ||
target1[0] = 99; | ||
Assert.NotEqual(lst, target1); | ||
|
||
target2 = imm.ToArray(); | ||
target2[0] = 99; | ||
Assert.NotEqual(lst, target2); | ||
} | ||
|
||
[Fact] | ||
public void TestContains() { | ||
int[] arr = [1, 2, 3, 1, 2, 2]; | ||
var lst = new List<int>(arr); | ||
var imm = arr.ToImmArray(); | ||
|
||
Assert.Equal(0, lst.IndexOf(1)); | ||
Assert.Equal(1, lst.IndexOf(2)); | ||
Assert.Equal(2, lst.IndexOf(3)); | ||
Assert.False(lst.Contains(9)); | ||
Assert.True(lst.Contains(3)); | ||
|
||
Assert.Equal(0, imm.IndexOf(1)); | ||
Assert.Equal(1, imm.IndexOf(2)); | ||
Assert.Equal(2, imm.IndexOf(3)); | ||
Assert.False(imm.Contains(9)); | ||
Assert.True(imm.Contains(3)); | ||
|
||
Assert.Equal(lst.IndexOf(-1), imm.IndexOf(-1)); | ||
Assert.Equal(lst.IndexOf(0), imm.IndexOf(0)); | ||
Assert.Equal(lst.IndexOf(1), imm.IndexOf(1)); | ||
Assert.Equal(lst.IndexOf(2), imm.IndexOf(2)); | ||
Assert.Equal(lst.IndexOf(3), imm.IndexOf(3)); | ||
Assert.Equal(lst.IndexOf(9), imm.IndexOf(9)); | ||
} | ||
|
||
[Fact] | ||
public void TestMutability() { | ||
int[] arr = [1, 2, 3, 4, 5, 6]; | ||
var lst = new List<int>(arr); | ||
Assert.Equal(arr, lst); | ||
|
||
ImmArray<int> foo = ImmArray.Of(1, 2, 3); | ||
|
||
// Are ImmArrays sane? | ||
var imm1 = arr.ToImmArray(); | ||
var imm2 = new ImmArray<int>(arr); | ||
var imm3 = lst.ToImmArray(); | ||
Assert.Equal(imm1, imm2); | ||
Assert.Equal(imm1, imm3); | ||
Assert.Equal(imm2, imm3); | ||
Assert.Equal(arr, imm1); | ||
Assert.Equal(arr, imm2); | ||
Assert.Equal(arr, imm3); | ||
Assert.Equal(lst, imm1); | ||
Assert.Equal(lst, imm2); | ||
Assert.Equal(lst, imm3); | ||
Assert.True(imm1.Equals(imm2)); | ||
Assert.True(imm1.Equals(imm3)); | ||
Assert.True(imm2.Equals(imm1)); | ||
Assert.True(imm2.Equals(imm3)); | ||
Assert.True(imm3.Equals(imm1)); | ||
Assert.True(imm3.Equals(imm2)); | ||
|
||
Assert.True(imm1.Equals((object)imm2)); | ||
Assert.True(imm1.Equals((object)imm3)); | ||
Assert.True(imm2.Equals((object)imm1)); | ||
Assert.True(imm2.Equals((object)imm3)); | ||
Assert.True(imm3.Equals((object)imm1)); | ||
Assert.True(imm3.Equals((object)imm2)); | ||
|
||
Assert.True(imm1 == imm2); | ||
Assert.True(imm2 == imm3); | ||
|
||
// Yes. | ||
// And now we mutate. | ||
|
||
// Mutating the shared backing array is visible. | ||
arr[0] = 0; | ||
Assert.Equal(1, imm1[0]); | ||
Assert.Equal(0, imm2[0]); | ||
Assert.Equal(1, imm3[0]); | ||
|
||
Assert.True(imm1.Equals(imm3)); | ||
Assert.True(imm3.Equals(imm1)); | ||
Assert.False(imm1.Equals(imm2)); | ||
Assert.False(imm2.Equals(imm1)); | ||
Assert.False(imm2.Equals(imm3)); | ||
Assert.False(imm3.Equals(imm2)); | ||
|
||
// Revert the bad change for sanity's sake. | ||
arr[0] = 1; | ||
Assert.True(imm1.Equals(imm2)); | ||
Assert.True(imm2.Equals(imm3)); | ||
|
||
// Mutating the list an ImmArray was created from is not visible. | ||
lst[1] = 0; | ||
Assert.Equal(2, imm1[1]); | ||
Assert.Equal(2, imm2[1]); | ||
Assert.Equal(2, imm3[1]); | ||
Assert.True(imm1.Equals(imm2)); | ||
Assert.True(imm1.Equals(imm3)); | ||
Assert.True(imm2.Equals(imm1)); | ||
Assert.True(imm2.Equals(imm3)); | ||
Assert.True(imm3.Equals(imm1)); | ||
Assert.True(imm3.Equals(imm2)); | ||
} | ||
|
||
[Fact] | ||
public void TestNoOps() { | ||
var imm = ImmArray.Of(7, 6, 5, 4); | ||
IList<int> ilist = imm; | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist[0] = 1); | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist.Add(1)); | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist.Clear()); | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist.Insert(1, 2)); | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist.Remove(4)); | ||
Assert.ThrowsAny<NotSupportedException>(() => ilist.RemoveAt(2)); | ||
Assert.ThrowsAny<NotSupportedException>(() => ((System.Collections.ICollection)ilist).CopyTo((Array)null, 0)); | ||
} | ||
|
||
[Fact] | ||
public void TestCompareTo() { | ||
Assert.True(null <= (ImmArray<string>)null); | ||
|
||
var imm1 = ImmArray.Of("foo", "bar", "baz"); | ||
Assert.True(null < imm1); | ||
Assert.True(null <= imm1); | ||
Assert.True(imm1 > null); | ||
Assert.True(imm1 >= null); | ||
|
||
var imm2 = ImmArray.Of("foo", "bar", "zip"); | ||
Assert.True(imm1 < imm2); | ||
Assert.True(imm1 <= imm2); | ||
Assert.True(imm2 > imm1); | ||
Assert.True(imm2 >= imm1); | ||
} | ||
|
||
|
||
[Fact] | ||
public void TestCompareToRecord() { | ||
var imm1 = ImmArray.Of( | ||
new Employee(1, "Bas Rutten", 10_000), | ||
new Employee(2, "Conor McGregor", 200_000), | ||
new Employee(3, "Frank Shamrock", 6_000)); | ||
Assert.True(null < imm1); | ||
Assert.True(null <= imm1); | ||
Assert.True(imm1 > null); | ||
Assert.True(imm1 >= null); | ||
|
||
var imm2 = ImmArray.Of( | ||
new Employee(1, "Bas Rutten", 10_000), | ||
new Employee(2, "Conor McGregor", 200_000), | ||
new Employee(3, "Frank Shamrock", 6_000)); | ||
Assert.ThrowsAny<ArgumentException>(() => imm1 < imm2); | ||
Assert.ThrowsAny<ArgumentException>(() => imm1 <= imm2); | ||
Assert.ThrowsAny<ArgumentException>(() => imm2 > imm1); | ||
Assert.ThrowsAny<ArgumentException>(() => imm2 >= imm1); | ||
} | ||
|
||
public record Employee(int Id, string Name, decimal Salary); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
namespace Xledger.Collections.Test; | ||
|
||
public class TestImmDict { | ||
[Fact] | ||
public void TestEmpty() { | ||
var imm = ImmDict<string, object>.Empty; | ||
Assert.Empty(imm); | ||
Assert.Equal(0, imm.Count); | ||
Assert.False(imm.GetEnumerator().MoveNext()); | ||
Assert.Equal(imm.GetHashCode(), new ImmDict<string, object>().GetHashCode()); | ||
Assert.Equal(imm, new ImmDict<string, object>()); | ||
Assert.Equal(imm, imm); | ||
Assert.Equal(new ImmDict<string, object>(), imm); | ||
Assert.Equal("[]", imm.ToString()); | ||
|
||
Assert.False(imm.Equals((object)null)); | ||
Assert.False(imm.Equals((ImmDict<string, object>)null)); | ||
} | ||
|
||
[Fact] | ||
public void TestSingle() { | ||
var imm1 = new ImmDict<int, object>(new Dictionary<int, object> { | ||
[1] = "foo", | ||
}); | ||
Assert.NotEmpty(imm1); | ||
Assert.Equal(1, imm1.Count); | ||
Assert.Equal(imm1, imm1); | ||
Assert.True(imm1.GetEnumerator().MoveNext()); | ||
Assert.Equal("[1: foo]", imm1.ToString()); | ||
|
||
var imm2 = new ImmDict<int, object>(new Dictionary<int, object> { | ||
[1] = "foo", | ||
}); | ||
Assert.Equal(imm1.GetHashCode(), imm2.GetHashCode()); | ||
Assert.Equal(imm1, imm2); | ||
Assert.Equal(imm2, imm1); | ||
} | ||
|
||
[Fact] | ||
public void TestContains() { | ||
var items = Enumerable.Range(-100, 1_000).ToDictionary(i => i, i => i * i); | ||
var dct = items; | ||
var imm = items.ToImmDict(); | ||
|
||
for (int i = -1000; i < 2000; ++i) { | ||
Assert.Equal(dct.ContainsKey(i), imm.ContainsKey(i)); | ||
if (dct.TryGetValue(i, out var dctValue)) { | ||
imm.TryGetValue(i, out var immValue); | ||
Assert.Equal(dctValue, immValue); | ||
} | ||
} | ||
} | ||
|
||
[Fact] | ||
public void TestNoOps() { | ||
var imm = ImmDict.Of((7, 6), (5, 4)); | ||
IDictionary<int, int> idict = imm; | ||
Assert.ThrowsAny<NotSupportedException>(() => idict.Add(1, 2)); | ||
Assert.ThrowsAny<NotSupportedException>(() => idict.Clear()); | ||
Assert.ThrowsAny<NotSupportedException>(() => idict.Remove(4)); | ||
Assert.ThrowsAny<NotSupportedException>(() => ((System.Collections.ICollection)idict).CopyTo((Array)null, 0)); | ||
} | ||
} | ||
|
||
|
Oops, something went wrong.