Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Бабин Георгий #240

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion cs/HomeExercises/HomeExercises.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace>HomeExercises</RootNamespace>
<AssemblyName>ObjectComparison</AssemblyName>
Expand Down
53 changes: 53 additions & 0 deletions cs/HomeExercises/NumberValidator/NumberValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Text.RegularExpressions;

namespace HomeExercises.NumberValidator
{
public class NumberValidator
{
private readonly Regex numberRegex;
private readonly bool onlyPositive;
private readonly int precision;
private readonly int scale;

public NumberValidator(int precision, int scale = 0, bool onlyPositive = false)
{
this.precision = precision;
this.scale = scale;
this.onlyPositive = onlyPositive;
if (precision <= 0)
throw new ArgumentException("precision must be a positive number");
if (scale < 0 || scale >= precision)
throw new ArgumentException("precision must be a non-negative number less or equal than precision");
numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase);
}

public bool IsValidNumber(string value)
{
// Проверяем соответствие входного значения формату N(m,k), в соответствии с правилом,
// описанным в Формате описи документов, направляемых в налоговый орган в электронном виде по телекоммуникационным каналам связи:
// Формат числового значения указывается в виде N(m.к), где m – максимальное количество знаков в числе, включая знак (для отрицательного числа),
// целую и дробную часть числа без разделяющей десятичной точки, k – максимальное число знаков дробной части числа.
// Если число знаков дробной части числа равно 0 (т.е. число целое), то формат числового значения имеет вид N(m).

if (string.IsNullOrEmpty(value))
return false;

var match = numberRegex.Match(value);
if (!match.Success)
return false;

// Знак и целая часть
var intPart = match.Groups[1].Value.Length + match.Groups[2].Value.Length;
// Дробная часть
var fracPart = match.Groups[4].Value.Length;

if (intPart + fracPart > precision || fracPart > scale)
return false;

if (onlyPositive && match.Groups[1].Value == "-")
return false;
return true;
}
}
}
65 changes: 65 additions & 0 deletions cs/HomeExercises/NumberValidator/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using FluentAssertions;
using NUnit.Framework;

namespace HomeExercises.NumberValidator
{
public class NumberValidatorTests
{
[TestCase(0, 2, TestName = "precision is zero")]
[TestCase(-1, 2, TestName = "precision is negative")]
[TestCase(1, -1, TestName = "scale is negative")]
[TestCase(1, 2, TestName = "scale is greater than precision")]
[TestCase(1, 1, TestName = "scale is equals precision")]
public void Constructor_Fails_OnIncorrectArguments(int precision, int scale)
{
Action a = () => { new NumberValidator(precision, scale); };
a.Should().Throw<ArgumentException>();
}

[Test]
public void Constructor_Success_WithOneArgument()
{
Action a = () => { new NumberValidator(1); };
a.Should().NotThrow();
}

[TestCase(3, 2, false, null, TestName = "value is null")]
[TestCase(3, 2, false, "", TestName = "value is empty")]
[TestCase(3, 2, false, " ", TestName = "value is space")]
[TestCase(3, 2, false, "+1..23", TestName = "value contains two separators")]
[TestCase(3, 2, false, "++0", TestName = "value contains two signs")]
[TestCase(3, 2, false, "1.2a", TestName = "value contains letters")]
[TestCase(3, 2, false, "+", TestName = "value only contains sign")]
[TestCase(3, 2, false, "0?0", TestName = "value separated by other symbol than dot or comma")]
[TestCase(3, 2, false, " 0", TestName = "value contains spaces before number")]
[TestCase(3, 2, false, "0 ", TestName = "value contains spaces after number")]
[TestCase(3, 2, false, "0.", TestName = "value hasn't contains numbers after separator")]
[TestCase(3, 2, false, ".0", TestName = "value hasn't contains numbers before separator")]
[TestCase(17, 2, false, "0.000", TestName = "value's fraction part length is greater than scale")]
[TestCase(5, 2, true, "-0.00", TestName = "negative sign when onlyPositive is true")]
[TestCase(3, 2, false, "+0.00", TestName = "intPart and fractPart together is greater than precision")]
public void IsValidNumber_ReturnsFalse_OnIncorrectArguments(int precision, int scale, bool onlyPositive, string value)

Choose a reason for hiding this comment

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

А если разрешены только положительные, а передаем отрицательное?

{
new NumberValidator(precision, scale, onlyPositive)
.IsValidNumber(value)
.Should()
.BeFalse();
}

[TestCase(17, 2, true, "0", TestName = "value without sign")]
[TestCase(17, 2, true, "+0", TestName = "value with positive sign")]
[TestCase(17, 2, false, "-0", TestName = "value with negative sign")]
[TestCase(17, 2, true, "0.0", TestName = "value with period as delimiter")]
[TestCase(17, 2, true, "0,0", TestName = "value with comma as delimiter")]
[TestCase(17, 2, true, "+0,0", TestName = "value with sign and delimiter")]
[TestCase(40, 20, true, "1234567890", TestName = "value with different numbers")]
public void IsValidNumber_ReturnsTrue_OnCorrectArguments(int precision, int scale, bool onlyPositive, string value)
{
new NumberValidator(precision, scale, onlyPositive)
.IsValidNumber(value)
.Should()
.BeTrue();
}
}
}
80 changes: 0 additions & 80 deletions cs/HomeExercises/NumberValidatorTests.cs

