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

MyNUnit #6

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 4 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/AfterAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace MyAttributes;

[AttributeUsage(AttributeTargets.Method)]
public class AfterAttribute : Attribute { }
4 changes: 4 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/AfterClassAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace MyAttributes;

[AttributeUsage(AttributeTargets.Method)]
public class AfterClassAttribute : Attribute { }
4 changes: 4 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/BeforeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace MyAttributes;

[AttributeUsage(AttributeTargets.Method)]
public class BeforeAttribute : Attribute { }
4 changes: 4 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/BeforeClassAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace MyAttributes;

[AttributeUsage(AttributeTargets.Method)]
public class BeforeClassAttribute : Attribute { }
10 changes: 10 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/MyAttributes.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>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
10 changes: 10 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/MyAttributesInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace MyAttributes;

public static class MyAttributesInfo
{
public static bool Exist(Type attributeType)
{
return attributeType == typeof(AfterAttribute) || attributeType == typeof(BeforeAttribute)
|| attributeType == typeof(AfterClassAttribute) || attributeType == typeof(BeforeClassAttribute);
}
}
14 changes: 14 additions & 0 deletions MyNUnit/MyNUnit/MyAttributes/TestAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace MyAttributes;

[AttributeUsage(AttributeTargets.Method)]
public class TestAttribute : Attribute
{
public TestAttribute(Type? expected, string? ignore)
{
Expected = expected;
Ignore = ignore;
}

public Type? Expected { get; set; }
public string? Ignore { get; set; }
}
43 changes: 43 additions & 0 deletions MyNUnit/MyNUnit/MyNUnit.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32505.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyNUnit", "MyNUnit\MyNUnit.csproj", "{68379B81-FCA0-4C2B-94C0-2F4FC757D4C8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyAttributes", "MyAttributes\MyAttributes.csproj", "{82174A65-08EA-4169-95DF-6643C7FBD67E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyNUnitTests", "MyNUnitTests\MyNUnitTests.csproj", "{1570ECD3-4404-4EC8-8E55-2A6B5FF0CC96}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClass", "TestClass\TestClass.csproj", "{C9DB29B3-4570-44FA-8BCA-47CBDA6A3581}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{68379B81-FCA0-4C2B-94C0-2F4FC757D4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{68379B81-FCA0-4C2B-94C0-2F4FC757D4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68379B81-FCA0-4C2B-94C0-2F4FC757D4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68379B81-FCA0-4C2B-94C0-2F4FC757D4C8}.Release|Any CPU.Build.0 = Release|Any CPU
{82174A65-08EA-4169-95DF-6643C7FBD67E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82174A65-08EA-4169-95DF-6643C7FBD67E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82174A65-08EA-4169-95DF-6643C7FBD67E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82174A65-08EA-4169-95DF-6643C7FBD67E}.Release|Any CPU.Build.0 = Release|Any CPU
{1570ECD3-4404-4EC8-8E55-2A6B5FF0CC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1570ECD3-4404-4EC8-8E55-2A6B5FF0CC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1570ECD3-4404-4EC8-8E55-2A6B5FF0CC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1570ECD3-4404-4EC8-8E55-2A6B5FF0CC96}.Release|Any CPU.Build.0 = Release|Any CPU
{C9DB29B3-4570-44FA-8BCA-47CBDA6A3581}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9DB29B3-4570-44FA-8BCA-47CBDA6A3581}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9DB29B3-4570-44FA-8BCA-47CBDA6A3581}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9DB29B3-4570-44FA-8BCA-47CBDA6A3581}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {74F30EA0-AE2F-4CCD-80FB-270BEC07C10D}
EndGlobalSection
EndGlobal
133 changes: 133 additions & 0 deletions MyNUnit/MyNUnit/MyNUnit/MyNUnit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
namespace MyNUnit;

using System.Diagnostics;
using MyAttributes;
using System.Reflection;
using System.Collections.Concurrent;


/// <summary>
/// Сlass representing a system for running tests
/// </summary>
public class MyNUnit
{
public static readonly BlockingCollection<TestInfo> typeInfos = new ();

private static void TestAttributeExecute((MethodInfo, IEnumerable<MethodInfo>) methodInfoPair)
{
var (methodInfo, listOfMethodInfo) = methodInfoPair;
var assemblyName = methodInfo.DeclaringType!.Assembly.GetName();
var testAttribute = (TestAttribute)(methodInfo.GetCustomAttribute(typeof(TestAttribute))!);
if (testAttribute.Ignore != null)
{
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Skipped, testAttribute.Ignore, null, 0));
return;
}

Start<BeforeAttribute>(listOfMethodInfo);

var typeObject = Activator.CreateInstance(methodInfo.DeclaringType!);
var stopWatch = new Stopwatch();

try
{
stopWatch.Start();
methodInfo.Invoke(typeObject, null);
stopWatch.Stop();
}
catch (Exception ex)
{
stopWatch.Stop();
if (testAttribute.Expected == null)
{
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Failed, null, $"Expected: null, but was: {ex.InnerException!.GetType()}", stopWatch.ElapsedMilliseconds));
}
else if (testAttribute.Expected != ex.InnerException!.GetType())
{
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Failed, null, $"Expected: {testAttribute.Expected}, but was: {ex.InnerException!.GetType()}", stopWatch.ElapsedMilliseconds));
}
else
{
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Passed, null, null, stopWatch.ElapsedMilliseconds));
}

Start<AfterAttribute>(listOfMethodInfo);

return;
}

if (testAttribute.Expected != null) {
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Failed, null, $"Expected: {testAttribute.Expected}, but was: null", stopWatch.ElapsedMilliseconds));
}
else {
typeInfos.Add(new TestInfo(assemblyName.Name!, methodInfo.Name, TestInfo.TestStatus.Passed, null, null, stopWatch.ElapsedMilliseconds));
}

Start<AfterAttribute>(listOfMethodInfo);
}

private static void AnotherAttributeExecute((MethodInfo, IEnumerable<MethodInfo>) methodInfoPair) {
var (methodInfo, _) = methodInfoPair;
var typeObject = Activator.CreateInstance(methodInfo.DeclaringType!);
methodInfo.Invoke(typeObject, null);
}

private static void Start<AttributeType>(IEnumerable<MethodInfo> methodInfos)
{
var methodWithAttribute = methodInfos.Where(methodInfo => Attribute.IsDefined(methodInfo, typeof(AttributeType)));
Action<(MethodInfo, IEnumerable<MethodInfo>)> actionDependentTheAttributeType = x => { };
if (typeof(AttributeType) == typeof(TestAttribute))
{
actionDependentTheAttributeType = TestAttributeExecute;
}
else if (MyAttributesInfo.Exist(typeof(AttributeType)))
{
actionDependentTheAttributeType = AnotherAttributeExecute;
}
else
{
throw new ArgumentException("Attribute of this type not exist");
}

Parallel.ForEach(methodWithAttribute, method => actionDependentTheAttributeType((method, methodInfos)));
}

private static void ParallelTestExecuteForEachType(Type type)
{
Start<BeforeClassAttribute>(type.GetTypeInfo().DeclaredMethods);
Start<TestAttribute>(type.GetTypeInfo().DeclaredMethods);
Start<AfterClassAttribute>(type.GetTypeInfo().DeclaredMethods);
}

/// <summary>
/// Method for running tests
/// </summary>
/// <param name="pathToDirectory">Path to directory containing .dll files</param>
public static void Run(string pathToDirectory) {
var types = Directory.EnumerateFiles(pathToDirectory, "*.dll").Select(fileName => Assembly.LoadFrom(fileName)).SelectMany(assembly => assembly.ExportedTypes);
Parallel.ForEach(types, classType => ParallelTestExecuteForEachType(classType));
}

/// <summary>
/// Method for running tests
/// </summary>
/// <param name="pathToDirectory">Path to directory containing .dll files</param>
public static void RunForFiles(IEnumerable<string> files)
{
var types = files.Select(fileName => Assembly.LoadFrom(fileName)).SelectMany(assembly => assembly.ExportedTypes);
Parallel.ForEach(types, classType => ParallelTestExecuteForEachType(classType));
}

