Browse Source

Better ProcessRunner that supports using the same pipe for stdout+stderr, and provides async APIs.

pull/32/merge
Daniel Grunwald 13 years ago
parent
commit
de381fe654
  1. 88
      SharpDevelop.Tests.sln
  2. 2
      src/AddIns/Analysis/MachineSpecifications/MachineSpecifications/src/MSpecTestProcessRunnerContext.cs
  3. 24
      src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestProcessRunner.cs
  4. 63
      src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestProcessRunner.cs
  5. 12
      src/AddIns/Analysis/UnitTesting/TestRunner/ITestRunner.cs
  6. 21
      src/AddIns/Analysis/UnitTesting/TestRunner/MessageReceivedEventArgs.cs
  7. 2
      src/AddIns/Analysis/UnitTesting/TestRunner/TestDebuggerBase.cs
  8. 9
      src/AddIns/Analysis/UnitTesting/TestRunner/TestExecutionManager.cs
  9. 29
      src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBase.cs
  10. 8
      src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBaseContext.cs
  11. 20
      src/AddIns/Analysis/UnitTesting/TestRunner/TestRunnerBase.cs
  12. 3
      src/AddIns/Analysis/UnitTesting/UnitTesting.csproj
  13. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin/DefaultChangeWatcher.cs
  14. 43
      src/AddIns/VersionControl/GitAddIn/Src/Git.cs
  15. 98
      src/AddIns/VersionControl/GitAddIn/Src/GitStatusCache.cs
  16. 137
      src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs
  17. 22
      src/AddIns/VersionControl/GitAddIn/Src/RegisterEventsCommand.cs
  18. 18
      src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs
  19. 5
      src/Main/Base/Project/Editor/IDocumentVersionProvider.cs
  20. 1
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  21. 46
      src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs
  22. 38
      src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/ServiceReference/SvcUtilRunner.cs
  23. 42
      src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategoryTextWriter.cs
  24. 3
      src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/DefaultFileNodeCommands.cs
  25. 15
      src/Main/Base/Project/Util/NativeMethods.cs
  26. 686
      src/Main/Base/Project/Util/ProcessRunner.cs
  27. 22
      src/Main/Base/Project/Util/ReactiveExtensions.cs
  28. 9
      src/Main/Base/Project/Util/SharpDevelopExtensions.cs
  29. 60
      src/Main/Base/Test/AbstractEntityIsOverridableTestFixture.cs
  30. 46
      src/Main/Base/Test/CheckAssemblyFlags.cs
  31. 20
      src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj
  32. 51
      src/Main/Base/Test/LineReceivedFromProcessTestFixture.cs
  33. 74
      src/Main/Base/Test/ProcessExitedTestFixture.cs
  34. 6
      src/Main/Base/Test/ProcessRunner/CancelLongRunningAppTestFixture.cs
  35. 5
      src/Main/Base/Test/ProcessRunner/ConsoleAppTestFixtureBase.cs
  36. 8
      src/Main/Base/Test/ProcessRunner/ExitCodeTestFixture.cs
  37. 4
      src/Main/Base/Test/ProcessRunner/NoSuchExecutableTestFixture.cs
  38. 34
      src/Main/Base/Test/ProcessRunner/ProcessExitedTestFixture.cs
  39. 19
      src/Main/Base/Test/ProcessRunner/ProcessRunnerNotStartedTestFixture.cs
  40. 123
      src/Main/Base/Test/ProcessRunner/StandardOutputFromProcessTestFixture.cs

88
SharpDevelop.Tests.sln

@ -1,10 +1,8 @@
 
