Skip to content

Commit

Permalink
unit tests for add methods, AddMany checks, added TryAdd and AddRange…
Browse files Browse the repository at this point in the history
…, unification of ranges - ranges are inclusive on both sides
  • Loading branch information
k-wojcik committed Jul 24, 2024
1 parent 1ed5199 commit 8b517bb
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 56 deletions.
10 changes: 9 additions & 1 deletion src/Roaring.Net/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static unsafe class NativeMethods
public static extern IntPtr roaring_bitmap_create_with_capacity(uint capacity);

[DllImport("roaring")]
public static extern IntPtr roaring_bitmap_from_range(uint min, uint max, uint step);
public static extern IntPtr roaring_bitmap_from_range(ulong min, ulong max, uint step);

[DllImport("roaring")]
public static extern IntPtr roaring_bitmap_of_ptr(uint count, uint* values);
Expand Down Expand Up @@ -41,6 +41,14 @@ internal static unsafe class NativeMethods
[DllImport("roaring")]
public static extern void roaring_bitmap_add_many(IntPtr bitmap, uint count, uint* values);

// TODO roaring_bitmap_add_bulk

[DllImport("roaring")]
public static extern bool roaring_bitmap_add_checked(IntPtr bitmap, uint value);

[DllImport("roaring")]
public static extern void roaring_bitmap_add_range_closed(IntPtr bitmap, uint min, uint max);

[DllImport("roaring")]
public static extern void roaring_bitmap_remove(IntPtr bitmap, uint value);

Expand Down
47 changes: 40 additions & 7 deletions src/Roaring.Net/Roaring32/Roaring32Bitmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public unsafe class Roaring32Bitmap : IDisposable

public static Roaring32Bitmap FromRange(uint min, uint max, uint step = 1)
{
if (min >= max)
if (min > max)
{
throw new ArgumentOutOfRangeException(nameof(min), min, "The minimum value cannot be greater than or equal to the maximum value.");
}
Expand All @@ -33,11 +33,27 @@ public static Roaring32Bitmap FromRange(uint min, uint max, uint step = 1)
{
throw new ArgumentOutOfRangeException(nameof(step), step, "The step cannot be equal to 0.");
}


if (min == uint.MaxValue)
{
return FromValues(new[]{ min });
}

if (max != uint.MaxValue)
{
max += 1;
}
else
{
var bitmap = new Roaring32Bitmap(NativeMethods.roaring_bitmap_from_range(min, max, step));
bitmap.Add(uint.MaxValue);
return bitmap;
}

return new(NativeMethods.roaring_bitmap_from_range(min, max, step));
}

public static Roaring32Bitmap FromValues(params uint[] values) => FromValues(values, 0U, (uint)values.Length);
public static Roaring32Bitmap FromValues(uint[] values) => FromValues(values, 0U, (uint)values.Length);

public static Roaring32Bitmap FromValues(uint[] values, uint offset, uint count)
{
Expand Down Expand Up @@ -73,16 +89,33 @@ public void Dispose()

//List operations

public void Add(uint value)
=> NativeMethods.roaring_bitmap_add(_pointer, value);
public void Add(uint value) => NativeMethods.roaring_bitmap_add(_pointer, value);

public void AddMany(params uint[] values)
=> AddMany(values, 0, (uint)values.Length);
public void AddMany(uint[] values) => AddMany(values, 0, (uint)values.Length);

public void AddMany(uint[] values, uint offset, uint count)
{
if (values.Length < offset + count)
{
throw new ArgumentOutOfRangeException(nameof(offset), offset, "Offset with count cannot be greater than the number of input elements.");
}

fixed (uint* valuePtr = values)
{
NativeMethods.roaring_bitmap_add_many(_pointer, count, valuePtr + offset);
}
}

public bool TryAdd(uint value) => NativeMethods.roaring_bitmap_add_checked(_pointer, value);

public void AddRange(uint min, uint max)
{
if (min > max)
{
throw new ArgumentOutOfRangeException(nameof(min), min, "The minimum value cannot be greater than or equal to the maximum value.");
}

NativeMethods.roaring_bitmap_add_range_closed(_pointer, min, max);
}

public void Remove(uint value)
Expand Down
222 changes: 222 additions & 0 deletions test/Roaring.Net.Test/Roaring32/AddTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
using System;
using System.Linq;
using Xunit;

namespace Roaring.Test.Roaring32;

public class AddTests
{
[Fact]
public void Add_EmptyBitmap_AddsValueToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act
testObject.Bitmap.Add(10);

// Assert
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Equal(1U, testObject.Bitmap.Cardinality);
}

[Fact]
public void Add_BitmapWithValues_AddsValueToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
Assert.DoesNotContain(testObject.Bitmap.Values, value=> value == 10U);

// Act
testObject.Bitmap.Add(10);

// Assert
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Equal(cardinality + 1, testObject.Bitmap.Cardinality);
}

[Fact]
public void Add_AddedValueExistsInBitmap_ValueIsNotAddedToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
var addedValue = testObject.Bitmap.Values.ToList()[2];

// Act
testObject.Bitmap.Add(addedValue);

// Assert
Assert.Single(testObject.Bitmap.Values, value=> value == addedValue);
Assert.Equal(cardinality, testObject.Bitmap.Cardinality);
}

[Fact]
public void AddMany_EmptyBitmap_AddsValuesToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act
testObject.Bitmap.AddMany([10, 11]);

// Assert
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Single(testObject.Bitmap.Values, value=> value == 11U);
Assert.Equal(2U, testObject.Bitmap.Cardinality);
}

[Fact]
public void AddMany_BitmapWithValues_AddsValueToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
Assert.DoesNotContain(testObject.Bitmap.Values, value=> value == 10U);
Assert.DoesNotContain(testObject.Bitmap.Values, value=> value == 11U);

// Act
testObject.Bitmap.AddMany([10, 11]);

// Assert
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Single(testObject.Bitmap.Values, value=> value == 11U);
Assert.Equal(cardinality + 2, testObject.Bitmap.Cardinality);
}

[Fact]
public void AddMany_AddedValueExistsInBitmap_ValueIsNotAddedToBitmap()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
var addedValues = testObject.Bitmap.Values.ToList()[2..10].ToArray();

// Act
testObject.Bitmap.AddMany(addedValues);

// Assert
Assert.Contains(testObject.Bitmap.Values, value => addedValues.Contains(value));
Assert.Equal(cardinality, testObject.Bitmap.Cardinality);
}


[Theory]
[InlineData(new uint[] {0, 1, 2, 3, 4}, 0, 5)]
[InlineData(new uint[] {0, 1, 2, 3, 4},4, 1)]
[InlineData(new uint[] {0, 1, 2, 3, 4},3, 2)]
[InlineData(new uint[] {0, 1, 2, 3, 4},1, 2)]
[InlineData(new uint[] {0, 1, 2, 3, 4},0, 0)]
public void AddMany_WithCorrectOffsetAndCount_AddsValuesToBitmap(uint[] values, uint offset, uint count)
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act
testObject.Bitmap.AddMany(values, offset, count);

// Assert
Assert.Equal(values[(int)offset..((int)offset + (int)count)], testObject.Bitmap.Values.ToArray());
}

[Theory]
[InlineData(new uint[] {0, 1, 2, 3, 4},0, 6)]
[InlineData(new uint[] {0, 1, 2, 3, 4},5, 1)]
[InlineData(new uint[] {0, 1, 2, 3, 4},4, 2)]
public void AddMany_OffsetAndCountOutOfAllowedRange_ThrowsArgumentOutOfRangeException(uint[] values, uint offset, uint count)
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act && Assert
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
testObject.Bitmap.AddMany(values, offset, count);
});
}

[Fact]
public void TryAdd_EmptyBitmap_AddsValueToBitmapAndReturnsTrue()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act
var actual = testObject.Bitmap.TryAdd(10);

