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

Яценко Ирина #235

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
55 changes: 55 additions & 0 deletions cs/HomeExercises/NumberValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Text.RegularExpressions;
using JetBrains.Annotations;

namespace HomeExercises
{
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);
}

[Pure]
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;
}
}
}
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.

23 changes: 23 additions & 0 deletions cs/HomeExercises/Person.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

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;
}
}
}
72 changes: 72 additions & 0 deletions cs/HomeExercises/Tests/NumberValidatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using NUnit.Framework;
using System;

namespace HomeExercises.Tests
{
[TestFixture(TestOf = typeof(NumberValidator))]
public class NumberValidatorTests
{
[Test]
public void NumberValidatorCtor_WhenPassValidArguments_ShouldNotThrows() =>
Assert.DoesNotThrow(() => new NumberValidator(1, 0, true));

private static TestCaseData[] ArgumentExceptionTestCases =
{
new TestCaseData(-1, 2, true).SetName("NegativePrecision"),
new TestCaseData(0, 2, true).SetName("PrecisionEqualToZero"),
new TestCaseData(1, -2, true).SetName("NegativeScale"),
new TestCaseData(2, 2, true).SetName("PrecisionIsEqualToTheScale"),
new TestCaseData(2, 3, true).SetName("ScaleIsMorePrecision")
};

[TestCaseSource(nameof(ArgumentExceptionTestCases))]
public void NumberValidatorCtor_WhenPassInvalidArguments_ShouldThrowArgumentException(int precision, int scale, bool onlyPositive) =>
Assert.Throws<ArgumentException>(() => new NumberValidator(precision, scale, onlyPositive));

private static TestCaseData[] InvalidArgumentTestCases =
{
new TestCaseData(3,2,true,"a.sd").Returns(false).SetName("LettersInsteadOfNumber"),
new TestCaseData(3,2,true,"2.!").Returns(false).SetName("SymbolsInsteadOfNumber"),
new TestCaseData(3,2,true,null!).Returns(false).SetName("PassNumberIsNull"),
new TestCaseData(3,2,true,"").Returns(false).SetName("PassNumberIsEmpty"),
new TestCaseData(3,2,true," ").Returns(false).SetName("OnlySpaceInNumber"),
new TestCaseData(3,2,true," 2").Returns(false).SetName("MultipleSpacesAndOneDigitInNumber"),
new TestCaseData(3,2,true,"2,.3").Returns(false).SetName("TwoSeparatorsArePassed"),
new TestCaseData(3,2,true,".").Returns(false).SetName("OnlySeparatorArePassed"),
new TestCaseData(3,2,true,"2 3").Returns(false).SetName("SeparatedBySpace"),
new TestCaseData(3,2,true,"-0.00").Returns(false).SetName("IntPartWithNegativeSignMoreThanPrecision"),
new TestCaseData(3,2,true,"+1.23").Returns(false).SetName("IntPartWithPositiveSignMoreThanPrecision"),
new TestCaseData(3,2,true,"0.000").Returns(false).SetName("FractionalPartMoreThanScale"),
new TestCaseData(3,2,true,"2%3").Returns(false).SetName("PercentSignInTheFormOfSeparator"),
new TestCaseData(3,2,true,"2$").Returns(false).SetName("AmpersandInTheFormOfSeparator"),
new TestCaseData(3,2,true,"#").Returns(false).SetName("OctothorpeInTheFormOfNumber"),
new TestCaseData(3,2,true,"2@3").Returns(false).SetName("CommercialAtSymbolInTheFormOfSeparator"),
new TestCaseData(3,2,true,"(2.3)").Returns(false).SetName("NumberInParentheses"),
new TestCaseData(3,2,true,"2;3").Returns(false).SetName("SemicolonInTheFormOfSeparator"),
new TestCaseData(3,2,true,"2/r").Returns(false).SetName("CarriageReturnInNumber"),
new TestCaseData(3,2,true,"/n3").Returns(false).SetName("NewLineInNumber"),
new TestCaseData(3,2,true,"/t3.4").Returns(false).SetName("TabInNumber"),
new TestCaseData(3,2,true,"3.4/b").Returns(false).SetName("BackSpaceInNumber"),
new TestCaseData(3,2,true,"3.47e+10").Returns(false).SetName("NumberInExponentialForm"),
new TestCaseData(3,2,true,"10^3").Returns(false).SetName("NumberInAPower"),
new TestCaseData(3,2,true,"11101010").Returns(false).SetName("BinaryNumberSystem"),
new TestCaseData(3,2,true,"0xEA").Returns(false).SetName("HexadecimalNumberSystem"),
};

private static TestCaseData[] ValidArgumentTestCases =
{
new TestCaseData(3,2,true,"2,3").Returns(true).SetName("CharactersAreSeparatedByComma"),
new TestCaseData(3,2,true,"0").Returns(true).SetName("FractionalPartIsMissing"),
new TestCaseData(3,2,true,"0.0").Returns(true).SetName("NumberIsValid"),
new TestCaseData(19,2,true,"9223372036854775807").Returns(true).SetName("LargeIntPartInNumber"),
new TestCaseData(27,25,true,"3.1415926535897932384626433").Returns(true).SetName("LargeFracPartInNumber"),
new TestCaseData(45,25,true,"9223372036854775807.1415926535897932384626433").Returns(true).SetName("LargeNumber")
};

[TestOf(nameof(NumberValidator.IsValidNumber))]
[TestCaseSource(nameof(InvalidArgumentTestCases))]
[TestCaseSource(nameof(ValidArgumentTestCases)), Repeat(2)]
public bool NumberValidation_ShouldBeCorrect(int precision, int scale, bool onlyPositive, string number) =>
new NumberValidator(precision, scale, onlyPositive).IsValidNumber(number);
}
}
57 changes: 57 additions & 0 deletions cs/HomeExercises/Tests/ObjectComparisonTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using FluentAssertions;
using NUnit.Framework;
using System;

namespace HomeExercises.Tests
{
[TestFixture(TestOf = typeof(TsarRegistry))]
public class ObjectComparisonTests
{
[Test]
[Description("Проверка текущего царя")]
[Category("ToRefactor")]
public void CheckCurrentTsar()
{
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, x => x
.Excluding(x => x.Id)
.Excluding(x => x.Parent!.Id));
}

[Test]
[Description("Альтернативное решение. Какие у него недостатки?")]
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));

// Какие недостатки у такого подхода?
// Данный подход делает класс труднорасширяемым, ведь при добавлении новых полей в класс Person
// нужно переписывать и метод сравнения для всех этих полей, так же новые поля cможет добавить
// другой разработчик, который не знает о таком методе сравнения, что приведет к новым, неотловоенным ошибкам -
// новые поля не будут сравниваться.
// В моем решении (CheckCurrentTsar), такой ошибки не возникнет и класс Person сможет без ошибок расширяться
// при условии, что сравниваться будут все поля, кроме Id (так же и их Parent), так как код написан не перебиранием
// всех полей для сравнения, а сравнением объекта в целом с исключением его Id.
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);
}
}
}
14 changes: 14 additions & 0 deletions cs/HomeExercises/TsarRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace HomeExercises
{
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));
}
}
}