Microsoft Visual Studio Solution File, Format Version 11.00 Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010 # Visual Studio 2010
# SharpDevelop 4.3 # SharpDevelop 5.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker35", "src\Main\ICSharpCode.SharpDevelop.BuildWorker35\ICSharpCode.SharpDevelop.BuildWorker35.csproj", "{B5F54272-49F0-40DB-845A-8D837875D3BA}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker35", "src\Main\ICSharpCode.SharpDevelop.BuildWorker35\ICSharpCode.SharpDevelop.BuildWorker35.csproj", "{B5F54272-49F0-40DB-845A-8D837875D3BA}"
EndProject EndProject
@ -27,8 +25,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpDevelop", "src\Main\SharpDevelop\SharpDevelop.csproj", "{1152B71B-3C05-4598-B20D-823B5D40559E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpDevelop", "src\Main\SharpDevelop\SharpDevelop.csproj", "{1152B71B-3C05-4598-B20D-823B5D40559E}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{2A232EF1-EB95-41C6-B63A-C106E0C95D3C}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{2A232EF1-EB95-41C6-B63A-C106E0C95D3C}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "src\Libraries\cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "src\Libraries\cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}"
EndProject EndProject
@ -58,20 +54,14 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "src\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "src\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{39327899-ED91-4F7F-988C-4FE4E17C014D}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{39327899-ED91-4F7F-988C-4FE4E17C014D}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Version Control", "Version Control", "{F208FF4F-E5D8-41D5-A7C7-B463976F156E}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Version Control", "Version Control", "{F208FF4F-E5D8-41D5-A7C7-B463976F156E}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitAddIn", "src\AddIns\VersionControl\GitAddIn\GitAddIn.csproj", "{83F15BA7-8478-4664-81BB-A82F146D88B3}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitAddIn", "src\AddIns\VersionControl\GitAddIn\GitAddIn.csproj", "{83F15BA7-8478-4664-81BB-A82F146D88B3}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubversionAddIn", "src\AddIns\VersionControl\SubversionAddIn\SubversionAddIn.csproj", "{17F4D7E0-6933-4C2E-8714-FD7E98D625D5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubversionAddIn", "src\AddIns\VersionControl\SubversionAddIn\SubversionAddIn.csproj", "{17F4D7E0-6933-4C2E-8714-FD7E98D625D5}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Language Bindings", "Language Bindings", "{E0646C25-36F2-4524-969F-FA621353AB94}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Language Bindings", "Language Bindings", "{E0646C25-36F2-4524-969F-FA621353AB94}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpBinding", "src\AddIns\BackendBindings\CSharpBinding\Project\CSharpBinding.csproj", "{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpBinding", "src\AddIns\BackendBindings\CSharpBinding\Project\CSharpBinding.csproj", "{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A}"
EndProject EndProject
@ -96,8 +86,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Scripting.Tests", "src\AddIns\BackendBindings\Scripting\Test\ICSharpCode.Scripting.Tests.csproj", "{85C09AD8-183B-403A-869A-7226646218A9}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Scripting.Tests", "src\AddIns\BackendBindings\Scripting\Test\ICSharpCode.Scripting.Tests.csproj", "{85C09AD8-183B-403A-869A-7226646218A9}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{F3662720-9EA2-4591-BBC6-97361DCE50A9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{F3662720-9EA2-4591-BBC6-97361DCE50A9}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpViewer", "src\AddIns\Misc\HelpViewer\HelpViewer.csproj", "{80F76D10-0B44-4D55-B4BD-DAEB5464090C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpViewer", "src\AddIns\Misc\HelpViewer\HelpViewer.csproj", "{80F76D10-0B44-4D55-B4BD-DAEB5464090C}"
EndProject EndProject
@ -114,8 +102,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegExpTk", "src\AddIns\Misc\RegExpTk\Project\RegExpTk.csproj", "{64A3E5E6-90BF-47F6-94DF-68C94B62C817}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegExpTk", "src\AddIns\Misc\RegExpTk\Project\RegExpTk.csproj", "{64A3E5E6-90BF-47F6-94DF-68C94B62C817}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UsageDataCollector", "UsageDataCollector", "{DEFC8584-BEC3-4921-BD0F-40482E450B7B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UsageDataCollector", "UsageDataCollector", "{DEFC8584-BEC3-4921-BD0F-40482E450B7B}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector\UsageDataCollector.csproj", "{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector\UsageDataCollector.csproj", "{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0}"
EndProject EndProject
@ -128,8 +114,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextTemplating", "src\AddIns\Misc\TextTemplating\Project\TextTemplating.csproj", "{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextTemplating", "src\AddIns\Misc\TextTemplating\Project\TextTemplating.csproj", "{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PackageManagement", "PackageManagement", "{485A4CCF-55CF-49F4-BD6D-A22B788C67DA}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PackageManagement", "PackageManagement", "{485A4CCF-55CF-49F4-BD6D-A22B788C67DA}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement", "src\AddIns\Misc\PackageManagement\Project\PackageManagement.csproj", "{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement", "src\AddIns\Misc\PackageManagement\Project\PackageManagement.csproj", "{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1}"
EndProject EndProject
@ -144,8 +128,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.Cmdlets.Tests", "src\AddIns\Misc\PackageManagement\Cmdlets\Test\PackageManagement.Cmdlets.Tests.csproj", "{11115C83-3DB1-431F-8B98-59040359238D}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.Cmdlets.Tests", "src\AddIns\Misc\PackageManagement\Cmdlets\Test\PackageManagement.Cmdlets.Tests.csproj", "{11115C83-3DB1-431F-8B98-59040359238D}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Display Bindings", "Display Bindings", "{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Display Bindings", "Display Bindings", "{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonEdit.AddIn", "src\AddIns\DisplayBindings\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj", "{0162E499-42D0-409B-AA25-EED21F75336B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonEdit.AddIn", "src\AddIns\DisplayBindings\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj", "{0162E499-42D0-409B-AA25-EED21F75336B}"
EndProject EndProject
@ -160,8 +142,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpyAddIn", "src\AddIns\DisplayBindings\ILSpyAddIn\ILSpyAddIn.csproj", "{8AA421C8-D7AF-4957-9F43-5135328ACB24}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpyAddIn", "src\AddIns\DisplayBindings\ILSpyAddIn\ILSpyAddIn.csproj", "{8AA421C8-D7AF-4957-9F43-5135328ACB24}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{0F5192F2-0744-4BA9-A074-6BE82D111B8D}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{0F5192F2-0744-4BA9-A074-6BE82D111B8D}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Addin", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Addin\ICSharpCode.Data.Addin.csproj", "{A9F12710-24E4-46D4-832C-6ECB395B9EAD}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Addin", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Addin\ICSharpCode.Data.Addin.csproj", "{A9F12710-24E4-46D4-832C-6ECB395B9EAD}"
EndProject EndProject
@ -176,8 +156,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.SQLServer", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.SQLServer\ICSharpCode.Data.SQLServer.csproj", "{AFE34868-AFA1-4E1C-9450-47AB4BE329D5}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.SQLServer", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.SQLServer\ICSharpCode.Data.SQLServer.csproj", "{AFE34868-AFA1-4E1C-9450-47AB4BE329D5}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WpfDesign", "WpfDesign", "{83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WpfDesign", "WpfDesign", "{83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign\Project\WpfDesign.csproj", "{66A378A1-E9F4-4AD5-8946-D0EC06C2902F}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign\Project\WpfDesign.csproj", "{66A378A1-E9F4-4AD5-8946-D0EC06C2902F}"
EndProject EndProject
@ -192,8 +170,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SettingsEditor", "src\AddIns\DisplayBindings\SettingsEditor\Project\SettingsEditor.csproj", "{85226AFB-CE71-4851-9A75-7EEC663A8E8A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SettingsEditor", "src\AddIns\DisplayBindings\SettingsEditor\Project\SettingsEditor.csproj", "{85226AFB-CE71-4851-9A75-7EEC663A8E8A}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{AF5E0DC1-1FA0-4346-A436-0C817C68F7C1}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{AF5E0DC1-1FA0-4346-A436-0C817C68F7C1}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "src\AddIns\Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "src\AddIns\Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}"
EndProject EndProject
@ -202,8 +178,6 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Tests", "src\AddIns\Debugger\Debugger.Tests\Debugger.Tests.csproj", "{A4C858C8-51B6-4265-A695-A20FCEBA1D19}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Tests", "src\AddIns\Debugger\Debugger.Tests\Debugger.Tests.csproj", "{A4C858C8-51B6-4265-A695-A20FCEBA1D19}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analysis", "Analysis", "{B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analysis", "Analysis", "{B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTesting", "src\AddIns\Analysis\UnitTesting\UnitTesting.csproj", "{1F261725-6318-4434-A1B1-6C70CE4CD324}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTesting", "src\AddIns\Analysis\UnitTesting\UnitTesting.csproj", "{1F261725-6318-4434-A1B1-6C70CE4CD324}"
EndProject EndProject
@ -633,6 +607,22 @@ Global
{56E98A01-8398-4A08-9578-C7337711A52B}.Release|Any CPU.Build.0 = Release|x86 {56E98A01-8398-4A08-9578-C7337711A52B}.Release|Any CPU.Build.0 = Release|x86
{56E98A01-8398-4A08-9578-C7337711A52B}.Release|x86.ActiveCfg = Release|x86 {56E98A01-8398-4A08-9578-C7337711A52B}.Release|x86.ActiveCfg = Release|x86
{56E98A01-8398-4A08-9578-C7337711A52B}.Release|x86.Build.0 = Release|x86 {56E98A01-8398-4A08-9578-C7337711A52B}.Release|x86.Build.0 = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|Any CPU.ActiveCfg = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|Any CPU.Build.0 = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|x86.ActiveCfg = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|x86.Build.0 = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|Any CPU.ActiveCfg = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|Any CPU.Build.0 = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|x86.ActiveCfg = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|x86.Build.0 = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|Any CPU.ActiveCfg = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|Any CPU.Build.0 = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|x86.ActiveCfg = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|x86.Build.0 = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|Any CPU.ActiveCfg = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|Any CPU.Build.0 = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|x86.ActiveCfg = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|x86.Build.0 = Release|x86
{0162E499-42D0-409B-AA25-EED21F75336B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0162E499-42D0-409B-AA25-EED21F75336B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0162E499-42D0-409B-AA25-EED21F75336B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0162E499-42D0-409B-AA25-EED21F75336B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0162E499-42D0-409B-AA25-EED21F75336B}.Debug|x86.ActiveCfg = Debug|Any CPU {0162E499-42D0-409B-AA25-EED21F75336B}.Debug|x86.ActiveCfg = Debug|Any CPU
@ -881,22 +871,6 @@ Global
{3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|Any CPU.Build.0 = Release|Any CPU {3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|Any CPU.Build.0 = Release|Any CPU
{3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|x86.ActiveCfg = Release|Any CPU {3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|x86.ActiveCfg = Release|Any CPU
{3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|x86.Build.0 = Release|Any CPU {3DF4060F-5EE0-41CF-8096-F27355FD5511}.Release|x86.Build.0 = Release|Any CPU
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|Any CPU.Build.0 = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|Any CPU.ActiveCfg = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|x86.Build.0 = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Debug|x86.ActiveCfg = Debug|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|Any CPU.Build.0 = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|Any CPU.ActiveCfg = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|x86.Build.0 = Release|x86
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}.Release|x86.ActiveCfg = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|Any CPU.Build.0 = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|Any CPU.ActiveCfg = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|x86.Build.0 = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Debug|x86.ActiveCfg = Debug|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|Any CPU.Build.0 = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|Any CPU.ActiveCfg = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|x86.Build.0 = Release|x86
{11115C83-3DB1-431F-8B98-59040359238D}.Release|x86.ActiveCfg = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -915,24 +889,19 @@ Global
{D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{6222A3A1-83CE-47A3-A4E4-A018F82D44D8} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {6222A3A1-83CE-47A3-A4E4-A018F82D44D8} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{53DCA265-3C3C-42F9-B647-F72BA678122B} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{DC393B66-92ED-4CAD-AB25-CFEF23F3D7C6} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{63D3B27A-D966-4902-90B3-30290E1692F1} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{2FF700C2-A38A-48BD-A637-8CAFD4FE6237} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {2FF700C2-A38A-48BD-A637-8CAFD4FE6237} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{4139CCF6-FB49-4A9D-B2CF-331E9EA3198D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {4139CCF6-FB49-4A9D-B2CF-331E9EA3198D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{DDE2A481-8271-4EAC-A330-8FA6A38D13D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {DDE2A481-8271-4EAC-A330-8FA6A38D13D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{984CC812-9470-4A13-AFF9-CC44068D666C} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C} {984CC812-9470-4A13-AFF9-CC44068D666C} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{53DCA265-3C3C-42F9-B647-F72BA678122B} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{DC393B66-92ED-4CAD-AB25-CFEF23F3D7C6} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{63D3B27A-D966-4902-90B3-30290E1692F1} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{F208FF4F-E5D8-41D5-A7C7-B463976F156E} = {39327899-ED91-4F7F-988C-4FE4E17C014D} {F208FF4F-E5D8-41D5-A7C7-B463976F156E} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{E0646C25-36F2-4524-969F-FA621353AB94} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{F3662720-9EA2-4591-BBC6-97361DCE50A9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{AF5E0DC1-1FA0-4346-A436-0C817C68F7C1} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{E2FD63DA-8478-4066-934C-DA82A852C83A} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{83F15BA7-8478-4664-81BB-A82F146D88B3} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E} {83F15BA7-8478-4664-81BB-A82F146D88B3} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{17F4D7E0-6933-4C2E-8714-FD7E98D625D5} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E} {17F4D7E0-6933-4C2E-8714-FD7E98D625D5} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{E0646C25-36F2-4524-969F-FA621353AB94} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A} = {E0646C25-36F2-4524-969F-FA621353AB94} {1F1AC7CD-D154-45BB-8EAF-804CA8055F5A} = {E0646C25-36F2-4524-969F-FA621353AB94}
{E954F3CB-A446-492F-A664-2B376EBC86E8} = {E0646C25-36F2-4524-969F-FA621353AB94} {E954F3CB-A446-492F-A664-2B376EBC86E8} = {E0646C25-36F2-4524-969F-FA621353AB94}
{70966F84-74C9-4067-A379-0C674A929233} = {E0646C25-36F2-4524-969F-FA621353AB94} {70966F84-74C9-4067-A379-0C674A929233} = {E0646C25-36F2-4524-969F-FA621353AB94}
@ -944,6 +913,7 @@ Global
{388E7B64-0393-4EB4-A3E3-5C474F141853} = {E0646C25-36F2-4524-969F-FA621353AB94} {388E7B64-0393-4EB4-A3E3-5C474F141853} = {E0646C25-36F2-4524-969F-FA621353AB94}
{7048AE18-EB93-4A84-82D0-DD60EB58ADBD} = {E0646C25-36F2-4524-969F-FA621353AB94} {7048AE18-EB93-4A84-82D0-DD60EB58ADBD} = {E0646C25-36F2-4524-969F-FA621353AB94}
{85C09AD8-183B-403A-869A-7226646218A9} = {E0646C25-36F2-4524-969F-FA621353AB94} {85C09AD8-183B-403A-869A-7226646218A9} = {E0646C25-36F2-4524-969F-FA621353AB94}
{F3662720-9EA2-4591-BBC6-97361DCE50A9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{80F76D10-0B44-4D55-B4BD-DAEB5464090C} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {80F76D10-0B44-4D55-B4BD-DAEB5464090C} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{9196DD8A-B4D4-4780-8742-C5762E547FC2} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {9196DD8A-B4D4-4780-8742-C5762E547FC2} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
@ -952,18 +922,19 @@ Global
{D022A6CE-7438-41E8-AC64-F2DE18EC54C6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {D022A6CE-7438-41E8-AC64-F2DE18EC54C6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{64A3E5E6-90BF-47F6-94DF-68C94B62C817} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {64A3E5E6-90BF-47F6-94DF-68C94B62C817} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{DEFC8584-BEC3-4921-BD0F-40482E450B7B} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {DEFC8584-BEC3-4921-BD0F-40482E450B7B} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{0008FCE9-9EB4-4E2E-979B-553278E5BBA6} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{A569DCC1-C608-45FD-B770-4F79335EF154} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {A569DCC1-C608-45FD-B770-4F79335EF154} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{5186325C-DD7F-4246-9BE7-3F384EFBF5A6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {5186325C-DD7F-4246-9BE7-3F384EFBF5A6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {B5D8C3E6-42EC-4D4B-AD05-3644B32563EF} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{485A4CCF-55CF-49F4-BD6D-A22B788C67DA} = {F3662720-9EA2-4591-BBC6-97361DCE50A9} {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{0008FCE9-9EB4-4E2E-979B-553278E5BBA6} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {AE4AB0FA-6087-4480-AF37-0FA1452B3DA1} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{A406803B-C584-43A3-BCEE-A0BB3132CB5F} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {A406803B-C584-43A3-BCEE-A0BB3132CB5F} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{C3F15E22-5793-4129-AF8C-6229112B86D2} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {C3F15E22-5793-4129-AF8C-6229112B86D2} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{56E98A01-8398-4A08-9578-C7337711A52B} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {56E98A01-8398-4A08-9578-C7337711A52B} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {E0A5E80A-003B-4335-A9DC-A76E2E46D38D} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{11115C83-3DB1-431F-8B98-59040359238D} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA} {11115C83-3DB1-431F-8B98-59040359238D} = {485A4CCF-55CF-49F4-BD6D-A22B788C67DA}
{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{0162E499-42D0-409B-AA25-EED21F75336B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {0162E499-42D0-409B-AA25-EED21F75336B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{E618A9CD-A39F-4925-A538-E8A3FEF24E54} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {E618A9CD-A39F-4925-A538-E8A3FEF24E54} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{DCA2703D-250A-463E-A68A-07ED105AE6BD} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {DCA2703D-250A-463E-A68A-07ED105AE6BD} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
@ -971,22 +942,24 @@ Global
{DFB936AD-90EE-4B4F-941E-4F4A636F0D92} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {DFB936AD-90EE-4B4F-941E-4F4A636F0D92} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{8AA421C8-D7AF-4957-9F43-5135328ACB24} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {8AA421C8-D7AF-4957-9F43-5135328ACB24} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{0F5192F2-0744-4BA9-A074-6BE82D111B8D} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} {0F5192F2-0744-4BA9-A074-6BE82D111B8D} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{85226AFB-CE71-4851-9A75-7EEC663A8E8A} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{A9F12710-24E4-46D4-832C-6ECB395B9EAD} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {A9F12710-24E4-46D4-832C-6ECB395B9EAD} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{B7823AE9-4B43-4859-8796-2EBDC116FBB8} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {B7823AE9-4B43-4859-8796-2EBDC116FBB8} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{BAD94D6E-4159-4CB6-B991-486F412D9BB6} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {BAD94D6E-4159-4CB6-B991-486F412D9BB6} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{5C70D6AB-0A33-43F9-B8B5-54558C35BBB1} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {5C70D6AB-0A33-43F9-B8B5-54558C35BBB1} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{EEF5E054-4192-4A57-8FBF-E860D808A51D} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {EEF5E054-4192-4A57-8FBF-E860D808A51D} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{AFE34868-AFA1-4E1C-9450-47AB4BE329D5} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D} {AFE34868-AFA1-4E1C-9450-47AB4BE329D5} = {0F5192F2-0744-4BA9-A074-6BE82D111B8D}
{83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{66A378A1-E9F4-4AD5-8946-D0EC06C2902F} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} {66A378A1-E9F4-4AD5-8946-D0EC06C2902F} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}
{9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} {9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}
{943DBBB3-E84E-4CF4-917C-C05AFA8743C1} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} {943DBBB3-E84E-4CF4-917C-C05AFA8743C1} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}
{78CC29AC-CC79-4355-B1F2-97936DF198AC} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} {78CC29AC-CC79-4355-B1F2-97936DF198AC} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}
{88DA149F-21B2-48AB-82C4-28FB6BDFD783} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5} {88DA149F-21B2-48AB-82C4-28FB6BDFD783} = {83BAB756-1010-4A2F-9B9D-7F9EBCB288F5}
{85226AFB-CE71-4851-9A75-7EEC663A8E8A} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{AF5E0DC1-1FA0-4346-A436-0C817C68F7C1} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1} {1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1}
{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1} {EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1}
{A4C858C8-51B6-4265-A695-A20FCEBA1D19} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1} {A4C858C8-51B6-4265-A695-A20FCEBA1D19} = {AF5E0DC1-1FA0-4346-A436-0C817C68F7C1}
{B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{1F261725-6318-4434-A1B1-6C70CE4CD324} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {1F261725-6318-4434-A1B1-6C70CE4CD324} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
{44A8DE09-CAB9-49D8-9CFC-5EB0A552F181} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {44A8DE09-CAB9-49D8-9CFC-5EB0A552F181} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
{3EAA45A9-735C-4AC7-A799-947B93EA449D} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {3EAA45A9-735C-4AC7-A799-947B93EA449D} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
@ -995,5 +968,6 @@ Global
{08CE9972-283B-44F4-82FA-966F7DFA6B7A} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {08CE9972-283B-44F4-82FA-966F7DFA6B7A} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
{172AE35D-2051-4977-AC13-0BF1B76374D5} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {172AE35D-2051-4977-AC13-0BF1B76374D5} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
{D1DA3B8F-7313-4BDA-8880-461C5F007751} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9} {D1DA3B8F-7313-4BDA-8880-461C5F007751} = {B3352C08-3CB4-4DD9-996F-B9DCE4356BB9}
{E2FD63DA-8478-4066-934C-DA82A852C83A} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

2
src/AddIns/Analysis/MachineSpecifications/MachineSpecifications/src/MSpecTestProcessRunnerContext.cs

@ -12,7 +12,7 @@ namespace ICSharpCode.MachineSpecifications
public MSpecTestProcessRunnerContext(TestExecutionOptions options) public MSpecTestProcessRunnerContext(TestExecutionOptions options)
: base( : base(
options, options,
new UnitTestProcessRunner(), new ProcessRunner(),
new MSpecUnitTestMonitor(), new MSpecUnitTestMonitor(),
SD.FileSystem, SD.FileSystem,
SD.MessageService) SD.MessageService)

24
src/AddIns/Analysis/UnitTesting/Interfaces/IUnitTestProcessRunner.cs

@ -1,24 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
public interface IUnitTestProcessRunner : IDisposable
{
bool LogStandardOutputAndError { get; set; }
string WorkingDirectory { get; set; }
Dictionary<string, string> EnvironmentVariables { get; }
void Start(string command, string arguments);
void Kill();
event EventHandler<LineReceivedEventArgs> OutputLineReceived;
event EventHandler<LineReceivedEventArgs> ErrorLineReceived;
event EventHandler ProcessExited;
}
}

63
src/AddIns/Analysis/UnitTesting/Interfaces/UnitTestProcessRunner.cs

@ -1,63 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using ICSharpCode.SharpDevelop;
namespace ICSharpCode.UnitTesting
{
public class UnitTestProcessRunner : IUnitTestProcessRunner
{
ProcessRunner runner;
public event EventHandler<LineReceivedEventArgs> OutputLineReceived {
add { runner.OutputLineReceived += value; }
remove { runner.OutputLineReceived -= value; }
}
public event EventHandler<LineReceivedEventArgs> ErrorLineReceived {
add { runner.ErrorLineReceived += value; }
remove { runner.ErrorLineReceived -= value; }
}
public event EventHandler ProcessExited {
add { runner.ProcessExited += value; }
remove { runner.ProcessExited -= value; }
}
public UnitTestProcessRunner()
{
runner = new ProcessRunner();
}
public bool LogStandardOutputAndError {
get { return runner.LogStandardOutputAndError; }
set { runner.LogStandardOutputAndError = value; }
}
public string WorkingDirectory {
get { return runner.WorkingDirectory; }
set { runner.WorkingDirectory = value; }
}
public Dictionary<string, string> EnvironmentVariables {
get { return runner.EnvironmentVariables; }
}
public void Start(string command, string arguments)
{
runner.Start(command, arguments);
}
public void Kill()
{
runner.Kill();
}
public void Dispose()
{
runner.Dispose();
}
}
}

12
src/AddIns/Analysis/UnitTesting/TestRunner/ITestRunner.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
@ -12,8 +13,15 @@ namespace ICSharpCode.UnitTesting
public interface ITestRunner : IDisposable public interface ITestRunner : IDisposable
{ {
event EventHandler<TestFinishedEventArgs> TestFinished; event EventHandler<TestFinishedEventArgs> TestFinished;
event EventHandler<MessageReceivedEventArgs> MessageReceived;
Task RunAsync(IEnumerable<ITest> selectedTests, IProgress<double> progress, CancellationToken cancellationToken); /// <summary>
/// Runs the unit tests.
/// </summary>
/// <param name="selectedTests">The tests that were selected for execution.</param>
/// <param name="progress">Object for reporting progress within the run (percentage value between 0 and 1)</param>
/// <param name="output">Output text writer </param>
/// <param name="cancellationToken"></param>
/// <returns>Returns a task that gets marked as completed when all tests have finished execution.</returns>
Task RunAsync(IEnumerable<ITest> selectedTests, IProgress<double> progress, TextWriter output, CancellationToken cancellationToken);
} }
} }

21
src/AddIns/Analysis/UnitTesting/TestRunner/MessageReceivedEventArgs.cs

@ -1,21 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
namespace ICSharpCode.UnitTesting
{
public class MessageReceivedEventArgs : EventArgs
{
string message;
public MessageReceivedEventArgs(string message)
{
this.message = message;
}
public string Message {
get { return message; }
}
}
}

2
src/AddIns/Analysis/UnitTesting/TestRunner/TestDebuggerBase.cs

@ -91,7 +91,7 @@ namespace ICSharpCode.UnitTesting
{ {
debugger.DebugStopped -= DebugStopped; debugger.DebugStopped -= DebugStopped;
testResultsReader.Join(); testResultsReader.Join();
OnAllTestsFinished(source, e); OnAllTestsFinished();
} }
public override void Stop() public override void Stop()

9
src/AddIns/Analysis/UnitTesting/TestRunner/TestExecutionManager.cs

@ -82,9 +82,9 @@ namespace ICSharpCode.UnitTesting.Frameworks
progressMonitor.Progress = GetProgress(projectsLeftToRun); progressMonitor.Progress = GetProgress(projectsLeftToRun);
using (testProgressMonitor = progressMonitor.CreateSubTask(1.0 / testsByProject.Count)) { using (testProgressMonitor = progressMonitor.CreateSubTask(1.0 / testsByProject.Count)) {
using (ITestRunner testRunner = currentProjectBeingTested.CreateTestRunner(options)) { using (ITestRunner testRunner = currentProjectBeingTested.CreateTestRunner(options)) {
testRunner.MessageReceived += testRunner_MessageReceived;
testRunner.TestFinished += testRunner_TestFinished; testRunner.TestFinished += testRunner_TestFinished;
await testRunner.RunAsync(g, testProgressMonitor, testProgressMonitor.CancellationToken); var writer = new MessageViewCategoryTextWriter(testService.UnitTestMessageView);
await testRunner.RunAsync(g, testProgressMonitor, writer, testProgressMonitor.CancellationToken);
} }
} }
projectsLeftToRun--; projectsLeftToRun--;
@ -156,11 +156,6 @@ namespace ICSharpCode.UnitTesting.Frameworks
return (double)(totalProjectCount - projectsLeftToRunCount) / totalProjectCount; return (double)(totalProjectCount - projectsLeftToRunCount) / totalProjectCount;
} }
void testRunner_MessageReceived(object sender, MessageReceivedEventArgs e)
{
testService.UnitTestMessageView.AppendLine(e.Message);
}
void testRunner_TestFinished(object sender, TestFinishedEventArgs e) void testRunner_TestFinished(object sender, TestFinishedEventArgs e)
{ {
mainThread.InvokeAsyncAndForget(delegate { mainThread.InvokeAsyncAndForget(delegate {

29
src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBase.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
@ -13,7 +14,7 @@ namespace ICSharpCode.UnitTesting
public abstract class TestProcessRunnerBase : TestRunnerBase public abstract class TestProcessRunnerBase : TestRunnerBase
{ {
TestExecutionOptions executionOptions; TestExecutionOptions executionOptions;
IUnitTestProcessRunner processRunner; IProcessRunner processRunner;
ITestResultsReader testResultsReader; ITestResultsReader testResultsReader;
IFileSystem fileSystem; IFileSystem fileSystem;
IMessageService messageService; IMessageService messageService;
@ -26,10 +27,6 @@ namespace ICSharpCode.UnitTesting
this.fileSystem = context.FileSystem; this.fileSystem = context.FileSystem;
this.messageService = context.MessageService; this.messageService = context.MessageService;
processRunner.LogStandardOutputAndError = false;
processRunner.OutputLineReceived += OutputLineReceived;
processRunner.ErrorLineReceived += OutputLineReceived;
processRunner.ProcessExited += OnAllTestsFinished;
testResultsReader.TestFinished += OnTestFinished; testResultsReader.TestFinished += OnTestFinished;
} }
@ -37,15 +34,10 @@ namespace ICSharpCode.UnitTesting
get { return testResultsReader; } get { return testResultsReader; }
} }
protected IUnitTestProcessRunner ProcessRunner { protected IProcessRunner ProcessRunner {
get { return processRunner; } get { return processRunner; }
} }
void OutputLineReceived(object source, LineReceivedEventArgs e)
{
OnMessageReceived(e.Line);
}
public override void Start(IEnumerable<ITest> selectedTests) public override void Start(IEnumerable<ITest> selectedTests)
{ {
ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests); ProcessStartInfo startInfo = GetProcessStartInfo(selectedTests);
@ -64,8 +56,12 @@ namespace ICSharpCode.UnitTesting
if (ApplicationFileNameExists(processStartInfo.FileName)) { if (ApplicationFileNameExists(processStartInfo.FileName)) {
testResultsReader.Start(); testResultsReader.Start();
processRunner.WorkingDirectory = processStartInfo.WorkingDirectory; processRunner.WorkingDirectory = DirectoryName.Create(processStartInfo.WorkingDirectory);
processRunner.Start(processStartInfo.FileName, processStartInfo.Arguments); processRunner.RedirectStandardOutputAndErrorToSingleStream = true;
processRunner.StartCommandLine("\"" + processStartInfo.FileName + "\" " + processStartInfo.Arguments);
Task.WhenAll(
processRunner.OpenStandardOutputReader().CopyToAsync(output),
processRunner.WaitForExitAsync()).ContinueWith(_ => OnAllTestsFinished()).FireAndForget();
} else { } else {
ShowApplicationDoesNotExistMessage(processStartInfo.FileName); ShowApplicationDoesNotExistMessage(processStartInfo.FileName);
} }
@ -82,10 +78,10 @@ namespace ICSharpCode.UnitTesting
messageService.ShowErrorFormatted(resourceString, fileName); messageService.ShowErrorFormatted(resourceString, fileName);
} }
protected override void OnAllTestsFinished(object source, EventArgs e) protected override void OnAllTestsFinished()
{ {
testResultsReader.Join(); testResultsReader.Join();
base.OnAllTestsFinished(source, e); base.OnAllTestsFinished();
} }
public override void Stop() public override void Stop()
@ -96,11 +92,8 @@ namespace ICSharpCode.UnitTesting
public override void Dispose() public override void Dispose()
{ {
processRunner.Dispose();
testResultsReader.Dispose(); testResultsReader.Dispose();
testResultsReader.TestFinished -= OnTestFinished; testResultsReader.TestFinished -= OnTestFinished;
processRunner.ErrorLineReceived -= OutputLineReceived;
processRunner.OutputLineReceived -= OutputLineReceived;
} }
} }
} }

8
src/AddIns/Analysis/UnitTesting/TestRunner/TestProcessRunnerBaseContext.cs

@ -10,14 +10,14 @@ namespace ICSharpCode.UnitTesting
public class TestProcessRunnerBaseContext public class TestProcessRunnerBaseContext
{ {
TestExecutionOptions executionOptions; TestExecutionOptions executionOptions;
IUnitTestProcessRunner processRunner; IProcessRunner processRunner;
ITestResultsReader testResultsReader; ITestResultsReader testResultsReader;
IFileSystem fileSystem; IFileSystem fileSystem;
IMessageService messageService; IMessageService messageService;
public TestProcessRunnerBaseContext(TestExecutionOptions executionOptions) public TestProcessRunnerBaseContext(TestExecutionOptions executionOptions)
: this(executionOptions, : this(executionOptions,
new UnitTestProcessRunner(), new ProcessRunner(),
new TestResultsReader(), new TestResultsReader(),
SD.FileSystem, SD.FileSystem,
SD.MessageService) SD.MessageService)
@ -25,7 +25,7 @@ namespace ICSharpCode.UnitTesting
} }
public TestProcessRunnerBaseContext(TestExecutionOptions executionOptions, public TestProcessRunnerBaseContext(TestExecutionOptions executionOptions,
IUnitTestProcessRunner processRunner, IProcessRunner processRunner,
ITestResultsReader testResultsMonitor, ITestResultsReader testResultsMonitor,
IFileSystem fileSystem, IFileSystem fileSystem,
IMessageService messageService) IMessageService messageService)
@ -41,7 +41,7 @@ namespace ICSharpCode.UnitTesting
get { return executionOptions; } get { return executionOptions; }
} }
public IUnitTestProcessRunner TestProcessRunner { public IProcessRunner TestProcessRunner {
get { return processRunner; } get { return processRunner; }
} }

20
src/AddIns/Analysis/UnitTesting/TestRunner/TestRunnerBase.cs

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -18,10 +19,12 @@ namespace ICSharpCode.UnitTesting
TaskCompletionSource<object> tcs; TaskCompletionSource<object> tcs;
CancellationTokenRegistration cancellationTokenRegistration; CancellationTokenRegistration cancellationTokenRegistration;
bool wasCancelled; bool wasCancelled;
protected TextWriter output;
public Task RunAsync(IEnumerable<ITest> selectedTests, IProgress<double> progress, CancellationToken cancellationToken) public Task RunAsync(IEnumerable<ITest> selectedTests, IProgress<double> progress, TextWriter output, CancellationToken cancellationToken)
{ {
this.progress = progress; this.progress = progress;
this.output = output;
progressPerTest = 1.0 / GetExpectedNumberOfTestResults(selectedTests); progressPerTest = 1.0 / GetExpectedNumberOfTestResults(selectedTests);
tcs = new TaskCompletionSource<object>(); tcs = new TaskCompletionSource<object>();
Start(selectedTests); Start(selectedTests);
@ -43,7 +46,7 @@ namespace ICSharpCode.UnitTesting
protected void LogCommandLine(ProcessStartInfo startInfo) protected void LogCommandLine(ProcessStartInfo startInfo)
{ {
string commandLine = GetCommandLine(startInfo); string commandLine = GetCommandLine(startInfo);
OnMessageReceived(commandLine); output.WriteLine(commandLine);
} }
protected string GetCommandLine(ProcessStartInfo startInfo) protected string GetCommandLine(ProcessStartInfo startInfo)
@ -51,13 +54,13 @@ namespace ICSharpCode.UnitTesting
return String.Format("\"{0}\" {1}", startInfo.FileName, startInfo.Arguments); return String.Format("\"{0}\" {1}", startInfo.FileName, startInfo.Arguments);
} }
protected virtual void OnAllTestsFinished(object source, EventArgs e) protected virtual void OnAllTestsFinished()
{ {
cancellationTokenRegistration.Dispose(); cancellationTokenRegistration.Dispose();
if (wasCancelled) if (wasCancelled)
tcs.SetCanceled(); tcs.SetCanceled();
else else
tcs.SetResult(e); tcs.SetResult(null);
} }
public event EventHandler<TestFinishedEventArgs> TestFinished; public event EventHandler<TestFinishedEventArgs> TestFinished;
@ -77,15 +80,6 @@ namespace ICSharpCode.UnitTesting
return testResult; return testResult;
} }
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
protected virtual void OnMessageReceived(string message)
{
if (MessageReceived != null) {
MessageReceived(this, new MessageReceivedEventArgs(message));
}
}
public abstract int GetExpectedNumberOfTestResults(IEnumerable<ITest> selectedTests); public abstract int GetExpectedNumberOfTestResults(IEnumerable<ITest> selectedTests);
public abstract void Dispose(); public abstract void Dispose();
public abstract void Stop(); public abstract void Stop();

3
src/AddIns/Analysis/UnitTesting/UnitTesting.csproj

@ -58,8 +58,6 @@
<Compile Include="Commands\UnitTestCommands.cs" /> <Compile Include="Commands\UnitTestCommands.cs" />
<Compile Include="Pad\UnitTestNodeFactory.cs" /> <Compile Include="Pad\UnitTestNodeFactory.cs" />
<Compile Include="Service\ITestService.cs" /> <Compile Include="Service\ITestService.cs" />
<Compile Include="Interfaces\IUnitTestProcessRunner.cs" />
<Compile Include="Interfaces\UnitTestProcessRunner.cs" />
<Compile Include="Model\ITest.cs" /> <Compile Include="Model\ITest.cs" />
<Compile Include="Model\ITestFramework.cs" /> <Compile Include="Model\ITestFramework.cs" />
<Compile Include="Model\ITestProject.cs" /> <Compile Include="Model\ITestProject.cs" />
@ -92,7 +90,6 @@
<Compile Include="Service\TestFrameworkDoozer.cs" /> <Compile Include="Service\TestFrameworkDoozer.cs" />
<Compile Include="Service\SDTestService.cs" /> <Compile Include="Service\SDTestService.cs" />
<Compile Include="TestRunner\ITestRunner.cs" /> <Compile Include="TestRunner\ITestRunner.cs" />
<Compile Include="TestRunner\MessageReceivedEventArgs.cs" />
<Compile Include="TestRunner\TestDebuggerBase.cs" /> <Compile Include="TestRunner\TestDebuggerBase.cs" />
<Compile Include="TestRunner\TestExecutionManager.cs" /> <Compile Include="TestRunner\TestExecutionManager.cs" />
<Compile Include="TestRunner\TestExecutionOptions.cs" /> <Compile Include="TestRunner\TestExecutionOptions.cs" />

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/ChangeMarkerMargin/DefaultChangeWatcher.cs

@ -143,7 +143,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
Stream GetBaseVersion(FileName fileName) Stream GetBaseVersion(FileName fileName)
{ {
foreach (IDocumentVersionProvider provider in VersioningServices.Instance.DocumentVersionProviders) { foreach (IDocumentVersionProvider provider in VersioningServices.Instance.DocumentVersionProviders) {
var result = provider.OpenBaseVersion(fileName); var result = provider.OpenBaseVersionAsync(fileName).GetAwaiter().GetResult();
if (result != null) { if (result != null) {
usedProvider = provider; usedProvider = provider;
return result; return result;

43
src/AddIns/VersionControl/GitAddIn/Src/Git.cs

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
@ -38,56 +39,36 @@ namespace ICSharpCode.GitAddIn
return null; return null;
} }
public static void Add(string fileName, Action<int> callback) public static Task AddAsync(string fileName)
{ {
string wcRoot = FindWorkingCopyRoot(fileName); string wcRoot = FindWorkingCopyRoot(fileName);
if (wcRoot == null) if (wcRoot == null)
return; return Task.FromResult(false);
RunGit(wcRoot, "add " + AdaptFileName(wcRoot, fileName), callback); return RunGitAsync(wcRoot, "add", AdaptFileName(wcRoot, fileName));
} }
public static void Remove(string fileName, bool indexOnly, Action<int> callback) public static Task RemoveAsync(string fileName, bool indexOnly)
{ {
string wcRoot = FindWorkingCopyRoot(fileName); string wcRoot = FindWorkingCopyRoot(fileName);
if (wcRoot == null) if (wcRoot == null)
return; return Task.FromResult(false);
if (indexOnly) if (indexOnly)
RunGit(wcRoot, "rm --cached " + AdaptFileName(wcRoot, fileName), callback); return RunGitAsync(wcRoot, "rm", "--cached", AdaptFileName(wcRoot, fileName));
else else
RunGit(wcRoot, "rm " + AdaptFileName(wcRoot, fileName), callback); return RunGitAsync(wcRoot, "rm", AdaptFileName(wcRoot, fileName));
} }
public static string AdaptFileName(string wcRoot, string fileName) public static string AdaptFileName(string wcRoot, string fileName)
{
return '"' + AdaptFileNameNoQuotes(wcRoot, fileName) + '"';
}
public static string AdaptFileNameNoQuotes(string wcRoot, string fileName)
{ {
string relFileName = FileUtility.GetRelativePath(wcRoot, fileName); string relFileName = FileUtility.GetRelativePath(wcRoot, fileName);
return relFileName.Replace('\\', '/'); return relFileName.Replace('\\', '/');
} }
public static void RunGit(string workingDir, string arguments, Action<int> finished) public static Task<int> RunGitAsync(string workingDir, params string[] arguments)
{ {
GitMessageView.AppendLine(workingDir + "> git " + arguments); ProcessRunner p = new ProcessRunner();
string git = FindGit(); p.WorkingDirectory = workingDir;
if (git == null) { return p.RunInOutputPadAsync(GitMessageView.Category, "git", arguments);
GitMessageView.AppendLine("Could not find git.exe");
return;
}
ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = workingDir;
runner.LogStandardOutputAndError = false;
runner.OutputLineReceived += (sender, e) => GitMessageView.AppendLine(e.Line);
runner.ErrorLineReceived += (sender, e) => GitMessageView.AppendLine(e.Line);
runner.ProcessExited += delegate {
GitMessageView.AppendLine("Done. (exit code " + runner.ExitCode + ")");
if (finished != null)
finished(runner.ExitCode);
};
runner.Start(git, arguments);
} }
/// <summary> /// <summary>

98
src/AddIns/VersionControl/GitAddIn/Src/GitStatusCache.cs

@ -6,8 +6,10 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.GitAddIn namespace ICSharpCode.GitAddIn
{ {
@ -43,7 +45,7 @@ namespace ICSharpCode.GitAddIn
if (wcroot == null) if (wcroot == null)
return GitStatus.None; return GitStatus.None;
GitStatusSet gss = GetStatusSet(wcroot); GitStatusSet gss = GetStatusSet(wcroot);
return gss.GetStatus(Git.AdaptFileNameNoQuotes(wcroot, fileName)); return gss.GetStatus(Git.AdaptFileName(wcroot, fileName));
} }
public static GitStatusSet GetStatusSet(string wcRoot) public static GitStatusSet GetStatusSet(string wcRoot)
@ -76,26 +78,40 @@ namespace ICSharpCode.GitAddIn
if (git == null) if (git == null)
return; return;
ProcessRunner runner = new ProcessRunner(); using (ProcessRunner runner = new ProcessRunner()) {
runner.WorkingDirectory = wcRoot; runner.WorkingDirectory = DirectoryName.Create(wcRoot);
runner.LogStandardOutputAndError = false; runner.RedirectStandardOutput = true;
runner.OutputLineReceived += delegate(object sender, LineReceivedEventArgs e) { runner.RedirectStandardError = true;
if (!string.IsNullOrEmpty(e.Line)) { runner.Start(git, "ls-files");
statusSet.AddEntry(e.Line, GitStatus.OK);
// process stderr in background
var errorTask = DisplayErrorStreamAsync(runner, GitMessageView.Category);
// process stderr on current thread:
using (var reader = runner.OpenStandardOutputReader()) {
string line;
while ((line = reader.ReadLine()) != null) {
if (line.Length > 0) {
statusSet.AddEntry(line, GitStatus.OK);
}
}
} }
}; errorTask.Wait();
}
string command = "ls-files"; }
bool hasErrors = false;
runner.ErrorLineReceived += delegate(object sender, LineReceivedEventArgs e) { static async Task DisplayErrorStreamAsync(ProcessRunner runner, MessageViewCategory category)
if (!hasErrors) { {
hasErrors = true; using (var reader = runner.OpenStandardErrorReader()) {
GitMessageView.AppendLine(runner.WorkingDirectory + "> git " + command); bool hasErrors = false;
string line;
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) {
if (!hasErrors) {
hasErrors = true;
GitMessageView.AppendLine(runner.WorkingDirectory + "> " + runner.CommandLine);
}
GitMessageView.AppendLine(line);
} }
GitMessageView.AppendLine(e.Line); }
};
runner.Start(git, command);
runner.WaitForExit();
} }
static void GitGetStatus(string wcRoot, GitStatusSet statusSet) static void GitGetStatus(string wcRoot, GitStatusSet statusSet)
@ -104,36 +120,28 @@ namespace ICSharpCode.GitAddIn
if (git == null) if (git == null)
return; return;
string command = "status --porcelain --untracked-files=no";
bool hasErrors = false;
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = wcRoot; runner.WorkingDirectory = DirectoryName.Create(wcRoot);
runner.LogStandardOutputAndError = false; runner.RedirectStandardOutput = true;
runner.OutputLineReceived += delegate(object sender, LineReceivedEventArgs e) { runner.RedirectStandardError = true;
if (!string.IsNullOrEmpty(e.Line)) { runner.Start(git, "status", "--porcelain", "--untracked-files=no");
Match m = statusParseRegex.Match(e.Line); // process stderr in background
if (m.Success) { var errorTask = DisplayErrorStreamAsync(runner, GitMessageView.Category);
statusSet.AddEntry(m.Groups[2].Value, StatusFromText(m.Groups[1].Value)); // process stderr on current thread:
} else { using (var reader = runner.OpenStandardOutputReader()) {
if (!hasErrors) { string line;
// in front of first output line, print the command line we invoked while ((line = reader.ReadLine()) != null) {
hasErrors = true; if (line.Length > 0) {
GitMessageView.AppendLine(runner.WorkingDirectory + "> git " + command); Match m = statusParseRegex.Match(line);
if (m.Success) {
statusSet.AddEntry(m.Groups[2].Value, StatusFromText(m.Groups[1].Value));
} else {
GitMessageView.AppendLine("unknown git status output: " + line);
} }
GitMessageView.AppendLine("unknown output: " + e.Line);
} }
} }
}; }
runner.ErrorLineReceived += delegate(object sender, LineReceivedEventArgs e) { errorTask.Wait();
if (!hasErrors) {
hasErrors = true;
GitMessageView.AppendLine(runner.WorkingDirectory + "> git " + command);
}
GitMessageView.AppendLine(e.Line);
};
runner.Start(git, command);
runner.WaitForExit();
} }
static GitStatus StatusFromText(string text) static GitStatus StatusFromText(string text)

137
src/AddIns/VersionControl/GitAddIn/Src/GitVersionProvider.cs

@ -7,6 +7,7 @@ using System.IO;
using System.IO.Pipes; using System.IO.Pipes;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
@ -16,65 +17,7 @@ namespace ICSharpCode.GitAddIn
{ {
public class GitVersionProvider : IDocumentVersionProvider public class GitVersionProvider : IDocumentVersionProvider
{ {
#region PInvoke public async Task<Stream> OpenBaseVersionAsync(FileName fileName)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref StartupInfo lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
const uint STARTF_USESTDHANDLES = 0x00000100;
const int STD_INPUT_HANDLE = -10;
const int STD_OUTPUT_HANDLE = -11;
const int STD_ERROR_HANDLE = -12;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct StartupInfo
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public SafePipeHandle hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
#endregion
public Stream OpenBaseVersion(string fileName)
{ {
if (!Git.IsInWorkingCopy(fileName)) if (!Git.IsInWorkingCopy(fileName))
return null; return null;
@ -83,67 +26,46 @@ namespace ICSharpCode.GitAddIn
if (git == null) if (git == null)
return null; return null;
return OpenOutput(git, fileName, GetBlobHash(git, fileName)); return OpenOutput(git, fileName, await GetBlobHashAsync(git, fileName).ConfigureAwait(false));
} }
internal static string GetBlobHash(string gitExe, string fileName) internal static async Task<string> GetBlobHashAsync(string gitExe, FileName fileName)
{ {
if (!File.Exists(fileName)) if (!File.Exists(fileName))
return null; return null;
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(fileName); runner.WorkingDirectory = fileName.GetParentDirectory();
runner.Start(gitExe, "ls-tree HEAD " + Path.GetFileName(fileName)); runner.RedirectStandardOutput = true;
runner.Start(gitExe, "ls-tree", "HEAD", fileName.GetFileName());
string blobHash = null; using (var reader = runner.OpenStandardOutputReader()) {
runner.OutputLineReceived += delegate(object sender, LineReceivedEventArgs e) { string firstLine = await reader.ReadLineAsync().ConfigureAwait(false);
string[] parts = e.Line.Split(new[] { " ", "\t" }, StringSplitOptions.RemoveEmptyEntries); if (firstLine != null) {
if (parts.Length >= 3) { string[] parts = firstLine.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (parts[2].Length == 40) if (parts.Length >= 3) {
blobHash = parts[2]; if (parts[2].Length == 40)
return parts[2];
}
} }
}; }
return null;
runner.WaitForExit();
return blobHash;
} }
Stream OpenOutput(string gitExe, string fileName, string blobHash) Stream OpenOutput(string gitExe, FileName fileName, string blobHash)
{ {
if (!File.Exists(fileName))
return null;
if (blobHash == null) if (blobHash == null)
return null; return null;
if (!File.Exists(fileName))
AnonymousPipeServerStream pipe = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
StartupInfo startupInfo = new GitVersionProvider.StartupInfo();
startupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.hStdOutput = pipe.ClientSafePipeHandle;
startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
startupInfo.cb = 16;
PROCESS_INFORMATION procInfo;
string commandLine = "\"" + gitExe + "\" cat-file blob " + blobHash;
string workingDir = Path.GetDirectoryName(fileName);
Debug.WriteLine(workingDir + "> " + commandLine);
const uint CREATE_NO_WINDOW = 0x08000000;
if (!CreateProcess(null, commandLine,
IntPtr.Zero, IntPtr.Zero, true, CREATE_NO_WINDOW, IntPtr.Zero, workingDir, ref startupInfo,
out procInfo)) {
pipe.DisposeLocalCopyOfClientHandle();
pipe.Close();
return null; return null;
}
pipe.DisposeLocalCopyOfClientHandle(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = fileName.GetParentDirectory();
return pipe; runner.RedirectStandardOutput = true;
runner.Start(gitExe, "cat-file", "blob", blobHash);
return runner.StandardOutput;
} }
public IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback) public IDisposable WatchBaseVersionChanges(FileName fileName, EventHandler callback)
{ {
if (!File.Exists(fileName)) if (!File.Exists(fileName))
return null; return null;
@ -154,17 +76,18 @@ namespace ICSharpCode.GitAddIn
if (git == null) if (git == null)
return null; return null;
return new BaseVersionChangeWatcher(fileName, GetBlobHash(git, fileName), callback); return new BaseVersionChangeWatcher(fileName, GetBlobHashAsync(git, fileName).Result, callback);
} }
} }
class BaseVersionChangeWatcher : IDisposable class BaseVersionChangeWatcher : IDisposable
{ {
EventHandler callback; EventHandler callback;
string fileName, hash; FileName fileName;
string hash;
RepoChangeWatcher watcher; RepoChangeWatcher watcher;
public BaseVersionChangeWatcher(string fileName, string hash, EventHandler callback) public BaseVersionChangeWatcher(FileName fileName, string hash, EventHandler callback)
{ {
string root = Git.FindWorkingCopyRoot(fileName); string root = Git.FindWorkingCopyRoot(fileName);
if (root == null) if (root == null)
@ -179,7 +102,7 @@ namespace ICSharpCode.GitAddIn
void HandleChanges() void HandleChanges()
{ {
string newHash = GitVersionProvider.GetBlobHash(Git.FindGit(), fileName); string newHash = GitVersionProvider.GetBlobHashAsync(Git.FindGit(), fileName).Result;
if (newHash != hash) { if (newHash != hash) {
LoggingService.Info(fileName + " was changed!"); LoggingService.Info(fileName + " was changed!");
callback(this, EventArgs.Empty); callback(this, EventArgs.Empty);

22
src/AddIns/VersionControl/GitAddIn/Src/RegisterEventsCommand.cs

@ -32,27 +32,25 @@ namespace ICSharpCode.GitAddIn
AbstractProjectBrowserTreeNode.OnNewNode += TreeNodeCreated; AbstractProjectBrowserTreeNode.OnNewNode += TreeNodeCreated;
} }
void AddFile(string fileName) async void AddFile(string fileName)
{ {
Git.Add(fileName, await Git.AddAsync(fileName);
exitcode => SD.MainThread.InvokeAsyncAndForget(() => ClearStatusCacheAndEnqueueFile(fileName)) ClearStatusCacheAndEnqueueFile(fileName);
);
} }
void RemoveFile(string fileName) async void RemoveFile(string fileName)
{ {
if (GitStatusCache.GetFileStatus(fileName) == GitStatus.Added) { if (GitStatusCache.GetFileStatus(fileName) == GitStatus.Added) {
Git.Remove(fileName, true, await Git.RemoveAsync(fileName, true);
exitcode => SD.MainThread.InvokeAsyncAndForget(() => ClearStatusCacheAndEnqueueFile(fileName))); ClearStatusCacheAndEnqueueFile(fileName);
} }
} }
void RenameFile(string sourceFileName, string targetFileName) async void RenameFile(string sourceFileName, string targetFileName)
{ {
Git.Add(targetFileName, await Git.AddAsync(targetFileName);
exitcode => SD.MainThread.InvokeAsyncAndForget(() => RemoveFile(sourceFileName)) ClearStatusCacheAndEnqueueFile(targetFileName);
); RemoveFile(sourceFileName);
SD.MainThread.InvokeAsyncAndForget(() => ClearStatusCacheAndEnqueueFile(targetFileName));
} }
void TreeNodeCreated(object sender, TreeViewEventArgs e) void TreeNodeCreated(object sender, TreeViewEventArgs e)

18
src/AddIns/VersionControl/SubversionAddIn/Src/SvnVersionProvider.cs

@ -3,6 +3,8 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Editor; using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.Svn.Commands; using ICSharpCode.Svn.Commands;
using SharpSvn; using SharpSvn;
@ -11,16 +13,18 @@ namespace ICSharpCode.Svn
{ {
public class SvnVersionProvider : IDocumentVersionProvider public class SvnVersionProvider : IDocumentVersionProvider
{ {
public Stream OpenBaseVersion(string fileName) public Task<Stream> OpenBaseVersionAsync(FileName fileName)
{ {
if (!SvnClientWrapper.IsInSourceControl(fileName)) return Task.Run(() => {
return null; if (!SvnClientWrapper.IsInSourceControl(fileName))
return null;
using (SvnClientWrapper client = new SvnClientWrapper())
return client.OpenBaseVersion(fileName); using (SvnClientWrapper client = new SvnClientWrapper())
return client.OpenBaseVersion(fileName);
});
} }
public IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback) public IDisposable WatchBaseVersionChanges(FileName fileName, EventHandler callback)
{ {
return null; return null;
} }

5
src/Main/Base/Project/Editor/IDocumentVersionProvider.cs

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
@ -21,7 +22,7 @@ namespace ICSharpCode.SharpDevelop.Editor
/// Provides the BASE-Version for a file. This can be either the file saved /// Provides the BASE-Version for a file. This can be either the file saved
/// to disk or a base version provided by any VCS. /// to disk or a base version provided by any VCS.
/// </summary> /// </summary>
Stream OpenBaseVersion(string fileName); Task<Stream> OpenBaseVersionAsync(FileName fileName);
/// <summary> /// <summary>
/// Starts watching for changes to the BASE-version of the specified file. /// Starts watching for changes to the BASE-version of the specified file.
@ -30,7 +31,7 @@ namespace ICSharpCode.SharpDevelop.Editor
/// <returns>Returns a disposable that can be used to stop watching for changes. /// <returns>Returns a disposable that can be used to stop watching for changes.
/// You must dispose the disposable to prevent a memory leak, the GC will /// You must dispose the disposable to prevent a memory leak, the GC will
/// not help out in this case!</returns> /// not help out in this case!</returns>
IDisposable WatchBaseVersionChanges(string fileName, EventHandler callback); IDisposable WatchBaseVersionChanges(FileName fileName, EventHandler callback);
} }
public class VersioningServices public class VersioningServices

1
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -234,6 +234,7 @@
<DependentUpon>OutputWindowOptionsPanel.xaml</DependentUpon> <DependentUpon>OutputWindowOptionsPanel.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="Src\Gui\Pads\CompilerMessageView\MessageViewCategoryTextWriter.cs" />
<Compile Include="Src\Internal\Templates\StringParserPropertyContainer.cs" /> <Compile Include="Src\Internal\Templates\StringParserPropertyContainer.cs" />
<Compile Include="Src\Internal\Templates\TemplateCategoryComparer.cs" /> <Compile Include="Src\Internal\Templates\TemplateCategoryComparer.cs" />
<Compile Include="Src\Project\MSBuildConfigurationOrPlatformNameCollection.cs" /> <Compile Include="Src\Project\MSBuildConfigurationOrPlatformNameCollection.cs" />

46
src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs

@ -5,7 +5,9 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@ -229,49 +231,23 @@ namespace ICSharpCode.SharpDevelop.Commands
return; return;
} }
if (string.IsNullOrEmpty(args) || args.Trim('"', ' ').Length == 0) {
args = "";
}
try { try {
ProcessRunner processRunner = new ProcessRunner();
processRunner.WorkingDirectory = DirectoryName.Create(StringParser.Parse(tool.InitialDirectory));
if (tool.UseOutputPad) { if (tool.UseOutputPad) {
ProcessRunner processRunner = new ProcessRunner(); processRunner.RunInOutputPadAsync(TaskService.BuildMessageViewCategory, command, ProcessRunner.CommandLineToArgumentArray(args)).FireAndForget();
processRunner.LogStandardOutputAndError = false;
processRunner.ProcessExited += ProcessExitEvent;
processRunner.OutputLineReceived += process_OutputLineReceived;
processRunner.ErrorLineReceived += process_OutputLineReceived;
processRunner.WorkingDirectory = StringParser.Parse(tool.InitialDirectory);
if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) {
processRunner.Start(command);
} else {
processRunner.Start(command, args);
}
} else { } else {
ProcessStartInfo startinfo; processRunner.CreationFlags = ProcessCreationFlags.CreateNewConsole;
if (args == null || args.Length == 0 || args.Trim('"', ' ').Length == 0) { processRunner.Start(command, ProcessRunner.CommandLineToArgumentArray(args));
startinfo = new ProcessStartInfo(command);
} else {
startinfo = new ProcessStartInfo(command, args);
}
startinfo.WorkingDirectory = StringParser.Parse(tool.InitialDirectory);
Process process = new Process();
process.StartInfo = startinfo;
process.Start();
} }
} catch (Exception ex) { } catch (Exception ex) {
MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + command + " " + args + "'\n" + ex.Message); MessageService.ShowError("${res:XML.MainMenu.ToolMenu.ExternalTools.ExecutionFailed} '" + command + " " + args + "'\n" + ex.Message);
} }
} }
void ProcessExitEvent(object sender, EventArgs e)
{
SD.MainThread.InvokeAsyncAndForget(delegate {
ProcessRunner p = (ProcessRunner)sender;
TaskService.BuildMessageViewCategory.AppendLine(StringParser.Parse("${res:XML.MainMenu.ToolMenu.ExternalTools.ExitedWithCode} " + p.ExitCode));
p.Dispose();
});
}
void process_OutputLineReceived(object sender, LineReceivedEventArgs e)
{
TaskService.BuildMessageViewCategory.AppendLine(e.Line);
}
} }
public class OpenContentsMenuBuilder : IMenuItemBuilder public class OpenContentsMenuBuilder : IMenuItemBuilder

38
src/Main/Base/Project/Src/Gui/Dialogs/ReferenceDialog/ServiceReference/SvcUtilRunner.cs

@ -18,15 +18,18 @@ namespace ICSharpCode.SharpDevelop.Gui.Dialogs.ReferenceDialog.ServiceReference
public int ExitCode { get; private set; } public int ExitCode { get; private set; }
public void Run() public async void Run()
{ {
SvcUtilMessageView.ClearText(); SvcUtilMessageView.ClearText();
var commandLine = new SvcUtilCommandLine(Options); var commandLine = new SvcUtilCommandLine(Options);
commandLine.Command = GetSvcUtilPath(); commandLine.Command = GetSvcUtilPath();
SvcUtilMessageView.AppendLine(commandLine.ToString()); using (ProcessRunner processRunner = new ProcessRunner()) {
ProcessRunner runner = CreateProcessRunner(); this.ExitCode = await processRunner.RunInOutputPadAsync(SvcUtilMessageView.Category, commandLine.Command, ProcessRunner.CommandLineToArgumentArray(commandLine.Arguments));
runner.Start(commandLine.Command, commandLine.Arguments); }
if (ProcessExited != null) {
ProcessExited(this, new EventArgs());
}
} }
string GetSvcUtilPath() string GetSvcUtilPath()
@ -45,36 +48,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Dialogs.ReferenceDialog.ServiceReference
SvcUtilMessageView.AppendLine(message); SvcUtilMessageView.AppendLine(message);
} }
ProcessRunner CreateProcessRunner()
{
var runner = new ProcessRunner();
runner.LogStandardOutputAndError = false;
runner.OutputLineReceived += LineReceived;
runner.ErrorLineReceived += LineReceived;
runner.ProcessExited += SvcUtilProcessExited;
return runner;
}
void LineReceived(object sender, LineReceivedEventArgs e) void LineReceived(object sender, LineReceivedEventArgs e)
{ {
SvcUtilMessageView.AppendLine(e.Line); SvcUtilMessageView.AppendLine(e.Line);
} }
void SvcUtilProcessExited(object sender, EventArgs e)
{
SvcUtilMessageView.AppendLine("SvcUtil finished.");
var runner = (ProcessRunner)sender;
ExitCode = runner.ExitCode;
SD.MainThread.InvokeAsyncAndForget(() => OnProcessExited());
}
void OnProcessExited()
{
if (ProcessExited != null) {
ProcessExited(this, new EventArgs());
}
}
} }
} }

42
src/Main/Base/Project/Src/Gui/Pads/CompilerMessageView/MessageViewCategoryTextWriter.cs

@ -0,0 +1,42 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.Text;
namespace ICSharpCode.SharpDevelop.Gui
{
/// <summary>
/// TextWriter implementation that writes into a MessageViewCategory.
/// </summary>
public class MessageViewCategoryTextWriter : TextWriter
{
readonly MessageViewCategory target;
public MessageViewCategoryTextWriter(MessageViewCategory target)
{
this.target = target;
}
public override Encoding Encoding {
get { return Encoding.Unicode; }
}
public override void Write(char value)
{
target.AppendText(value.ToString());
}
public override void Write(string value)
{
if (value != null)
target.AppendText(value);
}
public override void Write(char[] buffer, int index, int count)
{
target.AppendText(new string(buffer, index, count));
}
}
}

3
src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/Commands/DefaultFileNodeCommands.cs

@ -180,7 +180,8 @@ namespace ICSharpCode.SharpDevelop.Project.Commands
public static void OpenFolderInExplorer(string directory) public static void OpenFolderInExplorer(string directory)
{ {
if (directory != null && Directory.Exists(directory)) { if (directory != null && Directory.Exists(directory)) {
Process.Start(directory); // don't just Process.Start the directory; that causes problems when an .exe with the same name as the directory exists
Process.Start("explorer.exe", "\"" + directory + "\"");
} }
} }
} }

15
src/Main/Base/Project/Util/NativeMethods.cs

@ -7,6 +7,7 @@ using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
namespace ICSharpCode.SharpDevelop namespace ICSharpCode.SharpDevelop
{ {
@ -27,6 +28,18 @@ namespace ICSharpCode.SharpDevelop
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr SetForegroundWindow(IntPtr hWnd); public static extern IntPtr SetForegroundWindow(IntPtr hWnd);
[DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", BestFitMapping = false, CharSet = CharSet.Ansi)]
internal static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeWaitHandle targetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions);
internal const int DUPLICATE_SAME_ACCESS = 2;
#region SHFileOperation #region SHFileOperation
enum FO_FUNC : uint enum FO_FUNC : uint
{ {
@ -94,6 +107,7 @@ namespace ICSharpCode.SharpDevelop
} }
#endregion #endregion
#region Get OEM Encoding
[DllImport("kernel32.dll")] [DllImport("kernel32.dll")]
static extern int GetOEMCP(); static extern int GetOEMCP();
@ -108,5 +122,6 @@ namespace ICSharpCode.SharpDevelop
} }
} }
} }
#endregion
} }
} }

