diff --git a/CHANGELOG.md b/CHANGELOG.md index 718e9eb..bd80964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## v5.0.1 + +### 🐛 Bug Fixes + +- Fixed a bug where internal packages were not available at installation. Preventing NotoriousTest from being installed. +- All internal packages are now available to download from Nuget.org, but every class is internal. Which makes them + impossible to use. + ## v5.0.0 ### ✨ Features diff --git a/Directory.Build.props b/Directory.Build.props index 513e3ae..739c5f4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ icon.png LICENSE.txt README.md - 5.0.0 + 5.0.1 true high all diff --git a/DoggyDog/DoggyDog.csproj b/DoggyDog/DoggyDog.csproj index 568a89c..94f9b4b 100644 --- a/DoggyDog/DoggyDog.csproj +++ b/DoggyDog/DoggyDog.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/DoggyDog/DoggyDogRecoveryWatchdog.cs b/DoggyDog/DoggyDogRecoveryWatchdog.cs index 4adf86a..761eb72 100644 --- a/DoggyDog/DoggyDogRecoveryWatchdog.cs +++ b/DoggyDog/DoggyDogRecoveryWatchdog.cs @@ -4,7 +4,7 @@ using NotoriousTest.Core; using NotoriousTest.Core.Infrastructures.Cleaner; using NotoriousTest.Core.Registry; -using NotoriousTest.Watchdog; +using NotoriousTest.Internal.Watchdog; using System.Diagnostics; using System.Reflection; diff --git a/DoggyDog/Program.cs b/DoggyDog/Program.cs index 1940a72..c910339 100644 --- a/DoggyDog/Program.cs +++ b/DoggyDog/Program.cs @@ -4,7 +4,7 @@ using Microsoft.Data.Sqlite; -using NotoriousTest.SqlLiteRegistry; +using NotoriousTest.Internal.SqlLiteRegistry; Logger logger = Logger.Instance; diff --git a/NotoriousTest.IntegrationTests/DoggyDog/DoggyDogTestFramework.cs b/NotoriousTest.IntegrationTests/DoggyDog/DoggyDogTestFramework.cs index a95806f..fc71130 100644 --- a/NotoriousTest.IntegrationTests/DoggyDog/DoggyDogTestFramework.cs +++ b/NotoriousTest.IntegrationTests/DoggyDog/DoggyDogTestFramework.cs @@ -9,7 +9,7 @@ using NotoriousTest.Core.Logger; using NotoriousTest.Core.Registry; using NotoriousTest.Sqlite; -using NotoriousTest.SqlLiteRegistry; +using NotoriousTest.Internal.SqlLiteRegistry; using System.Diagnostics; using System.Runtime.InteropServices; diff --git a/NotoriousTest.IntegrationTests/Infrastructure/InfrastructureLifecycleTestsBase.cs b/NotoriousTest.IntegrationTests/Infrastructure/InfrastructureLifecycleTestsBase.cs index 32bcc3e..84a3677 100644 --- a/NotoriousTest.IntegrationTests/Infrastructure/InfrastructureLifecycleTestsBase.cs +++ b/NotoriousTest.IntegrationTests/Infrastructure/InfrastructureLifecycleTestsBase.cs @@ -3,7 +3,7 @@ using NotoriousTest.Core; using NotoriousTest.Core.Logger; using NotoriousTest.IntegrationTests.SystemUnderTest; -using NotoriousTest.SqlLiteRegistry; +using NotoriousTest.Internal.SqlLiteRegistry; using NotoriousTest.XUnit; namespace NotoriousTest.IntegrationTests.Infrastructure; diff --git a/NotoriousTest.Internal.Runtime/NotoriousTest.Internal.Runtime.csproj b/NotoriousTest.Internal.Runtime/NotoriousTest.Internal.Runtime.csproj new file mode 100644 index 0000000..11ea6a6 --- /dev/null +++ b/NotoriousTest.Internal.Runtime/NotoriousTest.Internal.Runtime.csproj @@ -0,0 +1,18 @@ + + + net8.0 + NotoriousTest.Internal.Runtime + [Internal] Provides the runtime configuration layer for NotoriousTest. Not intended for direct consumption — use the NotoriousTest package instead. + true + true + + + + + + + + + + + diff --git a/NotoriousTest.Internal.Runtime/RuntimeConfigurationEntity.cs b/NotoriousTest.Internal.Runtime/RuntimeConfigurationEntity.cs new file mode 100644 index 0000000..eab5ac6 --- /dev/null +++ b/NotoriousTest.Internal.Runtime/RuntimeConfigurationEntity.cs @@ -0,0 +1,11 @@ +namespace NotoriousTest.Internal.Runtime; + +internal record RuntimeConfigurationEntity(RuntimeOptions RuntimeOptions); + +internal record RuntimeOptions( + string Tfm, + FrameworkEntry? Framework, + FrameworkEntry[]? Frameworks +); + +internal record FrameworkEntry(string Name, string Version); diff --git a/NotoriousTest.Internal.Runtime/RuntimeConfigurationProvider.cs b/NotoriousTest.Internal.Runtime/RuntimeConfigurationProvider.cs new file mode 100644 index 0000000..e4b1b03 --- /dev/null +++ b/NotoriousTest.Internal.Runtime/RuntimeConfigurationProvider.cs @@ -0,0 +1,88 @@ +using System.Diagnostics; +using System.Reflection; +using System.Text.Json; +using NotoriousTest.Core.Logger; +using NotoriousTest.Core.Runtime; + +namespace NotoriousTest.Internal.Runtime; + +internal class RuntimeConfigurationProvider : IRuntime +{ + private const string RuntimeFile = "runtimeconfig.json"; + private readonly ITestLogger _logger; + + private readonly JsonSerializerOptions JSON_OPTIONS = new() { PropertyNameCaseInsensitive = true }; + private RuntimeConfiguration? _cache; + + public RuntimeConfigurationProvider(ITestLogger logger) + { + _logger = logger; + } + + public RuntimeConfiguration? GetSupportedRuntimes(Assembly executingAssembly) + { + if (_cache != null) return _cache; + + string assemblyPath = executingAssembly.Location; + string runtimePath = Path.ChangeExtension(assemblyPath, RuntimeFile); + + if (!File.Exists(runtimePath)) + return null; + + + string json = File.ReadAllText(runtimePath); + + + RuntimeConfigurationEntity? entity = JsonSerializer.Deserialize(json, JSON_OPTIONS); + if (entity == null) + return null; + + FrameworkEntry[]? frameworkEntries = entity.RuntimeOptions.Frameworks ?? [entity.RuntimeOptions.Framework!]; + SupportedFramework[]? frameworks = frameworkEntries.Select(ToDomain).ToArray(); + + var result = new RuntimeConfiguration(entity.RuntimeOptions.Tfm, frameworks); + _cache = result; + return result; + } + + private SupportedFramework ToDomain(FrameworkEntry entry) => + new(entry.Name, entry.Version, GetNearestVersionDirectory(entry)); + + private string? GetNearestVersionDirectory(FrameworkEntry framework) + { + string dotnetRoot = GetDotnetRoot(); + + string frameworkPath = $"{dotnetRoot}/shared/{framework.Name}"; + + string prefix = $"{new Version(framework.Version).Major}.{new Version(framework.Version).Minor}."; + // Find the nearest version + string? best = Directory.GetDirectories(frameworkPath) + .Where(d => Path.GetFileName(d).StartsWith(prefix)) + .OrderByDescending(d => new Version(Path.GetFileName(d))) + .FirstOrDefault(); + + return best; + } + + private string GetDotnetRoot() + { + string? dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); + + if (!string.IsNullOrWhiteSpace(dotnetRoot)) + return dotnetRoot; + + string mainModule = Process.GetCurrentProcess().MainModule!.FileName; + + // If the test project is self-contained (wich is rare), the main module will be the test executable itself. + bool isNotSelfContained = Path.GetFileNameWithoutExtension(mainModule) + .Equals("dotnet", StringComparison.OrdinalIgnoreCase); + + if (isNotSelfContained) + return Path.GetDirectoryName(mainModule)!; + + return Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + "dotnet" + ); + } +} diff --git a/NotoriousTest.Internal.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs b/NotoriousTest.Internal.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs new file mode 100644 index 0000000..dd45349 --- /dev/null +++ b/NotoriousTest.Internal.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs @@ -0,0 +1,55 @@ +using System.Globalization; +using System.Text.Json; +using NotoriousTest.Core.Registry; + +namespace NotoriousTest.Internal.SqlLiteRegistry; + +internal class InfrastructureRegistryEntryEntity +{ + public string InfrastructureId { get; set; } + public string InfrastructureType { get; set; } + public string EnvironmentId { get; set; } + public int ProcessID { get; set; } + public string? Metadata { get; set; } + public string? MetadataType { get; set; } + public string? LastResetDate { get; set; } + public string CreationDate { get; set; } + public string UpdateDate { get; set; } + + internal static InfrastructureRegistryEntryEntity? FromDomain(InfrastuctureRegistryEntry entry) => + new() + { + InfrastructureId = entry.InfrastructureId.ToString(), + InfrastructureType = entry.InfrastructureType.AssemblyQualifiedName, + EnvironmentId = entry.EnvironmentId.ToString(), + ProcessID = entry.ProcessID, + Metadata = JsonSerializer.Serialize(entry.Metadata), + MetadataType = entry.Metadata?.GetType().AssemblyQualifiedName, + LastResetDate = entry.LastResetDate?.ToString("O") + }; + + internal InfrastuctureRegistryEntry ToDomain() + { + Type? metadataType = null; + + if (MetadataType != null) metadataType = Type.GetType(MetadataType); + + var infraType = Type.GetType(InfrastructureType); + if (infraType == null) throw new TypeLoadException($"Unable to load type {InfrastructureType}"); + + return new InfrastuctureRegistryEntry + { + InfrastructureId = Guid.Parse(InfrastructureId), + InfrastructureType = Type.GetType(InfrastructureType), + EnvironmentId = Guid.Parse(EnvironmentId), + ProcessID = ProcessID, + Metadata = !(Metadata is null) && !(metadataType is null) + ? JsonSerializer.Deserialize(Metadata, metadataType) + : null, + LastResetDate = + LastResetDate != null ? DateTime.Parse(LastResetDate, null, DateTimeStyles.RoundtripKind) : null, + CreationDate = DateTime.Parse(CreationDate, null, DateTimeStyles.RoundtripKind), + UpdateDate = DateTime.Parse(UpdateDate, null, DateTimeStyles.RoundtripKind) + }; + } +} diff --git a/NotoriousTest.Internal.SqlLiteRegistry/NotoriousTest.Internal.SqlLiteRegistry.csproj b/NotoriousTest.Internal.SqlLiteRegistry/NotoriousTest.Internal.SqlLiteRegistry.csproj new file mode 100644 index 0000000..62dbd5a --- /dev/null +++ b/NotoriousTest.Internal.SqlLiteRegistry/NotoriousTest.Internal.SqlLiteRegistry.csproj @@ -0,0 +1,25 @@ + + + net8.0 + NotoriousTest.Internal.SqlLiteRegistry + [Internal] SQLite-backed infrastructure registry for NotoriousTest. Not intended for direct consumption — use the NotoriousTest package instead. + true + true + + + + + + + + + + + + + + + + + + diff --git a/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProvider.cs b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProvider.cs new file mode 100644 index 0000000..10b61fb --- /dev/null +++ b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProvider.cs @@ -0,0 +1,126 @@ +using System.Data; +using Dapper; +using Microsoft.Data.Sqlite; +using NotoriousTest.Core; +using NotoriousTest.Core.Registry; + +namespace NotoriousTest.Internal.SqlLiteRegistry; + +internal partial class SqliteRegistryProvider : IRegistry +{ + private readonly SqliteRegistryProviderConfiguration _configuration; + + public SqliteRegistryProvider(SqliteRegistryProviderConfiguration configuration) + { + _configuration = configuration; + } + + private SqliteConnectionStringBuilder ConnectionString => new(_configuration.ConnectionString); + + private SqliteConnection Connection + { + get + { + var connection = new SqliteConnection(ConnectionString.ToString()); + + if (connection.State != ConnectionState.Open) connection.Open(); + + connection.Execute("PRAGMA SYNCHRONOUS=NORMAL;PRAGMA JOURNAL_MODE=WAL;"); + return connection; + } + } + + public event Action OnInfrastructureReset; + public event Action OnInfrastructureDestroyed; + public event Action OnInfrastructureCreated; + + + public async Task Ensure() + { + string directory = Path.GetDirectoryName(ConnectionString.DataSource); + if (!Directory.Exists(directory)) Directory.CreateDirectory(directory); + + await using SqliteConnection connection = Connection; + + await connection.ExecuteAsync(ENSURE_REGISTRY); + } + + public async Task Register(InfrastuctureRegistryEntry entry) + { + await using SqliteConnection connection = Connection; + InfrastructureRegistryEntryEntity entity = + await connection.QuerySingleAsync(REGISTER_INFRASTRUCTURE, + InfrastructureRegistryEntryEntity.FromDomain(entry)); + + return entity.ToDomain(); + } + + public async Task Remove(Guid id) + { + await using SqliteConnection connection = Connection; + + int deletedRows = + await connection.ExecuteAsync(REMOVE_INFRASTRUCTURE, new { InfrastructureId = id.ToString() }); + return deletedRows > 0; + } + + public async Task NotifyReset(Guid id) + { + await using SqliteConnection connection = Connection; + + await connection.ExecuteAsync(UPDATE_INFRASTRUCTURE_RESET_DATE, new { InfrastructureId = id.ToString() }); + } + + public async Task> GetByProcessId(int processId) + { + await using SqliteConnection connection = Connection; + + IEnumerable entries = + await connection.QueryAsync(GET_BY_PROCESS_ID, + new { ProcessID = processId }); + return entries.Select(entry => entry.ToDomain()); + } + + public async Task> GetByEnvironmentId(EnvironmentId environmentId) + { + await using SqliteConnection connection = Connection; + + IEnumerable entries = + await connection.QueryAsync(GET_BY_ENVIRONMENT_ID, + new { EnvironmentId = environmentId.Value.ToString() }); + return entries.Select(entry => entry.ToDomain()); + } + + public async Task Watch(EnvironmentId environmentId, CancellationToken ct) + { + var snapshot = (await GetByEnvironmentId(environmentId)).ToDictionary(x => x.InfrastructureId); + + do + { + IEnumerable infras = await GetByEnvironmentId(environmentId); + + foreach (InfrastuctureRegistryEntry infra in infras) + { + if (!snapshot.TryGetValue(infra.InfrastructureId, out InfrastuctureRegistryEntry? previous)) + OnInfrastructureCreated?.Invoke(infra); + + if (previous is not null && infra.LastResetDate != previous.LastResetDate) + OnInfrastructureReset?.Invoke(infra); + } + + foreach ((Guid key, InfrastuctureRegistryEntry infrastructure) in snapshot) + if (!infras.Any(i => i.InfrastructureId == infrastructure.InfrastructureId)) + OnInfrastructureDestroyed?.Invoke(infrastructure); + + snapshot = infras.ToDictionary(x => x.InfrastructureId); + + try + { + await Task.Delay(10, ct); + } + catch (OperationCanceledException) + { + } + } while (!ct.IsCancellationRequested); + } +} diff --git a/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs new file mode 100644 index 0000000..601a00a --- /dev/null +++ b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs @@ -0,0 +1,6 @@ +namespace NotoriousTest.Internal.SqlLiteRegistry; + +internal class SqliteRegistryProviderConfiguration +{ + public string ConnectionString { get; set; } +} diff --git a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderQueries.cs b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderQueries.cs similarity index 69% rename from NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderQueries.cs rename to NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderQueries.cs index 07262c7..2e622a9 100644 --- a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderQueries.cs +++ b/NotoriousTest.Internal.SqlLiteRegistry/SqliteRegistryProviderQueries.cs @@ -1,9 +1,10 @@ -namespace NotoriousTest.SqlLiteRegistry +namespace NotoriousTest.Internal.SqlLiteRegistry; + +internal partial class SqliteRegistryProvider { - internal partial class SqliteRegistryProvider - { - protected const string TABLE_NAME = "InfrastructureRegistry"; - protected const string ENSURE_REGISTRY = @$" + protected const string TABLE_NAME = "InfrastructureRegistry"; + + protected const string ENSURE_REGISTRY = @$" CREATE TABLE IF NOT EXISTS {TABLE_NAME}( InfrastructureId TEXT PRIMARY KEY, InfrastructureType TEXT NOT NULL, @@ -17,30 +18,29 @@ LastResetDate TEXT ) "; - protected const string REGISTER_INFRASTRUCTURE = @$" + protected const string REGISTER_INFRASTRUCTURE = @$" INSERT INTO {TABLE_NAME}(InfrastructureId, InfrastructureType, EnvironmentId, ProcessID, Metadata, MetadataType, CreationDate, UpdateDate, LastResetDate) VALUES(@InfrastructureId, @InfrastructureType, @EnvironmentId, @ProcessID, @Metadata, @MetadataType, datetime('now', 'utc'), datetime('now', 'utc'), null) RETURNING * "; - protected const string REMOVE_INFRASTRUCTURE = @$" + protected const string REMOVE_INFRASTRUCTURE = @$" DELETE FROM {TABLE_NAME} WHERE InfrastructureId = @InfrastructureId; "; - protected const string GET_BY_PROCESS_ID = @$" + protected const string GET_BY_PROCESS_ID = @$" SELECT * FROM {TABLE_NAME} WHERE ProcessID = @ProcessID; "; - protected const string GET_BY_ENVIRONMENT_ID = @$" + protected const string GET_BY_ENVIRONMENT_ID = @$" SELECT * FROM {TABLE_NAME} WHERE EnvironmentId = @EnvironmentId; "; - protected const string GET_BY_ROWID = $@" + protected const string GET_BY_ROWID = $@" SELECT * FROM {TABLE_NAME} WHERE rowid = @rowId "; - protected const string UPDATE_INFRASTRUCTURE_RESET_DATE = @$" + protected const string UPDATE_INFRASTRUCTURE_RESET_DATE = @$" UPDATE {TABLE_NAME} SET LastResetDate = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE InfrastructureId = @InfrastructureId "; - } } diff --git a/NotoriousTest.Internal.TestSettings/NotoriousTest.Internal.TestSettings.csproj b/NotoriousTest.Internal.TestSettings/NotoriousTest.Internal.TestSettings.csproj new file mode 100644 index 0000000..66cc5cd --- /dev/null +++ b/NotoriousTest.Internal.TestSettings/NotoriousTest.Internal.TestSettings.csproj @@ -0,0 +1,19 @@ + + + net8.0 + NotoriousTest.Internal.TestSettings + [Internal] JSON-based test settings provider for NotoriousTest. Not intended for direct consumption — use the NotoriousTest package instead. + true + true + + + + + + + + + + + + diff --git a/NotoriousTest.Internal.TestSettings/TestSettingsProvider.cs b/NotoriousTest.Internal.TestSettings/TestSettingsProvider.cs new file mode 100644 index 0000000..b63a1d2 --- /dev/null +++ b/NotoriousTest.Internal.TestSettings/TestSettingsProvider.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Configuration; +using NotoriousTest.Core.Settings; + +namespace NotoriousTest.Internal.TestSettings; + +internal class TestSettingsProvider : ITestSettingsProvider +{ + private IConfiguration _cache; + + public IConfiguration Find() + { + if (_cache == null) + { + string? settingsPath = FindFile("testsettings.json"); + + + var configurationBuilder = new ConfigurationBuilder(); + if (settingsPath != null) configurationBuilder.AddJsonFile(settingsPath); + + _cache = configurationBuilder.Build(); + } + + return _cache; + } + + public T? Get(string key) where T : new() + { + IConfigurationSection section = Find().GetSection(key); + if (!section.Exists()) + return default; + + var config = new T(); + section.Bind(config); + + return config; + } + + private static string? FindFile(string fileName) => + Directory + .EnumerateFiles(AppContext.BaseDirectory, fileName, SearchOption.AllDirectories) + .FirstOrDefault(); +} diff --git a/NotoriousTest.Internal.Watchdog/DoggyDogWatchDog.cs b/NotoriousTest.Internal.Watchdog/DoggyDogWatchDog.cs new file mode 100644 index 0000000..66b2c22 --- /dev/null +++ b/NotoriousTest.Internal.Watchdog/DoggyDogWatchDog.cs @@ -0,0 +1,95 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using NotoriousTest.Core; +using NotoriousTest.Core.Logger; +using NotoriousTest.Core.Watchdog; +using NotoriousTest.Internal.SqlLiteRegistry; + +namespace NotoriousTest.Internal.Watchdog; + +internal class DoggyDogWatchDog : IWatchDog +{ + private readonly DoggyDogWatchdogConfiguration _config; + private readonly ITestLogger _logger; + private readonly SqliteRegistryProviderConfiguration _registyConfiguration; + + public DoggyDogWatchDog(SqliteRegistryProviderConfiguration registryConfiguration, ITestLogger logger, + DoggyDogWatchdogConfiguration config) + { + _registyConfiguration = registryConfiguration; + _logger = logger; + _config = config; + } + + public Process Start(Assembly currentAssembly, int currentPid, EnvironmentId environmentId, + IEnumerable? runtimePaths) + { + string assemblyPath = currentAssembly.Location; + string? runtimesParams = runtimePaths == null ? null : string.Join("|", runtimePaths); + + if (_config.ManualLaunch) + return WaitForDoggyDogToLaunch(currentPid, environmentId, assemblyPath, runtimesParams); + return LaunchDoggyDog(currentPid, environmentId, assemblyPath, runtimesParams) ?? + throw new Exception("Could not launch DoggyDog"); + } + + public void SendSuccessSignal(EnvironmentId contextId) => + File.WriteAllText(Path.Combine(Path.GetTempPath(), $"nt-{contextId.Value}.signal"), "OK"); + + private Process? LaunchDoggyDog(int currentPid, EnvironmentId contextId, string assemblyPath, + string? runtimesParams) + { + string watchdogPath = Path.Combine(AppContext.BaseDirectory, + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "DoggyDog.exe" : "DoggyDog"); + + string runtimeParameter = runtimesParams == null ? "" : $"--runtimes \"{runtimesParams}\" "; + var process = Process.Start(new ProcessStartInfo + { + FileName = watchdogPath, + Arguments = $"--pid {currentPid} " + + $"--assembly \"{assemblyPath}\" " + + $"--connectionString \"{_registyConfiguration.ConnectionString}\" " + + $"--environment {contextId.Value} " + + runtimeParameter + + "--loglevel Info", + UseShellExecute = true, + CreateNoWindow = false + }); + + + return process; + } + + private Process WaitForDoggyDogToLaunch(int currentPid, EnvironmentId environmentId, string assemblyPath, + string? runtimesParams) + { + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_PID", currentPid.ToString(), EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_ASSEMBLY", assemblyPath, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_CONNECTIONSTRING", _registyConfiguration.ConnectionString, + EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_ENVIRONMENT", environmentId.Value.ToString(), + EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_RUNTIMES", runtimesParams, EnvironmentVariableTarget.User); + Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_LOGLEVEL", "Debug", EnvironmentVariableTarget.User); + + Process? doggyDogProcess; + do + { + _logger.Log("Waiting for DoggyDog to launch...", environmentId); + doggyDogProcess = Process.GetProcessesByName("DoggyDog")?.FirstOrDefault(); + Thread.Sleep(5000); + } while (doggyDogProcess == null); + + return doggyDogProcess; + } + + public static bool ReadSuccessSignal(EnvironmentId contextId) + { + string path = Path.Combine(Path.GetTempPath(), $"nt-{contextId.Value}.signal"); + bool isSuccess = File.Exists(path); + if (isSuccess) File.Delete(path); + + return isSuccess; + } +} diff --git a/NotoriousTest.Watchdog/DoggyDogWatchdogConfiguration.cs b/NotoriousTest.Internal.Watchdog/DoggyDogWatchdogConfiguration.cs similarity index 79% rename from NotoriousTest.Watchdog/DoggyDogWatchdogConfiguration.cs rename to NotoriousTest.Internal.Watchdog/DoggyDogWatchdogConfiguration.cs index 1d6cdaa..a65a46c 100644 --- a/NotoriousTest.Watchdog/DoggyDogWatchdogConfiguration.cs +++ b/NotoriousTest.Internal.Watchdog/DoggyDogWatchdogConfiguration.cs @@ -1,4 +1,4 @@ -namespace NotoriousTest.Core.Watchdog +namespace NotoriousTest.Internal.Watchdog { public class DoggyDogWatchdogConfiguration { diff --git a/NotoriousTest.Internal.Watchdog/NotoriousTest.Internal.Watchdog.csproj b/NotoriousTest.Internal.Watchdog/NotoriousTest.Internal.Watchdog.csproj new file mode 100644 index 0000000..cdd92e9 --- /dev/null +++ b/NotoriousTest.Internal.Watchdog/NotoriousTest.Internal.Watchdog.csproj @@ -0,0 +1,17 @@ + + + net8.0 + NotoriousTest.Internal.Watchdog + [Internal] Watchdog service for detecting and recovering stale test environments in NotoriousTest. Not intended for direct consumption — use the NotoriousTest package instead. + true + true + + + + + + + + + + diff --git a/NotoriousTest.NoDoggyDog.slnf b/NotoriousTest.NoDoggyDog.slnf index 5e472c9..a5a07f6 100644 --- a/NotoriousTest.NoDoggyDog.slnf +++ b/NotoriousTest.NoDoggyDog.slnf @@ -3,10 +3,10 @@ "path": "NotoriousTest.slnx", "projects": [ "NotoriousTest.Core\\NotoriousTest.Core.csproj", - "NotoriousTest.Runtime\\NotoriousTest.Runtime.csproj", - "NotoriousTest.SqlLiteRegistry\\NotoriousTest.SqlLiteRegistry.csproj", - "NotoriousTest.TestSettings\\NotoriousTest.TestSettings.csproj", - "NotoriousTest.Watchdog\\NotoriousTest.Watchdog.csproj", + "NotoriousTest.Internal.Runtime\\NotoriousTest.Internal.Runtime.csproj", + "NotoriousTest.Internal.SqlLiteRegistry\\NotoriousTest.Internal.SqlLiteRegistry.csproj", + "NotoriousTest.Internal.TestSettings\\NotoriousTest.Internal.TestSettings.csproj", + "NotoriousTest.Internal.Watchdog\\NotoriousTest.Internal.Watchdog.csproj", "NotoriousTest\\NotoriousTest.csproj", "NotoriousTest.MSTest\\NotoriousTest.MSTest.csproj", "NotoriousTest.NUnit\\NotoriousTest.NUnit.csproj", diff --git a/NotoriousTest.Runtime/NotoriousTest.Runtime.csproj b/NotoriousTest.Runtime/NotoriousTest.Runtime.csproj deleted file mode 100644 index 5ca8149..0000000 --- a/NotoriousTest.Runtime/NotoriousTest.Runtime.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - net8.0 - false - - - - - - - - diff --git a/NotoriousTest.Runtime/RuntimeConfigurationEntity.cs b/NotoriousTest.Runtime/RuntimeConfigurationEntity.cs deleted file mode 100644 index afd7cba..0000000 --- a/NotoriousTest.Runtime/RuntimeConfigurationEntity.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NotoriousTest.Runtime -{ - public record RuntimeConfigurationEntity(RuntimeOptions RuntimeOptions); - - public record RuntimeOptions( - string Tfm, - FrameworkEntry? Framework, - FrameworkEntry[]? Frameworks - ); - - public record FrameworkEntry(string Name, string Version); -} diff --git a/NotoriousTest.Runtime/RuntimeConfigurationProvider.cs b/NotoriousTest.Runtime/RuntimeConfigurationProvider.cs deleted file mode 100644 index 1db7dfb..0000000 --- a/NotoriousTest.Runtime/RuntimeConfigurationProvider.cs +++ /dev/null @@ -1,88 +0,0 @@ -using NotoriousTest.Core.Logger; -using NotoriousTest.Core.Runtime; - -using System.Diagnostics; -using System.Reflection; -using System.Text.Json; - -namespace NotoriousTest.Runtime -{ - public class RuntimeConfigurationProvider : IRuntime - { - private const string RuntimeFile = "runtimeconfig.json"; - private readonly ITestLogger _logger; - private RuntimeConfiguration? _cache = null; - - private readonly JsonSerializerOptions JSON_OPTIONS = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; - public RuntimeConfigurationProvider(ITestLogger logger) - { - _logger = logger; - } - - public RuntimeConfiguration? GetSupportedRuntimes(Assembly executingAssembly) - { - if (_cache != null) return _cache; - - string assemblyPath = executingAssembly.Location; - string runtimePath = Path.ChangeExtension(assemblyPath, RuntimeFile); - - if (!File.Exists(runtimePath)) - return null; - - - string json = File.ReadAllText(runtimePath); - - - RuntimeConfigurationEntity? entity = JsonSerializer.Deserialize(json, JSON_OPTIONS); - if (entity == null) - return null; - - FrameworkEntry[]? frameworkEntries = entity.RuntimeOptions.Frameworks ?? [entity.RuntimeOptions.Framework!]; - SupportedFramework[]? frameworks = frameworkEntries.Select(ToDomain).ToArray(); - - var result = new RuntimeConfiguration(entity.RuntimeOptions.Tfm, frameworks); - _cache = result; - return result; - } - - private SupportedFramework ToDomain(FrameworkEntry entry) => new(entry.Name, entry.Version, GetNearestVersionDirectory(entry)); - - private string? GetNearestVersionDirectory(FrameworkEntry framework) - { - string dotnetRoot = GetDotnetRoot(); - - string frameworkPath = $"{dotnetRoot}/shared/{framework.Name}"; - - string prefix = $"{new Version(framework.Version).Major}.{new Version(framework.Version).Minor}."; - // Find the nearest version - string? best = Directory.GetDirectories(frameworkPath) - .Where(d => Path.GetFileName(d).StartsWith(prefix)) - .OrderByDescending(d => new Version(Path.GetFileName(d))) - .FirstOrDefault(); - - return best; - } - private string GetDotnetRoot() - { - - string? dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT"); - - if (!string.IsNullOrWhiteSpace(dotnetRoot)) - return dotnetRoot; - - string mainModule = Process.GetCurrentProcess().MainModule!.FileName; - - // If the test project is self-contained (wich is rare), the main module will be the test executable itself. - bool isNotSelfContained = Path.GetFileNameWithoutExtension(mainModule) - .Equals("dotnet", StringComparison.OrdinalIgnoreCase); - - if (isNotSelfContained) - return Path.GetDirectoryName(mainModule)!; - - return Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), - "dotnet" - ); - } - } -} diff --git a/NotoriousTest.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs b/NotoriousTest.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs deleted file mode 100644 index c5ea2be..0000000 --- a/NotoriousTest.SqlLiteRegistry/InfrastructureRegistryEntryEntity.cs +++ /dev/null @@ -1,64 +0,0 @@ -using NotoriousTest.Core.Registry; - -using System.Globalization; -using System.Text.Json; - -namespace NotoriousTest.SqlLiteRegistry -{ - internal class InfrastructureRegistryEntryEntity - { - public string InfrastructureId { get; set; } - public string InfrastructureType { get; set; } - public string EnvironmentId { get; set; } - public int ProcessID { get; set; } - public string? Metadata { get; set; } - public string? MetadataType { get; set; } - public string? LastResetDate { get; set; } - public string CreationDate { get; set; } - public string UpdateDate { get; set; } - - internal static InfrastructureRegistryEntryEntity? FromDomain(InfrastuctureRegistryEntry entry) - { - return new InfrastructureRegistryEntryEntity() - { - InfrastructureId = entry.InfrastructureId.ToString(), - InfrastructureType = entry.InfrastructureType.AssemblyQualifiedName, - EnvironmentId = entry.EnvironmentId.ToString(), - ProcessID = entry.ProcessID, - Metadata = JsonSerializer.Serialize(entry.Metadata), - MetadataType = entry.Metadata?.GetType().AssemblyQualifiedName, - LastResetDate = entry.LastResetDate?.ToString("O") - }; - } - - internal InfrastuctureRegistryEntry ToDomain() - { - Type? metadataType = null; - - if (MetadataType != null) - { - metadataType = Type.GetType(MetadataType); - } - - Type? infraType = Type.GetType(InfrastructureType); - if (infraType == null) - { - throw new TypeLoadException($"Unable to load type {InfrastructureType}"); - } - - return new InfrastuctureRegistryEntry() - { - InfrastructureId = Guid.Parse(InfrastructureId), - InfrastructureType = Type.GetType(InfrastructureType), - EnvironmentId = Guid.Parse(EnvironmentId), - ProcessID = ProcessID, - Metadata = !(Metadata is null) && !(metadataType is null) - ? JsonSerializer.Deserialize(Metadata, metadataType) - : null, - LastResetDate = LastResetDate != null ? DateTime.Parse(LastResetDate, null, DateTimeStyles.RoundtripKind) : null, - CreationDate = DateTime.Parse(CreationDate, null, DateTimeStyles.RoundtripKind), - UpdateDate = DateTime.Parse(UpdateDate, null, DateTimeStyles.RoundtripKind), - }; - } - } -} diff --git a/NotoriousTest.SqlLiteRegistry/NotoriousTest.SqlLiteRegistry.csproj b/NotoriousTest.SqlLiteRegistry/NotoriousTest.SqlLiteRegistry.csproj deleted file mode 100644 index ced8265..0000000 --- a/NotoriousTest.SqlLiteRegistry/NotoriousTest.SqlLiteRegistry.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - net8.0 - false - - - - - - - - - - - - - - - - - diff --git a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProvider.cs b/NotoriousTest.SqlLiteRegistry/SqliteRegistryProvider.cs deleted file mode 100644 index 76ce971..0000000 --- a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProvider.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Dapper; - -using Microsoft.Data.Sqlite; - -using NotoriousTest.Core; -using NotoriousTest.Core.Registry; -namespace NotoriousTest.SqlLiteRegistry -{ - internal partial class SqliteRegistryProvider : IRegistry - { - private readonly SqliteRegistryProviderConfiguration _configuration; - public event Action OnInfrastructureReset; - public event Action OnInfrastructureDestroyed; - public event Action OnInfrastructureCreated; - - public SqliteRegistryProvider(SqliteRegistryProviderConfiguration configuration) - { - _configuration = configuration; - } - - private SqliteConnectionStringBuilder ConnectionString => new(_configuration.ConnectionString); - - private SqliteConnection Connection - { - get - { - var connection = new SqliteConnection(ConnectionString.ToString()); - - if (connection.State != System.Data.ConnectionState.Open) - { - connection.Open(); - } - - connection.Execute("PRAGMA SYNCHRONOUS=NORMAL;PRAGMA JOURNAL_MODE=WAL;"); - return connection; - } - } - - - public async Task Ensure() - { - string directory = Path.GetDirectoryName(ConnectionString.DataSource); - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } - - await using SqliteConnection connection = Connection; - - await connection.ExecuteAsync(ENSURE_REGISTRY); - } - - public async Task Register(InfrastuctureRegistryEntry entry) - { - await using SqliteConnection connection = Connection; - InfrastructureRegistryEntryEntity entity = await connection.QuerySingleAsync(REGISTER_INFRASTRUCTURE, InfrastructureRegistryEntryEntity.FromDomain(entry)); - - return entity.ToDomain(); - } - - public async Task Remove(Guid id) - { - await using SqliteConnection connection = Connection; - - int deletedRows = await connection.ExecuteAsync(REMOVE_INFRASTRUCTURE, new { InfrastructureId = id.ToString() }); - return deletedRows > 0; - } - - public async Task NotifyReset(Guid id) - { - await using SqliteConnection connection = Connection; - - await connection.ExecuteAsync(UPDATE_INFRASTRUCTURE_RESET_DATE, new { InfrastructureId = id.ToString() }); - } - - public async Task> GetByProcessId(int processId) - { - await using SqliteConnection connection = Connection; - - IEnumerable entries = await connection.QueryAsync(GET_BY_PROCESS_ID, new { ProcessID = processId }); - return entries.Select(entry => entry.ToDomain()); - } - - public async Task> GetByEnvironmentId(EnvironmentId environmentId) - { - await using SqliteConnection connection = Connection; - - IEnumerable entries = await connection.QueryAsync(GET_BY_ENVIRONMENT_ID, new { EnvironmentId = environmentId.Value.ToString() }); - return entries.Select(entry => entry.ToDomain()); - } - - public async Task Watch(EnvironmentId environmentId, CancellationToken ct) - { - var snapshot = (await GetByEnvironmentId(environmentId)).ToDictionary(x => x.InfrastructureId); - - do - { - IEnumerable infras = await GetByEnvironmentId(environmentId); - - foreach (InfrastuctureRegistryEntry infra in infras) - { - if (!snapshot.TryGetValue(infra.InfrastructureId, out var previous)) - { - OnInfrastructureCreated?.Invoke(infra); - } - - if (previous is not null && infra.LastResetDate != previous.LastResetDate) - { - OnInfrastructureReset?.Invoke(infra); - } - } - - foreach (var (key, infrastructure) in snapshot) - { - if (!infras.Any(i => i.InfrastructureId == infrastructure.InfrastructureId)) - { - OnInfrastructureDestroyed?.Invoke(infrastructure); - } - } - - snapshot = infras.ToDictionary(x => x.InfrastructureId); - - try - { - await Task.Delay(10, ct); - } - catch (OperationCanceledException) { } - } while (!ct.IsCancellationRequested); - } - } -} diff --git a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs b/NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs deleted file mode 100644 index 98c0c55..0000000 --- a/NotoriousTest.SqlLiteRegistry/SqliteRegistryProviderConfiguration.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace NotoriousTest.SqlLiteRegistry -{ - public class SqliteRegistryProviderConfiguration - { - public string ConnectionString { get; set; } - } -} diff --git a/NotoriousTest.TestSettings/NotoriousTest.TestSettings.csproj b/NotoriousTest.TestSettings/NotoriousTest.TestSettings.csproj deleted file mode 100644 index 6b02e26..0000000 --- a/NotoriousTest.TestSettings/NotoriousTest.TestSettings.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net8.0 - false - - - - - - - - - - diff --git a/NotoriousTest.TestSettings/TestSettingsProvider.cs b/NotoriousTest.TestSettings/TestSettingsProvider.cs deleted file mode 100644 index 1fa81d1..0000000 --- a/NotoriousTest.TestSettings/TestSettingsProvider.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.Extensions.Configuration; - -namespace NotoriousTest.Core.Settings -{ - public class TestSettingsProvider : ITestSettingsProvider - { - private IConfiguration _cache; - public IConfiguration Find() - { - if (_cache == null) - { - string? settingsPath = FindFile("testsettings.json"); - - - var configurationBuilder = new ConfigurationBuilder(); - if (settingsPath != null) - { - configurationBuilder.AddJsonFile(settingsPath); - - } - - _cache = configurationBuilder.Build(); - - } - - return _cache; - } - - public T? Get(string key) where T : new() - { - IConfigurationSection section = Find().GetSection(key); - if (!section.Exists()) - return default; - - var config = new T(); - section.Bind(config); - - return config; - } - private static string? FindFile(string fileName) - { - return Directory - .EnumerateFiles(AppContext.BaseDirectory, fileName, SearchOption.AllDirectories) - .FirstOrDefault(); - } - } -} diff --git a/NotoriousTest.UnitTests/NotoriousTest.UnitTests.csproj b/NotoriousTest.UnitTests/NotoriousTest.UnitTests.csproj index fc50592..2644a4a 100644 --- a/NotoriousTest.UnitTests/NotoriousTest.UnitTests.csproj +++ b/NotoriousTest.UnitTests/NotoriousTest.UnitTests.csproj @@ -24,8 +24,8 @@ - - + + diff --git a/NotoriousTest.UnitTests/Registry/InfrastructureRegistryEntryEntityTests.cs b/NotoriousTest.UnitTests/Registry/InfrastructureRegistryEntryEntityTests.cs index 69e110f..f02230c 100644 --- a/NotoriousTest.UnitTests/Registry/InfrastructureRegistryEntryEntityTests.cs +++ b/NotoriousTest.UnitTests/Registry/InfrastructureRegistryEntryEntityTests.cs @@ -1,7 +1,7 @@ using AwesomeAssertions; using NotoriousTest.Core.Registry; -using NotoriousTest.SqlLiteRegistry; +using NotoriousTest.Internal.SqlLiteRegistry; using System.Text.Json; diff --git a/NotoriousTest.Watchdog/DoggyDogWatchDog.cs b/NotoriousTest.Watchdog/DoggyDogWatchDog.cs deleted file mode 100644 index 147055b..0000000 --- a/NotoriousTest.Watchdog/DoggyDogWatchDog.cs +++ /dev/null @@ -1,89 +0,0 @@ -using NotoriousTest.Core; -using NotoriousTest.Core.Logger; -using NotoriousTest.Core.Watchdog; -using NotoriousTest.SqlLiteRegistry; - -using System.Diagnostics; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace NotoriousTest.Watchdog -{ - public class DoggyDogWatchDog : IWatchDog - { - private readonly SqliteRegistryProviderConfiguration _registyConfiguration; - private readonly ITestLogger _logger; - private readonly DoggyDogWatchdogConfiguration _config; - - public DoggyDogWatchDog(SqliteRegistryProviderConfiguration registryConfiguration, ITestLogger logger, DoggyDogWatchdogConfiguration config) - { - _registyConfiguration = registryConfiguration; - _logger = logger; - _config = config; - } - - public Process Start(Assembly currentAssembly, int currentPid, EnvironmentId environmentId, IEnumerable? runtimePaths) - { - string assemblyPath = currentAssembly.Location; - string? runtimesParams = runtimePaths == null ? null : string.Join("|", runtimePaths); - - if (_config.ManualLaunch) - return WaitForDoggyDogToLaunch(currentPid, environmentId, assemblyPath, runtimesParams); - return LaunchDoggyDog(currentPid, environmentId, assemblyPath, runtimesParams) ?? throw new Exception("Could not launch DoggyDog"); - } - - private Process? LaunchDoggyDog(int currentPid, EnvironmentId contextId, string assemblyPath, string? runtimesParams) - { - string watchdogPath = Path.Combine(AppContext.BaseDirectory, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "DoggyDog.exe" : "DoggyDog"); - - string runtimeParameter = runtimesParams == null ? "" : $"--runtimes \"{runtimesParams}\" "; - var process = Process.Start(new ProcessStartInfo - { - FileName = watchdogPath, - Arguments = $"--pid {currentPid} " + - $"--assembly \"{assemblyPath}\" " + - $"--connectionString \"{_registyConfiguration.ConnectionString}\" " + - $"--environment {contextId.Value} " + - runtimeParameter + - "--loglevel Info", - UseShellExecute = true, - CreateNoWindow = false, - }); - - - return process; - } - - private Process WaitForDoggyDogToLaunch(int currentPid, EnvironmentId environmentId, string assemblyPath, string? runtimesParams) - { - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_PID", currentPid.ToString(), EnvironmentVariableTarget.User); - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_ASSEMBLY", assemblyPath, EnvironmentVariableTarget.User); - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_CONNECTIONSTRING", _registyConfiguration.ConnectionString, EnvironmentVariableTarget.User); - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_ENVIRONMENT", environmentId.Value.ToString(), EnvironmentVariableTarget.User); - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_RUNTIMES", runtimesParams, EnvironmentVariableTarget.User); - Environment.SetEnvironmentVariable("DOGGYDOG_DEBUG_LOGLEVEL", "Debug", EnvironmentVariableTarget.User); - - Process? doggyDogProcess; - do - { - _logger.Log("Waiting for DoggyDog to launch...", environmentId); - doggyDogProcess = Process.GetProcessesByName("DoggyDog")?.FirstOrDefault(); - Thread.Sleep(5000); - - } while (doggyDogProcess == null); - - return doggyDogProcess; - } - - public void SendSuccessSignal(EnvironmentId contextId) => File.WriteAllText(Path.Combine(Path.GetTempPath(), $"nt-{contextId.Value}.signal"), "OK"); - - public static bool ReadSuccessSignal(EnvironmentId contextId) - { - string path = Path.Combine(Path.GetTempPath(), $"nt-{contextId.Value}.signal"); - bool isSuccess = File.Exists(path); - if (isSuccess) File.Delete(path); - - return isSuccess; - } - } -} diff --git a/NotoriousTest.Watchdog/NotoriousTest.Watchdog.csproj b/NotoriousTest.Watchdog/NotoriousTest.Watchdog.csproj deleted file mode 100644 index e695800..0000000 --- a/NotoriousTest.Watchdog/NotoriousTest.Watchdog.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - net8.0 - false - - - - - - - - diff --git a/NotoriousTest.slnx b/NotoriousTest.slnx index ab48a9d..3d21f2e 100644 --- a/NotoriousTest.slnx +++ b/NotoriousTest.slnx @@ -23,10 +23,10 @@ - - - - + + + + diff --git a/NotoriousTest/DI/DependencyInjectionConfigurator.cs b/NotoriousTest/DI/DependencyInjectionConfigurator.cs index cc6ad08..bea5bc6 100644 --- a/NotoriousTest/DI/DependencyInjectionConfigurator.cs +++ b/NotoriousTest/DI/DependencyInjectionConfigurator.cs @@ -9,9 +9,10 @@ using NotoriousTest.Core.Settings; using NotoriousTest.Core.Watchdog; using NotoriousTest.Environments; -using NotoriousTest.Runtime; -using NotoriousTest.SqlLiteRegistry; -using NotoriousTest.Watchdog; +using NotoriousTest.Internal.Runtime; +using NotoriousTest.Internal.SqlLiteRegistry; +using NotoriousTest.Internal.TestSettings; +using NotoriousTest.Internal.Watchdog; namespace NotoriousTest.DI; diff --git a/NotoriousTest/NotoriousTest.csproj b/NotoriousTest/NotoriousTest.csproj index 5b9c055..d7993f8 100644 --- a/NotoriousTest/NotoriousTest.csproj +++ b/NotoriousTest/NotoriousTest.csproj @@ -18,10 +18,10 @@ - - - - + + + +