// Assert
Assert.True(actual);
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Equal(1U, testObject.Bitmap.Cardinality);
}

[Fact]
public void TryAdd_BitmapWithValues_AddsValueToBitmapAndReturnsTrue()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
Assert.DoesNotContain(testObject.Bitmap.Values, value=> value == 10U);

// Act
var actual = testObject.Bitmap.TryAdd(10);

// Assert
Assert.True(actual);
Assert.Single(testObject.Bitmap.Values, value=> value == 10U);
Assert.Equal(cardinality + 1, testObject.Bitmap.Cardinality);
}

[Fact]
public void TryAdd_AddedValueExistsInBitmap_ValueIsNotAddedToBitmapAndReturnsFalse()
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetDefault();
var cardinality = testObject.Bitmap.Cardinality;
var addedValue = testObject.Bitmap.Values.ToList()[2];

// Act
var actual = testObject.Bitmap.TryAdd(addedValue);

// Assert
Assert.False(actual);
Assert.Single(testObject.Bitmap.Values, value=> value == addedValue);
Assert.Equal(cardinality, testObject.Bitmap.Cardinality);
}

[Theory]
[InlineData(1, 0)]
[InlineData(10, 5)]
public void AddRange_ArgumentsOutOfAllowedRange_ThrowsArgumentOutOfRangeException(uint min, uint max)
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act && Assert
Assert.Throws<ArgumentOutOfRangeException>(() => testObject.Bitmap.AddRange(min, max));
}

[Theory]
[InlineData(0, 0)]
[InlineData(1, 1)]
[InlineData(0, 10)]
[InlineData(uint.MaxValue - 1, uint.MaxValue)]
[InlineData(uint.MaxValue, uint.MaxValue)]
public void AddRange_CorrectRange_BitmapContainsExpectedValues(uint min, uint max)
{
// Arrange
using var testObject = Roaring32BitmapTestObject.GetEmpty();

// Act
testObject.Bitmap.AddRange(min, max);

// Assert
var expected = Enumerable.Range((int)min, (int)(max - min + 1)) // 0..10
.Select(x=> (uint)x)
.ToList();
var actual = testObject.Bitmap.Values.ToList();

Assert.Equal(expected, actual);
}
}
18 changes: 12 additions & 6 deletions test/Roaring.Net.Test/Roaring32/CreationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,30 @@ public void Ctor_WithCapacity_CreatesBitmapWithPriviedCapacity()
}

[Theory]
[InlineData(0, 0, 1)]
[InlineData(1, 0, 1)]
[InlineData(10, 5, 1)]
[InlineData(1, 10, 0)]
public void FromRange_ArgumentsOutOfAllowedRange_ThrowsArgumentOutOfRangeException(uint min, uint max, uint step)
{
// Act && Assert
Assert.Throws<ArgumentOutOfRangeException>(() => Roaring32Bitmap.FromRange(min, max, step));
}

[Fact]
public void FromRange_CorrectRange_BitmapContainsExpectedValues()
[Theory]
[InlineData(0, 0, 1)]
[InlineData(1, 1, 1)]
[InlineData(10, 100, 2)]
[InlineData(33, 333, 33)]
[InlineData(uint.MaxValue - 1, uint.MaxValue, 1)]
[InlineData(uint.MaxValue, uint.MaxValue, 1)]
public void FromRange_CorrectRange_BitmapContainsExpectedValues(uint min, uint max, uint step)
{
// Act
using var uut = Roaring32Bitmap.FromRange(10, 100, 2);
using var uut = Roaring32Bitmap.FromRange(min, max, step);

// Assert
var expected = Enumerable.Range(0, 45)
.Select(x=> 10 + (uint)x * 2)
var expected = Enumerable.Range(0, (int)Math.Ceiling((max - min + 1) / (double)step))
.Select(x=> min + (uint)x * step)
.ToList();
var actual = uut.Values.ToList();

Expand Down
Loading

0 comments on commit 8b517bb

Please sign in to comment.