686
src/Main/Base/Project/Util/ProcessRunner.cs

@ -2,20 +2,69 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Core; using ICSharpCode.Core;
using Microsoft.Win32.SafeHandles;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Util;
namespace ICSharpCode.SharpDevelop namespace ICSharpCode.SharpDevelop
{ {
[Flags]
public enum ProcessCreationFlags
{
None = 0,
/// <summary>
/// Creates a new console instead of inheriting the parent console.
/// </summary>
CreateNewConsole = 0x00000010,
/// <summary>
/// Launches a console application without a console window.
/// </summary>
CreateNoWindow = 0x08000000
}
public interface IProcessRunner : IDisposable
{
Task<int> RunInOutputPadAsync(MessageViewCategory outputCategory, string program, params string[] arguments);
string WorkingDirectory { get; set; }
ProcessCreationFlags CreationFlags { get; set; }
IDictionary<string, string> EnvironmentVariables { get; }
bool RedirectStandardOutput { get; set; }
bool RedirectStandardError { get; set; }
bool RedirectStandardOutputAndErrorToSingleStream { get; set; }
void Start(string program, params string[] arguments);
void StartCommandLine(string commandLine);
void Kill();
Task WaitForExitAsync();
Stream StandardOutput { get; }
Stream StandardError { get; }
StreamReader OpenStandardOutputReader();
StreamReader OpenStandardErrorReader();
}
/// <summary> /// <summary>
/// Runs a process that sends output to standard output and to /// Class for starting external processes.
/// standard error. /// Very similar to System.Diagnostics.Process, but supports binary stdout/stderr (not only text),
/// and allows using the same pipe for both stdout and stderr.
///
/// Also, implements an interface to support mocking in unit tests.
/// </summary> /// </summary>
public class ProcessRunner : IDisposable public class ProcessRunner : IProcessRunner, IDisposable
{ {
public static Encoding OemEncoding { public static Encoding OemEncoding {
get { get {
@ -23,267 +72,532 @@ namespace ICSharpCode.SharpDevelop
} }
} }
Process process; #region SafeProcessHandle
StringBuilder standardOutput = new StringBuilder(); [SecurityCritical]
StringBuilder standardError = new StringBuilder(); sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
ManualResetEventSlim endOfOutput = new ManualResetEventSlim(false); {
int outputStreamsFinished; // this private ctor is required for SafeHandle implementations
SafeProcessHandle() : base(true)
{
}
internal SafeProcessHandle(IntPtr handle) : base(true)
{
base.SetHandle(handle);
}
[SecurityCritical]
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(handle);
}
}
#endregion
/// <summary> #region Native structures
/// Triggered when the process has exited. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
/// </summary> protected struct STARTUPINFO
public event EventHandler ProcessExited; {
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public SafePipeHandle hStdInput;
public SafePipeHandle hStdOutput;
public SafePipeHandle hStdError;
}
/// <summary> [StructLayout(LayoutKind.Sequential)]
/// Triggered when a line of text is read from the standard output. protected struct PROCESS_INFORMATION
/// </summary> {
public event EventHandler<LineReceivedEventArgs> OutputLineReceived; public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
#endregion
/// <summary> #region Native methods
/// Triggered when a line of text is read from the standard error. [DllImport("kernel32.dll", EntryPoint = "CreateProcess", CharSet = CharSet.Unicode, SetLastError = true)]
/// </summary> [return: MarshalAs(UnmanagedType.Bool)]
public event EventHandler<LineReceivedEventArgs> ErrorLineReceived; static extern bool NativeCreateProcess(
string lpApplicationName,
StringBuilder lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
[MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
uint dwCreationFlags,
string lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool TerminateProcess(SafeProcessHandle processHandle, int exitCode);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetExitCodeProcess(SafeProcessHandle processHandle, out int exitCode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
[DllImport("kernel32.dll")]
static extern IntPtr LocalFree(IntPtr hMem);
#endregion
#region CommandLine <-> Argument Array
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="ProcessRunner"/>. /// Decodes a command line into an array of arguments according to the CommandLineToArgvW rules.
/// </summary> /// </summary>
public ProcessRunner() /// <remarks>
/// Command line parsing rules:
/// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
/// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static unsafe string[] CommandLineToArgumentArray(string commandLine)
{ {
this.LogStandardOutputAndError = true; if (string.IsNullOrEmpty(commandLine))
return new string[0];
int numberOfArgs;
char** arr = CommandLineToArgvW(commandLine, out numberOfArgs);
if (arr == null)
throw new Win32Exception();
try {
string[] result = new string[numberOfArgs];
for (int i = 0; i < numberOfArgs; i++) {
result[i] = new string(arr[i]);
}
return result;
} finally {
// Free memory obtained by CommandLineToArgW.
LocalFree(new IntPtr(arr));
}
} }
/// <summary> static readonly char[] charsNeedingQuoting = { ' ', '\t', '\n', '\v', '"' };
/// Gets or sets the process's working directory.
/// </summary>
public string WorkingDirectory { get; set; }
/// <summary>
/// Gets or sets whether standard output is logged to the "StandardOutput" and "StandardError"
/// properties. When this property is false, output is still redirected to the
/// OutputLineReceived and ErrorLineReceived events, but the ProcessRunner uses less memory.
/// The default value is true.
/// </summary>
public bool LogStandardOutputAndError { get; set; }
/// <summary> /// <summary>
/// Gets the standard output returned from the process. /// Escapes a set of arguments according to the CommandLineToArgvW rules.
/// </summary> /// </summary>
public string StandardOutput { /// <remarks>
get { /// Command line parsing rules:
lock (standardOutput) /// - 2n backslashes followed by a quotation mark produce n backslashes, and the quotation mark is considered to be the end of the argument.
return standardOutput.ToString(); /// - (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.
/// - n backslashes not followed by a quotation mark simply produce n backslashes.
/// </remarks>
public static string ArgumentArrayToCommandLine(params string[] arguments)
{
if (arguments == null)
return null;
StringBuilder b = new StringBuilder();
for (int i = 0; i < arguments.Length; i++) {
if (i > 0)
b.Append(' ');
AppendArgument(b, arguments[i]);
} }
return b.ToString();
} }
/// <summary> static void AppendArgument(StringBuilder b, string arg)
/// Gets the standard error output returned from the process. {
/// </summary> if (arg.Length > 0 && arg.IndexOfAny(charsNeedingQuoting) < 0) {
public string StandardError { b.Append(arg);
get { } else {
lock (standardError) b.Append('"');
return standardError.ToString(); for (int j = 0; ; j++) {
int backslashCount = 0;
while (j < arg.Length && arg[j] == '\\') {
backslashCount++;
j++;
}
if (j == arg.Length) {
b.Append('\\', backslashCount * 2);
break;
} else if (arg[j] == '"') {
b.Append('\\', backslashCount * 2 + 1);
b.Append('"');
} else {
b.Append('\\', backslashCount);
b.Append(arg[j]);
}
}
b.Append('"');
} }
} }
#endregion
/// <summary> #region RunInOutputPad
/// Releases resources held by the <see cref="ProcessRunner"/> public async Task<int> RunInOutputPadAsync(MessageViewCategory outputCategory, string program, params string[] arguments)
/// </summary>
public void Dispose()
{ {
process.Dispose(); RedirectStandardOutputAndErrorToSingleStream = true;
endOfOutput.Dispose(); Start(program, arguments);
StringBuilder printedCommandLine = new StringBuilder();
if (WorkingDirectory != null) {
printedCommandLine.Append(WorkingDirectory);
printedCommandLine.Append("> ");
}
printedCommandLine.Append(CommandLine);
outputCategory.AppendLine(printedCommandLine.ToString());
using (TextReader reader = OpenStandardOutputReader()) {
await reader.CopyToAsync(new MessageViewCategoryTextWriter(outputCategory));
}
await WaitForExitAsync();
outputCategory.AppendLine(StringParser.Parse("${res:XML.MainMenu.ToolMenu.ExternalTools.ExitedWithCode} " + this.ExitCode));
return this.ExitCode;
} }
#endregion
#region Start Info Properties
/// <summary> /// <summary>
/// Gets the process exit code. /// Gets or sets the process's working directory.
/// </summary> /// </summary>
public int ExitCode { public string WorkingDirectory { get; set; }
ProcessCreationFlags creationFlags = ProcessCreationFlags.CreateNoWindow;
public ProcessCreationFlags CreationFlags {
get { return creationFlags; }
set { creationFlags = value; }
}
IDictionary<string, string> environmentVariables;
public IDictionary<string, string> EnvironmentVariables {
get { get {
int exitCode = 0; if (environmentVariables == null) {
if (process != null) { environmentVariables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
exitCode = process.ExitCode; foreach (DictionaryEntry e in Environment.GetEnvironmentVariables()) {
environmentVariables.Add((string)e.Key, (string)e.Value);
}
} }
return exitCode; return environmentVariables;
} }
} }
public string CommandLine { get; private set; }
public bool RedirectStandardOutput { get; set; }
public bool RedirectStandardError { get; set; }
/// <summary> /// <summary>
/// Waits for the process to exit. /// Gets whether to use a single stream for both stdout and stderr.
/// </summary> /// </summary>
public void WaitForExit() public bool RedirectStandardOutputAndErrorToSingleStream { get; set; }
#endregion
#region Start
bool wasStarted;
SafeProcessHandle safeProcessHandle;
public void Start(string program, params string[] arguments)
{ {
WaitForExit(Int32.MaxValue); StringBuilder commandLine = new StringBuilder();
AppendArgument(commandLine, program);
if (arguments != null) {
for (int i = 0; i < arguments.Length; i++) {
commandLine.Append(' ');
AppendArgument(commandLine, arguments[i]);
}
}
StartCommandLine(commandLine.ToString());
} }
/// <summary> public void StartCommandLine(string commandLine)
/// Waits for the process to exit.
/// </summary>
/// <param name="timeout">A timeout in milliseconds.</param>
/// <returns><see langword="true"/> if the associated process has
/// exited; otherwise, <see langword="false"/></returns>
public bool WaitForExit(int timeout)
{ {
if (process == null) { lock (lockObj) {
throw new InvalidOperationException("no process is running"); if (wasStarted)
throw new InvalidOperationException();
DoStart(commandLine);
}
}
protected virtual void DoStart(string commandLine)
{
this.CommandLine = commandLine;
const uint STARTF_USESTDHANDLES = 0x00000100;
const int STD_INPUT_HANDLE = -10;
const int STD_OUTPUT_HANDLE = -11;
const int STD_ERROR_HANDLE = -12;
const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
STARTUPINFO startupInfo = new STARTUPINFO();
startupInfo.cb = (uint)Marshal.SizeOf(typeof(STARTUPINFO));
startupInfo.dwFlags = STARTF_USESTDHANDLES;
// Create pipes
startupInfo.hStdInput = new SafePipeHandle(GetStdHandle(STD_INPUT_HANDLE), ownsHandle: false);
if (RedirectStandardOutput || RedirectStandardOutputAndErrorToSingleStream) {
standardOutput = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
startupInfo.hStdOutput = standardOutput.ClientSafePipeHandle;
} else {
startupInfo.hStdOutput = new SafePipeHandle(GetStdHandle(STD_OUTPUT_HANDLE), ownsHandle: false);
}
if (RedirectStandardOutputAndErrorToSingleStream) {
standardError = standardOutput;
startupInfo.hStdError = standardError.ClientSafePipeHandle;
} else if (RedirectStandardError) {
standardError = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable);
startupInfo.hStdError = standardError.ClientSafePipeHandle;
} else {
startupInfo.hStdError = new SafePipeHandle(GetStdHandle(STD_ERROR_HANDLE), ownsHandle: false);
} }
bool exited = process.WaitForExit(timeout); uint flags = (uint)this.CreationFlags;
if (exited) { string environmentBlock = null;
endOfOutput.Wait(timeout == int.MaxValue ? Timeout.Infinite : timeout); if (environmentVariables != null) {
environmentBlock = BuildEnvironmentBlock(environmentVariables);
flags |= CREATE_UNICODE_ENVIRONMENT;
} }
return exited; PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
try {
CreateProcess(null, new StringBuilder(commandLine), IntPtr.Zero, IntPtr.Zero, true, flags, environmentBlock, WorkingDirectory, ref startupInfo, out processInfo);
wasStarted = true;
} finally {
if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != new IntPtr(-1)) {
safeProcessHandle = new SafeProcessHandle(processInfo.hProcess);
}
if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != new IntPtr(-1)) {
NativeMethods.CloseHandle(processInfo.hThread);
}
// Dispose the client side handles of the pipe.
// They got copied into the new process, we don't need our local copies anymore.
startupInfo.hStdInput.Dispose();
startupInfo.hStdOutput.Dispose();
startupInfo.hStdError.Dispose();
if (!wasStarted) {
// In case of error, dispose the server side of the pipes as well
if (standardOutput != null) {
standardOutput.Dispose();
standardOutput = null;
}
if (standardError != null) {
standardError.Dispose();
standardError = null;
}
}
}
//StartStreamCopyAfterProcessCreation();
} }
public bool IsRunning { static string BuildEnvironmentBlock(IEnumerable<KeyValuePair<string, string>> environment)
get { {
bool isRunning = false; StringBuilder b = new StringBuilder();
foreach (var pair in environment.OrderBy(p => p.Key, StringComparer.OrdinalIgnoreCase)) {
if (process != null) { b.Append(pair.Key);
isRunning = !process.HasExited; b.Append('=');
} b.Append(pair.Value);
b.Append('\0');
return isRunning; }
b.Append('\0');
return b.ToString();
}
protected virtual void CreateProcess(
string lpApplicationName,
StringBuilder lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
string lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation)
{
if (!NativeCreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags,
lpEnvironment, lpCurrentDirectory, ref lpStartupInfo, out lpProcessInformation)) {
throw new Win32Exception();
} }
} }
#endregion
Dictionary<string, string> environmentVariables = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); public void Dispose()
{
if (safeProcessHandle != null)
safeProcessHandle.Dispose();
if (standardOutput != null)
standardOutput.Dispose();
if (standardError != null)
standardError.Dispose();
}
public Dictionary<string, string> EnvironmentVariables { #region HasExited / ExitCode / Kill
get { return environmentVariables; } public bool HasExited {
get { return WaitForExit(0); }
} }
/// <summary> /// <summary>
/// Starts the process. /// Gets the process exit code.
/// </summary> /// </summary>
/// <param name="command">The process filename.</param> public int ExitCode {
/// <param name="arguments">The command line arguments to get {
/// pass to the command.</param> if (!WaitForExit(0))
public void Start(string command, string arguments) throw new InvalidOperationException("Process has not yet exited");
{ return exitCode; // WaitForExit has the side effect of setting exitCode
Encoding encoding = OemEncoding;
process = new Process();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = command;
process.StartInfo.WorkingDirectory = WorkingDirectory;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.StandardOutputEncoding = encoding;
process.OutputDataReceived += OnOutputLineReceived;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.StandardErrorEncoding = encoding;
process.ErrorDataReceived += OnErrorLineReceived;
process.StartInfo.UseShellExecute = false;
process.StartInfo.Arguments = arguments;
foreach (var pair in environmentVariables)
process.StartInfo.EnvironmentVariables.Add(pair.Key, pair.Value);
if (ProcessExited != null) {
process.EnableRaisingEvents = true;
process.Exited += OnProcessExited;
}
bool started = false;
try {
process.Start();
started = true;
} finally {
if (!started) {
process.Exited -= OnProcessExited;
process = null;
}
} }
process.BeginOutputReadLine();
process.BeginErrorReadLine();
} }
/// <summary> /// <summary>
/// Starts the process. /// Sends the kill signal to the process.
/// Does not wait for the process to complete to exit after being killed.
/// </summary> /// </summary>
/// <param name="command">The process filename.</param> public void Kill()
public void Start(string command)
{ {
Start(command, String.Empty); if (!wasStarted)
throw new InvalidOperationException("Process was not started");
if (!TerminateProcess(safeProcessHandle, -1)) {
int err = Marshal.GetLastWin32Error();
// If TerminateProcess fails, maybe it's because the process has already exited.
if (!WaitForExit(0))
throw new Win32Exception(err);
}
} }
#endregion
/// <summary> #region WaitForExit
/// Kills the running process. sealed class ProcessWaitHandle : WaitHandle
/// </summary>
public void Kill()
{ {
if (process != null) { public ProcessWaitHandle(SafeProcessHandle processHandle)
if (!process.HasExited) { {
try { var currentProcess = new HandleRef(this, NativeMethods.GetCurrentProcess());
process.Kill(); SafeWaitHandle safeWaitHandle;
} catch (InvalidOperationException) { if (!NativeMethods.DuplicateHandle(currentProcess, processHandle, currentProcess, out safeWaitHandle, 0, false, 2)) {
// race condition (if the process has already exited) throw new Win32Exception();
}
// don't call process.Dispose() here - that causes the OnProcessExited
// event not to fire correctly
} }
endOfOutput.Wait(); base.SafeWaitHandle = safeWaitHandle;
} }
} }
/// <summary> bool hasExited;
/// Raises the <see cref="ProcessExited"/> event. int exitCode;
/// </summary>
protected void OnProcessExited(object sender, EventArgs e) public void WaitForExit()
{ {
if (ProcessExited != null) { WaitForExit(Timeout.Infinite);
if (endOfOutput != null) { }
endOfOutput.Wait();
public bool WaitForExit(int millisecondsTimeout)
{
if (hasExited)
return true;
if (!wasStarted)
throw new InvalidOperationException("Process was not yet started");
if (safeProcessHandle.IsClosed)
throw new ObjectDisposedException("ProcessRunner");
using (var waitHandle = new ProcessWaitHandle(safeProcessHandle)) {
if (waitHandle.WaitOne(millisecondsTimeout, false)) {
if (!GetExitCodeProcess(safeProcessHandle, out exitCode))
throw new Win32Exception();
// Wait until the output is processed
// if (standardOutputTask != null)
// standardOutputTask.Wait();
// if (standardErrorTask != null)
// standardErrorTask.Wait();
hasExited = true;
} }
ProcessExited(this, e);
} }
return hasExited;
} }
readonly object lockObj = new object();
TaskCompletionSource<object> waitForExitTCS;
ProcessWaitHandle waitForExitAsyncWaitHandle;
RegisteredWaitHandle waitForExitAsyncRegisteredWaitHandle;
/// <summary> /// <summary>
/// Raises the <see cref="OutputLineReceived"/> event. /// Asynchronously waits for the process to exit.
/// </summary> /// </summary>
/// <param name="sender">The event source.</param> public Task WaitForExitAsync()
/// <param name="e">The line received event arguments.</param>
protected void OnOutputLineReceived(object sender, DataReceivedEventArgs e)
{ {
if (e.Data == null) { if (hasExited)
if (Interlocked.Increment(ref outputStreamsFinished) == 2) return Task.FromResult(true);
endOfOutput.Set(); if (!wasStarted)
return; throw new InvalidOperationException("Process was not yet started");
} if (safeProcessHandle.IsClosed)
if (LogStandardOutputAndError) { throw new ObjectDisposedException("ProcessRunner");
lock (standardOutput) { lock (lockObj) {
standardOutput.AppendLine(e.Data); if (waitForExitTCS == null) {
waitForExitTCS = new TaskCompletionSource<object>();
waitForExitAsyncWaitHandle = new ProcessWaitHandle(safeProcessHandle);
waitForExitAsyncRegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(waitForExitAsyncWaitHandle, WaitForExitAsyncCallback, null, -1, true);
} }
return waitForExitTCS.Task;
}
}
void WaitForExitAsyncCallback(object context, bool wasSignaled)
{
waitForExitAsyncRegisteredWaitHandle.Unregister(null);
waitForExitAsyncRegisteredWaitHandle = null;
waitForExitAsyncWaitHandle.Close();
waitForExitAsyncWaitHandle = null;
// Wait until the output is processed
// if (standardOutputTask != null)
// await standardOutputTask;
// if (standardErrorTask != null)
// await standardErrorTask;
waitForExitTCS.SetResult(null);
}
#endregion
#region StandardOutput/StandardError
AnonymousPipeServerStream standardOutput;
AnonymousPipeServerStream standardError;
public Stream StandardOutput {
get {
if (standardOutput == null)
throw new InvalidOperationException(wasStarted ? "stdout was not redirected" : "Process not yet started");
return standardOutput;
} }
if (OutputLineReceived != null) { }
OutputLineReceived(this, new LineReceivedEventArgs(e.Data));
public Stream StandardError {
get {
if (standardError == null)
throw new InvalidOperationException(wasStarted ? "stderr was not redirected" : "Process not yet started");
return standardError;
} }
} }
/// <summary> /// <summary>
/// Raises the <see cref="ErrorLineReceived"/> event. /// Opens a text reader around the standard output.
/// </summary> /// </summary>
/// <param name="sender">The event source.</param> public StreamReader OpenStandardOutputReader()
/// <param name="e">The line received event arguments.</param>
protected void OnErrorLineReceived(object sender, DataReceivedEventArgs e)
{ {
if (e.Data == null) { return new StreamReader(this.StandardOutput, OemEncoding);
if (Interlocked.Increment(ref outputStreamsFinished) == 2) }
endOfOutput.Set();
return; /// <summary>
} /// Opens a text reader around the standard error.
if (LogStandardOutputAndError) { /// </summary>
lock (standardError) { public StreamReader OpenStandardErrorReader()
standardError.AppendLine(e.Data); {
} return new StreamReader(this.StandardError, OemEncoding);
}
if (ErrorLineReceived != null) {
ErrorLineReceived(this, new LineReceivedEventArgs(e.Data));
}
} }
#endregion
} }
} }

