mirror of
https://github.com/roflmuffin/CounterStrikeSharp.git
synced 2025-12-05 23:58:24 -08:00
test: add integration tests xunit plugin/runner (#928)
This commit is contained in:
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@@ -6,10 +6,11 @@
|
|||||||
{
|
{
|
||||||
"label": "sync-linux",
|
"label": "sync-linux",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "lftp -c \"open -u $LINUX_SERVER_SFTP_USERNAME,$LINUX_SERVER_SFTP_PASSWORD $LINUX_SERVER_SFTP_HOST; mirror -R ${workspaceFolder}/build/addons /game/csgo/addons; mirror -R ${workspaceFolder}/managed/CounterStrikeSharp.API/bin/Release/net8.0/ /game/csgo/addons/counterstrikesharp/api\"",
|
"command": "lftp -c \"open -u $LINUX_SERVER_SFTP_USERNAME,$LINUX_SERVER_SFTP_PASSWORD $LINUX_SERVER_SFTP_HOST; mirror -R ${workspaceFolder}/build/addons /game/csgo/addons; mirror -R ${workspaceFolder}/managed/CounterStrikeSharp.API/bin/Release/net8.0/ /game/csgo/addons/counterstrikesharp/api; mirror -R ${workspaceFolder}/managed/CounterStrikeSharp.Tests.Native/bin/Debug/net8.0/ /game/csgo/addons/counterstrikesharp/plugins/NativeTestsPlugin\"",
|
||||||
"dependsOn": [
|
"dependsOn": [
|
||||||
"build",
|
"build",
|
||||||
"build-api"
|
"build-api",
|
||||||
|
"build-test-plugin"
|
||||||
],
|
],
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
},
|
},
|
||||||
@@ -28,6 +29,15 @@
|
|||||||
"cwd": "${workspaceFolder}/managed/CounterStrikeSharp.API"
|
"cwd": "${workspaceFolder}/managed/CounterStrikeSharp.API"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "build-test-plugin",
|
||||||
|
"type": "shell",
|
||||||
|
"group": "build",
|
||||||
|
"command": "dotnet build -c Debug",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/managed/CounterStrikeSharp.Tests.Native"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "generate-schema",
|
"label": "generate-schema",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
|
|||||||
46
managed/CounterStrikeSharp.Tests.Native/CommandTests.cs
Normal file
46
managed/CounterStrikeSharp.Tests.Native/CommandTests.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NativeTestsPlugin;
|
||||||
|
|
||||||
|
public class CommandTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task AddCommandHandler()
|
||||||
|
{
|
||||||
|
var mock = new Mock<Action>();
|
||||||
|
var methodCallback = FunctionReference.Create(() =>
|
||||||
|
{
|
||||||
|
mock.Object.Invoke();
|
||||||
|
});
|
||||||
|
|
||||||
|
NativeAPI.AddCommand("test_native", "description", true, (int)ConCommandFlags.FCVAR_LINKED_CONCOMMAND, methodCallback);
|
||||||
|
NativeAPI.IssueServerCommand("test_native");
|
||||||
|
await WaitOneFrame();
|
||||||
|
mock.Verify(s => s(), Times.Once);
|
||||||
|
|
||||||
|
NativeAPI.RemoveCommand("test_native", methodCallback);
|
||||||
|
NativeAPI.IssueServerCommand("test_native");
|
||||||
|
await WaitOneFrame();
|
||||||
|
mock.Verify(s => s(), Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IssueServerCommand()
|
||||||
|
{
|
||||||
|
bool called = false;
|
||||||
|
NativeAPI.AddCommandListener("say", FunctionReference.Create(() =>
|
||||||
|
{
|
||||||
|
called = true;
|
||||||
|
}), true);
|
||||||
|
|
||||||
|
NativeAPI.IssueServerCommand("say Hello, world!");
|
||||||
|
await WaitOneFrame();
|
||||||
|
|
||||||
|
Assert.True(called, "The 'say' command handler was not called.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Spectre.Console;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace NativeTestsPlugin;
|
||||||
|
|
||||||
|
public class ConsoleTestReporterSink : LongLivedMarshalByRefObject, IMessageSink, IDisposable
|
||||||
|
{
|
||||||
|
public TaskCompletionSource<bool> Finished { get; } = new();
|
||||||
|
|
||||||
|
private int _passed = 0;
|
||||||
|
private int _failed = 0;
|
||||||
|
private int _skipped = 0;
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
|
public bool OnMessage(IMessageSinkMessage message)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
// A test has passed
|
||||||
|
case ITestPassed passed:
|
||||||
|
Interlocked.Increment(ref _passed);
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[underline green][[PASS]][/] [green]{passed.Test.DisplayName}[/]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// A test has failed
|
||||||
|
case ITestFailed failed:
|
||||||
|
Interlocked.Increment(ref _failed);
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[underline red][[FAIL]][/] [red]{failed.Test.DisplayName}[/]");
|
||||||
|
AnsiConsole.WriteLine($"\tReason: {failed.ExceptionTypes[0]} - {failed.Messages[0]}");
|
||||||
|
AnsiConsole.WriteLine(IndentStackTrace(failed.StackTraces[0] ?? "No stack trace available."));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// A test was skipped (e.g., using [Fact(Skip = "...")])
|
||||||
|
case ITestSkipped skipped:
|
||||||
|
Interlocked.Increment(ref _skipped);
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[underline yellow][[SKIP]][/] [yellow]{skipped.Test.DisplayName}[/]");
|
||||||
|
AnsiConsole.MarkupLineInterpolated($"[yellow]\tReason: {skipped.Reason}[/]");
|
||||||
|
break;
|
||||||
|
|
||||||
|
// This message indicates the entire test run for the assembly is complete.
|
||||||
|
case ITestAssemblyFinished:
|
||||||
|
// We signal the main thread that it can stop waiting now.
|
||||||
|
Finished.SetResult(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSummary()
|
||||||
|
{
|
||||||
|
return $"Summary: {_passed} Passed, {_failed} Failed, {_skipped} Skipped.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string IndentStackTrace(string stackTrace)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
var lines = stackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
builder.AppendLine($" {line}");
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Finished.TrySetResult(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
managed/CounterStrikeSharp.Tests.Native/EntityTests.cs
Normal file
28
managed/CounterStrikeSharp.Tests.Native/EntityTests.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NativeTestsPlugin;
|
||||||
|
|
||||||
|
public class EntityTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void FindEntityAndAccessSchemaMembers()
|
||||||
|
{
|
||||||
|
var world = Utilities.FindAllEntitiesByDesignerName<CWorld>("worldent").FirstOrDefault();
|
||||||
|
|
||||||
|
Assert.NotNull(world);
|
||||||
|
Assert.Equal("worldent", world.DesignerName);
|
||||||
|
Assert.Equal((uint)0, world.Index);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.Equal(0, world.AbsOrigin.X);
|
||||||
|
Assert.Equal(0, world.AbsOrigin.Y);
|
||||||
|
Assert.Equal(0, world.AbsOrigin.Z);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
39
managed/CounterStrikeSharp.Tests.Native/GameEventTests.cs
Normal file
39
managed/CounterStrikeSharp.Tests.Native/GameEventTests.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NativeTestsPlugin;
|
||||||
|
|
||||||
|
public class GameEventTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanRegisterAndDeregisterEventHandlers()
|
||||||
|
{
|
||||||
|
int callCount = 0;
|
||||||
|
var callback = FunctionReference.Create((EventPlayerConnect @event) =>
|
||||||
|
{
|
||||||
|
Assert.NotNull(@event);
|
||||||
|
Assert.NotEmpty(@event.Name);
|
||||||
|
Assert.True(@event.Bot);
|
||||||
|
callCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
NativeAPI.HookEvent("player_connect", callback, true);
|
||||||
|
|
||||||
|
// Test hooking
|
||||||
|
NativeAPI.IssueServerCommand("bot_kick");
|
||||||
|
NativeAPI.IssueServerCommand("bot_add");
|
||||||
|
await WaitOneFrame();
|
||||||
|
|
||||||
|
Assert.Equal(1, callCount);
|
||||||
|
NativeAPI.UnhookEvent("player_connect", callback, true);
|
||||||
|
|
||||||
|
// Test unhooking
|
||||||
|
NativeAPI.IssueServerCommand("bot_kick");
|
||||||
|
NativeAPI.IssueServerCommand("bot_add");
|
||||||
|
await WaitOneFrame();
|
||||||
|
Assert.Equal(1, callCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
2
managed/CounterStrikeSharp.Tests.Native/GlobalUsings.cs
Normal file
2
managed/CounterStrikeSharp.Tests.Native/GlobalUsings.cs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
global using static TestUtils;
|
||||||
|
global using System;
|
||||||
36
managed/CounterStrikeSharp.Tests.Native/ListenerTests.cs
Normal file
36
managed/CounterStrikeSharp.Tests.Native/ListenerTests.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class ListenerTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanRegisterAndDeregisterListeners()
|
||||||
|
{
|
||||||
|
int callCount = 0;
|
||||||
|
var callback = FunctionReference.Create((int playerSlot, string name, string ipAddress) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
Assert.NotNull(ipAddress);
|
||||||
|
Assert.NotEmpty(name);
|
||||||
|
Assert.Equal("127.0.0.1", ipAddress);
|
||||||
|
callCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
NativeAPI.AddListener("OnClientConnect", callback);
|
||||||
|
|
||||||
|
// Test hooking
|
||||||
|
NativeAPI.IssueServerCommand("bot_kick");
|
||||||
|
NativeAPI.IssueServerCommand("bot_add");
|
||||||
|
await WaitOneFrame();
|
||||||
|
|
||||||
|
Assert.Equal(1, callCount);
|
||||||
|
NativeAPI.RemoveListener("OnClientConnect", callback);
|
||||||
|
|
||||||
|
// Test unhooking
|
||||||
|
NativeAPI.IssueServerCommand("bot_kick");
|
||||||
|
NativeAPI.IssueServerCommand("bot_add");
|
||||||
|
await WaitOneFrame();
|
||||||
|
Assert.Equal(1, callCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
100
managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs
Normal file
100
managed/CounterStrikeSharp.Tests.Native/NativeTestsPlugin.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of CounterStrikeSharp.
|
||||||
|
* CounterStrikeSharp is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* CounterStrikeSharp is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NativeTestsPlugin
|
||||||
|
{
|
||||||
|
public class NativeTestsPlugin : BasePlugin
|
||||||
|
{
|
||||||
|
public override string ModuleName => "Native Tests";
|
||||||
|
public override string ModuleVersion => "v1.0.0";
|
||||||
|
|
||||||
|
public override string ModuleAuthor => "Roflmuffin";
|
||||||
|
|
||||||
|
public override string ModuleDescription => "A an automated test plugin.";
|
||||||
|
|
||||||
|
private int gameThreadId;
|
||||||
|
|
||||||
|
public override void Load(bool hotReload)
|
||||||
|
{
|
||||||
|
gameThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||||
|
// Loading blocks the game thread, so we use NextFrame to run our tests asynchronously.
|
||||||
|
Server.NextFrame(() => RunTests());
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task RunTests()
|
||||||
|
{
|
||||||
|
Console.WriteLine("*****************************************************************");
|
||||||
|
Console.WriteLine($"[{ModuleName}] Starting xUnit test run...");
|
||||||
|
Console.WriteLine("*****************************************************************");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var reporter = new ConsoleTestReporterSink();
|
||||||
|
|
||||||
|
var project = new XunitProject();
|
||||||
|
using var controller = new XunitFrontController(AppDomainSupport.IfAvailable, this.ModulePath);
|
||||||
|
|
||||||
|
var executionOptions = TestFrameworkOptions.ForExecution();
|
||||||
|
executionOptions.SetDisableParallelization(true);
|
||||||
|
executionOptions.SetMaxParallelThreads(1);
|
||||||
|
executionOptions.SetSynchronousMessageReporting(true);
|
||||||
|
SynchronizationContext.SetSynchronizationContext(new SourceSynchronizationContext(gameThreadId));
|
||||||
|
|
||||||
|
controller.RunAll(reporter, TestFrameworkOptions.ForDiscovery(), executionOptions);
|
||||||
|
|
||||||
|
await reporter.Finished.Task;
|
||||||
|
Console.WriteLine("*****************************************************************");
|
||||||
|
Console.WriteLine($"[{ModuleName}] Test run finished.");
|
||||||
|
Console.WriteLine(reporter.GetSummary());
|
||||||
|
Console.WriteLine("*****************************************************************");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.WriteLine($"[{ModuleName}] A critical error occurred during the test run setup: {ex.Message}");
|
||||||
|
Console.WriteLine(ex.StackTrace);
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SourceSynchronizationContext : SynchronizationContext
|
||||||
|
{
|
||||||
|
private readonly int _mainThreadId;
|
||||||
|
public SourceSynchronizationContext(int mainThreadId)
|
||||||
|
{
|
||||||
|
_mainThreadId = mainThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Post(SendOrPostCallback d, object state)
|
||||||
|
{
|
||||||
|
Server.NextWorldUpdate(() => d(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override SynchronizationContext CreateCopy()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Platforms>AnyCPU;x86</Platforms>
|
||||||
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\CounterStrikeSharp.API\CounterStrikeSharp.API.csproj">
|
||||||
|
<Private>false</Private>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="moq" Version="4.20.72" />
|
||||||
|
<PackageReference Include="Spectre.Console" Version="0.50.0" />
|
||||||
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.runner.utility" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
|
||||||
|
<PackageReference Include="xunit.assert" Version="2.9.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
2
managed/CounterStrikeSharp.Tests.Native/README.md
Normal file
2
managed/CounterStrikeSharp.Tests.Native/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Native Tests
|
||||||
|
This plugin is intended to be ran inside a running CS2 server running a version of CS# and runs tests against the exposed `NativeAPI` methods.
|
||||||
10
managed/CounterStrikeSharp.Tests.Native/TestUtils.cs
Normal file
10
managed/CounterStrikeSharp.Tests.Native/TestUtils.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CounterStrikeSharp.API;
|
||||||
|
|
||||||
|
public static class TestUtils
|
||||||
|
{
|
||||||
|
public static async Task WaitOneFrame()
|
||||||
|
{
|
||||||
|
await Server.RunOnTickAsync(Server.TickCount + 2, () => { }).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithUserMessages", "..\exam
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCheckTransmit", "..\examples\WithCheckTransmit\WithCheckTransmit.csproj", "{854B06B5-0E7B-438A-BA51-3F299557F884}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WithCheckTransmit", "..\examples\WithCheckTransmit\WithCheckTransmit.csproj", "{854B06B5-0E7B-438A-BA51-3F299557F884}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeTestsPlugin", "CounterStrikeSharp.Tests.Native\NativeTestsPlugin.csproj", "{317D3A98-D5C6-40BC-9234-CDAFC033ED0F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -137,6 +139,10 @@ Global
|
|||||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{854B06B5-0E7B-438A-BA51-3F299557F884}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.Build.0 = Release|Any CPU
|
{854B06B5-0E7B-438A-BA51-3F299557F884}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{317D3A98-D5C6-40BC-9234-CDAFC033ED0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{317D3A98-D5C6-40BC-9234-CDAFC033ED0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{317D3A98-D5C6-40BC-9234-CDAFC033ED0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{317D3A98-D5C6-40BC-9234-CDAFC033ED0F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
Reference in New Issue
Block a user