public static void OutputInformationAboutTheTests()
{
foreach(var typeInfo in typeInfos) {
Console.WriteLine();
Console.WriteLine($"AsseblyName: {typeInfo.AssemblyName}");
Console.WriteLine($" Name: {typeInfo.Name}");
Console.WriteLine($" Status: {typeInfo.Status}");
Console.WriteLine($" Error message: {typeInfo.ErrorMessage}");
Console.WriteLine($" Ignore reason: {typeInfo.IgnoreReason}");
Console.WriteLine($" Time: {typeInfo.Time} ms");
}
}
}
14 changes: 14 additions & 0 deletions MyNUnit/MyNUnit/MyNUnit/MyNUnit.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

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

</Project>
15 changes: 15 additions & 0 deletions MyNUnit/MyNUnit/MyNUnit/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

if (args.Length != 1)
{
Console.WriteLine("Wrong number of argumnets");
return;
}

if (!Directory.Exists(args[0])) {
Console.WriteLine("Directory not exist");
return;
}

MyNUnit.MyNUnit.Run(args[0]);
MyNUnit.MyNUnit.OutputInformationAboutTheTests();

82 changes: 82 additions & 0 deletions MyNUnit/MyNUnit/MyNUnit/TestInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
namespace MyNUnit;

/// <summary>
/// Сlass representing information about each test
/// </summary>
public class TestInfo : IEquatable<TestInfo>
{
public enum TestStatus {
Passed,
Failed,
Skipped
}

public string Name { get; }
public string AssemblyName { get; }
public TestStatus Status { get; }
public string? IgnoreReason { get; }
public string? ErrorMessage { get; }
public long Time { get; }

/// <summary>
/// Constructor
/// </summary>
/// <param name="name">Test name</param>
/// <param name="status">Test status</param>
/// <param name="ignoreReason">Ignore reason</param>
/// <param name="errorMessage">Error message</param>
public TestInfo(string assemblyName, string name, TestStatus status, string? ignoreReason, string? errorMessage, long time)
{
Name = name;
Status = status;
IgnoreReason = ignoreReason;
ErrorMessage = errorMessage;
Time = time;
AssemblyName = assemblyName;
}

public override bool Equals(object? obj)
{
if (obj == null)
{
return false;
}
if (obj is not TestInfo objAsTestInfo)
{
return false;
}
else
{
return Equals(objAsTestInfo);
}
}

public bool Equals(TestInfo? other)
{
if (other == null)
{
return false;
}

// Все свойства равны (кроме времени, так как один и тот же тест не пройдет за одно и тоже время)
return Name == other.Name && Status == other.Status && IgnoreReason == other.IgnoreReason && ErrorMessage == other.ErrorMessage;
}

public override int GetHashCode()
{
throw new NotImplementedException();
}
}

public class TestInfoComparer : EqualityComparer<TestInfo>
{
public override bool Equals(TestInfo? x, TestInfo? y)
{
return x!.Equals(y);
}

public override int GetHashCode(TestInfo obj)
{
throw new NotImplementedException();
}
}
36 changes: 36 additions & 0 deletions MyNUnit/MyNUnit/MyNUnitTests/MyNUnitTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace MyNUnitTests;

using MyNUnit;
using TestClass;

public class MyNUnitTest
{
private readonly TestInfoComparer comparer = new ();
[OneTimeSetUp]
public void Setup()
{
MyNUnit.Run("../../../../TestClass");
}

[Test]
public void ShouldTheAfterAndBeforeMethodsBeExecuted()
{
Assert.Multiple(() =>
{
Assert.That(ClassForTest.GetClassCounter, Is.EqualTo(0));
Assert.That(ClassForTest.GetAfterMethodCounter, Is.EqualTo(2));
Assert.That(ClassForTest.GetBeforeMethodCounter, Is.EqualTo(2));
});
}

[Test]
public void ShouldTestResultsCorrect()
{
Assert.Multiple(() =>
{
Assert.True(MyNUnit.typeInfos.Contains(new TestInfo("", "ExpectedDivideByZeroException", TestInfo.TestStatus.Passed, null, null, 0), comparer));
Assert.True(MyNUnit.typeInfos.Contains(new TestInfo("", "NoExpectedDivideByZeroException", TestInfo.TestStatus.Failed, null, $"Expected: {typeof(DivideByZeroException)}, but was: {typeof(AppDomainUnloadedException)}", 0), comparer));
Assert.True(MyNUnit.typeInfos.Contains(new TestInfo("", "IgnoreForBebraReasone", TestInfo.TestStatus.Skipped, "bebra", null, 0), comparer));
});
}
}
Loading