22
src/Main/Base/Project/Util/ReactiveExtensions.cs

@ -258,6 +258,28 @@ namespace ICSharpCode.SharpDevelop
} }
#endregion #endregion
#region ForEach
/// <summary>
/// Subscribes to the observable and runs an action for each element.
/// </summary>
public static async Task ForEachAsync<T>(this IObservable<T> source, Action<T> action)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
using (source.Subscribe(delegate (T item) {
try {
action(item);
} catch (Exception ex) {
tcs.TrySetException(ex);
}
},
exception => tcs.TrySetException(exception),
() => tcs.TrySetResult(null)))
{
await tcs.Task.ConfigureAwait(false);
}
}
#endregion
#region AnonymousObserver #region AnonymousObserver
public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext, Action<Exception> onError, Action onCompleted) public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext, Action<Exception> onError, Action onCompleted)
{ {

9
src/Main/Base/Project/Util/SharpDevelopExtensions.cs

@ -785,6 +785,15 @@ namespace ICSharpCode.SharpDevelop
return h; return h;
} }
} }
public static async Task CopyToAsync(this TextReader reader, TextWriter writer)
{
char[] buffer = new char[2048];
int read;
while ((read = await reader.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) {
writer.Write(buffer, 0, read);
}
}
#endregion #endregion
#region Service Provider Extensions #region Service Provider Extensions

60
src/Main/Base/Test/AbstractEntityIsOverridableTestFixture.cs

@ -1,60 +0,0 @@
#warning
//// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
//// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
//
//using System;
//using ICSharpCode.SharpDevelop.Dom;
//using ICSharpCode.SharpDevelop.Tests.Utils;
//using NUnit.Framework;
//
//namespace ICSharpCode.SharpDevelop.Tests
//{
// /// <summary>
// /// Tests that the IsOverridable property returns the expected value.
// /// </summary>
// [TestFixture]
// public class AbstractEntityIsOverridableTestFixture
// {
// MockEntity entity;
//
// [SetUp]
// public void SetUp()
// {
// entity = new MockEntity();
// }
//
// [Test]
// public void NotOverridableByDefault()
// {
// Assert.IsFalse(entity.IsOverridable);
// }
//
// [Test]
// public void IsOverrideSet()
// {
// entity.Modifiers = ModifierEnum.Override;
// Assert.IsTrue(entity.IsOverridable);
// }
//
// [Test]
// public void IsVirtualSet()
// {
// entity.Modifiers = ModifierEnum.Virtual;
// Assert.IsTrue(entity.IsOverridable);
// }
//
// [Test]
// public void IsAbstractSet()
// {
// entity.Modifiers = ModifierEnum.Abstract;
// Assert.IsTrue(entity.IsOverridable);
// }
//
// [Test]
// public void IsAbstractAndSealedSet()
// {
// entity.Modifiers = ModifierEnum.Abstract | ModifierEnum.Sealed;
// Assert.IsFalse(entity.IsOverridable);
// }
// }
//}

46
src/Main/Base/Test/CheckAssemblyFlags.cs

@ -1,46 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.Core;
using NUnit.Framework;
using System.Text.RegularExpressions;
namespace ICSharpCode.SharpDevelop.Tests
{
[TestFixture]
public class CheckAssemblyFlags
{
string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..");
bool Get32BitFlags(string assembly)
{
assembly = Path.Combine(binPath, assembly);
string corflags = FileUtility.GetSdkPath("corflags.exe");
Assert.IsNotNull(corflags, "corflags.exe not found, this test requires the .NET SDK!");
ProcessRunner pr = new ProcessRunner();
Console.WriteLine(corflags + " \"" + assembly + "\"");
pr.Start(corflags, "\"" + assembly + "\"");
if (!pr.WaitForExit(5000)) {
pr.Kill();
throw new InvalidOperationException("Timeout running corflags");
} else {
Console.WriteLine(pr.StandardOutput);
Match m = Regex.Match(pr.StandardOutput, @"32BIT(?:REQ)?\s*:\s*([01])");
if (m.Success) {
return m.Groups[1].Value == "1";
} else {
throw new InvalidOperationException("Invalid corflags output");
}
}
}
// All these processes must use the same value for 32-bit-flag:
[Test]
public void CheckSharpDevelop32Bit()
{
Assert.IsTrue(Get32BitFlags("SharpDevelop.exe"));
}
}
}

20
src/Main/Base/Test/ICSharpCode.SharpDevelop.Tests.csproj

@ -74,9 +74,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AbstractEntityIsOverridableTestFixture.cs" />
<Compile Include="AssemblyInfo.cs" /> <Compile Include="AssemblyInfo.cs" />
<Compile Include="CheckAssemblyFlags.cs" />
<Compile Include="CodeConverterTests.cs" /> <Compile Include="CodeConverterTests.cs" />
<Compile Include="Dom\CSharpModelTests.cs" /> <Compile Include="Dom\CSharpModelTests.cs" />
<Compile Include="Dom\ProjectEntityModelContextTests.cs" /> <Compile Include="Dom\ProjectEntityModelContextTests.cs" />
@ -90,6 +88,13 @@
<Compile Include="OutputTextLineParserTests.cs" /> <Compile Include="OutputTextLineParserTests.cs" />
<Compile Include="OverridableMethodsTestFixture.cs" /> <Compile Include="OverridableMethodsTestFixture.cs" />
<Compile Include="OverridablePropertiesTestFixture.cs" /> <Compile Include="OverridablePropertiesTestFixture.cs" />
<Compile Include="ProcessRunner\CancelLongRunningAppTestFixture.cs" />
<Compile Include="ProcessRunner\ConsoleAppTestFixtureBase.cs" />
<Compile Include="ProcessRunner\ExitCodeTestFixture.cs" />
<Compile Include="ProcessRunner\NoSuchExecutableTestFixture.cs" />
<Compile Include="ProcessRunner\ProcessExitedTestFixture.cs" />
<Compile Include="ProcessRunner\ProcessRunnerNotStartedTestFixture.cs" />
<Compile Include="ProcessRunner\StandardOutputFromProcessTestFixture.cs" />
<Compile Include="Project\BeforeBuildCustomToolProjectItemsTests.cs" /> <Compile Include="Project\BeforeBuildCustomToolProjectItemsTests.cs" />
<Compile Include="Project\MockSolution.cs" /> <Compile Include="Project\MockSolution.cs" />
<Compile Include="Project\ProjectCustomToolOptionsTests.cs" /> <Compile Include="Project\ProjectCustomToolOptionsTests.cs" />
@ -151,14 +156,6 @@
<Compile Include="Templates\CategorySortOrderTests.cs" /> <Compile Include="Templates\CategorySortOrderTests.cs" />
<Compile Include="Templates\FileTemplateCategoryComparerTests.cs" /> <Compile Include="Templates\FileTemplateCategoryComparerTests.cs" />
<Compile Include="WebReferences\HttpAuthenticationHeaderTests.cs" /> <Compile Include="WebReferences\HttpAuthenticationHeaderTests.cs" />
<Compile Include="StandardOutputFromProcessTestFixture.cs" />
<Compile Include="CancelLongRunningAppTestFixture.cs" />
<Compile Include="ExitCodeTestFixture.cs" />
<Compile Include="LineReceivedFromProcessTestFixture.cs" />
<Compile Include="NoSuchExecutableTestFixture.cs" />
<Compile Include="ProcessExitedTestFixture.cs" />
<Compile Include="ProcessRunnerNotStartedTestFixture.cs" />
<Compile Include="ConsoleAppTestFixtureBase.cs" />
<Compile Include="NavigationServiceTests\INavigationPointTextFixture.cs" /> <Compile Include="NavigationServiceTests\INavigationPointTextFixture.cs" />
<Compile Include="NavigationServiceTests\TestNavigationPoint.cs" /> <Compile Include="NavigationServiceTests\TestNavigationPoint.cs" />
<Compile Include="NavigationServiceTests\NavigationServiceTestFixture.cs" /> <Compile Include="NavigationServiceTests\NavigationServiceTestFixture.cs" />
@ -197,5 +194,8 @@
<EmbeddedResource Include="mime_utf-16_be_test.txt" /> <EmbeddedResource Include="mime_utf-16_be_test.txt" />
<EmbeddedResource Include="mime_utf-16_le_test.txt" /> <EmbeddedResource Include="mime_utf-16_le_test.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="ProcessRunner" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project> </Project>

51
src/Main/Base/Test/LineReceivedFromProcessTestFixture.cs

@ -1,51 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using NUnit.Framework;
using System;
using System.Collections;
using System.IO;
namespace ICSharpCode.SharpDevelop.Tests
{
/// <summary>
/// Tests the <see cref="ProcessRunner.LineReceived"/> event.
/// </summary>
[TestFixture]
public class LineReceivedFromProcessTestFixture : ConsoleAppTestFixtureBase
{
ProcessRunner runner;
ArrayList lines;
[SetUp]
public void Init()
{
lines = new ArrayList();
runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName());
}
[Test]
public void SingleLineOutput()
{
string echoText = "Test";
string expectedOutput = String.Concat(echoText, "\r\n");
runner.OutputLineReceived += OutputLineReceived;
runner.Start(GetConsoleAppFileName(), String.Concat("-echo:", echoText));
runner.WaitForExit();
Assert.AreEqual(0, runner.ExitCode, "Exit code should be zero.");
Assert.AreEqual(expectedOutput, runner.StandardOutput, "Should have some output.");
Assert.AreEqual(String.Empty, runner.StandardError, "Should not be any error output.");
Assert.AreEqual(1, lines.Count, "Should only have one output line.");
Assert.AreEqual(echoText, lines[0], "Line received is incorrect.");
}
void OutputLineReceived(object sender, LineReceivedEventArgs e)
{
lines.Add(e.Line);
}
}
}

