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
57 changes: 57 additions & 0 deletions NullElements/NullElements.Tests/BasicListTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// <copyright file="BasicListTests.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace NullElements.Tests;

public class BasicListTests
{
private BasicList<int> list;

[SetUp]
public void Setup()
{
list = [];
}

[Test]
public void EmptyList_ShouldReturn_EmptyEnumerator()
{
Assert.That(list.SequenceEqual([]), Is.True);
}

[Test]
public void ListWithOneValue_ShouldReturn_EnumeratorWithOneElement()
{
int value = 42;
list.Add(value);
Assert.That(list.SequenceEqual([value]), Is.True);
}

[Test]
public void ListWithManyValues_ShouldReturn_EnumeratorWithSameValules()
{
var values = Enumerable.Range(0, 1000);
foreach (var value in values)
{
list.Add(value);
}

Assert.That(list.SequenceEqual(values), Is.True);
}

[Test]
public void ModifyingList_WhileIterating_ShouldThrow()
{
var values = Enumerable.Range(0, 1000);
foreach (var value in values)
{
list.Add(value);
}

var enumerator = list.GetEnumerator();
enumerator.MoveNext();
list.Add(1010101);
Assert.Throws<InvalidOperationException>(() => enumerator.MoveNext());
}
}
11 changes: 11 additions & 0 deletions NullElements/NullElements.Tests/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// <copyright file="GlobalSuppressions.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

// 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("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is project with tests")]
27 changes: 27 additions & 0 deletions NullElements/NullElements.Tests/NullElements.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../NullElements/NullElements.csproj" />
</ItemGroup>

</Project>
51 changes: 51 additions & 0 deletions NullElements/NullElements.Tests/NullElementsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// <copyright file="NullElementsTests.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace NullElements.Tests;

using System.Numerics;

public class NullElementsTests
{
[Test]
public void CountNullElements_IsCorrect_ForReferenceTypes()
{
BasicList<object> list = [null, 42, null, "abc", null];
Assert.That(list.CountNullElements(new ReferenceNullComparer<object>()), Is.EqualTo(3));
}

[Test]
public void CountNullElements_IsCorrect_ForNumberTypes()
{
BasicList<int> intList = [0, 1, 0, 2, 0, 100, 0, -100];
Assert.That(intList.CountNullElements(new NumberNullComparer<int>()), Is.EqualTo(4));

BasicList<float> floatList = [0, 0.1f, 0, 0.2f, 0, 100.100f, 0, -42.42f, 0.00000001f, 0];
Assert.That(floatList.CountNullElements(new NumberNullComparer<float>()), Is.EqualTo(5));
}

[Test]
public void CountNullElements_IsCorrect_ForStrings()
{
BasicList<string> list = [null, string.Empty, "abc", "def", null, "apple", string.Empty];
Assert.That(list.CountNullElements(new StringNullComparer()), Is.EqualTo(4));
}

private class ReferenceNullComparer<T> : INullComparer<T>
where T : class
{
public bool IsNull(T item) => item == null;
}

private class NumberNullComparer<T> : INullComparer<T>
where T : INumber<T>
{
public bool IsNull(T item) => T.IsZero(item);
}

private class StringNullComparer : INullComparer<string>
{
public bool IsNull(string item) => string.IsNullOrEmpty(item);
}
}
28 changes: 28 additions & 0 deletions NullElements/NullElements.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements", "NullElements\NullElements.csproj", "{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NullElements.Tests", "NullElements.Tests\NullElements.Tests.csproj", "{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F421D0B-CDA8-4ED3-BE33-4746FDB67D9D}.Release|Any CPU.Build.0 = Release|Any CPU
{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8B1FAB8-D74A-4D16-A79C-558BE5680BA7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
67 changes: 67 additions & 0 deletions NullElements/NullElements/BasicList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// <copyright file="BasicList.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace NullElements;

using System.Collections;

/// <summary>
/// Basic array-backed list.
/// </summary>
/// <typeparam name="T">Type of items.</typeparam>
public class BasicList<T> : IEnumerable<T>
{
private T[] values = new T[4];
private int count = 0;
private int version = 0;

/// <summary>
/// Initializes a new instance of the <see cref="BasicList{T}"/> class.
/// </summary>
public BasicList()
{
values = new T[4];
count = 0;
}

/// <summary>
/// Adds item to the list.
/// </summary>
/// <param name="item">Item to add.</param>
public void Add(T item)
{
if (count >= values.Length)
{
var newValues = new T[values.Length * 2];
Array.Copy(values, newValues, values.Length);
values = newValues;
}

values[count] = item;
count++;
version++;
}

/// <inheritdoc/>
public IEnumerator<T> GetEnumerator()
{
int lastVersion = version;

for (int i = 0; i < count; i++)
{
if (version != lastVersion)
{
throw new InvalidOperationException();
}

yield return values[i];
}
}

/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
19 changes: 19 additions & 0 deletions NullElements/NullElements/INullComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// <copyright file="INullComparer.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace NullElements;

/// <summary>
/// Interface that can be used to check whether object is null.
/// </summary>
/// <typeparam name="T">Type of object to check.</typeparam>
public interface INullComparer<T>
{
/// <summary>
/// Checks whether specified object is null.
/// </summary>
/// <param name="item">Object to check.</param>
/// <returns><see langword="true"/> if specified object is null, <see langword="false"/> otherwise.</returns>
public bool IsNull(T item);
}
21 changes: 21 additions & 0 deletions NullElements/NullElements/NullElements.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// <copyright file="NullElements.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace NullElements;

/// <summary>
/// Extension class that contains <c>CountNullElements</c> function.
/// </summary>
public static class NullElements
{
/// <summary>
/// Counts null elements in the list.
/// </summary>
/// <typeparam name="T">Type of objects.</typeparam>
/// <param name="list">List to count objects from.</param>
/// <param name="comparer">Comparer that checks whether object is null.</param>
/// <returns>Count of null elements.</returns>
public static int CountNullElements<T>(this BasicList<T> list, INullComparer<T> comparer)
=> list.Count(comparer.IsNull);
}
9 changes: 9 additions & 0 deletions NullElements/NullElements/NullElements.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
Loading