diff --git a/Source/VisualStudioTools/Private/VSServerCommandlet.cpp b/Source/VisualStudioTools/Private/VSServerCommandlet.cpp new file mode 100644 index 0000000..b48702e --- /dev/null +++ b/Source/VisualStudioTools/Private/VSServerCommandlet.cpp @@ -0,0 +1,114 @@ +// Copyright 2022 (c) Microsoft. All rights reserved. + +#include "VSServerCommandlet.h" +#include "VSTestAdapterCommandlet.h" + +#include "HAL/PlatformNamedPipe.h" +#include "Runtime/Core/Public/Async/TaskGraphInterfaces.h" +#include "Runtime/Core/Public/Containers/Ticker.h" +#include "Runtime/Engine/Classes/Engine/World.h" +#include "Runtime/Engine/Public/TimerManager.h" +#include "Runtime/Launch/Resources/Version.h" +#include "Runtime\CoreUObject\Public\UObject\UObjectGlobals.h" +#include +#include +#include +#include +#include +#include + +#include "VisualStudioTools.h" + +static constexpr auto NamedPipeParam = TEXT("NamedPipe"); +static constexpr auto KillServerParam = TEXT("KillVSServer"); + +UVSServerCommandlet::UVSServerCommandlet() +{ + HelpDescription = TEXT("Commandlet for Unreal Engine server mode."); + HelpUsage = TEXT(" -run=VSServer [-stdout -multiprocess -silent -unattended -AllowStdOutLogVerbosity -NoShaderCompile]"); + + HelpParamNames.Add(NamedPipeParam); + HelpParamDescriptions.Add(TEXT("[Required] The name of the named pipe used to communicate with Visual Studio.")); + + HelpParamNames.Add(KillServerParam); + HelpParamDescriptions.Add(TEXT("[Optional] Quit the server mode commandlet immediately.")); +} + +void UVSServerCommandlet::ExecuteSubCommandlet(FString ueServerNamedPipe) +{ + char buffer[1024]; + DWORD dwRead; + std::string result = "0"; + + // Open the named pipe. + std::wstring pipeName = L"\\\\.\\pipe\\"; + pipeName.append(ueServerNamedPipe.GetCharArray().GetData()); + HANDLE HPipe = CreateFile(pipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (HPipe != INVALID_HANDLE_VALUE) + { + ConnectNamedPipe(HPipe, NULL); + DWORD dwState; + BOOL bSuccess = GetNamedPipeHandleState(HPipe, &dwState, NULL, NULL, NULL, NULL, 0); + if (bSuccess) + { + // Read data from the named pipe. + ReadFile(HPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL); + buffer[dwRead] = '\0'; + std::string strSubCommandletParams(buffer, dwRead); + FString SubCommandletParams = FString(strSubCommandletParams.c_str()); + + // Determine which sub-commandlet to invoke, and write back result response. + if (SubCommandletParams.Contains("VSTestAdapter")) + { + UVSTestAdapterCommandlet *Commandlet = NewObject(); + try + { + int32 subCommandletResult = Commandlet->Main(SubCommandletParams); + } + catch (const std::exception &ex) + { + UE_LOG(LogVisualStudioTools, Display, TEXT("Exception invoking VSTestAdapter commandlet: %s"), UTF8_TO_TCHAR(ex.what())); + result = "0"; + } + } + else if (SubCommandletParams.Contains("KillVSServer")) + { + // When KillVSServer is passed in, then kill the Unreal Editor process to end server mode. + exit(0); + } + else + { + // If cannot find which sub-commandlet to run, then return error. + result = "1"; + } + + WriteFile(HPipe, result.c_str(), result.size(), &dwRead, NULL); + } + } +} + +int32 UVSServerCommandlet::Main(const FString &ServerParams) +{ + TArray Tokens; + TArray Switches; + TMap ParamVals; + + ParseCommandLine(*ServerParams, Tokens, Switches, ParamVals); + if (ParamVals.Contains(NamedPipeParam)) + { + FString ueServerNamedPipe = ParamVals[NamedPipeParam]; + + // Infinite loop that listens to requests every second. + while (true) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + ExecuteSubCommandlet(ueServerNamedPipe); + } + } + else + { + UE_LOG(LogVisualStudioTools, Display, TEXT("Missing named pipe parameter.")); + } + + return 1; +} \ No newline at end of file diff --git a/Source/VisualStudioTools/Private/VSServerCommandlet.h b/Source/VisualStudioTools/Private/VSServerCommandlet.h new file mode 100644 index 0000000..a8c7ef5 --- /dev/null +++ b/Source/VisualStudioTools/Private/VSServerCommandlet.h @@ -0,0 +1,29 @@ +// Copyright 2022 (c) Microsoft. All rights reserved. +// Licensed under the MIT License. + +#pragma once + +#include "Commandlets/Commandlet.h" +#include + +#include +#include +#include + +#include "VSServerCommandlet.generated.h" + +UCLASS() +class UVSServerCommandlet + : public UCommandlet +{ + GENERATED_BODY() + +public: + UVSServerCommandlet(); + +public: + virtual int32 Main(const FString& Params) override; + +private: + void ExecuteSubCommandlet(FString ueServerNamedPipe); +}; \ No newline at end of file diff --git a/Source/VisualStudioTools/Private/VSTestAdapterCommandlet.cpp b/Source/VisualStudioTools/Private/VSTestAdapterCommandlet.cpp index 1b1dfd2..7e6568e 100644 --- a/Source/VisualStudioTools/Private/VSTestAdapterCommandlet.cpp +++ b/Source/VisualStudioTools/Private/VSTestAdapterCommandlet.cpp @@ -80,8 +80,8 @@ static int32 ListTests(const FString& TargetFile) } UE_LOG(LogVisualStudioTools, Display, TEXT("Found %d tests"), TestInfos.Num()); - OutFile.close(); + return 0; } @@ -182,7 +182,7 @@ UVSTestAdapterCommandlet::UVSTestAdapterCommandlet() HelpParamDescriptions.Add(TEXT("[Required] The output file from running test cases that we parse to retrieve test case results.")); HelpParamNames.Add(FiltersParam); - HelpParamDescriptions.Add(TEXT("[Optional] List of test filters to enable separated by '+'. Default is 'smoke+product+perf+stress+negative'")); + HelpParamDescriptions.Add(TEXT("[Optional] List of test filters to enable separated by '+'. Default is 'application+smoke+product+perf+stress+negative'")); HelpParamNames.Add(HelpParam); HelpParamDescriptions.Add(TEXT("[Optional] Print this help message and quit the commandlet immediately.")); @@ -214,11 +214,19 @@ int32 UVSTestAdapterCommandlet::Main(const FString& Params) } // Default to all the test filters on except for engine tests. - uint32 filter = EAutomationTestFlags::ProductFilter | EAutomationTestFlags::SmokeFilter | + uint32 filter = EAutomationTestFlags::ProductFilter | EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::SmokeFilter | EAutomationTestFlags::PerfFilter | EAutomationTestFlags::StressFilter | EAutomationTestFlags::NegativeFilter; if (ParamVals.Contains(FiltersParam)) { FString filters = ParamVals[FiltersParam]; + if (filters.Contains("application")) + { + filter |= EAutomationTestFlags::ApplicationContextMask; + } + else + { + filter &= ~EAutomationTestFlags::ApplicationContextMask; + } if (filters.Contains("smoke")) { filter |= EAutomationTestFlags::SmokeFilter; diff --git a/VisualStudioTools.uplugin b/VisualStudioTools.uplugin index b6d47ed..6ab922e 100644 --- a/VisualStudioTools.uplugin +++ b/VisualStudioTools.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "2.3", + "VersionName": "2.4", "FriendlyName": "Visual Studio Integration Tools", "Description": "Enables integration with Visual Studio IDE.", "Category": "Programming",