/***************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
***************************************************************************/
//*************************************************************************************************
// MyTestTuip.cs
//
// This file defines the MyTest Tuip and service.
//
// Copyright(c) Microsoft Corporation, 2004
//*************************************************************************************************
namespace Microsoft.VisualStudio.TestTools.Samples
{
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Exceptions;
using Microsoft.VisualStudio.TestTools.TestManagement;
using Microsoft.VisualStudio.TestTools.Vsip;
/// <summary>
/// MyTest ITuip, ICommandProvider, and service implementation.
/// The important methods to implement for ITuip are:
/// - InvokeEditor: this method is called to invoke an editor from
/// the Test View or Test Manager window for your test.
/// InvokeEditor can be used to invoke a complete GUI editor or
/// use VS services on existing editors (such as opening a source
/// file and positioning the caret on a particular line).
/// - InvokeResultViewer: this method is called to invoke a result
/// details viewer for your test results. InvokeResultViewer can
/// be used to show test result-specific information stored in your
/// derived TestResult class.
/// </summary>
internal sealed class MyTestTuip : BaseTuip, SMyTestService, ICommandProvider
{
#region Constructors
/// <summary>
/// The Basic constructor for the TUIP. This takes the service provider to be
/// used by the TUIP and and associated classes. This implementation just calls
/// the BaseTuip constructor
/// </summary>
/// <param name="serviceProvider">The Service Provider to be used</param>
public MyTestTuip(System.IServiceProvider serviceProvider) : base(serviceProvider)
{ }
#endregion
#region Overrides
/// <summary>
/// Invoke editor for my test.
/// Test View and Test Manager call this method to open the custom editor.
/// This method will be not called when you double click the file in solution
/// explorer.
/// This method is actually equivalent to double clicking the file in solution
/// explorer. As a result, this implementation will call the project's OpenFile
/// function which will call the appropriate editor factory.
///
/// If you wish to have no editor for your test type other than the default text
/// editor in VS, then you should not implement or override this method.
/// </summary>
/// <param name="uiBlob">Identifies the Project/Item blob to be displayed</param>
/// <param name="test">The test that the editor is being invoked for</param>
/*public override void InvokeEditor(UIBlob uiBlob, ITestElement test)
{
MyTestAssertHelper.ParameterNotNull(uiBlob, "uiBlob");
MyTestAssertHelper.ParameterNotNull(test, "test");
}*/
/// <summary>
/// Invoke the result viewer for MyTest. In this method, we need to keep references
/// to previously opened windows to ensure that we dont recreate the window needlessly.
/// </summary>
/// <param name="resultMessage">The result to be viewed</param>
public override void InvokeResultViewer(TestResultMessage resultMessage)
{
MyTestResult result = resultMessage as MyTestResult;
if (result == null || result.Test == null)
throw new ArgumentException("resultMessage");
// The ID of the window for the result
int idWindow;
// Bring up the existing tool window if it is open
if (!ResultWindowMapping.TryGetValue(result.Id, out idWindow))
{
// There wasn't already a window for this result
// so get another ID -- any old number will do
idWindow = m_idNext++;
}
// Get/create the window for the result. The underlying package will create the window if it
// doesnt already exist.
ToolWindowPane toolWindowPane = MyTestPackage.Instance.FindToolWindow(typeof(MyTestResultViewWindow), idWindow, true);
MyTestResultViewWindow windowResult = toolWindowPane as MyTestResultViewWindow; // Cast to the correct type
if (windowResult == null || windowResult.Frame == null)
{
throw new EqtException(Properties.Resources.ResultWindowCouldNotBeCreated);
}
else
{
windowResult.LoadResult(result); // Load the result into the window
}
// The "right" way to set the caption of the window is this:
// windowResult.Caption = result.Test.Name;
// Unfortunately, a bug prevents that from working, so we do this for now:
((IVsWindowFrame)windowResult.Frame).SetProperty((int)__VSFPROPID.VSFPROPID_Caption, result.Test.Name);
((IVsWindowFrame)windowResult.Frame).Show();
// Add mapping after tool window is opened successfully
if (!ResultWindowMapping.ContainsKey(result.Id))
ResultWindowMapping.Add(result.Id, idWindow);
}
/// <summary>
/// Closes the detailed results viewer for a specific result
/// </summary>
/// <param name="resultMessage">The result for which to close the window</param>
public override void CloseResultViewer(TestResultMessage resultMessage)
{
MyTestResult result = resultMessage as MyTestResult;
if (result == null || result.Test == null)
throw new ArgumentException("resultMessage");
// If there's no window for the result ID no need to close it
if (ResultWindowMapping.ContainsKey(result.Id))
{
// Retrieve the Window ID for the result
int toolWindowId = (int)ResultWindowMapping[result.Id];
// Get Hold of the window
ToolWindowPane toolWindowPane = MyTestPackage.Instance.FindToolWindow(typeof(MyTestResultViewWindow), toolWindowId, false);
MyTestResultViewWindow resultWindow = toolWindowPane as MyTestResultViewWindow;
// Close the window
if (resultWindow != null)
((IVsWindowFrame)resultWindow.Frame).CloseFrame(0);
}
}
/// <summary>
/// User Control to show in the Run Config dialog tab for this Test Type.
/// We have not implemented one here. Returning null signifies this no special editor
/// </summary>
public override IRunConfigurationCustomEditor RunConfigurationEditor
{
get { return new MyTestRunConfigControl(); }
}
#endregion
#region ICommandProvider Implementation
/// <summary>
/// These are context menu specific extentions that are
/// unique to this test type. This sample does not have any
/// type specific commands, so return null when called.
/// </summary>
/// <param name="contextMenuId">The ID Context Menu for which commands are being requested</param>
/// <returns>CommandID Array of type specific commands</returns>
public CommandID[] GetTypeSpecificCommands(CommandID contextMenuId)
{
return new CommandID[] { s_checkCountCmdId };
}
/// <summary>
/// Queries the Status of a specific Command ID on a context menu. This allows
/// the test type to disable/enable command depending on context and selected tests
/// </summary>
/// <param name="contextMenuId">Context Menu to be quieried</param>
/// <param name="commandId">Command being being quieried</param>
/// <param name="items">List of selected tests</param>
/// <returns></returns>
public CommandStatus QueryStatus(CommandID contextMenuId, CommandID commandId, IList items)
{
// Don't use operator == as CommandID does not override operator ==
if (contextMenuId.Equals(s_testContextCmdId))
{
if (commandId.Equals(s_deleteCmdId)
|| commandId.Equals(s_checkCountCmdId))
{
return new CommandStatus(true, true);
}
}
return new CommandStatus(false, false);
}
/// <summary>
/// Gets the handler for a specific command ID & Context menu. This is
/// used to invoke the command.
/// </summary>
/// <param name="contextMenuId">Conext Menu for which this command is for</param>
/// <param name="commandId">The command for which a handler is required</param>
/// <returns>The CommandEventHandler to be invoked</returns>
public CommandEventHandler GetCommandHandler(CommandID contextMenuId, CommandID commandId)
{
if (contextMenuId.Equals(s_testContextCmdId))
{
if (commandId.Equals(s_deleteCmdId))
{
return new CommandEventHandler(OnDeleteCommand);
}
else if (commandId.Equals(s_checkCountCmdId))
{
return new CommandEventHandler(OnCheckCountCommand);
}
}
return null;
}
/// <summary>
/// Delete selected tests.
/// </summary>
private CommandResult OnDeleteCommand(object sender, CommandEventArgs e)
{
bool successful = true;
string message = string.Empty;
//Base class provides a method to delete tests.
successful = base.DeleteAllTestsFromList(e.Items, typeof(MyTest), out message);
return new CommandResult(successful, message);
}
/// <summary>
/// Check the count of processes of selected my tests.
/// </summary>
private CommandResult OnCheckCountCommand(object sender, CommandEventArgs e)
{
string statistics = "Current counts of EXE processes refered by the selected My Test items:\r\n\r\n";
// This is a known issue that the type of e.Items is not public. We will fix this issue in next release.
// For now, try the following workaround. It gets all MyTest type tests.
ITestManagement testMgrService = MyTestPackage.Instance.GetService(typeof(STestManagement)) as ITestManagement;
StringDictionary procNames = new StringDictionary();
string procName = null;
foreach(ITestElement test in testMgrService.TmiInstance.GetTests())
{
if (test is MyTest && !procNames.ContainsKey(
procName = Path.GetFileNameWithoutExtension(((MyTest)test).CommandLine)))
{
procNames.Add(procName, string.Empty);
statistics += string.Format("{0}: {1}\r\n", procName, Process.GetProcessesByName(procName).Length);
}
}
MessageBox.Show(statistics);
return new CommandResult(true);
}
#endregion
#region Private data
// This maintains the Window mapping between a result ID and a Window ID
internal static Dictionary<TestResultId, int> ResultWindowMapping = new Dictionary<TestResultId, int>();
private static int m_idNext = 0; // id of the next window to create
// The following are IDs that are shipped with VSTS 2005.
private static readonly Guid s_testToolsCmdSetGuid = new Guid("{B85579AA-8BE0-4c4f-A850-90902B317571}");
private static readonly CommandID s_testContextCmdId = new CommandID(s_testToolsCmdSetGuid, 0x1306);
private static readonly CommandID s_deleteCmdId = new CommandID(s_testToolsCmdSetGuid, 0x3306);
// These are your own commands.
private static readonly Guid s_myTestCmdSetGuid = new Guid("{0C97A6F2-953A-45B4-DB0A-8CC3663C5B57}");
private static readonly CommandID s_checkCountCmdId = new CommandID(s_myTestCmdSetGuid, 0x1100);
#endregion
}
/// <summary>
/// MyTest service interface.
/// The interface itself is empty; it's just used to identify the service
/// (by GUID) to Visual Studio.
/// </summary>
// Suppress IdentifiersShouldHaveCorrectPrefix
// Best practices say that service interfaces should start with S.
[SuppressMessage("Microsoft.Naming", "CA1715")]
// Suppress AvoidEmptyInterfaces.
// Creating this empty interface is the recommended design pattern.
[SuppressMessage("Microsoft.Design", "CA1040")]
[Guid("19D5DFEB-BEAE-4307-BCFF-945BE3AA6CDF")]
public interface SMyTestService
{ }
}