Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI Tests

on:
push:
branches: ['**'] # Test on push to all branches
pull_request:
branches: ['**'] # Test on PR to all branches (can remove type filter or adjust as needed)

jobs:
test: # Renamed job for clarity

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
name: NuGet Generation
name: Manual NuGet Publication

on:
push:
branches: [ master ]
pull_request:
types: [closed]
branches: [ master ]
workflow_dispatch: # Allows manual triggering from the GitHub Actions UI

jobs:
build:

publish:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 3.1.202
- name: Install dependencies
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Pack solution
run: dotnet pack --configuration Release -o out --no-restore
Expand Down
10 changes: 8 additions & 2 deletions IntSet.sln
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
# Visual Studio Version 17
VisualStudioVersion = 17.14.36109.1 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntSet", "src\IntSet\IntSet.csproj", "{AE63B664-F383-48F8-8EEE-70FCB2169AF7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntSet.Tests", "src\IntSet.Tests\IntSet.Tests.csproj", "{175DFCC8-9221-4D95-81D3-42C7252227D0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}"
ProjectSection(SolutionItems) = preProject
.github\workflows\ci-tests.yml = .github\workflows\ci-tests.yml
.github\workflows\manual-publish.yml = .github\workflows\manual-publish.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
155 changes: 155 additions & 0 deletions src/IntSet.Tests/IntSetTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Xunit;

Expand Down Expand Up @@ -450,5 +451,159 @@ public void CopyTo_MatchExactSpace_CopiesAll()
set.CopyTo(dst, 2);
Assert.Equal(new[] { 0, 0, 1, 2, 3 }, dst);
}

#region GetElementsInRange Tests

[Fact]
public void GetElementsInRange_EmptySet()
{
var set = new Kibnet.IntSet();
Assert.Equal(new List<int>(), set.GetElementsInRange(1, 10).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(10, 1).ToList());
}

[Fact]
public void GetElementsInRange_PopulatedSet_Ascending()
{
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20, 25 });
Assert.Equal(new List<int> { 1, 5, 10, 15, 20, 25 }, set.GetElementsInRange(1, 25).ToList());
Assert.Equal(new List<int> { 10, 15, 20 }, set.GetElementsInRange(10, 20).ToList());
Assert.Equal(new List<int> { 1, 5 }, set.GetElementsInRange(0, 5).ToList());
Assert.Equal(new List<int> { 20, 25 }, set.GetElementsInRange(20, 30).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(11, 14).ToList());
Assert.Equal(new List<int> { 1, 5, 10, 15, 20, 25 }, set.GetElementsInRange(0, 30).ToList());
Assert.Equal(new List<int> { 5, 10, 15 }, set.GetElementsInRange(5, 15).ToList());
}

[Fact]
public void GetElementsInRange_PopulatedSet_Descending()
{
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20, 25 });
Assert.Equal(new List<int> { 25, 20, 15, 10, 5, 1 }, set.GetElementsInRange(25, 1).ToList());
Assert.Equal(new List<int> { 20, 15, 10 }, set.GetElementsInRange(20, 10).ToList());
Assert.Equal(new List<int> { 25, 20 }, set.GetElementsInRange(30, 20).ToList());
Assert.Equal(new List<int> { 5, 1 }, set.GetElementsInRange(5, 0).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(14, 11).ToList());
Assert.Equal(new List<int> { 25, 20, 15, 10, 5, 1 }, set.GetElementsInRange(30, 0).ToList());
Assert.Equal(new List<int> { 15, 10, 5 }, set.GetElementsInRange(15, 5).ToList());
}

[Fact]
public void GetElementsInRange_PopulatedSet_SingleElementRange()
{
var set = new Kibnet.IntSet(new[] { 1, 5, 10, 15, 20 });
Assert.Equal(new List<int> { 10 }, set.GetElementsInRange(10, 10).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(11, 11).ToList());
Assert.Equal(new List<int> { 1 }, set.GetElementsInRange(1, 1).ToList());
Assert.Equal(new List<int> { 20 }, set.GetElementsInRange(20, 20).ToList());
}

[Fact]
public void GetElementsInRange_ComplexScenarios_Gaps()
{
var set = new Kibnet.IntSet(new[] { 1, 2, 3, 10, 11, 12, 20, 21, 22 });
Assert.Equal(new List<int> { 3, 10, 11 }, set.GetElementsInRange(3, 11).ToList());
Assert.Equal(new List<int> { 11, 10, 3 }, set.GetElementsInRange(11, 3).ToList());
Assert.Equal(new List<int> { 3, 10 }, set.GetElementsInRange(3, 10).ToList()); // Test case name was 'Asc across gap'
Assert.Equal(new List<int> { 10, 3 }, set.GetElementsInRange(10, 3).ToList()); // Test case name was 'Desc across gap'
}

[Fact]
public void GetElementsInRange_ComplexScenarios_NegativeNumbers()
{
var negSet = new Kibnet.IntSet(new[] { -10, -5, 0, 5, 10 });
Assert.Equal(new List<int> { -5, 0, 5 }, negSet.GetElementsInRange(-5, 5).ToList());
Assert.Equal(new List<int> { 5, 0, -5 }, negSet.GetElementsInRange(5, -5).ToList());
Assert.Equal(new List<int> { -10, -5 }, negSet.GetElementsInRange(-15, -5).ToList());
Assert.Equal(new List<int> { -5, -10 }, negSet.GetElementsInRange(-5, -15).ToList());
Assert.Equal(new List<int> { -10 }, negSet.GetElementsInRange(-10, -10).ToList());
Assert.Equal(new List<int> { 0 }, negSet.GetElementsInRange(0, 0).ToList());
Assert.Equal(new List<int> { 10 }, negSet.GetElementsInRange(10, 10).ToList());
}

[Fact]
public void GetElementsInRange_FullRange_MinMaxInt()
{
var set = new Kibnet.IntSet(new[] { int.MinValue, 0, int.MaxValue });
Assert.Equal(new List<int> { int.MinValue, 0, int.MaxValue }, set.GetElementsInRange(int.MinValue, int.MaxValue).ToList());
Assert.Equal(new List<int> { int.MaxValue, 0, int.MinValue }, set.GetElementsInRange(int.MaxValue, int.MinValue).ToList());
}

[Fact]
public void GetElementsInRange_LargeNumbers()
{
var set = new Kibnet.IntSet(new[] { 1000000, 2000000, 3000000, int.MaxValue - 5, int.MaxValue });
Assert.Equal(new List<int> { 1000000, 2000000 }, set.GetElementsInRange(1000000, 2500000).ToList());
Assert.Equal(new List<int> { 2000000, 1000000 }, set.GetElementsInRange(2500000, 1000000).ToList());
Assert.Equal(new List<int> { int.MaxValue - 5, int.MaxValue }, set.GetElementsInRange(int.MaxValue - 10, int.MaxValue).ToList());
Assert.Equal(new List<int> { int.MaxValue, int.MaxValue -5 }, set.GetElementsInRange(int.MaxValue, int.MaxValue - 10).ToList());
}

[Fact]
public void GetElementsInRange_RangeOutsidePopulatedData()
{
var set = new Kibnet.IntSet(new[] { 10, 20, 30 });
Assert.Equal(new List<int>(), set.GetElementsInRange(1, 5).ToList()); // Range before
Assert.Equal(new List<int>(), set.GetElementsInRange(5, 1).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(35, 40).ToList()); // Range after
Assert.Equal(new List<int>(), set.GetElementsInRange(40, 35).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(22, 28).ToList()); // Range between elements
Assert.Equal(new List<int>(), set.GetElementsInRange(28, 22).ToList());
}

[Fact]
public void GetElementsInRange_SingleElementInSet_MatchingRange()
{
var set = new Kibnet.IntSet(new[] { 42 });
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(42, 42).ToList());
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(40, 45).ToList());
Assert.Equal(new List<int> { 42 }, set.GetElementsInRange(45, 40).ToList());
}

[Fact]
public void GetElementsInRange_SingleElementInSet_NonMatchingRange()
{
var set = new Kibnet.IntSet(new[] { 42 });
Assert.Equal(new List<int>(), set.GetElementsInRange(10, 20).ToList());
Assert.Equal(new List<int>(), set.GetElementsInRange(50, 60).ToList());
}

