Skip to content

Commit

Permalink
Add process utils tests. (#770)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim Przybylinski authored Jul 31, 2017
1 parent 5581455 commit f45cec8
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,18 @@
<ItemGroup>
<Compile Include="NullEmptyInvisibleConverterTests.cs" />
<Compile Include="PathUtilsTests.cs" />
<Compile Include="ProcessUtilsTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GoogleCloudExtension.Utils\GoogleCloudExtension.Utils.csproj">
<Project>{21501704-9d0c-442f-ad39-292b3da4bc57}</Project>
<Name>GoogleCloudExtension.Utils</Name>
</ProjectReference>
<ProjectReference Include="..\TestProjects\EchoApp\EchoApp.csproj">
<Project>{108901a6-861d-4021-a2c9-ce93554a26f5}</Project>
<Name>EchoApp</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;

namespace GoogleCloudExtension.Utils.UnitTests
{
[SuppressMessage("ReSharper", "UnassignedField.Global")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class JsonDataClass
{
public string Var;
}

[TestClass]
[DeploymentItem(EchoAppName)]
public class ProcessUtilsTests
{
private const string ProcessOutput = "ProcessOutput";
private const string StdOutArgs = "-out " + ProcessOutput;
private const string StdErrArgs = "-err " + ProcessOutput;
private const string ExpArgs = "-exp " + ProcessOutput;
private const string JsonArgs = "-out \"{\"" + nameof(JsonDataClass.Var) + "\":'" + ProcessOutput + "'}\"";
private const string EchoAppName = "EchoApp.exe";

[TestMethod]
[ExpectedException(typeof(Win32Exception))]
public async Task GetCommandOutputAsync_TargetInvalid()
{
await ProcessUtils.GetCommandOutputAsync("BadCommand.exe", StdOutArgs);
}

[TestMethod]
public async Task GetCommandOutputAsync_StandardOutput()
{
ProcessOutput output = await ProcessUtils.GetCommandOutputAsync(EchoAppName, StdOutArgs);

Assert.IsTrue(output.Succeeded);
Assert.AreEqual(ProcessOutput, output.StandardOutput);
Assert.AreEqual(string.Empty, output.StandardError);
}

[TestMethod]
public async Task GetCommandOutputAsync_StdErr()
{
ProcessOutput output = await ProcessUtils.GetCommandOutputAsync(EchoAppName, StdErrArgs);

Assert.IsTrue(output.Succeeded);
Assert.AreEqual(string.Empty, output.StandardOutput);
Assert.AreEqual(ProcessOutput, output.StandardError);
}

[TestMethod]
public async Task GetCommandOutputAsync_Exp()
{
ProcessOutput output = await ProcessUtils.GetCommandOutputAsync(EchoAppName, ExpArgs);

Assert.IsFalse(output.Succeeded);
Assert.AreEqual(ProcessOutput, output.StandardOutput);
Assert.AreEqual(string.Empty, output.StandardError);
}

[TestMethod]
[ExpectedException(typeof(Win32Exception))]
public async Task GetJsonOutputAsync_InvalidTarget()
{
await ProcessUtils.GetJsonOutputAsync<string>("BadTarget.exe", StdOutArgs);
}

[TestMethod]
[ExpectedException(typeof(JsonOutputException))]
public async Task GetJsonOutputAsync_ProcessError()
{
await ProcessUtils.GetJsonOutputAsync<string>(EchoAppName, ExpArgs);
}

[TestMethod]
[ExpectedException(typeof(JsonOutputException))]
public async Task GetJsonOutputAsync_InvalidJson()
{
await ProcessUtils.GetJsonOutputAsync<string>(EchoAppName, StdOutArgs);
}

[TestMethod]
public async Task GetJsonOutputAsync_Success()
{
JsonDataClass output = await ProcessUtils.GetJsonOutputAsync<JsonDataClass>(EchoAppName, JsonArgs);

Assert.IsNotNull(output);
Assert.IsNotNull(output.Var);
Assert.AreEqual(ProcessOutput, output.Var);
}
}
}
34 changes: 17 additions & 17 deletions GoogleCloudExtension/GoogleCloudExtension.Utils/ProcessUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public enum OutputStream
}

/// <summary>
/// Evemt args passed to the output handler of a process.
/// Event args passed to the output handler of a process.
/// </summary>
public class OutputHandlerEventArgs : EventArgs
{
Expand Down Expand Up @@ -126,19 +126,20 @@ public static Task<ProcessOutput> GetCommandOutputAsync(
string workingDir = null,
IDictionary<string, string> environment = null)
{
var startInfo = GetStartInfoForInteractiveProcess(
ProcessStartInfo startInfo = GetStartInfoForInteractiveProcess(
file: file,
args: args,
workingDir: workingDir,
environment: environment);

return Task.Run(async () =>
{
var process = Process.Start(startInfo);
var readErrorsTask = process.StandardError.ReadToEndAsync();
var readOutputTask = process.StandardOutput.ReadToEndAsync();
Process process = Process.Start(startInfo);
Debug.Assert(process != null, $"{nameof(process)} should never be null when UseShellExecute is false.");
Task<string> readErrorsTask = process.StandardError.ReadToEndAsync();
Task<string> readOutputTask = process.StandardOutput.ReadToEndAsync();
process.WaitForExit();
var succeeded = process.ExitCode == 0;
bool succeeded = process.ExitCode == 0;
return new ProcessOutput(
succeeded: succeeded,
standardOutput: await readOutputTask,
Expand All @@ -160,7 +161,7 @@ public static async Task<T> GetJsonOutputAsync<T>(
string workingDir = null,
IDictionary<string, string> environment = null)
{
var output = await ProcessUtils.GetCommandOutputAsync(
ProcessOutput output = await GetCommandOutputAsync(
file: file,
args: args,
workingDir: workingDir,
Expand Down Expand Up @@ -188,27 +189,26 @@ private static ProcessStartInfo GetStartInfoForInteractiveProcess(
// If the caller provides a working directory use it otherwise default to the user's profile directory
// so we have a stable working directory instead of a random working directory as Visual Studio changes the
// current working directory often.
ProcessStartInfo startInfo = new ProcessStartInfo
var startInfo = new ProcessStartInfo
{
FileName = file,
Arguments = args,
WorkingDirectory = workingDir ?? Environment.GetEnvironmentVariable("USERPROFILE"),
WorkingDirectory = workingDir ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true
};

// Customize the environment for the incoming process.
if (environment != null)
{
foreach (var entry in environment)
foreach (KeyValuePair<string, string> entry in environment)
{
startInfo.EnvironmentVariables[entry.Key] = entry.Value;
}
}

startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
return startInfo;
}

Expand All @@ -218,7 +218,7 @@ private static Task ReadLinesFromOutput(OutputStream outputStream, StreamReader
{
while (!stream.EndOfStream)
{
var line = stream.ReadLine();
string line = stream.ReadLine();
handler(null, new OutputHandlerEventArgs(line, outputStream));
}
});
Expand Down
9 changes: 9 additions & 0 deletions GoogleCloudExtension/GoogleCloudExtension.sln
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoogleCloudExtension.TeamEx
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectTemplate.Tests", "ProjectTemplates\ProjectTemplate.Tests\ProjectTemplate.Tests.csproj", "{03C93F30-2ED3-4F20-BFEC-F172937454A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestProjects", "TestProjects", "{A31CD856-439B-427E-8475-C00495ECD737}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EchoApp", "TestProjects\EchoApp\EchoApp.csproj", "{108901A6-861D-4021-A2C9-CE93554A26F5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -169,6 +173,10 @@ Global
{03C93F30-2ED3-4F20-BFEC-F172937454A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03C93F30-2ED3-4F20-BFEC-F172937454A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03C93F30-2ED3-4F20-BFEC-F172937454A1}.Release|Any CPU.Build.0 = Release|Any CPU
{108901A6-861D-4021-A2C9-CE93554A26F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{108901A6-861D-4021-A2C9-CE93554A26F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{108901A6-861D-4021-A2C9-CE93554A26F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{108901A6-861D-4021-A2C9-CE93554A26F5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -179,5 +187,6 @@ Global
{AF37626B-D22B-408A-BC79-C25C097FECF7} = {E7013004-B8B7-4226-8271-D2A0AD141DB2}
{30CB8C77-59A5-4E6C-B122-F614135CE7DA} = {E7013004-B8B7-4226-8271-D2A0AD141DB2}
{03C93F30-2ED3-4F20-BFEC-F172937454A1} = {E7013004-B8B7-4226-8271-D2A0AD141DB2}
{108901A6-861D-4021-A2C9-CE93554A26F5} = {A31CD856-439B-427E-8475-C00495ECD737}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
namespace GoogleCloudExtension.Projects.DotNetCore
{
/// <summary>
/// This class represetns a project.json based .NET Core project.
/// This class represents a project.json based .NET Core project.
/// </summary>
internal class JsonProject : IParsedProject
{
Expand Down
6 changes: 6 additions & 0 deletions GoogleCloudExtension/TestProjects/EchoApp/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
60 changes: 60 additions & 0 deletions GoogleCloudExtension/TestProjects/EchoApp/EchoApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{108901A6-861D-4021-A2C9-CE93554A26F5}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>EchoApp</RootNamespace>
<AssemblyName>EchoApp</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
33 changes: 33 additions & 0 deletions GoogleCloudExtension/TestProjects/EchoApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Linq;

namespace EchoApp
{
public static class Program
{
public static int Main(string[] args)
{
if (args.Length > 1)
{
string flag = args[0];
string message = string.Join(" ", args.Skip(1));
if (flag.Equals("-out", StringComparison.OrdinalIgnoreCase))
{
Console.Write(message);
return 0;
}
else if (flag.Equals("-err", StringComparison.OrdinalIgnoreCase))
{
Console.Error.Write(message);
return 0;
}
else if (flag.Equals("-exp", StringComparison.OrdinalIgnoreCase))
{
Console.Write(message);
return 1;
}
}
return -1;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("EchoApp")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("EchoApp")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("108901a6-861d-4021-a2c9-ce93554a26f5")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

0 comments on commit f45cec8

Please sign in to comment.