From 30e77a860cf66c8b1001ea8b738a39d00357f340 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Sun, 26 Nov 2023 00:58:28 +0500 Subject: [PATCH 1/6] update project --- cs/HomeExercises/HomeExercises.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cs/HomeExercises/HomeExercises.csproj b/cs/HomeExercises/HomeExercises.csproj index ede81aec..6103ad76 100644 --- a/cs/HomeExercises/HomeExercises.csproj +++ b/cs/HomeExercises/HomeExercises.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net7.0 false HomeExercises ObjectComparison @@ -10,11 +10,11 @@ - - - - - + + + + + \ No newline at end of file From 699fa253c5bebbca2db38216efaa6d4523d97b3a Mon Sep 17 00:00:00 2001 From: PavelUd Date: Sun, 26 Nov 2023 01:44:27 +0500 Subject: [PATCH 2/6] modified tests in ObjectComparison --- cs/HomeExercises/ObjectComparison.cs | 143 ++++++++++++++------------- 1 file changed, 73 insertions(+), 70 deletions(-) diff --git a/cs/HomeExercises/ObjectComparison.cs b/cs/HomeExercises/ObjectComparison.cs index 44d9aed4..c5b966b7 100644 --- a/cs/HomeExercises/ObjectComparison.cs +++ b/cs/HomeExercises/ObjectComparison.cs @@ -3,81 +3,84 @@ namespace HomeExercises { - public class ObjectComparison - { - [Test] - [Description("Проверка текущего царя")] - [Category("ToRefactor")] - public void CheckCurrentTsar() - { - var actualTsar = TsarRegistry.GetCurrentTsar(); + public class ObjectComparison + { + private const int MAX_TREE_HEIGHT = 75; + [SetUp] + public void SetUp() + { + AssertionOptions.FormattingOptions.MaxDepth = MAX_TREE_HEIGHT; + } + [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)); + 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(tsar => tsar.DeclaringType.Equals(typeof(Person)) && tsar.Name.Equals(nameof(Person.Id)))); + } - // Перепишите код на использование Fluent Assertions. - Assert.AreEqual(actualTsar.Name, expectedTsar.Name); - Assert.AreEqual(actualTsar.Age, expectedTsar.Age); - Assert.AreEqual(actualTsar.Height, expectedTsar.Height); - Assert.AreEqual(actualTsar.Weight, expectedTsar.Weight); + [Test] + [Description("Альтернативное решение. Какие у него недостатки?")] + // размыта зона ответственности этого теста(если тест упадет из-за неверного поля,то не понятно из-за какого конкретно) + //Очень негибкий тест: + // Любое изменение существующих полей класса Person приводит к ошибке + // При любом добавлении новых полей нужно снова изменять этот тест + // + 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.AreEqual(expectedTsar.Parent!.Name, actualTsar.Parent!.Name); - Assert.AreEqual(expectedTsar.Parent.Age, actualTsar.Parent.Age); - Assert.AreEqual(expectedTsar.Parent.Height, actualTsar.Parent.Height); - Assert.AreEqual(expectedTsar.Parent.Parent, actualTsar.Parent.Parent); - } + // Какие недостатки у такого подхода? + Assert.True(AreEqual(actualTsar, expectedTsar)); + } - [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)); + 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); + } + } - // Какие недостатки у такого подхода? - Assert.True(AreEqual(actualTsar, expectedTsar)); - } + 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)); + } + } - 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); - } - } + public class Person + { + public static int IdCounter = 0; + public int Age, Height, Weight; + public string Name; + public Person? Parent; + public int Id; - 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)); - } - } - - 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; - } - } + public Person(string name, int age, int height, int weight, Person? parent) + { + Id = IdCounter++; + Name = name; + Age = age; + Height = height; + Weight = weight; + Parent = parent; + } + } } \ No newline at end of file From 2a1c348396a75892169886a8c745585db0fe2e08 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Sun, 26 Nov 2023 01:53:26 +0500 Subject: [PATCH 3/6] added and refactored tests in NumberValidatorTests --- cs/HomeExercises/NumberValidatorTests.cs | 186 ++++++++++++++--------- 1 file changed, 114 insertions(+), 72 deletions(-) diff --git a/cs/HomeExercises/NumberValidatorTests.cs b/cs/HomeExercises/NumberValidatorTests.cs index a2878113..5f5f17a1 100644 --- a/cs/HomeExercises/NumberValidatorTests.cs +++ b/cs/HomeExercises/NumberValidatorTests.cs @@ -5,76 +5,118 @@ namespace HomeExercises { - public class NumberValidatorTests - { - [Test] - public void Test() - { - Assert.Throws(() => new NumberValidator(-1, 2, true)); - Assert.DoesNotThrow(() => new NumberValidator(1, 0, true)); - Assert.Throws(() => new NumberValidator(-1, 2, false)); - Assert.DoesNotThrow(() => new NumberValidator(1, 0, true)); - - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("00.00")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-0.00")); - Assert.IsTrue(new NumberValidator(17, 2, true).IsValidNumber("0.0")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+0.00")); - Assert.IsTrue(new NumberValidator(4, 2, true).IsValidNumber("+1.23")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("+1.23")); - Assert.IsFalse(new NumberValidator(17, 2, true).IsValidNumber("0.000")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("-1.23")); - Assert.IsFalse(new NumberValidator(3, 2, true).IsValidNumber("a.sd")); - } - } - - 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; - } - } + [TestFixture] + public class NumberValidatorTests + { + + + [TestCase(-1, 2, true, TestName = "Negative Precision")] + [TestCase(2, -1, true, TestName = "Negative Scale")] + [TestCase(2, 3, true, TestName = "Precision < Scale")] + [TestCase(3, 3, true, TestName = "Precision = Scale")] + [TestCase(2, 3, true, TestName = "Precision = Zero")] + [TestCase(null, 3, true, TestName = "Precision is null")] + [TestCase(null, 3, true, TestName = "Scale is null")] + + public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precision, int scale, bool onlyPositive) + { + Action createNumberValidator = () => new NumberValidator(precision, scale, onlyPositive); + createNumberValidator.Should().Throw(); + } + + + + [TestCase(2, 0, " ", TestName = "number is someWhiteSpaces")] + [TestCase(2, 0, "string.string", TestName = "number is word")] + [TestCase(2, 0, "", TestName = "Number is Empty")] + [TestCase(2, 0, null, TestName = "Number Is Null")] + [TestCase(4, 0, "-+20", TestName = "more than one sign")] + [TestCase(4, 2, "20?00", TestName = "invalid separator. It isn't '.' or ','")] + [TestCase(4, 2, "20......00", TestName = "more than one separators in a row")] + [TestCase(2, 0, "20.", TestName = "Number with separator, but Empty fractional part")] + [TestCase(1, 0, "+", TestName = "number with sign but without fractional and integer parts")] + [TestCase(4, 1, "2.0.0.0", TestName = "more than one separator")] + [TestCase(4, 1, "D75", TestName = "number in is not the correct number system")] + [TestCase(4, 1, " 200 ", TestName = "number surrounded by spaces")] + + public void IsValidNumber_IfInvalidTypeValue_ShouldBeFalse(int precision, int scale, string number, bool onlyPositive = true) + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + numberValidator.IsValidNumber(number).Should().BeFalse(); + } + + + + [TestCase(5, 2, true, "-20.00", TestName = "incorrect sign")] + [TestCase(3, 1, true, "20.00", TestName = "precision < length number")] + [TestCase(4, 0, true, "20.00", TestName = "scale < length fractional part")] + + public void IsValidNumber_IfIncorrectArgs_ShouldBeFalse(int precision, int scale, bool onlyPositive, string number) + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + numberValidator.IsValidNumber(number).Should().BeFalse(); + } + + + [TestCase(21, 1, true, "20.0", TestName = "precision > number Length")] + [TestCase(3, 2, true, "20.0", TestName = "scale > fractional part")] + [TestCase(3, 1, true, "20,0", TestName = "used ','")] + [TestCase(4, 1, true, "+20.0", TestName = "use '+' before number")] + [TestCase(4, 1, false, "+20.0", TestName = "use '+' without onlyPositive flag")] + [TestCase(2, 0, false, "20", TestName = "number without fractional part")] + [Test] + public void IsValidNumber_IfCorrectValue_ShouldBeTrue(int precision, int scale, bool onlyPositive, string number) + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + numberValidator.IsValidNumber(number).Should().BeTrue(); + } + } + + 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("scale 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; + } + } } \ No newline at end of file From 65d0b59730712b5873fe894f8e24ebecfda1b2bd Mon Sep 17 00:00:00 2001 From: PavelUd Date: Thu, 30 Nov 2023 00:39:02 +0500 Subject: [PATCH 4/6] refactored file --- cs/HomeExercises/ObjectComparison.cs | 149 ++++++++++++++------------- 1 file changed, 76 insertions(+), 73 deletions(-) diff --git a/cs/HomeExercises/ObjectComparison.cs b/cs/HomeExercises/ObjectComparison.cs index c5b966b7..1352b668 100644 --- a/cs/HomeExercises/ObjectComparison.cs +++ b/cs/HomeExercises/ObjectComparison.cs @@ -3,84 +3,87 @@ namespace HomeExercises { - public class ObjectComparison - { - private const int MAX_TREE_HEIGHT = 75; - [SetUp] - public void SetUp() - { - AssertionOptions.FormattingOptions.MaxDepth = MAX_TREE_HEIGHT; - } - [Test] - [Description("Проверка текущего царя")] - [Category("ToRefactor")] - public void CheckCurrentTsar() - { - var actualTsar = TsarRegistry.GetCurrentTsar(); + public class ObjectComparison + { + private const int MaxTreeHeight = 75; - 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(tsar => tsar.DeclaringType.Equals(typeof(Person)) && tsar.Name.Equals(nameof(Person.Id)))); - } + [SetUp] + public void SetUp() + { + AssertionOptions.FormattingOptions.MaxDepth = MaxTreeHeight; + } - [Test] - [Description("Альтернативное решение. Какие у него недостатки?")] - // размыта зона ответственности этого теста(если тест упадет из-за неверного поля,то не понятно из-за какого конкретно) - //Очень негибкий тест: - // Любое изменение существующих полей класса Person приводит к ошибке - // При любом добавлении новых полей нужно снова изменять этот тест - // - 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)); + [Test] + [Description("Проверка текущего царя")] + 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, options => + options.Excluding(tsar => + tsar.DeclaringType == typeof(Person) && tsar.Name.Equals(nameof(Person.Id)))); + } - // Какие недостатки у такого подхода? - Assert.True(AreEqual(actualTsar, expectedTsar)); - } + [Test] + [Description("Альтернативное решение. Какие у него недостатки?")] + // размыта зона ответственности этого теста(если тест упадет из-за неверного поля,то не понятно из-за какого конкретно) + //Очень негибкий тест: + // Любое изменение существующих полей класса Person приводит к ошибке + // При любом добавлении новых полей нужно снова изменять этот тест + // + 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)); - 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); - } - } + Assert.True(AreEqual(actualTsar, expectedTsar)); + } - 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)); - } - } + private static 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); + } + } - public class Person - { - public static int IdCounter = 0; - public int Age, Height, Weight; - public string Name; - public Person? Parent; - public int Id; + public abstract 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)); + } + } - public Person(string name, int age, int height, int weight, Person? parent) - { - Id = IdCounter++; - Name = name; - Age = age; - Height = height; - Weight = weight; - Parent = parent; - } - } + public class Person + { + private static int _idCounter; + public readonly int Age; + public readonly int Height; + public readonly int Weight; + public readonly string Name; + public readonly 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; + } + } } \ No newline at end of file From 975a841be47013564c3b3864a159ea2150f7a06b Mon Sep 17 00:00:00 2001 From: PavelUd Date: Thu, 30 Nov 2023 00:40:11 +0500 Subject: [PATCH 5/6] added and refactored tests in NumberValidatorTests --- cs/HomeExercises/NumberValidatorTests.cs | 255 +++++++++++++---------- 1 file changed, 141 insertions(+), 114 deletions(-) diff --git a/cs/HomeExercises/NumberValidatorTests.cs b/cs/HomeExercises/NumberValidatorTests.cs index 5f5f17a1..908aec8f 100644 --- a/cs/HomeExercises/NumberValidatorTests.cs +++ b/cs/HomeExercises/NumberValidatorTests.cs @@ -5,118 +5,145 @@ namespace HomeExercises { - [TestFixture] - public class NumberValidatorTests - { - - - [TestCase(-1, 2, true, TestName = "Negative Precision")] - [TestCase(2, -1, true, TestName = "Negative Scale")] - [TestCase(2, 3, true, TestName = "Precision < Scale")] - [TestCase(3, 3, true, TestName = "Precision = Scale")] - [TestCase(2, 3, true, TestName = "Precision = Zero")] - [TestCase(null, 3, true, TestName = "Precision is null")] - [TestCase(null, 3, true, TestName = "Scale is null")] - - public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precision, int scale, bool onlyPositive) - { - Action createNumberValidator = () => new NumberValidator(precision, scale, onlyPositive); - createNumberValidator.Should().Throw(); - } - - - - [TestCase(2, 0, " ", TestName = "number is someWhiteSpaces")] - [TestCase(2, 0, "string.string", TestName = "number is word")] - [TestCase(2, 0, "", TestName = "Number is Empty")] - [TestCase(2, 0, null, TestName = "Number Is Null")] - [TestCase(4, 0, "-+20", TestName = "more than one sign")] - [TestCase(4, 2, "20?00", TestName = "invalid separator. It isn't '.' or ','")] - [TestCase(4, 2, "20......00", TestName = "more than one separators in a row")] - [TestCase(2, 0, "20.", TestName = "Number with separator, but Empty fractional part")] - [TestCase(1, 0, "+", TestName = "number with sign but without fractional and integer parts")] - [TestCase(4, 1, "2.0.0.0", TestName = "more than one separator")] - [TestCase(4, 1, "D75", TestName = "number in is not the correct number system")] - [TestCase(4, 1, " 200 ", TestName = "number surrounded by spaces")] - - public void IsValidNumber_IfInvalidTypeValue_ShouldBeFalse(int precision, int scale, string number, bool onlyPositive = true) - { - var numberValidator = new NumberValidator(precision, scale, onlyPositive); - numberValidator.IsValidNumber(number).Should().BeFalse(); - } - - - - [TestCase(5, 2, true, "-20.00", TestName = "incorrect sign")] - [TestCase(3, 1, true, "20.00", TestName = "precision < length number")] - [TestCase(4, 0, true, "20.00", TestName = "scale < length fractional part")] - - public void IsValidNumber_IfIncorrectArgs_ShouldBeFalse(int precision, int scale, bool onlyPositive, string number) - { - var numberValidator = new NumberValidator(precision, scale, onlyPositive); - numberValidator.IsValidNumber(number).Should().BeFalse(); - } - - - [TestCase(21, 1, true, "20.0", TestName = "precision > number Length")] - [TestCase(3, 2, true, "20.0", TestName = "scale > fractional part")] - [TestCase(3, 1, true, "20,0", TestName = "used ','")] - [TestCase(4, 1, true, "+20.0", TestName = "use '+' before number")] - [TestCase(4, 1, false, "+20.0", TestName = "use '+' without onlyPositive flag")] - [TestCase(2, 0, false, "20", TestName = "number without fractional part")] - [Test] - public void IsValidNumber_IfCorrectValue_ShouldBeTrue(int precision, int scale, bool onlyPositive, string number) - { - var numberValidator = new NumberValidator(precision, scale, onlyPositive); - numberValidator.IsValidNumber(number).Should().BeTrue(); - } - } - - 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("scale 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; - } - } + [TestFixture] + public class NumberValidatorTests + { + [TestCase(-1, 2, true, TestName = "Negative Precision")] + [TestCase(2, -1, true, TestName = "Negative Scale")] + [TestCase(2, 3, true, TestName = "Precision < Scale")] + [TestCase(3, 3, true, TestName = "Precision = Scale")] + [TestCase(0, null, true, TestName = "Precision = Zero")] + [TestCase(null, 3, true, TestName = "Precision is null")] + public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precision, int scale, + bool onlyPositive) + { + Action createNumberValidator = () => + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + }; + createNumberValidator.Should().Throw(); + } + + [TestCase(3, 2, null, TestName = "OnlyPositive is null")] + [TestCase(3, null, true, TestName = "Scale is null")] + [TestCase(3, 2, true, TestName = "Scale < Precision")] + [TestCase(3, 0, true, TestName = "Scale == 0")] + public void NumberValidator_CreateCorrectNumber_ShouldBeNotTrowException(int precision, int scale, + bool onlyPositive) + { + Action createNumberValidator = () => + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + }; + createNumberValidator.Should().NotThrow(); + } + + [TestCase(2, 0, " ", TestName = "number is someWhiteSpaces")] + [TestCase(2, 0, "string", TestName = "number is word")] + [TestCase(2, 0, "", TestName = "Number is Empty")] + [TestCase(2, 0, null, TestName = "Number Is Null")] + [TestCase(3, 0, "@20", TestName = "invalid sign. It isn't '-' or '+'")] + [TestCase(4, 0, "-+20", TestName = "more than one sign")] + [TestCase(4, 2, "20?00", TestName = "invalid separator. It isn't '.' or ','")] + [TestCase(4, 2, "20......00", TestName = "more than one separators in a row")] + [TestCase(2, 0, "20.", TestName = "Number with separator, but Empty fractional part")] + [TestCase(1, 0, "+", TestName = "number with sign but without fractional and integer parts")] + [TestCase(4, 1, "2.0.0.0", TestName = "more than one separator")] + [TestCase(4, 1, "D75", TestName = "number in is not the correct number system")] + [TestCase(4, 1, " 200 ", TestName = "number surrounded by spaces")] + [TestCase(4, 1, "\n200\n", TestName = "number surrounded by singles LineFeed")] + [TestCase(3, 2, "+.20", TestName = "number without integer part")] + [TestCase(8, 6, "20.string", TestName = "fractional part is word")] + public void IsValidNumber_IfInvalidTypeValue_ShouldBeFalse(int precision, int scale, string number, + bool onlyPositive = true) + { + AssertIsValidNumber(precision, scale, onlyPositive, number, false); + } + + [TestCase(4, 2, true, "+20.00", TestName = "precision == length value, but number with '+'")] + [TestCase(4, 2, false, "-20.00", TestName = "precision == length value, but number with '-'")] + [TestCase(5, 2, true, "-20.00", TestName = "negative number but onlyPositive flag == true")] + [TestCase(3, 1, true, "20.00", TestName = "precision < length number")] + [TestCase(4, 0, true, "20.00", TestName = "scale < length fractional part")] + public void IsValidNumber_IfIncorrectArgs_ShouldBeFalse(int precision, int scale, bool onlyPositive, + string number) + { + AssertIsValidNumber(precision, scale, onlyPositive, number, false); + } + + [TestCase(3, 0, "+20", TestName = "use '+' before number")] + [TestCase(3, 1, "20.0", TestName = "used '.' as a sign")] + [TestCase(3, 1, "20,0", TestName = "used ',' as a sign")] + [TestCase(2, 0, "20", TestName = "number without fractional part")] + [TestCase(3, 0, "-20", TestName = "negative number")] + public void IsValidNumber_IfCorrectValue_ShouldBeTrue(int precision, int scale, string number, + bool onlyPositive = false) + { + AssertIsValidNumber(precision, scale, onlyPositive, number, true); + } + + [TestCase(4, 2, true, "20.00", TestName = "precision of the number without sign == value Length")] + [TestCase(5, 2, null, "-20.00", TestName = "negative number with onlyPositive == null")] + [TestCase(21, 1, true, "+20.0", TestName = "precision of the number > value length + sign")] + [TestCase(3, 2, true, "20.0", TestName = "scale > fractional part")] + [TestCase(3, 1, true, "20.0", TestName = "scale == fractional part")] + [TestCase(4, 1, false, "+20.0", TestName = "use '+' without onlyPositive flag")] + [TestCase(2, null, true, "20", TestName = "number with Empty Scale")] + [TestCase(3, 0, false, "-20", TestName = "precision of the number == value Length + sign")] + [TestCase(2, 1, true, "20", TestName = "number without fractional part but scale > 0")] + public void IsValidNumber_IfNumberWithCorrectArgs_ShouldBeTrue(int precision, int scale, bool onlyPositive, + string number) + { + AssertIsValidNumber(precision, scale, onlyPositive, number, true); + } + + private static void AssertIsValidNumber(int precision, int scale, bool onlyPositive, string number, + bool expected) + { + var numberValidator = new NumberValidator(precision, scale, onlyPositive); + numberValidator.IsValidNumber(number).Should().Be(expected); + } + } +} + +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("scale must be a non-negative number less or equal than precision"); + numberRegex = new Regex(@"^([+-]?)(\d+)([.,](\d+))?$", RegexOptions.IgnoreCase); + } + + public bool IsValidNumber(string value) + { + 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; + } } \ No newline at end of file From 6b8e74a043e1d82a95ff67b822b98ae273b04763 Mon Sep 17 00:00:00 2001 From: PavelUd Date: Thu, 30 Nov 2023 16:36:52 +0500 Subject: [PATCH 6/6] little fix --- cs/HomeExercises/NumberValidatorTests.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/cs/HomeExercises/NumberValidatorTests.cs b/cs/HomeExercises/NumberValidatorTests.cs index 908aec8f..12be8717 100644 --- a/cs/HomeExercises/NumberValidatorTests.cs +++ b/cs/HomeExercises/NumberValidatorTests.cs @@ -17,10 +17,7 @@ public class NumberValidatorTests public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precision, int scale, bool onlyPositive) { - Action createNumberValidator = () => - { - var numberValidator = new NumberValidator(precision, scale, onlyPositive); - }; + Action createNumberValidator = () => new NumberValidator(precision, scale, onlyPositive); createNumberValidator.Should().Throw(); } @@ -31,10 +28,7 @@ public void NumberValidator_CreateInvalidNumber_ShouldBeTrowException(int precis public void NumberValidator_CreateCorrectNumber_ShouldBeNotTrowException(int precision, int scale, bool onlyPositive) { - Action createNumberValidator = () => - { - var numberValidator = new NumberValidator(precision, scale, onlyPositive); - }; + Action createNumberValidator = () => new NumberValidator(precision, scale, onlyPositive); createNumberValidator.Should().NotThrow(); } @@ -134,9 +128,7 @@ public bool IsValidNumber(string 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)