// Test with a set that forces traversal through multiple levels of cards
[Fact]
public void GetElementsInRange_DeepTraversal()
{
// These numbers are chosen to likely span different i0, i1, i2, i3 blocks
var data = new List<int> { 0, 1, 2,
(1 << 8) + 5, (1 << 8) + 10, // Different i3
(1 << 14) + 7, (1 << 14) + 15, // Different i2
(1 << 20) + 3, (1 << 20) + 9, // Different i1
(1 << 26) + 100, (1 << 26) + 200, // Different i0
int.MaxValue - 10, int.MaxValue -1, int.MaxValue};
var set = new Kibnet.IntSet(data);

var expectedAsc = data.Where(x => x >= ((1 << 8) + 5) && x <= ((1 << 26) + 100)).OrderBy(x => x).ToList();
Assert.Equal(expectedAsc, set.GetElementsInRange((1 << 8) + 5, (1 << 26) + 100).ToList());

var expectedDesc = data.Where(x => x >= ((1 << 14) + 7) && x <= ((1 << 20) + 9)).OrderByDescending(x => x).ToList();
Assert.Equal(expectedDesc, set.GetElementsInRange((1 << 20) + 9, (1 << 14) + 7).ToList());

// Range including int.MaxValue
Assert.Equal(new List<int> { (1 << 26) + 200, int.MaxValue - 10, int.MaxValue -1, int.MaxValue }, set.GetElementsInRange((1 << 26) + 150, int.MaxValue).ToList());
Assert.Equal(new List<int> { int.MaxValue, int.MaxValue -1, int.MaxValue - 10, (1 << 26) + 200 }, set.GetElementsInRange(int.MaxValue, (1 << 26) + 150).ToList());

// Range including int.MinValue (if 0 is considered MinValue for positive range)
// If IntSet can store negative numbers, this test would be more relevant with actual int.MinValue
var dataWithNeg = new List<int>(data);
dataWithNeg.Add(int.MinValue);
dataWithNeg.Add(int.MinValue+1);
var setWithNeg = new Kibnet.IntSet(dataWithNeg);
dataWithNeg.Sort(); // For expected list

Assert.Equal(dataWithNeg.Where(x => x >= int.MinValue && x <= 2).ToList(), setWithNeg.GetElementsInRange(int.MinValue, 2).ToList());
Assert.Equal(dataWithNeg.Where(x => x >= int.MinValue && x <= 2).OrderByDescending(x=>x).ToList(), setWithNeg.GetElementsInRange(2, int.MinValue).ToList());

}

#endregion
}
}
133 changes: 133 additions & 0 deletions src/IntSet.Tests/StrykerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
var set = new IntSet();
Assert.True(set.Add(42));
Assert.True(set.Contains(42));
Assert.Equal(1, set.Count);

Check warning on line 16 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Single instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 16 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Single instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
Expand All @@ -22,7 +22,7 @@
var set = new IntSet();
set.Add(7);
Assert.False(set.Add(7));
Assert.Equal(1, set.Count);

Check warning on line 25 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Single instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 25 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Single instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
Expand All @@ -32,7 +32,7 @@
set.Add(100);
Assert.True(set.Remove(100));
Assert.False(set.Contains(100));
Assert.Equal(0, set.Count);

Check warning on line 35 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 35 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
Expand All @@ -40,7 +40,7 @@
{
var set = new IntSet();
Assert.False(set.Remove(5));
Assert.Equal(0, set.Count);

Check warning on line 43 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 43 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
Expand All @@ -56,7 +56,7 @@
var set = new IntSet(new[] { 1, 2, 3 });
set.Clear();
Assert.Empty(set);
Assert.Equal(0, set.Count);

Check warning on line 59 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 59 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
Expand Down Expand Up @@ -195,6 +195,87 @@
Assert.True(a.SetEquals(b));
}