74
src/Main/Base/Test/ProcessExitedTestFixture.cs

@ -1,74 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.Threading;
using NUnit.Framework;
namespace ICSharpCode.SharpDevelop.Tests
{
[TestFixture]
//[Ignore("Ignoring since need to run ConsoleApp.exe")]
public class ProcessExitedTestFixture : ConsoleAppTestFixtureBase
{
/// <summary>
/// Stores standard output received by the ProcessExit event.
/// </summary>
string standardOutput;
/// <summary>
/// Stores standard error received by the ProcessExit event.
/// </summary>
string standardError;
/// <summary>
/// Stores exit code received by the ProcessExit event.
/// </summary>
int exitCode = -1;
/// <summary>
/// Event that will be fired when the ProcessExit event occurs.
/// </summary>
AutoResetEvent exitEvent;
/// <summary>
/// Tests the Runner.ProcessExit event works.
/// </summary>
[Test]
public void ProcessExitEvent()
{
exitEvent = new AutoResetEvent(false);
ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName());
string echoText = "Test";
string expectedOutput = String.Concat(echoText, "\r\n");
runner.ProcessExited += new EventHandler(OnProcessExited);
runner.Start(GetConsoleAppFileName(), String.Concat("-echo:", echoText));
bool exited = exitEvent.WaitOne(2500, true);
Assert.IsTrue(exited, "Timed out waiting for exit event.");
Assert.AreEqual(0, exitCode, "Exit code should be zero.");
Assert.AreEqual(expectedOutput, standardOutput, "Should have some output.");
Assert.AreEqual(String.Empty, standardError, "Should not be any error output.");
}
/// <summary>
/// Handles the ProcessExited event.
/// </summary>
void OnProcessExited(object sender, EventArgs e)
{
ProcessRunner runner = (ProcessRunner)sender;
exitCode = runner.ExitCode;
standardOutput = runner.StandardOutput;
standardError = runner.StandardError;
exitEvent.Set();
}
}
}