This file was deleted.

83 changes: 0 additions & 83 deletions cs/HomeExercises/ObjectComparison.cs

This file was deleted.

21 changes: 21 additions & 0 deletions cs/HomeExercises/Person.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace HomeExercises
{
public class Person
{
public static int IdCounter = 0;
public int Age, Height, Weight;
public string Name;
public Person? Parent;
public int Id;

public Person(string name, int age, int height, int weight, Person? parent)
{
Id = IdCounter++;
Name = name;
Age = age;
Height = height;
Weight = weight;
Parent = parent;
}
}
}
11 changes: 11 additions & 0 deletions cs/HomeExercises/TsarRegistry/TsarRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace HomeExercises.TsarRegistry
{
public class TsarRegistry
{
public static Person GetCurrentTsar()
{
return new Person("Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));
}
}
}
59 changes: 59 additions & 0 deletions cs/HomeExercises/TsarRegistry/TsarRegistryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using FluentAssertions;
using NUnit.Framework;

namespace HomeExercises.TsarRegistry
{
public class TsarRegistryTests
{
[Test]
[Description("Проверка текущего царя")]
public void GetCurrentTsar_ReturnsCorrectTsar()
{
var actualTsar = TsarRegistry.GetCurrentTsar();

var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));

actualTsar
.Should()
.BeEquivalentTo(expectedTsar, options => options
.Excluding(memberInfo => memberInfo.SelectedMemberInfo.DeclaringType == typeof(Person)
&& memberInfo.SelectedMemberInfo.Name == nameof(Person.Id)));
}

[Test]
[Description("Альтернативное решение. Какие у него недостатки?")]
/* 1. Непонятно из названия, что тест проверяет и какого результата ожидает.
* 2. При использовании такого Assert, при падении теста мы узнаем лишь, что мы получили False,
* без дополнительной информации об отличающихся полях и т.д.
* 3. При изменении класса Person нам всегда придётся соответственно менять и локальный метод
* AreEqual для класса Person.
* 4. Если каким-то образом произойдёт бесконечная рекурсия внутри класса Person, то данный способ привдёт к
* бесконечному проходу по нему. Проверка через FluentAssertions ограничена глубиной
* в 10 вхождений и это значение кастомизируемо.
* 5. При добавлении новых полей в класс Person, которые мы хотим включать в этот Equals - метод может
* сильно разрастись и его читаемость сильно снизится.
*/
public void CheckCurrentTsar_WithCustomEquality()
{
var actualTsar = TsarRegistry.GetCurrentTsar();
var expectedTsar = new Person("Ivan IV The Terrible", 54, 170, 70,
new Person("Vasili III of Russia", 28, 170, 60, null));

// Какие недостатки у такого подхода?
Assert.True(AreEqual(actualTsar, expectedTsar));
}

private bool AreEqual(Person? actual, Person? expected)
{
if (actual == expected) return true;
if (actual == null || expected == null) return false;
return
actual.Name == expected.Name
&& actual.Age == expected.Age
&& actual.Height == expected.Height
&& actual.Weight == expected.Weight
&& AreEqual(actual.Parent, expected.Parent);
}
}
}