[Fact]
public void CheckUniqueAndUnfoundElements_EmptySet_ReturnsCorrectCounts()
{
var set = new IntSet();
var result = set.IsSubsetOf(new[] { 1, 2 });
Assert.Equal(true, result);

Check warning on line 203 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for boolean conditions. Use Assert.True instead. (https://xunit.net/xunit.analyzers/rules/xUnit2004)

Check warning on line 203 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for boolean conditions. Use Assert.True instead. (https://xunit.net/xunit.analyzers/rules/xUnit2004)
}

[Fact]
public void CheckUniqueAndUnfoundElements_MixedElements_ReturnsCorrectCounts()
{
var set = new IntSet(new[] { 1, 2, 3 });
var result = set.IsSubsetOf(new[] { 2, 3, 4, 5 });
Assert.Equal(false, result);

Check warning on line 211 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for boolean conditions. Use Assert.False instead. (https://xunit.net/xunit.analyzers/rules/xUnit2004)

Check warning on line 211 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for boolean conditions. Use Assert.False instead. (https://xunit.net/xunit.analyzers/rules/xUnit2004)
}

[Fact]
public void ContainsAllElements_EmptyEnumerable_ReturnsTrue()
{
var set = new IntSet(new[] { 1, 2, 3 });
Assert.True(set.IsSupersetOf(Enumerable.Empty<int>()));
}

[Fact]
public void ContainsAllElements_MixedElements_ReturnsFalse()
{
var set = new IntSet(new[] { 1, 2, 3 });
Assert.False(set.IsSupersetOf(new[] { 2, 3, 4 }));
}

[Fact]
public void IntersectWithIntSet_EmptySet_DoesNotChange()
{
var set = new IntSet(new[] { 1, 2, 3 });
var other = new IntSet();
set.IntersectWith(other);
Assert.Empty(set);
}

[Fact]
public void IntersectWithIntSet_PartialIntersection_RetainsCommonElements()
{
var set = new IntSet(new[] { 1, 2, 3 });
var other = new IntSet(new[] { 2, 3, 4 });
set.IntersectWith(other);
Assert.Equal(new[] { 2, 3 }, set.OrderBy(x => x));
}

[Fact]
public void IntersectWithEnumerable_EmptyEnumerable_DoesNotChange()
{
var set = new IntSet(new[] { 1, 2, 3 });
set.IntersectWith(Enumerable.Empty<int>());
Assert.Equal(Enumerable.Empty<int>(), set.OrderBy(x => x));
}

[Fact]
public void IntersectWithEnumerable_PartialIntersection_RetainsCommonElements()
{
var set = new IntSet(new[] { 1, 2, 3 });
var other = new[] { 2, 3, 4 };
set.IntersectWith(other);
Assert.Equal(new[] { 2, 3 }, set.OrderBy(x => x));
}

[Fact]
public void Constructor_FullSet_CreatesFullSet()
{
var set = new IntSet(false, true);
Assert.Equal(uint.MaxValue, set.LongCount);
Assert.True(set.root.Full);
Assert.Null(set.root.Cards);
}

[Fact]
public void Constructor_EmptyCollection_DoesNotThrow()
{
var set = new IntSet(new int[0]);
Assert.Empty(set);
}

[Fact]
public void SetEquals_DifferentCounts_ReturnsFalse()
{
Expand Down Expand Up @@ -328,6 +409,58 @@
Assert.NotNull(set.root.Cards[0].Cards[0].Cards[0].Cards[0].Bytes);
}

[Fact]
public void Remove_CascadeRemoval_RemovesAllLevels()
{
var set = new IntSet();
set.Add(1000000); // This will create a deep card structure
Assert.True(set.Contains(1000000));
Assert.True(set.Remove(1000000));
Assert.False(set.Contains(1000000));
Assert.Equal(0, set.Count);

Check warning on line 420 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)

Check warning on line 420 in src/IntSet.Tests/StrykerTests.cs

View workflow job for this annotation

GitHub Actions / test

Do not use Assert.Equal() to check for collection size. Use Assert.Empty instead. (https://xunit.net/xunit.analyzers/rules/xUnit2013)
}

[Fact]
public void LargeSetOperations_ReturnsCorrectCounts()
{
var set = new IntSet();
for (int i = 0; i < 1000; i++)
{
set.Add(i * 2); // Add even numbers
}

var otherSet = new IntSet(Enumerable.Range(0, 1000));
Assert.Equal(1000, set.Count); // 1000 even numbers
Assert.Equal(1000, otherSet.Count); // 1000 numbers

set.IntersectWith(otherSet);
Assert.Equal(500, set.Count); // 500 even numbers
}

[Fact]
public void BoundaryValuesOperations_WorkCorrectly()
{
var set = new IntSet();

// Test with max int value
set.Add(int.MaxValue);
Assert.True(set.Contains(int.MaxValue));
Assert.True(set.Remove(int.MaxValue));
Assert.False(set.Contains(int.MaxValue));

// Test with min int value
set.Add(int.MinValue);
Assert.True(set.Contains(int.MinValue));
Assert.True(set.Remove(int.MinValue));
Assert.False(set.Contains(int.MinValue));

// Test with positive boundary
set.Add(16383); // Last index in first card
Assert.True(set.Contains(16383));
Assert.True(set.Remove(16383));
Assert.False(set.Contains(16383));
}

[Fact]
public void UnionWith_AddsAllElements()
{
Expand Down
Loading