6
src/Main/Base/Test/CancelLongRunningAppTestFixture.cs → src/Main/Base/Test/ProcessRunner/CancelLongRunningAppTestFixture.cs

@ -42,7 +42,7 @@ namespace ICSharpCode.SharpDevelop.Tests
public void Init() public void Init()
{ {
runner = new ProcessRunner(); runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
} }
[Test] [Test]
@ -57,9 +57,9 @@ namespace ICSharpCode.SharpDevelop.Tests
Process[] runningProcesses = Process.GetProcessesByName(processName); Process[] runningProcesses = Process.GetProcessesByName(processName);
Assert.AreEqual(1, runningProcesses.Length, "Process is not running."); Assert.AreEqual(1, runningProcesses.Length, "Process is not running.");
Assert.IsTrue(runner.IsRunning, "IsRunning should be true."); //Assert.IsTrue(runner.IsRunning, "IsRunning should be true.");
runner.Kill(); runner.Kill();
Assert.IsFalse(runner.IsRunning, "IsRunning should be false."); //Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
// Check console app has been shutdown. // Check console app has been shutdown.
runningProcesses = Process.GetProcessesByName(processName); runningProcesses = Process.GetProcessesByName(processName);

5
src/Main/Base/Test/ConsoleAppTestFixtureBase.cs → src/Main/Base/Test/ProcessRunner/ConsoleAppTestFixtureBase.cs

@ -2,14 +2,15 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Tests namespace ICSharpCode.SharpDevelop.Tests
{ {
public class ConsoleAppTestFixtureBase public class ConsoleAppTestFixtureBase
{ {
public static string GetConsoleAppFileName() public static FileName GetConsoleAppFileName()
{ {
return typeof(ICSharpCode.NAntAddIn.Tests.ConsoleApp.ConsoleApp).Assembly.Location; return FileName.Create(typeof(ICSharpCode.NAntAddIn.Tests.ConsoleApp.ConsoleApp).Assembly.Location);
} }
} }
} }

8
src/Main/Base/Test/ExitCodeTestFixture.cs → src/Main/Base/Test/ProcessRunner/ExitCodeTestFixture.cs

@ -19,17 +19,17 @@ namespace ICSharpCode.SharpDevelop.Tests
public void NonZeroExitCode() public void NonZeroExitCode()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
int expectedExitCode = 1; int expectedExitCode = 1;
Assert.IsFalse(runner.IsRunning, "IsRunning should be false."); //Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
runner.Start(GetConsoleAppFileName(), String.Concat("-exitcode:", expectedExitCode.ToString())); runner.Start(GetConsoleAppFileName(), String.Concat("-exitcode:", expectedExitCode.ToString()));
runner.WaitForExit(); runner.WaitForExit();
Assert.AreEqual(expectedExitCode, runner.ExitCode, "Exit code is incorrect."); Assert.AreEqual(expectedExitCode, runner.ExitCode, "Exit code is incorrect.");
Assert.IsFalse(runner.IsRunning, "IsRunning should be false."); //Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
} }
} }
} }

