diff --git a/src/Unosquare.Swan/Components/CsProjFile.cs b/src/Unosquare.Swan/Components/CsProjFile.cs new file mode 100644 index 000000000..3bd550cb6 --- /dev/null +++ b/src/Unosquare.Swan/Components/CsProjFile.cs @@ -0,0 +1,100 @@ +namespace Unosquare.Swan.Components +{ + using System; + using System.IO; + using System.Linq; + using System.Xml.Linq; + + /// + /// Represents a CsProjFile parser + /// Based on https://github.com/maartenba/dotnetcli-init + /// + /// The type of CsProjMetadataBase + /// + public class CsProjFile + : IDisposable + where T : CsProjMetadataBase + { + private readonly Stream _stream; + private readonly bool _leaveOpen; + private readonly XDocument _xmlDocument; + + /// + /// Initializes a new instance of the class. + /// + /// The filename. + public CsProjFile(string filename = null) + : this(OpenFile(filename)) + { + // placeholder + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// if set to true [leave open]. + /// Project file is not of the new .csproj type. + public CsProjFile(Stream stream, bool leaveOpen = false) + { + _stream = stream; + _leaveOpen = leaveOpen; + + _xmlDocument = XDocument.Load(stream); + + var projectElement = _xmlDocument.Descendants("Project").FirstOrDefault(); + if (projectElement == null || projectElement.Attribute("Sdk")?.Value != "Microsoft.NET.Sdk") + { + throw new ArgumentException("Project file is not of the new .csproj type."); + } + + Metadata = Activator.CreateInstance(); + Metadata.SetData(_xmlDocument); + } + + /// + /// Gets the metadata. + /// + /// + /// The nu get metadata. + /// + public T Metadata { get; } + + /// + /// Saves this instance. + /// + public void Save() + { + _stream.SetLength(0); + _stream.Position = 0; + + _xmlDocument.Save(_stream); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (!_leaveOpen) + { + _stream?.Dispose(); + } + } + + private static FileStream OpenFile(string filename) + { + if (filename == null) + { + filename = Directory + .EnumerateFiles(Directory.GetCurrentDirectory(), "*.csproj", SearchOption.TopDirectoryOnly) + .FirstOrDefault(); + } + + if (string.IsNullOrWhiteSpace(filename)) + throw new ArgumentNullException(nameof(filename)); + + return File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite); + } + } +} \ No newline at end of file diff --git a/src/Unosquare.Swan/Components/CsProjMetadataBase.cs b/src/Unosquare.Swan/Components/CsProjMetadataBase.cs new file mode 100644 index 000000000..334817cee --- /dev/null +++ b/src/Unosquare.Swan/Components/CsProjMetadataBase.cs @@ -0,0 +1,79 @@ +namespace Unosquare.Swan.Components +{ + using System.Linq; + using System.Xml.Linq; + + /// + /// Represents a CsProj metadata abstract class + /// to use with CsProjFile parser. + /// + public abstract class CsProjMetadataBase + { + private XDocument _xmlDocument; + + /// + /// Gets the package identifier. + /// + /// + /// The package identifier. + /// + public string PackageId => FindElement(nameof(PackageId))?.Value; + + /// + /// Gets the name of the assembly. + /// + /// + /// The name of the assembly. + /// + public string AssemblyName => FindElement(nameof(AssemblyName))?.Value; + + /// + /// Gets the target frameworks. + /// + /// + /// The target frameworks. + /// + public string TargetFrameworks => FindElement(nameof(TargetFrameworks))?.Value; + + /// + /// Gets the target framework. + /// + /// + /// The target framework. + /// + public string TargetFramework => FindElement(nameof(TargetFramework))?.Value; + + /// + /// Gets the version. + /// + /// + /// The version. + /// + public string Version => FindElement(nameof(Version))?.Value; + + /// + /// Parses the cs proj tags. + /// + /// The arguments. + public abstract void ParseCsProjTags(ref string[] args); + + /// + /// Sets the data. + /// + /// The XML document. + public void SetData(XDocument xmlDocument) + { + _xmlDocument = xmlDocument; + } + + /// + /// Finds the element. + /// + /// Name of the element. + /// A XElement. + protected XElement FindElement(string elementName) + { + return _xmlDocument.Descendants(elementName).FirstOrDefault(); + } + } +} diff --git a/src/Unosquare.Swan/Unosquare.Swan.csproj b/src/Unosquare.Swan/Unosquare.Swan.csproj index 9844d5996..2b535f09e 100644 --- a/src/Unosquare.Swan/Unosquare.Swan.csproj +++ b/src/Unosquare.Swan/Unosquare.Swan.csproj @@ -10,7 +10,7 @@ Unosquare.Swan ..\..\StyleCop.Analyzers.ruleset Full - 0.21.1 + 0.22.0 Unosquare https://github.com/unosquare/swan/raw/master/swan-logo-32.png https://github.com/unosquare/swan diff --git a/test/Unosquare.Swan.Test/CsProjFileTest.cs b/test/Unosquare.Swan.Test/CsProjFileTest.cs new file mode 100644 index 000000000..9dd66cc6c --- /dev/null +++ b/test/Unosquare.Swan.Test/CsProjFileTest.cs @@ -0,0 +1,101 @@ +namespace Unosquare.Swan.Test +{ + using System.IO; + using System.Linq; + using System.Text; + using System.Xml; + using NUnit.Framework; + using Unosquare.Swan.Components; + using Unosquare.Swan.Test.Mocks; + using System; + + public abstract class CsProjFileTest : TestFixtureBase + { + protected string _data = @" + + Unit Testing project + Copyright(c) 2016-2017 - Unosquare + Unosquare SWAN Test + net46;netcoreapp2.0 + Unosquare.Swan.Test + Full + "; + protected string _wrongSDK = @""; + } + + [TestFixture] + public class CsProjFileConstructor : CsProjFileTest + { + [Test] + public void WithValidFileAndValidClass_ReturnsFileAndMetadata() + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_data))) + using (var csproj = new CsProjFile(stream)) + { + Assert.IsNotNull(csproj); + Assert.IsNotNull(csproj.Metadata); + Assert.IsNotNull(csproj.Metadata.Copyright); + } + } + + [Test] + public void WithNullStream_ThrowsXmlException() + { + Assert.Throws(() => + { + using (var stream = new MemoryStream()) + using (var csproj = new CsProjFile(stream)) { } + }); + } + + [Test] + public void IfPropertyWasNotFound_ReturnsNull() + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_data))) + using (var csproj = new CsProjFile(stream)) + { + Assert.IsNull(csproj.Metadata.NonExistentProp); + } + } + + [Test] + public void WithWrongSdk_ThrowsArgumentException() + { + Assert.Throws(() => + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_wrongSDK))) + using (var csproj = new CsProjFile(stream)){} + }); + } + + [Test] + public void WithAbstractClass_ThrowsMissingMethodException() + { + Assert.Throws(() => + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_data))) + using (var csproj = new CsProjFile(stream)) { } + }); + } + + [Test] + public void WithEmptyStringAsFileName_ThrowsArgumentNullException() + { + Assert.Throws(() => + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_data))) + using (var csproj = new CsProjFile(string.Empty)) { } + }); + } + + [Test] + public void WithNullAsFileName_ThrowsArgumentNullException() + { + Assert.Throws(() => + { + using (var stream = new MemoryStream(Encoding.ASCII.GetBytes(_data))) + using (var csproj = new CsProjFile(null)) { } + }); + } + } +} diff --git a/test/Unosquare.Swan.Test/ExtensionsDatesTest.cs b/test/Unosquare.Swan.Test/ExtensionsDatesTest.cs index 5cec6acdd..4329eb28f 100644 --- a/test/Unosquare.Swan.Test/ExtensionsDatesTest.cs +++ b/test/Unosquare.Swan.Test/ExtensionsDatesTest.cs @@ -89,7 +89,7 @@ public class ToUnixEpochDate [Test] public void GivingADate_ConvertItIntoTicks() { - var date = new DateTime(2017, 10, 27); + var date = new DateTime(2017, 10, 27).ToUniversalTime().Date; Assert.AreEqual(1509062400, date.ToUnixEpochDate()); } diff --git a/test/Unosquare.Swan.Test/LdapTest.cs b/test/Unosquare.Swan.Test/LdapTest.cs index 649b486d1..54a626838 100644 --- a/test/Unosquare.Swan.Test/LdapTest.cs +++ b/test/Unosquare.Swan.Test/LdapTest.cs @@ -183,17 +183,20 @@ public class ModifyTest : LdapTest [Test] public void ChangeUserProperty() { - var ex = Assert.ThrowsAsync(async () => + var ex = Assert.CatchAsync(async () => { var cn = await GetDefaultConnection(); await cn.Modify( "uid=euclid,dc=example,dc=com", - new[] { new LdapModification(LdapModificationOp.Replace, "mail", "new@ldap.forumsys.com")}); + new[] {new LdapModification(LdapModificationOp.Replace, "mail", "new@ldap.forumsys.com")}); cn.Disconnect(); }); - Assert.AreEqual(ex.ResultCode, LdapStatusCode.InsufficientAccessRights); + if (ex is LdapException ldapEx) + Assert.AreEqual(ldapEx.ResultCode, LdapStatusCode.InsufficientAccessRights); + else + Assert.IsNotNull(ex); } } diff --git a/test/Unosquare.Swan.Test/Mocks/CsProjFileMock.cs b/test/Unosquare.Swan.Test/Mocks/CsProjFileMock.cs new file mode 100644 index 000000000..8eb0d087b --- /dev/null +++ b/test/Unosquare.Swan.Test/Mocks/CsProjFileMock.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Unosquare.Swan.Components; + +namespace Unosquare.Swan.Test.Mocks +{ + public class CsMetadataMock : CsProjMetadataBase + { + public string Copyright => FindElement(nameof(Copyright))?.Value; + + public string NonExistentProp => FindElement(nameof(NonExistentProp))?.Value; + + public override void ParseCsProjTags(ref string[] args) + { + throw new NotImplementedException(); + } + } + + public abstract class CsAbstractMetadataMock : CsProjMetadataBase + { + public override void ParseCsProjTags(ref string[] args) + { + throw new NotImplementedException(); + } + } +} diff --git a/test/Unosquare.Swan.Test/Unosquare.Swan.Test.csproj b/test/Unosquare.Swan.Test/Unosquare.Swan.Test.csproj index 33f05bd0c..c8843c260 100644 --- a/test/Unosquare.Swan.Test/Unosquare.Swan.Test.csproj +++ b/test/Unosquare.Swan.Test/Unosquare.Swan.Test.csproj @@ -7,9 +7,12 @@ net46;netcoreapp2.0 Unosquare.Swan.Test ..\..\StyleCop.Analyzers.ruleset - Full + + Full + +