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
28 changes: 28 additions & 0 deletions Functions/Functions.Tests/Functions.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А где сами тесты?


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

<IsTestProject>true</IsTestProject>
</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="../Functions/Functions.csproj"/>
</ItemGroup>

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

namespace Functions.Tests;

public class FunctionsTests
{
private static readonly IEnumerable<int> NumberSequence = Enumerable.Range(0, 120);

[Test]
public void Map_ConvertsNumbersToString_SameAs_Select()
{
AssertMapBehavesSameAsSelect(NumberSequence, x => x.ToString());
}

[Test]
public void Map_ConvertsStringToNumbers_SameAs_Select()
{
AssertMapBehavesSameAsSelect(NumberSequence.Select(x => x.ToString()), int.Parse);
}

[Test]
public void Filter_SelectsNumbers_SameAs_Where()
{
AssertFilerBehavesSameAsWhere(NumberSequence, x => x % 2 == 0);
}

[Test]
public void Filter_SelectsStrings_SameAs_Where()
{
AssertFilerBehavesSameAsWhere(NumberSequence.Select(x => x.ToString()), x => x.Contains('4'));
}

[Test]
public void Fold_SumsNumbers_SameAsAggregate()
{
static int Sum(int x, int y) => x + y;

var aggregateResult = NumberSequence.Aggregate(Sum);
var foldResult = NumberSequence.Fold(Sum);

Assert.That(aggregateResult, Is.EqualTo(foldResult));
}

[Test]
public void Fold_AddsCharacters_SameAsAggregate()
{
static string Sum(string x, char y) => x + y;

var source = Enumerable.Range('0', '~' - '0').Select(x => (char)x);

var aggregateResult = source.Aggregate(string.Empty, Sum);
var foldResult = source.Fold(string.Empty, Sum);

Assert.That(aggregateResult, Is.EqualTo(foldResult));
}

[Test]
public void Fold_AddsCharactersAndReverses_SameAsAggregate()
{
static string Sum(string x, char y) => x + y;
static string Reverse(string s) => string.Concat(s.Reverse());

var source = Enumerable.Range('0', '~' - '0').Select(x => (char)x);

var aggregateResult = source.Aggregate(string.Empty, Sum, Reverse);
var foldResult = source.Fold(string.Empty, Sum, Reverse);

Assert.That(aggregateResult, Is.EqualTo(foldResult));
}

[Test]
public void Fold_Throws_OnEmptyCollection()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Range(0, 0).Fold((x, y) => x + y));
}

private static void AssertMapBehavesSameAsSelect<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, TResult> map)
{
var selectResult = source.Select(map);
var mapResult = source.Map(map);

Assert.That(selectResult.SequenceEqual(mapResult), Is.True);
}

private static void AssertFilerBehavesSameAsWhere<T>(IEnumerable<T> source, Func<T, bool> filter)
{
var whereResult = source.Where(filter);
var filterResult = source.Filter(filter);

Assert.That(whereResult.SequenceEqual(filterResult), Is.True);
}
}
11 changes: 11 additions & 0 deletions Functions/Functions.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 tests project")]
28 changes: 28 additions & 0 deletions Functions/Functions.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}") = "Functions", "Functions\Functions.csproj", "{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Functions.Tests", "Functions.Tests\Functions.Tests.csproj", "{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}"
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
{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E71A4E78-9D6C-4616-9F99-3B067B9B92F3}.Release|Any CPU.Build.0 = Release|Any CPU
{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1AAD59B-65F4-4F94-B960-24C9DE9C1BB8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
114 changes: 114 additions & 0 deletions Functions/Functions/Functions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// <copyright file="Functions.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace Functions;

/// <summary>
/// Utility class that contains Map, Filter and Fold functions.
/// </summary>
public static class Functions
{
/// <summary>
/// Maps all elements from <paramref name="source"/> to elements of type <typeparamref name="TResult"/> using <paramref name="map"/> function.
/// </summary>
/// <typeparam name="TSource">Source type.</typeparam>
/// <typeparam name="TResult">Type to map to.</typeparam>
/// <param name="source">Source to map.</param>
/// <param name="map">Function that maps <typeparamref name="TSource"/> to <typeparamref name="TResult"/>.</param>
/// <returns>Sequence of mapped elements.</returns>
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> map)
{
foreach (var item in source)
{
yield return map(item);
}
}

/// <summary>
/// Filters elements from <paramref name="source"/> that satisfy <paramref name="predicate"/>.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="source">Source to filter.</param>
/// <param name="predicate">Function that filters elements; if it returns <see langword="true"/>, adds element to the result, otherwise, doesn't.</param>
/// <returns>Sequence of filtered elements.</returns>
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
}
}

/// <summary>
/// Folds all elements in <paramref name="source"/> into one element.
/// </summary>
/// <typeparam name="T">Type of elements.</typeparam>
/// <param name="source">Source to fold.</param>
/// <param name="folder">Function that is used to accumulate result.</param>
/// <returns>Accumulated value.</returns>
/// <exception cref="InvalidOperationException">Sequence is empty.</exception>
public static T Fold<T>(this IEnumerable<T> source, Func<T, T, T> folder)
{
var enumerator = source.GetEnumerator();

if (!enumerator.MoveNext())
{
throw new InvalidOperationException("No elements in source");
}

var value = enumerator.Current;

while (enumerator.MoveNext())
{
value = folder(value, enumerator.Current);
}

return value;
}

/// <summary>
/// Folds all elements in <paramref name="source"/> into one element.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
/// <typeparam name="TResult">The type of accumulated value.</typeparam>
/// <param name="source">Source to fold.</param>
/// <param name="initialValue">Initial value of accumulated value.</param>
/// <param name="folder">Function that is used to accumulate result.</param>
/// <returns>Accumulated value.</returns>
public static TResult Fold<TSource, TResult>(this IEnumerable<TSource> source, TResult initialValue, Func<TResult, TSource, TResult> folder)
{
var value = initialValue;
foreach (var item in source)
{
value = folder(value, item);
}

return value;
}

/// <summary>
/// Folds all elements in <paramref name="source"/> into one element.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
/// <typeparam name="TIntermediate">The type of accumulated value.</typeparam>
/// <typeparam name="TResult">The result type.</typeparam>
/// <param name="source">Source to fold.</param>
/// <param name="initialValue">Initial value of accumulated value.</param>
/// <param name="folder">Function that is used to accumulate result.</param>
/// <param name="map">Function that is used to convert accumulated result.</param>
/// <returns>Converted accumulated value.</returns>
public static TResult Fold<TSource, TIntermediate, TResult>(this IEnumerable<TSource> source, TIntermediate initialValue, Func<TIntermediate, TSource, TIntermediate> folder, Func<TIntermediate, TResult> map)
{
var value = initialValue;
foreach (var item in source)
{
value = folder(value, item);
}

return map(value);
}
}
10 changes: 10 additions & 0 deletions Functions/Functions/Functions.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

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

</Project>