diff --git a/src/Core/WinSWCore/Util/ProcessHelper.cs b/src/Core/WinSWCore/Util/ProcessHelper.cs index fd4fdc1..3836afa 100644 --- a/src/Core/WinSWCore/Util/ProcessHelper.cs +++ b/src/Core/WinSWCore/Util/ProcessHelper.cs @@ -118,7 +118,6 @@ namespace winsw.Util } } - //TODO: generalize API /// /// Starts a process and asynchronosly waits for its termination. /// Once the process exits, the callback will be invoked. @@ -129,24 +128,27 @@ namespace winsw.Util /// Additional environment variables /// Working directory /// Priority - /// Completion callback - public static void StartProcessAndCallbackForExit(Process processToStart, String executable, string arguments, Dictionary envVars, - string workingDirectory, ProcessPriorityClass priority, ProcessCompletionCallback callback) + /// Completion callback. If null, the completion won't be monitored + public static void StartProcessAndCallbackForExit(Process processToStart, String executable = null, string arguments = null, Dictionary envVars = null, + string workingDirectory = null, ProcessPriorityClass? priority = null, ProcessCompletionCallback callback = null) { var ps = processToStart.StartInfo; - ps.FileName = executable; - ps.Arguments = arguments; - ps.WorkingDirectory = workingDirectory; + ps.FileName = executable ?? ps.FileName; + ps.Arguments = arguments ?? ps.Arguments; + ps.WorkingDirectory = workingDirectory ?? ps.WorkingDirectory; ps.CreateNoWindow = false; ps.UseShellExecute = false; ps.RedirectStandardInput = true; // this creates a pipe for stdin to the new process, instead of having it inherit our stdin. ps.RedirectStandardOutput = true; ps.RedirectStandardError = true; - foreach (string key in envVars.Keys) + if (envVars != null) { - Environment.SetEnvironmentVariable(key, envVars[key]); - // ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163) + foreach (string key in envVars.Keys) + { + Environment.SetEnvironmentVariable(key, envVars[key]); + // ps.EnvironmentVariables[key] = envs[key]; // bugged (lower cases all variable names due to StringDictionary being used, see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=326163) + } } //TODO: move outside, stubbed to reproduce the issue @@ -157,15 +159,20 @@ namespace winsw.Util processToStart.Start(); Logger.Info("Started process " + processToStart.Id); - if (priority != ProcessPriorityClass.Normal) - processToStart.PriorityClass = priority; + if (priority != null && priority.Value != ProcessPriorityClass.Normal) + { + processToStart.PriorityClass = priority.Value; + } // monitor the completion of the process - StartThread(delegate + if (callback != null) { - processToStart.WaitForExit(); - callback(processToStart); - }); + StartThread(delegate + { + processToStart.WaitForExit(); + callback(processToStart); + }); + } } /// diff --git a/src/Test/winswTests/Util/FilesystemTestHelper.cs b/src/Test/winswTests/Util/FilesystemTestHelper.cs new file mode 100644 index 0000000..63c1fb3 --- /dev/null +++ b/src/Test/winswTests/Util/FilesystemTestHelper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace winswTests.Util +{ + class FilesystemTestHelper + { + /// + /// Creates a temporary directory for testing. + /// + /// tmp Dir + public static string CreateTmpDirectory(String testName = null) + { + string tempDirectory = Path.Combine(Path.GetTempPath(), "winswTests_" + (testName ?? "") + Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + Console.Out.WriteLine("Created the temporary directory: {0}", tempDirectory); + return tempDirectory; + } + + /// + /// Parses output of the "set" command from the file + /// + /// File path + /// Dictionary of the strings. + public static Dictionary parseSetOutput(string filePath) + { + Dictionary res = new Dictionary(); + var lines = File.ReadAllLines(filePath); + foreach(var line in lines) { + line.Split("=".ToCharArray(), 2); + } + return res; + } + } +} diff --git a/src/Test/winswTests/Util/ProcessHelperTest.cs b/src/Test/winswTests/Util/ProcessHelperTest.cs new file mode 100644 index 0000000..d537e26 --- /dev/null +++ b/src/Test/winswTests/Util/ProcessHelperTest.cs @@ -0,0 +1,48 @@ +using System; +using System.Diagnostics; +using NUnit.Framework; +using winsw; +using System.IO; +using winsw.Util; + +namespace winswTests.Util +{ + + [TestFixture] + class ProcessHelperTest + { + /// + /// Also reported as JENKINS-42744 + /// + [Test] + public void ShouldPropagateVariablesInUppercase() + { + var tmpDir = FilesystemTestHelper.CreateTmpDirectory(); + String envFile = Path.Combine(tmpDir, "env.properties"); + String scriptFile = Path.Combine(tmpDir, "printenv.bat"); + File.WriteAllText(scriptFile, "set > " + envFile); + + + Process proc = new Process(); + var ps = proc.StartInfo; + ps.FileName = scriptFile; + + ProcessHelper.StartProcessAndCallbackForExit(proc); + var exited = proc.WaitForExit(5000); + if (!exited) + { + Assert.Fail("Process " + proc + " didn't exit after 5 seconds"); + } + + // Check several veriables, which are expected to be in Uppercase + var envVars = FilesystemTestHelper.parseSetOutput(envFile); + Assert.That(envVars.ContainsKey("PROCESSOR_ARCHITECTURE"), "No PROCESSOR_ARCHITECTURE in the injected vars"); + Assert.That(envVars.ContainsKey("COMPUTERNAME"), "No COMPUTERNAME in the injected vars"); + Assert.That(envVars.ContainsKey("PATHEXT"), "No PATHEXT in the injected vars"); + + // And just ensure that the parsing logic is case-sensitive + Assert.That(!envVars.ContainsKey("computername"), "Test error: the environment parsing logic is case-insensitive"); + + } + } +} diff --git a/src/Test/winswTests/winswTests.csproj b/src/Test/winswTests/winswTests.csproj index 7292731..27f3c39 100644 --- a/src/Test/winswTests/winswTests.csproj +++ b/src/Test/winswTests/winswTests.csproj @@ -60,6 +60,8 @@ + +