diff --git a/Hypercube.Client/Dependencies.cs b/Hypercube.Client/Dependencies.cs index 6985f28..2e75cf9 100644 --- a/Hypercube.Client/Dependencies.cs +++ b/Hypercube.Client/Dependencies.cs @@ -9,6 +9,7 @@ using Hypercube.Shared.Entities.Realisation.EventBus; using Hypercube.Shared.Entities.Realisation.Manager; using Hypercube.Shared.EventBus; +using Hypercube.Shared.Resources.Manager; using Hypercube.Shared.Timing; namespace Hypercube.Client; @@ -31,6 +32,9 @@ public static void Register(DependenciesContainer rootContainer) rootContainer.Register(); rootContainer.Register(); + // Resources + rootContainer.Register(); + // Texturing rootContainer.Register(); diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs index 4ba1f23..215ada9 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.Render.cs @@ -1,7 +1,6 @@ using Hypercube.Client.Graphics.Shading; using Hypercube.Client.Graphics.Texturing; using Hypercube.Client.Graphics.Viewports; -using Hypercube.Client.Graphics.Windows; using Hypercube.Shared.Math; using Hypercube.Shared.Math.Box; using Hypercube.Shared.Runtimes.Loop.Event; @@ -31,8 +30,14 @@ public sealed partial class Renderer private void OnLoad() { - _baseShader = new Shader("Resources/Shaders/base"); - _baseTexture = _textureManager.CreateHandler("Resources/Textures/icon.png"); + // mount directories + _resourceManager.MountContentFolder(".", "/"); + _resourceManager.MountContentFolder("Resources", "/"); + _resourceManager.MountContentFolder("Resources/Textures", "/"); + _resourceManager.MountContentFolder("Resources/Shaders", "/"); + + _baseShader = new Shader("/base", _resourceManager); + _baseTexture = _textureManager.CreateHandler("/icon.png"); _baseTexture.Bind(); _vbo = new BufferObject(BufferTarget.ArrayBuffer); diff --git a/Hypercube.Client/Graphics/Rendering/Renderer.cs b/Hypercube.Client/Graphics/Rendering/Renderer.cs index 4d55791..3e58e29 100644 --- a/Hypercube.Client/Graphics/Rendering/Renderer.cs +++ b/Hypercube.Client/Graphics/Rendering/Renderer.cs @@ -7,6 +7,7 @@ using Hypercube.Shared.Dependency; using Hypercube.Shared.EventBus; using Hypercube.Shared.Logging; +using Hypercube.Shared.Resources.Manager; using Hypercube.Shared.Runtimes.Event; using Hypercube.Shared.Runtimes.Loop.Event; using Hypercube.Shared.Timing; @@ -21,6 +22,7 @@ public sealed partial class Renderer : IRenderer, IPostInject [Dependency] private readonly IEventBus _eventBus = default!; [Dependency] private readonly ITextureManager _textureManager = default!; [Dependency] private readonly ITiming _timing = default!; + [Dependency] private readonly IResourceManager _resourceManager = default!; private readonly ILogger _logger = LoggingManager.GetLogger("renderer"); private readonly ILogger _loggerOpenGL = LoggingManager.GetLogger("open_gl")!; @@ -93,7 +95,7 @@ private void OnStartup(RuntimeStartupEvent args) break; } - var windowIcons = _windowManager.LoadWindowIcon(_textureManager, "Resources/Icons").ToList(); + var windowIcons = _windowManager.LoadWindowIcon(_textureManager, _resourceManager, "/Icons").ToList(); _windowManager.SetWindowIcons(MainWindow, windowIcons); diff --git a/Hypercube.Client/Graphics/Shading/Shader.cs b/Hypercube.Client/Graphics/Shading/Shader.cs index 347dd03..9ed479c 100644 --- a/Hypercube.Client/Graphics/Shading/Shader.cs +++ b/Hypercube.Client/Graphics/Shading/Shader.cs @@ -1,4 +1,6 @@ using Hypercube.Shared.Math.Vector; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; using OpenToolkit.Graphics.OpenGL4; using Vector2 = Hypercube.Shared.Math.Vector.Vector2; @@ -8,15 +10,19 @@ public class Shader : IShader { public readonly int _handle; private readonly Dictionary _uniformLocations = new(); - - public Shader(string path) : this($"{path}.vert", $"{path}.frag") + + public Shader(string path, IResourceManager manager) : this(new ResourcePath($"{path}.vert"), new ResourcePath($"{path}.frag"), + manager) { } - - private Shader(string vertPath, string fragPath) + + private Shader(ResourcePath vertPath, ResourcePath fragPath, IResourceManager resourceManager) { - var vertexShader = CreateShader(vertPath, ShaderType.VertexShader); - var fragmentShader = CreateShader(fragPath, ShaderType.FragmentShader); + var vertSource = resourceManager.ReadFileContentAllText(vertPath); + var fragSource = resourceManager.ReadFileContentAllText(fragPath); + + var vertexShader = CreateShader(vertSource, ShaderType.VertexShader); + var fragmentShader = CreateShader(fragSource, ShaderType.FragmentShader); _handle = GL.CreateProgram(); @@ -75,9 +81,8 @@ public void SetUniform(string name, Vector2 value) GL.Uniform2(_uniformLocations[name], value.X, value.Y); } - private int CreateShader(string path, ShaderType type) + private int CreateShader(string source, ShaderType type) { - var source = File.ReadAllText(path); var shader = GL.CreateShader(type); GL.ShaderSource(shader, source); diff --git a/Hypercube.Client/Graphics/Texturing/ITextureManager.cs b/Hypercube.Client/Graphics/Texturing/ITextureManager.cs index 75cc0ce..54c17b5 100644 --- a/Hypercube.Client/Graphics/Texturing/ITextureManager.cs +++ b/Hypercube.Client/Graphics/Texturing/ITextureManager.cs @@ -1,16 +1,18 @@ -namespace Hypercube.Client.Graphics.Texturing; +using Hypercube.Shared.Resources; + +namespace Hypercube.Client.Graphics.Texturing; public interface ITextureManager { - ITexture Create(string path); + ITexture Create(ResourcePath path); /// /// Creates ITexture, allows to set flipping mode /// /// Path to image /// DO FLIP /// ITexture - ITexture Create(string path, bool doFlip); + ITexture Create(ResourcePath path, bool doFlip); - ITextureHandle CreateHandler(string path); + ITextureHandle CreateHandler(ResourcePath path); ITextureHandle CreateHandler(ITexture texture); } \ No newline at end of file diff --git a/Hypercube.Client/Graphics/Texturing/TextureManager.cs b/Hypercube.Client/Graphics/Texturing/TextureManager.cs index b241759..c3642a0 100644 --- a/Hypercube.Client/Graphics/Texturing/TextureManager.cs +++ b/Hypercube.Client/Graphics/Texturing/TextureManager.cs @@ -1,21 +1,25 @@ -using StbImageSharp; +using Hypercube.Shared.Dependency; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; +using StbImageSharp; namespace Hypercube.Client.Graphics.Texturing; public sealed class TextureManager : ITextureManager { + [Dependency] private readonly IResourceManager _resourceManager = default!; + public TextureManager() { StbImage.stbi_set_flip_vertically_on_load(1); } - public ITexture Create(string path) + public ITexture Create(ResourcePath path) { - using var stream = File.OpenRead(path); - return Create(ImageResult.FromStream(stream, ColorComponents.RedGreenBlueAlpha)); + return Create(ImageResult.FromStream(_resourceManager.ReadFileContent(path) ?? throw new FileNotFoundException(), ColorComponents.RedGreenBlueAlpha)); } - public ITexture Create(string path, bool doFlip) + public ITexture Create(ResourcePath path, bool doFlip) { if (doFlip) StbImage.stbi_set_flip_vertically_on_load(0); @@ -30,7 +34,7 @@ public ITextureHandle CreateHandler(ITexture texture) return new TextureHandle(texture); } - public ITextureHandle CreateHandler(string path) + public ITextureHandle CreateHandler(ResourcePath path) { return CreateHandler(Create(path)); } diff --git a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs index 3f22442..884428e 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/GlfwWindowManager.Window.cs @@ -4,6 +4,8 @@ using Hypercube.Client.Graphics.Texturing; using Hypercube.Client.Graphics.Windows.Manager.Registrations; using Hypercube.Client.Utilities; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; using OpenTK.Windowing.Common; using OpenTK.Windowing.GraphicsLibraryFramework; using GlfwImage = OpenTK.Windowing.GraphicsLibraryFramework.Image; @@ -186,9 +188,9 @@ private bool TryGetWindow(Window* window, [NotNullWhen(true)] out GlfwWindowRegi return null; } - public IEnumerable LoadWindowIcon(ITextureManager textureMan, string resPath) + public IEnumerable LoadWindowIcon(ITextureManager textureMan, IResourceManager resourceManager, ResourcePath path) { - var files = Directory.EnumerateFiles(resPath, "*.png"); + var files = resourceManager.FindContentFiles(path); foreach (var file in files) { diff --git a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs index b9a4b3d..65bde37 100644 --- a/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs +++ b/Hypercube.Client/Graphics/Windows/Manager/IWindowManager.cs @@ -2,6 +2,8 @@ using Hypercube.Client.Graphics.OpenGL; using Hypercube.Client.Graphics.Texturing; using Hypercube.Shared.Math.Vector; +using Hypercube.Shared.Resources; +using Hypercube.Shared.Resources.Manager; namespace Hypercube.Client.Graphics.Windows.Manager; @@ -30,7 +32,7 @@ public interface IWindowManager : IDisposable void WindowSetVisible(WindowRegistration registration, bool visible); void WindowSetSize(WindowRegistration registration, Vector2Int size); void WindowSwapBuffers(WindowRegistration window); - IEnumerable LoadWindowIcon(ITextureManager textureMan, string resPath); + IEnumerable LoadWindowIcon(ITextureManager textureManager, IResourceManager resourceManager, ResourcePath resPath); void SetWindowIcons(WindowRegistration window, List images); nint GetProcAddress(string procName); diff --git a/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs new file mode 100644 index 0000000..9489f62 --- /dev/null +++ b/Hypercube.Shared/Resources/DirRoot/DirContentRoot.cs @@ -0,0 +1,64 @@ +using System.Diagnostics.CodeAnalysis; +using Hypercube.Shared.Logging; +using Hypercube.Shared.Utilities.Helpers; + +namespace Hypercube.Shared.Resources.DirRoot; + +public class DirContentRoot : IContentRoot +{ + private readonly DirectoryInfo _directory; + private Logger _logger; + + public DirContentRoot(DirectoryInfo directory, Logger logger) + { + _directory = directory; + _logger = logger; + } + + public bool TryGetFile(ResourcePath path, [NotNullWhen(true)] out Stream? stream) + { + if (!FileExists(path)) + { + stream = null; + return false; + } + + try + { + stream = File.Open(GetPath(path), FileMode.Open, FileAccess.Read); + return true; + } + catch (Exception ex) + { + _logger.Error(ex.Message); + stream = null; + } + return false; + } + + private bool FileExists(ResourcePath relPath) + { + var path = GetPath(relPath); + return File.Exists(path); + } + + private string GetPath(ResourcePath path) + { + return Path.GetFullPath(Path.Combine(_directory.FullName, path)); + } + + public IEnumerable FindFiles(ResourcePath path) + { + var fullPath = GetPath(path); + if (!Directory.Exists(fullPath)) + yield break; + + var paths = PathHelpers.GetFiles(fullPath); + + foreach (var filePath in paths) + { + var relPath = filePath.Substring(_directory.FullName.Length); + yield return ResourcePath.FromRelativeSystemPath(relPath, OperatingSystem.IsWindows() ? '\\' : '/'); + } + } +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/IContentRoot.cs b/Hypercube.Shared/Resources/IContentRoot.cs new file mode 100644 index 0000000..16f01e8 --- /dev/null +++ b/Hypercube.Shared/Resources/IContentRoot.cs @@ -0,0 +1,9 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Hypercube.Shared.Resources; + +public interface IContentRoot +{ + bool TryGetFile(ResourcePath path, [NotNullWhen(true)] out Stream? stream); + IEnumerable FindFiles(ResourcePath path); +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/Manager/IResourceManager.cs b/Hypercube.Shared/Resources/Manager/IResourceManager.cs new file mode 100644 index 0000000..ea58bf0 --- /dev/null +++ b/Hypercube.Shared/Resources/Manager/IResourceManager.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Hypercube.Shared.Resources.Manager; + +public interface IResourceManager +{ + StreamReader WrapStream(Stream stream); + void AddRoot(ResourcePath prefix, IContentRoot root); + void MountContentFolder(string file, ResourcePath? prefix = null); + Stream? ReadFileContent(ResourcePath path); + bool TryReadFileContent(ResourcePath path, [NotNullWhen(true)] out Stream? fileStream); + IEnumerable FindContentFiles(ResourcePath? path); + string ReadFileContentAllText(ResourcePath path); +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/Manager/ResourceManager.cs b/Hypercube.Shared/Resources/Manager/ResourceManager.cs new file mode 100644 index 0000000..03a697f --- /dev/null +++ b/Hypercube.Shared/Resources/Manager/ResourceManager.cs @@ -0,0 +1,130 @@ +using System.Diagnostics.CodeAnalysis; +using Hypercube.Shared.Logging; +using Hypercube.Shared.Resources.DirRoot; +using Hypercube.Shared.Utilities.Helpers; + +namespace Hypercube.Shared.Resources.Manager; + +public sealed class ResourceManager : IResourceManager +{ + private readonly Logger _logger = LoggingManager.GetLogger("resources"); + private (ResourcePath prefix, IContentRoot root)[] _roots = Array.Empty<(ResourcePath, IContentRoot)>(); + private readonly object _rootLock = new(); + + public void AddRoot(ResourcePath prefix, IContentRoot root) + { + lock (_rootLock) + { + var copy = _roots; + Array.Resize(ref copy, copy.Length + 1); + copy[^1] = (prefix, root); + _roots = copy; + } + } + + public void MountContentFolder(string file, ResourcePath? prefix = null) + { + prefix = ValidatePrefix(prefix); + + if (!Path.IsPathRooted(file)) + file = PathHelpers.GetExecRelativeFile(file); + + var dirInfo = new DirectoryInfo(file); + + if (!dirInfo.Exists) + throw new DirectoryNotFoundException($"Willing directory is not found. {dirInfo.FullName}"); + + var dirRoot = new DirContentRoot(dirInfo, LoggingManager.GetLogger("dirRoot")); + AddRoot(prefix.Value, dirRoot); + } + + public bool TryReadFileContent(ResourcePath path, [NotNullWhen(true)] out Stream? fileStream) + { + if (!path.Rooted) + throw new ArgumentException($"Path must be rooted: {path}"); + + if (path.Path.EndsWith(ResourcePath.Separator)) + { + fileStream = null; + return false; + } + + try + { + foreach (var (prefix, root) in _roots) + { + if (!path.TryRelativeTo(prefix, out var relative)) + continue; + + if (root.TryGetFile(relative.Value, out var stream)) + { + fileStream = stream; + return true; + } + } + } + catch (Exception ex) + { + _logger.Error(ex.Message); + } + + fileStream = null; + return false; + } + + public IEnumerable FindContentFiles(ResourcePath? path) + { + if (path is null) + throw new ArgumentNullException(nameof(path)); + + if (!path.Value.Rooted) + throw new ArgumentException("Path is not rooted", nameof(path)); + + + var returned = new HashSet(); + + foreach (var (prefix, root) in _roots) + { + if (!path.Value.TryRelativeTo(prefix, out var relative)) + continue; + + foreach (var filename in root.FindFiles(relative.Value)) + { + var newPath = prefix + filename; + + if (returned.Add(newPath)) + yield return newPath; + } + } + } + + public string ReadFileContentAllText(ResourcePath path) + { + var stream = ReadFileContent(path); + if (stream is null) + throw new ArgumentException($"File not found: {path.Path}"); + + return WrapStream(stream).ReadToEnd(); + } + + public Stream? ReadFileContent(ResourcePath path) + { + if (!TryReadFileContent(path, out var stream)) + return null; + + return stream; + } + + private ResourcePath ValidatePrefix(ResourcePath? prefix) + { + if (prefix.HasValue && !prefix.Value.Rooted) + throw new ArgumentException("Prefix must be rooted", nameof(prefix)); + + return prefix ?? "/"; + } + + public StreamReader WrapStream(Stream stream) + { + return new StreamReader(stream); + } +} \ No newline at end of file diff --git a/Hypercube.Shared/Resources/ResourcePath.cs b/Hypercube.Shared/Resources/ResourcePath.cs new file mode 100644 index 0000000..485ca27 --- /dev/null +++ b/Hypercube.Shared/Resources/ResourcePath.cs @@ -0,0 +1,183 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Hypercube.Shared.Resources; + +public readonly struct ResourcePath +{ + public const char Separator = '/'; + public const string SeparatorStr = "/"; + public const char WinSeparator = '\\'; + public const string WinSeparatorStr = @"\"; + public static readonly char SystemSeparator; + public static readonly string SystemSeparatorStr; + + public static readonly ResourcePath Self = "."; + + static ResourcePath() + { + SystemSeparator = OperatingSystem.IsWindows() ? '\\' : '/'; + SystemSeparatorStr = OperatingSystem.IsWindows() ? "\\" : "/"; + } + + public ResourcePath(string path) + { + if (OperatingSystem.IsWindows()) + { + Path = path.Replace('\\', '/'); + return; + } + + Path = path; + } + + public readonly string Path { get; } + + public bool Rooted => Path.Length > 0 && Path[0] == Separator; + public bool Relative => !Rooted; + public bool IsSelf => Path == Self.Path; + + public string FilenameWithExt + { + get + { + var sepIndex = Path.LastIndexOf('/') + 1; + return sepIndex == -1 ? string.Empty : Path[sepIndex..]; + } + } + + public string Extension + { + get + { + var filename = FilenameWithExt; + + var ind = filename.LastIndexOf('.'); + return ind <= 1 + ? string.Empty + : filename[ind..]; + } + } + + public string Filename + { + get + { + var filename = FilenameWithExt; + + var ind = filename.LastIndexOf('.'); + return ind <= 0 + ? filename + : filename[..ind]; + } + } + + public ResourcePath ParentDirectory + { + get + { + if (IsSelf) + return Self; + + var idx = Path.Length > 1 && Path[^1] == '/' + ? Path[..^1].LastIndexOf('/') + : Path.LastIndexOf('/'); + + return idx switch + { + -1 => Self, + 0 => new ResourcePath(Path[..1]), + _ => new ResourcePath(Path[..idx]) + }; + } + } + + public static implicit operator ResourcePath(string path) + { + return new ResourcePath(path); + } + public static implicit operator string(ResourcePath path) + { + return path.Path; + } + + public static ResourcePath operator +(ResourcePath l, ResourcePath r) + { + if (r.IsSelf) + return l; + + if (r.Rooted) + return r; + + if (l.Path == string.Empty) + return new ResourcePath("/" + r.Path); + + if (l.Path.EndsWith("/")) + return new ResourcePath(l.Path + r.Path); + + + return new ResourcePath($"{l.Path}/{r.Path}"); + } + public bool TryRelativeTo(ResourcePath basePath, [NotNullWhen(true)] out ResourcePath? relative) + { + if (this == basePath) + { + relative = Self; + return true; + } + + if (basePath == Self && Relative) + { + relative = this; + return true; + } + + if (Path.StartsWith(basePath.Path)) + { + var x = Path[basePath.Path.Length..].Trim('/'); + relative = x == string.Empty ? Self : new ResourcePath(x); + return true; + } + + relative = null; + return false; + } + + public static ResourcePath FromRelativeSystemPath(string path, char newSeparator) + { + // ReSharper disable once RedundantArgumentDefaultValue + return new ResourcePath(path.Replace(newSeparator, '/')); + } + + public static bool Equals(ResourcePath a, ResourcePath b) + { + return a.Path == b.Path; + } + + public bool Equals(ResourcePath b) + { + return Path == b.Path; + } + + public override bool Equals(object? other) + { + if (other is null || other is not ResourcePath b) + return false; + + return Path == b.Path; + } + + public override int GetHashCode() + { + return Path.GetHashCode(); + } + + public static bool operator ==(ResourcePath a, ResourcePath b) + { + return a.Equals(b); + } + + public static bool operator !=(ResourcePath a, ResourcePath b) + { + return !a.Equals(b); + } +} \ No newline at end of file diff --git a/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs new file mode 100644 index 0000000..3067606 --- /dev/null +++ b/Hypercube.Shared/Utilities/Helpers/PathHelpers.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace Hypercube.Shared.Utilities.Helpers; + +public static class PathHelpers +{ + public static string GetExecDirectory() + { + return AppDomain.CurrentDomain.BaseDirectory; + } + + public static string GetExecRelativeFile(string file) + { + return Path.GetFullPath(Path.Combine(GetExecDirectory(), file)); + } + + public static IEnumerable GetFiles(string path) + { + return Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories); + } + + public static bool IsFileSystemCaseSensitive() + { + return !OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS(); + } +} \ No newline at end of file diff --git a/Hypercube.UnitTests/Hypercube.UnitTests.csproj b/Hypercube.UnitTests/Hypercube.UnitTests.csproj index f2eeddd..d3f807d 100644 --- a/Hypercube.UnitTests/Hypercube.UnitTests.csproj +++ b/Hypercube.UnitTests/Hypercube.UnitTests.csproj @@ -19,6 +19,7 @@ + diff --git a/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs b/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs new file mode 100644 index 0000000..c66a637 --- /dev/null +++ b/Hypercube.UnitTests/ResourceManager/ResourceManagerTests.cs @@ -0,0 +1,22 @@ +namespace Hypercube.UnitTests.ResourceManager; + +public class ResourceManagerTests +{ + [Test] + public void ReadFileTest() + { + var resourceMan = new Shared.Resources.Manager.ResourceManager(); + resourceMan.MountContentFolder("Resources", "/"); + + if (!resourceMan.TryReadFileContent("/Tests/testFile.txt", out var stream)) + { + Assert.Fail("Unable to read file"); + return; + } + + var wrapped = resourceMan.WrapStream(stream); + var read = wrapped.ReadToEnd(); + Assert.That(read == "hey"); + Assert.Pass("Read file successfully"); + } +} \ No newline at end of file diff --git a/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs new file mode 100644 index 0000000..4fc9299 --- /dev/null +++ b/Hypercube.UnitTests/ResourceManager/ResourcePathTest.cs @@ -0,0 +1,64 @@ +using Hypercube.Shared.Resources; + +namespace Hypercube.UnitTests.ResourceManager; + +public class ResourcePathTest +{ + [Test] + public void GetFilenameTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Filename == "path"); + + Assert.Pass($"ResPath file name is {resPath.Filename}"); + } + + [Test] + public void GetExtensionTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Extension == ".txt"); + Assert.Pass($"ResPath ext is {resPath.Extension}"); + } + + [Test] + public void RootedTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.Rooted); + Assert.Pass("Res path is rooted"); + } + + [Test] + public void ParentDirTest() + { + var resPath = new ResourcePath("/Rooted/path.txt"); + Assert.That(resPath.ParentDirectory.Path == "/Rooted"); + Assert.Pass("Parent directory passed"); + } + + [Test] + public void PathConcatTest() + { + var resPath = new ResourcePath("/Rooted/"); + var resPath2 = new ResourcePath("path.txt"); + + var concated = resPath + resPath2; + Assert.That(concated.Path == "/Rooted/path.txt"); + + Assert.Pass($"Concatenated path is equal to {concated.Path}"); + } + + [Test] + public void PathEqualityTest() + { + var resPath = new ResourcePath("/Rooted/"); + var resPath2 = new ResourcePath("/Rooted/"); + var resPath3 = new ResourcePath("path.txt"); + + Assert.That(resPath != resPath3); + Assert.That(resPath == resPath2); + + Assert.Pass("Passed equality test"); + } +} \ No newline at end of file diff --git a/Resources/Tests/testFile.txt b/Resources/Tests/testFile.txt new file mode 100644 index 0000000..c09a35f --- /dev/null +++ b/Resources/Tests/testFile.txt @@ -0,0 +1 @@ +hey \ No newline at end of file