// // // // // $Revision$ // using System; using System.Collections.Specialized; using System.IO; using ICSharpCode.Core; using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Util; using ICSharpCode.UnitTesting; namespace ICSharpCode.CodeCoverage { /// /// Menu command selected after right clicking a test method in the text editor /// to run tests with code coverage. /// public class RunTestWithCodeCoverageCommand : AbstractRunTestCommand { static MessageViewCategory category; PartCoverRunner runner; public RunTestWithCodeCoverageCommand() { runner = new PartCoverRunner(); runner.Exited += PartCoverExited; runner.OutputLineReceived += OutputLineReceived; } protected override void RunTests(UnitTestApplicationStartHelper helper) { SetPartCoverRunnerProperties(helper); RunPartCover(); } protected override void OnStop() { runner.Stop(); } /// /// Clears the code coverage results on display before running /// a series of tests. /// protected override void OnBeforeRunTests() { WorkbenchSingleton.SafeThreadAsyncCall(Category.ClearText); WorkbenchSingleton.SafeThreadAsyncCall(CodeCoverageService.ClearResults); } /// /// Shows the code coverage results window only if there were no /// test failures. /// protected override void OnAfterRunTests() { if (!TaskService.HasCriticalErrors(false)) { ShowPad(WorkbenchSingleton.Workbench.GetPad(typeof(CodeCoveragePad))); } } /// /// Gets the message view output window. /// MessageViewCategory Category { get { if (category == null) { MessageViewCategory.Create(ref category, "CodeCoverage", StringParser.Parse("${res:ICSharpCode.UnitTesting.CodeCoverage}")); } return category; } } void SetPartCoverRunnerProperties(UnitTestApplicationStartHelper helper) { string partCoverOutputDirectory = GetPartCoverOutputDirectory(helper.Project); PartCoverSettings settings = GetPartCoverSettings(helper.Project); // By default get the code coverage for everything if // no include or exclude regular expressions have been // set for this project. Note that the CodeCoverageResults // will ignore any type that has no source code available // for it even though the type may be in the Part Cover // results file. if (settings.Include.Count == 0) { settings.Include.Add("[*]*"); } runner.PartCoverFileName = GetPartCoverFileName(); runner.Target = helper.UnitTestApplication; runner.TargetArguments = helper.GetArguments(); runner.TargetWorkingDirectory = Path.GetDirectoryName(helper.Assemblies[0]); runner.Output = Path.Combine(partCoverOutputDirectory, "Coverage.Xml"); AddStringsToCollection(settings.Include, runner.Include); AddStringsToCollection(settings.Exclude, runner.Exclude); } void RunPartCover() { // Remove existing coverage results file. if (File.Exists(runner.Output)) { File.Delete(runner.Output); } // Create PartCover output directory. if (!Directory.Exists(Path.GetDirectoryName(runner.Output))) { Directory.CreateDirectory(Path.GetDirectoryName(runner.Output)); } Category.AppendLine(StringParser.Parse("${res:ICSharpCode.CodeCoverage.RunningCodeCoverage}")); Category.AppendLine(runner.CommandLine); runner.Start(); } /// /// Displays the output from PartCover after it has exited. /// /// The event source. /// The PartCover exit event arguments. void PartCoverExited(object sender, PartCoverExitEventArgs e) { DisplayCoverageResults(runner.Output); WorkbenchSingleton.SafeThreadAsyncCall(TestsFinished); } void OutputLineReceived(object sender, LineReceivedEventArgs e) { Category.AppendLine(e.Line); } void DisplayCoverageResults(string fileName) { if (!File.Exists(fileName)) { Task task = new Task(null, String.Concat(StringParser.Parse("${res:ICSharpCode.CodeCoverage.NoCodeCoverageResultsGenerated}"), " ", fileName), 0, 0, TaskType.Error); WorkbenchSingleton.SafeThreadAsyncCall(TaskService.Add, task); return; } CodeCoverageResults results = new CodeCoverageResults(fileName); WorkbenchSingleton.SafeThreadAsyncCall(CodeCoverageService.ShowResults, results); } /// /// Returns the full path to the PartCover console application if it /// exists. /// /// /// Use Path.GetFullPath otherwise we end up with a filename path like: /// C:\Program Files\SharpDevelop\bin\..\bin\Tools\PartCover\PartCover.exe /// string GetPartCoverFileName() { return Path.GetFullPath(Path.Combine(FileUtility.ApplicationRootPath, @"bin\Tools\PartCover\PartCover.exe")); } string GetPartCoverOutputDirectory(IProject project) { return Path.Combine(project.Directory, "PartCover"); } PartCoverSettings GetPartCoverSettings(IProject project) { string fileName = PartCoverSettings.GetFileName(project); if (File.Exists(fileName)) { return new PartCoverSettings(fileName); } return new PartCoverSettings(); } void AddStringsToCollection(StringCollection source, StringCollection target) { target.Clear(); foreach (string item in source) { target.Add(item); } } } }