diff --git a/CodexPlugin/CodexAccess.cs b/CodexPlugin/CodexAccess.cs index 842beb7..40433cf 100644 --- a/CodexPlugin/CodexAccess.cs +++ b/CodexPlugin/CodexAccess.cs @@ -1,4 +1,4 @@ -using DistTestCore; +using Core; using KubernetesWorkflow; using Utils; diff --git a/CodexPlugin/CodexContainerRecipe.cs b/CodexPlugin/CodexContainerRecipe.cs index 1f7f689..f16a88b 100644 --- a/CodexPlugin/CodexContainerRecipe.cs +++ b/CodexPlugin/CodexContainerRecipe.cs @@ -1,5 +1,5 @@ //using DistTestCore.Marketplace; -using DistTestCore; +using Core; using KubernetesWorkflow; using Utils; diff --git a/CodexPlugin/CodexNodeFactory.cs b/CodexPlugin/CodexNodeFactory.cs index 90a5bfb..89b69eb 100644 --- a/CodexPlugin/CodexNodeFactory.cs +++ b/CodexPlugin/CodexNodeFactory.cs @@ -1,4 +1,4 @@ -using DistTestCore; +using Core; namespace CodexPlugin { diff --git a/CodexPlugin/CodexNodeGroup.cs b/CodexPlugin/CodexNodeGroup.cs index a4c868e..c2e619e 100644 --- a/CodexPlugin/CodexNodeGroup.cs +++ b/CodexPlugin/CodexNodeGroup.cs @@ -1,6 +1,5 @@ -using DistTestCore; +using Core; using KubernetesWorkflow; -using Logging; using System.Collections; namespace CodexPlugin diff --git a/CodexPlugin/CodexPlugin.cs b/CodexPlugin/CodexPlugin.cs index 2f9c531..dbc18b1 100644 --- a/CodexPlugin/CodexPlugin.cs +++ b/CodexPlugin/CodexPlugin.cs @@ -1,4 +1,4 @@ -using DistTestCore; +using Core; using KubernetesWorkflow; using Logging; @@ -40,7 +40,13 @@ namespace CodexPlugin public IOnlineCodexNode SetupCodexNode(Action setup) { - return null!; + return SetupCodexNodes(1, setup)[0]; + } + + public ICodexNodeGroup SetupCodexNodes(int number, Action setup) + { + var rc = StartCodexNodes(number, setup); + return WrapCodexContainers(rc); } public ICodexNodeGroup SetupCodexNodes(int number) @@ -48,6 +54,5 @@ namespace CodexPlugin var rc = StartCodexNodes(number, s => { }); return WrapCodexContainers(rc); } - } } diff --git a/CodexPlugin/CodexPlugin.csproj b/CodexPlugin/CodexPlugin.csproj index dc720d5..e17d31e 100644 --- a/CodexPlugin/CodexPlugin.csproj +++ b/CodexPlugin/CodexPlugin.csproj @@ -21,7 +21,7 @@ - + diff --git a/CodexPlugin/CodexStarter.cs b/CodexPlugin/CodexStarter.cs index 1507878..7480090 100644 --- a/CodexPlugin/CodexStarter.cs +++ b/CodexPlugin/CodexStarter.cs @@ -1,4 +1,4 @@ -using DistTestCore; +using Core; using KubernetesWorkflow; using Logging; diff --git a/CodexPlugin/OnlineCodexNode.cs b/CodexPlugin/OnlineCodexNode.cs index b3120f3..7895076 100644 --- a/CodexPlugin/OnlineCodexNode.cs +++ b/CodexPlugin/OnlineCodexNode.cs @@ -1,5 +1,4 @@ -using DistTestCore; -using DistTestCore.Logs; +using Core; using FileUtils; using Logging; using NUnit.Framework; @@ -12,8 +11,8 @@ namespace CodexPlugin string GetName(); CodexDebugResponse GetDebugInfo(); CodexDebugPeerResponse GetDebugPeer(string peerId); - ContentId UploadFile(TestFile file); - TestFile? DownloadContent(ContentId contentId, string fileLabel = ""); + ContentId UploadFile(TrackedFile file); + TrackedFile? DownloadContent(ContentId contentId, string fileLabel = ""); void ConnectToPeer(IOnlineCodexNode node); IDownloadedLog DownloadLog(int? tailLines = null); //IMetricsAccess Metrics { get; } @@ -62,7 +61,7 @@ namespace CodexPlugin return CodexAccess.GetDebugPeer(peerId); } - public ContentId UploadFile(TestFile file) + public ContentId UploadFile(TrackedFile file) { using var fileStream = File.OpenRead(file.Filename); @@ -80,7 +79,7 @@ namespace CodexPlugin return new ContentId(response); } - public TestFile? DownloadContent(ContentId contentId, string fileLabel = "") + public TrackedFile? DownloadContent(ContentId contentId, string fileLabel = "") { var logMessage = $"Downloading for contentId: '{contentId.Id}'..."; Log(logMessage); @@ -104,7 +103,11 @@ namespace CodexPlugin public IDownloadedLog DownloadLog(int? tailLines = null) { - return null!; // lifecycle.DownloadLog(CodexAccess.Container, tailLines); + var workflow = tools.CreateWorkflow(); + var file = tools.GetLog().CreateSubfile(); + var logHandler = new LogDownloadHandler(CodexAccess.GetName(), file); + workflow.DownloadContainerLog(CodexAccess.Container, logHandler); + return logHandler.DownloadLog(); } public void BringOffline() @@ -142,7 +145,7 @@ namespace CodexPlugin return multiAddress.Replace("0.0.0.0", peer.CodexAccess.Container.Pod.PodInfo.Ip); } - private void DownloadToFile(string contentId, TestFile file) + private void DownloadToFile(string contentId, TrackedFile file) { using var fileStream = File.OpenWrite(file.Filename); try diff --git a/CodexPlugin/PluginInterfaceExtensions.cs b/CodexPlugin/PluginInterfaceExtensions.cs index 8fa21f2..3300afe 100644 --- a/CodexPlugin/PluginInterfaceExtensions.cs +++ b/CodexPlugin/PluginInterfaceExtensions.cs @@ -1,4 +1,4 @@ -using DistTestCore; +using Core; using KubernetesWorkflow; namespace CodexPlugin @@ -20,6 +20,11 @@ namespace CodexPlugin return Plugin(pluginInterface).SetupCodexNode(setup); } + public static ICodexNodeGroup SetupCodexNodes(this PluginInterface pluginInterface, int number, Action setup) + { + return Plugin(pluginInterface).SetupCodexNodes(number, setup); + } + public static ICodexNodeGroup SetupCodexNodes(this PluginInterface pluginInterface, int number) { return Plugin(pluginInterface).SetupCodexNodes(number); diff --git a/Core/Core.csproj b/Core/Core.csproj new file mode 100644 index 0000000..58c7951 --- /dev/null +++ b/Core/Core.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/DistTestCore/DefaultContainerRecipe.cs b/Core/DefaultContainerRecipe.cs similarity index 98% rename from DistTestCore/DefaultContainerRecipe.cs rename to Core/DefaultContainerRecipe.cs index cfb8e57..1a34e71 100644 --- a/DistTestCore/DefaultContainerRecipe.cs +++ b/Core/DefaultContainerRecipe.cs @@ -1,7 +1,7 @@ using KubernetesWorkflow; using Logging; -namespace DistTestCore +namespace Core { public abstract class DefaultContainerRecipe : ContainerRecipeFactory { diff --git a/DistTestCore/Logs/DownloadedLog.cs b/Core/DownloadedLog.cs similarity index 73% rename from DistTestCore/Logs/DownloadedLog.cs rename to Core/DownloadedLog.cs index 606c411..ee31ed7 100644 --- a/DistTestCore/Logs/DownloadedLog.cs +++ b/Core/DownloadedLog.cs @@ -1,11 +1,10 @@ using Logging; -using NUnit.Framework; -namespace DistTestCore.Logs +namespace Core { public interface IDownloadedLog { - void AssertLogContains(string expectedString); + bool DoesLogContain(string expectedString); string[] FindLinesThatContain(params string[] tags); void DeleteFile(); } @@ -13,15 +12,13 @@ namespace DistTestCore.Logs public class DownloadedLog : IDownloadedLog { private readonly LogFile logFile; - private readonly string owner; - public DownloadedLog(LogFile logFile, string owner) + public DownloadedLog(LogFile logFile) { this.logFile = logFile; - this.owner = owner; } - public void AssertLogContains(string expectedString) + public bool DoesLogContain(string expectedString) { using var file = File.OpenRead(logFile.FullFilename); using var streamReader = new StreamReader(file); @@ -29,11 +26,12 @@ namespace DistTestCore.Logs var line = streamReader.ReadLine(); while (line != null) { - if (line.Contains(expectedString)) return; + if (line.Contains(expectedString)) return true; line = streamReader.ReadLine(); } - Assert.Fail($"{owner} Unable to find string '{expectedString}' in CodexNode log file {logFile.FullFilename}"); + //Assert.Fail($"{owner} Unable to find string '{expectedString}' in CodexNode log file {logFile.FullFilename}"); + return false; } public string[] FindLinesThatContain(params string[] tags) diff --git a/DistTestCore/Http.cs b/Core/Http.cs similarity index 99% rename from DistTestCore/Http.cs rename to Core/Http.cs index 5df53bb..dbb2f99 100644 --- a/DistTestCore/Http.cs +++ b/Core/Http.cs @@ -5,7 +5,7 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using Utils; -namespace DistTestCore +namespace Core { public class Http { diff --git a/DistTestCore/Logs/LogDownloadHandler.cs b/Core/LogDownloadHandler.cs similarity index 58% rename from DistTestCore/Logs/LogDownloadHandler.cs rename to Core/LogDownloadHandler.cs index 483e46b..ae6a1da 100644 --- a/DistTestCore/Logs/LogDownloadHandler.cs +++ b/Core/LogDownloadHandler.cs @@ -1,25 +1,23 @@ using KubernetesWorkflow; using Logging; -namespace DistTestCore.Logs +namespace Core { public class LogDownloadHandler : LogHandler, ILogHandler { - private readonly RunningContainer container; private readonly LogFile log; - public LogDownloadHandler(RunningContainer container, string description, LogFile log) + public LogDownloadHandler(string description, LogFile log) { - this.container = container; this.log = log; log.Write($"{description} -->> {log.FullFilename}"); log.WriteRaw(description); } - public DownloadedLog DownloadLog() + public IDownloadedLog DownloadLog() { - return new DownloadedLog(log, container.Name); + return new DownloadedLog(log); } protected override void ProcessLine(string line) diff --git a/DistTestCore/PluginInterface.cs b/Core/PluginInterface.cs similarity index 82% rename from DistTestCore/PluginInterface.cs rename to Core/PluginInterface.cs index 72b0da3..619d0b5 100644 --- a/DistTestCore/PluginInterface.cs +++ b/Core/PluginInterface.cs @@ -1,4 +1,4 @@ -namespace DistTestCore +namespace Core { public abstract class PluginInterface { diff --git a/DistTestCore/PluginManager.cs b/Core/PluginManager.cs similarity index 99% rename from DistTestCore/PluginManager.cs rename to Core/PluginManager.cs index b4bb804..27783de 100644 --- a/DistTestCore/PluginManager.cs +++ b/Core/PluginManager.cs @@ -4,7 +4,7 @@ using Logging; using System.Reflection; using Utils; -namespace DistTestCore +namespace Core { public class PluginManager { diff --git a/DistTestCore/Timing.cs b/Core/TimeSet.cs similarity index 52% rename from DistTestCore/Timing.cs rename to Core/TimeSet.cs index 38df6d8..db78fa5 100644 --- a/DistTestCore/Timing.cs +++ b/Core/TimeSet.cs @@ -1,12 +1,5 @@ -using NUnit.Framework; - -namespace DistTestCore +namespace Core { - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class UseLongTimeoutsAttribute : PropertyAttribute - { - } - public interface ITimeSet { TimeSpan HttpCallTimeout(); @@ -49,37 +42,4 @@ namespace DistTestCore return TimeSpan.FromSeconds(30); } } - - public class LongTimeSet : ITimeSet - { - public TimeSpan HttpCallTimeout() - { - return TimeSpan.FromHours(2); - } - - public TimeSpan HttpCallRetryTime() - { - return TimeSpan.FromHours(5); - } - - public TimeSpan HttpCallRetryDelay() - { - return TimeSpan.FromSeconds(2); - } - - public TimeSpan WaitForK8sServiceDelay() - { - return TimeSpan.FromSeconds(10); - } - - public TimeSpan K8sOperationTimeout() - { - return TimeSpan.FromMinutes(15); - } - - public TimeSpan WaitForMetricTimeout() - { - return TimeSpan.FromMinutes(5); - } - } } diff --git a/DistTestCore/Configuration.cs b/DistTestCore/Configuration.cs index 91d64d3..2eefcf0 100644 --- a/DistTestCore/Configuration.cs +++ b/DistTestCore/Configuration.cs @@ -1,6 +1,4 @@ -using KubernetesWorkflow; -using System.Net.NetworkInformation; -using Utils; +using Core; namespace DistTestCore { diff --git a/DistTestCore/DistTest.cs b/DistTestCore/DistTest.cs index fb56023..257332e 100644 --- a/DistTestCore/DistTest.cs +++ b/DistTestCore/DistTest.cs @@ -1,4 +1,4 @@ -using DistTestCore.Logs; +using Core; using FileUtils; using KubernetesWorkflow; using Logging; @@ -92,7 +92,7 @@ namespace DistTestCore } } - public TestFile GenerateTestFile(ByteSize size, string label = "") + public TrackedFile GenerateTestFile(ByteSize size, string label = "") { return Get().FileManager.GenerateTestFile(size, label); } diff --git a/DistTestCore/DistTestCore.csproj b/DistTestCore/DistTestCore.csproj index 6005043..69da090 100644 --- a/DistTestCore/DistTestCore.csproj +++ b/DistTestCore/DistTestCore.csproj @@ -19,6 +19,7 @@ + diff --git a/DistTestCore/Logs/DontDownloadLogsAndMetricsOnFailureAttribute.cs b/DistTestCore/DontDownloadLogsAndMetricsOnFailureAttribute.cs similarity index 93% rename from DistTestCore/Logs/DontDownloadLogsAndMetricsOnFailureAttribute.cs rename to DistTestCore/DontDownloadLogsAndMetricsOnFailureAttribute.cs index b95d875..335bbe3 100644 --- a/DistTestCore/Logs/DontDownloadLogsAndMetricsOnFailureAttribute.cs +++ b/DistTestCore/DontDownloadLogsAndMetricsOnFailureAttribute.cs @@ -1,6 +1,6 @@ using NUnit.Framework; -namespace DistTestCore.Logs +namespace DistTestCore { [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class DontDownloadLogsAndMetricsOnFailureAttribute : PropertyAttribute diff --git a/DistTestCore/LongTimeSet.cs b/DistTestCore/LongTimeSet.cs new file mode 100644 index 0000000..8d0d5e8 --- /dev/null +++ b/DistTestCore/LongTimeSet.cs @@ -0,0 +1,37 @@ +using Core; + +namespace DistTestCore +{ + public class LongTimeSet : ITimeSet + { + public TimeSpan HttpCallTimeout() + { + return TimeSpan.FromHours(2); + } + + public TimeSpan HttpCallRetryTime() + { + return TimeSpan.FromHours(5); + } + + public TimeSpan HttpCallRetryDelay() + { + return TimeSpan.FromSeconds(2); + } + + public TimeSpan WaitForK8sServiceDelay() + { + return TimeSpan.FromSeconds(10); + } + + public TimeSpan K8sOperationTimeout() + { + return TimeSpan.FromMinutes(15); + } + + public TimeSpan WaitForMetricTimeout() + { + return TimeSpan.FromMinutes(5); + } + } +} diff --git a/DistTestCore/LongTimeoutsTestAttribute.cs b/DistTestCore/LongTimeoutsTestAttribute.cs new file mode 100644 index 0000000..5bbc612 --- /dev/null +++ b/DistTestCore/LongTimeoutsTestAttribute.cs @@ -0,0 +1,9 @@ +using NUnit.Framework; + +namespace DistTestCore +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class UseLongTimeoutsAttribute : PropertyAttribute + { + } +} diff --git a/DistTestCore/TestLifecycle.cs b/DistTestCore/TestLifecycle.cs index a075fe5..864f6ca 100644 --- a/DistTestCore/TestLifecycle.cs +++ b/DistTestCore/TestLifecycle.cs @@ -1,4 +1,5 @@ -using FileUtils; +using Core; +using FileUtils; using KubernetesWorkflow; using Logging; using Utils; diff --git a/FileUtils/FileManager.cs b/FileUtils/FileManager.cs index 4fc7b52..6d389ef 100644 --- a/FileUtils/FileManager.cs +++ b/FileUtils/FileManager.cs @@ -6,8 +6,8 @@ namespace FileUtils { public interface IFileManager { - TestFile CreateEmptyTestFile(string label = ""); - TestFile GenerateTestFile(ByteSize size, string label = ""); + TrackedFile CreateEmptyTestFile(string label = ""); + TrackedFile GenerateTestFile(ByteSize size, string label = ""); void DeleteAllTestFiles(); void ScopedFiles(Action action); T ScopedFiles(Func action); @@ -20,7 +20,7 @@ namespace FileUtils private readonly Random random = new Random(); private readonly ILog log; private readonly string folder; - private readonly List> fileSetStack = new List>(); + private readonly List> fileSetStack = new List>(); public FileManager(ILog log, string rootFolder) { @@ -30,16 +30,16 @@ namespace FileUtils this.log = log; } - public TestFile CreateEmptyTestFile(string label = "") + public TrackedFile CreateEmptyTestFile(string label = "") { var path = Path.Combine(folder, Guid.NewGuid().ToString() + "_test.bin"); - var result = new TestFile(log, path, label); + var result = new TrackedFile(log, path, label); File.Create(result.Filename).Close(); if (fileSetStack.Any()) fileSetStack.Last().Add(result); return result; } - public TestFile GenerateTestFile(ByteSize size, string label) + public TrackedFile GenerateTestFile(ByteSize size, string label) { var sw = Stopwatch.Begin(log); var result = GenerateFile(size, label); @@ -69,7 +69,7 @@ namespace FileUtils private void PushFileSet() { - fileSetStack.Add(new List()); + fileSetStack.Add(new List()); } private void PopFileSet() @@ -88,7 +88,7 @@ namespace FileUtils } } - private TestFile GenerateFile(ByteSize size, string label) + private TrackedFile GenerateFile(ByteSize size, string label) { var result = CreateEmptyTestFile(label); CheckSpaceAvailable(result, size); @@ -97,7 +97,7 @@ namespace FileUtils return result; } - private void CheckSpaceAvailable(TestFile testFile, ByteSize size) + private void CheckSpaceAvailable(TrackedFile testFile, ByteSize size) { var file = new FileInfo(testFile.Filename); var drive = new DriveInfo(file.Directory!.Root.FullName); @@ -115,7 +115,7 @@ namespace FileUtils } } - private void GenerateFileBytes(TestFile result, ByteSize size) + private void GenerateFileBytes(TrackedFile result, ByteSize size) { long bytesLeft = size.SizeInBytes; int chunkSize = ChunkSize; @@ -135,7 +135,7 @@ namespace FileUtils } } - private void AppendRandomBytesToFile(TestFile result, long length) + private void AppendRandomBytesToFile(TrackedFile result, long length) { var bytes = new byte[length]; random.NextBytes(bytes); diff --git a/FileUtils/TestFile.cs b/FileUtils/TrackedFile.cs similarity index 89% rename from FileUtils/TestFile.cs rename to FileUtils/TrackedFile.cs index cb51544..3b6ccfb 100644 --- a/FileUtils/TestFile.cs +++ b/FileUtils/TrackedFile.cs @@ -4,11 +4,11 @@ using Utils; namespace FileUtils { - public class TestFile + public class TrackedFile { private readonly ILog log; - public TestFile(ILog log, string filename, string label) + public TrackedFile(ILog log, string filename, string label) { this.log = log; Filename = filename; @@ -18,7 +18,7 @@ namespace FileUtils public string Filename { get; } public string Label { get; } - public void AssertIsEqual(TestFile? actual) + public void AssertIsEqual(TrackedFile? actual) { var sw = Stopwatch.Begin(log); try @@ -27,7 +27,7 @@ namespace FileUtils } finally { - sw.End($"{nameof(TestFile)}.{nameof(AssertIsEqual)}"); + sw.End($"{nameof(TrackedFile)}.{nameof(AssertIsEqual)}"); } } @@ -38,7 +38,7 @@ namespace FileUtils return $"'{Filename}'{sizePostfix}"; } - private void AssertEqual(TestFile? actual) + private void AssertEqual(TrackedFile? actual) { if (actual == null) Assert.Fail("TestFile is null."); if (actual == this || actual!.Filename == Filename) Assert.Fail("TestFile is compared to itself."); diff --git a/KubernetesWorkflow/StartupWorkflow.cs b/KubernetesWorkflow/StartupWorkflow.cs index 97b66ed..5cc8f1e 100644 --- a/KubernetesWorkflow/StartupWorkflow.cs +++ b/KubernetesWorkflow/StartupWorkflow.cs @@ -7,7 +7,7 @@ namespace KubernetesWorkflow { RunningContainers Start(int numberOfContainers, Location location, ContainerRecipeFactory recipeFactory, StartupConfig startupConfig); void Stop(RunningContainers runningContainers); - void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines); + void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines = null); string ExecuteCommand(RunningContainer container, string command, params string[] args); void DeleteNamespace(); void DeleteNamespacesStartingWith(string namespacePrefix); @@ -53,7 +53,7 @@ namespace KubernetesWorkflow }); } - public void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines) + public void DownloadContainerLog(RunningContainer container, ILogHandler logHandler, int? tailLines = null) { K8s(controller => { diff --git a/cs-codex-dist-testing.sln b/cs-codex-dist-testing.sln index 59b290c..9cd1c7e 100644 --- a/cs-codex-dist-testing.sln +++ b/cs-codex-dist-testing.sln @@ -23,9 +23,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArgsUniform", "ArgsUniform\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexNetDownloader", "CodexNetDownloader\CodexNetDownloader.csproj", "{6CDF35D2-906A-4285-8E1F-4794588B948B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileUtils", "FileUtils\FileUtils.csproj", "{ECC954DA-8D4E-49EE-83AD-80085A43DEEB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FileUtils", "FileUtils\FileUtils.csproj", "{ECC954DA-8D4E-49EE-83AD-80085A43DEEB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodexPlugin", "CodexPlugin\CodexPlugin.csproj", "{DE4E802C-288C-45C4-84B6-8A5A6A96EF49}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodexPlugin", "CodexPlugin\CodexPlugin.csproj", "{DE4E802C-288C-45C4-84B6-8A5A6A96EF49}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{F2BF34B3-C660-43EF-BD42-BC5C60237FC4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -81,6 +83,10 @@ Global {DE4E802C-288C-45C4-84B6-8A5A6A96EF49}.Debug|Any CPU.Build.0 = Debug|Any CPU {DE4E802C-288C-45C4-84B6-8A5A6A96EF49}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE4E802C-288C-45C4-84B6-8A5A6A96EF49}.Release|Any CPU.Build.0 = Release|Any CPU + {F2BF34B3-C660-43EF-BD42-BC5C60237FC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2BF34B3-C660-43EF-BD42-BC5C60237FC4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2BF34B3-C660-43EF-BD42-BC5C60237FC4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2BF34B3-C660-43EF-BD42-BC5C60237FC4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE