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 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
f53e022
Проведено начальное проектирование.
BlizPerfect Nov 24, 2024
f8768e2
В работе приложения больше не используются конвёртеры тегов.
BlizPerfect Dec 1, 2024
476d331
В работе приложения больше не используются теги в явном виде.
BlizPerfect Dec 1, 2024
36691f2
Реализация с помощью паттерна "Посетитель" получения визуального пред…
BlizPerfect Dec 1, 2024
f04e7fe
Рефакторинг токенов.
BlizPerfect Dec 1, 2024
a0e7f63
Реализация парсера.
BlizPerfect Dec 1, 2024
f99e193
Смена типа выходного файла на библиотеку.
BlizPerfect Dec 1, 2024
f0948eb
Написание тестов
BlizPerfect Dec 1, 2024
1bfdc4a
Исправил один int в цикле на var.
BlizPerfect Dec 1, 2024
058c67f
Выполнен рефакторинг тестов и изменено поведение парсера.
BlizPerfect Dec 2, 2024
c166721
Рефакторинг тестов в MdTests.
BlizPerfect Dec 2, 2024
1835148
Рефакторинг ParserMd
BlizPerfect Dec 2, 2024
7b04aad
Измнена логика работы теста по замеру алгоритмической сложности.
BlizPerfect Dec 10, 2024
e7e24a7
Токен теперь хранит в себе свой тип
BlizPerfect Dec 10, 2024
d7acd5a
Md теперь принимает конкеретный ParserMd.
BlizPerfect Dec 10, 2024
b176806
Рефакторинг кода
BlizPerfect Dec 10, 2024
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
36 changes: 36 additions & 0 deletions cs/Markdown.Tests/Markdown.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.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">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NUnit.Console" Version="3.18.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Markdown\Markdown.csproj" />
</ItemGroup>

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

</Project>
208 changes: 208 additions & 0 deletions cs/Markdown.Tests/MdTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using FluentAssertions;
using Markdown.Parsers.MdParsers;
using Markdown.Renderers;
using System.Diagnostics;