4
src/Main/Base/Test/NoSuchExecutableTestFixture.cs → src/Main/Base/Test/ProcessRunner/NoSuchExecutableTestFixture.cs

@ -27,8 +27,8 @@ namespace ICSharpCode.SharpDevelop.Tests
} }
[Test] [Test]
[ExpectedException(typeof(InvalidOperationException))] [ExpectedException(typeof(Win32Exception))]
// "Cannot start process because a file name has not been provided.")] // "The parameter is incorrect.")]
// - Message depends on system language. // - Message depends on system language.
public void RunBlankProcessFilename() public void RunBlankProcessFilename()
{ {

34
src/Main/Base/Test/ProcessRunner/ProcessExitedTestFixture.cs

@ -0,0 +1,34 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using System.Threading;
using NUnit.Framework;
namespace ICSharpCode.SharpDevelop.Tests
{
[TestFixture]
//[Ignore("Ignoring since need to run ConsoleApp.exe")]
public class ProcessExitedTestFixture : ConsoleAppTestFixtureBase
{
/// <summary>
/// Tests the Runner.ProcessExit event works.
/// </summary>
[Test]
public void ProcessExitEvent()
{
ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
string echoText = "Test";
runner.Start(GetConsoleAppFileName(), String.Concat("-echo:", echoText));
bool exited = runner.WaitForExitAsync().Wait(2500);
Assert.IsTrue(exited, "Timed out waiting for exit event.");
Assert.AreEqual(0, runner.ExitCode, "Exit code should be zero.");
}
}
}

19
src/Main/Base/Test/ProcessRunnerNotStartedTestFixture.cs → src/Main/Base/Test/ProcessRunner/ProcessRunnerNotStartedTestFixture.cs

@ -11,7 +11,6 @@ namespace ICSharpCode.SharpDevelop.Tests
/// correctly if it has not been started. /// correctly if it has not been started.
/// </summary> /// </summary>
[TestFixture] [TestFixture]
//[Ignore("Ignoring since need to run ConsoleApp.exe")]
public class ProcessRunnerNotStartedTestFixture public class ProcessRunnerNotStartedTestFixture
{ {
ProcessRunner runner; ProcessRunner runner;
@ -23,28 +22,24 @@ namespace ICSharpCode.SharpDevelop.Tests
} }
[Test] [Test]
[ExpectedException(typeof(InvalidOperationException))]
public void ExitCode() public void ExitCode()
{ {
Assert.AreEqual(0, runner.ExitCode, "Exit code should be zero."); int exit = runner.ExitCode;
}
[Test]
public void StandardOutput()
{
Assert.AreEqual(String.Empty, runner.StandardOutput, "Standard output should be empty.");
} }
[Test] [Test]
public void StandardError() [ExpectedException(typeof(InvalidOperationException))]
public void WaitForExit()
{ {
Assert.AreEqual(String.Empty, runner.StandardError, "Standard error should be empty."); runner.WaitForExit();
} }
[Test] [Test]
[ExpectedException(typeof(InvalidOperationException))] [ExpectedException(typeof(InvalidOperationException))]
public void WaitForExit() public void WaitForExitAsync()
{ {
runner.WaitForExit(); runner.WaitForExitAsync();
} }
} }
} }

123
src/Main/Base/Test/StandardOutputFromProcessTestFixture.cs → src/Main/Base/Test/ProcessRunner/StandardOutputFromProcessTestFixture.cs

@ -2,6 +2,7 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -14,28 +15,8 @@ namespace ICSharpCode.SharpDevelop.Tests
/// Runs a process that returns some text on the standard output. /// Runs a process that returns some text on the standard output.
/// </summary> /// </summary>
[TestFixture] [TestFixture]
//[Ignore("Ignoring since need to run ConsoleApp.exe")]
public class StandardOutputFromProcessTestFixture : ConsoleAppTestFixtureBase public class StandardOutputFromProcessTestFixture : ConsoleAppTestFixtureBase
{ {
int preTestThreadCount;
/// <summary>
/// Initialises each test.
/// </summary>
[SetUp]
public void Init()
{
GetPreTestThreadCount();
}
[TearDown]
public void TearDown()
{
//If we use Output.ReadLine then ThreadPoolCompletion Port threads are
//used rendering a threadcount test useless.
//CheckPostTestThreadCount();
}
/// <summary> /// <summary>
/// Runs a process expecting a single line of output. /// Runs a process expecting a single line of output.
/// </summary> /// </summary>
@ -43,17 +24,15 @@ namespace ICSharpCode.SharpDevelop.Tests
public void SingleLineOfOutput() public void SingleLineOfOutput()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
string echoText = "Test"; string echoText = "Test";
string expectedOutput = String.Concat(echoText, "\r\n"); string expectedOutput = "Test\r\n";
runner.RedirectStandardOutput = true;
runner.Start(GetConsoleAppFileName(), String.Concat("-echo:", echoText)); runner.Start(GetConsoleAppFileName(), String.Concat("-echo:", echoText));
runner.WaitForExit(); string output = runner.OpenStandardOutputReader().ReadToEnd();
Assert.IsFalse(runner.IsRunning, "IsRunning should be false."); Assert.AreEqual(expectedOutput, output, "Should have some output.");
Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect.");
Assert.AreEqual(expectedOutput, runner.StandardOutput, "Should have some output.");
Assert.AreEqual(String.Empty, runner.StandardError, "Should not be any error output.");
} }
/// <summary> /// <summary>
@ -63,17 +42,20 @@ namespace ICSharpCode.SharpDevelop.Tests
public void NoOutput() public void NoOutput()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
runner.RedirectStandardOutput = true;
runner.RedirectStandardError = true;
runner.Start(GetConsoleAppFileName()); runner.Start(GetConsoleAppFileName());
runner.WaitForExit(); runner.WaitForExit();
Assert.IsFalse(runner.IsRunning, "IsRunning should be false."); //Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect."); Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect.");
Assert.AreEqual(String.Empty, runner.StandardOutput, "Should not be any output."); Assert.AreEqual("", runner.OpenStandardOutputReader().ReadToEnd(), "Should not be any output.");
Assert.AreEqual(String.Empty, runner.StandardError, "Should not be any error output."); Assert.AreEqual("", runner.OpenStandardErrorReader().ReadToEnd(), "Should not be any error output.");
} }
/*
/// <summary> /// <summary>
/// The process that is run tries to send such a large amount of /// The process that is run tries to send such a large amount of
/// data that it fills the standard output's buffer. /// data that it fills the standard output's buffer.
@ -82,20 +64,19 @@ namespace ICSharpCode.SharpDevelop.Tests
public void LargeAmountOfOutput() public void LargeAmountOfOutput()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
string filename = "test.txt"; string filename = "test.txt";
string fullFilename = Path.Combine(runner.WorkingDirectory, filename); string fullFilename = Path.Combine(runner.WorkingDirectory, filename);
try try
{ {
string outputText = GetOutputText(); IEnumerable<string> outputText = GetOutputText();
CreateTextFile(fullFilename, outputText); File.WriteAllLines(fullFilename, outputText);
runner.Start(GetConsoleAppFileName(), String.Concat("-file:", filename)); runner.Start(GetConsoleAppFileName(), String.Concat("-file:", filename));
bool exited = runner.WaitForExit(5000); bool exited = runner.WaitForExitAsync().Wait(5000);
Assert.IsTrue(exited, "App did not exit."); Assert.IsTrue(exited, "App did not exit.");
Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect."); Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect.");
Assert.AreEqual(outputText, runner.StandardOutput, "Should have some output."); Assert.AreEqual(outputText, runner.StandardOutput, "Should have some output.");
Assert.AreEqual(String.Empty, runner.StandardError, "Should not be any error output."); Assert.AreEqual(String.Empty, runner.StandardError, "Should not be any error output.");
@ -116,20 +97,19 @@ namespace ICSharpCode.SharpDevelop.Tests
public void LargeAmountOfErrorOutput() public void LargeAmountOfErrorOutput()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
string filename = "test.txt"; string filename = "test.txt";
string fullFilename = Path.Combine(runner.WorkingDirectory, filename); string fullFilename = Path.Combine(runner.WorkingDirectory, filename);
try try
{ {
string outputText = GetOutputText(); IEnumerable<string> outputText = GetOutputText();
CreateTextFile(fullFilename, outputText); File.WriteAllLines(fullFilename, outputText);
runner.Start(GetConsoleAppFileName(), String.Concat("-error.file:", filename)); runner.Start(GetConsoleAppFileName(), String.Concat("-error.file:", filename));
bool exited = runner.WaitForExit(5000); bool exited = runner.WaitForExitAsync().Wait(5000);
Assert.IsTrue(exited, "App did not exit."); Assert.IsTrue(exited, "App did not exit.");
Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect."); Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect.");
Assert.AreEqual(String.Empty, runner.StandardOutput, "Should not be any output."); Assert.AreEqual(String.Empty, runner.StandardOutput, "Should not be any output.");
Assert.AreEqual(outputText, runner.StandardError, "Should have some error output."); Assert.AreEqual(outputText, runner.StandardError, "Should have some error output.");
@ -141,7 +121,7 @@ namespace ICSharpCode.SharpDevelop.Tests
} }
} }
} }
*/
/// <summary> /// <summary>
/// Runs a process expecting a single line of output written /// Runs a process expecting a single line of output written
/// to standard error. /// to standard error.
@ -150,68 +130,29 @@ namespace ICSharpCode.SharpDevelop.Tests
public void SingleLineOfErrorOutput() public void SingleLineOfErrorOutput()
{ {
ProcessRunner runner = new ProcessRunner(); ProcessRunner runner = new ProcessRunner();
runner.WorkingDirectory = Path.GetDirectoryName(GetConsoleAppFileName()); runner.WorkingDirectory = GetConsoleAppFileName().GetParentDirectory();
string echoText = "Test"; string echoText = "Test";
string expectedOutput = String.Concat(echoText, "\r\n"); string expectedOutput = "Test\r\n";
runner.RedirectStandardError = true;
runner.Start(GetConsoleAppFileName(), String.Concat("-error.echo:", echoText)); runner.Start(GetConsoleAppFileName(), String.Concat("-error.echo:", echoText));
runner.WaitForExit(); string output = runner.OpenStandardErrorReader().ReadToEnd();
Assert.AreEqual(0, runner.ExitCode, "Exit code is incorrect."); Assert.AreEqual(expectedOutput, output, "Should have some output.");
Assert.IsFalse(runner.IsRunning, "IsRunning should be false.");
Assert.AreEqual(String.Empty, runner.StandardOutput, "Should not be any output.");
Assert.AreEqual(expectedOutput, runner.StandardError, "Should have some error output.");
} }
/// <summary>
/// Creates a UTF8 text file.
/// </summary>
/// <param name="filename">The filename of the text file.</param>
/// <param name="outputText">The text to store in the file.</param>
void CreateTextFile(string filename, string outputText)
{
FileStream stream = File.Create(filename);
byte[] bytes = UnicodeEncoding.UTF8.GetBytes(outputText);
stream.Write(bytes, 0, bytes.Length);
stream.Close();
}
/// <summary> /// <summary>
/// Gets the output text that the console app will /// Gets the output text that the console app will
/// attempt to send us back. /// attempt to send us back.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
string GetOutputText() IEnumerable<string> GetOutputText()
{ {
StringBuilder outputText = new StringBuilder(); List<string> outputText = new List<string>();
for (int i = 0; i < 300; ++i) { for (int i = 0; i < 300; ++i) {
outputText.Append("i="); outputText.Add("i=" + i.ToString() + " A line of text.");
outputText.Append(i.ToString());
outputText.Append(" A line of text.\r\n");
} }
return outputText;
return outputText.ToString();
}
/// <summary>
/// Reads the number of threads before the test is run.
/// </summary>
void GetPreTestThreadCount()
{
Process process = Process.GetCurrentProcess();
preTestThreadCount = process.Threads.Count;
}
/// <summary>
/// Reads the number of threads after the test is run.
/// </summary>
void CheckPostTestThreadCount()
{
GC.Collect();
int postThreadCount = Process.GetCurrentProcess().Threads.Count;
Assert.AreEqual(preTestThreadCount, postThreadCount, "Thread count mismatch.");
} }
} }
} }
Loading…
Cancel
Save