namespace Markdown.Tests
{
internal class MdTests
{
[Test]
public void Md_ThrowsException_ReceivingNullAsIParser()
{
Assert.Throws<ArgumentNullException>(() => new Md(null!, new RendererHTML()));
}

[Test]
public void Md_ThrowsException_ReceivingNullAsIRenderer()
{
Assert.Throws<ArgumentNullException>(() => new Md(new ParserMd(), null!));
}

[TestCase("__Bold token__", "<strong>Bold token</strong>")]
[TestCase("_Italic token_", "<em>Italic token</em>")]
[TestCase("# Header token", "<h1>Header token</h1>")]
[TestCase("Text token", "Text token")]
[TestCase("# _Set_ __of__ tokens", "<h1><em>Set</em> <strong>of</strong> tokens</h1>")]
public void Md_RendersCorrectly_SimpleTokens(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase(@"\_\_Text token\_\_", "__Text token__")]
[TestCase(@"\_Text token\_", "_Text token_")]
[TestCase(@"\# Text token", "# Text token")]
[TestCase(@"#\ Text token", "# Text token")]
public void Md_RendersCorrectly_SimpleEscapedTokens(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase(@"Ste\gosaur\us", @"Ste\gosaur\us")]
[TestCase(@"\", @"\")]
[TestCase(@"_Italic \token_", @"<em>Italic \token</em>")]
public void Md_RendersCorrectly_WhenEscapedNothing(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase(@"\\a", @"\a")]
[TestCase(@"\\_\\_Text token\\_\\_", @"\<em>\</em>Text token\<em>\</em>")]
[TestCase(@"\\_Italic token\\_", @"\<em>Italic token\</em>")]
public void Md_RendersCorrectly_WhenEscapeEscaped(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("__Outer bold _Inner italic part_ outer bold__", "<strong>Outer bold <em>Inner italic part</em> outer bold</strong>")]
public void Md_RendersCorrectly_ItalicInsideBold(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("_Outer italic __Inner Bold part__ outer Italic_", "<em>Outer italic __Inner Bold part__ outer Italic</em>")]
public void Md_RendersCorrectly_BoldInsideItalic(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("_Digits 12 3_", "<em>Digits 12 3</em>")]
[TestCase("Digits_12_3", "Digits_12_3")]
[TestCase("Digits__12__3", "Digits__12__3")]
public void Md_RendersCorrectly_DigitsWithUnderscores(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}


[TestCase("__Sta__rt", "<strong>Sta</strong>rt")]
[TestCase("_Sta_rt", "<em>Sta</em>rt")]
[TestCase("S__tar__t", "S<strong>tar</strong>t")]
[TestCase("S_tar_t", "S<em>tar</em>t")]
[TestCase("St__art__", "St<strong>art</strong>")]
[TestCase("St_art_", "St<em>art</em>")]
public void Md_RendersCorrectly_WordsWithUnderscores(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("Hel_lo, Wor_ld", "Hel_lo, Wor_ld")]
[TestCase("Hel__lo, Wor__ld", "Hel__lo, Wor__ld")]
public void Md_RendersCorrectly_UnderscoresInsideDifferentWords(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("_Hello world__", "_Hello world__")]
[TestCase("__Hello world_", "__Hello world_")]
public void Md_RendersCorrectly_UnpairedTags(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("_ Hello world_", "_ Hello world_")]
[TestCase("_ Hello world _", "_ Hello world _")]
[TestCase("_Hello world _", "_Hello world _")]
[TestCase("__ Hello world__", "__ Hello world__")]
[TestCase("__ Hello world __", "__ Hello world __")]
[TestCase("__Hello world __", "__Hello world __")]
public void Md_RendersCorrectly_WithSpaceAfterOrBeforeTag(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("_Hello__ _world__", "_Hello__ _world__")]
[TestCase("__Hello_ __world_", "__Hello_ __world_")]
public void Md_RendersCorrectly_WithUnderscoreIntersections(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("__", "__")]
[TestCase("____", "____")]
public void Md_RendersCorrectly_UnderscoresWithEmptyValue(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase("# Header token 1\n# Header token 2", "<h1>Header token 1</h1><h1>Header token 2</h1>")]
[TestCase("this is regular sentence # 123", "this is regular sentence # 123")]
public void Md_RendersCorrectly_Heading(string input, string expected)
{
var md = new Md(new ParserMd(), new RendererHTML());
var actual = md.Render(input);
actual.Should().Be(expected);
}

[TestCase(5, 10, 0.5)]
public void Md_ShouldWorkInLinearTime(int iterations, int baseIterationSize, double measurementError)
{
var testSizes = new int[iterations];
testSizes[0] = baseIterationSize;
for (var i = 1; i < iterations; i++)
{
testSizes[i] = 2 * testSizes[i - 1];
}

var executionTimes = GetExecutionTimes(testSizes);

for (var i = 1; i < iterations; i++)
{
var growthFactor = executionTimes[i] / executionTimes[i - 1];
Assert.That(growthFactor, Is.LessThanOrEqualTo(2.0 + measurementError));
}
}

private double[] GetExecutionTimes(int[] sizes)
{
var results = new double[sizes.Length];
var md = new Md(new ParserMd(), new RendererHTML());
md.Render(GenerateText(sizes[^1]));

for (var i = 0; i < sizes.Length; i++)
{
md = new Md(new ParserMd(), new RendererHTML());
var text = GenerateText(sizes[i]);

var stopwatch = Stopwatch.StartNew();
md.Render(text);
stopwatch.Stop();

results[i] = stopwatch.Elapsed.TotalMilliseconds;
}

return results;
}

private string GenerateText(int numberOfRepetitions)
=> string.Concat(
Enumerable.Repeat(@"__This _is_ a__ simple text \_for\_ crea\ting complex _test_ __text__.",
numberOfRepetitions));
}
}
77 changes: 77 additions & 0 deletions cs/Markdown.Tests/ParsersTests/ParserMdTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using FluentAssertions;
using Markdown.Parsers.MdParsers;
using Markdown.Tokens.HtmlTokens;

namespace Markdown.Tests.ParsersTests
{
[TestFixture]
public class ParserMdTests
Copy link

Choose a reason for hiding this comment

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

Напишу сюда, так как теста на случай с пересечением тегов нету

В случае __пересечения _двойных__ и одинарных_ подчерков ни один из них не считается выделением.

Сейчас выделением считается первое попадание. Что нарушает спецификацию.
image

Choose a reason for hiding this comment

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

Вроде проблема осталась
image

Тест __Hello_ __world_ не совсем релевантен, потому что по спецификации __hello __ не может быть превращён в <strong>hello </strong> из-за пробела.

За подчерками, начинающими выделение, должен следовать непробельный символ. Иначе эти_ подчерки_ не считаются выделением и остаются просто символами подчерка.

BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved
{
private ParserMd _parser;
private readonly List<IRenderable> _expected = new List<IRenderable>();

[SetUp]
public void SetUp()
{
_parser = new ParserMd();
_expected.Clear();
}

[TestCase("__Bold token__")]
public void Parse_SimpleBoldText_Correctly(string text)
{
_expected.Add(
new BoldToken(
new TextToken("Bold token")));
CheckCorrectness(text);
}
BlizPerfect marked this conversation as resolved.
Show resolved Hide resolved

[TestCase("# Header token")]
public void Parse_SimpleHeaderText_Correctly(string text)
{
_expected.Add(
new HeaderToken(
new TextToken("Header token")));
CheckCorrectness(text);
}

[TestCase("_Italic token_")]
public void Parse_SimpleItalicText_Correctly(string text)
{
_expected.Add(
new ItalicToken(
new TextToken("Italic token")));
CheckCorrectness(text);
}

[TestCase("Text Token")]
public void Parse_SimpleText_Correctly(string text)
{
_expected.Add(
new TextToken(text));
CheckCorrectness(text);
}

[TestCase("# _Set_ __of__ tokens")]
public void Parse_ComplexText_Correctly(string text)
{
_expected.Add(
new HeaderToken(
new SetToken(
new ItalicToken(
new TextToken("Set")),
new TextToken(" "),
new BoldToken(
new TextToken("of")),
new TextToken(" tokens"))));
CheckCorrectness(text);
}

private void CheckCorrectness(string text)
{
var actual = _parser.Parse(text);
actual.Should().BeEquivalentTo(_expected,
options => options.RespectingRuntimeTypes());
}
}
}
Loading